composer package updates
[openemr.git] / vendor / doctrine / orm / lib / Doctrine / ORM / Query / SqlWalker.php
blobe32e676f016d90ec8bc27d24665795d5852a4ada
1 <?php
2 /*
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;
29 /**
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>
38 * @since 2.0
39 * @todo Rename: SQLWalker
41 class SqlWalker implements TreeWalker
43 /**
44 * @var string
46 const HINT_DISTINCT = 'doctrine.distinct';
48 /**
49 * @var ResultSetMapping
51 private $rsm;
53 /**
54 * Counter for generating unique column aliases.
56 * @var integer
58 private $aliasCounter = 0;
60 /**
61 * Counter for generating unique table aliases.
63 * @var integer
65 private $tableAliasCounter = 0;
67 /**
68 * Counter for generating unique scalar result.
70 * @var integer
72 private $scalarResultCounter = 1;
74 /**
75 * Counter for generating unique parameter indexes.
77 * @var integer
79 private $sqlParamIndex = 0;
81 /**
82 * Counter for generating indexes.
84 * @var integer
86 private $newObjectCounter = 0;
88 /**
89 * @var ParserResult
91 private $parserResult;
93 /**
94 * @var \Doctrine\ORM\EntityManager
96 private $em;
98 /**
99 * @var \Doctrine\DBAL\Connection
101 private $conn;
104 * @var \Doctrine\ORM\AbstractQuery
106 private $query;
109 * @var array
111 private $tableAliasMap = array();
114 * Map from result variable names to their SQL column alias names.
116 * @var array
118 private $scalarResultAliasMap = array();
121 * Map from Table-Alias + Column-Name to OrderBy-Direction.
123 * @var array
125 private $orderedColumnsMap = array();
128 * Map from DQL-Alias + Field-Name to SQL Column Alias.
130 * @var array
132 private $scalarFields = array();
135 * Map of all components/classes that appear in the DQL query.
137 * @var array
139 private $queryComponents;
142 * A list of classes that appear in non-scalar SelectExpressions.
144 * @var array
146 private $selectedClasses = array();
149 * The DQL alias of the root class of the currently traversed query.
151 * @var array
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.
159 * @var boolean
161 private $useSqlTableAliases = true;
164 * The database platform abstraction.
166 * @var \Doctrine\DBAL\Platforms\AbstractPlatform
168 private $platform;
171 * The quote strategy.
173 * @var \Doctrine\ORM\Mapping\QuoteStrategy
175 private $quoteStrategy;
178 * {@inheritDoc}
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.
195 * @return Query.
197 public function getQuery()
199 return $this->query;
203 * Gets the Connection used by the walker.
205 * @return \Doctrine\DBAL\Connection
207 public function getConnection()
209 return $this->conn;
213 * Gets the EntityManager used by the walker.
215 * @return \Doctrine\ORM\EntityManager
217 public function getEntityManager()
219 return $this->em;
223 * Gets the information about a single query component.
225 * @param string $dqlAlias The DQL alias.
227 * @return array
229 public function getQueryComponent($dqlAlias)
231 return $this->queryComponents[$dqlAlias];
235 * {@inheritdoc}
237 public function getQueryComponents()
239 return $this->queryComponents;
243 * {@inheritdoc}
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;
257 * {@inheritdoc}
259 public function getExecutor($AST)
261 switch (true) {
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);
276 default:
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
309 * @return string
311 public function setSQLTableAlias($tableName, $alias, $dqlAlias = '')
313 $tableName .= ($dqlAlias) ? '@[' . $dqlAlias . ']' : '';
315 $this->tableAliasMap[$tableName] = $alias;
317 return $alias;
321 * Gets an SQL column alias for a column name.
323 * @param string $columnName
325 * @return string
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)
343 $sql = '';
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 ';
356 $sqlParts = array();
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)) {
372 return $sql;
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 ';
382 $sqlParts = array();
384 foreach ($this->quoteStrategy->getIdentifierColumnNames($subClass, $this->platform) as $columnName) {
385 $sqlParts[] = $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
388 $sql .= implode(' AND ', $sqlParts);
391 return $sql;
395 * @return string
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'])) {
406 continue;
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])) {
421 continue;
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.
437 * @return string
439 private function _generateDiscriminatorColumnConditionSQL(array $dqlAliases)
441 $sqlParts = array();
443 foreach ($dqlAliases as $dqlAlias) {
444 $class = $this->queryComponents[$dqlAlias]['metadata'];
446 if ( ! $class->isInheritanceTypeSingleTable()) continue;
448 $conn = $this->em->getConnection();
449 $values = array();
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()) {
479 return '';
482 switch($targetEntity->inheritanceType) {
483 case ClassMetadata::INHERITANCE_TYPE_NONE:
484 break;
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) {
489 return '';
491 break;
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);
496 break;
497 default:
498 //@todo: throw exception?
499 return '';
500 break;
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);
514 * {@inheritdoc}
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) {
546 return $sql;
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);
567 return $sql;
571 * {@inheritdoc}
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);
583 * {@inheritdoc}
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
600 * @return string
602 public function walkEntityIdentificationVariable($identVariable)
604 $class = $this->queryComponents[$identVariable]['metadata'];
605 $tableAlias = $this->getSQLTableAlias($class->getTableName(), $identVariable);
606 $sqlParts = array();
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'];
627 if (
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);
638 * {@inheritdoc}
640 public function walkPathExpression($pathExpr)
642 $sql = '';
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);
655 break;
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']);
684 break;
686 default:
687 throw QueryException::invalidPathExpression($pathExpr);
690 return $sql;
694 * {@inheritdoc}
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);
719 } else {
720 $this->rsm->addJoinedEntityResult(
721 $class->name,
722 $dqlAlias,
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) {
743 continue;
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)) {
749 continue;
750 } else if ( !$addMetaColumns && !isset($assoc['id'])) {
751 continue;
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);
762 $type = null;
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) {
776 continue;
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);
803 return $sql;
807 * {@inheritdoc}
809 public function walkFromClause($fromClause)
811 $identificationVarDecls = $fromClause->identificationVariableDeclarations;
812 $sqlParts = array();
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
826 * @return string
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);
840 return $sql;
844 * Walks down a IndexBy AST node.
846 * @param AST\IndexBy $indexBy
848 * @return void
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]);
859 return;
862 $this->rsm->addIndexBy($alias, $field);
866 * Walks down a RangeVariableDeclaration AST node, thereby generating the appropriate SQL.
868 * @param AST\RangeVariableDeclaration $rangeVariableDeclaration
870 * @return string
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
883 * @return string
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()) {
901 return $sql;
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
920 * @return string
922 * @throws QueryException
924 public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joinType = AST\Join::JOIN_TYPE_INNER, $condExpr = null)
926 $sql = '';
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.
954 switch (true) {
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;
965 continue;
968 $conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $targetTableAlias . '.' . $quotedSourceColumn;
971 // Apply remaining inheritance restrictions
972 $discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($joinedDqlAlias));
974 if ($discrSql) {
975 $conditions[] = $discrSql;
978 // Apply the filters
979 $filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias);
981 if ($filterExpr) {
982 $conditions[] = $filterExpr;
985 $targetTableJoin = array(
986 'table' => $targetTableName . ' ' . $targetTableAlias,
987 'condition' => implode(' AND ', $conditions),
989 break;
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));
1029 if ($discrSql) {
1030 $conditions[] = $discrSql;
1033 // Apply the filters
1034 $filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias);
1036 if ($filterExpr) {
1037 $conditions[] = $filterExpr;
1040 $targetTableJoin = array(
1041 'table' => $targetTableName . ' ' . $targetTableAlias,
1042 'condition' => implode(' AND ', $conditions),
1044 break;
1046 default:
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'];
1058 } else {
1059 $sql .= $targetTableJoin['table'] . ' ON ' . $targetTableJoin['condition'] . $ctiJoins;
1061 } else {
1062 $sql .= $targetTableJoin['table'] . ' ON ' . $targetTableJoin['condition'];
1065 if ($withCondition) {
1066 $sql .= ' AND ' . $withCondition;
1069 // Apply the indexes
1070 if ($indexBy) {
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']);
1077 return $sql;
1081 * {@inheritdoc}
1083 public function walkFunction($function)
1085 return $function->getSql($this);
1089 * {@inheritdoc}
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);
1103 * {@inheritdoc}
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;
1123 * {@inheritdoc}
1125 public function walkHavingClause($havingClause)
1127 return ' HAVING ' . $this->walkConditionalExpression($havingClause->conditionalExpression);
1131 * {@inheritdoc}
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)
1139 ? ' LEFT JOIN '
1140 : ' INNER JOIN ';
1142 switch (true) {
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)
1150 ? ' AND '
1151 : ' ON ';
1153 $sql .= $this->generateRangeVariableDeclarationSQL($joinDeclaration, !$isUnconditionalJoin);
1155 $conditions = array($condition);
1157 // Apply remaining inheritance restrictions
1158 $discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($dqlAlias));
1160 if ($discrSql) {
1161 $conditions[] = $discrSql;
1164 // Apply the filters
1165 $filterExpr = $this->generateFilterConditionSQL($class, $tableAlias);
1167 if ($filterExpr) {
1168 $conditions[] = $filterExpr;
1171 $sql .= $condExprConjunction . implode(' AND ', $conditions);
1172 break;
1174 case ($joinDeclaration instanceof AST\JoinAssociationDeclaration):
1175 $sql .= $this->walkJoinAssociationDeclaration($joinDeclaration, $joinType, $join->conditionalExpression);
1176 break;
1179 return $sql;
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)
1191 switch (true) {
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);
1204 default:
1205 return '';
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)
1218 $sql = 'COALESCE(';
1220 $scalarExpressions = array();
1222 foreach ($coalesceExpression->scalarExpressions as $scalarExpression) {
1223 $scalarExpressions[] = $this->walkSimpleArithmeticExpression($scalarExpression);
1226 $sql .= implode(', ', $scalarExpressions) . ')';
1228 return $sql;
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)
1260 $sql = 'CASE';
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';
1269 return $sql;
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';
1290 return $sql;
1294 * {@inheritdoc}
1296 public function walkSelectExpression($selectExpression)
1298 $sql = '';
1299 $expr = $selectExpression->expression;
1300 $hidden = $selectExpression->hiddenAliasResultVariable;
1302 switch (true) {
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;
1335 if ( ! $hidden) {
1336 $this->rsm->addScalarResult($columnAlias, $resultAlias, $fieldType);
1337 $this->scalarFields[$dqlAlias][$fieldName] = $columnAlias;
1339 break;
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;
1359 if ( ! $hidden) {
1360 // We cannot resolve field type here; assume 'string'.
1361 $this->rsm->addScalarResult($columnAlias, $resultAlias, 'string');
1363 break;
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;
1373 if ( ! $hidden) {
1374 // We cannot resolve field type here; assume 'string'.
1375 $this->rsm->addScalarResult($columnAlias, $resultAlias, 'string');
1377 break;
1379 case ($expr instanceof AST\NewObjectExpression):
1380 $sql .= $this->walkNewObject($expr,$selectExpression->fieldIdentificationVariable);
1381 break;
1383 default:
1384 // IdentificationVariable or PartialObjectExpression
1385 if ($expr instanceof AST\PartialObjectExpression) {
1386 $dqlAlias = $expr->identificationVariable;
1387 $partialFieldSet = $expr->partialFieldSet;
1388 } else {
1389 $dqlAlias = $expr;
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(
1399 'class' => $class,
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)) {
1410 continue;
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)) {
1446 continue;
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);
1471 return $sql;
1475 * {@inheritdoc}
1477 public function walkQuantifiedExpression($qExpr)
1479 return ' ' . strtoupper($qExpr->type) . '(' . $this->walkSubselect($qExpr->subselect) . ')';
1483 * {@inheritdoc}
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;
1504 return $sql;
1508 * {@inheritdoc}
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);
1523 * {@inheritdoc}
1525 public function walkSimpleSelectClause($simpleSelectClause)
1527 return 'SELECT' . ($simpleSelectClause->isDistinct ? ' DISTINCT' : '')
1528 . $this->walkSimpleSelectExpression($simpleSelectClause->simpleSelectExpression);
1532 * @param \Doctrine\ORM\Query\AST\ParenthesisExpression $parenthesisExpression
1534 * @return string.
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';
1556 switch (true) {
1557 case ($e instanceof AST\NewObjectExpression):
1558 $sqlSelectExpressions[] = $e->dispatch($this);
1559 break;
1561 case ($e instanceof AST\Subselect):
1562 $sqlSelectExpressions[] = '(' . $e->dispatch($this) . ') AS ' . $columnAlias;
1563 break;
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;
1573 break;
1575 case ($e instanceof AST\Literal):
1576 switch ($e->type) {
1577 case AST\Literal::BOOLEAN:
1578 $fieldType = 'boolean';
1579 break;
1581 case AST\Literal::NUMERIC:
1582 $fieldType = is_float($e->value) ? 'float' : 'integer';
1583 break;
1586 $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' . $columnAlias;
1587 break;
1589 default:
1590 $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' . $columnAlias;
1591 break;
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);
1608 * {@inheritdoc}
1610 public function walkSimpleSelectExpression($simpleSelectExpression)
1612 $expr = $simpleSelectExpression->expression;
1613 $sql = ' ';
1615 switch (true) {
1616 case ($expr instanceof AST\PathExpression):
1617 $sql .= $this->walkPathExpression($expr);
1618 break;
1620 case ($expr instanceof AST\AggregateExpression):
1621 $alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
1623 $sql .= $this->walkAggregateExpression($expr) . ' AS dctrn__' . $alias;
1624 break;
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;
1633 break;
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;
1650 break;
1652 case ($expr instanceof AST\ParenthesisExpression):
1653 $sql .= $this->walkParenthesisExpression($expr);
1654 break;
1656 default: // IdentificationVariable
1657 $sql .= $this->walkEntityIdentificationVariable($expr);
1658 break;
1661 return $sql;
1665 * {@inheritdoc}
1667 public function walkAggregateExpression($aggExpression)
1669 return $aggExpression->functionName . '(' . ($aggExpression->isDistinct ? 'DISTINCT ' : '')
1670 . $this->walkSimpleArithmeticExpression($aggExpression->pathExpression) . ')';
1674 * {@inheritdoc}
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);
1688 * {@inheritdoc}
1690 public function walkGroupByItem($groupByItem)
1692 // StateFieldPathExpression
1693 if ( ! is_string($groupByItem)) {
1694 return $this->walkPathExpression($groupByItem);
1697 // ResultVariable
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);
1735 * {@inheritdoc}
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;
1746 return $sql;
1750 * {@inheritdoc}
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));
1763 return $sql;
1767 * {@inheritdoc}
1769 public function walkUpdateItem($updateItem)
1771 $useTableAliasesBefore = $this->useSqlTableAliases;
1772 $this->useSqlTableAliases = false;
1774 $sql = $this->walkPathExpression($updateItem->pathExpression) . ' = ';
1775 $newValue = $updateItem->newValue;
1777 switch (true) {
1778 case ($newValue instanceof AST\Node):
1779 $sql .= $newValue->dispatch($this);
1780 break;
1782 case ($newValue === null):
1783 $sql .= 'NULL';
1784 break;
1786 default:
1787 $sql .= $this->conn->quote($newValue);
1788 break;
1791 $this->useSqlTableAliases = $useTableAliasesBefore;
1793 return $sql;
1797 * {@inheritdoc}
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)) {
1816 if ($condSql) {
1817 $condSql = '(' . $condSql . ') AND ';
1820 $condSql .= implode(' AND ', $filterClauses);
1824 if ($condSql) {
1825 return ' WHERE ' . (( ! $discrSql) ? $condSql : '(' . $condSql . ') AND ' . $discrSql);
1828 if ($discrSql) {
1829 return ' WHERE ' . $discrSql;
1832 return '';
1836 * {@inheritdoc}
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));
1850 * {@inheritdoc}
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));
1864 * {@inheritdoc}
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);
1876 * {@inheritdoc}
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) . ')';
1892 * {@inheritdoc}
1894 public function walkExistsExpression($existsExpr)
1896 $sql = ($existsExpr->not) ? 'NOT ' : '';
1898 $sql .= 'EXISTS (' . $this->walkSubselect($existsExpr->subselect) . ')';
1900 return $sql;
1904 * {@inheritdoc}
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'];
1919 switch (true) {
1920 // InputParameter
1921 case ($entityExpr instanceof AST\InputParameter):
1922 $dqlParamKey = $entityExpr->name;
1923 $entitySql = '?';
1924 break;
1926 // SingleValuedAssociationPathExpression | IdentificationVariable
1927 case ($entityExpr instanceof AST\PathExpression):
1928 $entitySql = $this->walkPathExpression($entityExpr);
1929 break;
1931 default:
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 ';
1977 // join conditions
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);
1988 $sql .= ' WHERE ';
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);
2010 return $sql . ')';
2014 * {@inheritdoc}
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');
2025 * {@inheritdoc}
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;
2046 * {@inheritdoc}
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));
2056 $sql .= ')';
2058 return $sql;
2062 * {@inheritdoc}
2064 public function walkInstanceOfExpression($instanceOfExpr)
2066 $sql = '';
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);
2089 continue;
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) . ')';
2111 return $sql;
2115 * {@inheritdoc}
2117 public function walkInParameter($inParam)
2119 return $inParam instanceof AST\InputParameter
2120 ? $this->walkInputParameter($inParam)
2121 : $this->walkLiteral($inParam);
2125 * {@inheritdoc}
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);
2137 return $boolVal;
2139 case AST\Literal::NUMERIC:
2140 return $literal->value;
2142 default:
2143 throw QueryException::invalidLiteral($literal);
2148 * {@inheritdoc}
2150 public function walkBetweenExpression($betweenExpr)
2152 $sql = $this->walkArithmeticExpression($betweenExpr->expression);
2154 if ($betweenExpr->not) {
2155 $sql .= ' NOT';
2158 $sql .= ' BETWEEN ' . $this->walkArithmeticExpression($betweenExpr->leftBetweenExpression)
2159 . ' AND ' . $this->walkArithmeticExpression($betweenExpr->rightBetweenExpression);
2161 return $sql;
2165 * {@inheritdoc}
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);
2182 } else {
2183 $sql .= $this->walkLiteral($likeExpr->stringPattern);
2186 if ($likeExpr->escapeChar) {
2187 $sql .= ' ESCAPE ' . $this->walkLiteral($likeExpr->escapeChar);
2190 return $sql;
2194 * {@inheritdoc}
2196 public function walkStateFieldPathExpression($stateFieldPathExpression)
2198 return $this->walkPathExpression($stateFieldPathExpression);
2202 * {@inheritdoc}
2204 public function walkComparisonExpression($compExpr)
2206 $leftExpr = $compExpr->leftExpression;
2207 $rightExpr = $compExpr->rightExpression;
2208 $sql = '';
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));
2220 return $sql;
2224 * {@inheritdoc}
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);
2236 return '?';
2240 * {@inheritdoc}
2242 public function walkArithmeticExpression($arithmeticExpr)
2244 return ($arithmeticExpr->isSimpleArithmeticExpression())
2245 ? $this->walkSimpleArithmeticExpression($arithmeticExpr->simpleArithmeticExpression)
2246 : '(' . $this->walkSubselect($arithmeticExpr->subselect) . ')';
2250 * {@inheritdoc}
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));
2262 * {@inheritdoc}
2264 public function walkArithmeticTerm($term)
2266 if (is_string($term)) {
2267 return (isset($this->queryComponents[$term]))
2268 ? $this->walkResultVariable($this->queryComponents[$term]['token']['value'])
2269 : $term;
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));
2282 * {@inheritdoc}
2284 public function walkArithmeticFactor($factor)
2286 if (is_string($factor)) {
2287 return (isset($this->queryComponents[$factor]))
2288 ? $this->walkResultVariable($this->queryComponents[$factor]['token']['value'])
2289 : $factor;
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);
2324 * {@inheritdoc}
2326 public function walkStringPrimary($stringPrimary)
2328 return (is_string($stringPrimary))
2329 ? $this->conn->quote($stringPrimary)
2330 : $stringPrimary->dispatch($this);
2334 * {@inheritdoc}
2336 public function walkResultVariable($resultVariable)
2338 $resultAlias = $this->scalarResultAliasMap[$resultVariable];
2340 if (is_array($resultAlias)) {
2341 return implode(', ', $resultAlias);
2344 return $resultAlias;