Completed
Push — master ( 2a239b...205ee7 )
by Marco
22s
created

lib/Doctrine/ORM/Query/SqlWalker.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ORM\Query;
21
22
use Doctrine\DBAL\LockMode;
23
use Doctrine\DBAL\Types\Type;
24
use Doctrine\ORM\Mapping\ClassMetadata;
25
use Doctrine\ORM\Mapping\ClassMetadataInfo;
26
use Doctrine\ORM\OptimisticLockException;
27
use Doctrine\ORM\Query;
28
use Doctrine\ORM\Utility\PersisterHelper;
29
30
/**
31
 * The SqlWalker is a TreeWalker that walks over a DQL AST and constructs
32
 * the corresponding SQL.
33
 *
34
 * @author Guilherme Blanco <[email protected]>
35
 * @author Roman Borschel <[email protected]>
36
 * @author Benjamin Eberlei <[email protected]>
37
 * @author Alexander <[email protected]>
38
 * @author Fabio B. Silva <[email protected]>
39
 * @since  2.0
40
 * @todo Rename: SQLWalker
41
 */
42
class SqlWalker implements TreeWalker
43
{
44
    /**
45
     * @var string
46
     */
47
    const HINT_DISTINCT = 'doctrine.distinct';
48
49
    /**
50
     * @var ResultSetMapping
51
     */
52
    private $rsm;
53
54
    /**
55
     * Counter for generating unique column aliases.
56
     *
57
     * @var integer
58
     */
59
    private $aliasCounter = 0;
60
61
    /**
62
     * Counter for generating unique table aliases.
63
     *
64
     * @var integer
65
     */
66
    private $tableAliasCounter = 0;
67
68
    /**
69
     * Counter for generating unique scalar result.
70
     *
71
     * @var integer
72
     */
73
    private $scalarResultCounter = 1;
74
75
    /**
76
     * Counter for generating unique parameter indexes.
77
     *
78
     * @var integer
79
     */
80
    private $sqlParamIndex = 0;
81
82
    /**
83
     * Counter for generating indexes.
84
     *
85
     * @var integer
86
     */
87
    private $newObjectCounter = 0;
88
89
    /**
90
     * @var ParserResult
91
     */
92
    private $parserResult;
93
94
    /**
95
     * @var \Doctrine\ORM\EntityManager
96
     */
97
    private $em;
98
99
    /**
100
     * @var \Doctrine\DBAL\Connection
101
     */
102
    private $conn;
103
104
    /**
105
     * @var \Doctrine\ORM\AbstractQuery
106
     */
107
    private $query;
108
109
    /**
110
     * @var array
111
     */
112
    private $tableAliasMap = [];
113
114
    /**
115
     * Map from result variable names to their SQL column alias names.
116
     *
117
     * @var array
118
     */
119
    private $scalarResultAliasMap = [];
120
121
    /**
122
     * Map from Table-Alias + Column-Name to OrderBy-Direction.
123
     *
124
     * @var array
125
     */
126
    private $orderedColumnsMap = [];
127
128
    /**
129
     * Map from DQL-Alias + Field-Name to SQL Column Alias.
130
     *
131
     * @var array
132
     */
133
    private $scalarFields = [];
134
135
    /**
136
     * Map of all components/classes that appear in the DQL query.
137
     *
138
     * @var array
139
     */
140
    private $queryComponents;
141
142
    /**
143
     * A list of classes that appear in non-scalar SelectExpressions.
144
     *
145
     * @var array
146
     */
147
    private $selectedClasses = [];
148
149
    /**
150
     * The DQL alias of the root class of the currently traversed query.
151
     *
152
     * @var array
153
     */
154
    private $rootAliases = [];
155
156
    /**
157
     * Flag that indicates whether to generate SQL table aliases in the SQL.
158
     * These should only be generated for SELECT queries, not for UPDATE/DELETE.
159
     *
160
     * @var boolean
161
     */
162
    private $useSqlTableAliases = true;
163
164
    /**
165
     * The database platform abstraction.
166
     *
167
     * @var \Doctrine\DBAL\Platforms\AbstractPlatform
168
     */
169
    private $platform;
170
171
    /**
172
     * The quote strategy.
173
     *
174
     * @var \Doctrine\ORM\Mapping\QuoteStrategy
175
     */
176
    private $quoteStrategy;
177
178
    /**
179
     * {@inheritDoc}
180
     */
181 683
    public function __construct($query, $parserResult, array $queryComponents)
182
    {
183 683
        $this->query            = $query;
184 683
        $this->parserResult     = $parserResult;
185 683
        $this->queryComponents  = $queryComponents;
186 683
        $this->rsm              = $parserResult->getResultSetMapping();
187 683
        $this->em               = $query->getEntityManager();
188 683
        $this->conn             = $this->em->getConnection();
189 683
        $this->platform         = $this->conn->getDatabasePlatform();
190 683
        $this->quoteStrategy    = $this->em->getConfiguration()->getQuoteStrategy();
191 683
    }
192
193
    /**
194
     * Gets the Query instance used by the walker.
195
     *
196
     * @return Query.
197
     */
198
    public function getQuery()
199
    {
200
        return $this->query;
201
    }
202
203
    /**
204
     * Gets the Connection used by the walker.
205
     *
206
     * @return \Doctrine\DBAL\Connection
207
     */
208 35
    public function getConnection()
209
    {
210 35
        return $this->conn;
211
    }
212
213
    /**
214
     * Gets the EntityManager used by the walker.
215
     *
216
     * @return \Doctrine\ORM\EntityManager
217
     */
218 21
    public function getEntityManager()
219
    {
220 21
        return $this->em;
221
    }
222
223
    /**
224
     * Gets the information about a single query component.
225
     *
226
     * @param string $dqlAlias The DQL alias.
227
     *
228
     * @return array
229
     */
230 17
    public function getQueryComponent($dqlAlias)
231
    {
232 17
        return $this->queryComponents[$dqlAlias];
233
    }
234
235
    /**
236
     * {@inheritdoc}
237
     */
238
    public function getQueryComponents()
239
    {
240
        return $this->queryComponents;
241
    }
242
243
    /**
244
     * {@inheritdoc}
245
     */
246 1
    public function setQueryComponent($dqlAlias, array $queryComponent)
247
    {
248 1
        $requiredKeys = ['metadata', 'parent', 'relation', 'map', 'nestingLevel', 'token'];
249
250 1
        if (array_diff($requiredKeys, array_keys($queryComponent))) {
251 1
            throw QueryException::invalidQueryComponent($dqlAlias);
252
        }
253
254
        $this->queryComponents[$dqlAlias] = $queryComponent;
255
    }
256
257
    /**
258
     * {@inheritdoc}
259
     */
260 677
    public function getExecutor($AST)
261
    {
262
        switch (true) {
263 677
            case ($AST instanceof AST\DeleteStatement):
264 38
                $primaryClass = $this->em->getClassMetadata($AST->deleteClause->abstractSchemaName);
265
266 38
                return ($primaryClass->isInheritanceTypeJoined())
267 2
                    ? new Exec\MultiTableDeleteExecutor($AST, $this)
268 38
                    : new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
269
270 643
            case ($AST instanceof AST\UpdateStatement):
271 28
                $primaryClass = $this->em->getClassMetadata($AST->updateClause->abstractSchemaName);
272
273 28
                return ($primaryClass->isInheritanceTypeJoined())
274 3
                    ? new Exec\MultiTableUpdateExecutor($AST, $this)
275 28
                    : new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
276
277
            default:
278 619
                return new Exec\SingleSelectExecutor($AST, $this);
279
        }
280
    }
281
282
    /**
283
     * Generates a unique, short SQL table alias.
284
     *
285
     * @param string $tableName Table name
286
     * @param string $dqlAlias  The DQL alias.
287
     *
288
     * @return string Generated table alias.
289
     */
290 629
    public function getSQLTableAlias($tableName, $dqlAlias = '')
291
    {
292 629
        $tableName .= ($dqlAlias) ? '@[' . $dqlAlias . ']' : '';
293
294 629
        if ( ! isset($this->tableAliasMap[$tableName])) {
295 629
            $this->tableAliasMap[$tableName] = (preg_match('/[a-z]/i', $tableName[0]) ? strtolower($tableName[0]) : 't')
296 629
                . $this->tableAliasCounter++ . '_';
297
        }
298
299 629
        return $this->tableAliasMap[$tableName];
300
    }
301
302
    /**
303
     * Forces the SqlWalker to use a specific alias for a table name, rather than
304
     * generating an alias on its own.
305
     *
306
     * @param string $tableName
307
     * @param string $alias
308
     * @param string $dqlAlias
309
     *
310
     * @return string
311
     */
312 64
    public function setSQLTableAlias($tableName, $alias, $dqlAlias = '')
313
    {
314 64
        $tableName .= ($dqlAlias) ? '@[' . $dqlAlias . ']' : '';
315
316 64
        $this->tableAliasMap[$tableName] = $alias;
317
318 64
        return $alias;
319
    }
320
321
    /**
322
     * Gets an SQL column alias for a column name.
323
     *
324
     * @param string $columnName
325
     *
326
     * @return string
327
     */
328 619
    public function getSQLColumnAlias($columnName)
329
    {
330 619
        return $this->quoteStrategy->getColumnAlias($columnName, $this->aliasCounter++, $this->platform);
331
    }
332
333
    /**
334
     * Generates the SQL JOINs that are necessary for Class Table Inheritance
335
     * for the given class.
336
     *
337
     * @param ClassMetadata $class    The class for which to generate the joins.
338
     * @param string        $dqlAlias The DQL alias of the class.
339
     *
340
     * @return string The SQL.
341
     */
342 91
    private function _generateClassTableInheritanceJoins($class, $dqlAlias)
343
    {
344 91
        $sql = '';
345
346 91
        $baseTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
347
348
        // INNER JOIN parent class tables
349 91
        foreach ($class->parentClasses as $parentClassName) {
350 64
            $parentClass = $this->em->getClassMetadata($parentClassName);
351 64
            $tableAlias  = $this->getSQLTableAlias($parentClass->getTableName(), $dqlAlias);
352
353
            // If this is a joined association we must use left joins to preserve the correct result.
354 64
            $sql .= isset($this->queryComponents[$dqlAlias]['relation']) ? ' LEFT ' : ' INNER ';
355 64
            $sql .= 'JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->platform) . ' ' . $tableAlias . ' ON ';
356
357 64
            $sqlParts = [];
358
359 64
            foreach ($this->quoteStrategy->getIdentifierColumnNames($class, $this->platform) as $columnName) {
360 64
                $sqlParts[] = $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
361
            }
362
363
            // Add filters on the root class
364 64
            if ($filterSql = $this->generateFilterConditionSQL($parentClass, $tableAlias)) {
365 1
                $sqlParts[] = $filterSql;
366
            }
367
368 64
            $sql .= implode(' AND ', $sqlParts);
369
        }
370
371
        // Ignore subclassing inclusion if partial objects is disallowed
372 91
        if ($this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {
373 21
            return $sql;
374
        }
375
376
        // LEFT JOIN child class tables
377 70
        foreach ($class->subClasses as $subClassName) {
378 32
            $subClass   = $this->em->getClassMetadata($subClassName);
379 32
            $tableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
380
381 32
            $sql .= ' LEFT JOIN ' . $this->quoteStrategy->getTableName($subClass, $this->platform) . ' ' . $tableAlias . ' ON ';
382
383 32
            $sqlParts = [];
384
385 32
            foreach ($this->quoteStrategy->getIdentifierColumnNames($subClass, $this->platform) as $columnName) {
386 32
                $sqlParts[] = $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
387
            }
388
389 32
            $sql .= implode(' AND ', $sqlParts);
390
        }
391
392 70
        return $sql;
393
    }
394
395
    /**
396
     * @return string
397
     */
398 612
    private function _generateOrderedCollectionOrderByItems()
399
    {
400 612
        $orderedColumns = [];
401
402 612
        foreach ($this->selectedClasses as $selectedClass) {
403 476
            $dqlAlias  = $selectedClass['dqlAlias'];
404 476
            $qComp     = $this->queryComponents[$dqlAlias];
405
406 476
            if ( ! isset($qComp['relation']['orderBy'])) {
407 476
                continue;
408
            }
409
410 6
            $persister = $this->em->getUnitOfWork()->getEntityPersister($qComp['metadata']->name);
411
412 6
            foreach ($qComp['relation']['orderBy'] as $fieldName => $orientation) {
413 6
                $columnName = $this->quoteStrategy->getColumnName($fieldName, $qComp['metadata'], $this->platform);
414 6
                $tableName  = ($qComp['metadata']->isInheritanceTypeJoined())
415 1
                    ? $persister->getOwningTable($fieldName)
416 6
                    : $qComp['metadata']->getTableName();
417
418 6
                $orderedColumn = $this->getSQLTableAlias($tableName, $dqlAlias) . '.' . $columnName;
419
420
                // OrderByClause should replace an ordered relation. see - DDC-2475
421 6
                if (isset($this->orderedColumnsMap[$orderedColumn])) {
422 1
                    continue;
423
                }
424
425 6
                $this->orderedColumnsMap[$orderedColumn] = $orientation;
426 6
                $orderedColumns[] = $orderedColumn . ' ' . $orientation;
427
            }
428
        }
429
430 612
        return implode(', ', $orderedColumns);
431
    }
432
433
    /**
434
     * Generates a discriminator column SQL condition for the class with the given DQL alias.
435
     *
436
     * @param array $dqlAliases List of root DQL aliases to inspect for discriminator restrictions.
437
     *
438
     * @return string
439
     */
440 671
    private function _generateDiscriminatorColumnConditionSQL(array $dqlAliases)
441
    {
442 671
        $sqlParts = [];
443
444 671
        foreach ($dqlAliases as $dqlAlias) {
445 671
            $class = $this->queryComponents[$dqlAlias]['metadata'];
446
447 671
            if ( ! $class->isInheritanceTypeSingleTable()) continue;
448
449 39
            $conn   = $this->em->getConnection();
450 39
            $values = [];
451
452 39
            if ($class->discriminatorValue !== null) { // discriminators can be 0
453 20
                $values[] = $conn->quote($class->discriminatorValue);
454
            }
455
456 39
            foreach ($class->subClasses as $subclassName) {
457 29
                $values[] = $conn->quote($this->em->getClassMetadata($subclassName)->discriminatorValue);
458
            }
459
460 39
            $sqlTableAlias = ($this->useSqlTableAliases)
461 34
                ? $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.'
462 39
                : '';
463
464 39
            $sqlParts[] = $sqlTableAlias . $class->discriminatorColumn['name'] . ' IN (' . implode(', ', $values) . ')';
465
        }
466
467 671
        $sql = implode(' AND ', $sqlParts);
468
469 671
        return (count($sqlParts) > 1) ? '(' . $sql . ')' : $sql;
470
    }
471
472
    /**
473
     * Generates the filter SQL for a given entity and table alias.
474
     *
475
     * @param ClassMetadata $targetEntity     Metadata of the target entity.
476
     * @param string        $targetTableAlias The table alias of the joined/selected table.
477
     *
478
     * @return string The SQL query part to add to a query.
479
     */
480 315
    private function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias)
481
    {
482 315
        if (!$this->em->hasFilters()) {
483 277
            return '';
484
        }
485
486 43
        switch($targetEntity->inheritanceType) {
487 43
            case ClassMetadata::INHERITANCE_TYPE_NONE:
488 33
                break;
489 10
            case ClassMetadata::INHERITANCE_TYPE_JOINED:
490
                // The classes in the inheritance will be added to the query one by one,
491
                // but only the root node is getting filtered
492 6
                if ($targetEntity->name !== $targetEntity->rootEntityName) {
493 4
                    return '';
494
                }
495 6
                break;
496 4
            case ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE:
497
                // With STI the table will only be queried once, make sure that the filters
498
                // are added to the root entity
499 4
                $targetEntity = $this->em->getClassMetadata($targetEntity->rootEntityName);
500 4
                break;
501
            default:
502
                //@todo: throw exception?
503
                return '';
504
        }
505
506 43
        $filterClauses = [];
507 43
        foreach ($this->em->getFilters()->getEnabledFilters() as $filter) {
508 10
            if ('' !== $filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) {
509 10
                $filterClauses[] = '(' . $filterExpr . ')';
510
            }
511
        }
512
513 43
        return implode(' AND ', $filterClauses);
514
    }
515
516
    /**
517
     * {@inheritdoc}
518
     */
519 619
    public function walkSelectStatement(AST\SelectStatement $AST)
520
    {
521 619
        $limit    = $this->query->getMaxResults();
522 619
        $offset   = $this->query->getFirstResult();
523 619
        $lockMode = $this->query->getHint(Query::HINT_LOCK_MODE);
524 619
        $sql      = $this->walkSelectClause($AST->selectClause)
525 619
            . $this->walkFromClause($AST->fromClause)
526 616
            . $this->walkWhereClause($AST->whereClause);
527
528 613
        if ($AST->groupByClause) {
529 23
            $sql .= $this->walkGroupByClause($AST->groupByClause);
530
        }
531
532 613
        if ($AST->havingClause) {
533 14
            $sql .= $this->walkHavingClause($AST->havingClause);
534
        }
535
536 613
        if ($AST->orderByClause) {
537 142
            $sql .= $this->walkOrderByClause($AST->orderByClause);
538
        }
539
540 612
        if ( ! $AST->orderByClause && ($orderBySql = $this->_generateOrderedCollectionOrderByItems())) {
541 6
            $sql .= ' ORDER BY ' . $orderBySql;
542
        }
543
544 612
        if ($limit !== null || $offset !== null) {
545 39
            $sql = $this->platform->modifyLimitQuery($sql, $limit, $offset);
546
        }
547
548 612
        if ($lockMode === null || $lockMode === false || $lockMode === LockMode::NONE) {
549 607
            return $sql;
550
        }
551
552 5
        if ($lockMode === LockMode::PESSIMISTIC_READ) {
553 3
            return $sql . ' ' . $this->platform->getReadLockSQL();
554
        }
555
556 2
        if ($lockMode === LockMode::PESSIMISTIC_WRITE) {
557 1
            return $sql . ' ' . $this->platform->getWriteLockSQL();
558
        }
559
560 1
        if ($lockMode !== LockMode::OPTIMISTIC) {
561
            throw QueryException::invalidLockMode();
562
        }
563
564 1
        foreach ($this->selectedClasses as $selectedClass) {
565 1
            if ( ! $selectedClass['class']->isVersioned) {
566 1
                throw OptimisticLockException::lockFailed($selectedClass['class']->name);
567
            }
568
        }
569
570
        return $sql;
571
    }
572
573
    /**
574
     * {@inheritdoc}
575
     */
576 25
    public function walkUpdateStatement(AST\UpdateStatement $AST)
577
    {
578 25
        $this->useSqlTableAliases = false;
579 25
        $this->rsm->isSelect      = false;
580
581 25
        return $this->walkUpdateClause($AST->updateClause)
582 25
            . $this->walkWhereClause($AST->whereClause);
583
    }
584
585
    /**
586
     * {@inheritdoc}
587
     */
588 36
    public function walkDeleteStatement(AST\DeleteStatement $AST)
589
    {
590 36
        $this->useSqlTableAliases = false;
591 36
        $this->rsm->isSelect      = false;
592
593 36
        return $this->walkDeleteClause($AST->deleteClause)
594 36
            . $this->walkWhereClause($AST->whereClause);
595
    }
596
597
    /**
598
     * Walks down an IdentificationVariable AST node, thereby generating the appropriate SQL.
599
     * This one differs of ->walkIdentificationVariable() because it generates the entity identifiers.
600
     *
601
     * @param string $identVariable
602
     *
603
     * @return string
604
     */
605 2
    public function walkEntityIdentificationVariable($identVariable)
606
    {
607 2
        $class      = $this->queryComponents[$identVariable]['metadata'];
608 2
        $tableAlias = $this->getSQLTableAlias($class->getTableName(), $identVariable);
609 2
        $sqlParts   = [];
610
611 2
        foreach ($this->quoteStrategy->getIdentifierColumnNames($class, $this->platform) as $columnName) {
612 2
            $sqlParts[] = $tableAlias . '.' . $columnName;
613
        }
614
615 2
        return implode(', ', $sqlParts);
616
    }
617
618
    /**
619
     * Walks down an IdentificationVariable (no AST node associated), thereby generating the SQL.
620
     *
621
     * @param string $identificationVariable
622
     * @param string $fieldName
623
     *
624
     * @return string The SQL.
625
     */
626 419
    public function walkIdentificationVariable($identificationVariable, $fieldName = null)
627
    {
628 419
        $class = $this->queryComponents[$identificationVariable]['metadata'];
629
630
        if (
631 419
            $fieldName !== null && $class->isInheritanceTypeJoined() &&
632 419
            isset($class->fieldMappings[$fieldName]['inherited'])
633
        ) {
634 37
            $class = $this->em->getClassMetadata($class->fieldMappings[$fieldName]['inherited']);
635
        }
636
637 419
        return $this->getSQLTableAlias($class->getTableName(), $identificationVariable);
638
    }
639
640
    /**
641
     * {@inheritdoc}
642
     */
643 487
    public function walkPathExpression($pathExpr)
644
    {
645 487
        $sql = '';
646
647 487
        switch ($pathExpr->type) {
648 487
            case AST\PathExpression::TYPE_STATE_FIELD:
649 467
                $fieldName = $pathExpr->field;
650 467
                $dqlAlias = $pathExpr->identificationVariable;
651 467
                $class = $this->queryComponents[$dqlAlias]['metadata'];
652
653 467
                if ($this->useSqlTableAliases) {
654 419
                    $sql .= $this->walkIdentificationVariable($dqlAlias, $fieldName) . '.';
655
                }
656
657 467
                $sql .= $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform);
658 467
                break;
659
660 61
            case AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION:
661
                // 1- the owning side:
662
                //    Just use the foreign key, i.e. u.group_id
663 61
                $fieldName = $pathExpr->field;
664 61
                $dqlAlias = $pathExpr->identificationVariable;
665 61
                $class = $this->queryComponents[$dqlAlias]['metadata'];
666
667 61
                if (isset($class->associationMappings[$fieldName]['inherited'])) {
668 2
                    $class = $this->em->getClassMetadata($class->associationMappings[$fieldName]['inherited']);
669
                }
670
671 61
                $assoc = $class->associationMappings[$fieldName];
672
673 61
                if ( ! $assoc['isOwningSide']) {
674 2
                    throw QueryException::associationPathInverseSideNotSupported();
675
                }
676
677
                // COMPOSITE KEYS NOT (YET?) SUPPORTED
678 59
                if (count($assoc['sourceToTargetKeyColumns']) > 1) {
679 1
                    throw QueryException::associationPathCompositeKeyNotSupported();
680
                }
681
682 58
                if ($this->useSqlTableAliases) {
683 55
                    $sql .= $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.';
684
                }
685
686 58
                $sql .= reset($assoc['targetToSourceKeyColumns']);
687 58
                break;
688
689
            default:
690
                throw QueryException::invalidPathExpression($pathExpr);
691
        }
692
693 484
        return $sql;
694
    }
695
696
    /**
697
     * {@inheritdoc}
698
     */
699 619
    public function walkSelectClause($selectClause)
700
    {
701 619
        $sql = 'SELECT ' . (($selectClause->isDistinct) ? 'DISTINCT ' : '');
702 619
        $sqlSelectExpressions = array_filter(array_map([$this, 'walkSelectExpression'], $selectClause->selectExpressions));
703
704 619
        if ($this->query->getHint(Query::HINT_INTERNAL_ITERATION) == true && $selectClause->isDistinct) {
705
            $this->query->setHint(self::HINT_DISTINCT, true);
706
        }
707
708 619
        $addMetaColumns = ! $this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) &&
709 456
            $this->query->getHydrationMode() == Query::HYDRATE_OBJECT
710
            ||
711 288
            $this->query->getHydrationMode() != Query::HYDRATE_OBJECT &&
712 619
            $this->query->getHint(Query::HINT_INCLUDE_META_COLUMNS);
713
714 619
        foreach ($this->selectedClasses as $selectedClass) {
715 483
            $class       = $selectedClass['class'];
716 483
            $dqlAlias    = $selectedClass['dqlAlias'];
717 483
            $resultAlias = $selectedClass['resultAlias'];
718
719
            // Register as entity or joined entity result
720 483
            if ($this->queryComponents[$dqlAlias]['relation'] === null) {
721 483
                $this->rsm->addEntityResult($class->name, $dqlAlias, $resultAlias);
722
            } else {
723 157
                $this->rsm->addJoinedEntityResult(
724 157
                    $class->name,
725
                    $dqlAlias,
726 157
                    $this->queryComponents[$dqlAlias]['parent'],
727 157
                    $this->queryComponents[$dqlAlias]['relation']['fieldName']
728
                );
729
            }
730
731 483
            if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) {
732
                // Add discriminator columns to SQL
733 90
                $rootClass   = $this->em->getClassMetadata($class->rootEntityName);
734 90
                $tblAlias    = $this->getSQLTableAlias($rootClass->getTableName(), $dqlAlias);
735 90
                $discrColumn = $rootClass->discriminatorColumn;
736 90
                $columnAlias = $this->getSQLColumnAlias($discrColumn['name']);
737
738 90
                $sqlSelectExpressions[] = $tblAlias . '.' . $discrColumn['name'] . ' AS ' . $columnAlias;
739
740 90
                $this->rsm->setDiscriminatorColumn($dqlAlias, $columnAlias);
741 90
                $this->rsm->addMetaResult($dqlAlias, $columnAlias, $discrColumn['fieldName'], false, $discrColumn['type']);
742
            }
743
744
            // Add foreign key columns to SQL, if necessary
745 483
            if ( ! $addMetaColumns && ! $class->containsForeignIdentifier) {
746 180
                continue;
747
            }
748
749
            // Add foreign key columns of class and also parent classes
750 353
            foreach ($class->associationMappings as $assoc) {
751 315
                if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) {
752 264
                    continue;
753 282
                } else if ( !$addMetaColumns && !isset($assoc['id'])) {
754
                    continue;
755
                }
756
757 282
                $targetClass   = $this->em->getClassMetadata($assoc['targetEntity']);
758 282
                $isIdentifier  = (isset($assoc['id']) && $assoc['id'] === true);
759 282
                $owningClass   = (isset($assoc['inherited'])) ? $this->em->getClassMetadata($assoc['inherited']) : $class;
760 282
                $sqlTableAlias = $this->getSQLTableAlias($owningClass->getTableName(), $dqlAlias);
761
762 282
                foreach ($assoc['joinColumns'] as $joinColumn) {
763 282
                    $columnName  = $joinColumn['name'];
764 282
                    $columnAlias = $this->getSQLColumnAlias($columnName);
765 282
                    $columnType  = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em);
766
767 282
                    $quotedColumnName       = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
768 282
                    $sqlSelectExpressions[] = $sqlTableAlias . '.' . $quotedColumnName . ' AS ' . $columnAlias;
769
770 282
                    $this->rsm->addMetaResult($dqlAlias, $columnAlias, $columnName, $isIdentifier, $columnType);
771
                }
772
            }
773
774
            // Add foreign key columns to SQL, if necessary
775 353
            if ( ! $addMetaColumns) {
776 8
                continue;
777
            }
778
779
            // Add foreign key columns of subclasses
780 348
            foreach ($class->subClasses as $subClassName) {
781 31
                $subClass      = $this->em->getClassMetadata($subClassName);
782 31
                $sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
783
784 31
                foreach ($subClass->associationMappings as $assoc) {
785
                    // Skip if association is inherited
786 27
                    if (isset($assoc['inherited'])) continue;
787
788 16
                    if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
789 14
                        $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
790
791 14
                        foreach ($assoc['joinColumns'] as $joinColumn) {
792 14
                            $columnName  = $joinColumn['name'];
793 14
                            $columnAlias = $this->getSQLColumnAlias($columnName);
794 14
                            $columnType  = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em);
795
796 14
                            $quotedColumnName       = $this->quoteStrategy->getJoinColumnName($joinColumn, $subClass, $this->platform);
797 14
                            $sqlSelectExpressions[] = $sqlTableAlias . '.' . $quotedColumnName . ' AS ' . $columnAlias;
798
799 348
                            $this->rsm->addMetaResult($dqlAlias, $columnAlias, $columnName, $subClass->isIdentifier($columnName), $columnType);
800
                        }
801
                    }
802
                }
803
            }
804
        }
805
806 619
        $sql .= implode(', ', $sqlSelectExpressions);
807
808 619
        return $sql;
809
    }
810
811
    /**
812
     * {@inheritdoc}
813
     */
814 620
    public function walkFromClause($fromClause)
815
    {
816 620
        $identificationVarDecls = $fromClause->identificationVariableDeclarations;
817 620
        $sqlParts = [];
818
819 620
        foreach ($identificationVarDecls as $identificationVariableDecl) {
820 620
            $sqlParts[] = $this->walkIdentificationVariableDeclaration($identificationVariableDecl);
821
        }
822
823 617
        return ' FROM ' . implode(', ', $sqlParts);
824
    }
825
826
    /**
827
     * Walks down a IdentificationVariableDeclaration AST node, thereby generating the appropriate SQL.
828
     *
829
     * @param AST\IdentificationVariableDeclaration $identificationVariableDecl
830
     *
831
     * @return string
832
     */
833 621
    public function walkIdentificationVariableDeclaration($identificationVariableDecl)
834
    {
835 621
        $sql = $this->walkRangeVariableDeclaration($identificationVariableDecl->rangeVariableDeclaration);
836
837 621
        if ($identificationVariableDecl->indexBy) {
838 5
            $this->walkIndexBy($identificationVariableDecl->indexBy);
839
        }
840
841 621
        foreach ($identificationVariableDecl->joins as $join) {
842 243
            $sql .= $this->walkJoin($join);
843
        }
844
845 618
        return $sql;
846
    }
847
848
    /**
849
     * Walks down a IndexBy AST node.
850
     *
851
     * @param AST\IndexBy $indexBy
852
     *
853
     * @return void
854
     */
855 8
    public function walkIndexBy($indexBy)
856
    {
857 8
        $pathExpression = $indexBy->simpleStateFieldPathExpression;
858 8
        $alias          = $pathExpression->identificationVariable;
859 8
        $field          = $pathExpression->field;
860
861 8
        if (isset($this->scalarFields[$alias][$field])) {
862
            $this->rsm->addIndexByScalar($this->scalarFields[$alias][$field]);
863
864
            return;
865
        }
866
867 8
        $this->rsm->addIndexBy($alias, $field);
868 8
    }
869
870
    /**
871
     * Walks down a RangeVariableDeclaration AST node, thereby generating the appropriate SQL.
872
     *
873
     * @param AST\RangeVariableDeclaration $rangeVariableDeclaration
874
     *
875
     * @return string
876
     */
877 621
    public function walkRangeVariableDeclaration($rangeVariableDeclaration)
878
    {
879 621
        $class    = $this->em->getClassMetadata($rangeVariableDeclaration->abstractSchemaName);
880 621
        $dqlAlias = $rangeVariableDeclaration->aliasIdentificationVariable;
881
882 621
        if ($rangeVariableDeclaration->isRoot) {
883 621
            $this->rootAliases[] = $dqlAlias;
884
        }
885
886 621
        $sql = $this->platform->appendLockHint(
887 621
            $this->quoteStrategy->getTableName($class, $this->platform) . ' ' .
888 621
            $this->getSQLTableAlias($class->getTableName(), $dqlAlias),
889 621
            $this->query->getHint(Query::HINT_LOCK_MODE)
890
        );
891
892 621
        if ($class->isInheritanceTypeJoined()) {
893 87
            $sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias);
894
        }
895
896 621
        return $sql;
897
    }
898
899
    /**
900
     * Walks down a JoinAssociationDeclaration AST node, thereby generating the appropriate SQL.
901
     *
902
     * @param AST\JoinAssociationDeclaration $joinAssociationDeclaration
903
     * @param int                            $joinType
904
     * @param AST\ConditionalExpression      $condExpr
905
     *
906
     * @return string
907
     *
908
     * @throws QueryException
909
     */
910 226
    public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joinType = AST\Join::JOIN_TYPE_INNER, $condExpr = null)
911
    {
912 226
        $sql = '';
913
914 226
        $associationPathExpression = $joinAssociationDeclaration->joinAssociationPathExpression;
915 226
        $joinedDqlAlias            = $joinAssociationDeclaration->aliasIdentificationVariable;
916 226
        $indexBy                   = $joinAssociationDeclaration->indexBy;
917
918 226
        $relation        = $this->queryComponents[$joinedDqlAlias]['relation'];
919 226
        $targetClass     = $this->em->getClassMetadata($relation['targetEntity']);
920 226
        $sourceClass     = $this->em->getClassMetadata($relation['sourceEntity']);
921 226
        $targetTableName = $this->quoteStrategy->getTableName($targetClass, $this->platform);
922
923 226
        $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName(), $joinedDqlAlias);
924 226
        $sourceTableAlias = $this->getSQLTableAlias($sourceClass->getTableName(), $associationPathExpression->identificationVariable);
925
926
        // Ensure we got the owning side, since it has all mapping info
927 226
        $assoc = ( ! $relation['isOwningSide']) ? $targetClass->associationMappings[$relation['mappedBy']] : $relation;
928
929 226
        if ($this->query->getHint(Query::HINT_INTERNAL_ITERATION) == true && (!$this->query->getHint(self::HINT_DISTINCT) || isset($this->selectedClasses[$joinedDqlAlias]))) {
930 4
            if ($relation['type'] == ClassMetadata::ONE_TO_MANY || $relation['type'] == ClassMetadata::MANY_TO_MANY) {
931 3
                throw QueryException::iterateWithFetchJoinNotAllowed($assoc);
932
            }
933
        }
934
935 223
        $targetTableJoin = null;
936
937
        // This condition is not checking ClassMetadata::MANY_TO_ONE, because by definition it cannot
938
        // be the owning side and previously we ensured that $assoc is always the owning side of the associations.
939
        // The owning side is necessary at this point because only it contains the JoinColumn information.
940
        switch (true) {
941 223
            case ($assoc['type'] & ClassMetadata::TO_ONE):
942 176
                $conditions = [];
943
944 176
                foreach ($assoc['joinColumns'] as $joinColumn) {
945 176
                    $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
946 176
                    $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform);
947
948 176
                    if ($relation['isOwningSide']) {
949 103
                        $conditions[] = $sourceTableAlias . '.' . $quotedSourceColumn . ' = ' . $targetTableAlias . '.' . $quotedTargetColumn;
950
951 103
                        continue;
952
                    }
953
954 106
                    $conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $targetTableAlias . '.' . $quotedSourceColumn;
955
                }
956
957
                // Apply remaining inheritance restrictions
958 176
                $discrSql = $this->_generateDiscriminatorColumnConditionSQL([$joinedDqlAlias]);
959
960 176
                if ($discrSql) {
961 3
                    $conditions[] = $discrSql;
962
                }
963
964
                // Apply the filters
965 176
                $filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias);
966
967 176
                if ($filterExpr) {
968 1
                    $conditions[] = $filterExpr;
969
                }
970
971
                $targetTableJoin = [
972 176
                    'table' => $targetTableName . ' ' . $targetTableAlias,
973 176
                    'condition' => implode(' AND ', $conditions),
974
                ];
975 176
                break;
976
977 57
            case ($assoc['type'] == ClassMetadata::MANY_TO_MANY):
978
                // Join relation table
979 57
                $joinTable      = $assoc['joinTable'];
980 57
                $joinTableAlias = $this->getSQLTableAlias($joinTable['name'], $joinedDqlAlias);
981 57
                $joinTableName  = $this->quoteStrategy->getJoinTableName($assoc, $sourceClass, $this->platform);
982
983 57
                $conditions      = [];
984 57
                $relationColumns = ($relation['isOwningSide'])
985 48
                    ? $assoc['joinTable']['joinColumns']
986 57
                    : $assoc['joinTable']['inverseJoinColumns'];
987
988 57
                foreach ($relationColumns as $joinColumn) {
989 57
                    $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
990 57
                    $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform);
991
992 57
                    $conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $quotedSourceColumn;
993
                }
994
995 57
                $sql .= $joinTableName . ' ' . $joinTableAlias . ' ON ' . implode(' AND ', $conditions);
996
997
                // Join target table
998 57
                $sql .= ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) ? ' LEFT JOIN ' : ' INNER JOIN ';
999
1000 57
                $conditions      = [];
1001 57
                $relationColumns = ($relation['isOwningSide'])
1002 48
                    ? $assoc['joinTable']['inverseJoinColumns']
1003 57
                    : $assoc['joinTable']['joinColumns'];
1004
1005 57
                foreach ($relationColumns as $joinColumn) {
1006 57
                    $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
1007 57
                    $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform);
1008
1009 57
                    $conditions[] = $targetTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $quotedSourceColumn;
1010
                }
1011
1012
                // Apply remaining inheritance restrictions
1013 57
                $discrSql = $this->_generateDiscriminatorColumnConditionSQL([$joinedDqlAlias]);
1014
1015 57
                if ($discrSql) {
1016 1
                    $conditions[] = $discrSql;
1017
                }
1018
1019
                // Apply the filters
1020 57
                $filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias);
1021
1022 57
                if ($filterExpr) {
1023 1
                    $conditions[] = $filterExpr;
1024
                }
1025
1026
                $targetTableJoin = [
1027 57
                    'table' => $targetTableName . ' ' . $targetTableAlias,
1028 57
                    'condition' => implode(' AND ', $conditions),
1029
                ];
1030 57
                break;
1031
1032
            default:
1033
                throw new \BadMethodCallException('Type of association must be one of *_TO_ONE or MANY_TO_MANY');
1034
        }
1035
1036
        // Handle WITH clause
1037 223
        $withCondition = (null === $condExpr) ? '' : ('(' . $this->walkConditionalExpression($condExpr) . ')');
1038
1039 223
        if ($targetClass->isInheritanceTypeJoined()) {
1040 9
            $ctiJoins = $this->_generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias);
1041
            // If we have WITH condition, we need to build nested joins for target class table and cti joins
1042 9
            if ($withCondition) {
1043 1
                $sql .= '(' . $targetTableJoin['table'] . $ctiJoins . ') ON ' . $targetTableJoin['condition'];
1044
            } else {
1045 9
                $sql .= $targetTableJoin['table'] . ' ON ' . $targetTableJoin['condition'] . $ctiJoins;
1046
            }
1047
        } else {
1048 214
            $sql .= $targetTableJoin['table'] . ' ON ' . $targetTableJoin['condition'];
1049
        }
1050
1051 223
        if ($withCondition) {
1052 4
            $sql .= ' AND ' . $withCondition;
1053
        }
1054
1055
        // Apply the indexes
1056 223
        if ($indexBy) {
1057
            // For Many-To-One or One-To-One associations this obviously makes no sense, but is ignored silently.
1058 5
            $this->walkIndexBy($indexBy);
1059 218
        } else if (isset($relation['indexBy'])) {
1060 2
            $this->rsm->addIndexBy($joinedDqlAlias, $relation['indexBy']);
1061
        }
1062
1063 223
        return $sql;
1064
    }
1065
1066
    /**
1067
     * {@inheritdoc}
1068
     */
1069 63
    public function walkFunction($function)
1070
    {
1071 63
        return $function->getSql($this);
1072
    }
1073
1074
    /**
1075
     * {@inheritdoc}
1076
     */
1077 153
    public function walkOrderByClause($orderByClause)
1078
    {
1079 153
        $orderByItems = array_map([$this, 'walkOrderByItem'], $orderByClause->orderByItems);
1080
1081 152
        if (($collectionOrderByItems = $this->_generateOrderedCollectionOrderByItems()) !== '') {
1082
            $orderByItems = array_merge($orderByItems, (array) $collectionOrderByItems);
1083
        }
1084
1085 152
        return ' ORDER BY ' . implode(', ', $orderByItems);
1086
    }
1087
1088
    /**
1089
     * {@inheritdoc}
1090
     */
1091 171
    public function walkOrderByItem($orderByItem)
1092
    {
1093 171
        $type = strtoupper($orderByItem->type);
1094 171
        $expr = $orderByItem->expression;
1095 171
        $sql  = ($expr instanceof AST\Node)
1096 164
            ? $expr->dispatch($this)
1097 170
            : $this->walkResultVariable($this->queryComponents[$expr]['token']['value']);
1098
1099 170
        $this->orderedColumnsMap[$sql] = $type;
1100
1101 170
        if ($expr instanceof AST\Subselect) {
1102 2
            return '(' . $sql . ') ' . $type;
1103
        }
1104
1105 168
        return $sql . ' ' . $type;
1106
    }
1107
1108
    /**
1109
     * {@inheritdoc}
1110
     */
1111 14
    public function walkHavingClause($havingClause)
1112
    {
1113 14
        return ' HAVING ' . $this->walkConditionalExpression($havingClause->conditionalExpression);
1114
    }
1115
1116
    /**
1117
     * {@inheritdoc}
1118
     */
1119 243
    public function walkJoin($join)
1120
    {
1121 243
        $joinType        = $join->joinType;
1122 243
        $joinDeclaration = $join->joinAssociationDeclaration;
1123
1124 243
        $sql = ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER)
1125 55
            ? ' LEFT JOIN '
1126 243
            : ' INNER JOIN ';
1127
1128
        switch (true) {
1129 243
            case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\RangeVariableDeclaration):
1130 17
                $class      = $this->em->getClassMetadata($joinDeclaration->abstractSchemaName);
1131 17
                $dqlAlias   = $joinDeclaration->aliasIdentificationVariable;
1132 17
                $tableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
1133 17
                $conditions = [];
1134
1135 17
                if ($join->conditionalExpression) {
1136 15
                    $conditions[] = '(' . $this->walkConditionalExpression($join->conditionalExpression) . ')';
1137
                }
1138
1139 17
                $condExprConjunction = ($class->isInheritanceTypeJoined() && $joinType != AST\Join::JOIN_TYPE_LEFT && $joinType != AST\Join::JOIN_TYPE_LEFTOUTER)
1140 3
                    ? ' AND '
1141 17
                    : ' ON ';
1142
1143 17
                $sql .= $this->walkRangeVariableDeclaration($joinDeclaration);
1144
1145
                // Apply remaining inheritance restrictions
1146 17
                $discrSql = $this->_generateDiscriminatorColumnConditionSQL([$dqlAlias]);
1147
1148 17
                if ($discrSql) {
1149 3
                    $conditions[] = $discrSql;
1150
                }
1151
1152
                // Apply the filters
1153 17
                $filterExpr = $this->generateFilterConditionSQL($class, $tableAlias);
1154
1155 17
                if ($filterExpr) {
1156
                    $conditions[] = $filterExpr;
1157
                }
1158
1159 17
                if ($conditions) {
1160 15
                    $sql .= $condExprConjunction . implode(' AND ', $conditions);
1161
                }
1162
1163 17
                break;
1164
1165 226
            case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\JoinAssociationDeclaration):
1166 226
                $sql .= $this->walkJoinAssociationDeclaration($joinDeclaration, $joinType, $join->conditionalExpression);
1167 223
                break;
1168
        }
1169
1170 240
        return $sql;
1171
    }
1172
1173
    /**
1174
     * Walks down a CoalesceExpression AST node and generates the corresponding SQL.
1175
     *
1176
     * @param AST\CoalesceExpression $coalesceExpression
1177
     *
1178
     * @return string The SQL.
1179
     */
1180 2
    public function walkCoalesceExpression($coalesceExpression)
1181
    {
1182 2
        $sql = 'COALESCE(';
1183
1184 2
        $scalarExpressions = [];
1185
1186 2
        foreach ($coalesceExpression->scalarExpressions as $scalarExpression) {
1187 2
            $scalarExpressions[] = $this->walkSimpleArithmeticExpression($scalarExpression);
1188
        }
1189
1190 2
        $sql .= implode(', ', $scalarExpressions) . ')';
1191
1192 2
        return $sql;
1193
    }
1194
1195
    /**
1196
     * Walks down a NullIfExpression AST node and generates the corresponding SQL.
1197
     *
1198
     * @param AST\NullIfExpression $nullIfExpression
1199
     *
1200
     * @return string The SQL.
1201
     */
1202 3
    public function walkNullIfExpression($nullIfExpression)
1203
    {
1204 3
        $firstExpression = is_string($nullIfExpression->firstExpression)
1205
            ? $this->conn->quote($nullIfExpression->firstExpression)
1206 3
            : $this->walkSimpleArithmeticExpression($nullIfExpression->firstExpression);
1207
1208 3
        $secondExpression = is_string($nullIfExpression->secondExpression)
1209
            ? $this->conn->quote($nullIfExpression->secondExpression)
1210 3
            : $this->walkSimpleArithmeticExpression($nullIfExpression->secondExpression);
1211
1212 3
        return 'NULLIF(' . $firstExpression . ', ' . $secondExpression . ')';
1213
    }
1214
1215
    /**
1216
     * Walks down a GeneralCaseExpression AST node and generates the corresponding SQL.
1217
     *
1218
     * @param AST\GeneralCaseExpression $generalCaseExpression
1219
     *
1220
     * @return string The SQL.
1221
     */
1222 9
    public function walkGeneralCaseExpression(AST\GeneralCaseExpression $generalCaseExpression)
1223
    {
1224 9
        $sql = 'CASE';
1225
1226 9
        foreach ($generalCaseExpression->whenClauses as $whenClause) {
1227 9
            $sql .= ' WHEN ' . $this->walkConditionalExpression($whenClause->caseConditionExpression);
1228 9
            $sql .= ' THEN ' . $this->walkSimpleArithmeticExpression($whenClause->thenScalarExpression);
1229
        }
1230
1231 9
        $sql .= ' ELSE ' . $this->walkSimpleArithmeticExpression($generalCaseExpression->elseScalarExpression) . ' END';
1232
1233 9
        return $sql;
1234
    }
1235
1236
    /**
1237
     * Walks down a SimpleCaseExpression AST node and generates the corresponding SQL.
1238
     *
1239
     * @param AST\SimpleCaseExpression $simpleCaseExpression
1240
     *
1241
     * @return string The SQL.
1242
     */
1243 5
    public function walkSimpleCaseExpression($simpleCaseExpression)
1244
    {
1245 5
        $sql = 'CASE ' . $this->walkStateFieldPathExpression($simpleCaseExpression->caseOperand);
1246
1247 5
        foreach ($simpleCaseExpression->simpleWhenClauses as $simpleWhenClause) {
1248 5
            $sql .= ' WHEN ' . $this->walkSimpleArithmeticExpression($simpleWhenClause->caseScalarExpression);
1249 5
            $sql .= ' THEN ' . $this->walkSimpleArithmeticExpression($simpleWhenClause->thenScalarExpression);
1250
        }
1251
1252 5
        $sql .= ' ELSE ' . $this->walkSimpleArithmeticExpression($simpleCaseExpression->elseScalarExpression) . ' END';
1253
1254 5
        return $sql;
1255
    }
1256
1257
    /**
1258
     * {@inheritdoc}
1259
     */
1260 619
    public function walkSelectExpression($selectExpression)
1261
    {
1262 619
        $sql    = '';
1263 619
        $expr   = $selectExpression->expression;
1264 619
        $hidden = $selectExpression->hiddenAliasResultVariable;
1265
1266
        switch (true) {
1267 619
            case ($expr instanceof AST\PathExpression):
1268 101
                if ($expr->type !== AST\PathExpression::TYPE_STATE_FIELD) {
1269
                    throw QueryException::invalidPathExpression($expr);
1270
                }
1271
1272 101
                $fieldName = $expr->field;
1273 101
                $dqlAlias  = $expr->identificationVariable;
1274 101
                $qComp     = $this->queryComponents[$dqlAlias];
1275 101
                $class     = $qComp['metadata'];
1276
1277 101
                $resultAlias = $selectExpression->fieldIdentificationVariable ?: $fieldName;
1278 101
                $tableName   = ($class->isInheritanceTypeJoined())
1279 11
                    ? $this->em->getUnitOfWork()->getEntityPersister($class->name)->getOwningTable($fieldName)
1280 101
                    : $class->getTableName();
1281
1282 101
                $sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias);
1283 101
                $fieldMapping  = $class->fieldMappings[$fieldName];
1284 101
                $columnName    = $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform);
1285 101
                $columnAlias   = $this->getSQLColumnAlias($fieldMapping['columnName']);
1286 101
                $col           = $sqlTableAlias . '.' . $columnName;
1287
1288 101
                if (isset($fieldMapping['requireSQLConversion'])) {
1289 2
                    $type = Type::getType($fieldMapping['type']);
1290 2
                    $col  = $type->convertToPHPValueSQL($col, $this->conn->getDatabasePlatform());
1291
                }
1292
1293 101
                $sql .= $col . ' AS ' . $columnAlias;
1294
1295 101
                $this->scalarResultAliasMap[$resultAlias] = $columnAlias;
1296
1297 101
                if ( ! $hidden) {
1298 101
                    $this->rsm->addScalarResult($columnAlias, $resultAlias, $fieldMapping['type']);
1299 101
                    $this->scalarFields[$dqlAlias][$fieldName] = $columnAlias;
1300
                }
1301
1302 101
                break;
1303
1304 569
            case ($expr instanceof AST\AggregateExpression):
1305 534
            case ($expr instanceof AST\Functions\FunctionNode):
1306 521
            case ($expr instanceof AST\SimpleArithmeticExpression):
1307 521
            case ($expr instanceof AST\ArithmeticTerm):
1308 519
            case ($expr instanceof AST\ArithmeticFactor):
1309 518
            case ($expr instanceof AST\ParenthesisExpression):
1310 517
            case ($expr instanceof AST\Literal):
1311 516
            case ($expr instanceof AST\NullIfExpression):
1312 515
            case ($expr instanceof AST\CoalesceExpression):
1313 514
            case ($expr instanceof AST\GeneralCaseExpression):
1314 510
            case ($expr instanceof AST\SimpleCaseExpression):
1315 106
                $columnAlias = $this->getSQLColumnAlias('sclr');
1316 106
                $resultAlias = $selectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
1317
1318 106
                $sql .= $expr->dispatch($this) . ' AS ' . $columnAlias;
1319
1320 106
                $this->scalarResultAliasMap[$resultAlias] = $columnAlias;
1321
1322 106
                if ( ! $hidden) {
1323
                    // We cannot resolve field type here; assume 'string'.
1324 106
                    $this->rsm->addScalarResult($columnAlias, $resultAlias, 'string');
1325
                }
1326 106
                break;
1327
1328 509
            case ($expr instanceof AST\Subselect):
1329 15
                $columnAlias = $this->getSQLColumnAlias('sclr');
1330 15
                $resultAlias = $selectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
1331
1332 15
                $sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias;
1333
1334 15
                $this->scalarResultAliasMap[$resultAlias] = $columnAlias;
1335
1336 15
                if ( ! $hidden) {
1337
                    // We cannot resolve field type here; assume 'string'.
1338 13
                    $this->rsm->addScalarResult($columnAlias, $resultAlias, 'string');
1339
                }
1340 15
                break;
1341
1342 505
            case ($expr instanceof AST\NewObjectExpression):
1343 22
                $sql .= $this->walkNewObject($expr,$selectExpression->fieldIdentificationVariable);
1344 22
                break;
1345
1346
            default:
1347
                // IdentificationVariable or PartialObjectExpression
1348 483
                if ($expr instanceof AST\PartialObjectExpression) {
1349 16
                    $dqlAlias = $expr->identificationVariable;
1350 16
                    $partialFieldSet = $expr->partialFieldSet;
1351
                } else {
1352 478
                    $dqlAlias = $expr;
1353 478
                    $partialFieldSet = [];
1354
                }
1355
1356 483
                $queryComp   = $this->queryComponents[$dqlAlias];
1357 483
                $class       = $queryComp['metadata'];
1358 483
                $resultAlias = $selectExpression->fieldIdentificationVariable ?: null;
1359
1360 483
                if ( ! isset($this->selectedClasses[$dqlAlias])) {
1361 483
                    $this->selectedClasses[$dqlAlias] = [
1362 483
                        'class'       => $class,
1363 483
                        'dqlAlias'    => $dqlAlias,
1364 483
                        'resultAlias' => $resultAlias
1365
                    ];
1366
                }
1367
1368 483
                $sqlParts = [];
1369
1370
                // Select all fields from the queried class
1371 483
                foreach ($class->fieldMappings as $fieldName => $mapping) {
1372 482
                    if ($partialFieldSet && ! in_array($fieldName, $partialFieldSet)) {
1373 14
                        continue;
1374
                    }
1375
1376 481
                    $tableName = (isset($mapping['inherited']))
1377 51
                        ? $this->em->getClassMetadata($mapping['inherited'])->getTableName()
1378 481
                        : $class->getTableName();
1379
1380 481
                    $sqlTableAlias    = $this->getSQLTableAlias($tableName, $dqlAlias);
1381 481
                    $columnAlias      = $this->getSQLColumnAlias($mapping['columnName']);
1382 481
                    $quotedColumnName = $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform);
1383
1384 481
                    $col = $sqlTableAlias . '.' . $quotedColumnName;
1385
1386 481
                    if (isset($mapping['requireSQLConversion'])) {
1387 5
                        $type = Type::getType($mapping['type']);
1388 5
                        $col = $type->convertToPHPValueSQL($col, $this->platform);
1389
                    }
1390
1391 481
                    $sqlParts[] = $col . ' AS '. $columnAlias;
1392
1393 481
                    $this->scalarResultAliasMap[$resultAlias][] = $columnAlias;
1394
1395 481
                    $this->rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $class->name);
1396
                }
1397
1398
                // Add any additional fields of subclasses (excluding inherited fields)
1399
                // 1) on Single Table Inheritance: always, since its marginal overhead
1400
                // 2) on Class Table Inheritance only if partial objects are disallowed,
1401
                //    since it requires outer joining subtables.
1402 483
                if ($class->isInheritanceTypeSingleTable() || ! $this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {
1403 390
                    foreach ($class->subClasses as $subClassName) {
1404 42
                        $subClass      = $this->em->getClassMetadata($subClassName);
1405 42
                        $sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
1406
1407 42
                        foreach ($subClass->fieldMappings as $fieldName => $mapping) {
1408 42
                            if (isset($mapping['inherited']) || ($partialFieldSet && !in_array($fieldName, $partialFieldSet))) {
1409 42
                                continue;
1410
                            }
1411
1412 34
                            $columnAlias      = $this->getSQLColumnAlias($mapping['columnName']);
1413 34
                            $quotedColumnName = $this->quoteStrategy->getColumnName($fieldName, $subClass, $this->platform);
1414
1415 34
                            $col = $sqlTableAlias . '.' . $quotedColumnName;
1416
1417 34
                            if (isset($mapping['requireSQLConversion'])) {
1418
                                $type = Type::getType($mapping['type']);
1419
                                $col = $type->convertToPHPValueSQL($col, $this->platform);
1420
                            }
1421
1422 34
                            $sqlParts[] = $col . ' AS ' . $columnAlias;
1423
1424 34
                            $this->scalarResultAliasMap[$resultAlias][] = $columnAlias;
1425
1426 42
                            $this->rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $subClassName);
1427
                        }
1428
                    }
1429
                }
1430
1431 483
                $sql .= implode(', ', $sqlParts);
1432
        }
1433
1434 619
        return $sql;
1435
    }
1436
1437
    /**
1438
     * {@inheritdoc}
1439
     */
1440
    public function walkQuantifiedExpression($qExpr)
1441
    {
1442
        return ' ' . strtoupper($qExpr->type) . '(' . $this->walkSubselect($qExpr->subselect) . ')';
1443
    }
1444
1445
    /**
1446
     * {@inheritdoc}
1447
     */
1448 33
    public function walkSubselect($subselect)
1449
    {
1450 33
        $useAliasesBefore  = $this->useSqlTableAliases;
1451 33
        $rootAliasesBefore = $this->rootAliases;
1452
1453 33
        $this->rootAliases = []; // reset the rootAliases for the subselect
1454 33
        $this->useSqlTableAliases = true;
1455
1456 33
        $sql  = $this->walkSimpleSelectClause($subselect->simpleSelectClause);
1457 33
        $sql .= $this->walkSubselectFromClause($subselect->subselectFromClause);
1458 33
        $sql .= $this->walkWhereClause($subselect->whereClause);
0 ignored issues
show
It seems like $subselect->whereClause can be null; however, walkWhereClause() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
1459
1460 33
        $sql .= $subselect->groupByClause ? $this->walkGroupByClause($subselect->groupByClause) : '';
1461 33
        $sql .= $subselect->havingClause ? $this->walkHavingClause($subselect->havingClause) : '';
1462 33
        $sql .= $subselect->orderByClause ? $this->walkOrderByClause($subselect->orderByClause) : '';
1463
1464 33
        $this->rootAliases        = $rootAliasesBefore; // put the main aliases back
1465 33
        $this->useSqlTableAliases = $useAliasesBefore;
1466
1467 33
        return $sql;
1468
    }
1469
1470
    /**
1471
     * {@inheritdoc}
1472
     */
1473 33
    public function walkSubselectFromClause($subselectFromClause)
1474
    {
1475 33
        $identificationVarDecls = $subselectFromClause->identificationVariableDeclarations;
1476 33
        $sqlParts               = [];
1477
1478 33
        foreach ($identificationVarDecls as $subselectIdVarDecl) {
1479 33
            $sqlParts[] = $this->walkIdentificationVariableDeclaration($subselectIdVarDecl);
1480
        }
1481
1482 33
        return ' FROM ' . implode(', ', $sqlParts);
1483
    }
1484
1485
    /**
1486
     * {@inheritdoc}
1487
     */
1488 33
    public function walkSimpleSelectClause($simpleSelectClause)
1489
    {
1490 33
        return 'SELECT' . ($simpleSelectClause->isDistinct ? ' DISTINCT' : '')
1491 33
            . $this->walkSimpleSelectExpression($simpleSelectClause->simpleSelectExpression);
1492
    }
1493
1494
    /**
1495
     * @param \Doctrine\ORM\Query\AST\ParenthesisExpression $parenthesisExpression
1496
     *
1497
     * @return string.
1498
     */
1499 22
    public function walkParenthesisExpression(AST\ParenthesisExpression $parenthesisExpression)
1500
    {
1501 22
        return sprintf('(%s)', $parenthesisExpression->expression->dispatch($this));
1502
    }
1503
1504
    /**
1505
     * @param AST\NewObjectExpression $newObjectExpression
1506
     *
1507
     * @return string The SQL.
1508
     */
1509 22
    public function walkNewObject($newObjectExpression, $newObjectResultAlias=null)
1510
    {
1511 22
        $sqlSelectExpressions = [];
1512 22
        $objIndex             = $newObjectResultAlias?:$this->newObjectCounter++;
1513
1514 22
        foreach ($newObjectExpression->args as $argIndex => $e) {
1515 22
            $resultAlias = $this->scalarResultCounter++;
1516 22
            $columnAlias = $this->getSQLColumnAlias('sclr');
1517 22
            $fieldType   = 'string';
1518
1519
            switch (true) {
1520 22
                case ($e instanceof AST\NewObjectExpression):
1521
                    $sqlSelectExpressions[] = $e->dispatch($this);
1522
                    break;
1523
1524 22
                case ($e instanceof AST\Subselect):
1525 1
                    $sqlSelectExpressions[] = '(' . $e->dispatch($this) . ') AS ' . $columnAlias;
1526 1
                    break;
1527
1528 22
                case ($e instanceof AST\PathExpression):
1529 22
                    $dqlAlias  = $e->identificationVariable;
1530 22
                    $qComp     = $this->queryComponents[$dqlAlias];
1531 22
                    $class     = $qComp['metadata'];
1532 22
                    $fieldType = $class->fieldMappings[$e->field]['type'];
1533
1534 22
                    $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' . $columnAlias;
1535 22
                    break;
1536
1537 6
                case ($e instanceof AST\Literal):
1538 1
                    switch ($e->type) {
1539 1
                        case AST\Literal::BOOLEAN:
1540 1
                            $fieldType = 'boolean';
1541 1
                            break;
1542
1543 1
                        case AST\Literal::NUMERIC:
1544 1
                            $fieldType = is_float($e->value) ? 'float' : 'integer';
1545 1
                            break;
1546
                    }
1547
1548 1
                    $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' . $columnAlias;
1549 1
                    break;
1550
1551
                default:
1552 5
                    $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' . $columnAlias;
1553 5
                    break;
1554
            }
1555
1556 22
            $this->scalarResultAliasMap[$resultAlias] = $columnAlias;
1557 22
            $this->rsm->addScalarResult($columnAlias, $resultAlias, $fieldType);
1558
1559 22
            $this->rsm->newObjectMappings[$columnAlias] = [
1560 22
                'className' => $newObjectExpression->className,
1561 22
                'objIndex'  => $objIndex,
1562 22
                'argIndex'  => $argIndex
1563
            ];
1564
        }
1565
1566 22
        return implode(', ', $sqlSelectExpressions);
1567
    }
1568
1569
    /**
1570
     * {@inheritdoc}
1571
     */
1572 33
    public function walkSimpleSelectExpression($simpleSelectExpression)
1573
    {
1574 33
        $expr = $simpleSelectExpression->expression;
1575 33
        $sql  = ' ';
1576
1577
        switch (true) {
1578 33
            case ($expr instanceof AST\PathExpression):
1579 9
                $sql .= $this->walkPathExpression($expr);
1580 9
                break;
1581
1582 24
            case ($expr instanceof AST\AggregateExpression):
1583 14
                $alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
1584
1585 14
                $sql .= $this->walkAggregateExpression($expr) . ' AS dctrn__' . $alias;
1586 14
                break;
1587
1588 11
            case ($expr instanceof AST\Subselect):
1589
                $alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
1590
1591
                $columnAlias = 'sclr' . $this->aliasCounter++;
1592
                $this->scalarResultAliasMap[$alias] = $columnAlias;
1593
1594
                $sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias;
1595
                break;
1596
1597 11
            case ($expr instanceof AST\Functions\FunctionNode):
1598 11
            case ($expr instanceof AST\SimpleArithmeticExpression):
1599 10
            case ($expr instanceof AST\ArithmeticTerm):
1600 9
            case ($expr instanceof AST\ArithmeticFactor):
1601 9
            case ($expr instanceof AST\Literal):
1602 7
            case ($expr instanceof AST\NullIfExpression):
1603 7
            case ($expr instanceof AST\CoalesceExpression):
1604 7
            case ($expr instanceof AST\GeneralCaseExpression):
1605 5
            case ($expr instanceof AST\SimpleCaseExpression):
1606 8
                $alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
1607
1608 8
                $columnAlias = $this->getSQLColumnAlias('sclr');
1609 8
                $this->scalarResultAliasMap[$alias] = $columnAlias;
1610
1611 8
                $sql .= $expr->dispatch($this) . ' AS ' . $columnAlias;
1612 8
                break;
1613
1614 3
            case ($expr instanceof AST\ParenthesisExpression):
1615 1
                $sql .= $this->walkParenthesisExpression($expr);
1616 1
                break;
1617
1618
            default: // IdentificationVariable
1619 2
                $sql .= $this->walkEntityIdentificationVariable($expr);
1620 2
                break;
1621
        }
1622
1623 33
        return $sql;
1624
    }
1625
1626
    /**
1627
     * {@inheritdoc}
1628
     */
1629 75
    public function walkAggregateExpression($aggExpression)
1630
    {
1631 75
        return $aggExpression->functionName . '(' . ($aggExpression->isDistinct ? 'DISTINCT ' : '')
1632 75
            . $this->walkSimpleArithmeticExpression($aggExpression->pathExpression) . ')';
1633
    }
1634
1635
    /**
1636
     * {@inheritdoc}
1637
     */
1638 23
    public function walkGroupByClause($groupByClause)
1639
    {
1640 23
        $sqlParts = [];
1641
1642 23
        foreach ($groupByClause->groupByItems as $groupByItem) {
1643 23
            $sqlParts[] = $this->walkGroupByItem($groupByItem);
1644
        }
1645
1646 23
        return ' GROUP BY ' . implode(', ', $sqlParts);
1647
    }
1648
1649
    /**
1650
     * {@inheritdoc}
1651
     */
1652 23
    public function walkGroupByItem($groupByItem)
1653
    {
1654
        // StateFieldPathExpression
1655 23
        if ( ! is_string($groupByItem)) {
1656 10
            return $this->walkPathExpression($groupByItem);
1657
        }
1658
1659
        // ResultVariable
1660 13
        if (isset($this->queryComponents[$groupByItem]['resultVariable'])) {
1661 2
            $resultVariable = $this->queryComponents[$groupByItem]['resultVariable'];
1662
1663 2
            if ($resultVariable instanceof AST\PathExpression) {
1664 1
                return $this->walkPathExpression($resultVariable);
1665
            }
1666
1667 1
            if (isset($resultVariable->pathExpression)) {
1668
                return $this->walkPathExpression($resultVariable->pathExpression);
1669
            }
1670
1671 1
            return $this->walkResultVariable($groupByItem);
1672
        }
1673
1674
        // IdentificationVariable
1675 11
        $sqlParts = [];
1676
1677 11
        foreach ($this->queryComponents[$groupByItem]['metadata']->fieldNames as $field) {
1678 11
            $item       = new AST\PathExpression(AST\PathExpression::TYPE_STATE_FIELD, $groupByItem, $field);
1679 11
            $item->type = AST\PathExpression::TYPE_STATE_FIELD;
1680
1681 11
            $sqlParts[] = $this->walkPathExpression($item);
1682
        }
1683
1684 11
        foreach ($this->queryComponents[$groupByItem]['metadata']->associationMappings as $mapping) {
1685 11
            if ($mapping['isOwningSide'] && $mapping['type'] & ClassMetadataInfo::TO_ONE) {
1686 7
                $item       = new AST\PathExpression(AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $groupByItem, $mapping['fieldName']);
1687 7
                $item->type = AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION;
1688
1689 11
                $sqlParts[] = $this->walkPathExpression($item);
1690
            }
1691
        }
1692
1693 11
        return implode(', ', $sqlParts);
1694
    }
1695
1696
    /**
1697
     * {@inheritdoc}
1698
     */
1699 36
    public function walkDeleteClause(AST\DeleteClause $deleteClause)
1700
    {
1701 36
        $class     = $this->em->getClassMetadata($deleteClause->abstractSchemaName);
1702 36
        $tableName = $class->getTableName();
1703 36
        $sql       = 'DELETE FROM ' . $this->quoteStrategy->getTableName($class, $this->platform);
1704
1705 36
        $this->setSQLTableAlias($tableName, $tableName, $deleteClause->aliasIdentificationVariable);
1706 36
        $this->rootAliases[] = $deleteClause->aliasIdentificationVariable;
1707
1708 36
        return $sql;
1709
    }
1710
1711
    /**
1712
     * {@inheritdoc}
1713
     */
1714 25
    public function walkUpdateClause($updateClause)
1715
    {
1716 25
        $class     = $this->em->getClassMetadata($updateClause->abstractSchemaName);
1717 25
        $tableName = $class->getTableName();
1718 25
        $sql       = 'UPDATE ' . $this->quoteStrategy->getTableName($class, $this->platform);
1719
1720 25
        $this->setSQLTableAlias($tableName, $tableName, $updateClause->aliasIdentificationVariable);
1721 25
        $this->rootAliases[] = $updateClause->aliasIdentificationVariable;
1722
1723 25
        $sql .= ' SET ' . implode(', ', array_map([$this, 'walkUpdateItem'], $updateClause->updateItems));
1724
1725 25
        return $sql;
1726
    }
1727
1728
    /**
1729
     * {@inheritdoc}
1730
     */
1731 28
    public function walkUpdateItem($updateItem)
1732
    {
1733 28
        $useTableAliasesBefore = $this->useSqlTableAliases;
1734 28
        $this->useSqlTableAliases = false;
1735
1736 28
        $sql      = $this->walkPathExpression($updateItem->pathExpression) . ' = ';
1737 28
        $newValue = $updateItem->newValue;
1738
1739
        switch (true) {
1740 28
            case ($newValue instanceof AST\Node):
1741 27
                $sql .= $newValue->dispatch($this);
1742 27
                break;
1743
1744 1
            case ($newValue === null):
1745 1
                $sql .= 'NULL';
1746 1
                break;
1747
1748
            default:
1749
                $sql .= $this->conn->quote($newValue);
1750
                break;
1751
        }
1752
1753 28
        $this->useSqlTableAliases = $useTableAliasesBefore;
1754
1755 28
        return $sql;
1756
    }
1757
1758
    /**
1759
     * {@inheritdoc}
1760
     */
1761 674
    public function walkWhereClause($whereClause)
1762
    {
1763 674
        $condSql  = null !== $whereClause ? $this->walkConditionalExpression($whereClause->conditionalExpression) : '';
1764 671
        $discrSql = $this->_generateDiscriminatorColumnConditionSql($this->rootAliases);
1765
1766 671
        if ($this->em->hasFilters()) {
1767 43
            $filterClauses = [];
1768 43
            foreach ($this->rootAliases as $dqlAlias) {
1769 43
                $class = $this->queryComponents[$dqlAlias]['metadata'];
1770 43
                $tableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
1771
1772 43
                if ($filterExpr = $this->generateFilterConditionSQL($class, $tableAlias)) {
1773 43
                    $filterClauses[] = $filterExpr;
1774
                }
1775
            }
1776
1777 43
            if (count($filterClauses)) {
1778 6
                if ($condSql) {
1779 2
                    $condSql = '(' . $condSql . ') AND ';
1780
                }
1781
1782 6
                $condSql .= implode(' AND ', $filterClauses);
1783
            }
1784
        }
1785
1786 671
        if ($condSql) {
1787 330
            return ' WHERE ' . (( ! $discrSql) ? $condSql : '(' . $condSql . ') AND ' . $discrSql);
1788
        }
1789
1790 417
        if ($discrSql) {
1791 22
            return ' WHERE ' . $discrSql;
1792
        }
1793
1794 397
        return '';
1795
    }
1796
1797
    /**
1798
     * {@inheritdoc}
1799
     */
1800 360
    public function walkConditionalExpression($condExpr)
1801
    {
1802
        // Phase 2 AST optimization: Skip processing of ConditionalExpression
1803
        // if only one ConditionalTerm is defined
1804 360
        if ( ! ($condExpr instanceof AST\ConditionalExpression)) {
1805 305
            return $this->walkConditionalTerm($condExpr);
1806
        }
1807
1808 72
        return implode(' OR ', array_map([$this, 'walkConditionalTerm'], $condExpr->conditionalTerms));
1809
    }
1810
1811
    /**
1812
     * {@inheritdoc}
1813
     */
1814 360
    public function walkConditionalTerm($condTerm)
1815
    {
1816
        // Phase 2 AST optimization: Skip processing of ConditionalTerm
1817
        // if only one ConditionalFactor is defined
1818 360
        if ( ! ($condTerm instanceof AST\ConditionalTerm)) {
1819 294
            return $this->walkConditionalFactor($condTerm);
1820
        }
1821
1822 89
        return implode(' AND ', array_map([$this, 'walkConditionalFactor'], $condTerm->conditionalFactors));
1823
    }
1824
1825
    /**
1826
     * {@inheritdoc}
1827
     */
1828 360
    public function walkConditionalFactor($factor)
1829
    {
1830
        // Phase 2 AST optimization: Skip processing of ConditionalFactor
1831
        // if only one ConditionalPrimary is defined
1832 360
        return ( ! ($factor instanceof AST\ConditionalFactor))
1833 357
            ? $this->walkConditionalPrimary($factor)
1834 357
            : ($factor->not ? 'NOT ' : '') . $this->walkConditionalPrimary($factor->conditionalPrimary);
1835
    }
1836
1837
    /**
1838
     * {@inheritdoc}
1839
     */
1840 360
    public function walkConditionalPrimary($primary)
1841
    {
1842 360
        if ($primary->isSimpleConditionalExpression()) {
1843 360
            return $primary->simpleConditionalExpression->dispatch($this);
1844
        }
1845
1846 25
        if ($primary->isConditionalExpression()) {
1847 25
            $condExpr = $primary->conditionalExpression;
1848
1849 25
            return '(' . $this->walkConditionalExpression($condExpr) . ')';
1850
        }
1851
    }
1852
1853
    /**
1854
     * {@inheritdoc}
1855
     */
1856 5
    public function walkExistsExpression($existsExpr)
1857
    {
1858 5
        $sql = ($existsExpr->not) ? 'NOT ' : '';
1859
1860 5
        $sql .= 'EXISTS (' . $this->walkSubselect($existsExpr->subselect) . ')';
1861
1862 5
        return $sql;
1863
    }
1864
1865
    /**
1866
     * {@inheritdoc}
1867
     */
1868 6
    public function walkCollectionMemberExpression($collMemberExpr)
1869
    {
1870 6
        $sql = $collMemberExpr->not ? 'NOT ' : '';
1871 6
        $sql .= 'EXISTS (SELECT 1 FROM ';
1872
1873 6
        $entityExpr   = $collMemberExpr->entityExpression;
1874 6
        $collPathExpr = $collMemberExpr->collectionValuedPathExpression;
1875
1876 6
        $fieldName = $collPathExpr->field;
1877 6
        $dqlAlias  = $collPathExpr->identificationVariable;
1878
1879 6
        $class = $this->queryComponents[$dqlAlias]['metadata'];
1880
1881
        switch (true) {
1882
            // InputParameter
1883 6
            case ($entityExpr instanceof AST\InputParameter):
1884 4
                $dqlParamKey = $entityExpr->name;
1885 4
                $entitySql   = '?';
1886 4
                break;
1887
1888
            // SingleValuedAssociationPathExpression | IdentificationVariable
1889 2
            case ($entityExpr instanceof AST\PathExpression):
1890 2
                $entitySql = $this->walkPathExpression($entityExpr);
1891 2
                break;
1892
1893
            default:
1894
                throw new \BadMethodCallException("Not implemented");
1895
        }
1896
1897 6
        $assoc = $class->associationMappings[$fieldName];
1898
1899 6
        if ($assoc['type'] == ClassMetadata::ONE_TO_MANY) {
1900 1
            $targetClass      = $this->em->getClassMetadata($assoc['targetEntity']);
1901 1
            $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName());
1902 1
            $sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
1903
1904 1
            $sql .= $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' ' . $targetTableAlias . ' WHERE ';
1905
1906 1
            $owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']];
1907 1
            $sqlParts    = [];
1908
1909 1
            foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) {
1910 1
                $targetColumn = $this->quoteStrategy->getColumnName($class->fieldNames[$targetColumn], $class, $this->platform);
1911
1912 1
                $sqlParts[] = $sourceTableAlias . '.' . $targetColumn . ' = ' . $targetTableAlias . '.' . $sourceColumn;
1913
            }
1914
1915 1
            foreach ($this->quoteStrategy->getIdentifierColumnNames($targetClass, $this->platform) as $targetColumnName) {
1916 1
                if (isset($dqlParamKey)) {
1917 1
                    $this->parserResult->addParameterMapping($dqlParamKey, $this->sqlParamIndex++);
1918
                }
1919
1920 1
                $sqlParts[] = $targetTableAlias . '.'  . $targetColumnName . ' = ' . $entitySql;
1921
            }
1922
1923 1
            $sql .= implode(' AND ', $sqlParts);
1924
        } else { // many-to-many
1925 5
            $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
1926
1927 5
            $owningAssoc = $assoc['isOwningSide'] ? $assoc : $targetClass->associationMappings[$assoc['mappedBy']];
1928 5
            $joinTable = $owningAssoc['joinTable'];
1929
1930
            // SQL table aliases
1931 5
            $joinTableAlias   = $this->getSQLTableAlias($joinTable['name']);
1932 5
            $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName());
1933 5
            $sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
1934
1935
            // join to target table
1936 5
            $sql .= $this->quoteStrategy->getJoinTableName($owningAssoc, $targetClass, $this->platform) . ' ' . $joinTableAlias
1937 5
                . ' INNER JOIN ' . $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' ' . $targetTableAlias . ' ON ';
1938
1939
            // join conditions
1940 5
            $joinColumns  = $assoc['isOwningSide'] ? $joinTable['inverseJoinColumns'] : $joinTable['joinColumns'];
1941 5
            $joinSqlParts = [];
1942
1943 5
            foreach ($joinColumns as $joinColumn) {
1944 5
                $targetColumn = $this->quoteStrategy->getColumnName($targetClass->fieldNames[$joinColumn['referencedColumnName']], $targetClass, $this->platform);
1945
1946 5
                $joinSqlParts[] = $joinTableAlias . '.' . $joinColumn['name'] . ' = ' . $targetTableAlias . '.' . $targetColumn;
1947
            }
1948
1949 5
            $sql .= implode(' AND ', $joinSqlParts);
1950 5
            $sql .= ' WHERE ';
1951
1952 5
            $joinColumns = $assoc['isOwningSide'] ? $joinTable['joinColumns'] : $joinTable['inverseJoinColumns'];
1953 5
            $sqlParts    = [];
1954
1955 5
            foreach ($joinColumns as $joinColumn) {
1956 5
                $targetColumn = $this->quoteStrategy->getColumnName($class->fieldNames[$joinColumn['referencedColumnName']], $class, $this->platform);
1957
1958 5
                $sqlParts[] = $joinTableAlias . '.' . $joinColumn['name'] . ' = ' . $sourceTableAlias . '.' . $targetColumn;
1959
            }
1960
1961 5
            foreach ($this->quoteStrategy->getIdentifierColumnNames($targetClass, $this->platform) as $targetColumnName) {
1962 5
                if (isset($dqlParamKey)) {
1963 3
                    $this->parserResult->addParameterMapping($dqlParamKey, $this->sqlParamIndex++);
1964
                }
1965
1966 5
                $sqlParts[] = $targetTableAlias . '.' . $targetColumnName . ' IN (' . $entitySql . ')';
1967
            }
1968
1969 5
            $sql .= implode(' AND ', $sqlParts);
1970
        }
1971
1972 6
        return $sql . ')';
1973
    }
1974
1975
    /**
1976
     * {@inheritdoc}
1977
     */
1978 3
    public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr)
1979
    {
1980 3
        $sizeFunc = new AST\Functions\SizeFunction('size');
1981 3
        $sizeFunc->collectionPathExpression = $emptyCollCompExpr->expression;
1982
1983 3
        return $sizeFunc->getSql($this) . ($emptyCollCompExpr->not ? ' > 0' : ' = 0');
1984
    }
1985
1986
    /**
1987
     * {@inheritdoc}
1988
     */
1989 11
    public function walkNullComparisonExpression($nullCompExpr)
1990
    {
1991 11
        $expression = $nullCompExpr->expression;
1992 11
        $comparison = ' IS' . ($nullCompExpr->not ? ' NOT' : '') . ' NULL';
1993
1994
        // Handle ResultVariable
1995 11
        if (is_string($expression) && isset($this->queryComponents[$expression]['resultVariable'])) {
1996 2
            return $this->walkResultVariable($expression) . $comparison;
1997
        }
1998
1999
        // Handle InputParameter mapping inclusion to ParserResult
2000 9
        if ($expression instanceof AST\InputParameter) {
2001
            return $this->walkInputParameter($expression) . $comparison;
2002
        }
2003
2004 9
        return $expression->dispatch($this) . $comparison;
2005
    }
2006
2007
    /**
2008
     * {@inheritdoc}
2009
     */
2010 85
    public function walkInExpression($inExpr)
2011
    {
2012 85
        $sql = $this->walkArithmeticExpression($inExpr->expression) . ($inExpr->not ? ' NOT' : '') . ' IN (';
2013
2014 84
        $sql .= ($inExpr->subselect)
2015 7
            ? $this->walkSubselect($inExpr->subselect)
2016 84
            : implode(', ', array_map([$this, 'walkInParameter'], $inExpr->literals));
2017
2018 84
        $sql .= ')';
2019
2020 84
        return $sql;
2021
    }
2022
2023
    /**
2024
     * {@inheritdoc}
2025
     */
2026 9
    public function walkInstanceOfExpression($instanceOfExpr)
2027
    {
2028 9
        $sql = '';
2029
2030 9
        $dqlAlias = $instanceOfExpr->identificationVariable;
2031 9
        $discrClass = $class = $this->queryComponents[$dqlAlias]['metadata'];
2032
2033 9
        if ($class->discriminatorColumn) {
2034 9
            $discrClass = $this->em->getClassMetadata($class->rootEntityName);
2035
        }
2036
2037 9
        if ($this->useSqlTableAliases) {
2038 9
            $sql .= $this->getSQLTableAlias($discrClass->getTableName(), $dqlAlias) . '.';
2039
        }
2040
2041 9
        $sql .= $class->discriminatorColumn['name'] . ($instanceOfExpr->not ? ' NOT IN ' : ' IN ');
2042
2043 9
        $sqlParameterList = [];
2044
2045 9
        foreach ($instanceOfExpr->value as $parameter) {
2046 9
            if ($parameter instanceof AST\InputParameter) {
2047 3
                $this->rsm->addMetadataParameterMapping($parameter->name, 'discriminatorValue');
2048
2049 3
                $sqlParameterList[] = $this->walkInputParameter($parameter);
2050
2051 3
                continue;
2052
            }
2053
2054
            // Get name from ClassMetadata to resolve aliases.
2055 6
            $entityClassName    = $this->em->getClassMetadata($parameter)->name;
2056 6
            $discriminatorValue = $class->discriminatorValue;
2057
2058 6
            if ($entityClassName !== $class->name) {
2059 5
                $discrMap = array_flip($class->discriminatorMap);
2060
2061 5
                if ( ! isset($discrMap[$entityClassName])) {
2062 1
                    throw QueryException::instanceOfUnrelatedClass($entityClassName, $class->rootEntityName);
2063
                }
2064
2065 4
                $discriminatorValue = $discrMap[$entityClassName];
2066
            }
2067
2068 5
            $sqlParameterList[] = $this->conn->quote($discriminatorValue);
2069
        }
2070
2071 8
        $sql .= '(' . implode(', ', $sqlParameterList) . ')';
2072
2073 8
        return $sql;
2074
    }
2075
2076
    /**
2077
     * {@inheritdoc}
2078
     */
2079 77
    public function walkInParameter($inParam)
2080
    {
2081 77
        return $inParam instanceof AST\InputParameter
2082 68
            ? $this->walkInputParameter($inParam)
2083 77
            : $this->walkLiteral($inParam);
2084
    }
2085
2086
    /**
2087
     * {@inheritdoc}
2088
     */
2089 148
    public function walkLiteral($literal)
2090
    {
2091 148
        switch ($literal->type) {
2092 148
            case AST\Literal::STRING:
2093 50
                return $this->conn->quote($literal->value);
2094
2095 111
            case AST\Literal::BOOLEAN:
2096 8
                return $this->conn->getDatabasePlatform()->convertBooleans('true' === strtolower($literal->value));
2097
2098 104
            case AST\Literal::NUMERIC:
2099 104
                return $literal->value;
2100
2101
            default:
2102
                throw QueryException::invalidLiteral($literal);
2103
        }
2104
    }
2105
2106
    /**
2107
     * {@inheritdoc}
2108
     */
2109 6
    public function walkBetweenExpression($betweenExpr)
2110
    {
2111 6
        $sql = $this->walkArithmeticExpression($betweenExpr->expression);
2112
2113 6
        if ($betweenExpr->not) {
2114 2
            $sql .= ' NOT';
2115
        }
2116
2117 6
        $sql .= ' BETWEEN ' . $this->walkArithmeticExpression($betweenExpr->leftBetweenExpression)
2118 6
            . ' AND ' . $this->walkArithmeticExpression($betweenExpr->rightBetweenExpression);
2119
2120 6
        return $sql;
2121
    }
2122
2123
    /**
2124
     * {@inheritdoc}
2125
     */
2126 9
    public function walkLikeExpression($likeExpr)
2127
    {
2128 9
        $stringExpr = $likeExpr->stringExpression;
2129 9
        $leftExpr   = (is_string($stringExpr) && isset($this->queryComponents[$stringExpr]['resultVariable']))
2130 1
            ? $this->walkResultVariable($stringExpr)
2131 9
            : $stringExpr->dispatch($this);
2132
2133 9
        $sql = $leftExpr . ($likeExpr->not ? ' NOT' : '') . ' LIKE ';
2134
2135 9
        if ($likeExpr->stringPattern instanceof AST\InputParameter) {
2136 4
            $sql .= $this->walkInputParameter($likeExpr->stringPattern);
2137 6
        } elseif ($likeExpr->stringPattern instanceof AST\Functions\FunctionNode) {
2138 2
            $sql .= $this->walkFunction($likeExpr->stringPattern);
2139 6
        } elseif ($likeExpr->stringPattern instanceof AST\PathExpression) {
2140 2
            $sql .= $this->walkPathExpression($likeExpr->stringPattern);
2141
        } else {
2142 6
            $sql .= $this->walkLiteral($likeExpr->stringPattern);
2143
        }
2144
2145 9
        if ($likeExpr->escapeChar) {
2146 1
            $sql .= ' ESCAPE ' . $this->walkLiteral($likeExpr->escapeChar);
2147
        }
2148
2149 9
        return $sql;
2150
    }
2151
2152
    /**
2153
     * {@inheritdoc}
2154
     */
2155 5
    public function walkStateFieldPathExpression($stateFieldPathExpression)
2156
    {
2157 5
        return $this->walkPathExpression($stateFieldPathExpression);
2158
    }
2159
2160
    /**
2161
     * {@inheritdoc}
2162
     */
2163 257
    public function walkComparisonExpression($compExpr)
2164
    {
2165 257
        $leftExpr  = $compExpr->leftExpression;
2166 257
        $rightExpr = $compExpr->rightExpression;
2167 257
        $sql       = '';
2168
2169 257
        $sql .= ($leftExpr instanceof AST\Node)
2170 257
            ? $leftExpr->dispatch($this)
2171 256
            : (is_numeric($leftExpr) ? $leftExpr : $this->conn->quote($leftExpr));
2172
2173 256
        $sql .= ' ' . $compExpr->operator . ' ';
2174
2175 256
        $sql .= ($rightExpr instanceof AST\Node)
2176 254
            ? $rightExpr->dispatch($this)
2177 256
            : (is_numeric($rightExpr) ? $rightExpr : $this->conn->quote($rightExpr));
2178
2179 256
        return $sql;
2180
    }
2181
2182
    /**
2183
     * {@inheritdoc}
2184
     */
2185 212
    public function walkInputParameter($inputParam)
2186
    {
2187 212
        $this->parserResult->addParameterMapping($inputParam->name, $this->sqlParamIndex++);
2188
2189 212
        $parameter = $this->query->getParameter($inputParam->name);
2190
2191 212
        if ($parameter && Type::hasType($type = $parameter->getType())) {
2192 53
            return Type::getType($type)->convertToDatabaseValueSQL('?', $this->platform);
2193
        }
2194
2195 164
        return '?';
2196
    }
2197
2198
    /**
2199
     * {@inheritdoc}
2200
     */
2201 323
    public function walkArithmeticExpression($arithmeticExpr)
2202
    {
2203 323
        return ($arithmeticExpr->isSimpleArithmeticExpression())
2204 323
            ? $this->walkSimpleArithmeticExpression($arithmeticExpr->simpleArithmeticExpression)
2205 321
            : '(' . $this->walkSubselect($arithmeticExpr->subselect) . ')';
2206
    }
2207
2208
    /**
2209
     * {@inheritdoc}
2210
     */
2211 385
    public function walkSimpleArithmeticExpression($simpleArithmeticExpr)
2212
    {
2213 385
        if ( ! ($simpleArithmeticExpr instanceof AST\SimpleArithmeticExpression)) {
2214 335
            return $this->walkArithmeticTerm($simpleArithmeticExpr);
2215
        }
2216
2217 75
        return implode(' ', array_map([$this, 'walkArithmeticTerm'], $simpleArithmeticExpr->arithmeticTerms));
2218
    }
2219
2220
    /**
2221
     * {@inheritdoc}
2222
     */
2223 406
    public function walkArithmeticTerm($term)
2224
    {
2225 406
        if (is_string($term)) {
2226 21
            return (isset($this->queryComponents[$term]))
2227 6
                ? $this->walkResultVariable($this->queryComponents[$term]['token']['value'])
2228 21
                : $term;
2229
        }
2230
2231
        // Phase 2 AST optimization: Skip processing of ArithmeticTerm
2232
        // if only one ArithmeticFactor is defined
2233 405
        if ( ! ($term instanceof AST\ArithmeticTerm)) {
2234 383
            return $this->walkArithmeticFactor($term);
2235
        }
2236
2237 47
        return implode(' ', array_map([$this, 'walkArithmeticFactor'], $term->arithmeticFactors));
2238
    }
2239
2240
    /**
2241
     * {@inheritdoc}
2242
     */
2243 406
    public function walkArithmeticFactor($factor)
2244
    {
2245 406
        if (is_string($factor)) {
2246 47
            return (isset($this->queryComponents[$factor]))
2247 2
                ? $this->walkResultVariable($this->queryComponents[$factor]['token']['value'])
2248 47
                : $factor;
2249
        }
2250
2251
        // Phase 2 AST optimization: Skip processing of ArithmeticFactor
2252
        // if only one ArithmeticPrimary is defined
2253 406
        if ( ! ($factor instanceof AST\ArithmeticFactor)) {
2254 405
            return $this->walkArithmeticPrimary($factor);
2255
        }
2256
2257 3
        $sign = $factor->isNegativeSigned() ? '-' : ($factor->isPositiveSigned() ? '+' : '');
2258
2259 3
        return $sign . $this->walkArithmeticPrimary($factor->arithmeticPrimary);
2260
    }
2261
2262
    /**
2263
     * Walks down an ArithmeticPrimary that represents an AST node, thereby generating the appropriate SQL.
2264
     *
2265
     * @param mixed $primary
2266
     *
2267
     * @return string The SQL.
2268
     */
2269 406
    public function walkArithmeticPrimary($primary)
2270
    {
2271 406
        if ($primary instanceof AST\SimpleArithmeticExpression) {
2272
            return '(' . $this->walkSimpleArithmeticExpression($primary) . ')';
2273
        }
2274
2275 406
        if ($primary instanceof AST\Node) {
2276 406
            return $primary->dispatch($this);
2277
        }
2278
2279
        return $this->walkEntityIdentificationVariable($primary);
2280
    }
2281
2282
    /**
2283
     * {@inheritdoc}
2284
     */
2285 18
    public function walkStringPrimary($stringPrimary)
2286
    {
2287 18
        return (is_string($stringPrimary))
2288
            ? $this->conn->quote($stringPrimary)
2289 18
            : $stringPrimary->dispatch($this);
2290
    }
2291
2292
    /**
2293
     * {@inheritdoc}
2294
     */
2295 30
    public function walkResultVariable($resultVariable)
2296
    {
2297 30
        $resultAlias = $this->scalarResultAliasMap[$resultVariable];
2298
2299 30
        if (is_array($resultAlias)) {
2300 1
            return implode(', ', $resultAlias);
2301
        }
2302
2303 29
        return $resultAlias;
2304
    }
2305
}
2306