/** * Walks down a SelectStatement AST node, wrapping it in a COUNT (SELECT DISTINCT) * * Note that the ORDER BY clause is not removed. Many SQL implementations (e.g. MySQL) * are able to cache subqueries. By keeping the ORDER BY clause intact, the limitSubQuery * that will most likely be executed next can be read from the native SQL cache. * * @param SelectStatement $AST * @return string */ public function walkSelectStatement(SelectStatement $AST) { $sql = parent::walkSelectStatement($AST); // Find out the SQL alias of the identifier column of the root entity // It may be possible to make this work with multiple root entities but that // would probably require issuing multiple queries or doing a UNION SELECT // so for now, It's not supported. // Get the root entity and alias from the AST fromClause $from = $AST->fromClause->identificationVariableDeclarations; if (count($from) > 1) { throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction"); } $rootClass = $from[0]->rangeVariableDeclaration->abstractSchemaName; $rootAlias = $from[0]->rangeVariableDeclaration->aliasIdentificationVariable; // Get the identity properties from the metadata $rootIdentifier = $this->queryComponents[$rootAlias]['metadata']->identifier; // For every identifier, find out the SQL alias by combing through the ResultSetMapping $sqlIdentifier = array(); foreach ($rootIdentifier as $property) { foreach (array_keys($this->rsm->fieldMappings, $property) as $alias) { if ($this->rsm->columnOwnerMap[$alias] == $rootAlias) { $sqlIdentifier[$property] = $alias; } } } if (count($rootIdentifier) != count($sqlIdentifier)) { throw new \RuntimeException(sprintf('Not all identifier properties can be found in the ResultSetMapping: %s', implode(', ', array_diff($rootIdentifier, array_keys($sqlIdentifier))))); } // Build the counter query return sprintf('SELECT %s AS _dctrn_count FROM (SELECT DISTINCT %s FROM (%s) AS _dctrn_result) AS _dctrn_table', $this->platform->getCountExpression('*'), implode(', ', $sqlIdentifier), $sql); }