Skip to content
Browse files

Fix pagination ordering on calculated columns on SQL Server.

On older versions of SQL Server, the pagination is accomplished using a
ROW_NUMBER() OVER clause.  Unfortunately, OVER does not support the use of
column aliases.  But for calculated columns the only practical way to
specify their use in ordering is with their alias, this currently causes a
SQL error.

This fix will substitute the calculation's SQL syntax in place of the
column alias before generating the OVER clause for calculated columns,
fixing the bug.
  • Loading branch information...
Mike Fellows Mike Fellows
Mike Fellows authored and Mike Fellows committed Jul 25, 2017
1 parent d254ead commit 2759482cbda4f840c88d6d8c15137435eb992b0f
Showing with 27 additions and 1 deletion.
  1. +27 −1 src/Database/Dialect/SqlserverDialectTrait.php
@@ -14,13 +14,15 @@
namespace Cake\Database\Dialect;
use Cake\Database\ExpressionInterface;
use Cake\Database\Expression\FunctionExpression;
use Cake\Database\Expression\OrderByExpression;
use Cake\Database\Expression\UnaryExpression;
use Cake\Database\Query;
use Cake\Database\Schema\SqlserverSchema;
use Cake\Database\SqlDialectTrait;
use Cake\Database\SqlserverCompiler;
use Cake\Database\ValueBinder;
use PDO;
@@ -102,7 +104,31 @@ public function _version()
protected function _pagingSubquery($original, $limit, $offset)
$field = '_cake_paging_._cake_page_rownum_';
$order = $original->clause('order') ?: new OrderByExpression('(SELECT NULL)');
if ($original->clause('order')) {
$order = clone $original->clause('order');
} else {
$order = new OrderByExpression('(SELECT NULL)');
// SQL server does not support column aliases in OVER clauses. But for
// calculated columns the alias is the only practical identifier to use
// when specifying the order. So if a column alias is specified in the
// order clause, and the value of that alias is an expression, change
// the alias into what it represents by setting the clause's key to be
// the SQL representation of its value. The UnaryExpression creation
// below will then do the right thing and use the calculation in the
// ROW_NUMBER() OVER clause instead of the alias.
$select = $original->clause('select');
$order->iterateParts(function ($direction, &$orderBy) use ($select) {
if (isset($select[$orderBy])) {
if ($select[$orderBy] instanceof ExpressionInterface) {
$orderBy = $select[$orderBy]->sql(new ValueBinder());
return $direction;
$query = clone $original;

0 comments on commit 2759482

Please sign in to comment.
You can’t perform that action at this time.