Fixed handling OFFSET without LIMIT on SQL Server < 2008

parent 193fa286
...@@ -28,6 +28,8 @@ use Doctrine\DBAL\Schema\Index; ...@@ -28,6 +28,8 @@ use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Schema\TableDiff; use Doctrine\DBAL\Schema\TableDiff;
use Doctrine\DBAL\Types; use Doctrine\DBAL\Types;
use function implode;
use function sprintf;
/** /**
* The SQLServerPlatform provides the behavior, features and SQL dialect of the * The SQLServerPlatform provides the behavior, features and SQL dialect of the
...@@ -1207,19 +1209,29 @@ class SQLServerPlatform extends AbstractPlatform ...@@ -1207,19 +1209,29 @@ class SQLServerPlatform extends AbstractPlatform
*/ */
protected function doModifyLimitQuery($query, $limit, $offset = null) protected function doModifyLimitQuery($query, $limit, $offset = null)
{ {
if ($limit === null) { $where = [];
return $query;
if ($offset > 0) {
$where[] = sprintf('doctrine_rownum >= %d', $offset + 1);
} }
$start = $offset + 1; if ($limit !== null) {
$end = $offset + $limit; $where[] = sprintf('doctrine_rownum <= %d', $offset + $limit);
$top = sprintf('TOP %d', $offset + $limit);
} else {
$top = 'TOP 9223372036854775807';
}
if (empty($where)) {
return $query;
}
// We'll find a SELECT or SELECT distinct and prepend 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.
$selectPattern = '/^(\s*SELECT\s+(?:DISTINCT\s+)?)(.*)$/im'; $selectPattern = '/^(\s*SELECT\s+(?:DISTINCT\s+)?)(.*)$/im';
$replacePattern = sprintf('$1%s $2', "TOP $end"); $replacePattern = sprintf('$1%s $2', $top);
$query = preg_replace($selectPattern, $replacePattern, $query); $query = preg_replace($selectPattern, $replacePattern, $query);
if (stristr($query, "ORDER BY")) { if (stristr($query, "ORDER BY")) {
...@@ -1234,10 +1246,9 @@ class SQLServerPlatform extends AbstractPlatform ...@@ -1234,10 +1246,9 @@ class SQLServerPlatform extends AbstractPlatform
. "SELECT * FROM (" . "SELECT * FROM ("
. "SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS doctrine_rownum FROM dctrn_cte" . "SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS doctrine_rownum FROM dctrn_cte"
. ") AS doctrine_tbl " . ") AS doctrine_tbl "
. "WHERE doctrine_rownum BETWEEN %d AND %d ORDER BY doctrine_rownum ASC", . 'WHERE %s ORDER BY doctrine_rownum ASC',
$query, $query,
$start, implode(' AND ', $where)
$end
); );
} }
......
...@@ -49,12 +49,22 @@ class SQLServerPlatformTest extends AbstractSQLServerPlatformTestCase ...@@ -49,12 +49,22 @@ class SQLServerPlatformTest extends AbstractSQLServerPlatformTestCase
public function getModifyLimitQueries() public function getModifyLimitQueries()
{ {
return array( return [
// Test re-ordered query with correctly-scrubbed ORDER BY clause // Test re-ordered query with correctly-scrubbed ORDER BY clause
array('SELECT id_0, MIN(sclr_2) AS dctrn_minrownum FROM (SELECT c0_.id AS id_0, c0_.title AS title_1, ROW_NUMBER() OVER(ORDER BY c0_.title ASC) AS sclr_2 FROM TestTable c0_ ORDER BY c0_.title ASC) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC', 30, null, 'WITH dctrn_cte AS (SELECT TOP 30 id_0, MIN(sclr_2) AS dctrn_minrownum FROM (SELECT c0_.id AS id_0, c0_.title AS title_1, ROW_NUMBER() OVER(ORDER BY c0_.title ASC) AS sclr_2 FROM TestTable c0_) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC) SELECT * FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS doctrine_rownum FROM dctrn_cte) AS doctrine_tbl WHERE doctrine_rownum BETWEEN 1 AND 30 ORDER BY doctrine_rownum ASC'), [
'SELECT id_0, MIN(sclr_2) AS dctrn_minrownum FROM (SELECT c0_.id AS id_0, c0_.title AS title_1, ROW_NUMBER() OVER(ORDER BY c0_.title ASC) AS sclr_2 FROM TestTable c0_ ORDER BY c0_.title ASC) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC',
30,
null,
'WITH dctrn_cte AS (SELECT TOP 30 id_0, MIN(sclr_2) AS dctrn_minrownum FROM (SELECT c0_.id AS id_0, c0_.title AS title_1, ROW_NUMBER() OVER(ORDER BY c0_.title ASC) AS sclr_2 FROM TestTable c0_) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC) SELECT * FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS doctrine_rownum FROM dctrn_cte) AS doctrine_tbl WHERE doctrine_rownum <= 30 ORDER BY doctrine_rownum ASC',
],
// Test re-ordered query with no scrubbed ORDER BY clause // Test re-ordered query with no scrubbed ORDER BY clause
array('SELECT id_0, MIN(sclr_2) AS dctrn_minrownum FROM (SELECT c0_.id AS id_0, c0_.title AS title_1, ROW_NUMBER() OVER(ORDER BY c0_.title ASC) AS sclr_2 FROM TestTable c0_) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC', 30, null, 'WITH dctrn_cte AS (SELECT TOP 30 id_0, MIN(sclr_2) AS dctrn_minrownum FROM (SELECT c0_.id AS id_0, c0_.title AS title_1, ROW_NUMBER() OVER(ORDER BY c0_.title ASC) AS sclr_2 FROM TestTable c0_) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC) SELECT * FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS doctrine_rownum FROM dctrn_cte) AS doctrine_tbl WHERE doctrine_rownum BETWEEN 1 AND 30 ORDER BY doctrine_rownum ASC'), [
); 'SELECT id_0, MIN(sclr_2) AS dctrn_minrownum FROM (SELECT c0_.id AS id_0, c0_.title AS title_1, ROW_NUMBER() OVER(ORDER BY c0_.title ASC) AS sclr_2 FROM TestTable c0_) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC',
30,
null,
'WITH dctrn_cte AS (SELECT TOP 30 id_0, MIN(sclr_2) AS dctrn_minrownum FROM (SELECT c0_.id AS id_0, c0_.title AS title_1, ROW_NUMBER() OVER(ORDER BY c0_.title ASC) AS sclr_2 FROM TestTable c0_) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC) SELECT * FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS doctrine_rownum FROM dctrn_cte) AS doctrine_tbl WHERE doctrine_rownum <= 30 ORDER BY doctrine_rownum ASC',
],
];
} }
} }
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