vendor/doctrine/orm/lib/Doctrine/ORM/Query/SqlWalker.php line 1795

Open in your IDE?
  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.
  14.  *
  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>.
  18.  */
  19. namespace Doctrine\ORM\Query;
  20. use Doctrine\DBAL\LockMode;
  21. use Doctrine\DBAL\Types\Type;
  22. use Doctrine\ORM\Mapping\ClassMetadata;
  23. use Doctrine\ORM\Mapping\ClassMetadataInfo;
  24. use Doctrine\ORM\OptimisticLockException;
  25. use Doctrine\ORM\Query;
  26. use Doctrine\ORM\Utility\HierarchyDiscriminatorResolver;
  27. use Doctrine\ORM\Utility\PersisterHelper;
  28. use function trim;
  29. /**
  30.  * The SqlWalker is a TreeWalker that walks over a DQL AST and constructs
  31.  * the corresponding SQL.
  32.  *
  33.  * @author Guilherme Blanco <[email protected]>
  34.  * @author Roman Borschel <[email protected]>
  35.  * @author Benjamin Eberlei <[email protected]>
  36.  * @author Alexander <[email protected]>
  37.  * @author Fabio B. Silva <[email protected]>
  38.  * @since  2.0
  39.  */
  40. class SqlWalker implements TreeWalker
  41. {
  42.     /**
  43.      * @var string
  44.      */
  45.     const HINT_DISTINCT 'doctrine.distinct';
  46.     /**
  47.      * Used to mark a query as containing a PARTIAL expression, which needs to be known by SLC.
  48.      */
  49.     public const HINT_PARTIAL 'doctrine.partial';
  50.     /**
  51.      * @var ResultSetMapping
  52.      */
  53.     private $rsm;
  54.     /**
  55.      * Counter for generating unique column aliases.
  56.      *
  57.      * @var integer
  58.      */
  59.     private $aliasCounter 0;
  60.     /**
  61.      * Counter for generating unique table aliases.
  62.      *
  63.      * @var integer
  64.      */
  65.     private $tableAliasCounter 0;
  66.     /**
  67.      * Counter for generating unique scalar result.
  68.      *
  69.      * @var integer
  70.      */
  71.     private $scalarResultCounter 1;
  72.     /**
  73.      * Counter for generating unique parameter indexes.
  74.      *
  75.      * @var integer
  76.      */
  77.     private $sqlParamIndex 0;
  78.     /**
  79.      * Counter for generating indexes.
  80.      *
  81.      * @var integer
  82.      */
  83.     private $newObjectCounter 0;
  84.     /**
  85.      * @var ParserResult
  86.      */
  87.     private $parserResult;
  88.     /**
  89.      * @var \Doctrine\ORM\EntityManager
  90.      */
  91.     private $em;
  92.     /**
  93.      * @var \Doctrine\DBAL\Connection
  94.      */
  95.     private $conn;
  96.     /**
  97.      * @var \Doctrine\ORM\AbstractQuery
  98.      */
  99.     private $query;
  100.     /**
  101.      * @var array
  102.      */
  103.     private $tableAliasMap = [];
  104.     /**
  105.      * Map from result variable names to their SQL column alias names.
  106.      *
  107.      * @var array
  108.      */
  109.     private $scalarResultAliasMap = [];
  110.     /**
  111.      * Map from Table-Alias + Column-Name to OrderBy-Direction.
  112.      *
  113.      * @var array
  114.      */
  115.     private $orderedColumnsMap = [];
  116.     /**
  117.      * Map from DQL-Alias + Field-Name to SQL Column Alias.
  118.      *
  119.      * @var array
  120.      */
  121.     private $scalarFields = [];
  122.     /**
  123.      * Map of all components/classes that appear in the DQL query.
  124.      *
  125.      * @var array
  126.      *
  127.      * @psalm-var array<string, array{metadata: ClassMetadata}>
  128.      */
  129.     private $queryComponents;
  130.     /**
  131.      * A list of classes that appear in non-scalar SelectExpressions.
  132.      *
  133.      * @var array
  134.      */
  135.     private $selectedClasses = [];
  136.     /**
  137.      * The DQL alias of the root class of the currently traversed query.
  138.      *
  139.      * @var array
  140.      */
  141.     private $rootAliases = [];
  142.     /**
  143.      * Flag that indicates whether to generate SQL table aliases in the SQL.
  144.      * These should only be generated for SELECT queries, not for UPDATE/DELETE.
  145.      *
  146.      * @var boolean
  147.      */
  148.     private $useSqlTableAliases true;
  149.     /**
  150.      * The database platform abstraction.
  151.      *
  152.      * @var \Doctrine\DBAL\Platforms\AbstractPlatform
  153.      */
  154.     private $platform;
  155.     /**
  156.      * The quote strategy.
  157.      *
  158.      * @var \Doctrine\ORM\Mapping\QuoteStrategy
  159.      */
  160.     private $quoteStrategy;
  161.     /**
  162.      * {@inheritDoc}
  163.      */
  164.     public function __construct($query$parserResult, array $queryComponents)
  165.     {
  166.         $this->query            $query;
  167.         $this->parserResult     $parserResult;
  168.         $this->queryComponents  $queryComponents;
  169.         $this->rsm              $parserResult->getResultSetMapping();
  170.         $this->em               $query->getEntityManager();
  171.         $this->conn             $this->em->getConnection();
  172.         $this->platform         $this->conn->getDatabasePlatform();
  173.         $this->quoteStrategy    $this->em->getConfiguration()->getQuoteStrategy();
  174.     }
  175.     /**
  176.      * Gets the Query instance used by the walker.
  177.      *
  178.      * @return Query
  179.      */
  180.     public function getQuery()
  181.     {
  182.         return $this->query;
  183.     }
  184.     /**
  185.      * Gets the Connection used by the walker.
  186.      *
  187.      * @return \Doctrine\DBAL\Connection
  188.      */
  189.     public function getConnection()
  190.     {
  191.         return $this->conn;
  192.     }
  193.     /**
  194.      * Gets the EntityManager used by the walker.
  195.      *
  196.      * @return \Doctrine\ORM\EntityManager
  197.      */
  198.     public function getEntityManager()
  199.     {
  200.         return $this->em;
  201.     }
  202.     /**
  203.      * Gets the information about a single query component.
  204.      *
  205.      * @param string $dqlAlias The DQL alias.
  206.      *
  207.      * @return array
  208.      *
  209.      * @psalm-return array{metadata: ClassMetadata}
  210.      */
  211.     public function getQueryComponent($dqlAlias)
  212.     {
  213.         return $this->queryComponents[$dqlAlias];
  214.     }
  215.     /**
  216.      * {@inheritdoc}
  217.      */
  218.     public function getQueryComponents()
  219.     {
  220.         return $this->queryComponents;
  221.     }
  222.     /**
  223.      * {@inheritdoc}
  224.      */
  225.     public function setQueryComponent($dqlAlias, array $queryComponent)
  226.     {
  227.         $requiredKeys = ['metadata''parent''relation''map''nestingLevel''token'];
  228.         if (array_diff($requiredKeysarray_keys($queryComponent))) {
  229.             throw QueryException::invalidQueryComponent($dqlAlias);
  230.         }
  231.         $this->queryComponents[$dqlAlias] = $queryComponent;
  232.     }
  233.     /**
  234.      * {@inheritdoc}
  235.      */
  236.     public function getExecutor($AST)
  237.     {
  238.         switch (true) {
  239.             case ($AST instanceof AST\DeleteStatement):
  240.                 $primaryClass $this->em->getClassMetadata($AST->deleteClause->abstractSchemaName);
  241.                 return ($primaryClass->isInheritanceTypeJoined())
  242.                     ? new Exec\MultiTableDeleteExecutor($AST$this)
  243.                     : new Exec\SingleTableDeleteUpdateExecutor($AST$this);
  244.             case ($AST instanceof AST\UpdateStatement):
  245.                 $primaryClass $this->em->getClassMetadata($AST->updateClause->abstractSchemaName);
  246.                 return ($primaryClass->isInheritanceTypeJoined())
  247.                     ? new Exec\MultiTableUpdateExecutor($AST$this)
  248.                     : new Exec\SingleTableDeleteUpdateExecutor($AST$this);
  249.             default:
  250.                 return new Exec\SingleSelectExecutor($AST$this);
  251.         }
  252.     }
  253.     /**
  254.      * Generates a unique, short SQL table alias.
  255.      *
  256.      * @param string $tableName Table name
  257.      * @param string $dqlAlias  The DQL alias.
  258.      *
  259.      * @return string Generated table alias.
  260.      */
  261.     public function getSQLTableAlias($tableName$dqlAlias '')
  262.     {
  263.         $tableName .= ($dqlAlias) ? '@[' $dqlAlias ']' '';
  264.         if ( ! isset($this->tableAliasMap[$tableName])) {
  265.             $this->tableAliasMap[$tableName] = (preg_match('/[a-z]/i'$tableName[0]) ? strtolower($tableName[0]) : 't')
  266.                 . $this->tableAliasCounter++ . '_';
  267.         }
  268.         return $this->tableAliasMap[$tableName];
  269.     }
  270.     /**
  271.      * Forces the SqlWalker to use a specific alias for a table name, rather than
  272.      * generating an alias on its own.
  273.      *
  274.      * @param string $tableName
  275.      * @param string $alias
  276.      * @param string $dqlAlias
  277.      *
  278.      * @return string
  279.      */
  280.     public function setSQLTableAlias($tableName$alias$dqlAlias '')
  281.     {
  282.         $tableName .= ($dqlAlias) ? '@[' $dqlAlias ']' '';
  283.         $this->tableAliasMap[$tableName] = $alias;
  284.         return $alias;
  285.     }
  286.     /**
  287.      * Gets an SQL column alias for a column name.
  288.      *
  289.      * @param string $columnName
  290.      *
  291.      * @return string
  292.      */
  293.     public function getSQLColumnAlias($columnName)
  294.     {
  295.         return $this->quoteStrategy->getColumnAlias($columnName$this->aliasCounter++, $this->platform);
  296.     }
  297.     /**
  298.      * Generates the SQL JOINs that are necessary for Class Table Inheritance
  299.      * for the given class.
  300.      *
  301.      * @param ClassMetadata $class    The class for which to generate the joins.
  302.      * @param string        $dqlAlias The DQL alias of the class.
  303.      *
  304.      * @return string The SQL.
  305.      */
  306.     private function _generateClassTableInheritanceJoins($class$dqlAlias)
  307.     {
  308.         $sql '';
  309.         $baseTableAlias $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
  310.         // INNER JOIN parent class tables
  311.         foreach ($class->parentClasses as $parentClassName) {
  312.             $parentClass $this->em->getClassMetadata($parentClassName);
  313.             $tableAlias  $this->getSQLTableAlias($parentClass->getTableName(), $dqlAlias);
  314.             // If this is a joined association we must use left joins to preserve the correct result.
  315.             $sql .= isset($this->queryComponents[$dqlAlias]['relation']) ? ' LEFT ' ' INNER ';
  316.             $sql .= 'JOIN ' $this->quoteStrategy->getTableName($parentClass$this->platform) . ' ' $tableAlias ' ON ';
  317.             $sqlParts = [];
  318.             foreach ($this->quoteStrategy->getIdentifierColumnNames($class$this->platform) as $columnName) {
  319.                 $sqlParts[] = $baseTableAlias '.' $columnName ' = ' $tableAlias '.' $columnName;
  320.             }
  321.             // Add filters on the root class
  322.             if ($filterSql $this->generateFilterConditionSQL($parentClass$tableAlias)) {
  323.                 $sqlParts[] = $filterSql;
  324.             }
  325.             $sql .= implode(' AND '$sqlParts);
  326.         }
  327.         // Ignore subclassing inclusion if partial objects is disallowed
  328.         if ($this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {
  329.             return $sql;
  330.         }
  331.         // LEFT JOIN child class tables
  332.         foreach ($class->subClasses as $subClassName) {
  333.             $subClass   $this->em->getClassMetadata($subClassName);
  334.             $tableAlias $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
  335.             $sql .= ' LEFT JOIN ' $this->quoteStrategy->getTableName($subClass$this->platform) . ' ' $tableAlias ' ON ';
  336.             $sqlParts = [];
  337.             foreach ($this->quoteStrategy->getIdentifierColumnNames($subClass$this->platform) as $columnName) {
  338.                 $sqlParts[] = $baseTableAlias '.' $columnName ' = ' $tableAlias '.' $columnName;
  339.             }
  340.             $sql .= implode(' AND '$sqlParts);
  341.         }
  342.         return $sql;
  343.     }
  344.     /**
  345.      * @return string
  346.      */
  347.     private function _generateOrderedCollectionOrderByItems()
  348.     {
  349.         $orderedColumns = [];
  350.         foreach ($this->selectedClasses as $selectedClass) {
  351.             $dqlAlias  $selectedClass['dqlAlias'];
  352.             $qComp     $this->queryComponents[$dqlAlias];
  353.             if ( ! isset($qComp['relation']['orderBy'])) {
  354.                 continue;
  355.             }
  356.             $persister $this->em->getUnitOfWork()->getEntityPersister($qComp['metadata']->name);
  357.             foreach ($qComp['relation']['orderBy'] as $fieldName => $orientation) {
  358.                 $columnName $this->quoteStrategy->getColumnName($fieldName$qComp['metadata'], $this->platform);
  359.                 $tableName  = ($qComp['metadata']->isInheritanceTypeJoined())
  360.                     ? $persister->getOwningTable($fieldName)
  361.                     : $qComp['metadata']->getTableName();
  362.                 $orderedColumn $this->getSQLTableAlias($tableName$dqlAlias) . '.' $columnName;
  363.                 // OrderByClause should replace an ordered relation. see - DDC-2475
  364.                 if (isset($this->orderedColumnsMap[$orderedColumn])) {
  365.                     continue;
  366.                 }
  367.                 $this->orderedColumnsMap[$orderedColumn] = $orientation;
  368.                 $orderedColumns[] = $orderedColumn ' ' $orientation;
  369.             }
  370.         }
  371.         return implode(', '$orderedColumns);
  372.     }
  373.     /**
  374.      * Generates a discriminator column SQL condition for the class with the given DQL alias.
  375.      *
  376.      * @param array $dqlAliases List of root DQL aliases to inspect for discriminator restrictions.
  377.      *
  378.      * @return string
  379.      */
  380.     private function _generateDiscriminatorColumnConditionSQL(array $dqlAliases)
  381.     {
  382.         $sqlParts = [];
  383.         foreach ($dqlAliases as $dqlAlias) {
  384.             $class $this->queryComponents[$dqlAlias]['metadata'];
  385.             if ( ! $class->isInheritanceTypeSingleTable()) continue;
  386.             $conn   $this->em->getConnection();
  387.             $values = [];
  388.             if ($class->discriminatorValue !== null) { // discriminators can be 0
  389.                 $values[] = $conn->quote($class->discriminatorValue);
  390.             }
  391.             foreach ($class->subClasses as $subclassName) {
  392.                 $values[] = $conn->quote($this->em->getClassMetadata($subclassName)->discriminatorValue);
  393.             }
  394.             $sqlTableAlias = ($this->useSqlTableAliases)
  395.                 ? $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.'
  396.                 '';
  397.             $sqlParts[] = $sqlTableAlias $class->discriminatorColumn['name'] . ' IN (' implode(', '$values) . ')';
  398.         }
  399.         $sql implode(' AND '$sqlParts);
  400.         return (count($sqlParts) > 1) ? '(' $sql ')' $sql;
  401.     }
  402.     /**
  403.      * Generates the filter SQL for a given entity and table alias.
  404.      *
  405.      * @param ClassMetadata $targetEntity     Metadata of the target entity.
  406.      * @param string        $targetTableAlias The table alias of the joined/selected table.
  407.      *
  408.      * @return string The SQL query part to add to a query.
  409.      */
  410.     private function generateFilterConditionSQL(ClassMetadata $targetEntity$targetTableAlias)
  411.     {
  412.         if (!$this->em->hasFilters()) {
  413.             return '';
  414.         }
  415.         switch($targetEntity->inheritanceType) {
  416.             case ClassMetadata::INHERITANCE_TYPE_NONE:
  417.                 break;
  418.             case ClassMetadata::INHERITANCE_TYPE_JOINED:
  419.                 // The classes in the inheritance will be added to the query one by one,
  420.                 // but only the root node is getting filtered
  421.                 if ($targetEntity->name !== $targetEntity->rootEntityName) {
  422.                     return '';
  423.                 }
  424.                 break;
  425.             case ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE:
  426.                 // With STI the table will only be queried once, make sure that the filters
  427.                 // are added to the root entity
  428.                 $targetEntity $this->em->getClassMetadata($targetEntity->rootEntityName);
  429.                 break;
  430.             default:
  431.                 //@todo: throw exception?
  432.                 return '';
  433.         }
  434.         $filterClauses = [];
  435.         foreach ($this->em->getFilters()->getEnabledFilters() as $filter) {
  436.             if ('' !== $filterExpr $filter->addFilterConstraint($targetEntity$targetTableAlias)) {
  437.                 $filterClauses[] = '(' $filterExpr ')';
  438.             }
  439.         }
  440.         return implode(' AND '$filterClauses);
  441.     }
  442.     /**
  443.      * {@inheritdoc}
  444.      */
  445.     public function walkSelectStatement(AST\SelectStatement $AST)
  446.     {
  447.         $limit    $this->query->getMaxResults();
  448.         $offset   $this->query->getFirstResult();
  449.         $lockMode $this->query->getHint(Query::HINT_LOCK_MODE);
  450.         $sql      $this->walkSelectClause($AST->selectClause)
  451.             . $this->walkFromClause($AST->fromClause)
  452.             . $this->walkWhereClause($AST->whereClause);
  453.         if ($AST->groupByClause) {
  454.             $sql .= $this->walkGroupByClause($AST->groupByClause);
  455.         }
  456.         if ($AST->havingClause) {
  457.             $sql .= $this->walkHavingClause($AST->havingClause);
  458.         }
  459.         if ($AST->orderByClause) {
  460.             $sql .= $this->walkOrderByClause($AST->orderByClause);
  461.         }
  462.         if ( ! $AST->orderByClause && ($orderBySql $this->_generateOrderedCollectionOrderByItems())) {
  463.             $sql .= ' ORDER BY ' $orderBySql;
  464.         }
  465.         if ($limit !== null || $offset !== null) {
  466.             $sql $this->platform->modifyLimitQuery($sql$limit$offset);
  467.         }
  468.         if ($lockMode === null || $lockMode === false || $lockMode === LockMode::NONE) {
  469.             return $sql;
  470.         }
  471.         if ($lockMode === LockMode::PESSIMISTIC_READ) {
  472.             return $sql ' ' $this->platform->getReadLockSQL();
  473.         }
  474.         if ($lockMode === LockMode::PESSIMISTIC_WRITE) {
  475.             return $sql ' ' $this->platform->getWriteLockSQL();
  476.         }
  477.         if ($lockMode !== LockMode::OPTIMISTIC) {
  478.             throw QueryException::invalidLockMode();
  479.         }
  480.         foreach ($this->selectedClasses as $selectedClass) {
  481.             if ( ! $selectedClass['class']->isVersioned) {
  482.                 throw OptimisticLockException::lockFailed($selectedClass['class']->name);
  483.             }
  484.         }
  485.         return $sql;
  486.     }
  487.     /**
  488.      * {@inheritdoc}
  489.      */
  490.     public function walkUpdateStatement(AST\UpdateStatement $AST)
  491.     {
  492.         $this->useSqlTableAliases false;
  493.         $this->rsm->isSelect      false;
  494.         return $this->walkUpdateClause($AST->updateClause)
  495.             . $this->walkWhereClause($AST->whereClause);
  496.     }
  497.     /**
  498.      * {@inheritdoc}
  499.      */
  500.     public function walkDeleteStatement(AST\DeleteStatement $AST)
  501.     {
  502.         $this->useSqlTableAliases false;
  503.         $this->rsm->isSelect      false;
  504.         return $this->walkDeleteClause($AST->deleteClause)
  505.             . $this->walkWhereClause($AST->whereClause);
  506.     }
  507.     /**
  508.      * Walks down an IdentificationVariable AST node, thereby generating the appropriate SQL.
  509.      * This one differs of ->walkIdentificationVariable() because it generates the entity identifiers.
  510.      *
  511.      * @param string $identVariable
  512.      *
  513.      * @return string
  514.      */
  515.     public function walkEntityIdentificationVariable($identVariable)
  516.     {
  517.         $class      $this->queryComponents[$identVariable]['metadata'];
  518.         $tableAlias $this->getSQLTableAlias($class->getTableName(), $identVariable);
  519.         $sqlParts   = [];
  520.         foreach ($this->quoteStrategy->getIdentifierColumnNames($class$this->platform) as $columnName) {
  521.             $sqlParts[] = $tableAlias '.' $columnName;
  522.         }
  523.         return implode(', '$sqlParts);
  524.     }
  525.     /**
  526.      * Walks down an IdentificationVariable (no AST node associated), thereby generating the SQL.
  527.      *
  528.      * @param string $identificationVariable
  529.      * @param string $fieldName
  530.      *
  531.      * @return string The SQL.
  532.      */
  533.     public function walkIdentificationVariable($identificationVariable$fieldName null)
  534.     {
  535.         $class $this->queryComponents[$identificationVariable]['metadata'];
  536.         if (
  537.             $fieldName !== null && $class->isInheritanceTypeJoined() &&
  538.             isset($class->fieldMappings[$fieldName]['inherited'])
  539.         ) {
  540.             $class $this->em->getClassMetadata($class->fieldMappings[$fieldName]['inherited']);
  541.         }
  542.         return $this->getSQLTableAlias($class->getTableName(), $identificationVariable);
  543.     }
  544.     /**
  545.      * {@inheritdoc}
  546.      */
  547.     public function walkPathExpression($pathExpr)
  548.     {
  549.         $sql '';
  550.         /* @var $pathExpr Query\AST\PathExpression */
  551.         switch ($pathExpr->type) {
  552.             case AST\PathExpression::TYPE_STATE_FIELD:
  553.                 $fieldName $pathExpr->field;
  554.                 $dqlAlias $pathExpr->identificationVariable;
  555.                 $class $this->queryComponents[$dqlAlias]['metadata'];
  556.                 if ($this->useSqlTableAliases) {
  557.                     $sql .= $this->walkIdentificationVariable($dqlAlias$fieldName) . '.';
  558.                 }
  559.                 $sql .= $this->quoteStrategy->getColumnName($fieldName$class$this->platform);
  560.                 break;
  561.             case AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION:
  562.                 // 1- the owning side:
  563.                 //    Just use the foreign key, i.e. u.group_id
  564.                 $fieldName $pathExpr->field;
  565.                 $dqlAlias $pathExpr->identificationVariable;
  566.                 $class $this->queryComponents[$dqlAlias]['metadata'];
  567.                 if (isset($class->associationMappings[$fieldName]['inherited'])) {
  568.                     $class $this->em->getClassMetadata($class->associationMappings[$fieldName]['inherited']);
  569.                 }
  570.                 $assoc $class->associationMappings[$fieldName];
  571.                 if ( ! $assoc['isOwningSide']) {
  572.                     throw QueryException::associationPathInverseSideNotSupported($pathExpr);
  573.                 }
  574.                 // COMPOSITE KEYS NOT (YET?) SUPPORTED
  575.                 if (count($assoc['sourceToTargetKeyColumns']) > 1) {
  576.                     throw QueryException::associationPathCompositeKeyNotSupported();
  577.                 }
  578.                 if ($this->useSqlTableAliases) {
  579.                     $sql .= $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.';
  580.                 }
  581.                 $sql .= reset($assoc['targetToSourceKeyColumns']);
  582.                 break;
  583.             default:
  584.                 throw QueryException::invalidPathExpression($pathExpr);
  585.         }
  586.         return $sql;
  587.     }
  588.     /**
  589.      * {@inheritdoc}
  590.      */
  591.     public function walkSelectClause($selectClause)
  592.     {
  593.         $sql 'SELECT ' . (($selectClause->isDistinct) ? 'DISTINCT ' '');
  594.         $sqlSelectExpressions array_filter(array_map([$this'walkSelectExpression'], $selectClause->selectExpressions));
  595.         if ($this->query->getHint(Query::HINT_INTERNAL_ITERATION) == true && $selectClause->isDistinct) {
  596.             $this->query->setHint(self::HINT_DISTINCTtrue);
  597.         }
  598.         $addMetaColumns = ! $this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) &&
  599.             $this->query->getHydrationMode() == Query::HYDRATE_OBJECT
  600.             ||
  601.             $this->query->getHydrationMode() != Query::HYDRATE_OBJECT &&
  602.             $this->query->getHint(Query::HINT_INCLUDE_META_COLUMNS);
  603.         foreach ($this->selectedClasses as $selectedClass) {
  604.             $class       $selectedClass['class'];
  605.             $dqlAlias    $selectedClass['dqlAlias'];
  606.             $resultAlias $selectedClass['resultAlias'];
  607.             // Register as entity or joined entity result
  608.             if ($this->queryComponents[$dqlAlias]['relation'] === null) {
  609.                 $this->rsm->addEntityResult($class->name$dqlAlias$resultAlias);
  610.             } else {
  611.                 $this->rsm->addJoinedEntityResult(
  612.                     $class->name,
  613.                     $dqlAlias,
  614.                     $this->queryComponents[$dqlAlias]['parent'],
  615.                     $this->queryComponents[$dqlAlias]['relation']['fieldName']
  616.                 );
  617.             }
  618.             if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) {
  619.                 // Add discriminator columns to SQL
  620.                 $rootClass   $this->em->getClassMetadata($class->rootEntityName);
  621.                 $tblAlias    $this->getSQLTableAlias($rootClass->getTableName(), $dqlAlias);
  622.                 $discrColumn $rootClass->discriminatorColumn;
  623.                 $columnAlias $this->getSQLColumnAlias($discrColumn['name']);
  624.                 $sqlSelectExpressions[] = $tblAlias '.' $discrColumn['name'] . ' AS ' $columnAlias;
  625.                 $this->rsm->setDiscriminatorColumn($dqlAlias$columnAlias);
  626.                 $this->rsm->addMetaResult($dqlAlias$columnAlias$discrColumn['fieldName'], false$discrColumn['type']);
  627.             }
  628.             // Add foreign key columns to SQL, if necessary
  629.             if ( ! $addMetaColumns && ! $class->containsForeignIdentifier) {
  630.                 continue;
  631.             }
  632.             // Add foreign key columns of class and also parent classes
  633.             foreach ($class->associationMappings as $assoc) {
  634.                 if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)
  635.                     || ( ! $addMetaColumns && !isset($assoc['id']))) {
  636.                     continue;
  637.                 }
  638.                 $targetClass   $this->em->getClassMetadata($assoc['targetEntity']);
  639.                 $isIdentifier  = (isset($assoc['id']) && $assoc['id'] === true);
  640.                 $owningClass   = (isset($assoc['inherited'])) ? $this->em->getClassMetadata($assoc['inherited']) : $class;
  641.                 $sqlTableAlias $this->getSQLTableAlias($owningClass->getTableName(), $dqlAlias);
  642.                 foreach ($assoc['joinColumns'] as $joinColumn) {
  643.                     $columnName  $joinColumn['name'];
  644.                     $columnAlias $this->getSQLColumnAlias($columnName);
  645.                     $columnType  PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass$this->em);
  646.                     $quotedColumnName       $this->quoteStrategy->getJoinColumnName($joinColumn$class$this->platform);
  647.                     $sqlSelectExpressions[] = $sqlTableAlias '.' $quotedColumnName ' AS ' $columnAlias;
  648.                     $this->rsm->addMetaResult($dqlAlias$columnAlias$columnName$isIdentifier$columnType);
  649.                 }
  650.             }
  651.             // Add foreign key columns to SQL, if necessary
  652.             if ( ! $addMetaColumns) {
  653.                 continue;
  654.             }
  655.             // Add foreign key columns of subclasses
  656.             foreach ($class->subClasses as $subClassName) {
  657.                 $subClass      $this->em->getClassMetadata($subClassName);
  658.                 $sqlTableAlias $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
  659.                 foreach ($subClass->associationMappings as $assoc) {
  660.                     // Skip if association is inherited
  661.                     if (isset($assoc['inherited'])) continue;
  662.                     if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
  663.                         $targetClass $this->em->getClassMetadata($assoc['targetEntity']);
  664.                         foreach ($assoc['joinColumns'] as $joinColumn) {
  665.                             $columnName  $joinColumn['name'];
  666.                             $columnAlias $this->getSQLColumnAlias($columnName);
  667.                             $columnType  PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass$this->em);
  668.                             $quotedColumnName       $this->quoteStrategy->getJoinColumnName($joinColumn$subClass$this->platform);
  669.                             $sqlSelectExpressions[] = $sqlTableAlias '.' $quotedColumnName ' AS ' $columnAlias;
  670.                             $this->rsm->addMetaResult($dqlAlias$columnAlias$columnName$subClass->isIdentifier($columnName), $columnType);
  671.                         }
  672.                     }
  673.                 }
  674.             }
  675.         }
  676.         $sql .= implode(', '$sqlSelectExpressions);
  677.         return $sql;
  678.     }
  679.     /**
  680.      * {@inheritdoc}
  681.      */
  682.     public function walkFromClause($fromClause)
  683.     {
  684.         $identificationVarDecls $fromClause->identificationVariableDeclarations;
  685.         $sqlParts = [];
  686.         foreach ($identificationVarDecls as $identificationVariableDecl) {
  687.             $sqlParts[] = $this->walkIdentificationVariableDeclaration($identificationVariableDecl);
  688.         }
  689.         return ' FROM ' implode(', '$sqlParts);
  690.     }
  691.     /**
  692.      * Walks down a IdentificationVariableDeclaration AST node, thereby generating the appropriate SQL.
  693.      *
  694.      * @param AST\IdentificationVariableDeclaration $identificationVariableDecl
  695.      *
  696.      * @return string
  697.      */
  698.     public function walkIdentificationVariableDeclaration($identificationVariableDecl)
  699.     {
  700.         $sql $this->walkRangeVariableDeclaration($identificationVariableDecl->rangeVariableDeclaration);
  701.         if ($identificationVariableDecl->indexBy) {
  702.             $this->walkIndexBy($identificationVariableDecl->indexBy);
  703.         }
  704.         foreach ($identificationVariableDecl->joins as $join) {
  705.             $sql .= $this->walkJoin($join);
  706.         }
  707.         return $sql;
  708.     }
  709.     /**
  710.      * Walks down a IndexBy AST node.
  711.      *
  712.      * @param AST\IndexBy $indexBy
  713.      *
  714.      * @return void
  715.      */
  716.     public function walkIndexBy($indexBy)
  717.     {
  718.         $pathExpression $indexBy->simpleStateFieldPathExpression;
  719.         $alias          $pathExpression->identificationVariable;
  720.         $field          $pathExpression->field;
  721.         if (isset($this->scalarFields[$alias][$field])) {
  722.             $this->rsm->addIndexByScalar($this->scalarFields[$alias][$field]);
  723.             return;
  724.         }
  725.         $this->rsm->addIndexBy($alias$field);
  726.     }
  727.     /**
  728.      * Walks down a RangeVariableDeclaration AST node, thereby generating the appropriate SQL.
  729.      *
  730.      * @param AST\RangeVariableDeclaration $rangeVariableDeclaration
  731.      *
  732.      * @return string
  733.      */
  734.     public function walkRangeVariableDeclaration($rangeVariableDeclaration)
  735.     {
  736.         return $this->generateRangeVariableDeclarationSQL($rangeVariableDeclarationfalse);
  737.     }
  738.     /**
  739.      * Generate appropriate SQL for RangeVariableDeclaration AST node
  740.      *
  741.      * @param AST\RangeVariableDeclaration $rangeVariableDeclaration
  742.      * @param bool $buildNestedJoins
  743.      *
  744.      * @return string
  745.      */
  746.     private function generateRangeVariableDeclarationSQL($rangeVariableDeclarationbool $buildNestedJoins) : string
  747.     {
  748.         $class    $this->em->getClassMetadata($rangeVariableDeclaration->abstractSchemaName);
  749.         $dqlAlias $rangeVariableDeclaration->aliasIdentificationVariable;
  750.         if ($rangeVariableDeclaration->isRoot) {
  751.             $this->rootAliases[] = $dqlAlias;
  752.         }
  753.         $sql $this->platform->appendLockHint(
  754.             $this->quoteStrategy->getTableName($class$this->platform) . ' ' .
  755.             $this->getSQLTableAlias($class->getTableName(), $dqlAlias),
  756.             $this->query->getHint(Query::HINT_LOCK_MODE)
  757.         );
  758.         if ( ! $class->isInheritanceTypeJoined()) {
  759.             return $sql;
  760.         }
  761.         $classTableInheritanceJoins $this->_generateClassTableInheritanceJoins($class$dqlAlias);
  762.         if ( ! $buildNestedJoins) {
  763.             return $sql $classTableInheritanceJoins;
  764.         }
  765.         return $classTableInheritanceJoins === '' $sql '(' $sql $classTableInheritanceJoins ')';
  766.     }
  767.     /**
  768.      * Walks down a JoinAssociationDeclaration AST node, thereby generating the appropriate SQL.
  769.      *
  770.      * @param AST\JoinAssociationDeclaration $joinAssociationDeclaration
  771.      * @param int                            $joinType
  772.      * @param AST\ConditionalExpression      $condExpr
  773.      *
  774.      * @return string
  775.      *
  776.      * @throws QueryException
  777.      */
  778.     public function walkJoinAssociationDeclaration($joinAssociationDeclaration$joinType AST\Join::JOIN_TYPE_INNER$condExpr null)
  779.     {
  780.         $sql '';
  781.         $associationPathExpression $joinAssociationDeclaration->joinAssociationPathExpression;
  782.         $joinedDqlAlias            $joinAssociationDeclaration->aliasIdentificationVariable;
  783.         $indexBy                   $joinAssociationDeclaration->indexBy;
  784.         $relation        $this->queryComponents[$joinedDqlAlias]['relation'];
  785.         $targetClass     $this->em->getClassMetadata($relation['targetEntity']);
  786.         $sourceClass     $this->em->getClassMetadata($relation['sourceEntity']);
  787.         $targetTableName $this->quoteStrategy->getTableName($targetClass$this->platform);
  788.         $targetTableAlias $this->getSQLTableAlias($targetClass->getTableName(), $joinedDqlAlias);
  789.         $sourceTableAlias $this->getSQLTableAlias($sourceClass->getTableName(), $associationPathExpression->identificationVariable);
  790.         // Ensure we got the owning side, since it has all mapping info
  791.         $assoc = ( ! $relation['isOwningSide']) ? $targetClass->associationMappings[$relation['mappedBy']] : $relation;
  792.         if ($this->query->getHint(Query::HINT_INTERNAL_ITERATION) == true && (!$this->query->getHint(self::HINT_DISTINCT) || isset($this->selectedClasses[$joinedDqlAlias]))) {
  793.             if ($relation['type'] == ClassMetadata::ONE_TO_MANY || $relation['type'] == ClassMetadata::MANY_TO_MANY) {
  794.                 throw QueryException::iterateWithFetchJoinNotAllowed($assoc);
  795.             }
  796.         }
  797.         $targetTableJoin null;
  798.         // This condition is not checking ClassMetadata::MANY_TO_ONE, because by definition it cannot
  799.         // be the owning side and previously we ensured that $assoc is always the owning side of the associations.
  800.         // The owning side is necessary at this point because only it contains the JoinColumn information.
  801.         switch (true) {
  802.             case ($assoc['type'] & ClassMetadata::TO_ONE):
  803.                 $conditions = [];
  804.                 foreach ($assoc['joinColumns'] as $joinColumn) {
  805.                     $quotedSourceColumn $this->quoteStrategy->getJoinColumnName($joinColumn$targetClass$this->platform);
  806.                     $quotedTargetColumn $this->quoteStrategy->getReferencedJoinColumnName($joinColumn$targetClass$this->platform);
  807.                     if ($relation['isOwningSide']) {
  808.                         $conditions[] = $sourceTableAlias '.' $quotedSourceColumn ' = ' $targetTableAlias '.' $quotedTargetColumn;
  809.                         continue;
  810.                     }
  811.                     $conditions[] = $sourceTableAlias '.' $quotedTargetColumn ' = ' $targetTableAlias '.' $quotedSourceColumn;
  812.                 }
  813.                 // Apply remaining inheritance restrictions
  814.                 $discrSql $this->_generateDiscriminatorColumnConditionSQL([$joinedDqlAlias]);
  815.                 if ($discrSql) {
  816.                     $conditions[] = $discrSql;
  817.                 }
  818.                 // Apply the filters
  819.                 $filterExpr $this->generateFilterConditionSQL($targetClass$targetTableAlias);
  820.                 if ($filterExpr) {
  821.                     $conditions[] = $filterExpr;
  822.                 }
  823.                 $targetTableJoin = [
  824.                     'table' => $targetTableName ' ' $targetTableAlias,
  825.                     'condition' => implode(' AND '$conditions),
  826.                 ];
  827.                 break;
  828.             case ($assoc['type'] == ClassMetadata::MANY_TO_MANY):
  829.                 // Join relation table
  830.                 $joinTable      $assoc['joinTable'];
  831.                 $joinTableAlias $this->getSQLTableAlias($joinTable['name'], $joinedDqlAlias);
  832.                 $joinTableName  $this->quoteStrategy->getJoinTableName($assoc$sourceClass$this->platform);
  833.                 $conditions      = [];
  834.                 $relationColumns = ($relation['isOwningSide'])
  835.                     ? $assoc['joinTable']['joinColumns']
  836.                     : $assoc['joinTable']['inverseJoinColumns'];
  837.                 foreach ($relationColumns as $joinColumn) {
  838.                     $quotedSourceColumn $this->quoteStrategy->getJoinColumnName($joinColumn$targetClass$this->platform);
  839.                     $quotedTargetColumn $this->quoteStrategy->getReferencedJoinColumnName($joinColumn$targetClass$this->platform);
  840.                     $conditions[] = $sourceTableAlias '.' $quotedTargetColumn ' = ' $joinTableAlias '.' $quotedSourceColumn;
  841.                 }
  842.                 $sql .= $joinTableName ' ' $joinTableAlias ' ON ' implode(' AND '$conditions);
  843.                 // Join target table
  844.                 $sql .= ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) ? ' LEFT JOIN ' ' INNER JOIN ';
  845.                 $conditions      = [];
  846.                 $relationColumns = ($relation['isOwningSide'])
  847.                     ? $assoc['joinTable']['inverseJoinColumns']
  848.                     : $assoc['joinTable']['joinColumns'];
  849.                 foreach ($relationColumns as $joinColumn) {
  850.                     $quotedSourceColumn $this->quoteStrategy->getJoinColumnName($joinColumn$targetClass$this->platform);
  851.                     $quotedTargetColumn $this->quoteStrategy->getReferencedJoinColumnName($joinColumn$targetClass$this->platform);
  852.                     $conditions[] = $targetTableAlias '.' $quotedTargetColumn ' = ' $joinTableAlias '.' $quotedSourceColumn;
  853.                 }
  854.                 // Apply remaining inheritance restrictions
  855.                 $discrSql $this->_generateDiscriminatorColumnConditionSQL([$joinedDqlAlias]);
  856.                 if ($discrSql) {
  857.                     $conditions[] = $discrSql;
  858.                 }
  859.                 // Apply the filters
  860.                 $filterExpr $this->generateFilterConditionSQL($targetClass$targetTableAlias);
  861.                 if ($filterExpr) {
  862.                     $conditions[] = $filterExpr;
  863.                 }
  864.                 $targetTableJoin = [
  865.                     'table' => $targetTableName ' ' $targetTableAlias,
  866.                     'condition' => implode(' AND '$conditions),
  867.                 ];
  868.                 break;
  869.             default:
  870.                 throw new \BadMethodCallException('Type of association must be one of *_TO_ONE or MANY_TO_MANY');
  871.         }
  872.         // Handle WITH clause
  873.         $withCondition = (null === $condExpr) ? '' : ('(' $this->walkConditionalExpression($condExpr) . ')');
  874.         if ($targetClass->isInheritanceTypeJoined()) {
  875.             $ctiJoins $this->_generateClassTableInheritanceJoins($targetClass$joinedDqlAlias);
  876.             // If we have WITH condition, we need to build nested joins for target class table and cti joins
  877.             if ($withCondition) {
  878.                 $sql .= '(' $targetTableJoin['table'] . $ctiJoins ') ON ' $targetTableJoin['condition'];
  879.             } else {
  880.                 $sql .= $targetTableJoin['table'] . ' ON ' $targetTableJoin['condition'] . $ctiJoins;
  881.             }
  882.         } else {
  883.             $sql .= $targetTableJoin['table'] . ' ON ' $targetTableJoin['condition'];
  884.         }
  885.         if ($withCondition) {
  886.             $sql .= ' AND ' $withCondition;
  887.         }
  888.         // Apply the indexes
  889.         if ($indexBy) {
  890.             // For Many-To-One or One-To-One associations this obviously makes no sense, but is ignored silently.
  891.             $this->walkIndexBy($indexBy);
  892.         } else if (isset($relation['indexBy'])) {
  893.             $this->rsm->addIndexBy($joinedDqlAlias$relation['indexBy']);
  894.         }
  895.         return $sql;
  896.     }
  897.     /**
  898.      * {@inheritdoc}
  899.      */
  900.     public function walkFunction($function)
  901.     {
  902.         return $function->getSql($this);
  903.     }
  904.     /**
  905.      * {@inheritdoc}
  906.      */
  907.     public function walkOrderByClause($orderByClause)
  908.     {
  909.         $orderByItems array_map([$this'walkOrderByItem'], $orderByClause->orderByItems);
  910.         if (($collectionOrderByItems $this->_generateOrderedCollectionOrderByItems()) !== '') {
  911.             $orderByItems array_merge($orderByItems, (array) $collectionOrderByItems);
  912.         }
  913.         return ' ORDER BY ' implode(', '$orderByItems);
  914.     }
  915.     /**
  916.      * {@inheritdoc}
  917.      */
  918.     public function walkOrderByItem($orderByItem)
  919.     {
  920.         $type strtoupper($orderByItem->type);
  921.         $expr $orderByItem->expression;
  922.         $sql  = ($expr instanceof AST\Node)
  923.             ? $expr->dispatch($this)
  924.             : $this->walkResultVariable($this->queryComponents[$expr]['token']['value']);
  925.         $this->orderedColumnsMap[$sql] = $type;
  926.         if ($expr instanceof AST\Subselect) {
  927.             return '(' $sql ') ' $type;
  928.         }
  929.         return $sql ' ' $type;
  930.     }
  931.     /**
  932.      * {@inheritdoc}
  933.      */
  934.     public function walkHavingClause($havingClause)
  935.     {
  936.         return ' HAVING ' $this->walkConditionalExpression($havingClause->conditionalExpression);
  937.     }
  938.     /**
  939.      * {@inheritdoc}
  940.      */
  941.     public function walkJoin($join)
  942.     {
  943.         $joinType        $join->joinType;
  944.         $joinDeclaration $join->joinAssociationDeclaration;
  945.         $sql = ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER)
  946.             ? ' LEFT JOIN '
  947.             ' INNER JOIN ';
  948.         switch (true) {
  949.             case ($joinDeclaration instanceof AST\RangeVariableDeclaration):
  950.                 $class      $this->em->getClassMetadata($joinDeclaration->abstractSchemaName);
  951.                 $dqlAlias   $joinDeclaration->aliasIdentificationVariable;
  952.                 $tableAlias $this->getSQLTableAlias($class->table['name'], $dqlAlias);
  953.                 $conditions = [];
  954.                 if ($join->conditionalExpression) {
  955.                     $conditions[] = '(' $this->walkConditionalExpression($join->conditionalExpression) . ')';
  956.                 }
  957.                 $isUnconditionalJoin = empty($conditions);
  958.                 $condExprConjunction = ($class->isInheritanceTypeJoined() && $joinType != AST\Join::JOIN_TYPE_LEFT && $joinType != AST\Join::JOIN_TYPE_LEFTOUTER && $isUnconditionalJoin)
  959.                     ? ' AND '
  960.                     ' ON ';
  961.                 $sql .= $this->generateRangeVariableDeclarationSQL($joinDeclaration, !$isUnconditionalJoin);
  962.                 // Apply remaining inheritance restrictions
  963.                 $discrSql $this->_generateDiscriminatorColumnConditionSQL([$dqlAlias]);
  964.                 if ($discrSql) {
  965.                     $conditions[] = $discrSql;
  966.                 }
  967.                 // Apply the filters
  968.                 $filterExpr $this->generateFilterConditionSQL($class$tableAlias);
  969.                 if ($filterExpr) {
  970.                     $conditions[] = $filterExpr;
  971.                 }
  972.                 if ($conditions) {
  973.                     $sql .= $condExprConjunction implode(' AND '$conditions);
  974.                 }
  975.                 break;
  976.             case ($joinDeclaration instanceof AST\JoinAssociationDeclaration):
  977.                 $sql .= $this->walkJoinAssociationDeclaration($joinDeclaration$joinType$join->conditionalExpression);
  978.                 break;
  979.         }
  980.         return $sql;
  981.     }
  982.     /**
  983.      * Walks down a CoalesceExpression AST node and generates the corresponding SQL.
  984.      *
  985.      * @param AST\CoalesceExpression $coalesceExpression
  986.      *
  987.      * @return string The SQL.
  988.      */
  989.     public function walkCoalesceExpression($coalesceExpression)
  990.     {
  991.         $sql 'COALESCE(';
  992.         $scalarExpressions = [];
  993.         foreach ($coalesceExpression->scalarExpressions as $scalarExpression) {
  994.             $scalarExpressions[] = $this->walkSimpleArithmeticExpression($scalarExpression);
  995.         }
  996.         $sql .= implode(', '$scalarExpressions) . ')';
  997.         return $sql;
  998.     }
  999.     /**
  1000.      * Walks down a NullIfExpression AST node and generates the corresponding SQL.
  1001.      *
  1002.      * @param AST\NullIfExpression $nullIfExpression
  1003.      *
  1004.      * @return string The SQL.
  1005.      */
  1006.     public function walkNullIfExpression($nullIfExpression)
  1007.     {
  1008.         $firstExpression is_string($nullIfExpression->firstExpression)
  1009.             ? $this->conn->quote($nullIfExpression->firstExpression)
  1010.             : $this->walkSimpleArithmeticExpression($nullIfExpression->firstExpression);
  1011.         $secondExpression is_string($nullIfExpression->secondExpression)
  1012.             ? $this->conn->quote($nullIfExpression->secondExpression)
  1013.             : $this->walkSimpleArithmeticExpression($nullIfExpression->secondExpression);
  1014.         return 'NULLIF(' $firstExpression ', ' $secondExpression ')';
  1015.     }
  1016.     /**
  1017.      * Walks down a GeneralCaseExpression AST node and generates the corresponding SQL.
  1018.      *
  1019.      * @param AST\GeneralCaseExpression $generalCaseExpression
  1020.      *
  1021.      * @return string The SQL.
  1022.      */
  1023.     public function walkGeneralCaseExpression(AST\GeneralCaseExpression $generalCaseExpression)
  1024.     {
  1025.         $sql 'CASE';
  1026.         foreach ($generalCaseExpression->whenClauses as $whenClause) {
  1027.             $sql .= ' WHEN ' $this->walkConditionalExpression($whenClause->caseConditionExpression);
  1028.             $sql .= ' THEN ' $this->walkSimpleArithmeticExpression($whenClause->thenScalarExpression);
  1029.         }
  1030.         $sql .= ' ELSE ' $this->walkSimpleArithmeticExpression($generalCaseExpression->elseScalarExpression) . ' END';
  1031.         return $sql;
  1032.     }
  1033.     /**
  1034.      * Walks down a SimpleCaseExpression AST node and generates the corresponding SQL.
  1035.      *
  1036.      * @param AST\SimpleCaseExpression $simpleCaseExpression
  1037.      *
  1038.      * @return string The SQL.
  1039.      */
  1040.     public function walkSimpleCaseExpression($simpleCaseExpression)
  1041.     {
  1042.         $sql 'CASE ' $this->walkStateFieldPathExpression($simpleCaseExpression->caseOperand);
  1043.         foreach ($simpleCaseExpression->simpleWhenClauses as $simpleWhenClause) {
  1044.             $sql .= ' WHEN ' $this->walkSimpleArithmeticExpression($simpleWhenClause->caseScalarExpression);
  1045.             $sql .= ' THEN ' $this->walkSimpleArithmeticExpression($simpleWhenClause->thenScalarExpression);
  1046.         }
  1047.         $sql .= ' ELSE ' $this->walkSimpleArithmeticExpression($simpleCaseExpression->elseScalarExpression) . ' END';
  1048.         return $sql;
  1049.     }
  1050.     /**
  1051.      * {@inheritdoc}
  1052.      */
  1053.     public function walkSelectExpression($selectExpression)
  1054.     {
  1055.         $sql    '';
  1056.         $expr   $selectExpression->expression;
  1057.         $hidden $selectExpression->hiddenAliasResultVariable;
  1058.         switch (true) {
  1059.             case ($expr instanceof AST\PathExpression):
  1060.                 if ($expr->type !== AST\PathExpression::TYPE_STATE_FIELD) {
  1061.                     throw QueryException::invalidPathExpression($expr);
  1062.                 }
  1063.                 $fieldName $expr->field;
  1064.                 $dqlAlias  $expr->identificationVariable;
  1065.                 $qComp     $this->queryComponents[$dqlAlias];
  1066.                 $class     $qComp['metadata'];
  1067.                 $resultAlias $selectExpression->fieldIdentificationVariable ?: $fieldName;
  1068.                 $tableName   = ($class->isInheritanceTypeJoined())
  1069.                     ? $this->em->getUnitOfWork()->getEntityPersister($class->name)->getOwningTable($fieldName)
  1070.                     : $class->getTableName();
  1071.                 $sqlTableAlias $this->getSQLTableAlias($tableName$dqlAlias);
  1072.                 $fieldMapping  $class->fieldMappings[$fieldName];
  1073.                 $columnName    $this->quoteStrategy->getColumnName($fieldName$class$this->platform);
  1074.                 $columnAlias   $this->getSQLColumnAlias($fieldMapping['columnName']);
  1075.                 $col           $sqlTableAlias '.' $columnName;
  1076.                 if (isset($fieldMapping['requireSQLConversion'])) {
  1077.                     $type Type::getType($fieldMapping['type']);
  1078.                     $col  $type->convertToPHPValueSQL($col$this->conn->getDatabasePlatform());
  1079.                 }
  1080.                 $sql .= $col ' AS ' $columnAlias;
  1081.                 $this->scalarResultAliasMap[$resultAlias] = $columnAlias;
  1082.                 if ( ! $hidden) {
  1083.                     $this->rsm->addScalarResult($columnAlias$resultAlias$fieldMapping['type']);
  1084.                     $this->scalarFields[$dqlAlias][$fieldName] = $columnAlias;
  1085.                 }
  1086.                 break;
  1087.             case ($expr instanceof AST\AggregateExpression):
  1088.             case ($expr instanceof AST\Functions\FunctionNode):
  1089.             case ($expr instanceof AST\SimpleArithmeticExpression):
  1090.             case ($expr instanceof AST\ArithmeticTerm):
  1091.             case ($expr instanceof AST\ArithmeticFactor):
  1092.             case ($expr instanceof AST\ParenthesisExpression):
  1093.             case ($expr instanceof AST\Literal):
  1094.             case ($expr instanceof AST\NullIfExpression):
  1095.             case ($expr instanceof AST\CoalesceExpression):
  1096.             case ($expr instanceof AST\GeneralCaseExpression):
  1097.             case ($expr instanceof AST\SimpleCaseExpression):
  1098.                 $columnAlias $this->getSQLColumnAlias('sclr');
  1099.                 $resultAlias $selectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
  1100.                 $sql .= $expr->dispatch($this) . ' AS ' $columnAlias;
  1101.                 $this->scalarResultAliasMap[$resultAlias] = $columnAlias;
  1102.                 if ( ! $hidden) {
  1103.                     // We cannot resolve field type here; assume 'string'.
  1104.                     $this->rsm->addScalarResult($columnAlias$resultAlias'string');
  1105.                 }
  1106.                 break;
  1107.             case ($expr instanceof AST\Subselect):
  1108.                 $columnAlias $this->getSQLColumnAlias('sclr');
  1109.                 $resultAlias $selectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
  1110.                 $sql .= '(' $this->walkSubselect($expr) . ') AS ' $columnAlias;
  1111.                 $this->scalarResultAliasMap[$resultAlias] = $columnAlias;
  1112.                 if ( ! $hidden) {
  1113.                     // We cannot resolve field type here; assume 'string'.
  1114.                     $this->rsm->addScalarResult($columnAlias$resultAlias'string');
  1115.                 }
  1116.                 break;
  1117.             case ($expr instanceof AST\NewObjectExpression):
  1118.                 $sql .= $this->walkNewObject($expr,$selectExpression->fieldIdentificationVariable);
  1119.                 break;
  1120.             default:
  1121.                 // IdentificationVariable or PartialObjectExpression
  1122.                 if ($expr instanceof AST\PartialObjectExpression) {
  1123.                     $this->query->setHint(self::HINT_PARTIALtrue);
  1124.                     $dqlAlias $expr->identificationVariable;
  1125.                     $partialFieldSet $expr->partialFieldSet;
  1126.                 } else {
  1127.                     $dqlAlias $expr;
  1128.                     $partialFieldSet = [];
  1129.                 }
  1130.                 $queryComp   $this->queryComponents[$dqlAlias];
  1131.                 $class       $queryComp['metadata'];
  1132.                 $resultAlias $selectExpression->fieldIdentificationVariable ?: null;
  1133.                 if ( ! isset($this->selectedClasses[$dqlAlias])) {
  1134.                     $this->selectedClasses[$dqlAlias] = [
  1135.                         'class'       => $class,
  1136.                         'dqlAlias'    => $dqlAlias,
  1137.                         'resultAlias' => $resultAlias
  1138.                     ];
  1139.                 }
  1140.                 $sqlParts = [];
  1141.                 // Select all fields from the queried class
  1142.                 foreach ($class->fieldMappings as $fieldName => $mapping) {
  1143.                     if ($partialFieldSet && ! in_array($fieldName$partialFieldSet)) {
  1144.                         continue;
  1145.                     }
  1146.                     $tableName = (isset($mapping['inherited']))
  1147.                         ? $this->em->getClassMetadata($mapping['inherited'])->getTableName()
  1148.                         : $class->getTableName();
  1149.                     $sqlTableAlias    $this->getSQLTableAlias($tableName$dqlAlias);
  1150.                     $columnAlias      $this->getSQLColumnAlias($mapping['columnName']);
  1151.                     $quotedColumnName $this->quoteStrategy->getColumnName($fieldName$class$this->platform);
  1152.                     $col $sqlTableAlias '.' $quotedColumnName;
  1153.                     if (isset($mapping['requireSQLConversion'])) {
  1154.                         $type Type::getType($mapping['type']);
  1155.                         $col $type->convertToPHPValueSQL($col$this->platform);
  1156.                     }
  1157.                     $sqlParts[] = $col ' AS '$columnAlias;
  1158.                     $this->scalarResultAliasMap[$resultAlias][] = $columnAlias;
  1159.                     $this->rsm->addFieldResult($dqlAlias$columnAlias$fieldName$class->name);
  1160.                 }
  1161.                 // Add any additional fields of subclasses (excluding inherited fields)
  1162.                 // 1) on Single Table Inheritance: always, since its marginal overhead
  1163.                 // 2) on Class Table Inheritance only if partial objects are disallowed,
  1164.                 //    since it requires outer joining subtables.
  1165.                 if ($class->isInheritanceTypeSingleTable() || ! $this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {
  1166.                     foreach ($class->subClasses as $subClassName) {
  1167.                         $subClass      $this->em->getClassMetadata($subClassName);
  1168.                         $sqlTableAlias $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
  1169.                         foreach ($subClass->fieldMappings as $fieldName => $mapping) {
  1170.                             if (isset($mapping['inherited']) || ($partialFieldSet && !in_array($fieldName$partialFieldSet))) {
  1171.                                 continue;
  1172.                             }
  1173.                             $columnAlias      $this->getSQLColumnAlias($mapping['columnName']);
  1174.                             $quotedColumnName $this->quoteStrategy->getColumnName($fieldName$subClass$this->platform);
  1175.                             $col $sqlTableAlias '.' $quotedColumnName;
  1176.                             if (isset($mapping['requireSQLConversion'])) {
  1177.                                 $type Type::getType($mapping['type']);
  1178.                                 $col $type->convertToPHPValueSQL($col$this->platform);
  1179.                             }
  1180.                             $sqlParts[] = $col ' AS ' $columnAlias;
  1181.                             $this->scalarResultAliasMap[$resultAlias][] = $columnAlias;
  1182.                             $this->rsm->addFieldResult($dqlAlias$columnAlias$fieldName$subClassName);
  1183.                         }
  1184.                     }
  1185.                 }
  1186.                 $sql .= implode(', '$sqlParts);
  1187.         }
  1188.         return $sql;
  1189.     }
  1190.     /**
  1191.      * {@inheritdoc}
  1192.      */
  1193.     public function walkQuantifiedExpression($qExpr)
  1194.     {
  1195.         return ' ' strtoupper($qExpr->type) . '(' $this->walkSubselect($qExpr->subselect) . ')';
  1196.     }
  1197.     /**
  1198.      * {@inheritdoc}
  1199.      */
  1200.     public function walkSubselect($subselect)
  1201.     {
  1202.         $useAliasesBefore  $this->useSqlTableAliases;
  1203.         $rootAliasesBefore $this->rootAliases;
  1204.         $this->rootAliases = []; // reset the rootAliases for the subselect
  1205.         $this->useSqlTableAliases true;
  1206.         $sql  $this->walkSimpleSelectClause($subselect->simpleSelectClause);
  1207.         $sql .= $this->walkSubselectFromClause($subselect->subselectFromClause);
  1208.         $sql .= $this->walkWhereClause($subselect->whereClause);
  1209.         $sql .= $subselect->groupByClause $this->walkGroupByClause($subselect->groupByClause) : '';
  1210.         $sql .= $subselect->havingClause $this->walkHavingClause($subselect->havingClause) : '';
  1211.         $sql .= $subselect->orderByClause $this->walkOrderByClause($subselect->orderByClause) : '';
  1212.         $this->rootAliases        $rootAliasesBefore// put the main aliases back
  1213.         $this->useSqlTableAliases $useAliasesBefore;
  1214.         return $sql;
  1215.     }
  1216.     /**
  1217.      * {@inheritdoc}
  1218.      */
  1219.     public function walkSubselectFromClause($subselectFromClause)
  1220.     {
  1221.         $identificationVarDecls $subselectFromClause->identificationVariableDeclarations;
  1222.         $sqlParts               = [];
  1223.         foreach ($identificationVarDecls as $subselectIdVarDecl) {
  1224.             $sqlParts[] = $this->walkIdentificationVariableDeclaration($subselectIdVarDecl);
  1225.         }
  1226.         return ' FROM ' implode(', '$sqlParts);
  1227.     }
  1228.     /**
  1229.      * {@inheritdoc}
  1230.      */
  1231.     public function walkSimpleSelectClause($simpleSelectClause)
  1232.     {
  1233.         return 'SELECT' . ($simpleSelectClause->isDistinct ' DISTINCT' '')
  1234.             . $this->walkSimpleSelectExpression($simpleSelectClause->simpleSelectExpression);
  1235.     }
  1236.     /**
  1237.      * @param \Doctrine\ORM\Query\AST\ParenthesisExpression $parenthesisExpression
  1238.      *
  1239.      * @return string
  1240.      */
  1241.     public function walkParenthesisExpression(AST\ParenthesisExpression $parenthesisExpression)
  1242.     {
  1243.         return sprintf('(%s)'$parenthesisExpression->expression->dispatch($this));
  1244.     }
  1245.     /**
  1246.      * @param AST\NewObjectExpression   $newObjectExpression
  1247.      * @param null|string               $newObjectResultAlias
  1248.      * @return string The SQL.
  1249.      */
  1250.     public function walkNewObject($newObjectExpression$newObjectResultAlias=null)
  1251.     {
  1252.         $sqlSelectExpressions = [];
  1253.         $objIndex             $newObjectResultAlias?:$this->newObjectCounter++;
  1254.         foreach ($newObjectExpression->args as $argIndex => $e) {
  1255.             $resultAlias $this->scalarResultCounter++;
  1256.             $columnAlias $this->getSQLColumnAlias('sclr');
  1257.             $fieldType   'string';
  1258.             switch (true) {
  1259.                 case ($e instanceof AST\NewObjectExpression):
  1260.                     $sqlSelectExpressions[] = $e->dispatch($this);
  1261.                     break;
  1262.                 case ($e instanceof AST\Subselect):
  1263.                     $sqlSelectExpressions[] = '(' $e->dispatch($this) . ') AS ' $columnAlias;
  1264.                     break;
  1265.                 case ($e instanceof AST\PathExpression):
  1266.                     $dqlAlias     $e->identificationVariable;
  1267.                     $qComp        $this->queryComponents[$dqlAlias];
  1268.                     $class        $qComp['metadata'];
  1269.                     $fieldType    $class->fieldMappings[$e->field]['type'];
  1270.                     $fieldName    $e->field;
  1271.                     $fieldMapping $class->fieldMappings[$fieldName];
  1272.                     $col          trim($e->dispatch($this));
  1273.                     if (isset($fieldMapping['requireSQLConversion'])) {
  1274.                         $type Type::getType($fieldType);
  1275.                         $col  $type->convertToPHPValueSQL($col$this->platform);
  1276.                     }
  1277.                     $sqlSelectExpressions[] = $col ' AS ' $columnAlias;
  1278.                     break;
  1279.                 case ($e instanceof AST\Literal):
  1280.                     switch ($e->type) {
  1281.                         case AST\Literal::BOOLEAN:
  1282.                             $fieldType 'boolean';
  1283.                             break;
  1284.                         case AST\Literal::NUMERIC:
  1285.                             $fieldType is_float($e->value) ? 'float' 'integer';
  1286.                             break;
  1287.                     }
  1288.                     $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' $columnAlias;
  1289.                     break;
  1290.                 default:
  1291.                     $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' $columnAlias;
  1292.                     break;
  1293.             }
  1294.             $this->scalarResultAliasMap[$resultAlias] = $columnAlias;
  1295.             $this->rsm->addScalarResult($columnAlias$resultAlias$fieldType);
  1296.             $this->rsm->newObjectMappings[$columnAlias] = [
  1297.                 'className' => $newObjectExpression->className,
  1298.                 'objIndex'  => $objIndex,
  1299.                 'argIndex'  => $argIndex
  1300.             ];
  1301.         }
  1302.         return implode(', '$sqlSelectExpressions);
  1303.     }
  1304.     /**
  1305.      * {@inheritdoc}
  1306.      */
  1307.     public function walkSimpleSelectExpression($simpleSelectExpression)
  1308.     {
  1309.         $expr $simpleSelectExpression->expression;
  1310.         $sql  ' ';
  1311.         switch (true) {
  1312.             case ($expr instanceof AST\PathExpression):
  1313.                 $sql .= $this->walkPathExpression($expr);
  1314.                 break;
  1315.             case ($expr instanceof AST\Subselect):
  1316.                 $alias $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
  1317.                 $columnAlias 'sclr' $this->aliasCounter++;
  1318.                 $this->scalarResultAliasMap[$alias] = $columnAlias;
  1319.                 $sql .= '(' $this->walkSubselect($expr) . ') AS ' $columnAlias;
  1320.                 break;
  1321.             case ($expr instanceof AST\Functions\FunctionNode):
  1322.             case ($expr instanceof AST\SimpleArithmeticExpression):
  1323.             case ($expr instanceof AST\ArithmeticTerm):
  1324.             case ($expr instanceof AST\ArithmeticFactor):
  1325.             case ($expr instanceof AST\Literal):
  1326.             case ($expr instanceof AST\NullIfExpression):
  1327.             case ($expr instanceof AST\CoalesceExpression):
  1328.             case ($expr instanceof AST\GeneralCaseExpression):
  1329.             case ($expr instanceof AST\SimpleCaseExpression):
  1330.                 $alias $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
  1331.                 $columnAlias $this->getSQLColumnAlias('sclr');
  1332.                 $this->scalarResultAliasMap[$alias] = $columnAlias;
  1333.                 $sql .= $expr->dispatch($this) . ' AS ' $columnAlias;
  1334.                 break;
  1335.             case ($expr instanceof AST\ParenthesisExpression):
  1336.                 $sql .= $this->walkParenthesisExpression($expr);
  1337.                 break;
  1338.             default: // IdentificationVariable
  1339.                 $sql .= $this->walkEntityIdentificationVariable($expr);
  1340.                 break;
  1341.         }
  1342.         return $sql;
  1343.     }
  1344.     /**
  1345.      * {@inheritdoc}
  1346.      */
  1347.     public function walkAggregateExpression($aggExpression)
  1348.     {
  1349.         return $aggExpression->functionName '(' . ($aggExpression->isDistinct 'DISTINCT ' '')
  1350.             . $this->walkSimpleArithmeticExpression($aggExpression->pathExpression) . ')';
  1351.     }
  1352.     /**
  1353.      * {@inheritdoc}
  1354.      */
  1355.     public function walkGroupByClause($groupByClause)
  1356.     {
  1357.         $sqlParts = [];
  1358.         foreach ($groupByClause->groupByItems as $groupByItem) {
  1359.             $sqlParts[] = $this->walkGroupByItem($groupByItem);
  1360.         }
  1361.         return ' GROUP BY ' implode(', '$sqlParts);
  1362.     }
  1363.     /**
  1364.      * {@inheritdoc}
  1365.      */
  1366.     public function walkGroupByItem($groupByItem)
  1367.     {
  1368.         // StateFieldPathExpression
  1369.         if ( ! is_string($groupByItem)) {
  1370.             return $this->walkPathExpression($groupByItem);
  1371.         }
  1372.         // ResultVariable
  1373.         if (isset($this->queryComponents[$groupByItem]['resultVariable'])) {
  1374.             $resultVariable $this->queryComponents[$groupByItem]['resultVariable'];
  1375.             if ($resultVariable instanceof AST\PathExpression) {
  1376.                 return $this->walkPathExpression($resultVariable);
  1377.             }
  1378.             if (isset($resultVariable->pathExpression)) {
  1379.                 return $this->walkPathExpression($resultVariable->pathExpression);
  1380.             }
  1381.             return $this->walkResultVariable($groupByItem);
  1382.         }
  1383.         // IdentificationVariable
  1384.         $sqlParts = [];
  1385.         foreach ($this->queryComponents[$groupByItem]['metadata']->fieldNames as $field) {
  1386.             $item       = new AST\PathExpression(AST\PathExpression::TYPE_STATE_FIELD$groupByItem$field);
  1387.             $item->type AST\PathExpression::TYPE_STATE_FIELD;
  1388.             $sqlParts[] = $this->walkPathExpression($item);
  1389.         }
  1390.         foreach ($this->queryComponents[$groupByItem]['metadata']->associationMappings as $mapping) {
  1391.             if ($mapping['isOwningSide'] && $mapping['type'] & ClassMetadataInfo::TO_ONE) {
  1392.                 $item       = new AST\PathExpression(AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION$groupByItem$mapping['fieldName']);
  1393.                 $item->type AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION;
  1394.                 $sqlParts[] = $this->walkPathExpression($item);
  1395.             }
  1396.         }
  1397.         return implode(', '$sqlParts);
  1398.     }
  1399.     /**
  1400.      * {@inheritdoc}
  1401.      */
  1402.     public function walkDeleteClause(AST\DeleteClause $deleteClause)
  1403.     {
  1404.         $class     $this->em->getClassMetadata($deleteClause->abstractSchemaName);
  1405.         $tableName $class->getTableName();
  1406.         $sql       'DELETE FROM ' $this->quoteStrategy->getTableName($class$this->platform);
  1407.         $this->setSQLTableAlias($tableName$tableName$deleteClause->aliasIdentificationVariable);
  1408.         $this->rootAliases[] = $deleteClause->aliasIdentificationVariable;
  1409.         return $sql;
  1410.     }
  1411.     /**
  1412.      * {@inheritdoc}
  1413.      */
  1414.     public function walkUpdateClause($updateClause)
  1415.     {
  1416.         $class     $this->em->getClassMetadata($updateClause->abstractSchemaName);
  1417.         $tableName $class->getTableName();
  1418.         $sql       'UPDATE ' $this->quoteStrategy->getTableName($class$this->platform);
  1419.         $this->setSQLTableAlias($tableName$tableName$updateClause->aliasIdentificationVariable);
  1420.         $this->rootAliases[] = $updateClause->aliasIdentificationVariable;
  1421.         $sql .= ' SET ' implode(', 'array_map([$this'walkUpdateItem'], $updateClause->updateItems));
  1422.         return $sql;
  1423.     }
  1424.     /**
  1425.      * {@inheritdoc}
  1426.      */
  1427.     public function walkUpdateItem($updateItem)
  1428.     {
  1429.         $useTableAliasesBefore $this->useSqlTableAliases;
  1430.         $this->useSqlTableAliases false;
  1431.         $sql      $this->walkPathExpression($updateItem->pathExpression) . ' = ';
  1432.         $newValue $updateItem->newValue;
  1433.         switch (true) {
  1434.             case ($newValue instanceof AST\Node):
  1435.                 $sql .= $newValue->dispatch($this);
  1436.                 break;
  1437.             case ($newValue === null):
  1438.                 $sql .= 'NULL';
  1439.                 break;
  1440.             default:
  1441.                 $sql .= $this->conn->quote($newValue);
  1442.                 break;
  1443.         }
  1444.         $this->useSqlTableAliases $useTableAliasesBefore;
  1445.         return $sql;
  1446.     }
  1447.     /**
  1448.      * {@inheritdoc}
  1449.      */
  1450.     public function walkWhereClause($whereClause)
  1451.     {
  1452.         $condSql  null !== $whereClause $this->walkConditionalExpression($whereClause->conditionalExpression) : '';
  1453.         $discrSql $this->_generateDiscriminatorColumnConditionSQL($this->rootAliases);
  1454.         if ($this->em->hasFilters()) {
  1455.             $filterClauses = [];
  1456.             foreach ($this->rootAliases as $dqlAlias) {
  1457.                 $class $this->queryComponents[$dqlAlias]['metadata'];
  1458.                 $tableAlias $this->getSQLTableAlias($class->table['name'], $dqlAlias);
  1459.                 if ($filterExpr $this->generateFilterConditionSQL($class$tableAlias)) {
  1460.                     $filterClauses[] = $filterExpr;
  1461.                 }
  1462.             }
  1463.             if (count($filterClauses)) {
  1464.                 if ($condSql) {
  1465.                     $condSql '(' $condSql ') AND ';
  1466.                 }
  1467.                 $condSql .= implode(' AND '$filterClauses);
  1468.             }
  1469.         }
  1470.         if ($condSql) {
  1471.             return ' WHERE ' . (( ! $discrSql) ? $condSql '(' $condSql ') AND ' $discrSql);
  1472.         }
  1473.         if ($discrSql) {
  1474.             return ' WHERE ' $discrSql;
  1475.         }
  1476.         return '';
  1477.     }
  1478.     /**
  1479.      * {@inheritdoc}
  1480.      */
  1481.     public function walkConditionalExpression($condExpr)
  1482.     {
  1483.         // Phase 2 AST optimization: Skip processing of ConditionalExpression
  1484.         // if only one ConditionalTerm is defined
  1485.         if ( ! ($condExpr instanceof AST\ConditionalExpression)) {
  1486.             return $this->walkConditionalTerm($condExpr);
  1487.         }
  1488.         return implode(' OR 'array_map([$this'walkConditionalTerm'], $condExpr->conditionalTerms));
  1489.     }
  1490.     /**
  1491.      * {@inheritdoc}
  1492.      */
  1493.     public function walkConditionalTerm($condTerm)
  1494.     {
  1495.         // Phase 2 AST optimization: Skip processing of ConditionalTerm
  1496.         // if only one ConditionalFactor is defined
  1497.         if ( ! ($condTerm instanceof AST\ConditionalTerm)) {
  1498.             return $this->walkConditionalFactor($condTerm);
  1499.         }
  1500.         return implode(' AND 'array_map([$this'walkConditionalFactor'], $condTerm->conditionalFactors));
  1501.     }
  1502.     /**
  1503.      * {@inheritdoc}
  1504.      */
  1505.     public function walkConditionalFactor($factor)
  1506.     {
  1507.         // Phase 2 AST optimization: Skip processing of ConditionalFactor
  1508.         // if only one ConditionalPrimary is defined
  1509.         return ( ! ($factor instanceof AST\ConditionalFactor))
  1510.             ? $this->walkConditionalPrimary($factor)
  1511.             : ($factor->not 'NOT ' '') . $this->walkConditionalPrimary($factor->conditionalPrimary);
  1512.     }
  1513.     /**
  1514.      * {@inheritdoc}
  1515.      */
  1516.     public function walkConditionalPrimary($primary)
  1517.     {
  1518.         if ($primary->isSimpleConditionalExpression()) {
  1519.             return $primary->simpleConditionalExpression->dispatch($this);
  1520.         }
  1521.         if ($primary->isConditionalExpression()) {
  1522.             $condExpr $primary->conditionalExpression;
  1523.             return '(' $this->walkConditionalExpression($condExpr) . ')';
  1524.         }
  1525.     }
  1526.     /**
  1527.      * {@inheritdoc}
  1528.      */
  1529.     public function walkExistsExpression($existsExpr)
  1530.     {
  1531.         $sql = ($existsExpr->not) ? 'NOT ' '';
  1532.         $sql .= 'EXISTS (' $this->walkSubselect($existsExpr->subselect) . ')';
  1533.         return $sql;
  1534.     }
  1535.     /**
  1536.      * {@inheritdoc}
  1537.      */
  1538.     public function walkCollectionMemberExpression($collMemberExpr)
  1539.     {
  1540.         $sql $collMemberExpr->not 'NOT ' '';
  1541.         $sql .= 'EXISTS (SELECT 1 FROM ';
  1542.         $entityExpr   $collMemberExpr->entityExpression;
  1543.         $collPathExpr $collMemberExpr->collectionValuedPathExpression;
  1544.         $fieldName $collPathExpr->field;
  1545.         $dqlAlias  $collPathExpr->identificationVariable;
  1546.         $class $this->queryComponents[$dqlAlias]['metadata'];
  1547.         switch (true) {
  1548.             // InputParameter
  1549.             case ($entityExpr instanceof AST\InputParameter):
  1550.                 $dqlParamKey $entityExpr->name;
  1551.                 $entitySql   '?';
  1552.                 break;
  1553.             // SingleValuedAssociationPathExpression | IdentificationVariable
  1554.             case ($entityExpr instanceof AST\PathExpression):
  1555.                 $entitySql $this->walkPathExpression($entityExpr);
  1556.                 break;
  1557.             default:
  1558.                 throw new \BadMethodCallException("Not implemented");
  1559.         }
  1560.         $assoc $class->associationMappings[$fieldName];
  1561.         if ($assoc['type'] == ClassMetadata::ONE_TO_MANY) {
  1562.             $targetClass      $this->em->getClassMetadata($assoc['targetEntity']);
  1563.             $targetTableAlias $this->getSQLTableAlias($targetClass->getTableName());
  1564.             $sourceTableAlias $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
  1565.             $sql .= $this->quoteStrategy->getTableName($targetClass$this->platform) . ' ' $targetTableAlias ' WHERE ';
  1566.             $owningAssoc $targetClass->associationMappings[$assoc['mappedBy']];
  1567.             $sqlParts    = [];
  1568.             foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) {
  1569.                 $targetColumn $this->quoteStrategy->getColumnName($class->fieldNames[$targetColumn], $class$this->platform);
  1570.                 $sqlParts[] = $sourceTableAlias '.' $targetColumn ' = ' $targetTableAlias '.' $sourceColumn;
  1571.             }
  1572.             foreach ($this->quoteStrategy->getIdentifierColumnNames($targetClass$this->platform) as $targetColumnName) {
  1573.                 if (isset($dqlParamKey)) {
  1574.                     $this->parserResult->addParameterMapping($dqlParamKey$this->sqlParamIndex++);
  1575.                 }
  1576.                 $sqlParts[] = $targetTableAlias '.'  $targetColumnName ' = ' $entitySql;
  1577.             }
  1578.             $sql .= implode(' AND '$sqlParts);
  1579.         } else { // many-to-many
  1580.             $targetClass $this->em->getClassMetadata($assoc['targetEntity']);
  1581.             $owningAssoc $assoc['isOwningSide'] ? $assoc $targetClass->associationMappings[$assoc['mappedBy']];
  1582.             $joinTable $owningAssoc['joinTable'];
  1583.             // SQL table aliases
  1584.             $joinTableAlias   $this->getSQLTableAlias($joinTable['name']);
  1585.             $targetTableAlias $this->getSQLTableAlias($targetClass->getTableName());
  1586.             $sourceTableAlias $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
  1587.             // join to target table
  1588.             $sql .= $this->quoteStrategy->getJoinTableName($owningAssoc$targetClass$this->platform) . ' ' $joinTableAlias
  1589.                 ' INNER JOIN ' $this->quoteStrategy->getTableName($targetClass$this->platform) . ' ' $targetTableAlias ' ON ';
  1590.             // join conditions
  1591.             $joinColumns  $assoc['isOwningSide'] ? $joinTable['inverseJoinColumns'] : $joinTable['joinColumns'];
  1592.             $joinSqlParts = [];
  1593.             foreach ($joinColumns as $joinColumn) {
  1594.                 $targetColumn $this->quoteStrategy->getColumnName($targetClass->fieldNames[$joinColumn['referencedColumnName']], $targetClass$this->platform);
  1595.                 $joinSqlParts[] = $joinTableAlias '.' $joinColumn['name'] . ' = ' $targetTableAlias '.' $targetColumn;
  1596.             }
  1597.             $sql .= implode(' AND '$joinSqlParts);
  1598.             $sql .= ' WHERE ';
  1599.             $joinColumns $assoc['isOwningSide'] ? $joinTable['joinColumns'] : $joinTable['inverseJoinColumns'];
  1600.             $sqlParts    = [];
  1601.             foreach ($joinColumns as $joinColumn) {
  1602.                 $targetColumn $this->quoteStrategy->getColumnName($class->fieldNames[$joinColumn['referencedColumnName']], $class$this->platform);
  1603.                 $sqlParts[] = $joinTableAlias '.' $joinColumn['name'] . ' = ' $sourceTableAlias '.' $targetColumn;
  1604.             }
  1605.             foreach ($this->quoteStrategy->getIdentifierColumnNames($targetClass$this->platform) as $targetColumnName) {
  1606.                 if (isset($dqlParamKey)) {
  1607.                     $this->parserResult->addParameterMapping($dqlParamKey$this->sqlParamIndex++);
  1608.                 }
  1609.                 $sqlParts[] = $targetTableAlias '.' $targetColumnName ' IN (' $entitySql ')';
  1610.             }
  1611.             $sql .= implode(' AND '$sqlParts);
  1612.         }
  1613.         return $sql ')';
  1614.     }
  1615.     /**
  1616.      * {@inheritdoc}
  1617.      */
  1618.     public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr)
  1619.     {
  1620.         $sizeFunc = new AST\Functions\SizeFunction('size');
  1621.         $sizeFunc->collectionPathExpression $emptyCollCompExpr->expression;
  1622.         return $sizeFunc->getSql($this) . ($emptyCollCompExpr->not ' > 0' ' = 0');
  1623.     }
  1624.     /**
  1625.      * {@inheritdoc}
  1626.      */
  1627.     public function walkNullComparisonExpression($nullCompExpr)
  1628.     {
  1629.         $expression $nullCompExpr->expression;
  1630.         $comparison ' IS' . ($nullCompExpr->not ' NOT' '') . ' NULL';
  1631.         // Handle ResultVariable
  1632.         if (is_string($expression) && isset($this->queryComponents[$expression]['resultVariable'])) {
  1633.             return $this->walkResultVariable($expression) . $comparison;
  1634.         }
  1635.         // Handle InputParameter mapping inclusion to ParserResult
  1636.         if ($expression instanceof AST\InputParameter) {
  1637.             return $this->walkInputParameter($expression) . $comparison;
  1638.         }
  1639.         return $expression->dispatch($this) . $comparison;
  1640.     }
  1641.     /**
  1642.      * {@inheritdoc}
  1643.      */
  1644.     public function walkInExpression($inExpr)
  1645.     {
  1646.         $sql $this->walkArithmeticExpression($inExpr->expression) . ($inExpr->not ' NOT' '') . ' IN (';
  1647.         $sql .= ($inExpr->subselect)
  1648.             ? $this->walkSubselect($inExpr->subselect)
  1649.             : implode(', 'array_map([$this'walkInParameter'], $inExpr->literals));
  1650.         $sql .= ')';
  1651.         return $sql;
  1652.     }
  1653.     /**
  1654.      * {@inheritdoc}
  1655.      * @throws \Doctrine\ORM\Query\QueryException
  1656.      */
  1657.     public function walkInstanceOfExpression($instanceOfExpr)
  1658.     {
  1659.         $sql '';
  1660.         $dqlAlias $instanceOfExpr->identificationVariable;
  1661.         $discrClass $class $this->queryComponents[$dqlAlias]['metadata'];
  1662.         if ($class->discriminatorColumn) {
  1663.             $discrClass $this->em->getClassMetadata($class->rootEntityName);
  1664.         }
  1665.         if ($this->useSqlTableAliases) {
  1666.             $sql .= $this->getSQLTableAlias($discrClass->getTableName(), $dqlAlias) . '.';
  1667.         }
  1668.         $sql .= $class->discriminatorColumn['name'] . ($instanceOfExpr->not ' NOT IN ' ' IN ');
  1669.         $sql .= $this->getChildDiscriminatorsFromClassMetadata($discrClass$instanceOfExpr);
  1670.         return $sql;
  1671.     }
  1672.     /**
  1673.      * {@inheritdoc}
  1674.      */
  1675.     public function walkInParameter($inParam)
  1676.     {
  1677.         return $inParam instanceof AST\InputParameter
  1678.             $this->walkInputParameter($inParam)
  1679.             : $this->walkLiteral($inParam);
  1680.     }
  1681.     /**
  1682.      * {@inheritdoc}
  1683.      */
  1684.     public function walkLiteral($literal)
  1685.     {
  1686.         switch ($literal->type) {
  1687.             case AST\Literal::STRING:
  1688.                 return $this->conn->quote($literal->value);
  1689.             case AST\Literal::BOOLEAN:
  1690.                 return $this->conn->getDatabasePlatform()->convertBooleans('true' === strtolower($literal->value));
  1691.             case AST\Literal::NUMERIC:
  1692.                 return $literal->value;
  1693.             default:
  1694.                 throw QueryException::invalidLiteral($literal);
  1695.         }
  1696.     }
  1697.     /**
  1698.      * {@inheritdoc}
  1699.      */
  1700.     public function walkBetweenExpression($betweenExpr)
  1701.     {
  1702.         $sql $this->walkArithmeticExpression($betweenExpr->expression);
  1703.         if ($betweenExpr->not) {
  1704.             $sql .= ' NOT';
  1705.         }
  1706.         $sql .= ' BETWEEN ' $this->walkArithmeticExpression($betweenExpr->leftBetweenExpression)
  1707.             . ' AND ' $this->walkArithmeticExpression($betweenExpr->rightBetweenExpression);
  1708.         return $sql;
  1709.     }
  1710.     /**
  1711.      * {@inheritdoc}
  1712.      */
  1713.     public function walkLikeExpression($likeExpr)
  1714.     {
  1715.         $stringExpr $likeExpr->stringExpression;
  1716.         $leftExpr   = (is_string($stringExpr) && isset($this->queryComponents[$stringExpr]['resultVariable']))
  1717.             ? $this->walkResultVariable($stringExpr)
  1718.             : $stringExpr->dispatch($this);
  1719.         $sql $leftExpr . ($likeExpr->not ' NOT' '') . ' LIKE ';
  1720.         if ($likeExpr->stringPattern instanceof AST\InputParameter) {
  1721.             $sql .= $this->walkInputParameter($likeExpr->stringPattern);
  1722.         } elseif ($likeExpr->stringPattern instanceof AST\Functions\FunctionNode) {
  1723.             $sql .= $this->walkFunction($likeExpr->stringPattern);
  1724.         } elseif ($likeExpr->stringPattern instanceof AST\PathExpression) {
  1725.             $sql .= $this->walkPathExpression($likeExpr->stringPattern);
  1726.         } else {
  1727.             $sql .= $this->walkLiteral($likeExpr->stringPattern);
  1728.         }
  1729.         if ($likeExpr->escapeChar) {
  1730.             $sql .= ' ESCAPE ' $this->walkLiteral($likeExpr->escapeChar);
  1731.         }
  1732.         return $sql;
  1733.     }
  1734.     /**
  1735.      * {@inheritdoc}
  1736.      */
  1737.     public function walkStateFieldPathExpression($stateFieldPathExpression)
  1738.     {
  1739.         return $this->walkPathExpression($stateFieldPathExpression);
  1740.     }
  1741.     /**
  1742.      * {@inheritdoc}
  1743.      */
  1744.     public function walkComparisonExpression($compExpr)
  1745.     {
  1746.         $leftExpr  $compExpr->leftExpression;
  1747.         $rightExpr $compExpr->rightExpression;
  1748.         $sql       '';
  1749.         $sql .= ($leftExpr instanceof AST\Node)
  1750.             ? $leftExpr->dispatch($this)
  1751.             : (is_numeric($leftExpr) ? $leftExpr $this->conn->quote($leftExpr));
  1752.         $sql .= ' ' $compExpr->operator ' ';
  1753.         $sql .= ($rightExpr instanceof AST\Node)
  1754.             ? $rightExpr->dispatch($this)
  1755.             : (is_numeric($rightExpr) ? $rightExpr $this->conn->quote($rightExpr));
  1756.         return $sql;
  1757.     }
  1758.     /**
  1759.      * {@inheritdoc}
  1760.      */
  1761.     public function walkInputParameter($inputParam)
  1762.     {
  1763.         $this->parserResult->addParameterMapping($inputParam->name$this->sqlParamIndex++);
  1764.         $parameter $this->query->getParameter($inputParam->name);
  1765.         if ($parameter && Type::hasType($type $parameter->getType())) {
  1766.             return Type::getType($type)->convertToDatabaseValueSQL('?'$this->platform);
  1767.         }
  1768.         return '?';
  1769.     }
  1770.     /**
  1771.      * {@inheritdoc}
  1772.      */
  1773.     public function walkArithmeticExpression($arithmeticExpr)
  1774.     {
  1775.         return ($arithmeticExpr->isSimpleArithmeticExpression())
  1776.             ? $this->walkSimpleArithmeticExpression($arithmeticExpr->simpleArithmeticExpression)
  1777.             : '(' $this->walkSubselect($arithmeticExpr->subselect) . ')';
  1778.     }
  1779.     /**
  1780.      * {@inheritdoc}
  1781.      */
  1782.     public function walkSimpleArithmeticExpression($simpleArithmeticExpr)
  1783.     {
  1784.         if ( ! ($simpleArithmeticExpr instanceof AST\SimpleArithmeticExpression)) {
  1785.             return $this->walkArithmeticTerm($simpleArithmeticExpr);
  1786.         }
  1787.         return implode(' 'array_map([$this'walkArithmeticTerm'], $simpleArithmeticExpr->arithmeticTerms));
  1788.     }
  1789.     /**
  1790.      * {@inheritdoc}
  1791.      */
  1792.     public function walkArithmeticTerm($term)
  1793.     {
  1794.         if (is_string($term)) {
  1795.             return (isset($this->queryComponents[$term]))
  1796.                 ? $this->walkResultVariable($this->queryComponents[$term]['token']['value'])
  1797.                 : $term;
  1798.         }
  1799.         // Phase 2 AST optimization: Skip processing of ArithmeticTerm
  1800.         // if only one ArithmeticFactor is defined
  1801.         if ( ! ($term instanceof AST\ArithmeticTerm)) {
  1802.             return $this->walkArithmeticFactor($term);
  1803.         }
  1804.         return implode(' 'array_map([$this'walkArithmeticFactor'], $term->arithmeticFactors));
  1805.     }
  1806.     /**
  1807.      * {@inheritdoc}
  1808.      */
  1809.     public function walkArithmeticFactor($factor)
  1810.     {
  1811.         if (is_string($factor)) {
  1812.             return (isset($this->queryComponents[$factor]))
  1813.                 ? $this->walkResultVariable($this->queryComponents[$factor]['token']['value'])
  1814.                 : $factor;
  1815.         }
  1816.         // Phase 2 AST optimization: Skip processing of ArithmeticFactor
  1817.         // if only one ArithmeticPrimary is defined
  1818.         if ( ! ($factor instanceof AST\ArithmeticFactor)) {
  1819.             return $this->walkArithmeticPrimary($factor);
  1820.         }
  1821.         $sign $factor->isNegativeSigned() ? '-' : ($factor->isPositiveSigned() ? '+' '');
  1822.         return $sign $this->walkArithmeticPrimary($factor->arithmeticPrimary);
  1823.     }
  1824.     /**
  1825.      * Walks down an ArithmeticPrimary that represents an AST node, thereby generating the appropriate SQL.
  1826.      *
  1827.      * @param mixed $primary
  1828.      *
  1829.      * @return string The SQL.
  1830.      */
  1831.     public function walkArithmeticPrimary($primary)
  1832.     {
  1833.         if ($primary instanceof AST\SimpleArithmeticExpression) {
  1834.             return '(' $this->walkSimpleArithmeticExpression($primary) . ')';
  1835.         }
  1836.         if ($primary instanceof AST\Node) {
  1837.             return $primary->dispatch($this);
  1838.         }
  1839.         return $this->walkEntityIdentificationVariable($primary);
  1840.     }
  1841.     /**
  1842.      * {@inheritdoc}
  1843.      */
  1844.     public function walkStringPrimary($stringPrimary)
  1845.     {
  1846.         return (is_string($stringPrimary))
  1847.             ? $this->conn->quote($stringPrimary)
  1848.             : $stringPrimary->dispatch($this);
  1849.     }
  1850.     /**
  1851.      * {@inheritdoc}
  1852.      */
  1853.     public function walkResultVariable($resultVariable)
  1854.     {
  1855.         $resultAlias $this->scalarResultAliasMap[$resultVariable];
  1856.         if (is_array($resultAlias)) {
  1857.             return implode(', '$resultAlias);
  1858.         }
  1859.         return $resultAlias;
  1860.     }
  1861.     /**
  1862.      * @param ClassMetadataInfo $rootClass
  1863.      * @param AST\InstanceOfExpression $instanceOfExpr
  1864.      * @return string The list in parentheses of valid child discriminators from the given class
  1865.      * @throws QueryException
  1866.      */
  1867.     private function getChildDiscriminatorsFromClassMetadata(ClassMetadataInfo $rootClassAST\InstanceOfExpression $instanceOfExpr): string
  1868.     {
  1869.         $sqlParameterList = [];
  1870.         $discriminators = [];
  1871.         foreach ($instanceOfExpr->value as $parameter) {
  1872.             if ($parameter instanceof AST\InputParameter) {
  1873.                 $this->rsm->discriminatorParameters[$parameter->name] = $parameter->name;
  1874.                 $sqlParameterList[] = $this->walkInParameter($parameter);
  1875.                 continue;
  1876.             }
  1877.             $metadata $this->em->getClassMetadata($parameter);
  1878.             if ($metadata->getName() !== $rootClass->name && ! $metadata->getReflectionClass()->isSubclassOf($rootClass->name)) {
  1879.                 throw QueryException::instanceOfUnrelatedClass($parameter$rootClass->name);
  1880.             }
  1881.             $discriminators += HierarchyDiscriminatorResolver::resolveDiscriminatorsForClass($metadata$this->em);
  1882.         }
  1883.         foreach (array_keys($discriminators) as $dis) {
  1884.             $sqlParameterList[] = $this->conn->quote($dis);
  1885.         }
  1886.         return '(' implode(', '$sqlParameterList) . ')';
  1887.     }
  1888. }