|
1 | 1 | <?php |
2 | 2 | /* |
3 | | - * $Id: Mssql.php 7691 2011-02-04 15:43:29Z jwage $ |
| 3 | + * $Id: Mssql.php 7659 2010-06-08 18:16:17Z jwage $ |
4 | 4 | * |
5 | 5 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
6 | 6 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
27 | 27 | * @license http://www.opensource.org/licenses/lgpl-license.php LGPL |
28 | 28 | * @author Konsta Vesterinen <kvesteri@cc.hut.fi> |
29 | 29 | * @author Lukas Smith <smith@pooteeweet.org> (PEAR MDB2 library) |
30 | | - * @version $Revision: 7691 $ |
| 30 | + * @version $Revision: 7659 $ |
31 | 31 | * @link www.doctrine-project.org |
32 | 32 | * @since 1.0 |
33 | 33 | */ |
@@ -140,152 +140,98 @@ public function quoteIdentifier($identifier, $checkOption = false) |
140 | 140 | * @link http://lists.bestpractical.com/pipermail/rt-devel/2005-June/007339.html |
141 | 141 | * @return string |
142 | 142 | */ |
143 | | - public function modifyLimitQuery($query, $limit = false, $offset = false, $isManip = false, $isSubQuery = false, Doctrine_Query $queryOrigin = null) |
| 143 | + public function modifyLimitQuery($query, $limit = false, $offset = false, $isManip = false, $isSubQuery = false) |
144 | 144 | { |
145 | | - if ($limit === false || !($limit > 0)) { |
146 | | - return $query; |
147 | | - } |
148 | | - |
149 | | - $orderby = stristr($query, 'ORDER BY'); |
| 145 | + if ($limit > 0) { |
| 146 | + $count = intval($limit); |
| 147 | + $offset = intval($offset); |
150 | 148 |
|
151 | | - if ($offset !== false && $orderby === false) { |
152 | | - throw new Doctrine_Connection_Exception("OFFSET cannot be used in MSSQL without ORDER BY due to emulation reasons."); |
153 | | - } |
154 | | - |
155 | | - $count = intval($limit); |
156 | | - $offset = intval($offset); |
| 149 | + if ($offset < 0) { |
| 150 | + throw new Doctrine_Connection_Exception("LIMIT argument offset=$offset is not valid"); |
| 151 | + } |
157 | 152 |
|
158 | | - if ($offset < 0) { |
159 | | - throw new Doctrine_Connection_Exception("LIMIT argument offset=$offset is not valid"); |
160 | | - } |
| 153 | + $orderby = stristr($query, 'ORDER BY'); |
161 | 154 |
|
162 | | - $orderbySql = $queryOrigin->getSqlQueryPart('orderby'); |
163 | | - $orderbyDql = $queryOrigin->getDqlPart('orderby'); |
| 155 | + if ($orderby !== false) { |
| 156 | + // Ticket #1835: Fix for ORDER BY alias |
| 157 | + // Ticket #2050: Fix for multiple ORDER BY clause |
| 158 | + $order = str_ireplace('ORDER BY', '', $orderby); |
| 159 | + $orders = explode(',', $order); |
164 | 160 |
|
165 | | - if ($orderby !== false) { |
166 | | - $orders = $this->parseOrderBy(implode(', ', $queryOrigin->getDqlPart('orderby'))); |
| 161 | + for ($i = 0; $i < count($orders); $i++) { |
| 162 | + $sorts[$i] = (stripos($orders[$i], ' desc') !== false) ? 'DESC' : 'ASC'; |
| 163 | + $orders[$i] = trim(preg_replace('/\s+(ASC|DESC)$/i', '', $orders[$i])); |
167 | 164 |
|
168 | | - for ($i = 0; $i < count($orders); $i++) { |
169 | | - $sorts[$i] = (stripos($orders[$i], ' desc') !== false) ? 'DESC' : 'ASC'; |
170 | | - $orders[$i] = trim(preg_replace('/\s+(ASC|DESC)$/i', '', $orders[$i])); |
| 165 | + // find alias in query string |
| 166 | + $helper_string = stristr($query, $orders[$i]); |
171 | 167 |
|
172 | | - list($fieldAliases[$i], $fields[$i]) = strstr($orders[$i], '.') ? explode('.', $orders[$i]) : array('', $orders[$i]); |
173 | | - $columnAlias[$i] = $queryOrigin->getSqlTableAlias($queryOrigin->getExpressionOwner($orders[$i])); |
| 168 | + $from_clause_pos = strpos($helper_string, ' FROM '); |
| 169 | + $fields_string = substr($helper_string, 0, $from_clause_pos + 1); |
174 | 170 |
|
175 | | - $cmp = $queryOrigin->getQueryComponent($queryOrigin->getExpressionOwner($orders[$i])); |
176 | | - $tables[$i] = $cmp['table']; |
177 | | - $columns[$i] = $cmp['table']->getColumnName($fields[$i]); |
| 171 | + $field_array = explode(',', $fields_string); |
| 172 | + $field_array = array_shift($field_array); |
| 173 | + $aux2 = spliti(' as ', $field_array); |
| 174 | + $aux2 = explode('.', end($aux2)); |
178 | 175 |
|
179 | | - // TODO: This sould be refactored as method called Doctrine_Table::getColumnAlias(<column name>). |
180 | | - $aliases[$i] = $columnAlias[$i] . '__' . $columns[$i]; |
| 176 | + $aliases[$i] = trim(end($aux2)); |
| 177 | + } |
181 | 178 | } |
182 | | - } |
183 | | - |
184 | | - // Ticket #1259: Fix for limit-subquery in MSSQL |
185 | | - $selectRegExp = 'SELECT\s+'; |
186 | | - $selectReplace = 'SELECT '; |
187 | | - |
188 | | - if (preg_match('/^SELECT(\s+)DISTINCT/i', $query)) { |
189 | | - $selectRegExp .= 'DISTINCT\s+'; |
190 | | - $selectReplace .= 'DISTINCT '; |
191 | | - } |
192 | | - |
193 | | - $fields_string = substr($query, strlen($selectReplace), strpos($query, ' FROM ') - strlen($selectReplace)); |
194 | | - $field_array = explode(',', $fields_string); |
195 | | - $field_array = array_shift($field_array); |
196 | | - $aux2 = preg_split('/ as /i', $field_array); |
197 | | - $aux2 = explode('.', end($aux2)); |
198 | | - $key_field = trim(end($aux2)); |
199 | 179 |
|
200 | | - $query = preg_replace('/^'.$selectRegExp.'/i', $selectReplace . 'TOP ' . ($count + $offset) . ' ', $query); |
| 180 | + // Ticket #1259: Fix for limit-subquery in MSSQL |
| 181 | + $selectRegExp = 'SELECT\s+'; |
| 182 | + $selectReplace = 'SELECT '; |
201 | 183 |
|
202 | | - if ($isSubQuery === true) { |
203 | | - $query = 'SELECT TOP ' . $count . ' ' . $this->quoteIdentifier('inner_tbl') . '.' . $key_field . ' FROM (' . $query . ') AS ' . $this->quoteIdentifier('inner_tbl'); |
204 | | - } else { |
205 | | - $query = 'SELECT * FROM (SELECT TOP ' . $count . ' * FROM (' . $query . ') AS ' . $this->quoteIdentifier('inner_tbl'); |
206 | | - } |
| 184 | + if (preg_match('/^SELECT(\s+)DISTINCT/i', $query)) { |
| 185 | + $selectRegExp .= 'DISTINCT\s+'; |
| 186 | + $selectReplace .= 'DISTINCT '; |
| 187 | + } |
207 | 188 |
|
208 | | - if ($orderby !== false) { |
209 | | - $query .= ' ORDER BY '; |
| 189 | + $fields_string = substr($query, strlen($selectReplace), strpos($query, ' FROM ') - strlen($selectReplace)); |
| 190 | + $field_array = explode(',', $fields_string); |
| 191 | + $field_array = array_shift($field_array); |
| 192 | + $aux2 = spliti(' as ', $field_array); |
| 193 | + $aux2 = explode('.', end($aux2)); |
| 194 | + $key_field = trim(end($aux2)); |
210 | 195 |
|
211 | | - for ($i = 0, $l = count($orders); $i < $l; $i++) { |
212 | | - if ($i > 0) { // not first order clause |
213 | | - $query .= ', '; |
214 | | - } |
| 196 | + $query = preg_replace('/^'.$selectRegExp.'/i', $selectReplace . 'TOP ' . ($count + $offset) . ' ', $query); |
215 | 197 |
|
216 | | - $query .= $this->modifyOrderByColumn($tables[$i], $columns[$i], $this->quoteIdentifier('inner_tbl') . '.' . $this->quoteIdentifier($aliases[$i])) . ' '; |
217 | | - $query .= (stripos($sorts[$i], 'asc') !== false) ? 'DESC' : 'ASC'; |
| 198 | + if ($isSubQuery === true) { |
| 199 | + $query = 'SELECT TOP ' . $count . ' ' . $this->quoteIdentifier('inner_tbl') . '.' . $key_field . ' FROM (' . $query . ') AS ' . $this->quoteIdentifier('inner_tbl'); |
| 200 | + } else { |
| 201 | + $query = 'SELECT * FROM (SELECT TOP ' . $count . ' * FROM (' . $query . ') AS ' . $this->quoteIdentifier('inner_tbl'); |
218 | 202 | } |
219 | | - } |
220 | | - |
221 | | - if ($isSubQuery !== true) { |
222 | | - $query .= ') AS ' . $this->quoteIdentifier('outer_tbl'); |
223 | 203 |
|
224 | 204 | if ($orderby !== false) { |
225 | | - $query .= ' ORDER BY '; |
| 205 | + $query .= ' ORDER BY '; |
226 | 206 |
|
227 | | - for ($i = 0, $l = count($orders); $i < $l; $i++) { |
228 | | - if ($i > 0) { // not first order clause |
229 | | - $query .= ', '; |
230 | | - } |
| 207 | + for ($i = 0, $l = count($orders); $i < $l; $i++) { |
| 208 | + if ($i > 0) { // not first order clause |
| 209 | + $query .= ', '; |
| 210 | + } |
231 | 211 |
|
232 | | - $query .= $this->modifyOrderByColumn($tables[$i], $columns[$i], $this->quoteIdentifier('outer_tbl') . '.' . $this->quoteIdentifier($aliases[$i])) . ' ' . $sorts[$i]; |
| 212 | + $query .= $this->quoteIdentifier('inner_tbl') . '.' . $aliases[$i] . ' '; |
| 213 | + $query .= (stripos($sorts[$i], 'asc') !== false) ? 'DESC' : 'ASC'; |
233 | 214 | } |
234 | 215 | } |
235 | | - } |
236 | 216 |
|
237 | | - return $query; |
238 | | - } |
| 217 | + if ($isSubQuery !== true) { |
| 218 | + $query .= ') AS ' . $this->quoteIdentifier('outer_tbl'); |
239 | 219 |
|
240 | | - /** |
241 | | - * Parse an OrderBy-Statement into chunks |
242 | | - * |
243 | | - * @param string $orderby |
244 | | - */ |
245 | | - private function parseOrderBy($orderby) |
246 | | - { |
247 | | - $matches = array(); |
248 | | - $chunks = array(); |
249 | | - $tokens = array(); |
250 | | - $parsed = str_ireplace('ORDER BY', '', $orderby); |
| 220 | + if ($orderby !== false) { |
| 221 | + $query .= ' ORDER BY '; |
251 | 222 |
|
252 | | - preg_match_all('/(\w+\(.+?\)\s+(ASC|DESC)),?/', $orderby, $matches); |
253 | | - |
254 | | - $matchesWithExpressions = $matches[1]; |
| 223 | + for ($i = 0, $l = count($orders); $i < $l; $i++) { |
| 224 | + if ($i > 0) { // not first order clause |
| 225 | + $query .= ', '; |
| 226 | + } |
255 | 227 |
|
256 | | - foreach ($matchesWithExpressions as $match) { |
257 | | - $chunks[] = $match; |
258 | | - $parsed = str_replace($match, '##' . (count($chunks) - 1) . '##', $parsed); |
259 | | - } |
260 | | - |
261 | | - $tokens = preg_split('/,/', $parsed); |
262 | | - |
263 | | - for ($i = 0, $iMax = count($tokens); $i < $iMax; $i++) { |
264 | | - $tokens[$i] = trim(preg_replace('/##(\d+)##/e', "\$chunks[\\1]", $tokens[$i])); |
| 228 | + $query .= $this->quoteIdentifier('outer_tbl') . '.' . $aliases[$i] . ' ' . $sorts[$i]; |
| 229 | + } |
| 230 | + } |
| 231 | + } |
265 | 232 | } |
266 | 233 |
|
267 | | - return $tokens; |
268 | | - } |
269 | | - |
270 | | - /** |
271 | | - * Order and Group By are not possible on columns from type text. |
272 | | - * This method fix this issue by wrap the given term (column) into a CAST directive. |
273 | | - * |
274 | | - * @see DC-828 |
275 | | - * @param Doctrine_Table $table |
276 | | - * @param string $field |
277 | | - * @param string $term The term which will changed if it's necessary, depending to the field type. |
278 | | - * @return string |
279 | | - */ |
280 | | - public function modifyOrderByColumn(Doctrine_Table $table, $field, $term) |
281 | | - { |
282 | | - $def = $table->getDefinitionOf($field); |
283 | | - |
284 | | - if ($def['type'] == 'string' && $def['length'] === NULL) { |
285 | | - $term = 'CAST(' . $term . ' AS varchar(8000))'; |
286 | | - } |
287 | | - |
288 | | - return $term; |
| 234 | + return $query; |
289 | 235 | } |
290 | 236 |
|
291 | 237 | /** |
@@ -401,12 +347,18 @@ public function exec($query, array $params = array()) |
401 | 347 | protected function replaceBoundParamsWithInlineValuesInQuery($query, array $params) { |
402 | 348 |
|
403 | 349 | foreach($params as $key => $value) { |
404 | | - $re = '/(?<=WHERE|VALUES|SET|JOIN)(.*?)(\?)/'; |
405 | | - $query = preg_replace($re, "\\1##{$key}##", $query, 1); |
| 350 | + if(is_null($value)) { |
| 351 | + $value = 'NULL'; |
| 352 | + } |
| 353 | + else { |
| 354 | + $value = $this->quote($value); |
| 355 | + } |
| 356 | + |
| 357 | + $re = '/([=,\(][^\\\']*)(\?)/iU'; |
| 358 | + |
| 359 | + $query = preg_replace($re, "\\1 {$value}", $query, 1); |
| 360 | + |
406 | 361 | } |
407 | | - |
408 | | - $replacement = 'is_null($value) ? \'NULL\' : $this->quote($params[\\1])'; |
409 | | - $query = preg_replace('/##(\d+)##/e', $replacement, $query); |
410 | 362 |
|
411 | 363 | return $query; |
412 | 364 |
|
|
0 commit comments