PageRenderTime 78ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/Doctrine/ORM/Query/SqlWalker.php

https://bitbucket.org/chives/doctrine2
PHP | 2306 lines | 1273 code | 426 blank | 607 comment | 151 complexity | 05c6614576dbae890645cf47497d251f MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  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\Query;
  24. use Doctrine\ORM\Query\QueryException;
  25. use Doctrine\ORM\Mapping\ClassMetadataInfo;
  26. /**
  27. * The SqlWalker is a TreeWalker that walks over a DQL AST and constructs
  28. * the corresponding SQL.
  29. *
  30. * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
  31. * @author Roman Borschel <roman@code-factory.org>
  32. * @author Benjamin Eberlei <kontakt@beberlei.de>
  33. * @author Alexander <iam.asm89@gmail.com>
  34. * @author Fabio B. Silva <fabio.bat.silva@gmail.com>
  35. * @since 2.0
  36. * @todo Rename: SQLWalker
  37. */
  38. class SqlWalker implements TreeWalker
  39. {
  40. /**
  41. * @var string
  42. */
  43. const HINT_DISTINCT = 'doctrine.distinct';
  44. /**
  45. * @var ResultSetMapping
  46. */
  47. private $rsm;
  48. /**
  49. * Counters for generating unique column aliases.
  50. *
  51. * @var integer
  52. */
  53. private $aliasCounter = 0;
  54. /**
  55. * Counters for generating unique table aliases.
  56. *
  57. * @var integer
  58. */
  59. private $tableAliasCounter = 0;
  60. /**
  61. * Counters for generating unique scalar result.
  62. *
  63. * @var integer
  64. */
  65. private $scalarResultCounter = 1;
  66. /**
  67. * Counters for generating unique parameter indexes.
  68. *
  69. * @var integer
  70. */
  71. private $sqlParamIndex = 0;
  72. /**
  73. * Counters for generating indexes.
  74. *
  75. * @var integer
  76. */
  77. private $newObjectCounter = 0;
  78. /**
  79. * @var ParserResult
  80. */
  81. private $parserResult;
  82. /**
  83. * @var EntityManager
  84. */
  85. private $em;
  86. /**
  87. * @var \Doctrine\DBAL\Connection
  88. */
  89. private $conn;
  90. /**
  91. * @var AbstractQuery
  92. */
  93. private $query;
  94. /**
  95. * @var array
  96. */
  97. private $tableAliasMap = array();
  98. /**
  99. * Map from result variable names to their SQL column alias names.
  100. *
  101. * @var array
  102. */
  103. private $scalarResultAliasMap = array();
  104. /**
  105. * Map from DQL-Alias + Field-Name to SQL Column Alias
  106. *
  107. * @var array
  108. */
  109. private $scalarFields = array();
  110. /**
  111. * Map of all components/classes that appear in the DQL query.
  112. *
  113. * @var array
  114. */
  115. private $queryComponents;
  116. /**
  117. * A list of classes that appear in non-scalar SelectExpressions.
  118. *
  119. * @var array
  120. */
  121. private $selectedClasses = array();
  122. /**
  123. * The DQL alias of the root class of the currently traversed query.
  124. *
  125. * @var array
  126. */
  127. private $rootAliases = array();
  128. /**
  129. * Flag that indicates whether to generate SQL table aliases in the SQL.
  130. * These should only be generated for SELECT queries, not for UPDATE/DELETE.
  131. *
  132. * @var boolean
  133. */
  134. private $useSqlTableAliases = true;
  135. /**
  136. * The database platform abstraction.
  137. *
  138. * @var AbstractPlatform
  139. */
  140. private $platform;
  141. /**
  142. * The quote strategy.
  143. *
  144. * @var \Doctrine\ORM\Mapping\QuoteStrategy
  145. */
  146. private $quoteStrategy;
  147. /**
  148. * {@inheritDoc}
  149. */
  150. public function __construct($query, $parserResult, array $queryComponents)
  151. {
  152. $this->query = $query;
  153. $this->parserResult = $parserResult;
  154. $this->queryComponents = $queryComponents;
  155. $this->rsm = $parserResult->getResultSetMapping();
  156. $this->em = $query->getEntityManager();
  157. $this->conn = $this->em->getConnection();
  158. $this->platform = $this->conn->getDatabasePlatform();
  159. $this->quoteStrategy = $this->em->getConfiguration()->getQuoteStrategy();
  160. }
  161. /**
  162. * Gets the Query instance used by the walker.
  163. *
  164. * @return Query.
  165. */
  166. public function getQuery()
  167. {
  168. return $this->query;
  169. }
  170. /**
  171. * Gets the Connection used by the walker.
  172. *
  173. * @return Connection
  174. */
  175. public function getConnection()
  176. {
  177. return $this->conn;
  178. }
  179. /**
  180. * Gets the EntityManager used by the walker.
  181. *
  182. * @return EntityManager
  183. */
  184. public function getEntityManager()
  185. {
  186. return $this->em;
  187. }
  188. /**
  189. * Gets the information about a single query component.
  190. *
  191. * @param string $dqlAlias The DQL alias.
  192. * @return array
  193. */
  194. public function getQueryComponent($dqlAlias)
  195. {
  196. return $this->queryComponents[$dqlAlias];
  197. }
  198. /**
  199. * Return internal queryComponents array
  200. *
  201. * @return array
  202. */
  203. public function getQueryComponents()
  204. {
  205. return $this->queryComponents;
  206. }
  207. /**
  208. * Set or override a query component for a given dql alias.
  209. *
  210. * @param string $dqlAlias The DQL alias.
  211. * @param array $queryComponent
  212. */
  213. public function setQueryComponent($dqlAlias, array $queryComponent)
  214. {
  215. $requiredKeys = array('metadata', 'parent', 'relation', 'map', 'nestingLevel', 'token');
  216. if (array_diff($requiredKeys, array_keys($queryComponent))) {
  217. throw QueryException::invalidQueryComponent($dqlAlias);
  218. }
  219. $this->queryComponents[$dqlAlias] = $queryComponent;
  220. }
  221. /**
  222. * Gets an executor that can be used to execute the result of this walker.
  223. *
  224. * @return AbstractExecutor
  225. */
  226. public function getExecutor($AST)
  227. {
  228. switch (true) {
  229. case ($AST instanceof AST\DeleteStatement):
  230. $primaryClass = $this->em->getClassMetadata($AST->deleteClause->abstractSchemaName);
  231. return ($primaryClass->isInheritanceTypeJoined())
  232. ? new Exec\MultiTableDeleteExecutor($AST, $this)
  233. : new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
  234. case ($AST instanceof AST\UpdateStatement):
  235. $primaryClass = $this->em->getClassMetadata($AST->updateClause->abstractSchemaName);
  236. return ($primaryClass->isInheritanceTypeJoined())
  237. ? new Exec\MultiTableUpdateExecutor($AST, $this)
  238. : new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
  239. default:
  240. return new Exec\SingleSelectExecutor($AST, $this);
  241. }
  242. }
  243. /**
  244. * Generates a unique, short SQL table alias.
  245. *
  246. * @param string $tableName Table name
  247. * @param string $dqlAlias The DQL alias.
  248. * @return string Generated table alias.
  249. */
  250. public function getSQLTableAlias($tableName, $dqlAlias = '')
  251. {
  252. $tableName .= ($dqlAlias) ? '@[' . $dqlAlias . ']' : '';
  253. if ( ! isset($this->tableAliasMap[$tableName])) {
  254. $this->tableAliasMap[$tableName] = strtolower(substr($tableName, 0, 1)) . $this->tableAliasCounter++ . '_';
  255. }
  256. return $this->tableAliasMap[$tableName];
  257. }
  258. /**
  259. * Forces the SqlWalker to use a specific alias for a table name, rather than
  260. * generating an alias on its own.
  261. *
  262. * @param string $tableName
  263. * @param string $alias
  264. * @param string $dqlAlias
  265. * @return string
  266. */
  267. public function setSQLTableAlias($tableName, $alias, $dqlAlias = '')
  268. {
  269. $tableName .= ($dqlAlias) ? '@[' . $dqlAlias . ']' : '';
  270. $this->tableAliasMap[$tableName] = $alias;
  271. return $alias;
  272. }
  273. /**
  274. * Gets an SQL column alias for a column name.
  275. *
  276. * @param string $columnName
  277. * @return string
  278. */
  279. public function getSQLColumnAlias($columnName)
  280. {
  281. return $this->quoteStrategy->getColumnAlias($columnName, $this->aliasCounter++, $this->platform);
  282. }
  283. /**
  284. * Generates the SQL JOINs that are necessary for Class Table Inheritance
  285. * for the given class.
  286. *
  287. * @param ClassMetadata $class The class for which to generate the joins.
  288. * @param string $dqlAlias The DQL alias of the class.
  289. * @return string The SQL.
  290. */
  291. private function _generateClassTableInheritanceJoins($class, $dqlAlias)
  292. {
  293. $sql = '';
  294. $baseTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
  295. // INNER JOIN parent class tables
  296. foreach ($class->parentClasses as $parentClassName) {
  297. $parentClass = $this->em->getClassMetadata($parentClassName);
  298. $tableAlias = $this->getSQLTableAlias($parentClass->getTableName(), $dqlAlias);
  299. // If this is a joined association we must use left joins to preserve the correct result.
  300. $sql .= isset($this->queryComponents[$dqlAlias]['relation']) ? ' LEFT ' : ' INNER ';
  301. $sql .= 'JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->platform) . ' ' . $tableAlias . ' ON ';
  302. $sqlParts = array();
  303. foreach ($this->quoteStrategy->getIdentifierColumnNames($class, $this->platform) as $columnName) {
  304. $sqlParts[] = $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
  305. }
  306. // Add filters on the root class
  307. if ($filterSql = $this->generateFilterConditionSQL($parentClass, $tableAlias)) {
  308. $sqlParts[] = $filterSql;
  309. }
  310. $sql .= implode(' AND ', $sqlParts);
  311. }
  312. // Ignore subclassing inclusion if partial objects is disallowed
  313. if ($this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {
  314. return $sql;
  315. }
  316. // LEFT JOIN child class tables
  317. foreach ($class->subClasses as $subClassName) {
  318. $subClass = $this->em->getClassMetadata($subClassName);
  319. $tableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
  320. $sql .= ' LEFT JOIN ' . $this->quoteStrategy->getTableName($subClass, $this->platform) . ' ' . $tableAlias . ' ON ';
  321. $sqlParts = array();
  322. foreach ($this->quoteStrategy->getIdentifierColumnNames($subClass, $this->platform) as $columnName) {
  323. $sqlParts[] = $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
  324. }
  325. $sql .= implode(' AND ', $sqlParts);
  326. }
  327. return $sql;
  328. }
  329. private function _generateOrderedCollectionOrderByItems()
  330. {
  331. $sqlParts = array();
  332. foreach ($this->selectedClasses as $selectedClass) {
  333. $dqlAlias = $selectedClass['dqlAlias'];
  334. $qComp = $this->queryComponents[$dqlAlias];
  335. if ( ! isset($qComp['relation']['orderBy'])) continue;
  336. foreach ($qComp['relation']['orderBy'] as $fieldName => $orientation) {
  337. $columnName = $this->quoteStrategy->getColumnName($fieldName, $qComp['metadata'], $this->platform);
  338. $tableName = ($qComp['metadata']->isInheritanceTypeJoined())
  339. ? $this->em->getUnitOfWork()->getEntityPersister($qComp['metadata']->name)->getOwningTable($fieldName)
  340. : $qComp['metadata']->getTableName();
  341. $sqlParts[] = $this->getSQLTableAlias($tableName, $dqlAlias) . '.' . $columnName . ' ' . $orientation;
  342. }
  343. }
  344. return implode(', ', $sqlParts);
  345. }
  346. /**
  347. * Generates a discriminator column SQL condition for the class with the given DQL alias.
  348. *
  349. * @param array $dqlAliases List of root DQL aliases to inspect for discriminator restrictions.
  350. * @return string
  351. */
  352. private function _generateDiscriminatorColumnConditionSQL(array $dqlAliases)
  353. {
  354. $sqlParts = array();
  355. foreach ($dqlAliases as $dqlAlias) {
  356. $class = $this->queryComponents[$dqlAlias]['metadata'];
  357. if ( ! $class->isInheritanceTypeSingleTable()) continue;
  358. $conn = $this->em->getConnection();
  359. $values = array();
  360. if ($class->discriminatorValue !== null) { // discrimnators can be 0
  361. $values[] = $conn->quote($class->discriminatorValue);
  362. }
  363. foreach ($class->subClasses as $subclassName) {
  364. $values[] = $conn->quote($this->em->getClassMetadata($subclassName)->discriminatorValue);
  365. }
  366. $sqlParts[] = (($this->useSqlTableAliases) ? $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.' : '')
  367. . $class->discriminatorColumn['name'] . ' IN (' . implode(', ', $values) . ')';
  368. }
  369. $sql = implode(' AND ', $sqlParts);
  370. return (count($sqlParts) > 1) ? '(' . $sql . ')' : $sql;
  371. }
  372. /**
  373. * Generates the filter SQL for a given entity and table alias.
  374. *
  375. * @param ClassMetadata $targetEntity Metadata of the target entity.
  376. * @param string $targetTableAlias The table alias of the joined/selected table.
  377. *
  378. * @return string The SQL query part to add to a query.
  379. */
  380. private function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias)
  381. {
  382. if (!$this->em->hasFilters()) {
  383. return '';
  384. }
  385. switch($targetEntity->inheritanceType) {
  386. case ClassMetadata::INHERITANCE_TYPE_NONE:
  387. break;
  388. case ClassMetadata::INHERITANCE_TYPE_JOINED:
  389. // The classes in the inheritance will be added to the query one by one,
  390. // but only the root node is getting filtered
  391. if ($targetEntity->name !== $targetEntity->rootEntityName) {
  392. return '';
  393. }
  394. break;
  395. case ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE:
  396. // With STI the table will only be queried once, make sure that the filters
  397. // are added to the root entity
  398. $targetEntity = $this->em->getClassMetadata($targetEntity->rootEntityName);
  399. break;
  400. default:
  401. //@todo: throw exception?
  402. return '';
  403. break;
  404. }
  405. $filterClauses = array();
  406. foreach ($this->em->getFilters()->getEnabledFilters() as $filter) {
  407. if ('' !== $filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) {
  408. $filterClauses[] = '(' . $filterExpr . ')';
  409. }
  410. }
  411. return implode(' AND ', $filterClauses);
  412. }
  413. /**
  414. * Walks down a SelectStatement AST node, thereby generating the appropriate SQL.
  415. *
  416. * @return string The SQL.
  417. */
  418. public function walkSelectStatement(AST\SelectStatement $AST)
  419. {
  420. $sql = $this->walkSelectClause($AST->selectClause);
  421. $sql .= $this->walkFromClause($AST->fromClause);
  422. $sql .= $this->walkWhereClause($AST->whereClause);
  423. $sql .= $AST->groupByClause ? $this->walkGroupByClause($AST->groupByClause) : '';
  424. $sql .= $AST->havingClause ? $this->walkHavingClause($AST->havingClause) : '';
  425. if (($orderByClause = $AST->orderByClause) !== null) {
  426. $sql .= $AST->orderByClause ? $this->walkOrderByClause($AST->orderByClause) : '';
  427. } else if (($orderBySql = $this->_generateOrderedCollectionOrderByItems()) !== '') {
  428. $sql .= ' ORDER BY ' . $orderBySql;
  429. }
  430. $sql = $this->platform->modifyLimitQuery(
  431. $sql, $this->query->getMaxResults(), $this->query->getFirstResult()
  432. );
  433. if (($lockMode = $this->query->getHint(Query::HINT_LOCK_MODE)) !== false) {
  434. switch ($lockMode) {
  435. case LockMode::PESSIMISTIC_READ:
  436. $sql .= ' ' . $this->platform->getReadLockSQL();
  437. break;
  438. case LockMode::PESSIMISTIC_WRITE:
  439. $sql .= ' ' . $this->platform->getWriteLockSQL();
  440. break;
  441. case LockMode::OPTIMISTIC:
  442. foreach ($this->selectedClasses as $selectedClass) {
  443. if ( ! $selectedClass['class']->isVersioned) {
  444. throw \Doctrine\ORM\OptimisticLockException::lockFailed($selectedClass['class']->name);
  445. }
  446. }
  447. break;
  448. case LockMode::NONE:
  449. break;
  450. default:
  451. throw \Doctrine\ORM\Query\QueryException::invalidLockMode();
  452. }
  453. }
  454. return $sql;
  455. }
  456. /**
  457. * Walks down an UpdateStatement AST node, thereby generating the appropriate SQL.
  458. *
  459. * @param UpdateStatement
  460. * @return string The SQL.
  461. */
  462. public function walkUpdateStatement(AST\UpdateStatement $AST)
  463. {
  464. $this->useSqlTableAliases = false;
  465. return $this->walkUpdateClause($AST->updateClause)
  466. . $this->walkWhereClause($AST->whereClause);
  467. }
  468. /**
  469. * Walks down a DeleteStatement AST node, thereby generating the appropriate SQL.
  470. *
  471. * @param DeleteStatement
  472. * @return string The SQL.
  473. */
  474. public function walkDeleteStatement(AST\DeleteStatement $AST)
  475. {
  476. $this->useSqlTableAliases = false;
  477. return $this->walkDeleteClause($AST->deleteClause)
  478. . $this->walkWhereClause($AST->whereClause);
  479. }
  480. /**
  481. * Walks down an IdentificationVariable AST node, thereby generating the appropriate SQL.
  482. * This one differs of ->walkIdentificationVariable() because it generates the entity identifiers.
  483. *
  484. * @param string $identVariable
  485. * @return string
  486. */
  487. public function walkEntityIdentificationVariable($identVariable)
  488. {
  489. $class = $this->queryComponents[$identVariable]['metadata'];
  490. $tableAlias = $this->getSQLTableAlias($class->getTableName(), $identVariable);
  491. $sqlParts = array();
  492. foreach ($this->quoteStrategy->getIdentifierColumnNames($class, $this->platform) as $columnName) {
  493. $sqlParts[] = $tableAlias . '.' . $columnName;
  494. }
  495. return implode(', ', $sqlParts);
  496. }
  497. /**
  498. * Walks down an IdentificationVariable (no AST node associated), thereby generating the SQL.
  499. *
  500. * @param string $identificationVariable
  501. * @param string $fieldName
  502. * @return string The SQL.
  503. */
  504. public function walkIdentificationVariable($identificationVariable, $fieldName = null)
  505. {
  506. $class = $this->queryComponents[$identificationVariable]['metadata'];
  507. if (
  508. $fieldName !== null && $class->isInheritanceTypeJoined() &&
  509. isset($class->fieldMappings[$fieldName]['inherited'])
  510. ) {
  511. $class = $this->em->getClassMetadata($class->fieldMappings[$fieldName]['inherited']);
  512. }
  513. return $this->getSQLTableAlias($class->getTableName(), $identificationVariable);
  514. }
  515. /**
  516. * Walks down a PathExpression AST node, thereby generating the appropriate SQL.
  517. *
  518. * @param mixed
  519. * @return string The SQL.
  520. */
  521. public function walkPathExpression($pathExpr)
  522. {
  523. $sql = '';
  524. switch ($pathExpr->type) {
  525. case AST\PathExpression::TYPE_STATE_FIELD:
  526. $fieldName = $pathExpr->field;
  527. $dqlAlias = $pathExpr->identificationVariable;
  528. $class = $this->queryComponents[$dqlAlias]['metadata'];
  529. if ($this->useSqlTableAliases) {
  530. $sql .= $this->walkIdentificationVariable($dqlAlias, $fieldName) . '.';
  531. }
  532. $sql .= $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform);
  533. break;
  534. case AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION:
  535. // 1- the owning side:
  536. // Just use the foreign key, i.e. u.group_id
  537. $fieldName = $pathExpr->field;
  538. $dqlAlias = $pathExpr->identificationVariable;
  539. $class = $this->queryComponents[$dqlAlias]['metadata'];
  540. if (isset($class->associationMappings[$fieldName]['inherited'])) {
  541. $class = $this->em->getClassMetadata($class->associationMappings[$fieldName]['inherited']);
  542. }
  543. $assoc = $class->associationMappings[$fieldName];
  544. if ( ! $assoc['isOwningSide']) {
  545. throw QueryException::associationPathInverseSideNotSupported();
  546. }
  547. // COMPOSITE KEYS NOT (YET?) SUPPORTED
  548. if (count($assoc['sourceToTargetKeyColumns']) > 1) {
  549. throw QueryException::associationPathCompositeKeyNotSupported();
  550. }
  551. if ($this->useSqlTableAliases) {
  552. $sql .= $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.';
  553. }
  554. $sql .= reset($assoc['targetToSourceKeyColumns']);
  555. break;
  556. default:
  557. throw QueryException::invalidPathExpression($pathExpr);
  558. }
  559. return $sql;
  560. }
  561. /**
  562. * Walks down a SelectClause AST node, thereby generating the appropriate SQL.
  563. *
  564. * @param $selectClause
  565. * @return string The SQL.
  566. */
  567. public function walkSelectClause($selectClause)
  568. {
  569. $sql = 'SELECT ' . (($selectClause->isDistinct) ? 'DISTINCT ' : '');
  570. $sqlSelectExpressions = array_filter(array_map(array($this, 'walkSelectExpression'), $selectClause->selectExpressions));
  571. if ($this->query->getHint(Query::HINT_INTERNAL_ITERATION) == true && $selectClause->isDistinct) {
  572. $this->query->setHint(self::HINT_DISTINCT, true);
  573. }
  574. $addMetaColumns = ! $this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) &&
  575. $this->query->getHydrationMode() == Query::HYDRATE_OBJECT
  576. ||
  577. $this->query->getHydrationMode() != Query::HYDRATE_OBJECT &&
  578. $this->query->getHint(Query::HINT_INCLUDE_META_COLUMNS);
  579. foreach ($this->selectedClasses as $selectedClass) {
  580. $class = $selectedClass['class'];
  581. $dqlAlias = $selectedClass['dqlAlias'];
  582. $resultAlias = $selectedClass['resultAlias'];
  583. // Register as entity or joined entity result
  584. if ($this->queryComponents[$dqlAlias]['relation'] === null) {
  585. $this->rsm->addEntityResult($class->name, $dqlAlias, $resultAlias);
  586. } else {
  587. $this->rsm->addJoinedEntityResult(
  588. $class->name,
  589. $dqlAlias,
  590. $this->queryComponents[$dqlAlias]['parent'],
  591. $this->queryComponents[$dqlAlias]['relation']['fieldName']
  592. );
  593. }
  594. if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) {
  595. // Add discriminator columns to SQL
  596. $rootClass = $this->em->getClassMetadata($class->rootEntityName);
  597. $tblAlias = $this->getSQLTableAlias($rootClass->getTableName(), $dqlAlias);
  598. $discrColumn = $rootClass->discriminatorColumn;
  599. $columnAlias = $this->getSQLColumnAlias($discrColumn['name']);
  600. $sqlSelectExpressions[] = $tblAlias . '.' . $discrColumn['name'] . ' AS ' . $columnAlias;
  601. $this->rsm->setDiscriminatorColumn($dqlAlias, $columnAlias);
  602. $this->rsm->addMetaResult($dqlAlias, $columnAlias, $discrColumn['fieldName']);
  603. }
  604. // Add foreign key columns to SQL, if necessary
  605. if ( ! $addMetaColumns && ! $class->containsForeignIdentifier) {
  606. continue;
  607. }
  608. // Add foreign key columns of class and also parent classes
  609. foreach ($class->associationMappings as $assoc) {
  610. if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) {
  611. continue;
  612. } else if ( !$addMetaColumns && !isset($assoc['id'])) {
  613. continue;
  614. }
  615. $owningClass = (isset($assoc['inherited'])) ? $this->em->getClassMetadata($assoc['inherited']) : $class;
  616. $sqlTableAlias = $this->getSQLTableAlias($owningClass->getTableName(), $dqlAlias);
  617. foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
  618. $columnAlias = $this->getSQLColumnAlias($srcColumn);
  619. $sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
  620. $this->rsm->addMetaResult($dqlAlias, $columnAlias, $srcColumn, (isset($assoc['id']) && $assoc['id'] === true));
  621. }
  622. }
  623. // Add foreign key columns to SQL, if necessary
  624. if ( ! $addMetaColumns) {
  625. continue;
  626. }
  627. // Add foreign key columns of subclasses
  628. foreach ($class->subClasses as $subClassName) {
  629. $subClass = $this->em->getClassMetadata($subClassName);
  630. $sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
  631. foreach ($subClass->associationMappings as $assoc) {
  632. // Skip if association is inherited
  633. if (isset($assoc['inherited'])) continue;
  634. if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) continue;
  635. foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
  636. $columnAlias = $this->getSQLColumnAlias($srcColumn);
  637. $sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
  638. $this->rsm->addMetaResult($dqlAlias, $columnAlias, $srcColumn);
  639. }
  640. }
  641. }
  642. }
  643. $sql .= implode(', ', $sqlSelectExpressions);
  644. return $sql;
  645. }
  646. /**
  647. * Walks down a FromClause AST node, thereby generating the appropriate SQL.
  648. *
  649. * @return string The SQL.
  650. */
  651. public function walkFromClause($fromClause)
  652. {
  653. $identificationVarDecls = $fromClause->identificationVariableDeclarations;
  654. $sqlParts = array();
  655. foreach ($identificationVarDecls as $identificationVariableDecl) {
  656. $sql = $this->walkRangeVariableDeclaration($identificationVariableDecl->rangeVariableDeclaration);
  657. foreach ($identificationVariableDecl->joins as $join) {
  658. $sql .= $this->walkJoin($join);
  659. }
  660. if ($identificationVariableDecl->indexBy) {
  661. $alias = $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->identificationVariable;
  662. $field = $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->field;
  663. if (isset($this->scalarFields[$alias][$field])) {
  664. $this->rsm->addIndexByScalar($this->scalarFields[$alias][$field]);
  665. } else {
  666. $this->rsm->addIndexBy(
  667. $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->identificationVariable,
  668. $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->field
  669. );
  670. }
  671. }
  672. $sqlParts[] = $this->platform->appendLockHint($sql, $this->query->getHint(Query::HINT_LOCK_MODE));
  673. }
  674. return ' FROM ' . implode(', ', $sqlParts);
  675. }
  676. /**
  677. * Walks down a RangeVariableDeclaration AST node, thereby generating the appropriate SQL.
  678. *
  679. * @return string
  680. */
  681. public function walkRangeVariableDeclaration($rangeVariableDeclaration)
  682. {
  683. $class = $this->em->getClassMetadata($rangeVariableDeclaration->abstractSchemaName);
  684. $dqlAlias = $rangeVariableDeclaration->aliasIdentificationVariable;
  685. $this->rootAliases[] = $dqlAlias;
  686. $sql = $class->getQuotedTableName($this->platform) . ' '
  687. . $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
  688. if ($class->isInheritanceTypeJoined()) {
  689. $sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias);
  690. }
  691. return $sql;
  692. }
  693. /**
  694. * Walks down a JoinAssociationDeclaration AST node, thereby generating the appropriate SQL.
  695. *
  696. * @return string
  697. */
  698. public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joinType = AST\Join::JOIN_TYPE_INNER)
  699. {
  700. $sql = '';
  701. $associationPathExpression = $joinAssociationDeclaration->joinAssociationPathExpression;
  702. $joinedDqlAlias = $joinAssociationDeclaration->aliasIdentificationVariable;
  703. $indexBy = $joinAssociationDeclaration->indexBy;
  704. $relation = $this->queryComponents[$joinedDqlAlias]['relation'];
  705. $targetClass = $this->em->getClassMetadata($relation['targetEntity']);
  706. $sourceClass = $this->em->getClassMetadata($relation['sourceEntity']);
  707. $targetTableName = $targetClass->getQuotedTableName($this->platform);
  708. $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName(), $joinedDqlAlias);
  709. $sourceTableAlias = $this->getSQLTableAlias($sourceClass->getTableName(), $associationPathExpression->identificationVariable);
  710. // Ensure we got the owning side, since it has all mapping info
  711. $assoc = ( ! $relation['isOwningSide']) ? $targetClass->associationMappings[$relation['mappedBy']] : $relation;
  712. if ($this->query->getHint(Query::HINT_INTERNAL_ITERATION) == true && (!$this->query->getHint(self::HINT_DISTINCT) || isset($this->selectedClasses[$joinedDqlAlias]))) {
  713. if ($relation['type'] == ClassMetadata::ONE_TO_MANY || $relation['type'] == ClassMetadata::MANY_TO_MANY) {
  714. throw QueryException::iterateWithFetchJoinNotAllowed($assoc);
  715. }
  716. }
  717. // This condition is not checking ClassMetadata::MANY_TO_ONE, because by definition it cannot
  718. // be the owning side and previously we ensured that $assoc is always the owning side of the associations.
  719. // The owning side is necessary at this point because only it contains the JoinColumn information.
  720. switch (true) {
  721. case ($assoc['type'] & ClassMetadata::TO_ONE):
  722. $conditions = array();
  723. foreach ($assoc['joinColumns'] as $joinColumn) {
  724. $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
  725. $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform);
  726. if ($relation['isOwningSide']) {
  727. $conditions[] = $sourceTableAlias . '.' . $quotedSourceColumn . ' = ' . $targetTableAlias . '.' . $quotedTargetColumn;
  728. continue;
  729. }
  730. $conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $targetTableAlias . '.' . $quotedSourceColumn;
  731. }
  732. // Apply remaining inheritance restrictions
  733. $discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($joinedDqlAlias));
  734. if ($discrSql) {
  735. $conditions[] = $discrSql;
  736. }
  737. // Apply the filters
  738. $filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias);
  739. if ($filterExpr) {
  740. $conditions[] = $filterExpr;
  741. }
  742. $sql .= $targetTableName . ' ' . $targetTableAlias . ' ON ' . implode(' AND ', $conditions);
  743. break;
  744. case ($assoc['type'] == ClassMetadata::MANY_TO_MANY):
  745. // Join relation table
  746. $joinTable = $assoc['joinTable'];
  747. $joinTableAlias = $this->getSQLTableAlias($joinTable['name'], $joinedDqlAlias);
  748. $joinTableName = $sourceClass->getQuotedJoinTableName($assoc, $this->platform);
  749. $conditions = array();
  750. $relationColumns = ($relation['isOwningSide'])
  751. ? $assoc['joinTable']['joinColumns']
  752. : $assoc['joinTable']['inverseJoinColumns'];
  753. foreach ($relationColumns as $joinColumn) {
  754. $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
  755. $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform);
  756. $conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $quotedSourceColumn;
  757. }
  758. $sql .= $joinTableName . ' ' . $joinTableAlias . ' ON ' . implode(' AND ', $conditions);
  759. // Join target table
  760. $sql .= ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) ? ' LEFT JOIN ' : ' INNER JOIN ';
  761. $conditions = array();
  762. $relationColumns = ($relation['isOwningSide'])
  763. ? $assoc['joinTable']['inverseJoinColumns']
  764. : $assoc['joinTable']['joinColumns'];
  765. foreach ($relationColumns as $joinColumn) {
  766. $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
  767. $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform);
  768. $conditions[] = $targetTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $quotedSourceColumn;
  769. }
  770. // Apply remaining inheritance restrictions
  771. $discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($joinedDqlAlias));
  772. if ($discrSql) {
  773. $conditions[] = $discrSql;
  774. }
  775. // Apply the filters
  776. $filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias);
  777. if ($filterExpr) {
  778. $conditions[] = $filterExpr;
  779. }
  780. $sql .= $targetTableName . ' ' . $targetTableAlias . ' ON ' . implode(' AND ', $conditions);
  781. break;
  782. }
  783. // FIXME: these should either be nested or all forced to be left joins (DDC-XXX)
  784. if ($targetClass->isInheritanceTypeJoined()) {
  785. $sql .= $this->_generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias);
  786. }
  787. // Apply the indexes
  788. if ($indexBy) {
  789. // For Many-To-One or One-To-One associations this obviously makes no sense, but is ignored silently.
  790. $this->rsm->addIndexBy(
  791. $indexBy->simpleStateFieldPathExpression->identificationVariable,
  792. $indexBy->simpleStateFieldPathExpression->field
  793. );
  794. } else if (isset($relation['indexBy'])) {
  795. $this->rsm->addIndexBy($joinedDqlAlias, $relation['indexBy']);
  796. }
  797. return $sql;
  798. }
  799. /**
  800. * Walks down a FunctionNode AST node, thereby generating the appropriate SQL.
  801. *
  802. * @return string The SQL.
  803. */
  804. public function walkFunction($function)
  805. {
  806. return $function->getSql($this);
  807. }
  808. /**
  809. * Walks down an OrderByClause AST node, thereby generating the appropriate SQL.
  810. *
  811. * @param OrderByClause
  812. * @return string The SQL.
  813. */
  814. public function walkOrderByClause($orderByClause)
  815. {
  816. $orderByItems = array_map(array($this, 'walkOrderByItem'), $orderByClause->orderByItems);
  817. if (($collectionOrderByItems = $this->_generateOrderedCollectionOrderByItems()) !== '') {
  818. $orderByItems = array_merge($orderByItems, (array) $collectionOrderByItems);
  819. }
  820. return ' ORDER BY ' . implode(', ', $orderByItems);
  821. }
  822. /**
  823. * Walks down an OrderByItem AST node, thereby generating the appropriate SQL.
  824. *
  825. * @param OrderByItem
  826. * @return string The SQL.
  827. */
  828. public function walkOrderByItem($orderByItem)
  829. {
  830. $expr = $orderByItem->expression;
  831. $sql = ($expr instanceof AST\Node)
  832. ? $expr->dispatch($this)
  833. : $this->walkResultVariable($this->queryComponents[$expr]['token']['value']);
  834. return $sql . ' ' . strtoupper($orderByItem->type);
  835. }
  836. /**
  837. * Walks down a HavingClause AST node, thereby generating the appropriate SQL.
  838. *
  839. * @param HavingClause
  840. * @return string The SQL.
  841. */
  842. public function walkHavingClause($havingClause)
  843. {
  844. return ' HAVING ' . $this->walkConditionalExpression($havingClause->conditionalExpression);
  845. }
  846. /**
  847. * Walks down a Join AST node and creates the corresponding SQL.
  848. *
  849. * @return string The SQL.
  850. */
  851. public function walkJoin($join)
  852. {
  853. $joinType = $join->joinType;
  854. $joinDeclaration = $join->joinAssociationDeclaration;
  855. $sql = ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER)
  856. ? ' LEFT JOIN '
  857. : ' INNER JOIN ';
  858. switch (true) {
  859. case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\RangeVariableDeclaration):
  860. $class = $this->em->getClassMetadata($joinDeclaration->abstractSchemaName);
  861. $condExprConjunction = $class->isInheritanceTypeJoined() && $joinType != AST\Join::JOIN_TYPE_LEFT && $joinType != AST\Join::JOIN_TYPE_LEFTOUTER
  862. ? ' AND '
  863. : ' ON ';
  864. $sql .= $this->walkRangeVariableDeclaration($joinDeclaration)
  865. . $condExprConjunction . '(' . $this->walkConditionalExpression($join->conditionalExpression) . ')';
  866. break;
  867. case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\JoinAssociationDeclaration):
  868. $sql .= $this->walkJoinAssociationDeclaration($joinDeclaration, $joinType);
  869. // Handle WITH clause
  870. if (($condExpr = $join->conditionalExpression) !== null) {
  871. // Phase 2 AST optimization: Skip processment of ConditionalExpression
  872. // if only one ConditionalTerm is defined
  873. $sql .= ' AND (' . $this->walkConditionalExpression($condExpr) . ')';
  874. }
  875. break;
  876. }
  877. return $sql;
  878. }
  879. /**
  880. * Walks down a CaseExpression AST node and generates the corresponding SQL.
  881. *
  882. * @param CoalesceExpression|NullIfExpression|GeneralCaseExpression|SimpleCaseExpression $expression
  883. * @return string The SQL.
  884. */
  885. public function walkCaseExpression($expression)
  886. {
  887. switch (true) {
  888. case ($expression instanceof AST\CoalesceExpression):
  889. return $this->walkCoalesceExpression($expression);
  890. case ($expression instanceof AST\NullIfExpression):
  891. return $this->walkNullIfExpression($expression);
  892. case ($expression instanceof AST\GeneralCaseExpression):
  893. return $this->walkGeneralCaseExpression($expression);
  894. case ($expression instanceof AST\SimpleCaseExpression):
  895. return $this->walkSimpleCaseExpression($expression);
  896. default:
  897. return '';
  898. }
  899. }
  900. /**
  901. * Walks down a CoalesceExpression AST node and generates the corresponding SQL.
  902. *
  903. * @param CoalesceExpression $coalesceExpression
  904. * @return string The SQL.
  905. */
  906. public function walkCoalesceExpression($coalesceExpression)
  907. {
  908. $sql = 'COALESCE(';
  909. $scalarExpressions = array();
  910. foreach ($coalesceExpression->scalarExpressions as $scalarExpression) {
  911. $scalarExpressions[] = $this->walkSimpleArithmeticExpression($scalarExpression);
  912. }
  913. $sql .= implode(', ', $scalarExpressions) . ')';
  914. return $sql;
  915. }
  916. /**
  917. * Walks down a NullIfExpression AST node and generates the corresponding SQL.
  918. *
  919. * @param NullIfExpression $nullIfExpression
  920. * @return string The SQL.
  921. */
  922. public function walkNullIfExpression($nullIfExpression)
  923. {
  924. $firstExpression = is_string($nullIfExpression->firstExpression)
  925. ? $this->conn->quote($nullIfExpression->firstExpression)
  926. : $this->walkSimpleArithmeticExpression($nullIfExpression->firstExpression);
  927. $secondExpression = is_string($nullIfExpression->secondExpression)
  928. ? $this->conn->quote($nullIfExpression->secondExpression)
  929. : $this->walkSimpleArithmeticExpression($nullIfExpression->secondExpression);
  930. return 'NULLIF(' . $firstExpression . ', ' . $secondExpression . ')';
  931. }
  932. /**
  933. * Walks down a GeneralCaseExpression AST node and generates the corresponding SQL.
  934. *
  935. * @param GeneralCaseExpression $generalCaseExpression
  936. * @return string The SQL.
  937. */
  938. public function walkGeneralCaseExpression(AST\GeneralCaseExpression $generalCaseExpression)
  939. {
  940. $sql = 'CASE';
  941. foreach ($generalCaseExpression->whenClauses as $whenClause) {
  942. $sql .= ' WHEN ' . $this->walkConditionalExpression($whenClause->caseConditionExpression);
  943. $sql .= ' THEN ' . $this->walkSimpleArithmeticExpression($whenClause->thenScalarExpression);
  944. }
  945. $sql .= ' ELSE ' . $this->walkSimpleArithmeticExpression($generalCaseExpression->elseScalarExpression) . ' END';
  946. return $sql;
  947. }
  948. /**
  949. * Walks down a SimpleCaseExpression AST node and generates the corresponding SQL.
  950. *
  951. * @param SimpleCaseExpression $simpleCaseExpression
  952. * @return string The SQL.
  953. */
  954. public function walkSimpleCaseExpression($simpleCaseExpression)
  955. {
  956. $sql = 'CASE ' . $this->walkStateFieldPathExpression($simpleCaseExpression->caseOperand);
  957. foreach ($simpleCaseExpression->simpleWhenClauses as $simpleWhenClause) {
  958. $sql .= ' WHEN ' . $this->walkSimpleArithmeticExpression($simpleWhenClause->caseScalarExpression);
  959. $sql .= ' THEN ' . $this->walkSimpleArithmeticExpression($simpleWhenClause->thenScalarExpression);
  960. }
  961. $sql .= ' ELSE ' . $this->walkSimpleArithmeticExpression($simpleCaseExpression->elseScalarExpression) . ' END';
  962. return $sql;
  963. }
  964. /**
  965. * Walks down a SelectExpression AST node and generates the corresponding SQL.
  966. *
  967. * @param SelectExpression $selectExpression
  968. * @return string The SQL.
  969. */
  970. public function walkSelectExpression($selectExpression)
  971. {
  972. $sql = '';
  973. $expr = $selectExpression->expression;
  974. $hidden = $selectExpression->hiddenAliasResultVariable;
  975. switch (true) {
  976. case ($expr instanceof AST\PathExpression):
  977. if ($expr->type !== AST\PathExpression::TYPE_STATE_FIELD) {
  978. throw QueryException::invalidPathExpression($expr);
  979. }
  980. $fieldName = $expr->field;
  981. $dqlAlias = $expr->identificationVariable;
  982. $qComp = $this->queryComponents[$dqlAlias];
  983. $class = $qComp['metadata'];
  984. $resultAlias = $selectExpression->fieldIdentificationVariable ?: $fieldName;
  985. $tableName = ($class->isInheritanceTypeJoined())
  986. ? $this->em->getUnitOfWork()->getEntityPersister($class->name)->getOwningTable($fieldName)
  987. : $class->getTableName();
  988. $sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias);
  989. $columnName = $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform);
  990. $columnAlias = $this->getSQLColumnAlias($class->fieldMappings[$fieldName]['columnName']);
  991. $col = $sqlTableAlias . '.' . $columnName;
  992. $fieldType = $class->getTypeOfField($fieldName);
  993. if (isset($class->fieldMappings[$fieldName]['requireSQLConversion'])) {
  994. $type = Type::getType($fieldType);
  995. $col = $type->convertToPHPValueSQL($col, $this->conn->getDatabasePlatform());
  996. }
  997. $sql .= $col . ' AS ' . $columnAlias;
  998. $this->scalarResultAliasMap[$resultAlias] = $columnAlias;
  999. if ( ! $hidden) {
  1000. $this->rsm->addScalarResult($columnAlias, $resultAlias, $fieldType);
  1001. $this->scalarFields[$dqlAlias][$fieldName] = $columnAlias;
  1002. }
  1003. break;
  1004. case ($expr instanceof AST\AggregateExpression):
  1005. case ($expr instanceof AST\Functions\FunctionNode):
  1006. case ($expr instanceof AST\SimpleArithmeticExpression):
  1007. case ($expr instanceof AST\ArithmeticTerm):
  1008. case ($expr instanceof AST\ArithmeticFactor):
  1009. case ($expr instanceof AST\Literal):
  1010. case ($expr instanceof AST\NullIfExpression):
  1011. case ($expr instanceof AST\CoalesceExpression):
  1012. case ($expr instanceof AST\GeneralCaseExpression):
  1013. case ($expr instanceof AST\SimpleCaseExpression):
  1014. $columnAlias = $this->getSQLColumnAlias('sclr');
  1015. $resultAlias = $selectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
  1016. $sql .= $expr->dispatch($this) . ' AS ' . $columnAlias;
  1017. $this->scalarResultAliasMap[$resultAlias] = $columnAlias;
  1018. if ( ! $hidden) {
  1019. // We cannot resolve field type here; assume 'string'.
  1020. $this->rsm->addScalarResult($columnAlias, $resultAlias, 'string');
  1021. }
  1022. break;
  1023. case ($expr instanceof AST\Subselect):
  1024. $columnAlias = $this->getSQLColumnAlias('sclr');
  1025. $resultAlias = $selectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
  1026. $sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias;
  1027. $this->scalarResultAliasMap[$resultAlias] = $columnAlias;
  1028. if ( ! $hidden) {
  1029. // We cannot resolve field type here; assume 'string'.
  1030. $this->rsm->addScalarResult($columnAlias, $resultAlias, 'string');
  1031. }
  1032. break;
  1033. case ($expr instanceof AST\NewObjectExpression):
  1034. $sql .= $this->walkNewObject($expr);
  1035. break;
  1036. default:
  1037. // IdentificationVariable or PartialObjectExpression
  1038. if ($expr instanceof AST\PartialObjectExpression) {
  1039. $dqlAlias = $expr->identificationVariable;
  1040. $partialFieldSet = $expr->partialFieldSet;
  1041. } else {
  1042. $dqlAlias = $expr;
  1043. $partialFieldSet = array();
  1044. }
  1045. $queryComp = $this->queryComponents[$dqlAlias];
  1046. $class = $queryComp['metadata'];
  1047. $resultAlias = $selectExpression->fieldIdentificationVariable ?: null;
  1048. if ( ! isset($this->selectedClasses[$dqlAlias])) {
  1049. $this->selectedClasses[$dqlAlias] = array(
  1050. 'class' => $class,
  1051. 'dqlAlias' => $dqlAlias,
  1052. 'resultAlias' => $resultAlias
  1053. );
  1054. }
  1055. $sqlParts = array();
  1056. // Select all fields from the queried class
  1057. foreach ($class->fieldMappings as $fieldName => $mapping) {
  1058. if ($partialFieldSet && ! in_array($fieldName, $partialFieldSet)) {
  1059. continue;
  1060. }
  1061. $tableName = (isset($mapping['inherited']))
  1062. ? $this->em->getClassMetadata($mapping['inherited'])->getTableName()
  1063. : $class->getTableName();
  1064. $sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias);
  1065. $columnAlias = $this->getSQLColumnAlias($mapping['columnName']);
  1066. $quotedColumnName = $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform);
  1067. $col = $sqlTableAlias . '.' . $quotedColumnName;
  1068. if (isset($class->fieldMappings[$fieldName]['requireSQLConversion'])) {
  1069. $type = Type::getType($class->getTypeOfField($fieldName));
  1070. $col = $type->convertToPHPValueSQL($col, $this->platform);
  1071. }
  1072. $sqlParts[] = $col . ' AS '. $columnAlias;
  1073. $this->scalarResultAliasMap[$resultAlias][] = $columnAlias;
  1074. $this->rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $class->name);
  1075. }
  1076. // Add any additional fields of subclasses (excluding inherited fields)
  1077. // 1) on…

Large files files are truncated, but you can click here to view the full file