Commit ba4f1555 authored by Bill Schaller's avatar Bill Schaller Committed by Steve Müller

Resolved @todo to allow ORDER BY in TOP N subquery with SQL Server 2008 doModifyLimitQuery

parent ccc2287e
...@@ -1183,7 +1183,7 @@ class SQLServerPlatform extends AbstractPlatform ...@@ -1183,7 +1183,7 @@ class SQLServerPlatform extends AbstractPlatform
$start = $offset + 1; $start = $offset + 1;
$end = $offset + $limit; $end = $offset + $limit;
// We'll find a SELECT or SELECT distinct and append TOP n to it // We'll find a SELECT or SELECT distinct and prepend TOP n to it
// Even if the TOP n is very large, the use of a CTE will // Even if the TOP n is very large, the use of a CTE will
// allow the SQL Server query planner to optimize it so it doesn't // allow the SQL Server query planner to optimize it so it doesn't
// actually scan the entire range covered by the TOP clause. // actually scan the entire range covered by the TOP clause.
...@@ -1193,7 +1193,7 @@ class SQLServerPlatform extends AbstractPlatform ...@@ -1193,7 +1193,7 @@ class SQLServerPlatform extends AbstractPlatform
if (stristr($query, "ORDER BY")) { if (stristr($query, "ORDER BY")) {
// Inner order by is not valid in SQL Server for our purposes // Inner order by is not valid in SQL Server for our purposes
// TODO throw DBALException here? // unless it's in a TOP N subquery.
$query = $this->scrubInnerOrderBy($query); $query = $this->scrubInnerOrderBy($query);
} }
...@@ -1212,19 +1212,21 @@ class SQLServerPlatform extends AbstractPlatform ...@@ -1212,19 +1212,21 @@ class SQLServerPlatform extends AbstractPlatform
/** /**
* Remove ORDER BY clauses in subqueries - they're not supported by SQL Server. * Remove ORDER BY clauses in subqueries - they're not supported by SQL Server.
* Caveat: will leave ORDER BY in TOP N subqueries.
* *
* @param $query * @param $query
* @return string * @return string
* *
* @todo allow ORDER BY clauses in subqueries that have TOP n, as that is ok.
*/ */
private function scrubInnerOrderBy($query) { private function scrubInnerOrderBy($query) {
$count = substr_count(strtoupper($query), "ORDER BY"); $count = substr_count(strtoupper($query), "ORDER BY");
$offset = 0;
while ($count-- > 0) { while ($count-- > 0) {
$qLen = strlen($query); $qLen = strlen($query);
$orderByPos = stripos($query, " ORDER BY"); $orderByPos = stripos($query, " ORDER BY", $offset);
$parenCount = 0; $parenCount = 0;
$currentPosition = $orderByPos; $currentPosition = $orderByPos;
while ($parenCount >= 0 && $currentPosition < $qLen) { while ($parenCount >= 0 && $currentPosition < $qLen) {
if ($query[$currentPosition] == '(') { if ($query[$currentPosition] == '(') {
$parenCount++; $parenCount++;
...@@ -1234,13 +1236,61 @@ class SQLServerPlatform extends AbstractPlatform ...@@ -1234,13 +1236,61 @@ class SQLServerPlatform extends AbstractPlatform
$currentPosition++; $currentPosition++;
} }
if ($this->isOrderByInTopNSubquery($query, $orderByPos)) {
// If the order by clause is in a TOP N subquery, do not remove
// it and continue iteration from the current position.
$offset = $currentPosition;
continue;
}
if ($currentPosition < $qLen - 1) { if ($currentPosition < $qLen - 1) {
$query = substr($query, 0, $orderByPos) . substr($query, $currentPosition - 1); $query = substr($query, 0, $orderByPos) . substr($query, $currentPosition - 1);
$offset = $orderByPos;
} }
} }
return $query; return $query;
} }
/**
* Check an ORDER BY clause to see if it is in a TOP N query or subquery.
*
* @param string $query The query
* @param int $currentPosition Start position of ORDER BY clause
* @return bool true if ORDER BY is in a TOP N query, false otherwise
*/
private function isOrderByInTopNSubquery($query, $currentPosition)
{
// Grab query text on the same nesting level as the ORDER BY clause we're examining.
$subQueryBuffer = '';
$parenCount = 0;
// If $parentCount goes negative, we've exited the subquery we're examining.
// If $currentPosition goes negative, we've reached the beginning of the query.
while ($parenCount >= 0 && $currentPosition >= 0) {
if ($query[$currentPosition] == '(') {
$parenCount--;
}
if ($query[$currentPosition] == ')') {
$parenCount++;
}
if ($parenCount === 0) {
// Only yank query text on the same nesting level as the ORDER BY clause.
$subQueryBuffer = $query[$currentPosition] . $subQueryBuffer;
} else {
$subQueryBuffer = " " . $subQueryBuffer;
}
$currentPosition--;
}
if (preg_match('/SELECT\s+TOP\s/', $subQueryBuffer)) {
return true;
}
return false;
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment