3 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15 * This software consists of voluntary contributions made by many individuals
16 * and is licensed under the MIT license. For more information, see
17 * <http://www.doctrine-project.org>.
20 namespace Doctrine\ORM\Query
;
22 use Doctrine\DBAL\LockMode
;
23 use Doctrine\DBAL\Types\Type
;
24 use Doctrine\ORM\Mapping\ClassMetadata
;
25 use Doctrine\ORM\Query
;
26 use Doctrine\ORM\OptimisticLockException
;
27 use Doctrine\ORM\Mapping\ClassMetadataInfo
;
30 * The SqlWalker is a TreeWalker that walks over a DQL AST and constructs
31 * the corresponding SQL.
33 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
34 * @author Roman Borschel <roman@code-factory.org>
35 * @author Benjamin Eberlei <kontakt@beberlei.de>
36 * @author Alexander <iam.asm89@gmail.com>
37 * @author Fabio B. Silva <fabio.bat.silva@gmail.com>
39 * @todo Rename: SQLWalker
41 class SqlWalker
implements TreeWalker
46 const HINT_DISTINCT
= 'doctrine.distinct';
49 * @var ResultSetMapping
54 * Counter for generating unique column aliases.
58 private $aliasCounter = 0;
61 * Counter for generating unique table aliases.
65 private $tableAliasCounter = 0;
68 * Counter for generating unique scalar result.
72 private $scalarResultCounter = 1;
75 * Counter for generating unique parameter indexes.
79 private $sqlParamIndex = 0;
82 * Counter for generating indexes.
86 private $newObjectCounter = 0;
91 private $parserResult;
94 * @var \Doctrine\ORM\EntityManager
99 * @var \Doctrine\DBAL\Connection
104 * @var \Doctrine\ORM\AbstractQuery
111 private $tableAliasMap = array();
114 * Map from result variable names to their SQL column alias names.
118 private $scalarResultAliasMap = array();
121 * Map from Table-Alias + Column-Name to OrderBy-Direction.
125 private $orderedColumnsMap = array();
128 * Map from DQL-Alias + Field-Name to SQL Column Alias.
132 private $scalarFields = array();
135 * Map of all components/classes that appear in the DQL query.
139 private $queryComponents;
142 * A list of classes that appear in non-scalar SelectExpressions.
146 private $selectedClasses = array();
149 * The DQL alias of the root class of the currently traversed query.
153 private $rootAliases = array();
156 * Flag that indicates whether to generate SQL table aliases in the SQL.
157 * These should only be generated for SELECT queries, not for UPDATE/DELETE.
161 private $useSqlTableAliases = true;
164 * The database platform abstraction.
166 * @var \Doctrine\DBAL\Platforms\AbstractPlatform
171 * The quote strategy.
173 * @var \Doctrine\ORM\Mapping\QuoteStrategy
175 private $quoteStrategy;
180 public function __construct($query, $parserResult, array $queryComponents)
182 $this->query
= $query;
183 $this->parserResult
= $parserResult;
184 $this->queryComponents
= $queryComponents;
185 $this->rsm
= $parserResult->getResultSetMapping();
186 $this->em
= $query->getEntityManager();
187 $this->conn
= $this->em
->getConnection();
188 $this->platform
= $this->conn
->getDatabasePlatform();
189 $this->quoteStrategy
= $this->em
->getConfiguration()->getQuoteStrategy();
193 * Gets the Query instance used by the walker.
197 public function getQuery()
203 * Gets the Connection used by the walker.
205 * @return \Doctrine\DBAL\Connection
207 public function getConnection()
213 * Gets the EntityManager used by the walker.
215 * @return \Doctrine\ORM\EntityManager
217 public function getEntityManager()
223 * Gets the information about a single query component.
225 * @param string $dqlAlias The DQL alias.
229 public function getQueryComponent($dqlAlias)
231 return $this->queryComponents
[$dqlAlias];
237 public function getQueryComponents()
239 return $this->queryComponents
;
245 public function setQueryComponent($dqlAlias, array $queryComponent)
247 $requiredKeys = array('metadata', 'parent', 'relation', 'map', 'nestingLevel', 'token');
249 if (array_diff($requiredKeys, array_keys($queryComponent))) {
250 throw QueryException
::invalidQueryComponent($dqlAlias);
253 $this->queryComponents
[$dqlAlias] = $queryComponent;
259 public function getExecutor($AST)
262 case ($AST instanceof AST\DeleteStatement
):
263 $primaryClass = $this->em
->getClassMetadata($AST->deleteClause
->abstractSchemaName
);
265 return ($primaryClass->isInheritanceTypeJoined())
266 ?
new Exec\
MultiTableDeleteExecutor($AST, $this)
267 : new Exec\
SingleTableDeleteUpdateExecutor($AST, $this);
269 case ($AST instanceof AST\UpdateStatement
):
270 $primaryClass = $this->em
->getClassMetadata($AST->updateClause
->abstractSchemaName
);
272 return ($primaryClass->isInheritanceTypeJoined())
273 ?
new Exec\
MultiTableUpdateExecutor($AST, $this)
274 : new Exec\
SingleTableDeleteUpdateExecutor($AST, $this);
277 return new Exec\
SingleSelectExecutor($AST, $this);
282 * Generates a unique, short SQL table alias.
284 * @param string $tableName Table name
285 * @param string $dqlAlias The DQL alias.
287 * @return string Generated table alias.
289 public function getSQLTableAlias($tableName, $dqlAlias = '')
291 $tableName .= ($dqlAlias) ?
'@[' . $dqlAlias . ']' : '';
293 if ( ! isset($this->tableAliasMap
[$tableName])) {
294 $this->tableAliasMap
[$tableName] = (preg_match('/[a-z]/i', $tableName[0]) ?
strtolower($tableName[0]) : 't')
295 . $this->tableAliasCounter++
. '_';
298 return $this->tableAliasMap
[$tableName];
302 * Forces the SqlWalker to use a specific alias for a table name, rather than
303 * generating an alias on its own.
305 * @param string $tableName
306 * @param string $alias
307 * @param string $dqlAlias
311 public function setSQLTableAlias($tableName, $alias, $dqlAlias = '')
313 $tableName .= ($dqlAlias) ?
'@[' . $dqlAlias . ']' : '';
315 $this->tableAliasMap
[$tableName] = $alias;
321 * Gets an SQL column alias for a column name.
323 * @param string $columnName
327 public function getSQLColumnAlias($columnName)
329 return $this->quoteStrategy
->getColumnAlias($columnName, $this->aliasCounter++
, $this->platform
);
333 * Generates the SQL JOINs that are necessary for Class Table Inheritance
334 * for the given class.
336 * @param ClassMetadata $class The class for which to generate the joins.
337 * @param string $dqlAlias The DQL alias of the class.
339 * @return string The SQL.
341 private function _generateClassTableInheritanceJoins($class, $dqlAlias)
345 $baseTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
347 // INNER JOIN parent class tables
348 foreach ($class->parentClasses
as $parentClassName) {
349 $parentClass = $this->em
->getClassMetadata($parentClassName);
350 $tableAlias = $this->getSQLTableAlias($parentClass->getTableName(), $dqlAlias);
352 // If this is a joined association we must use left joins to preserve the correct result.
353 $sql .= isset($this->queryComponents
[$dqlAlias]['relation']) ?
' LEFT ' : ' INNER ';
354 $sql .= 'JOIN ' . $this->quoteStrategy
->getTableName($parentClass, $this->platform
) . ' ' . $tableAlias . ' ON ';
358 foreach ($this->quoteStrategy
->getIdentifierColumnNames($class, $this->platform
) as $columnName) {
359 $sqlParts[] = $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
362 // Add filters on the root class
363 if ($filterSql = $this->generateFilterConditionSQL($parentClass, $tableAlias)) {
364 $sqlParts[] = $filterSql;
367 $sql .= implode(' AND ', $sqlParts);
370 // Ignore subclassing inclusion if partial objects is disallowed
371 if ($this->query
->getHint(Query
::HINT_FORCE_PARTIAL_LOAD
)) {
375 // LEFT JOIN child class tables
376 foreach ($class->subClasses
as $subClassName) {
377 $subClass = $this->em
->getClassMetadata($subClassName);
378 $tableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
380 $sql .= ' LEFT JOIN ' . $this->quoteStrategy
->getTableName($subClass, $this->platform
) . ' ' . $tableAlias . ' ON ';
384 foreach ($this->quoteStrategy
->getIdentifierColumnNames($subClass, $this->platform
) as $columnName) {
385 $sqlParts[] = $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
388 $sql .= implode(' AND ', $sqlParts);
397 private function _generateOrderedCollectionOrderByItems()
399 $orderedColumns = array();
401 foreach ($this->selectedClasses
as $selectedClass) {
402 $dqlAlias = $selectedClass['dqlAlias'];
403 $qComp = $this->queryComponents
[$dqlAlias];
405 if ( ! isset($qComp['relation']['orderBy'])) {
409 $persister = $this->em
->getUnitOfWork()->getEntityPersister($qComp['metadata']->name
);
411 foreach ($qComp['relation']['orderBy'] as $fieldName => $orientation) {
412 $columnName = $this->quoteStrategy
->getColumnName($fieldName, $qComp['metadata'], $this->platform
);
413 $tableName = ($qComp['metadata']->isInheritanceTypeJoined())
414 ?
$persister->getOwningTable($fieldName)
415 : $qComp['metadata']->getTableName();
417 $orderedColumn = $this->getSQLTableAlias($tableName, $dqlAlias) . '.' . $columnName;
419 // OrderByClause should replace an ordered relation. see - DDC-2475
420 if (isset($this->orderedColumnsMap
[$orderedColumn])) {
424 $this->orderedColumnsMap
[$orderedColumn] = $orientation;
425 $orderedColumns[] = $orderedColumn . ' ' . $orientation;
429 return implode(', ', $orderedColumns);
433 * Generates a discriminator column SQL condition for the class with the given DQL alias.
435 * @param array $dqlAliases List of root DQL aliases to inspect for discriminator restrictions.
439 private function _generateDiscriminatorColumnConditionSQL(array $dqlAliases)
443 foreach ($dqlAliases as $dqlAlias) {
444 $class = $this->queryComponents
[$dqlAlias]['metadata'];
446 if ( ! $class->isInheritanceTypeSingleTable()) continue;
448 $conn = $this->em
->getConnection();
451 if ($class->discriminatorValue
!== null) { // discriminators can be 0
452 $values[] = $conn->quote($class->discriminatorValue
);
455 foreach ($class->subClasses
as $subclassName) {
456 $values[] = $conn->quote($this->em
->getClassMetadata($subclassName)->discriminatorValue
);
459 $sqlParts[] = (($this->useSqlTableAliases
) ?
$this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.' : '')
460 . $class->discriminatorColumn
['name'] . ' IN (' . implode(', ', $values) . ')';
463 $sql = implode(' AND ', $sqlParts);
465 return (count($sqlParts) > 1) ?
'(' . $sql . ')' : $sql;
469 * Generates the filter SQL for a given entity and table alias.
471 * @param ClassMetadata $targetEntity Metadata of the target entity.
472 * @param string $targetTableAlias The table alias of the joined/selected table.
474 * @return string The SQL query part to add to a query.
476 private function generateFilterConditionSQL(ClassMetadata
$targetEntity, $targetTableAlias)
478 if (!$this->em
->hasFilters()) {
482 switch($targetEntity->inheritanceType
) {
483 case ClassMetadata
::INHERITANCE_TYPE_NONE
:
485 case ClassMetadata
::INHERITANCE_TYPE_JOINED
:
486 // The classes in the inheritance will be added to the query one by one,
487 // but only the root node is getting filtered
488 if ($targetEntity->name
!== $targetEntity->rootEntityName
) {
492 case ClassMetadata
::INHERITANCE_TYPE_SINGLE_TABLE
:
493 // With STI the table will only be queried once, make sure that the filters
494 // are added to the root entity
495 $targetEntity = $this->em
->getClassMetadata($targetEntity->rootEntityName
);
498 //@todo: throw exception?
503 $filterClauses = array();
504 foreach ($this->em
->getFilters()->getEnabledFilters() as $filter) {
505 if ('' !== $filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) {
506 $filterClauses[] = '(' . $filterExpr . ')';
510 return implode(' AND ', $filterClauses);
516 public function walkSelectStatement(AST\SelectStatement
$AST)
518 $limit = $this->query
->getMaxResults();
519 $offset = $this->query
->getFirstResult();
520 $lockMode = $this->query
->getHint(Query
::HINT_LOCK_MODE
);
521 $sql = $this->walkSelectClause($AST->selectClause
)
522 . $this->walkFromClause($AST->fromClause
)
523 . $this->walkWhereClause($AST->whereClause
);
525 if ($AST->groupByClause
) {
526 $sql .= $this->walkGroupByClause($AST->groupByClause
);
529 if ($AST->havingClause
) {
530 $sql .= $this->walkHavingClause($AST->havingClause
);
533 if ($AST->orderByClause
) {
534 $sql .= $this->walkOrderByClause($AST->orderByClause
);
537 if ( ! $AST->orderByClause
&& ($orderBySql = $this->_generateOrderedCollectionOrderByItems())) {
538 $sql .= ' ORDER BY ' . $orderBySql;
541 if ($limit !== null ||
$offset !== null) {
542 $sql = $this->platform
->modifyLimitQuery($sql, $limit, $offset);
545 if ($lockMode === null ||
$lockMode === false ||
$lockMode === LockMode
::NONE
) {
549 if ($lockMode === LockMode
::PESSIMISTIC_READ
) {
550 return $sql . ' ' . $this->platform
->getReadLockSQL();
553 if ($lockMode === LockMode
::PESSIMISTIC_WRITE
) {
554 return $sql . ' ' . $this->platform
->getWriteLockSQL();
557 if ($lockMode !== LockMode
::OPTIMISTIC
) {
558 throw QueryException
::invalidLockMode();
561 foreach ($this->selectedClasses
as $selectedClass) {
562 if ( ! $selectedClass['class']->isVersioned
) {
563 throw OptimisticLockException
::lockFailed($selectedClass['class']->name
);
573 public function walkUpdateStatement(AST\UpdateStatement
$AST)
575 $this->useSqlTableAliases
= false;
576 $this->rsm
->isSelect
= false;
578 return $this->walkUpdateClause($AST->updateClause
)
579 . $this->walkWhereClause($AST->whereClause
);
585 public function walkDeleteStatement(AST\DeleteStatement
$AST)
587 $this->useSqlTableAliases
= false;
588 $this->rsm
->isSelect
= false;
590 return $this->walkDeleteClause($AST->deleteClause
)
591 . $this->walkWhereClause($AST->whereClause
);
595 * Walks down an IdentificationVariable AST node, thereby generating the appropriate SQL.
596 * This one differs of ->walkIdentificationVariable() because it generates the entity identifiers.
598 * @param string $identVariable
602 public function walkEntityIdentificationVariable($identVariable)
604 $class = $this->queryComponents
[$identVariable]['metadata'];
605 $tableAlias = $this->getSQLTableAlias($class->getTableName(), $identVariable);
608 foreach ($this->quoteStrategy
->getIdentifierColumnNames($class, $this->platform
) as $columnName) {
609 $sqlParts[] = $tableAlias . '.' . $columnName;
612 return implode(', ', $sqlParts);
616 * Walks down an IdentificationVariable (no AST node associated), thereby generating the SQL.
618 * @param string $identificationVariable
619 * @param string $fieldName
621 * @return string The SQL.
623 public function walkIdentificationVariable($identificationVariable, $fieldName = null)
625 $class = $this->queryComponents
[$identificationVariable]['metadata'];
628 $fieldName !== null && $class->isInheritanceTypeJoined() &&
629 isset($class->fieldMappings
[$fieldName]['inherited'])
631 $class = $this->em
->getClassMetadata($class->fieldMappings
[$fieldName]['inherited']);
634 return $this->getSQLTableAlias($class->getTableName(), $identificationVariable);
640 public function walkPathExpression($pathExpr)
644 switch ($pathExpr->type
) {
645 case AST\PathExpression
::TYPE_STATE_FIELD
:
646 $fieldName = $pathExpr->field
;
647 $dqlAlias = $pathExpr->identificationVariable
;
648 $class = $this->queryComponents
[$dqlAlias]['metadata'];
650 if ($this->useSqlTableAliases
) {
651 $sql .= $this->walkIdentificationVariable($dqlAlias, $fieldName) . '.';
654 $sql .= $this->quoteStrategy
->getColumnName($fieldName, $class, $this->platform
);
657 case AST\PathExpression
::TYPE_SINGLE_VALUED_ASSOCIATION
:
658 // 1- the owning side:
659 // Just use the foreign key, i.e. u.group_id
660 $fieldName = $pathExpr->field
;
661 $dqlAlias = $pathExpr->identificationVariable
;
662 $class = $this->queryComponents
[$dqlAlias]['metadata'];
664 if (isset($class->associationMappings
[$fieldName]['inherited'])) {
665 $class = $this->em
->getClassMetadata($class->associationMappings
[$fieldName]['inherited']);
668 $assoc = $class->associationMappings
[$fieldName];
670 if ( ! $assoc['isOwningSide']) {
671 throw QueryException
::associationPathInverseSideNotSupported();
674 // COMPOSITE KEYS NOT (YET?) SUPPORTED
675 if (count($assoc['sourceToTargetKeyColumns']) > 1) {
676 throw QueryException
::associationPathCompositeKeyNotSupported();
679 if ($this->useSqlTableAliases
) {
680 $sql .= $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.';
683 $sql .= reset($assoc['targetToSourceKeyColumns']);
687 throw QueryException
::invalidPathExpression($pathExpr);
696 public function walkSelectClause($selectClause)
698 $sql = 'SELECT ' . (($selectClause->isDistinct
) ?
'DISTINCT ' : '');
699 $sqlSelectExpressions = array_filter(array_map(array($this, 'walkSelectExpression'), $selectClause->selectExpressions
));
701 if ($this->query
->getHint(Query
::HINT_INTERNAL_ITERATION
) == true && $selectClause->isDistinct
) {
702 $this->query
->setHint(self
::HINT_DISTINCT
, true);
705 $addMetaColumns = ! $this->query
->getHint(Query
::HINT_FORCE_PARTIAL_LOAD
) &&
706 $this->query
->getHydrationMode() == Query
::HYDRATE_OBJECT
708 $this->query
->getHydrationMode() != Query
::HYDRATE_OBJECT
&&
709 $this->query
->getHint(Query
::HINT_INCLUDE_META_COLUMNS
);
711 foreach ($this->selectedClasses
as $selectedClass) {
712 $class = $selectedClass['class'];
713 $dqlAlias = $selectedClass['dqlAlias'];
714 $resultAlias = $selectedClass['resultAlias'];
716 // Register as entity or joined entity result
717 if ($this->queryComponents
[$dqlAlias]['relation'] === null) {
718 $this->rsm
->addEntityResult($class->name
, $dqlAlias, $resultAlias);
720 $this->rsm
->addJoinedEntityResult(
723 $this->queryComponents
[$dqlAlias]['parent'],
724 $this->queryComponents
[$dqlAlias]['relation']['fieldName']
728 if ($class->isInheritanceTypeSingleTable() ||
$class->isInheritanceTypeJoined()) {
729 // Add discriminator columns to SQL
730 $rootClass = $this->em
->getClassMetadata($class->rootEntityName
);
731 $tblAlias = $this->getSQLTableAlias($rootClass->getTableName(), $dqlAlias);
732 $discrColumn = $rootClass->discriminatorColumn
;
733 $columnAlias = $this->getSQLColumnAlias($discrColumn['name']);
735 $sqlSelectExpressions[] = $tblAlias . '.' . $discrColumn['name'] . ' AS ' . $columnAlias;
737 $this->rsm
->setDiscriminatorColumn($dqlAlias, $columnAlias);
738 $this->rsm
->addMetaResult($dqlAlias, $columnAlias, $discrColumn['fieldName']);
741 // Add foreign key columns to SQL, if necessary
742 if ( ! $addMetaColumns && ! $class->containsForeignIdentifier
) {
746 // Add foreign key columns of class and also parent classes
747 foreach ($class->associationMappings
as $assoc) {
748 if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata
::TO_ONE
)) {
750 } else if ( !$addMetaColumns && !isset($assoc['id'])) {
754 $owningClass = (isset($assoc['inherited'])) ?
$this->em
->getClassMetadata($assoc['inherited']) : $class;
755 $sqlTableAlias = $this->getSQLTableAlias($owningClass->getTableName(), $dqlAlias);
757 $targetClass = $this->em
->getClassMetadata($assoc['targetEntity']);
759 foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
760 $columnAlias = $this->getSQLColumnAlias($srcColumn);
763 $isIdentifier = (isset($assoc['id']) && $assoc['id'] === true);
764 $sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
766 if (isset($targetClass->fieldNames
[$targetColumn])) {
767 $type = $targetClass->fieldMappings
[$targetClass->fieldNames
[$targetColumn]]['type'];
770 $this->rsm
->addMetaResult($dqlAlias, $columnAlias, $srcColumn, $isIdentifier, $type);
774 // Add foreign key columns to SQL, if necessary
775 if ( ! $addMetaColumns) {
779 // Add foreign key columns of subclasses
780 foreach ($class->subClasses
as $subClassName) {
781 $subClass = $this->em
->getClassMetadata($subClassName);
782 $sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
784 foreach ($subClass->associationMappings
as $assoc) {
785 // Skip if association is inherited
786 if (isset($assoc['inherited'])) continue;
788 if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata
::TO_ONE
)) continue;
790 foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
791 $columnAlias = $this->getSQLColumnAlias($srcColumn);
793 $sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
795 $this->rsm
->addMetaResult($dqlAlias, $columnAlias, $srcColumn);
801 $sql .= implode(', ', $sqlSelectExpressions);
809 public function walkFromClause($fromClause)
811 $identificationVarDecls = $fromClause->identificationVariableDeclarations
;
814 foreach ($identificationVarDecls as $identificationVariableDecl) {
815 $sqlParts[] = $this->walkIdentificationVariableDeclaration($identificationVariableDecl);
818 return ' FROM ' . implode(', ', $sqlParts);
822 * Walks down a IdentificationVariableDeclaration AST node, thereby generating the appropriate SQL.
824 * @param AST\IdentificationVariableDeclaration $identificationVariableDecl
828 public function walkIdentificationVariableDeclaration($identificationVariableDecl)
830 $sql = $this->walkRangeVariableDeclaration($identificationVariableDecl->rangeVariableDeclaration
);
832 if ($identificationVariableDecl->indexBy
) {
833 $this->walkIndexBy($identificationVariableDecl->indexBy
);
836 foreach ($identificationVariableDecl->joins
as $join) {
837 $sql .= $this->walkJoin($join);
844 * Walks down a IndexBy AST node.
846 * @param AST\IndexBy $indexBy
850 public function walkIndexBy($indexBy)
852 $pathExpression = $indexBy->simpleStateFieldPathExpression
;
853 $alias = $pathExpression->identificationVariable
;
854 $field = $pathExpression->field
;
856 if (isset($this->scalarFields
[$alias][$field])) {
857 $this->rsm
->addIndexByScalar($this->scalarFields
[$alias][$field]);
862 $this->rsm
->addIndexBy($alias, $field);
866 * Walks down a RangeVariableDeclaration AST node, thereby generating the appropriate SQL.
868 * @param AST\RangeVariableDeclaration $rangeVariableDeclaration
872 public function walkRangeVariableDeclaration($rangeVariableDeclaration)
874 return $this->generateRangeVariableDeclarationSQL($rangeVariableDeclaration, false);
878 * Generate appropriate SQL for RangeVariableDeclaration AST node
880 * @param AST\RangeVariableDeclaration $rangeVariableDeclaration
881 * @param bool $buildNestedJoins
885 private function generateRangeVariableDeclarationSQL($rangeVariableDeclaration, $buildNestedJoins)
887 $class = $this->em
->getClassMetadata($rangeVariableDeclaration->abstractSchemaName
);
888 $dqlAlias = $rangeVariableDeclaration->aliasIdentificationVariable
;
890 if ($rangeVariableDeclaration->isRoot
) {
891 $this->rootAliases
[] = $dqlAlias;
894 $sql = $this->platform
->appendLockHint(
895 $this->quoteStrategy
->getTableName($class, $this->platform
) . ' ' .
896 $this->getSQLTableAlias($class->getTableName(), $dqlAlias),
897 $this->query
->getHint(Query
::HINT_LOCK_MODE
)
900 if ( ! $class->isInheritanceTypeJoined()) {
904 $classTableInheritanceJoins = $this->_generateClassTableInheritanceJoins($class, $dqlAlias);
906 if ( ! $buildNestedJoins) {
907 return $sql . $classTableInheritanceJoins;
910 return $classTableInheritanceJoins === '' ?
$sql : '(' . $sql . $classTableInheritanceJoins . ')';
914 * Walks down a JoinAssociationDeclaration AST node, thereby generating the appropriate SQL.
916 * @param AST\JoinAssociationDeclaration $joinAssociationDeclaration
917 * @param int $joinType
918 * @param AST\ConditionalExpression $condExpr
922 * @throws QueryException
924 public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joinType = AST\Join
::JOIN_TYPE_INNER
, $condExpr = null)
928 $associationPathExpression = $joinAssociationDeclaration->joinAssociationPathExpression
;
929 $joinedDqlAlias = $joinAssociationDeclaration->aliasIdentificationVariable
;
930 $indexBy = $joinAssociationDeclaration->indexBy
;
932 $relation = $this->queryComponents
[$joinedDqlAlias]['relation'];
933 $targetClass = $this->em
->getClassMetadata($relation['targetEntity']);
934 $sourceClass = $this->em
->getClassMetadata($relation['sourceEntity']);
935 $targetTableName = $this->quoteStrategy
->getTableName($targetClass,$this->platform
);
937 $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName(), $joinedDqlAlias);
938 $sourceTableAlias = $this->getSQLTableAlias($sourceClass->getTableName(), $associationPathExpression->identificationVariable
);
940 // Ensure we got the owning side, since it has all mapping info
941 $assoc = ( ! $relation['isOwningSide']) ?
$targetClass->associationMappings
[$relation['mappedBy']] : $relation;
943 if ($this->query
->getHint(Query
::HINT_INTERNAL_ITERATION
) == true && (!$this->query
->getHint(self
::HINT_DISTINCT
) ||
isset($this->selectedClasses
[$joinedDqlAlias]))) {
944 if ($relation['type'] == ClassMetadata
::ONE_TO_MANY ||
$relation['type'] == ClassMetadata
::MANY_TO_MANY
) {
945 throw QueryException
::iterateWithFetchJoinNotAllowed($assoc);
949 $targetTableJoin = null;
951 // This condition is not checking ClassMetadata::MANY_TO_ONE, because by definition it cannot
952 // be the owning side and previously we ensured that $assoc is always the owning side of the associations.
953 // The owning side is necessary at this point because only it contains the JoinColumn information.
955 case ($assoc['type'] & ClassMetadata
::TO_ONE
):
956 $conditions = array();
958 foreach ($assoc['joinColumns'] as $joinColumn) {
959 $quotedSourceColumn = $this->quoteStrategy
->getJoinColumnName($joinColumn, $targetClass, $this->platform
);
960 $quotedTargetColumn = $this->quoteStrategy
->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform
);
962 if ($relation['isOwningSide']) {
963 $conditions[] = $sourceTableAlias . '.' . $quotedSourceColumn . ' = ' . $targetTableAlias . '.' . $quotedTargetColumn;
968 $conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $targetTableAlias . '.' . $quotedSourceColumn;
971 // Apply remaining inheritance restrictions
972 $discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($joinedDqlAlias));
975 $conditions[] = $discrSql;
979 $filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias);
982 $conditions[] = $filterExpr;
985 $targetTableJoin = array(
986 'table' => $targetTableName . ' ' . $targetTableAlias,
987 'condition' => implode(' AND ', $conditions),
991 case ($assoc['type'] == ClassMetadata
::MANY_TO_MANY
):
992 // Join relation table
993 $joinTable = $assoc['joinTable'];
994 $joinTableAlias = $this->getSQLTableAlias($joinTable['name'], $joinedDqlAlias);
995 $joinTableName = $this->quoteStrategy
->getJoinTableName($assoc, $sourceClass, $this->platform
);
997 $conditions = array();
998 $relationColumns = ($relation['isOwningSide'])
999 ?
$assoc['joinTable']['joinColumns']
1000 : $assoc['joinTable']['inverseJoinColumns'];
1002 foreach ($relationColumns as $joinColumn) {
1003 $quotedSourceColumn = $this->quoteStrategy
->getJoinColumnName($joinColumn, $targetClass, $this->platform
);
1004 $quotedTargetColumn = $this->quoteStrategy
->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform
);
1006 $conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $quotedSourceColumn;
1009 $sql .= $joinTableName . ' ' . $joinTableAlias . ' ON ' . implode(' AND ', $conditions);
1011 // Join target table
1012 $sql .= ($joinType == AST\Join
::JOIN_TYPE_LEFT ||
$joinType == AST\Join
::JOIN_TYPE_LEFTOUTER
) ?
' LEFT JOIN ' : ' INNER JOIN ';
1014 $conditions = array();
1015 $relationColumns = ($relation['isOwningSide'])
1016 ?
$assoc['joinTable']['inverseJoinColumns']
1017 : $assoc['joinTable']['joinColumns'];
1019 foreach ($relationColumns as $joinColumn) {
1020 $quotedSourceColumn = $this->quoteStrategy
->getJoinColumnName($joinColumn, $targetClass, $this->platform
);
1021 $quotedTargetColumn = $this->quoteStrategy
->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform
);
1023 $conditions[] = $targetTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $quotedSourceColumn;
1026 // Apply remaining inheritance restrictions
1027 $discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($joinedDqlAlias));
1030 $conditions[] = $discrSql;
1033 // Apply the filters
1034 $filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias);
1037 $conditions[] = $filterExpr;
1040 $targetTableJoin = array(
1041 'table' => $targetTableName . ' ' . $targetTableAlias,
1042 'condition' => implode(' AND ', $conditions),
1047 throw new \
BadMethodCallException('Type of association must be one of *_TO_ONE or MANY_TO_MANY');
1050 // Handle WITH clause
1051 $withCondition = (null === $condExpr) ?
'' : ('(' . $this->walkConditionalExpression($condExpr) . ')');
1053 if ($targetClass->isInheritanceTypeJoined()) {
1054 $ctiJoins = $this->_generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias);
1055 // If we have WITH condition, we need to build nested joins for target class table and cti joins
1056 if ($withCondition) {
1057 $sql .= '(' . $targetTableJoin['table'] . $ctiJoins . ') ON ' . $targetTableJoin['condition'];
1059 $sql .= $targetTableJoin['table'] . ' ON ' . $targetTableJoin['condition'] . $ctiJoins;
1062 $sql .= $targetTableJoin['table'] . ' ON ' . $targetTableJoin['condition'];
1065 if ($withCondition) {
1066 $sql .= ' AND ' . $withCondition;
1069 // Apply the indexes
1071 // For Many-To-One or One-To-One associations this obviously makes no sense, but is ignored silently.
1072 $this->walkIndexBy($indexBy);
1073 } else if (isset($relation['indexBy'])) {
1074 $this->rsm
->addIndexBy($joinedDqlAlias, $relation['indexBy']);
1083 public function walkFunction($function)
1085 return $function->getSql($this);
1091 public function walkOrderByClause($orderByClause)
1093 $orderByItems = array_map(array($this, 'walkOrderByItem'), $orderByClause->orderByItems
);
1095 if (($collectionOrderByItems = $this->_generateOrderedCollectionOrderByItems()) !== '') {
1096 $orderByItems = array_merge($orderByItems, (array) $collectionOrderByItems);
1099 return ' ORDER BY ' . implode(', ', $orderByItems);
1105 public function walkOrderByItem($orderByItem)
1107 $type = strtoupper($orderByItem->type
);
1108 $expr = $orderByItem->expression
;
1109 $sql = ($expr instanceof AST\Node
)
1110 ?
$expr->dispatch($this)
1111 : $this->walkResultVariable($this->queryComponents
[$expr]['token']['value']);
1113 $this->orderedColumnsMap
[$sql] = $type;
1115 if ($expr instanceof AST\Subselect
) {
1116 return '(' . $sql . ') ' . $type;
1119 return $sql . ' ' . $type;
1125 public function walkHavingClause($havingClause)
1127 return ' HAVING ' . $this->walkConditionalExpression($havingClause->conditionalExpression
);
1133 public function walkJoin($join)
1135 $joinType = $join->joinType
;
1136 $joinDeclaration = $join->joinAssociationDeclaration
;
1138 $sql = ($joinType == AST\Join
::JOIN_TYPE_LEFT ||
$joinType == AST\Join
::JOIN_TYPE_LEFTOUTER
)
1143 case ($joinDeclaration instanceof AST\RangeVariableDeclaration
):
1144 $class = $this->em
->getClassMetadata($joinDeclaration->abstractSchemaName
);
1145 $dqlAlias = $joinDeclaration->aliasIdentificationVariable
;
1146 $tableAlias = $this->getSQLTableAlias($class->table
['name'], $dqlAlias);
1147 $condition = '(' . $this->walkConditionalExpression($join->conditionalExpression
) . ')';
1148 $isUnconditionalJoin = empty($condition);
1149 $condExprConjunction = ($class->isInheritanceTypeJoined() && $joinType != AST\Join
::JOIN_TYPE_LEFT
&& $joinType != AST\Join
::JOIN_TYPE_LEFTOUTER
&& $isUnconditionalJoin)
1153 $sql .= $this->generateRangeVariableDeclarationSQL($joinDeclaration, !$isUnconditionalJoin);
1155 $conditions = array($condition);
1157 // Apply remaining inheritance restrictions
1158 $discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($dqlAlias));
1161 $conditions[] = $discrSql;
1164 // Apply the filters
1165 $filterExpr = $this->generateFilterConditionSQL($class, $tableAlias);
1168 $conditions[] = $filterExpr;
1171 $sql .= $condExprConjunction . implode(' AND ', $conditions);
1174 case ($joinDeclaration instanceof AST\JoinAssociationDeclaration
):
1175 $sql .= $this->walkJoinAssociationDeclaration($joinDeclaration, $joinType, $join->conditionalExpression
);
1183 * Walks down a CaseExpression AST node and generates the corresponding SQL.
1185 * @param AST\CoalesceExpression|AST\NullIfExpression|AST\GeneralCaseExpression|AST\SimpleCaseExpression $expression
1187 * @return string The SQL.
1189 public function walkCaseExpression($expression)
1192 case ($expression instanceof AST\CoalesceExpression
):
1193 return $this->walkCoalesceExpression($expression);
1195 case ($expression instanceof AST\NullIfExpression
):
1196 return $this->walkNullIfExpression($expression);
1198 case ($expression instanceof AST\GeneralCaseExpression
):
1199 return $this->walkGeneralCaseExpression($expression);
1201 case ($expression instanceof AST\SimpleCaseExpression
):
1202 return $this->walkSimpleCaseExpression($expression);
1210 * Walks down a CoalesceExpression AST node and generates the corresponding SQL.
1212 * @param AST\CoalesceExpression $coalesceExpression
1214 * @return string The SQL.
1216 public function walkCoalesceExpression($coalesceExpression)
1220 $scalarExpressions = array();
1222 foreach ($coalesceExpression->scalarExpressions
as $scalarExpression) {
1223 $scalarExpressions[] = $this->walkSimpleArithmeticExpression($scalarExpression);
1226 $sql .= implode(', ', $scalarExpressions) . ')';
1232 * Walks down a NullIfExpression AST node and generates the corresponding SQL.
1234 * @param AST\NullIfExpression $nullIfExpression
1236 * @return string The SQL.
1238 public function walkNullIfExpression($nullIfExpression)
1240 $firstExpression = is_string($nullIfExpression->firstExpression
)
1241 ?
$this->conn
->quote($nullIfExpression->firstExpression
)
1242 : $this->walkSimpleArithmeticExpression($nullIfExpression->firstExpression
);
1244 $secondExpression = is_string($nullIfExpression->secondExpression
)
1245 ?
$this->conn
->quote($nullIfExpression->secondExpression
)
1246 : $this->walkSimpleArithmeticExpression($nullIfExpression->secondExpression
);
1248 return 'NULLIF(' . $firstExpression . ', ' . $secondExpression . ')';
1252 * Walks down a GeneralCaseExpression AST node and generates the corresponding SQL.
1254 * @param AST\GeneralCaseExpression $generalCaseExpression
1256 * @return string The SQL.
1258 public function walkGeneralCaseExpression(AST\GeneralCaseExpression
$generalCaseExpression)
1262 foreach ($generalCaseExpression->whenClauses
as $whenClause) {
1263 $sql .= ' WHEN ' . $this->walkConditionalExpression($whenClause->caseConditionExpression
);
1264 $sql .= ' THEN ' . $this->walkSimpleArithmeticExpression($whenClause->thenScalarExpression
);
1267 $sql .= ' ELSE ' . $this->walkSimpleArithmeticExpression($generalCaseExpression->elseScalarExpression
) . ' END';
1273 * Walks down a SimpleCaseExpression AST node and generates the corresponding SQL.
1275 * @param AST\SimpleCaseExpression $simpleCaseExpression
1277 * @return string The SQL.
1279 public function walkSimpleCaseExpression($simpleCaseExpression)
1281 $sql = 'CASE ' . $this->walkStateFieldPathExpression($simpleCaseExpression->caseOperand
);
1283 foreach ($simpleCaseExpression->simpleWhenClauses
as $simpleWhenClause) {
1284 $sql .= ' WHEN ' . $this->walkSimpleArithmeticExpression($simpleWhenClause->caseScalarExpression
);
1285 $sql .= ' THEN ' . $this->walkSimpleArithmeticExpression($simpleWhenClause->thenScalarExpression
);
1288 $sql .= ' ELSE ' . $this->walkSimpleArithmeticExpression($simpleCaseExpression->elseScalarExpression
) . ' END';
1296 public function walkSelectExpression($selectExpression)
1299 $expr = $selectExpression->expression
;
1300 $hidden = $selectExpression->hiddenAliasResultVariable
;
1303 case ($expr instanceof AST\PathExpression
):
1304 if ($expr->type
!== AST\PathExpression
::TYPE_STATE_FIELD
) {
1305 throw QueryException
::invalidPathExpression($expr);
1308 $fieldName = $expr->field
;
1309 $dqlAlias = $expr->identificationVariable
;
1310 $qComp = $this->queryComponents
[$dqlAlias];
1311 $class = $qComp['metadata'];
1313 $resultAlias = $selectExpression->fieldIdentificationVariable ?
: $fieldName;
1314 $tableName = ($class->isInheritanceTypeJoined())
1315 ?
$this->em
->getUnitOfWork()->getEntityPersister($class->name
)->getOwningTable($fieldName)
1316 : $class->getTableName();
1318 $sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias);
1319 $columnName = $this->quoteStrategy
->getColumnName($fieldName, $class, $this->platform
);
1320 $columnAlias = $this->getSQLColumnAlias($class->fieldMappings
[$fieldName]['columnName']);
1322 $col = $sqlTableAlias . '.' . $columnName;
1324 $fieldType = $class->getTypeOfField($fieldName);
1326 if (isset($class->fieldMappings
[$fieldName]['requireSQLConversion'])) {
1327 $type = Type
::getType($fieldType);
1328 $col = $type->convertToPHPValueSQL($col, $this->conn
->getDatabasePlatform());
1331 $sql .= $col . ' AS ' . $columnAlias;
1333 $this->scalarResultAliasMap
[$resultAlias] = $columnAlias;
1336 $this->rsm
->addScalarResult($columnAlias, $resultAlias, $fieldType);
1337 $this->scalarFields
[$dqlAlias][$fieldName] = $columnAlias;
1341 case ($expr instanceof AST\AggregateExpression
):
1342 case ($expr instanceof AST\Functions\FunctionNode
):
1343 case ($expr instanceof AST\SimpleArithmeticExpression
):
1344 case ($expr instanceof AST\ArithmeticTerm
):
1345 case ($expr instanceof AST\ArithmeticFactor
):
1346 case ($expr instanceof AST\ParenthesisExpression
):
1347 case ($expr instanceof AST\Literal
):
1348 case ($expr instanceof AST\NullIfExpression
):
1349 case ($expr instanceof AST\CoalesceExpression
):
1350 case ($expr instanceof AST\GeneralCaseExpression
):
1351 case ($expr instanceof AST\SimpleCaseExpression
):
1352 $columnAlias = $this->getSQLColumnAlias('sclr');
1353 $resultAlias = $selectExpression->fieldIdentificationVariable ?
: $this->scalarResultCounter++
;
1355 $sql .= $expr->dispatch($this) . ' AS ' . $columnAlias;
1357 $this->scalarResultAliasMap
[$resultAlias] = $columnAlias;
1360 // We cannot resolve field type here; assume 'string'.
1361 $this->rsm
->addScalarResult($columnAlias, $resultAlias, 'string');
1365 case ($expr instanceof AST\Subselect
):
1366 $columnAlias = $this->getSQLColumnAlias('sclr');
1367 $resultAlias = $selectExpression->fieldIdentificationVariable ?
: $this->scalarResultCounter++
;
1369 $sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias;
1371 $this->scalarResultAliasMap
[$resultAlias] = $columnAlias;
1374 // We cannot resolve field type here; assume 'string'.
1375 $this->rsm
->addScalarResult($columnAlias, $resultAlias, 'string');
1379 case ($expr instanceof AST\NewObjectExpression
):
1380 $sql .= $this->walkNewObject($expr,$selectExpression->fieldIdentificationVariable
);
1384 // IdentificationVariable or PartialObjectExpression
1385 if ($expr instanceof AST\PartialObjectExpression
) {
1386 $dqlAlias = $expr->identificationVariable
;
1387 $partialFieldSet = $expr->partialFieldSet
;
1390 $partialFieldSet = array();
1393 $queryComp = $this->queryComponents
[$dqlAlias];
1394 $class = $queryComp['metadata'];
1395 $resultAlias = $selectExpression->fieldIdentificationVariable ?
: null;
1397 if ( ! isset($this->selectedClasses
[$dqlAlias])) {
1398 $this->selectedClasses
[$dqlAlias] = array(
1400 'dqlAlias' => $dqlAlias,
1401 'resultAlias' => $resultAlias
1405 $sqlParts = array();
1407 // Select all fields from the queried class
1408 foreach ($class->fieldMappings
as $fieldName => $mapping) {
1409 if ($partialFieldSet && ! in_array($fieldName, $partialFieldSet)) {
1413 $tableName = (isset($mapping['inherited']))
1414 ?
$this->em
->getClassMetadata($mapping['inherited'])->getTableName()
1415 : $class->getTableName();
1417 $sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias);
1418 $columnAlias = $this->getSQLColumnAlias($mapping['columnName']);
1419 $quotedColumnName = $this->quoteStrategy
->getColumnName($fieldName, $class, $this->platform
);
1421 $col = $sqlTableAlias . '.' . $quotedColumnName;
1423 if (isset($class->fieldMappings
[$fieldName]['requireSQLConversion'])) {
1424 $type = Type
::getType($class->getTypeOfField($fieldName));
1425 $col = $type->convertToPHPValueSQL($col, $this->platform
);
1428 $sqlParts[] = $col . ' AS '. $columnAlias;
1430 $this->scalarResultAliasMap
[$resultAlias][] = $columnAlias;
1432 $this->rsm
->addFieldResult($dqlAlias, $columnAlias, $fieldName, $class->name
);
1435 // Add any additional fields of subclasses (excluding inherited fields)
1436 // 1) on Single Table Inheritance: always, since its marginal overhead
1437 // 2) on Class Table Inheritance only if partial objects are disallowed,
1438 // since it requires outer joining subtables.
1439 if ($class->isInheritanceTypeSingleTable() ||
! $this->query
->getHint(Query
::HINT_FORCE_PARTIAL_LOAD
)) {
1440 foreach ($class->subClasses
as $subClassName) {
1441 $subClass = $this->em
->getClassMetadata($subClassName);
1442 $sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
1444 foreach ($subClass->fieldMappings
as $fieldName => $mapping) {
1445 if (isset($mapping['inherited']) ||
$partialFieldSet && !in_array($fieldName, $partialFieldSet)) {
1449 $columnAlias = $this->getSQLColumnAlias($mapping['columnName']);
1450 $quotedColumnName = $this->quoteStrategy
->getColumnName($fieldName, $subClass, $this->platform
);
1452 $col = $sqlTableAlias . '.' . $quotedColumnName;
1454 if (isset($subClass->fieldMappings
[$fieldName]['requireSQLConversion'])) {
1455 $type = Type
::getType($subClass->getTypeOfField($fieldName));
1456 $col = $type->convertToPHPValueSQL($col, $this->platform
);
1459 $sqlParts[] = $col . ' AS ' . $columnAlias;
1461 $this->scalarResultAliasMap
[$resultAlias][] = $columnAlias;
1463 $this->rsm
->addFieldResult($dqlAlias, $columnAlias, $fieldName, $subClassName);
1468 $sql .= implode(', ', $sqlParts);
1477 public function walkQuantifiedExpression($qExpr)
1479 return ' ' . strtoupper($qExpr->type
) . '(' . $this->walkSubselect($qExpr->subselect
) . ')';
1485 public function walkSubselect($subselect)
1487 $useAliasesBefore = $this->useSqlTableAliases
;
1488 $rootAliasesBefore = $this->rootAliases
;
1490 $this->rootAliases
= array(); // reset the rootAliases for the subselect
1491 $this->useSqlTableAliases
= true;
1493 $sql = $this->walkSimpleSelectClause($subselect->simpleSelectClause
);
1494 $sql .= $this->walkSubselectFromClause($subselect->subselectFromClause
);
1495 $sql .= $this->walkWhereClause($subselect->whereClause
);
1497 $sql .= $subselect->groupByClause ?
$this->walkGroupByClause($subselect->groupByClause
) : '';
1498 $sql .= $subselect->havingClause ?
$this->walkHavingClause($subselect->havingClause
) : '';
1499 $sql .= $subselect->orderByClause ?
$this->walkOrderByClause($subselect->orderByClause
) : '';
1501 $this->rootAliases
= $rootAliasesBefore; // put the main aliases back
1502 $this->useSqlTableAliases
= $useAliasesBefore;
1510 public function walkSubselectFromClause($subselectFromClause)
1512 $identificationVarDecls = $subselectFromClause->identificationVariableDeclarations
;
1513 $sqlParts = array ();
1515 foreach ($identificationVarDecls as $subselectIdVarDecl) {
1516 $sqlParts[] = $this->walkIdentificationVariableDeclaration($subselectIdVarDecl);
1519 return ' FROM ' . implode(', ', $sqlParts);
1525 public function walkSimpleSelectClause($simpleSelectClause)
1527 return 'SELECT' . ($simpleSelectClause->isDistinct ?
' DISTINCT' : '')
1528 . $this->walkSimpleSelectExpression($simpleSelectClause->simpleSelectExpression
);
1532 * @param \Doctrine\ORM\Query\AST\ParenthesisExpression $parenthesisExpression
1536 public function walkParenthesisExpression(AST\ParenthesisExpression
$parenthesisExpression)
1538 return sprintf('(%s)', $parenthesisExpression->expression
->dispatch($this));
1542 * @param AST\NewObjectExpression $newObjectExpression
1544 * @return string The SQL.
1546 public function walkNewObject($newObjectExpression, $newObjectResultAlias=null)
1548 $sqlSelectExpressions = array();
1549 $objIndex = $newObjectResultAlias?
:$this->newObjectCounter++
;
1551 foreach ($newObjectExpression->args
as $argIndex => $e) {
1552 $resultAlias = $this->scalarResultCounter++
;
1553 $columnAlias = $this->getSQLColumnAlias('sclr');
1554 $fieldType = 'string';
1557 case ($e instanceof AST\NewObjectExpression
):
1558 $sqlSelectExpressions[] = $e->dispatch($this);
1561 case ($e instanceof AST\Subselect
):
1562 $sqlSelectExpressions[] = '(' . $e->dispatch($this) . ') AS ' . $columnAlias;
1565 case ($e instanceof AST\PathExpression
):
1566 $fieldName = $e->field
;
1567 $dqlAlias = $e->identificationVariable
;
1568 $qComp = $this->queryComponents
[$dqlAlias];
1569 $class = $qComp['metadata'];
1570 $fieldType = $class->getTypeOfField($fieldName);
1572 $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' . $columnAlias;
1575 case ($e instanceof AST\Literal
):
1577 case AST\Literal
::BOOLEAN
:
1578 $fieldType = 'boolean';
1581 case AST\Literal
::NUMERIC:
1582 $fieldType = is_float($e->value
) ?
'float' : 'integer';
1586 $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' . $columnAlias;
1590 $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' . $columnAlias;
1594 $this->scalarResultAliasMap
[$resultAlias] = $columnAlias;
1595 $this->rsm
->addScalarResult($columnAlias, $resultAlias, $fieldType);
1597 $this->rsm
->newObjectMappings
[$columnAlias] = array(
1598 'className' => $newObjectExpression->className
,
1599 'objIndex' => $objIndex,
1600 'argIndex' => $argIndex
1604 return implode(', ', $sqlSelectExpressions);
1610 public function walkSimpleSelectExpression($simpleSelectExpression)
1612 $expr = $simpleSelectExpression->expression
;
1616 case ($expr instanceof AST\PathExpression
):
1617 $sql .= $this->walkPathExpression($expr);
1620 case ($expr instanceof AST\AggregateExpression
):
1621 $alias = $simpleSelectExpression->fieldIdentificationVariable ?
: $this->scalarResultCounter++
;
1623 $sql .= $this->walkAggregateExpression($expr) . ' AS dctrn__' . $alias;
1626 case ($expr instanceof AST\Subselect
):
1627 $alias = $simpleSelectExpression->fieldIdentificationVariable ?
: $this->scalarResultCounter++
;
1629 $columnAlias = 'sclr' . $this->aliasCounter++
;
1630 $this->scalarResultAliasMap
[$alias] = $columnAlias;
1632 $sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias;
1635 case ($expr instanceof AST\Functions\FunctionNode
):
1636 case ($expr instanceof AST\SimpleArithmeticExpression
):
1637 case ($expr instanceof AST\ArithmeticTerm
):
1638 case ($expr instanceof AST\ArithmeticFactor
):
1639 case ($expr instanceof AST\Literal
):
1640 case ($expr instanceof AST\NullIfExpression
):
1641 case ($expr instanceof AST\CoalesceExpression
):
1642 case ($expr instanceof AST\GeneralCaseExpression
):
1643 case ($expr instanceof AST\SimpleCaseExpression
):
1644 $alias = $simpleSelectExpression->fieldIdentificationVariable ?
: $this->scalarResultCounter++
;
1646 $columnAlias = $this->getSQLColumnAlias('sclr');
1647 $this->scalarResultAliasMap
[$alias] = $columnAlias;
1649 $sql .= $expr->dispatch($this) . ' AS ' . $columnAlias;
1652 case ($expr instanceof AST\ParenthesisExpression
):
1653 $sql .= $this->walkParenthesisExpression($expr);
1656 default: // IdentificationVariable
1657 $sql .= $this->walkEntityIdentificationVariable($expr);
1667 public function walkAggregateExpression($aggExpression)
1669 return $aggExpression->functionName
. '(' . ($aggExpression->isDistinct ?
'DISTINCT ' : '')
1670 . $this->walkSimpleArithmeticExpression($aggExpression->pathExpression
) . ')';
1676 public function walkGroupByClause($groupByClause)
1678 $sqlParts = array();
1680 foreach ($groupByClause->groupByItems
as $groupByItem) {
1681 $sqlParts[] = $this->walkGroupByItem($groupByItem);
1684 return ' GROUP BY ' . implode(', ', $sqlParts);
1690 public function walkGroupByItem($groupByItem)
1692 // StateFieldPathExpression
1693 if ( ! is_string($groupByItem)) {
1694 return $this->walkPathExpression($groupByItem);
1698 if (isset($this->queryComponents
[$groupByItem]['resultVariable'])) {
1699 $resultVariable = $this->queryComponents
[$groupByItem]['resultVariable'];
1701 if ($resultVariable instanceof AST\PathExpression
) {
1702 return $this->walkPathExpression($resultVariable);
1705 if (isset($resultVariable->pathExpression
)) {
1706 return $this->walkPathExpression($resultVariable->pathExpression
);
1709 return $this->walkResultVariable($groupByItem);
1712 // IdentificationVariable
1713 $sqlParts = array();
1715 foreach ($this->queryComponents
[$groupByItem]['metadata']->fieldNames
as $field) {
1716 $item = new AST\
PathExpression(AST\PathExpression
::TYPE_STATE_FIELD
, $groupByItem, $field);
1717 $item->type
= AST\PathExpression
::TYPE_STATE_FIELD
;
1719 $sqlParts[] = $this->walkPathExpression($item);
1722 foreach ($this->queryComponents
[$groupByItem]['metadata']->associationMappings
as $mapping) {
1723 if ($mapping['isOwningSide'] && $mapping['type'] & ClassMetadataInfo
::TO_ONE
) {
1724 $item = new AST\
PathExpression(AST\PathExpression
::TYPE_SINGLE_VALUED_ASSOCIATION
, $groupByItem, $mapping['fieldName']);
1725 $item->type
= AST\PathExpression
::TYPE_SINGLE_VALUED_ASSOCIATION
;
1727 $sqlParts[] = $this->walkPathExpression($item);
1731 return implode(', ', $sqlParts);
1737 public function walkDeleteClause(AST\DeleteClause
$deleteClause)
1739 $class = $this->em
->getClassMetadata($deleteClause->abstractSchemaName
);
1740 $tableName = $class->getTableName();
1741 $sql = 'DELETE FROM ' . $this->quoteStrategy
->getTableName($class, $this->platform
);
1743 $this->setSQLTableAlias($tableName, $tableName, $deleteClause->aliasIdentificationVariable
);
1744 $this->rootAliases
[] = $deleteClause->aliasIdentificationVariable
;
1752 public function walkUpdateClause($updateClause)
1754 $class = $this->em
->getClassMetadata($updateClause->abstractSchemaName
);
1755 $tableName = $class->getTableName();
1756 $sql = 'UPDATE ' . $this->quoteStrategy
->getTableName($class, $this->platform
);
1758 $this->setSQLTableAlias($tableName, $tableName, $updateClause->aliasIdentificationVariable
);
1759 $this->rootAliases
[] = $updateClause->aliasIdentificationVariable
;
1761 $sql .= ' SET ' . implode(', ', array_map(array($this, 'walkUpdateItem'), $updateClause->updateItems
));
1769 public function walkUpdateItem($updateItem)
1771 $useTableAliasesBefore = $this->useSqlTableAliases
;
1772 $this->useSqlTableAliases
= false;
1774 $sql = $this->walkPathExpression($updateItem->pathExpression
) . ' = ';
1775 $newValue = $updateItem->newValue
;
1778 case ($newValue instanceof AST\Node
):
1779 $sql .= $newValue->dispatch($this);
1782 case ($newValue === null):
1787 $sql .= $this->conn
->quote($newValue);
1791 $this->useSqlTableAliases
= $useTableAliasesBefore;
1799 public function walkWhereClause($whereClause)
1801 $condSql = null !== $whereClause ?
$this->walkConditionalExpression($whereClause->conditionalExpression
) : '';
1802 $discrSql = $this->_generateDiscriminatorColumnConditionSql($this->rootAliases
);
1804 if ($this->em
->hasFilters()) {
1805 $filterClauses = array();
1806 foreach ($this->rootAliases
as $dqlAlias) {
1807 $class = $this->queryComponents
[$dqlAlias]['metadata'];
1808 $tableAlias = $this->getSQLTableAlias($class->table
['name'], $dqlAlias);
1810 if ($filterExpr = $this->generateFilterConditionSQL($class, $tableAlias)) {
1811 $filterClauses[] = $filterExpr;
1815 if (count($filterClauses)) {
1817 $condSql = '(' . $condSql . ') AND ';
1820 $condSql .= implode(' AND ', $filterClauses);
1825 return ' WHERE ' . (( ! $discrSql) ?
$condSql : '(' . $condSql . ') AND ' . $discrSql);
1829 return ' WHERE ' . $discrSql;
1838 public function walkConditionalExpression($condExpr)
1840 // Phase 2 AST optimization: Skip processing of ConditionalExpression
1841 // if only one ConditionalTerm is defined
1842 if ( ! ($condExpr instanceof AST\ConditionalExpression
)) {
1843 return $this->walkConditionalTerm($condExpr);
1846 return implode(' OR ', array_map(array($this, 'walkConditionalTerm'), $condExpr->conditionalTerms
));
1852 public function walkConditionalTerm($condTerm)
1854 // Phase 2 AST optimization: Skip processing of ConditionalTerm
1855 // if only one ConditionalFactor is defined
1856 if ( ! ($condTerm instanceof AST\ConditionalTerm
)) {
1857 return $this->walkConditionalFactor($condTerm);
1860 return implode(' AND ', array_map(array($this, 'walkConditionalFactor'), $condTerm->conditionalFactors
));
1866 public function walkConditionalFactor($factor)
1868 // Phase 2 AST optimization: Skip processing of ConditionalFactor
1869 // if only one ConditionalPrimary is defined
1870 return ( ! ($factor instanceof AST\ConditionalFactor
))
1871 ?
$this->walkConditionalPrimary($factor)
1872 : ($factor->not ?
'NOT ' : '') . $this->walkConditionalPrimary($factor->conditionalPrimary
);
1878 public function walkConditionalPrimary($primary)
1880 if ($primary->isSimpleConditionalExpression()) {
1881 return $primary->simpleConditionalExpression
->dispatch($this);
1884 if ($primary->isConditionalExpression()) {
1885 $condExpr = $primary->conditionalExpression
;
1887 return '(' . $this->walkConditionalExpression($condExpr) . ')';
1894 public function walkExistsExpression($existsExpr)
1896 $sql = ($existsExpr->not
) ?
'NOT ' : '';
1898 $sql .= 'EXISTS (' . $this->walkSubselect($existsExpr->subselect
) . ')';
1906 public function walkCollectionMemberExpression($collMemberExpr)
1908 $sql = $collMemberExpr->not ?
'NOT ' : '';
1909 $sql .= 'EXISTS (SELECT 1 FROM ';
1911 $entityExpr = $collMemberExpr->entityExpression
;
1912 $collPathExpr = $collMemberExpr->collectionValuedPathExpression
;
1914 $fieldName = $collPathExpr->field
;
1915 $dqlAlias = $collPathExpr->identificationVariable
;
1917 $class = $this->queryComponents
[$dqlAlias]['metadata'];
1921 case ($entityExpr instanceof AST\InputParameter
):
1922 $dqlParamKey = $entityExpr->name
;
1926 // SingleValuedAssociationPathExpression | IdentificationVariable
1927 case ($entityExpr instanceof AST\PathExpression
):
1928 $entitySql = $this->walkPathExpression($entityExpr);
1932 throw new \
BadMethodCallException("Not implemented");
1935 $assoc = $class->associationMappings
[$fieldName];
1937 if ($assoc['type'] == ClassMetadata
::ONE_TO_MANY
) {
1938 $targetClass = $this->em
->getClassMetadata($assoc['targetEntity']);
1939 $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName());
1940 $sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
1942 $sql .= $this->quoteStrategy
->getTableName($targetClass, $this->platform
) . ' ' . $targetTableAlias . ' WHERE ';
1944 $owningAssoc = $targetClass->associationMappings
[$assoc['mappedBy']];
1945 $sqlParts = array();
1947 foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) {
1948 $targetColumn = $this->quoteStrategy
->getColumnName($class->fieldNames
[$targetColumn], $class, $this->platform
);
1950 $sqlParts[] = $sourceTableAlias . '.' . $targetColumn . ' = ' . $targetTableAlias . '.' . $sourceColumn;
1953 foreach ($this->quoteStrategy
->getIdentifierColumnNames($targetClass, $this->platform
) as $targetColumnName) {
1954 if (isset($dqlParamKey)) {
1955 $this->parserResult
->addParameterMapping($dqlParamKey, $this->sqlParamIndex++
);
1958 $sqlParts[] = $targetTableAlias . '.' . $targetColumnName . ' = ' . $entitySql;
1961 $sql .= implode(' AND ', $sqlParts);
1962 } else { // many-to-many
1963 $targetClass = $this->em
->getClassMetadata($assoc['targetEntity']);
1965 $owningAssoc = $assoc['isOwningSide'] ?
$assoc : $targetClass->associationMappings
[$assoc['mappedBy']];
1966 $joinTable = $owningAssoc['joinTable'];
1968 // SQL table aliases
1969 $joinTableAlias = $this->getSQLTableAlias($joinTable['name']);
1970 $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName());
1971 $sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
1973 // join to target table
1974 $sql .= $this->quoteStrategy
->getJoinTableName($owningAssoc, $targetClass, $this->platform
) . ' ' . $joinTableAlias
1975 . ' INNER JOIN ' . $this->quoteStrategy
->getTableName($targetClass, $this->platform
) . ' ' . $targetTableAlias . ' ON ';
1978 $joinColumns = $assoc['isOwningSide'] ?
$joinTable['inverseJoinColumns'] : $joinTable['joinColumns'];
1979 $joinSqlParts = array();
1981 foreach ($joinColumns as $joinColumn) {
1982 $targetColumn = $this->quoteStrategy
->getColumnName($targetClass->fieldNames
[$joinColumn['referencedColumnName']], $targetClass, $this->platform
);
1984 $joinSqlParts[] = $joinTableAlias . '.' . $joinColumn['name'] . ' = ' . $targetTableAlias . '.' . $targetColumn;
1987 $sql .= implode(' AND ', $joinSqlParts);
1990 $joinColumns = $assoc['isOwningSide'] ?
$joinTable['joinColumns'] : $joinTable['inverseJoinColumns'];
1991 $sqlParts = array();
1993 foreach ($joinColumns as $joinColumn) {
1994 $targetColumn = $this->quoteStrategy
->getColumnName($class->fieldNames
[$joinColumn['referencedColumnName']], $class, $this->platform
);
1996 $sqlParts[] = $joinTableAlias . '.' . $joinColumn['name'] . ' = ' . $sourceTableAlias . '.' . $targetColumn;
1999 foreach ($this->quoteStrategy
->getIdentifierColumnNames($targetClass, $this->platform
) as $targetColumnName) {
2000 if (isset($dqlParamKey)) {
2001 $this->parserResult
->addParameterMapping($dqlParamKey, $this->sqlParamIndex++
);
2004 $sqlParts[] = $targetTableAlias . '.' . $targetColumnName . ' IN (' . $entitySql . ')';
2007 $sql .= implode(' AND ', $sqlParts);
2016 public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr)
2018 $sizeFunc = new AST\Functions\
SizeFunction('size');
2019 $sizeFunc->collectionPathExpression
= $emptyCollCompExpr->expression
;
2021 return $sizeFunc->getSql($this) . ($emptyCollCompExpr->not ?
' > 0' : ' = 0');
2027 public function walkNullComparisonExpression($nullCompExpr)
2029 $expression = $nullCompExpr->expression
;
2030 $comparison = ' IS' . ($nullCompExpr->not ?
' NOT' : '') . ' NULL';
2032 // Handle ResultVariable
2033 if (is_string($expression) && isset($this->queryComponents
[$expression]['resultVariable'])) {
2034 return $this->walkResultVariable($expression) . $comparison;
2037 // Handle InputParameter mapping inclusion to ParserResult
2038 if ($expression instanceof AST\InputParameter
) {
2039 return $this->walkInputParameter($expression) . $comparison;
2042 return $expression->dispatch($this) . $comparison;
2048 public function walkInExpression($inExpr)
2050 $sql = $this->walkArithmeticExpression($inExpr->expression
) . ($inExpr->not ?
' NOT' : '') . ' IN (';
2052 $sql .= ($inExpr->subselect
)
2053 ?
$this->walkSubselect($inExpr->subselect
)
2054 : implode(', ', array_map(array($this, 'walkInParameter'), $inExpr->literals
));
2064 public function walkInstanceOfExpression($instanceOfExpr)
2068 $dqlAlias = $instanceOfExpr->identificationVariable
;
2069 $discrClass = $class = $this->queryComponents
[$dqlAlias]['metadata'];
2071 if ($class->discriminatorColumn
) {
2072 $discrClass = $this->em
->getClassMetadata($class->rootEntityName
);
2075 if ($this->useSqlTableAliases
) {
2076 $sql .= $this->getSQLTableAlias($discrClass->getTableName(), $dqlAlias) . '.';
2079 $sql .= $class->discriminatorColumn
['name'] . ($instanceOfExpr->not ?
' NOT IN ' : ' IN ');
2081 $sqlParameterList = array();
2083 foreach ($instanceOfExpr->value
as $parameter) {
2084 if ($parameter instanceof AST\InputParameter
) {
2085 $this->rsm
->addMetadataParameterMapping($parameter->name
, 'discriminatorValue');
2087 $sqlParameterList[] = $this->walkInputParameter($parameter);
2092 // Get name from ClassMetadata to resolve aliases.
2093 $entityClassName = $this->em
->getClassMetadata($parameter)->name
;
2094 $discriminatorValue = $class->discriminatorValue
;
2096 if ($entityClassName !== $class->name
) {
2097 $discrMap = array_flip($class->discriminatorMap
);
2099 if ( ! isset($discrMap[$entityClassName])) {
2100 throw QueryException
::instanceOfUnrelatedClass($entityClassName, $class->rootEntityName
);
2103 $discriminatorValue = $discrMap[$entityClassName];
2106 $sqlParameterList[] = $this->conn
->quote($discriminatorValue);
2109 $sql .= '(' . implode(', ', $sqlParameterList) . ')';
2117 public function walkInParameter($inParam)
2119 return $inParam instanceof AST\InputParameter
2120 ?
$this->walkInputParameter($inParam)
2121 : $this->walkLiteral($inParam);
2127 public function walkLiteral($literal)
2129 switch ($literal->type
) {
2130 case AST\Literal
::STRING:
2131 return $this->conn
->quote($literal->value
);
2133 case AST\Literal
::BOOLEAN
:
2134 $bool = strtolower($literal->value
) == 'true' ?
true : false;
2135 $boolVal = $this->conn
->getDatabasePlatform()->convertBooleans($bool);
2139 case AST\Literal
::NUMERIC:
2140 return $literal->value
;
2143 throw QueryException
::invalidLiteral($literal);
2150 public function walkBetweenExpression($betweenExpr)
2152 $sql = $this->walkArithmeticExpression($betweenExpr->expression
);
2154 if ($betweenExpr->not
) {
2158 $sql .= ' BETWEEN ' . $this->walkArithmeticExpression($betweenExpr->leftBetweenExpression
)
2159 . ' AND ' . $this->walkArithmeticExpression($betweenExpr->rightBetweenExpression
);
2167 public function walkLikeExpression($likeExpr)
2169 $stringExpr = $likeExpr->stringExpression
;
2170 $leftExpr = (is_string($stringExpr) && isset($this->queryComponents
[$stringExpr]['resultVariable']))
2171 ?
$this->walkResultVariable($stringExpr)
2172 : $stringExpr->dispatch($this);
2174 $sql = $leftExpr . ($likeExpr->not ?
' NOT' : '') . ' LIKE ';
2176 if ($likeExpr->stringPattern
instanceof AST\InputParameter
) {
2177 $sql .= $this->walkInputParameter($likeExpr->stringPattern
);
2178 } elseif ($likeExpr->stringPattern
instanceof AST\Functions\FunctionNode
) {
2179 $sql .= $this->walkFunction($likeExpr->stringPattern
);
2180 } elseif ($likeExpr->stringPattern
instanceof AST\PathExpression
) {
2181 $sql .= $this->walkPathExpression($likeExpr->stringPattern
);
2183 $sql .= $this->walkLiteral($likeExpr->stringPattern
);
2186 if ($likeExpr->escapeChar
) {
2187 $sql .= ' ESCAPE ' . $this->walkLiteral($likeExpr->escapeChar
);
2196 public function walkStateFieldPathExpression($stateFieldPathExpression)
2198 return $this->walkPathExpression($stateFieldPathExpression);
2204 public function walkComparisonExpression($compExpr)
2206 $leftExpr = $compExpr->leftExpression
;
2207 $rightExpr = $compExpr->rightExpression
;
2210 $sql .= ($leftExpr instanceof AST\Node
)
2211 ?
$leftExpr->dispatch($this)
2212 : (is_numeric($leftExpr) ?
$leftExpr : $this->conn
->quote($leftExpr));
2214 $sql .= ' ' . $compExpr->operator
. ' ';
2216 $sql .= ($rightExpr instanceof AST\Node
)
2217 ?
$rightExpr->dispatch($this)
2218 : (is_numeric($rightExpr) ?
$rightExpr : $this->conn
->quote($rightExpr));
2226 public function walkInputParameter($inputParam)
2228 $this->parserResult
->addParameterMapping($inputParam->name
, $this->sqlParamIndex++
);
2230 $parameter = $this->query
->getParameter($inputParam->name
);
2232 if ($parameter && Type
::hasType($type = $parameter->getType())) {
2233 return Type
::getType($type)->convertToDatabaseValueSQL('?', $this->platform
);
2242 public function walkArithmeticExpression($arithmeticExpr)
2244 return ($arithmeticExpr->isSimpleArithmeticExpression())
2245 ?
$this->walkSimpleArithmeticExpression($arithmeticExpr->simpleArithmeticExpression
)
2246 : '(' . $this->walkSubselect($arithmeticExpr->subselect
) . ')';
2252 public function walkSimpleArithmeticExpression($simpleArithmeticExpr)
2254 if ( ! ($simpleArithmeticExpr instanceof AST\SimpleArithmeticExpression
)) {
2255 return $this->walkArithmeticTerm($simpleArithmeticExpr);
2258 return implode(' ', array_map(array($this, 'walkArithmeticTerm'), $simpleArithmeticExpr->arithmeticTerms
));
2264 public function walkArithmeticTerm($term)
2266 if (is_string($term)) {
2267 return (isset($this->queryComponents
[$term]))
2268 ?
$this->walkResultVariable($this->queryComponents
[$term]['token']['value'])
2272 // Phase 2 AST optimization: Skip processing of ArithmeticTerm
2273 // if only one ArithmeticFactor is defined
2274 if ( ! ($term instanceof AST\ArithmeticTerm
)) {
2275 return $this->walkArithmeticFactor($term);
2278 return implode(' ', array_map(array($this, 'walkArithmeticFactor'), $term->arithmeticFactors
));
2284 public function walkArithmeticFactor($factor)
2286 if (is_string($factor)) {
2287 return (isset($this->queryComponents
[$factor]))
2288 ?
$this->walkResultVariable($this->queryComponents
[$factor]['token']['value'])
2292 // Phase 2 AST optimization: Skip processing of ArithmeticFactor
2293 // if only one ArithmeticPrimary is defined
2294 if ( ! ($factor instanceof AST\ArithmeticFactor
)) {
2295 return $this->walkArithmeticPrimary($factor);
2298 $sign = $factor->isNegativeSigned() ?
'-' : ($factor->isPositiveSigned() ?
'+' : '');
2300 return $sign . $this->walkArithmeticPrimary($factor->arithmeticPrimary
);
2304 * Walks down an ArithmeticPrimary that represents an AST node, thereby generating the appropriate SQL.
2306 * @param mixed $primary
2308 * @return string The SQL.
2310 public function walkArithmeticPrimary($primary)
2312 if ($primary instanceof AST\SimpleArithmeticExpression
) {
2313 return '(' . $this->walkSimpleArithmeticExpression($primary) . ')';
2316 if ($primary instanceof AST\Node
) {
2317 return $primary->dispatch($this);
2320 return $this->walkEntityIdentificationVariable($primary);
2326 public function walkStringPrimary($stringPrimary)
2328 return (is_string($stringPrimary))
2329 ?
$this->conn
->quote($stringPrimary)
2330 : $stringPrimary->dispatch($this);
2336 public function walkResultVariable($resultVariable)
2338 $resultAlias = $this->scalarResultAliasMap
[$resultVariable];
2340 if (is_array($resultAlias)) {
2341 return implode(', ', $resultAlias);
2344 return $resultAlias;