PageRenderTime 68ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

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

https://github.com/casoetan/ServerGroveLiveChat
PHP | 1882 lines | 1140 code | 285 blank | 457 comment | 252 complexity | d2b00888246e968c2cd133bd4bebc7f7 MD5 | raw file
Possible License(s): LGPL-2.1, LGPL-3.0, ISC, BSD-3-Clause

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

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