diff --git a/src/Query/SqlWalker.php b/src/Query/SqlWalker.php index a943deb050f..d53da61845c 100644 --- a/src/Query/SqlWalker.php +++ b/src/Query/SqlWalker.php @@ -55,6 +55,11 @@ class SqlWalker */ public const HINT_PARTIAL = 'doctrine.partial'; + /** + * Used to prevent nested ORDER BY statements which cause an exception in SQL Server + */ + public const HINT_DISABLE_COLLECTION_ORDER_BY = 'doctrine.disableCollectionOrderBy'; + private readonly ResultSetMapping $rsm; /** @@ -511,9 +516,11 @@ protected function createSqlForFinalizer(AST\SelectStatement $selectStatement): $sql .= $this->walkOrderByClause($selectStatement->orderByClause); } - $orderBySql = $this->generateOrderedCollectionOrderByItems(); - if (! $selectStatement->orderByClause && $orderBySql) { - $sql .= ' ORDER BY ' . $orderBySql; + if (! $this->query->getHint(self::HINT_DISABLE_COLLECTION_ORDER_BY)) { + $orderBySql = $this->generateOrderedCollectionOrderByItems(); + if (!$selectStatement->orderByClause && $orderBySql) { + $sql .= ' ORDER BY ' . $orderBySql; + } } $this->assertOptimisticLockingHasAllClassesVersioned(); diff --git a/src/Tools/Pagination/CountOutputWalker.php b/src/Tools/Pagination/CountOutputWalker.php index b42123907f4..f1289b41afc 100644 --- a/src/Tools/Pagination/CountOutputWalker.php +++ b/src/Tools/Pagination/CountOutputWalker.php @@ -12,6 +12,7 @@ use Doctrine\ORM\Query\ParserResult; use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\ORM\Query\SqlOutputWalker; +use Doctrine\ORM\Query\SqlWalker; use RuntimeException; use function array_diff; @@ -56,6 +57,10 @@ public function __construct(Query $query, ParserResult $parserResult, array $que protected function createSqlForFinalizer(SelectStatement $selectStatement): string { if ($this->platform instanceof SQLServerPlatform) { + // disable collection‑based ORDER BY for the inner select + $query = $this->getQuery(); + $query->setHint(SqlWalker::HINT_DISABLE_COLLECTION_ORDER_BY, true); + // preserve original clearing also that is only partially sufficient $selectStatement->orderByClause = null; } diff --git a/src/Tools/Pagination/LimitSubqueryOutputWalker.php b/src/Tools/Pagination/LimitSubqueryOutputWalker.php index a7450257d32..05e97bcf76c 100644 --- a/src/Tools/Pagination/LimitSubqueryOutputWalker.php +++ b/src/Tools/Pagination/LimitSubqueryOutputWalker.php @@ -26,6 +26,7 @@ use Doctrine\ORM\Query\QueryException; use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\ORM\Query\SqlOutputWalker; +use Doctrine\ORM\Query\SqlWalker; use LogicException; use RuntimeException; @@ -103,6 +104,11 @@ public function __construct( $this->maxResults = $cloneQuery->getMaxResults(); $cloneQuery->setFirstResult(0)->setMaxResults(null); + if ($this->platform instanceof SQLServerPlatform) { + // disable collection‑based ORDER BY for the inner select + $cloneQuery->setHint(SqlWalker::HINT_DISABLE_COLLECTION_ORDER_BY, true); + } + $this->em = $cloneQuery->getEntityManager(); $this->quoteStrategy = $this->em->getConfiguration()->getQuoteStrategy(); diff --git a/src/Tools/Pagination/Paginator.php b/src/Tools/Pagination/Paginator.php index d74ab6c63f8..310b0b2d603 100644 --- a/src/Tools/Pagination/Paginator.php +++ b/src/Tools/Pagination/Paginator.php @@ -9,10 +9,12 @@ use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Internal\SQLResultCasing; use Doctrine\ORM\NoResultException; +use Doctrine\DBAL\Platforms\SQLServerPlatform; use Doctrine\ORM\Query; use Doctrine\ORM\Query\Parameter; use Doctrine\ORM\Query\Parser; use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\ORM\Query\SqlWalker; use Doctrine\ORM\QueryBuilder; use IteratorAggregate; use Traversable; @@ -122,6 +124,12 @@ public function getIterator(): Traversable $this->unbindUnusedQueryParams($subQuery); } + // disable collection ordering for SQL Server + $platform = $subQuery->getEntityManager()->getConnection()->getDatabasePlatform(); + if ($platform instanceof SQLServerPlatform) { + $subQuery->setHint(SqlWalker::HINT_DISABLE_COLLECTION_ORDER_BY, true); + } + $subQuery->setFirstResult($offset)->setMaxResults($length); $foundIdRows = $subQuery->getScalarResult();