Commit a650fdbd authored by Bill Schaller's avatar Bill Schaller

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

parent 4f255183
......@@ -1183,7 +1183,7 @@ class SQLServerPlatform extends AbstractPlatform
$start = $offset + 1;
$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
// allow the SQL Server query planner to optimize it so it doesn't
// actually scan the entire range covered by the TOP clause.
......@@ -1193,7 +1193,7 @@ class SQLServerPlatform extends AbstractPlatform
if (stristr($query, "ORDER BY")) {
// 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);
}
......@@ -1212,19 +1212,21 @@ class SQLServerPlatform extends AbstractPlatform
/**
* Remove ORDER BY clauses in subqueries - they're not supported by SQL Server.
* Caveat: will leave ORDER BY in TOP N subqueries.
*
* @param $query
* @return string
*
* @todo allow ORDER BY clauses in subqueries that have TOP n, as that is ok.
*/
private function scrubInnerOrderBy($query) {
$count = substr_count(strtoupper($query), "ORDER BY");
$offset = 0;
while ($count-- > 0) {
$qLen = strlen($query);
$orderByPos = stripos($query, " ORDER BY");
$orderByPos = stripos($query, " ORDER BY", $offset);
$parenCount = 0;
$currentPosition = $orderByPos;
while ($parenCount >= 0 && $currentPosition < $qLen) {
if ($query[$currentPosition] == '(') {
$parenCount++;
......@@ -1234,13 +1236,61 @@ class SQLServerPlatform extends AbstractPlatform
$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) {
$query = substr($query, 0, $orderByPos) . substr($query, $currentPosition - 1);
$offset = $orderByPos;
}
}
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}
*/
......
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