Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Adding new limit/offset pagination for SQLServer 11.

  • Loading branch information...
commit e33a9a78469014705039c13b487da8757c3985d7 1 parent 0e09f75
@markstory markstory authored
View
29 lib/Cake/Model/Datasource/Database/Sqlserver.php
@@ -116,6 +116,8 @@ class Sqlserver extends DboSource {
const ROW_COUNTER = '_cake_page_rownum_';
+ protected $_version;
+
/**
* Connects to the database using options in the given configuration array.
*
@@ -140,7 +142,7 @@ public function connect() {
throw new MissingConnectionException(array('class' => $e->getMessage()));
}
-// $this->_execute("SET DATEFORMAT ymd");
+ $this->_version = $this->_connection->getAttribute(PDO::ATTR_SERVER_VERSION);
return $this->connected;
}
@@ -375,7 +377,7 @@ public function limit($limit, $offset = null) {
}
$rt .= ' ' . $limit;
if (is_int($offset) && $offset > 0) {
- $rt .= ' OFFSET ' . $offset;
+ $rt = ' OFFSET ' . intval($offset) . ' ROWS FETCH FIRST ' . intval($limit) . ' ROWS ONLY';
}
return $rt;
}
@@ -495,24 +497,33 @@ public function renderStatement($type, $data) {
$fields = substr($fields, 9);
}
- if (preg_match('/offset\s+([0-9]+)/i', $limit, $offset)) {
- $limit = preg_replace('/\s*offset.*$/i', '', $limit);
- preg_match('/top\s+([0-9]+)/i', $limit, $limitVal);
- $offset = intval($offset[1]) + intval($limitVal[1]);
+ // hack order as SQLServer requires an order if there is a limit.
+ if ($limit && !$order) {
+ $order = 'ORDER BY (SELECT NULL)';
+ }
+
+ // For older versions use the subquery version of pagination.
+ if (version_compare($this->_version, '11', '<') && preg_match('/FETCH\sFIRST\s+([0-9]+)/i', $limit, $offset)) {
+ preg_match('/OFFSET\s*(\d+)\s*.*?(\d+)\s*ROWS/', $limit, $limitOffset);
+
+ $limit = 'TOP ' . intval($limitOffset[2]);
+ $page = intval($limitOffset[1] / $limitOffset[2]);
+ $offset = intval($limitOffset[2] * $page);
if (!$order) {
$order = 'ORDER BY (SELECT NULL)';
@lorenzo Owner
lorenzo added a note

I think this line can be removed

@markstory Owner

I think that ticket is for MySQL, SQLServer actually requires an ORDER BY for pagination to work. Without an order by the SQLServer will have SQL errors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
}
$rowCounter = self::ROW_COUNTER;
- $pagination = "
+ return "
SELECT {$limit} * FROM (
SELECT {$fields}, ROW_NUMBER() OVER ({$order}) AS {$rowCounter}
FROM {$table} {$alias} {$joins} {$conditions} {$group}
) AS _cake_paging_
- WHERE _cake_paging_.{$rowCounter} >= {$offset}
+ WHERE _cake_paging_.{$rowCounter} > {$offset}
ORDER BY _cake_paging_.{$rowCounter}
";
- return $pagination;
+ } elseif (strpos($limit, 'FETCH') !== false) {
+ return "SELECT {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$group} {$order} {$limit}";
} else {
return "SELECT {$limit} {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$group} {$order}";
}
View
32 lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php
@@ -249,7 +249,7 @@ class SqlserverTest extends CakeTestCase {
*
* @var array
*/
- public $fixtures = array('core.category', 'core.author', 'core.post');
+ public $fixtures = array('core.user', 'core.category', 'core.author', 'core.post');
/**
* Sets up a Dbo class instance for testing
@@ -617,27 +617,31 @@ public function testInsertMulti() {
* @return void
*/
public function testLimitOffsetHack() {
- $this->loadFixtures('Author', 'Post');
+ $this->loadFixtures('Author', 'Post', 'User');
$query = array(
- 'limit' => 1,
+ 'limit' => 2,
'page' => 1,
- 'order' => 'Post.title ASC',
+ 'order' => 'User.user ASC',
);
- $Post = ClassRegistry::init('Post');
- $results = $Post->find('all', $query);
+ $User = ClassRegistry::init('User');
+ $results = $User->find('all', $query);
- $this->assertEquals(1, count($results));
- $this->assertEquals('First Post', $results[0]['Post']['title']);
+ $this->assertEquals(2, count($results));
+ $this->assertEquals('garrett', $results[0]['User']['user']);
+ $this->assertEquals('larry', $results[1]['User']['user']);
$query = array(
- 'limit' => 1,
+ 'limit' => 2,
'page' => 2,
- 'order' => 'Post.title ASC',
+ 'order' => 'User.user ASC',
);
- $Post = ClassRegistry::init('Post');
- $results = $Post->find('all', $query);
- $this->assertEquals(1, count($results));
+ $User = ClassRegistry::init('User');
+ $results = $User->find('all', $query);
+
+ $this->assertEquals(2, count($results));
$this->assertFalse(isset($results[0][0]));
- $this->assertEquals('Second Post', $results[0]['Post']['title']);
+ $this->assertEquals('mariano', $results[0]['User']['user']);
+ $this->assertEquals('nate', $results[1]['User']['user']);
}
+
}
@lorenzo

I think this line can be removed

@markstory

I think that ticket is for MySQL, SQLServer actually requires an ORDER BY for pagination to work. Without an order by the SQLServer will have SQL errors.

Please sign in to comment.
Something went wrong with that request. Please try again.