Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
 
3
/*
4
*  $Id: DBMSSQL.php 1262 2009-10-26 20:54:39Z francois $
5
*
6
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
7
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
8
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
9
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
10
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
11
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
12
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
13
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
14
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
15
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
16
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17
*
18
* This software consists of voluntary contributions made by many individuals
19
* and is licensed under the LGPL. For more information please see
20
* <http://propel.phpdb.org>.
21
*/
22
 
23
/**
24
 * This is used to connect to a MSSQL database.
25
 *
26
 * @author     Hans Lellelid <hans@xmpl.org> (Propel)
27
 * @version    $Revision: 1262 $
28
 * @package    propel.adapter
29
 */
30
class DBMSSQL extends DBAdapter {
31
 
32
	/**
33
	 * This method is used to ignore case.
34
	 *
35
	 * @param      in The string to transform to upper case.
36
	 * @return     The upper case string.
37
	 */
38
	public function toUpperCase($in)
39
	{
40
		return "UPPER(" . $in . ")";
41
	}
42
 
43
	/**
44
	 * This method is used to ignore case.
45
	 *
46
	 * @param      in The string whose case to ignore.
47
	 * @return     The string in a case that can be ignored.
48
	 */
49
	public function ignoreCase($in)
50
	{
51
		return "UPPER(" . $in . ")";
52
	}
53
 
54
	/**
55
	 * Returns SQL which concatenates the second string to the first.
56
	 *
57
	 * @param      string String to concatenate.
58
	 * @param      string String to append.
59
	 * @return     string
60
	 */
61
	public function concatString($s1, $s2)
62
	{
63
		return "($s1 + $s2)";
64
	}
65
 
66
	/**
67
	 * Returns SQL which extracts a substring.
68
	 *
69
	 * @param      string String to extract from.
70
	 * @param      int Offset to start from.
71
	 * @param      int Number of characters to extract.
72
	 * @return     string
73
	 */
74
	public function subString($s, $pos, $len)
75
	{
76
		return "SUBSTRING($s, $pos, $len)";
77
	}
78
 
79
	/**
80
	 * Returns SQL which calculates the length (in chars) of a string.
81
	 *
82
	 * @param      string String to calculate length of.
83
	 * @return     string
84
	 */
85
	public function strLength($s)
86
	{
87
		return "LEN($s)";
88
	}
89
 
90
	/**
91
	 * @see        DBAdapter::quoteIdentifier()
92
	 */
93
	public function quoteIdentifier($text)
94
	{
95
		return '[' . $text . ']';
96
	}
97
 
98
	/**
99
	 * @see        DBAdapter::random()
100
	 */
101
	public function random($seed = null)
102
	{
103
		return 'rand('.((int) $seed).')';
104
	}
105
 
106
  /**
107
   * Simulated Limit/Offset
108
   * This rewrites the $sql query to apply the offset and limit.
109
   * @see        DBAdapter::applyLimit()
110
   * @author     Justin Carlson <justin.carlson@gmail.com>
111
   * @author     Benjamin Runnels <kraven@kraven.org>
112
   */
113
  public function applyLimit(&$sql, $offset, $limit)
114
  {
115
    // make sure offset and limit are numeric
116
    if (!is_numeric($offset) || !is_numeric($limit))
117
    {
118
      throw new Exception("DBMSSQL::applyLimit() expects a number for argument 2 and 3");
119
    }
120
 
121
    //split the select and from clauses out of the original query
122
    $selectSegment = array();
123
    preg_match('/\Aselect(.*)from(.*)/si',$sql,$selectSegment);
124
    if (count($selectSegment)==3)
125
    {
126
      $selectStatement = trim($selectSegment[1]);
127
      $fromStatement = trim($selectSegment[2]);
128
    }
129
    else
130
    {
131
      throw new Exception("DBMSSQL::applyLimit() could not locate the select statement at the start of the query. ");
132
    }
133
 
134
    //handle the ORDER BY clause if present
135
    $orderSegment = array();
136
    preg_match('/order by(.*)\Z/si',$fromStatement,$orderSegment);
137
    if (count($orderSegment)==2)
138
    {
139
      //remove the ORDER BY from $sql
140
      $fromStatement = trim(str_replace($orderSegment[0], '', $fromStatement));
141
      //the ORDER BY clause is used in our inner select ROW_NUMBER() clause
142
      $countColumn = trim($orderSegment[1]);
143
    }
144
 
145
    //setup inner and outer select selects
146
    $innerSelect = '';
147
    $outerSelect = '';
148
    foreach(explode(', ',$selectStatement) as $selCol) {
149
      @list($column,,$alias) = explode(' ', $selCol);
150
      //make sure the current column isn't * or an aggregate
151
      if ($column!='*' && !strstr($column,'(')) {
152
        //we can use the first non-aggregate column for ROW_NUMBER() if it wasn't already set from an order by clause
153
        if(!isset($countColumn)) {
154
          $countColumn = $column;
155
        }
156
 
157
        //add an alias to the inner select so all columns will be unique
158
        $innerSelect .= $column." AS [$column],";
159
 
160
        //use the alias in the outer select if one was present on the original select column
161
        if(isset($alias)) {
162
          $outerSelect .= "[$column] AS $alias,";
163
        } else {
164
          $outerSelect .= "[$column],";
165
        }
166
      } else {
167
        //agregate columns must always have an alias clause
168
        if(!isset($alias)) {
169
          throw new Exception("DBMSSQL::applyLimit() requires aggregate columns to have an Alias clause");
170
        }
171
        //use the whole aggregate column in the inner select
172
        $innerSelect .= "$selCol,";
173
        //only add the alias for the aggregate to the outer select
174
        $outerSelect .= "$alias,";
175
      }
176
    }
177
 
178
    //check if we got this far and still don't have a viable column to user with ROW_NUMBER()
179
    if(!isset($countColumn)) {
180
      throw new Exception("DBMSSQL::applyLimit() requires an ORDER BY clause or at least one non-aggregate column in the select statement");
181
    }
182
 
183
    //ROW_NUMBER() starts at 1 not 0
184
    $from = ($offset+1);
185
    $to = ($limit+$offset);
186
 
187
    //substring our select strings to get rid of the last comma and add our FROM and SELECT clauses
188
    $innerSelect = "SELECT ROW_NUMBER() OVER(ORDER BY $countColumn) AS RowNumber, ".substr($innerSelect,0,-1).' FROM';
189
    $outerSelect = 'SELECT '.substr($outerSelect,0,-1).' FROM';
190
 
191
    // build the query
192
    $sql = "$outerSelect ($innerSelect $fromStatement) AS derivedb WHERE RowNumber BETWEEN $from AND $to";
193
  }
194
}