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