Failed Conditions
Push — master ( 6744b4...2b8acb )
by Marco
60:45 queued 60:36
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 691
    public function __construct($query, $parserResult, array $queryComponents)
182
    {
183 691
        $this->query            = $query;
184 691
        $this->parserResult     = $parserResult;
185 691
        $this->queryComponents  = $queryComponents;
186 691
        $this->rsm              = $parserResult->getResultSetMapping();
187 691
        $this->em               = $query->getEntityManager();
188 691
        $this->conn             = $this->em->getConnection();
189 691
        $this->platform         = $this->conn->getDatabasePlatform();
190 691
        $this->quoteStrategy    = $this->em->getConfiguration()->getQuoteStrategy();
191 691
    }
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 22
    public function getEntityManager()
219
    {
220 22
        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 View Code Duplication
    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 685
    public function getExecutor($AST)
261
    {
262
        switch (true) {
263 685
            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 651
            case ($AST instanceof AST\UpdateStatement):
271 29
                $primaryClass = $this->em->getClassMetadata($AST->updateClause->abstractSchemaName);
272
273 29
                return ($primaryClass->isInheritanceTypeJoined())
274 4
                    ? new Exec\MultiTableUpdateExecutor($AST, $this)
275 29
                    : new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
276
277
            default:
278 626
                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 637
    public function getSQLTableAlias($tableName, $dqlAlias = '')
291
    {
292 637
        $tableName .= ($dqlAlias) ? '@[' . $dqlAlias . ']' : '';
293
294 637
        if ( ! isset($this->tableAliasMap[$tableName])) {
295 637
            $this->tableAliasMap[$tableName] = (preg_match('/[a-z]/i', $tableName[0]) ? strtolower($tableName[0]) : 't')
296 637
                . $this->tableAliasCounter++ . '_';
297
        }
298
299 637
        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 65
    public function setSQLTableAlias($tableName, $alias, $dqlAlias = '')
313
    {
314 65
        $tableName .= ($dqlAlias) ? '@[' . $dqlAlias . ']' : '';
315
316 65
        $this->tableAliasMap[$tableName] = $alias;
317
318 65
        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 626
    public function getSQLColumnAlias($columnName)
329
    {
330 626
        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 93
    private function _generateClassTableInheritanceJoins($class, $dqlAlias)
343
    {
344 93
        $sql = '';
345
346 93
        $baseTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
347
348
        // INNER JOIN parent class tables
349 93
        foreach ($class->parentClasses as $parentClassName) {
350 66
            $parentClass = $this->em->getClassMetadata($parentClassName);
351 66
            $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 66
            $sql .= isset($this->queryComponents[$dqlAlias]['relation']) ? ' LEFT ' : ' INNER ';
355 66
            $sql .= 'JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->platform) . ' ' . $tableAlias . ' ON ';
356
357 66
            $sqlParts = [];
358
359 66 View Code Duplication
            foreach ($this->quoteStrategy->getIdentifierColumnNames($class, $this->platform) as $columnName) {
360 66
                $sqlParts[] = $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
361
            }
362
363
            // Add filters on the root class
364 66
            if ($filterSql = $this->generateFilterConditionSQL($parentClass, $tableAlias)) {
365 1
                $sqlParts[] = $filterSql;
366
            }
367
368 66
            $sql .= implode(' AND ', $sqlParts);
369
        }
370
371
        // Ignore subclassing inclusion if partial objects is disallowed
372 93
        if ($this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {
373 21
            return $sql;
374
        }
375
376
        // LEFT JOIN child class tables
377 72
        foreach ($class->subClasses as $subClassName) {
378 33
            $subClass   = $this->em->getClassMetadata($subClassName);
379 33
            $tableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
380
381 33
            $sql .= ' LEFT JOIN ' . $this->quoteStrategy->getTableName($subClass, $this->platform) . ' ' . $tableAlias . ' ON ';
382
383 33
            $sqlParts = [];
384
385 33 View Code Duplication
            foreach ($this->quoteStrategy->getIdentifierColumnNames($subClass, $this->platform) as $columnName) {
386 33
                $sqlParts[] = $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
387
            }
388
389 33
            $sql .= implode(' AND ', $sqlParts);
390
        }
391
392 72
        return $sql;
393
    }
394
395
    /**
396
     * @return string
397
     */
398 620
    private function _generateOrderedCollectionOrderByItems()
399
    {
400 620
        $orderedColumns = [];
401
402 620
        foreach ($this->selectedClasses as $selectedClass) {
403 482
            $dqlAlias  = $selectedClass['dqlAlias'];
404 482
            $qComp     = $this->queryComponents[$dqlAlias];
405
406 482
            if ( ! isset($qComp['relation']['orderBy'])) {
407 482
                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 620
        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 680
    private function _generateDiscriminatorColumnConditionSQL(array $dqlAliases)
441
    {
442 680
        $sqlParts = [];
443
444 680
        foreach ($dqlAliases as $dqlAlias) {
445 680
            $class = $this->queryComponents[$dqlAlias]['metadata'];
446
447 680
            if ( ! $class->isInheritanceTypeSingleTable()) continue;
448
449 40
            $conn   = $this->em->getConnection();
450 40
            $values = [];
451
452 40
            if ($class->discriminatorValue !== null) { // discriminators can be 0
453 21
                $values[] = $conn->quote($class->discriminatorValue);
454
            }
455
456 40
            foreach ($class->subClasses as $subclassName) {
457 29
                $values[] = $conn->quote($this->em->getClassMetadata($subclassName)->discriminatorValue);
458
            }
459
460 40
            $sqlTableAlias = ($this->useSqlTableAliases)
461 35
                ? $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.'
462 40
                : '';
463
464 40
            $sqlParts[] = $sqlTableAlias . $class->discriminatorColumn['name'] . ' IN (' . implode(', ', $values) . ')';
465
        }
466
467 680
        $sql = implode(' AND ', $sqlParts);
468
469 680
        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 320
    private function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias)
481
    {
482 320
        if (!$this->em->hasFilters()) {
483 282
            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 626
    public function walkSelectStatement(AST\SelectStatement $AST)
520
    {
521 626
        $limit    = $this->query->getMaxResults();
522 626
        $offset   = $this->query->getFirstResult();
523 626
        $lockMode = $this->query->getHint(Query::HINT_LOCK_MODE);
524 626
        $sql      = $this->walkSelectClause($AST->selectClause)
525 626
            . $this->walkFromClause($AST->fromClause)
526 624
            . $this->walkWhereClause($AST->whereClause);
527
528 621
        if ($AST->groupByClause) {
529 24
            $sql .= $this->walkGroupByClause($AST->groupByClause);
530
        }
531
532 621
        if ($AST->havingClause) {
533 14
            $sql .= $this->walkHavingClause($AST->havingClause);
534
        }
535
536 621
        if ($AST->orderByClause) {
537 142
            $sql .= $this->walkOrderByClause($AST->orderByClause);
538
        }
539
540 620
        if ( ! $AST->orderByClause && ($orderBySql = $this->_generateOrderedCollectionOrderByItems())) {
541 6
            $sql .= ' ORDER BY ' . $orderBySql;
542
        }
543
544 620 View Code Duplication
        if ($limit !== null || $offset !== null) {
545 39
            $sql = $this->platform->modifyLimitQuery($sql, $limit, $offset);
546
        }
547
548 620
        if ($lockMode === null || $lockMode === false || $lockMode === LockMode::NONE) {
549 615
            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 425
    public function walkIdentificationVariable($identificationVariable, $fieldName = null)
627
    {
628 425
        $class = $this->queryComponents[$identificationVariable]['metadata'];
629
630
        if (
631 425
            $fieldName !== null && $class->isInheritanceTypeJoined() &&
632 425
            isset($class->fieldMappings[$fieldName]['inherited'])
633
        ) {
634 37
            $class = $this->em->getClassMetadata($class->fieldMappings[$fieldName]['inherited']);
635
        }
636
637 425
        return $this->getSQLTableAlias($class->getTableName(), $identificationVariable);
638
    }
639
640
    /**
641
     * {@inheritdoc}
642
     */
643 494
    public function walkPathExpression($pathExpr)
644
    {
645 494
        $sql = '';
646
647
        /* @var $pathExpr Query\AST\PathExpression */
648 494
        switch ($pathExpr->type) {
649 494
            case AST\PathExpression::TYPE_STATE_FIELD:
650 473
                $fieldName = $pathExpr->field;
651 473
                $dqlAlias = $pathExpr->identificationVariable;
652 473
                $class = $this->queryComponents[$dqlAlias]['metadata'];
653
654 473
                if ($this->useSqlTableAliases) {
655 425
                    $sql .= $this->walkIdentificationVariable($dqlAlias, $fieldName) . '.';
656
                }
657
658 473
                $sql .= $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform);
659 473
                break;
660
661 62
            case AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION:
662
                // 1- the owning side:
663
                //    Just use the foreign key, i.e. u.group_id
664 62
                $fieldName = $pathExpr->field;
665 62
                $dqlAlias = $pathExpr->identificationVariable;
666 62
                $class = $this->queryComponents[$dqlAlias]['metadata'];
667
668 62
                if (isset($class->associationMappings[$fieldName]['inherited'])) {
669 2
                    $class = $this->em->getClassMetadata($class->associationMappings[$fieldName]['inherited']);
670
                }
671
672 62
                $assoc = $class->associationMappings[$fieldName];
673
674 62
                if ( ! $assoc['isOwningSide']) {
675 2
                    throw QueryException::associationPathInverseSideNotSupported($pathExpr);
676
                }
677
678
                // COMPOSITE KEYS NOT (YET?) SUPPORTED
679 60
                if (count($assoc['sourceToTargetKeyColumns']) > 1) {
680 1
                    throw QueryException::associationPathCompositeKeyNotSupported();
681
                }
682
683 59
                if ($this->useSqlTableAliases) {
684 56
                    $sql .= $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.';
685
                }
686
687 59
                $sql .= reset($assoc['targetToSourceKeyColumns']);
688 59
                break;
689
690
            default:
691
                throw QueryException::invalidPathExpression($pathExpr);
692
        }
693
694 491
        return $sql;
695
    }
696
697
    /**
698
     * {@inheritdoc}
699
     */
700 626
    public function walkSelectClause($selectClause)
701
    {
702 626
        $sql = 'SELECT ' . (($selectClause->isDistinct) ? 'DISTINCT ' : '');
703 626
        $sqlSelectExpressions = array_filter(array_map([$this, 'walkSelectExpression'], $selectClause->selectExpressions));
704
705 626
        if ($this->query->getHint(Query::HINT_INTERNAL_ITERATION) == true && $selectClause->isDistinct) {
706 1
            $this->query->setHint(self::HINT_DISTINCT, true);
707
        }
708
709 626
        $addMetaColumns = ! $this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) &&
710 463
            $this->query->getHydrationMode() == Query::HYDRATE_OBJECT
711
            ||
712 290
            $this->query->getHydrationMode() != Query::HYDRATE_OBJECT &&
713 626
            $this->query->getHint(Query::HINT_INCLUDE_META_COLUMNS);
714
715 626
        foreach ($this->selectedClasses as $selectedClass) {
716 488
            $class       = $selectedClass['class'];
717 488
            $dqlAlias    = $selectedClass['dqlAlias'];
718 488
            $resultAlias = $selectedClass['resultAlias'];
719
720
            // Register as entity or joined entity result
721 488
            if ($this->queryComponents[$dqlAlias]['relation'] === null) {
722 488
                $this->rsm->addEntityResult($class->name, $dqlAlias, $resultAlias);
723
            } else {
724 158
                $this->rsm->addJoinedEntityResult(
725 158
                    $class->name,
726 158
                    $dqlAlias,
727 158
                    $this->queryComponents[$dqlAlias]['parent'],
728 158
                    $this->queryComponents[$dqlAlias]['relation']['fieldName']
729
                );
730
            }
731
732 488
            if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) {
733
                // Add discriminator columns to SQL
734 91
                $rootClass   = $this->em->getClassMetadata($class->rootEntityName);
735 91
                $tblAlias    = $this->getSQLTableAlias($rootClass->getTableName(), $dqlAlias);
736 91
                $discrColumn = $rootClass->discriminatorColumn;
737 91
                $columnAlias = $this->getSQLColumnAlias($discrColumn['name']);
738
739 91
                $sqlSelectExpressions[] = $tblAlias . '.' . $discrColumn['name'] . ' AS ' . $columnAlias;
740
741 91
                $this->rsm->setDiscriminatorColumn($dqlAlias, $columnAlias);
742 91
                $this->rsm->addMetaResult($dqlAlias, $columnAlias, $discrColumn['fieldName'], false, $discrColumn['type']);
743
            }
744
745
            // Add foreign key columns to SQL, if necessary
746 488
            if ( ! $addMetaColumns && ! $class->containsForeignIdentifier) {
747 181
                continue;
748
            }
749
750
            // Add foreign key columns of class and also parent classes
751 357
            foreach ($class->associationMappings as $assoc) {
752 318
                if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) {
753 267
                    continue;
754 285
                } else if ( !$addMetaColumns && !isset($assoc['id'])) {
755
                    continue;
756
                }
757
758 285
                $targetClass   = $this->em->getClassMetadata($assoc['targetEntity']);
759 285
                $isIdentifier  = (isset($assoc['id']) && $assoc['id'] === true);
760 285
                $owningClass   = (isset($assoc['inherited'])) ? $this->em->getClassMetadata($assoc['inherited']) : $class;
761 285
                $sqlTableAlias = $this->getSQLTableAlias($owningClass->getTableName(), $dqlAlias);
762
763 285 View Code Duplication
                foreach ($assoc['joinColumns'] as $joinColumn) {
764 285
                    $columnName  = $joinColumn['name'];
765 285
                    $columnAlias = $this->getSQLColumnAlias($columnName);
766 285
                    $columnType  = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em);
767
768 285
                    $quotedColumnName       = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
769 285
                    $sqlSelectExpressions[] = $sqlTableAlias . '.' . $quotedColumnName . ' AS ' . $columnAlias;
770
771 285
                    $this->rsm->addMetaResult($dqlAlias, $columnAlias, $columnName, $isIdentifier, $columnType);
772
                }
773
            }
774
775
            // Add foreign key columns to SQL, if necessary
776 357
            if ( ! $addMetaColumns) {
777 8
                continue;
778
            }
779
780
            // Add foreign key columns of subclasses
781 352
            foreach ($class->subClasses as $subClassName) {
782 31
                $subClass      = $this->em->getClassMetadata($subClassName);
783 31
                $sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
784
785 31
                foreach ($subClass->associationMappings as $assoc) {
786
                    // Skip if association is inherited
787 27
                    if (isset($assoc['inherited'])) continue;
788
789 16
                    if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
790 14
                        $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
791
792 14 View Code Duplication
                        foreach ($assoc['joinColumns'] as $joinColumn) {
793 14
                            $columnName  = $joinColumn['name'];
794 14
                            $columnAlias = $this->getSQLColumnAlias($columnName);
795 14
                            $columnType  = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em);
796
797 14
                            $quotedColumnName       = $this->quoteStrategy->getJoinColumnName($joinColumn, $subClass, $this->platform);
798 14
                            $sqlSelectExpressions[] = $sqlTableAlias . '.' . $quotedColumnName . ' AS ' . $columnAlias;
799
800 352
                            $this->rsm->addMetaResult($dqlAlias, $columnAlias, $columnName, $subClass->isIdentifier($columnName), $columnType);
801
                        }
802
                    }
803
                }
804
            }
805
        }
806
807 626
        $sql .= implode(', ', $sqlSelectExpressions);
808
809 626
        return $sql;
810
    }
811
812
    /**
813
     * {@inheritdoc}
814
     */
815 628 View Code Duplication
    public function walkFromClause($fromClause)
816
    {
817 628
        $identificationVarDecls = $fromClause->identificationVariableDeclarations;
818 628
        $sqlParts = [];
819
820 628
        foreach ($identificationVarDecls as $identificationVariableDecl) {
821 628
            $sqlParts[] = $this->walkIdentificationVariableDeclaration($identificationVariableDecl);
822
        }
823
824 626
        return ' FROM ' . implode(', ', $sqlParts);
825
    }
826
827
    /**
828
     * Walks down a IdentificationVariableDeclaration AST node, thereby generating the appropriate SQL.
829
     *
830
     * @param AST\IdentificationVariableDeclaration $identificationVariableDecl
831
     *
832
     * @return string
833
     */
834 629
    public function walkIdentificationVariableDeclaration($identificationVariableDecl)
835
    {
836 629
        $sql = $this->walkRangeVariableDeclaration($identificationVariableDecl->rangeVariableDeclaration);
837
838 629
        if ($identificationVariableDecl->indexBy) {
839 5
            $this->walkIndexBy($identificationVariableDecl->indexBy);
840
        }
841
842 629
        foreach ($identificationVariableDecl->joins as $join) {
843 245
            $sql .= $this->walkJoin($join);
844
        }
845
846 627
        return $sql;
847
    }
848
849
    /**
850
     * Walks down a IndexBy AST node.
851
     *
852
     * @param AST\IndexBy $indexBy
853
     *
854
     * @return void
855
     */
856 8
    public function walkIndexBy($indexBy)
857
    {
858 8
        $pathExpression = $indexBy->simpleStateFieldPathExpression;
859 8
        $alias          = $pathExpression->identificationVariable;
860 8
        $field          = $pathExpression->field;
861
862 8
        if (isset($this->scalarFields[$alias][$field])) {
863
            $this->rsm->addIndexByScalar($this->scalarFields[$alias][$field]);
864
865
            return;
866
        }
867
868 8
        $this->rsm->addIndexBy($alias, $field);
869 8
    }
870
871
    /**
872
     * Walks down a RangeVariableDeclaration AST node, thereby generating the appropriate SQL.
873
     *
874
     * @param AST\RangeVariableDeclaration $rangeVariableDeclaration
875
     *
876
     * @return string
877
     */
878 629
    public function walkRangeVariableDeclaration($rangeVariableDeclaration)
879
    {
880 629
        $class    = $this->em->getClassMetadata($rangeVariableDeclaration->abstractSchemaName);
881 629
        $dqlAlias = $rangeVariableDeclaration->aliasIdentificationVariable;
882
883 629
        if ($rangeVariableDeclaration->isRoot) {
884 629
            $this->rootAliases[] = $dqlAlias;
885
        }
886
887 629
        $sql = $this->platform->appendLockHint(
888 629
            $this->quoteStrategy->getTableName($class, $this->platform) . ' ' .
889 629
            $this->getSQLTableAlias($class->getTableName(), $dqlAlias),
890 629
            $this->query->getHint(Query::HINT_LOCK_MODE)
891
        );
892
893 629
        if ($class->isInheritanceTypeJoined()) {
894 89
            $sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias);
895
        }
896
897 629
        return $sql;
898
    }
899
900
    /**
901
     * Walks down a JoinAssociationDeclaration AST node, thereby generating the appropriate SQL.
902
     *
903
     * @param AST\JoinAssociationDeclaration $joinAssociationDeclaration
904
     * @param int                            $joinType
905
     * @param AST\ConditionalExpression      $condExpr
906
     *
907
     * @return string
908
     *
909
     * @throws QueryException
910
     */
911 228
    public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joinType = AST\Join::JOIN_TYPE_INNER, $condExpr = null)
912
    {
913 228
        $sql = '';
914
915 228
        $associationPathExpression = $joinAssociationDeclaration->joinAssociationPathExpression;
916 228
        $joinedDqlAlias            = $joinAssociationDeclaration->aliasIdentificationVariable;
917 228
        $indexBy                   = $joinAssociationDeclaration->indexBy;
918
919 228
        $relation        = $this->queryComponents[$joinedDqlAlias]['relation'];
920 228
        $targetClass     = $this->em->getClassMetadata($relation['targetEntity']);
921 228
        $sourceClass     = $this->em->getClassMetadata($relation['sourceEntity']);
922 228
        $targetTableName = $this->quoteStrategy->getTableName($targetClass, $this->platform);
923
924 228
        $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName(), $joinedDqlAlias);
925 228
        $sourceTableAlias = $this->getSQLTableAlias($sourceClass->getTableName(), $associationPathExpression->identificationVariable);
926
927
        // Ensure we got the owning side, since it has all mapping info
928 228
        $assoc = ( ! $relation['isOwningSide']) ? $targetClass->associationMappings[$relation['mappedBy']] : $relation;
929
930 228
        if ($this->query->getHint(Query::HINT_INTERNAL_ITERATION) == true && (!$this->query->getHint(self::HINT_DISTINCT) || isset($this->selectedClasses[$joinedDqlAlias]))) {
931 3
            if ($relation['type'] == ClassMetadata::ONE_TO_MANY || $relation['type'] == ClassMetadata::MANY_TO_MANY) {
932 2
                throw QueryException::iterateWithFetchJoinNotAllowed($assoc);
933
            }
934
        }
935
936 226
        $targetTableJoin = null;
937
938
        // This condition is not checking ClassMetadata::MANY_TO_ONE, because by definition it cannot
939
        // be the owning side and previously we ensured that $assoc is always the owning side of the associations.
940
        // The owning side is necessary at this point because only it contains the JoinColumn information.
941
        switch (true) {
942 226
            case ($assoc['type'] & ClassMetadata::TO_ONE):
943 179
                $conditions = [];
944
945 179
                foreach ($assoc['joinColumns'] as $joinColumn) {
946 179
                    $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
947 179
                    $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform);
948
949 179
                    if ($relation['isOwningSide']) {
950 103
                        $conditions[] = $sourceTableAlias . '.' . $quotedSourceColumn . ' = ' . $targetTableAlias . '.' . $quotedTargetColumn;
951
952 103
                        continue;
953
                    }
954
955 109
                    $conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $targetTableAlias . '.' . $quotedSourceColumn;
956
                }
957
958
                // Apply remaining inheritance restrictions
959 179
                $discrSql = $this->_generateDiscriminatorColumnConditionSQL([$joinedDqlAlias]);
960
961 179
                if ($discrSql) {
962 3
                    $conditions[] = $discrSql;
963
                }
964
965
                // Apply the filters
966 179
                $filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias);
967
968 179
                if ($filterExpr) {
969 1
                    $conditions[] = $filterExpr;
970
                }
971
972
                $targetTableJoin = [
973 179
                    'table' => $targetTableName . ' ' . $targetTableAlias,
974 179
                    'condition' => implode(' AND ', $conditions),
975
                ];
976 179
                break;
977
978 57
            case ($assoc['type'] == ClassMetadata::MANY_TO_MANY):
979
                // Join relation table
980 57
                $joinTable      = $assoc['joinTable'];
981 57
                $joinTableAlias = $this->getSQLTableAlias($joinTable['name'], $joinedDqlAlias);
982 57
                $joinTableName  = $this->quoteStrategy->getJoinTableName($assoc, $sourceClass, $this->platform);
983
984 57
                $conditions      = [];
985 57
                $relationColumns = ($relation['isOwningSide'])
986 48
                    ? $assoc['joinTable']['joinColumns']
987 57
                    : $assoc['joinTable']['inverseJoinColumns'];
988
989 57 View Code Duplication
                foreach ($relationColumns as $joinColumn) {
990 57
                    $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
991 57
                    $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform);
992
993 57
                    $conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $quotedSourceColumn;
994
                }
995
996 57
                $sql .= $joinTableName . ' ' . $joinTableAlias . ' ON ' . implode(' AND ', $conditions);
997
998
                // Join target table
999 57
                $sql .= ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) ? ' LEFT JOIN ' : ' INNER JOIN ';
1000
1001 57
                $conditions      = [];
1002 57
                $relationColumns = ($relation['isOwningSide'])
1003 48
                    ? $assoc['joinTable']['inverseJoinColumns']
1004 57
                    : $assoc['joinTable']['joinColumns'];
1005
1006 57 View Code Duplication
                foreach ($relationColumns as $joinColumn) {
1007 57
                    $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
1008 57
                    $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform);
1009
1010 57
                    $conditions[] = $targetTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $quotedSourceColumn;
1011
                }
1012
1013
                // Apply remaining inheritance restrictions
1014 57
                $discrSql = $this->_generateDiscriminatorColumnConditionSQL([$joinedDqlAlias]);
1015
1016 57
                if ($discrSql) {
1017 1
                    $conditions[] = $discrSql;
1018
                }
1019
1020
                // Apply the filters
1021 57
                $filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias);
1022
1023 57
                if ($filterExpr) {
1024 1
                    $conditions[] = $filterExpr;
1025
                }
1026
1027
                $targetTableJoin = [
1028 57
                    'table' => $targetTableName . ' ' . $targetTableAlias,
1029 57
                    'condition' => implode(' AND ', $conditions),
1030
                ];
1031 57
                break;
1032
1033
            default:
1034
                throw new \BadMethodCallException('Type of association must be one of *_TO_ONE or MANY_TO_MANY');
1035
        }
1036
1037
        // Handle WITH clause
1038 226
        $withCondition = (null === $condExpr) ? '' : ('(' . $this->walkConditionalExpression($condExpr) . ')');
1039
1040 226
        if ($targetClass->isInheritanceTypeJoined()) {
1041 9
            $ctiJoins = $this->_generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias);
1042
            // If we have WITH condition, we need to build nested joins for target class table and cti joins
1043 9
            if ($withCondition) {
1044 1
                $sql .= '(' . $targetTableJoin['table'] . $ctiJoins . ') ON ' . $targetTableJoin['condition'];
1045
            } else {
1046 9
                $sql .= $targetTableJoin['table'] . ' ON ' . $targetTableJoin['condition'] . $ctiJoins;
1047
            }
1048
        } else {
1049 217
            $sql .= $targetTableJoin['table'] . ' ON ' . $targetTableJoin['condition'];
1050
        }
1051
1052 226
        if ($withCondition) {
1053 5
            $sql .= ' AND ' . $withCondition;
1054
        }
1055
1056
        // Apply the indexes
1057 226
        if ($indexBy) {
1058
            // For Many-To-One or One-To-One associations this obviously makes no sense, but is ignored silently.
1059 5
            $this->walkIndexBy($indexBy);
1060 221
        } else if (isset($relation['indexBy'])) {
1061 3
            $this->rsm->addIndexBy($joinedDqlAlias, $relation['indexBy']);
1062
        }
1063
1064 226
        return $sql;
1065
    }
1066
1067
    /**
1068
     * {@inheritdoc}
1069
     */
1070 118
    public function walkFunction($function)
1071
    {
1072 118
        return $function->getSql($this);
1073
    }
1074
1075
    /**
1076
     * {@inheritdoc}
1077
     */
1078 153
    public function walkOrderByClause($orderByClause)
1079
    {
1080 153
        $orderByItems = array_map([$this, 'walkOrderByItem'], $orderByClause->orderByItems);
1081
1082 152
        if (($collectionOrderByItems = $this->_generateOrderedCollectionOrderByItems()) !== '') {
1083
            $orderByItems = array_merge($orderByItems, (array) $collectionOrderByItems);
1084
        }
1085
1086 152
        return ' ORDER BY ' . implode(', ', $orderByItems);
1087
    }
1088
1089
    /**
1090
     * {@inheritdoc}
1091
     */
1092 171
    public function walkOrderByItem($orderByItem)
1093
    {
1094 171
        $type = strtoupper($orderByItem->type);
1095 171
        $expr = $orderByItem->expression;
1096 171
        $sql  = ($expr instanceof AST\Node)
1097 164
            ? $expr->dispatch($this)
1098 170
            : $this->walkResultVariable($this->queryComponents[$expr]['token']['value']);
1099
1100 170
        $this->orderedColumnsMap[$sql] = $type;
1101
1102 170
        if ($expr instanceof AST\Subselect) {
1103 2
            return '(' . $sql . ') ' . $type;
1104
        }
1105
1106 168
        return $sql . ' ' . $type;
1107
    }
1108
1109
    /**
1110
     * {@inheritdoc}
1111
     */
1112 14
    public function walkHavingClause($havingClause)
1113
    {
1114 14
        return ' HAVING ' . $this->walkConditionalExpression($havingClause->conditionalExpression);
1115
    }
1116
1117
    /**
1118
     * {@inheritdoc}
1119
     */
1120 245
    public function walkJoin($join)
1121
    {
1122 245
        $joinType        = $join->joinType;
1123 245
        $joinDeclaration = $join->joinAssociationDeclaration;
1124
1125 245
        $sql = ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER)
1126 57
            ? ' LEFT JOIN '
1127 245
            : ' INNER JOIN ';
1128
1129
        switch (true) {
1130 245
            case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\RangeVariableDeclaration):
1131 17
                $class      = $this->em->getClassMetadata($joinDeclaration->abstractSchemaName);
1132 17
                $dqlAlias   = $joinDeclaration->aliasIdentificationVariable;
1133 17
                $tableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
1134 17
                $conditions = [];
1135
1136 17
                if ($join->conditionalExpression) {
1137 15
                    $conditions[] = '(' . $this->walkConditionalExpression($join->conditionalExpression) . ')';
1138
                }
1139
1140 17
                $condExprConjunction = ($class->isInheritanceTypeJoined() && $joinType != AST\Join::JOIN_TYPE_LEFT && $joinType != AST\Join::JOIN_TYPE_LEFTOUTER)
1141 3
                    ? ' AND '
1142 17
                    : ' ON ';
1143
1144 17
                $sql .= $this->walkRangeVariableDeclaration($joinDeclaration);
1145
1146
                // Apply remaining inheritance restrictions
1147 17
                $discrSql = $this->_generateDiscriminatorColumnConditionSQL([$dqlAlias]);
1148
1149 17
                if ($discrSql) {
1150 3
                    $conditions[] = $discrSql;
1151
                }
1152
1153
                // Apply the filters
1154 17
                $filterExpr = $this->generateFilterConditionSQL($class, $tableAlias);
1155
1156 17
                if ($filterExpr) {
1157
                    $conditions[] = $filterExpr;
1158
                }
1159
1160 17
                if ($conditions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $conditions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1161 15
                    $sql .= $condExprConjunction . implode(' AND ', $conditions);
1162
                }
1163
1164 17
                break;
1165
1166 228
            case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\JoinAssociationDeclaration):
1167 228
                $sql .= $this->walkJoinAssociationDeclaration($joinDeclaration, $joinType, $join->conditionalExpression);
1168 226
                break;
1169
        }
1170
1171 243
        return $sql;
1172
    }
1173
1174
    /**
1175
     * Walks down a CoalesceExpression AST node and generates the corresponding SQL.
1176
     *
1177
     * @param AST\CoalesceExpression $coalesceExpression
1178
     *
1179
     * @return string The SQL.
1180
     */
1181 2
    public function walkCoalesceExpression($coalesceExpression)
1182
    {
1183 2
        $sql = 'COALESCE(';
1184
1185 2
        $scalarExpressions = [];
1186
1187 2
        foreach ($coalesceExpression->scalarExpressions as $scalarExpression) {
1188 2
            $scalarExpressions[] = $this->walkSimpleArithmeticExpression($scalarExpression);
1189
        }
1190
1191 2
        $sql .= implode(', ', $scalarExpressions) . ')';
1192
1193 2
        return $sql;
1194
    }
1195
1196
    /**
1197
     * Walks down a NullIfExpression AST node and generates the corresponding SQL.
1198
     *
1199
     * @param AST\NullIfExpression $nullIfExpression
1200
     *
1201
     * @return string The SQL.
1202
     */
1203 3
    public function walkNullIfExpression($nullIfExpression)
1204
    {
1205 3
        $firstExpression = is_string($nullIfExpression->firstExpression)
1206
            ? $this->conn->quote($nullIfExpression->firstExpression)
1207 3
            : $this->walkSimpleArithmeticExpression($nullIfExpression->firstExpression);
1208
1209 3
        $secondExpression = is_string($nullIfExpression->secondExpression)
1210
            ? $this->conn->quote($nullIfExpression->secondExpression)
1211 3
            : $this->walkSimpleArithmeticExpression($nullIfExpression->secondExpression);
1212
1213 3
        return 'NULLIF(' . $firstExpression . ', ' . $secondExpression . ')';
1214
    }
1215
1216
    /**
1217
     * Walks down a GeneralCaseExpression AST node and generates the corresponding SQL.
1218
     *
1219
     * @param AST\GeneralCaseExpression $generalCaseExpression
1220
     *
1221
     * @return string The SQL.
1222
     */
1223 9 View Code Duplication
    public function walkGeneralCaseExpression(AST\GeneralCaseExpression $generalCaseExpression)
1224
    {
1225 9
        $sql = 'CASE';
1226
1227 9
        foreach ($generalCaseExpression->whenClauses as $whenClause) {
1228 9
            $sql .= ' WHEN ' . $this->walkConditionalExpression($whenClause->caseConditionExpression);
1229 9
            $sql .= ' THEN ' . $this->walkSimpleArithmeticExpression($whenClause->thenScalarExpression);
1230
        }
1231
1232 9
        $sql .= ' ELSE ' . $this->walkSimpleArithmeticExpression($generalCaseExpression->elseScalarExpression) . ' END';
1233
1234 9
        return $sql;
1235
    }
1236
1237
    /**
1238
     * Walks down a SimpleCaseExpression AST node and generates the corresponding SQL.
1239
     *
1240
     * @param AST\SimpleCaseExpression $simpleCaseExpression
1241
     *
1242
     * @return string The SQL.
1243
     */
1244 5 View Code Duplication
    public function walkSimpleCaseExpression($simpleCaseExpression)
1245
    {
1246 5
        $sql = 'CASE ' . $this->walkStateFieldPathExpression($simpleCaseExpression->caseOperand);
1247
1248 5
        foreach ($simpleCaseExpression->simpleWhenClauses as $simpleWhenClause) {
1249 5
            $sql .= ' WHEN ' . $this->walkSimpleArithmeticExpression($simpleWhenClause->caseScalarExpression);
1250 5
            $sql .= ' THEN ' . $this->walkSimpleArithmeticExpression($simpleWhenClause->thenScalarExpression);
1251
        }
1252
1253 5
        $sql .= ' ELSE ' . $this->walkSimpleArithmeticExpression($simpleCaseExpression->elseScalarExpression) . ' END';
1254
1255 5
        return $sql;
1256
    }
1257
1258
    /**
1259
     * {@inheritdoc}
1260
     */
1261 626
    public function walkSelectExpression($selectExpression)
1262
    {
1263 626
        $sql    = '';
1264 626
        $expr   = $selectExpression->expression;
1265 626
        $hidden = $selectExpression->hiddenAliasResultVariable;
1266
1267
        switch (true) {
1268 626
            case ($expr instanceof AST\PathExpression):
1269 102
                if ($expr->type !== AST\PathExpression::TYPE_STATE_FIELD) {
1270
                    throw QueryException::invalidPathExpression($expr);
1271
                }
1272
1273 102
                $fieldName = $expr->field;
1274 102
                $dqlAlias  = $expr->identificationVariable;
1275 102
                $qComp     = $this->queryComponents[$dqlAlias];
1276 102
                $class     = $qComp['metadata'];
1277
1278 102
                $resultAlias = $selectExpression->fieldIdentificationVariable ?: $fieldName;
1279 102
                $tableName   = ($class->isInheritanceTypeJoined())
1280 11
                    ? $this->em->getUnitOfWork()->getEntityPersister($class->name)->getOwningTable($fieldName)
1281 102
                    : $class->getTableName();
1282
1283 102
                $sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias);
1284 102
                $fieldMapping  = $class->fieldMappings[$fieldName];
1285 102
                $columnName    = $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform);
1286 102
                $columnAlias   = $this->getSQLColumnAlias($fieldMapping['columnName']);
1287 102
                $col           = $sqlTableAlias . '.' . $columnName;
1288
1289 102
                if (isset($fieldMapping['requireSQLConversion'])) {
1290 2
                    $type = Type::getType($fieldMapping['type']);
1291 2
                    $col  = $type->convertToPHPValueSQL($col, $this->conn->getDatabasePlatform());
1292
                }
1293
1294 102
                $sql .= $col . ' AS ' . $columnAlias;
1295
1296 102
                $this->scalarResultAliasMap[$resultAlias] = $columnAlias;
1297
1298 102
                if ( ! $hidden) {
1299 102
                    $this->rsm->addScalarResult($columnAlias, $resultAlias, $fieldMapping['type']);
1300 102
                    $this->scalarFields[$dqlAlias][$fieldName] = $columnAlias;
1301
                }
1302
1303 102
                break;
1304
1305 575
            case ($expr instanceof AST\AggregateExpression):
1306 566
            case ($expr instanceof AST\Functions\FunctionNode):
1307 526
            case ($expr instanceof AST\SimpleArithmeticExpression):
1308 526
            case ($expr instanceof AST\ArithmeticTerm):
1309 524
            case ($expr instanceof AST\ArithmeticFactor):
1310 523
            case ($expr instanceof AST\ParenthesisExpression):
1311 522
            case ($expr instanceof AST\Literal):
1312 521
            case ($expr instanceof AST\NullIfExpression):
1313 520
            case ($expr instanceof AST\CoalesceExpression):
1314 519
            case ($expr instanceof AST\GeneralCaseExpression):
1315 515 View Code Duplication
            case ($expr instanceof AST\SimpleCaseExpression):
1316 107
                $columnAlias = $this->getSQLColumnAlias('sclr');
1317 107
                $resultAlias = $selectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
1318
1319 107
                $sql .= $expr->dispatch($this) . ' AS ' . $columnAlias;
1320
1321 107
                $this->scalarResultAliasMap[$resultAlias] = $columnAlias;
1322
1323 107
                if ( ! $hidden) {
1324
                    // We cannot resolve field type here; assume 'string'.
1325 107
                    $this->rsm->addScalarResult($columnAlias, $resultAlias, 'string');
1326
                }
1327 107
                break;
1328
1329 514 View Code Duplication
            case ($expr instanceof AST\Subselect):
1330 15
                $columnAlias = $this->getSQLColumnAlias('sclr');
1331 15
                $resultAlias = $selectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
1332
1333 15
                $sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias;
1334
1335 15
                $this->scalarResultAliasMap[$resultAlias] = $columnAlias;
1336
1337 15
                if ( ! $hidden) {
1338
                    // We cannot resolve field type here; assume 'string'.
1339 13
                    $this->rsm->addScalarResult($columnAlias, $resultAlias, 'string');
1340
                }
1341 15
                break;
1342
1343 510
            case ($expr instanceof AST\NewObjectExpression):
1344 22
                $sql .= $this->walkNewObject($expr,$selectExpression->fieldIdentificationVariable);
1345 22
                break;
1346
1347
            default:
1348
                // IdentificationVariable or PartialObjectExpression
1349 488
                if ($expr instanceof AST\PartialObjectExpression) {
1350 16
                    $dqlAlias = $expr->identificationVariable;
1351 16
                    $partialFieldSet = $expr->partialFieldSet;
1352
                } else {
1353 483
                    $dqlAlias = $expr;
1354 483
                    $partialFieldSet = [];
1355
                }
1356
1357 488
                $queryComp   = $this->queryComponents[$dqlAlias];
1358 488
                $class       = $queryComp['metadata'];
1359 488
                $resultAlias = $selectExpression->fieldIdentificationVariable ?: null;
1360
1361 488
                if ( ! isset($this->selectedClasses[$dqlAlias])) {
1362 488
                    $this->selectedClasses[$dqlAlias] = [
1363 488
                        'class'       => $class,
1364 488
                        'dqlAlias'    => $dqlAlias,
1365 488
                        'resultAlias' => $resultAlias
1366
                    ];
1367
                }
1368
1369 488
                $sqlParts = [];
1370
1371
                // Select all fields from the queried class
1372 488 View Code Duplication
                foreach ($class->fieldMappings as $fieldName => $mapping) {
1373 487
                    if ($partialFieldSet && ! in_array($fieldName, $partialFieldSet)) {
1374 14
                        continue;
1375
                    }
1376
1377 486
                    $tableName = (isset($mapping['inherited']))
1378 52
                        ? $this->em->getClassMetadata($mapping['inherited'])->getTableName()
1379 486
                        : $class->getTableName();
1380
1381 486
                    $sqlTableAlias    = $this->getSQLTableAlias($tableName, $dqlAlias);
1382 486
                    $columnAlias      = $this->getSQLColumnAlias($mapping['columnName']);
1383 486
                    $quotedColumnName = $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform);
1384
1385 486
                    $col = $sqlTableAlias . '.' . $quotedColumnName;
1386
1387 486
                    if (isset($mapping['requireSQLConversion'])) {
1388 5
                        $type = Type::getType($mapping['type']);
1389 5
                        $col = $type->convertToPHPValueSQL($col, $this->platform);
1390
                    }
1391
1392 486
                    $sqlParts[] = $col . ' AS '. $columnAlias;
1393
1394 486
                    $this->scalarResultAliasMap[$resultAlias][] = $columnAlias;
1395
1396 486
                    $this->rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $class->name);
1397
                }
1398
1399
                // Add any additional fields of subclasses (excluding inherited fields)
1400
                // 1) on Single Table Inheritance: always, since its marginal overhead
1401
                // 2) on Class Table Inheritance only if partial objects are disallowed,
1402
                //    since it requires outer joining subtables.
1403 488
                if ($class->isInheritanceTypeSingleTable() || ! $this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {
1404 395 View Code Duplication
                    foreach ($class->subClasses as $subClassName) {
1405 42
                        $subClass      = $this->em->getClassMetadata($subClassName);
1406 42
                        $sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
1407
1408 42
                        foreach ($subClass->fieldMappings as $fieldName => $mapping) {
1409 42
                            if (isset($mapping['inherited']) || ($partialFieldSet && !in_array($fieldName, $partialFieldSet))) {
1410 42
                                continue;
1411
                            }
1412
1413 34
                            $columnAlias      = $this->getSQLColumnAlias($mapping['columnName']);
1414 34
                            $quotedColumnName = $this->quoteStrategy->getColumnName($fieldName, $subClass, $this->platform);
1415
1416 34
                            $col = $sqlTableAlias . '.' . $quotedColumnName;
1417
1418 34
                            if (isset($mapping['requireSQLConversion'])) {
1419
                                $type = Type::getType($mapping['type']);
1420
                                $col = $type->convertToPHPValueSQL($col, $this->platform);
1421
                            }
1422
1423 34
                            $sqlParts[] = $col . ' AS ' . $columnAlias;
1424
1425 34
                            $this->scalarResultAliasMap[$resultAlias][] = $columnAlias;
1426
1427 42
                            $this->rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $subClassName);
1428
                        }
1429
                    }
1430
                }
1431
1432 488
                $sql .= implode(', ', $sqlParts);
1433
        }
1434
1435 626
        return $sql;
1436
    }
1437
1438
    /**
1439
     * {@inheritdoc}
1440
     */
1441
    public function walkQuantifiedExpression($qExpr)
1442
    {
1443
        return ' ' . strtoupper($qExpr->type) . '(' . $this->walkSubselect($qExpr->subselect) . ')';
1444
    }
1445
1446
    /**
1447
     * {@inheritdoc}
1448
     */
1449 33
    public function walkSubselect($subselect)
1450
    {
1451 33
        $useAliasesBefore  = $this->useSqlTableAliases;
1452 33
        $rootAliasesBefore = $this->rootAliases;
1453
1454 33
        $this->rootAliases = []; // reset the rootAliases for the subselect
1455 33
        $this->useSqlTableAliases = true;
1456
1457 33
        $sql  = $this->walkSimpleSelectClause($subselect->simpleSelectClause);
1458 33
        $sql .= $this->walkSubselectFromClause($subselect->subselectFromClause);
1459 33
        $sql .= $this->walkWhereClause($subselect->whereClause);
1460
1461 33
        $sql .= $subselect->groupByClause ? $this->walkGroupByClause($subselect->groupByClause) : '';
1462 33
        $sql .= $subselect->havingClause ? $this->walkHavingClause($subselect->havingClause) : '';
1463 33
        $sql .= $subselect->orderByClause ? $this->walkOrderByClause($subselect->orderByClause) : '';
1464
1465 33
        $this->rootAliases        = $rootAliasesBefore; // put the main aliases back
1466 33
        $this->useSqlTableAliases = $useAliasesBefore;
1467
1468 33
        return $sql;
1469
    }
1470
1471
    /**
1472
     * {@inheritdoc}
1473
     */
1474 33 View Code Duplication
    public function walkSubselectFromClause($subselectFromClause)
1475
    {
1476 33
        $identificationVarDecls = $subselectFromClause->identificationVariableDeclarations;
1477 33
        $sqlParts               = [];
1478
1479 33
        foreach ($identificationVarDecls as $subselectIdVarDecl) {
1480 33
            $sqlParts[] = $this->walkIdentificationVariableDeclaration($subselectIdVarDecl);
1481
        }
1482
1483 33
        return ' FROM ' . implode(', ', $sqlParts);
1484
    }
1485
1486
    /**
1487
     * {@inheritdoc}
1488
     */
1489 33
    public function walkSimpleSelectClause($simpleSelectClause)
1490
    {
1491 33
        return 'SELECT' . ($simpleSelectClause->isDistinct ? ' DISTINCT' : '')
1492 33
            . $this->walkSimpleSelectExpression($simpleSelectClause->simpleSelectExpression);
1493
    }
1494
1495
    /**
1496
     * @param \Doctrine\ORM\Query\AST\ParenthesisExpression $parenthesisExpression
1497
     *
1498
     * @return string.
1499
     */
1500 22
    public function walkParenthesisExpression(AST\ParenthesisExpression $parenthesisExpression)
1501
    {
1502 22
        return sprintf('(%s)', $parenthesisExpression->expression->dispatch($this));
1503
    }
1504
1505
    /**
1506
     * @param AST\NewObjectExpression   $newObjectExpression
1507
     * @param null|string               $newObjectResultAlias
1508
     * @return string The SQL.
1509
     */
1510 22
    public function walkNewObject($newObjectExpression, $newObjectResultAlias=null)
1511
    {
1512 22
        $sqlSelectExpressions = [];
1513 22
        $objIndex             = $newObjectResultAlias?:$this->newObjectCounter++;
1514
1515 22
        foreach ($newObjectExpression->args as $argIndex => $e) {
1516 22
            $resultAlias = $this->scalarResultCounter++;
1517 22
            $columnAlias = $this->getSQLColumnAlias('sclr');
1518 22
            $fieldType   = 'string';
1519
1520
            switch (true) {
1521 22
                case ($e instanceof AST\NewObjectExpression):
1522
                    $sqlSelectExpressions[] = $e->dispatch($this);
1523
                    break;
1524
1525 22
                case ($e instanceof AST\Subselect):
1526 1
                    $sqlSelectExpressions[] = '(' . $e->dispatch($this) . ') AS ' . $columnAlias;
1527 1
                    break;
1528
1529 22
                case ($e instanceof AST\PathExpression):
1530 22
                    $dqlAlias  = $e->identificationVariable;
1531 22
                    $qComp     = $this->queryComponents[$dqlAlias];
1532 22
                    $class     = $qComp['metadata'];
1533 22
                    $fieldType = $class->fieldMappings[$e->field]['type'];
1534
1535 22
                    $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' . $columnAlias;
1536 22
                    break;
1537
1538 6
                case ($e instanceof AST\Literal):
1539 1
                    switch ($e->type) {
1540 1
                        case AST\Literal::BOOLEAN:
1541 1
                            $fieldType = 'boolean';
1542 1
                            break;
1543
1544 1
                        case AST\Literal::NUMERIC:
1545 1
                            $fieldType = is_float($e->value) ? 'float' : 'integer';
1546 1
                            break;
1547
                    }
1548
1549 1
                    $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' . $columnAlias;
1550 1
                    break;
1551
1552
                default:
1553 5
                    $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' . $columnAlias;
1554 5
                    break;
1555
            }
1556
1557 22
            $this->scalarResultAliasMap[$resultAlias] = $columnAlias;
1558 22
            $this->rsm->addScalarResult($columnAlias, $resultAlias, $fieldType);
1559
1560 22
            $this->rsm->newObjectMappings[$columnAlias] = [
1561 22
                'className' => $newObjectExpression->className,
1562 22
                'objIndex'  => $objIndex,
1563 22
                'argIndex'  => $argIndex
1564
            ];
1565
        }
1566
1567 22
        return implode(', ', $sqlSelectExpressions);
1568
    }
1569
1570
    /**
1571
     * {@inheritdoc}
1572
     */
1573 33
    public function walkSimpleSelectExpression($simpleSelectExpression)
1574
    {
1575 33
        $expr = $simpleSelectExpression->expression;
1576 33
        $sql  = ' ';
1577
1578
        switch (true) {
1579 33
            case ($expr instanceof AST\PathExpression):
1580 9
                $sql .= $this->walkPathExpression($expr);
1581 9
                break;
1582
1583 24 View Code Duplication
            case ($expr instanceof AST\Subselect):
1584
                $alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
1585
1586
                $columnAlias = 'sclr' . $this->aliasCounter++;
1587
                $this->scalarResultAliasMap[$alias] = $columnAlias;
1588
1589
                $sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias;
1590
                break;
1591
1592 24
            case ($expr instanceof AST\Functions\FunctionNode):
1593 11
            case ($expr instanceof AST\SimpleArithmeticExpression):
1594 10
            case ($expr instanceof AST\ArithmeticTerm):
1595 9
            case ($expr instanceof AST\ArithmeticFactor):
1596 9
            case ($expr instanceof AST\Literal):
1597 7
            case ($expr instanceof AST\NullIfExpression):
1598 7
            case ($expr instanceof AST\CoalesceExpression):
1599 7
            case ($expr instanceof AST\GeneralCaseExpression):
1600 5 View Code Duplication
            case ($expr instanceof AST\SimpleCaseExpression):
1601 21
                $alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
1602
1603 21
                $columnAlias = $this->getSQLColumnAlias('sclr');
1604 21
                $this->scalarResultAliasMap[$alias] = $columnAlias;
1605
1606 21
                $sql .= $expr->dispatch($this) . ' AS ' . $columnAlias;
1607 21
                break;
1608
1609 3
            case ($expr instanceof AST\ParenthesisExpression):
1610 1
                $sql .= $this->walkParenthesisExpression($expr);
1611 1
                break;
1612
1613
            default: // IdentificationVariable
1614 2
                $sql .= $this->walkEntityIdentificationVariable($expr);
1615 2
                break;
1616
        }
1617
1618 33
        return $sql;
1619
    }
1620
1621
    /**
1622
     * {@inheritdoc}
1623
     */
1624 76
    public function walkAggregateExpression($aggExpression)
1625
    {
1626 76
        return $aggExpression->functionName . '(' . ($aggExpression->isDistinct ? 'DISTINCT ' : '')
1627 76
            . $this->walkSimpleArithmeticExpression($aggExpression->pathExpression) . ')';
1628
    }
1629
1630
    /**
1631
     * {@inheritdoc}
1632
     */
1633 24
    public function walkGroupByClause($groupByClause)
1634
    {
1635 24
        $sqlParts = [];
1636
1637 24
        foreach ($groupByClause->groupByItems as $groupByItem) {
1638 24
            $sqlParts[] = $this->walkGroupByItem($groupByItem);
1639
        }
1640
1641 24
        return ' GROUP BY ' . implode(', ', $sqlParts);
1642
    }
1643
1644
    /**
1645
     * {@inheritdoc}
1646
     */
1647 24
    public function walkGroupByItem($groupByItem)
1648
    {
1649
        // StateFieldPathExpression
1650 24
        if ( ! is_string($groupByItem)) {
1651 11
            return $this->walkPathExpression($groupByItem);
1652
        }
1653
1654
        // ResultVariable
1655 13
        if (isset($this->queryComponents[$groupByItem]['resultVariable'])) {
1656 2
            $resultVariable = $this->queryComponents[$groupByItem]['resultVariable'];
1657
1658 2
            if ($resultVariable instanceof AST\PathExpression) {
1659 1
                return $this->walkPathExpression($resultVariable);
1660
            }
1661
1662 1
            if (isset($resultVariable->pathExpression)) {
1663
                return $this->walkPathExpression($resultVariable->pathExpression);
1664
            }
1665
1666 1
            return $this->walkResultVariable($groupByItem);
1667
        }
1668
1669
        // IdentificationVariable
1670 11
        $sqlParts = [];
1671
1672 11
        foreach ($this->queryComponents[$groupByItem]['metadata']->fieldNames as $field) {
1673 11
            $item       = new AST\PathExpression(AST\PathExpression::TYPE_STATE_FIELD, $groupByItem, $field);
1674 11
            $item->type = AST\PathExpression::TYPE_STATE_FIELD;
1675
1676 11
            $sqlParts[] = $this->walkPathExpression($item);
1677
        }
1678
1679 11
        foreach ($this->queryComponents[$groupByItem]['metadata']->associationMappings as $mapping) {
1680 11
            if ($mapping['isOwningSide'] && $mapping['type'] & ClassMetadataInfo::TO_ONE) {
1681 7
                $item       = new AST\PathExpression(AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $groupByItem, $mapping['fieldName']);
1682 7
                $item->type = AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION;
1683
1684 11
                $sqlParts[] = $this->walkPathExpression($item);
1685
            }
1686
        }
1687
1688 11
        return implode(', ', $sqlParts);
1689
    }
1690
1691
    /**
1692
     * {@inheritdoc}
1693
     */
1694 36
    public function walkDeleteClause(AST\DeleteClause $deleteClause)
1695
    {
1696 36
        $class     = $this->em->getClassMetadata($deleteClause->abstractSchemaName);
1697 36
        $tableName = $class->getTableName();
1698 36
        $sql       = 'DELETE FROM ' . $this->quoteStrategy->getTableName($class, $this->platform);
1699
1700 36
        $this->setSQLTableAlias($tableName, $tableName, $deleteClause->aliasIdentificationVariable);
1701 36
        $this->rootAliases[] = $deleteClause->aliasIdentificationVariable;
1702
1703 36
        return $sql;
1704
    }
1705
1706
    /**
1707
     * {@inheritdoc}
1708
     */
1709 25
    public function walkUpdateClause($updateClause)
1710
    {
1711 25
        $class     = $this->em->getClassMetadata($updateClause->abstractSchemaName);
1712 25
        $tableName = $class->getTableName();
1713 25
        $sql       = 'UPDATE ' . $this->quoteStrategy->getTableName($class, $this->platform);
1714
1715 25
        $this->setSQLTableAlias($tableName, $tableName, $updateClause->aliasIdentificationVariable);
1716 25
        $this->rootAliases[] = $updateClause->aliasIdentificationVariable;
1717
1718 25
        $sql .= ' SET ' . implode(', ', array_map([$this, 'walkUpdateItem'], $updateClause->updateItems));
1719
1720 25
        return $sql;
1721
    }
1722
1723
    /**
1724
     * {@inheritdoc}
1725
     */
1726 29
    public function walkUpdateItem($updateItem)
1727
    {
1728 29
        $useTableAliasesBefore = $this->useSqlTableAliases;
1729 29
        $this->useSqlTableAliases = false;
1730
1731 29
        $sql      = $this->walkPathExpression($updateItem->pathExpression) . ' = ';
1732 29
        $newValue = $updateItem->newValue;
1733
1734
        switch (true) {
1735 29
            case ($newValue instanceof AST\Node):
1736 28
                $sql .= $newValue->dispatch($this);
1737 28
                break;
1738
1739 1
            case ($newValue === null):
1740 1
                $sql .= 'NULL';
1741 1
                break;
1742
1743
            default:
1744
                $sql .= $this->conn->quote($newValue);
1745
                break;
1746
        }
1747
1748 29
        $this->useSqlTableAliases = $useTableAliasesBefore;
1749
1750 29
        return $sql;
1751
    }
1752
1753
    /**
1754
     * {@inheritdoc}
1755
     */
1756 683
    public function walkWhereClause($whereClause)
1757
    {
1758 683
        $condSql  = null !== $whereClause ? $this->walkConditionalExpression($whereClause->conditionalExpression) : '';
1759 680
        $discrSql = $this->_generateDiscriminatorColumnConditionSQL($this->rootAliases);
1760
1761 680
        if ($this->em->hasFilters()) {
1762 43
            $filterClauses = [];
1763 43
            foreach ($this->rootAliases as $dqlAlias) {
1764 43
                $class = $this->queryComponents[$dqlAlias]['metadata'];
1765 43
                $tableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
1766
1767 43
                if ($filterExpr = $this->generateFilterConditionSQL($class, $tableAlias)) {
1768 43
                    $filterClauses[] = $filterExpr;
1769
                }
1770
            }
1771
1772 43
            if (count($filterClauses)) {
1773 6
                if ($condSql) {
1774 2
                    $condSql = '(' . $condSql . ') AND ';
1775
                }
1776
1777 6
                $condSql .= implode(' AND ', $filterClauses);
1778
            }
1779
        }
1780
1781 680
        if ($condSql) {
1782 334
            return ' WHERE ' . (( ! $discrSql) ? $condSql : '(' . $condSql . ') AND ' . $discrSql);
1783
        }
1784
1785 422
        if ($discrSql) {
1786 23
            return ' WHERE ' . $discrSql;
1787
        }
1788
1789 402
        return '';
1790
    }
1791
1792
    /**
1793
     * {@inheritdoc}
1794
     */
1795 365
    public function walkConditionalExpression($condExpr)
1796
    {
1797
        // Phase 2 AST optimization: Skip processing of ConditionalExpression
1798
        // if only one ConditionalTerm is defined
1799 365
        if ( ! ($condExpr instanceof AST\ConditionalExpression)) {
1800 310
            return $this->walkConditionalTerm($condExpr);
1801
        }
1802
1803 72
        return implode(' OR ', array_map([$this, 'walkConditionalTerm'], $condExpr->conditionalTerms));
1804
    }
1805
1806
    /**
1807
     * {@inheritdoc}
1808
     */
1809 365
    public function walkConditionalTerm($condTerm)
1810
    {
1811
        // Phase 2 AST optimization: Skip processing of ConditionalTerm
1812
        // if only one ConditionalFactor is defined
1813 365
        if ( ! ($condTerm instanceof AST\ConditionalTerm)) {
1814 298
            return $this->walkConditionalFactor($condTerm);
1815
        }
1816
1817 90
        return implode(' AND ', array_map([$this, 'walkConditionalFactor'], $condTerm->conditionalFactors));
1818
    }
1819
1820
    /**
1821
     * {@inheritdoc}
1822
     */
1823 365
    public function walkConditionalFactor($factor)
1824
    {
1825
        // Phase 2 AST optimization: Skip processing of ConditionalFactor
1826
        // if only one ConditionalPrimary is defined
1827 365
        return ( ! ($factor instanceof AST\ConditionalFactor))
1828 362
            ? $this->walkConditionalPrimary($factor)
1829 362
            : ($factor->not ? 'NOT ' : '') . $this->walkConditionalPrimary($factor->conditionalPrimary);
1830
    }
1831
1832
    /**
1833
     * {@inheritdoc}
1834
     */
1835 365
    public function walkConditionalPrimary($primary)
1836
    {
1837 365
        if ($primary->isSimpleConditionalExpression()) {
1838 365
            return $primary->simpleConditionalExpression->dispatch($this);
1839
        }
1840
1841 25
        if ($primary->isConditionalExpression()) {
1842 25
            $condExpr = $primary->conditionalExpression;
1843
1844 25
            return '(' . $this->walkConditionalExpression($condExpr) . ')';
1845
        }
1846
    }
1847
1848
    /**
1849
     * {@inheritdoc}
1850
     */
1851 5
    public function walkExistsExpression($existsExpr)
1852
    {
1853 5
        $sql = ($existsExpr->not) ? 'NOT ' : '';
1854
1855 5
        $sql .= 'EXISTS (' . $this->walkSubselect($existsExpr->subselect) . ')';
1856
1857 5
        return $sql;
1858
    }
1859
1860
    /**
1861
     * {@inheritdoc}
1862
     */
1863 6
    public function walkCollectionMemberExpression($collMemberExpr)
1864
    {
1865 6
        $sql = $collMemberExpr->not ? 'NOT ' : '';
1866 6
        $sql .= 'EXISTS (SELECT 1 FROM ';
1867
1868 6
        $entityExpr   = $collMemberExpr->entityExpression;
1869 6
        $collPathExpr = $collMemberExpr->collectionValuedPathExpression;
1870
1871 6
        $fieldName = $collPathExpr->field;
1872 6
        $dqlAlias  = $collPathExpr->identificationVariable;
1873
1874 6
        $class = $this->queryComponents[$dqlAlias]['metadata'];
1875
1876
        switch (true) {
1877
            // InputParameter
1878 6
            case ($entityExpr instanceof AST\InputParameter):
1879 4
                $dqlParamKey = $entityExpr->name;
1880 4
                $entitySql   = '?';
1881 4
                break;
1882
1883
            // SingleValuedAssociationPathExpression | IdentificationVariable
1884 2
            case ($entityExpr instanceof AST\PathExpression):
1885 2
                $entitySql = $this->walkPathExpression($entityExpr);
1886 2
                break;
1887
1888
            default:
1889
                throw new \BadMethodCallException("Not implemented");
1890
        }
1891
1892 6
        $assoc = $class->associationMappings[$fieldName];
1893
1894 6
        if ($assoc['type'] == ClassMetadata::ONE_TO_MANY) {
1895 1
            $targetClass      = $this->em->getClassMetadata($assoc['targetEntity']);
1896 1
            $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName());
1897 1
            $sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
1898
1899 1
            $sql .= $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' ' . $targetTableAlias . ' WHERE ';
1900
1901 1
            $owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']];
1902 1
            $sqlParts    = [];
1903
1904 1 View Code Duplication
            foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) {
1905 1
                $targetColumn = $this->quoteStrategy->getColumnName($class->fieldNames[$targetColumn], $class, $this->platform);
1906
1907 1
                $sqlParts[] = $sourceTableAlias . '.' . $targetColumn . ' = ' . $targetTableAlias . '.' . $sourceColumn;
1908
            }
1909
1910 1 View Code Duplication
            foreach ($this->quoteStrategy->getIdentifierColumnNames($targetClass, $this->platform) as $targetColumnName) {
1911 1
                if (isset($dqlParamKey)) {
1912 1
                    $this->parserResult->addParameterMapping($dqlParamKey, $this->sqlParamIndex++);
1913
                }
1914
1915 1
                $sqlParts[] = $targetTableAlias . '.'  . $targetColumnName . ' = ' . $entitySql;
1916
            }
1917
1918 1
            $sql .= implode(' AND ', $sqlParts);
1919
        } else { // many-to-many
1920 5
            $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
1921
1922 5
            $owningAssoc = $assoc['isOwningSide'] ? $assoc : $targetClass->associationMappings[$assoc['mappedBy']];
1923 5
            $joinTable = $owningAssoc['joinTable'];
1924
1925
            // SQL table aliases
1926 5
            $joinTableAlias   = $this->getSQLTableAlias($joinTable['name']);
1927 5
            $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName());
1928 5
            $sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
1929
1930
            // join to target table
1931 5
            $sql .= $this->quoteStrategy->getJoinTableName($owningAssoc, $targetClass, $this->platform) . ' ' . $joinTableAlias
1932 5
                . ' INNER JOIN ' . $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' ' . $targetTableAlias . ' ON ';
1933
1934
            // join conditions
1935 5
            $joinColumns  = $assoc['isOwningSide'] ? $joinTable['inverseJoinColumns'] : $joinTable['joinColumns'];
1936 5
            $joinSqlParts = [];
1937
1938 5 View Code Duplication
            foreach ($joinColumns as $joinColumn) {
1939 5
                $targetColumn = $this->quoteStrategy->getColumnName($targetClass->fieldNames[$joinColumn['referencedColumnName']], $targetClass, $this->platform);
1940
1941 5
                $joinSqlParts[] = $joinTableAlias . '.' . $joinColumn['name'] . ' = ' . $targetTableAlias . '.' . $targetColumn;
1942
            }
1943
1944 5
            $sql .= implode(' AND ', $joinSqlParts);
1945 5
            $sql .= ' WHERE ';
1946
1947 5
            $joinColumns = $assoc['isOwningSide'] ? $joinTable['joinColumns'] : $joinTable['inverseJoinColumns'];
1948 5
            $sqlParts    = [];
1949
1950 5 View Code Duplication
            foreach ($joinColumns as $joinColumn) {
1951 5
                $targetColumn = $this->quoteStrategy->getColumnName($class->fieldNames[$joinColumn['referencedColumnName']], $class, $this->platform);
1952
1953 5
                $sqlParts[] = $joinTableAlias . '.' . $joinColumn['name'] . ' = ' . $sourceTableAlias . '.' . $targetColumn;
1954
            }
1955
1956 5 View Code Duplication
            foreach ($this->quoteStrategy->getIdentifierColumnNames($targetClass, $this->platform) as $targetColumnName) {
1957 5
                if (isset($dqlParamKey)) {
1958 3
                    $this->parserResult->addParameterMapping($dqlParamKey, $this->sqlParamIndex++);
1959
                }
1960
1961 5
                $sqlParts[] = $targetTableAlias . '.' . $targetColumnName . ' IN (' . $entitySql . ')';
1962
            }
1963
1964 5
            $sql .= implode(' AND ', $sqlParts);
1965
        }
1966
1967 6
        return $sql . ')';
1968
    }
1969
1970
    /**
1971
     * {@inheritdoc}
1972
     */
1973 3
    public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr)
1974
    {
1975 3
        $sizeFunc = new AST\Functions\SizeFunction('size');
1976 3
        $sizeFunc->collectionPathExpression = $emptyCollCompExpr->expression;
1977
1978 3
        return $sizeFunc->getSql($this) . ($emptyCollCompExpr->not ? ' > 0' : ' = 0');
1979
    }
1980
1981
    /**
1982
     * {@inheritdoc}
1983
     */
1984 11
    public function walkNullComparisonExpression($nullCompExpr)
1985
    {
1986 11
        $expression = $nullCompExpr->expression;
1987 11
        $comparison = ' IS' . ($nullCompExpr->not ? ' NOT' : '') . ' NULL';
1988
1989
        // Handle ResultVariable
1990 11
        if (is_string($expression) && isset($this->queryComponents[$expression]['resultVariable'])) {
1991 2
            return $this->walkResultVariable($expression) . $comparison;
1992
        }
1993
1994
        // Handle InputParameter mapping inclusion to ParserResult
1995 9
        if ($expression instanceof AST\InputParameter) {
1996
            return $this->walkInputParameter($expression) . $comparison;
1997
        }
1998
1999 9
        return $expression->dispatch($this) . $comparison;
2000
    }
2001
2002
    /**
2003
     * {@inheritdoc}
2004
     */
2005 85
    public function walkInExpression($inExpr)
2006
    {
2007 85
        $sql = $this->walkArithmeticExpression($inExpr->expression) . ($inExpr->not ? ' NOT' : '') . ' IN (';
2008
2009 84
        $sql .= ($inExpr->subselect)
2010 7
            ? $this->walkSubselect($inExpr->subselect)
2011 84
            : implode(', ', array_map([$this, 'walkInParameter'], $inExpr->literals));
2012
2013 84
        $sql .= ')';
2014
2015 84
        return $sql;
2016
    }
2017
2018
    /**
2019
     * {@inheritdoc}
2020
     */
2021 9
    public function walkInstanceOfExpression($instanceOfExpr)
2022
    {
2023 9
        $sql = '';
2024
2025 9
        $dqlAlias = $instanceOfExpr->identificationVariable;
2026 9
        $discrClass = $class = $this->queryComponents[$dqlAlias]['metadata'];
2027
2028 9
        if ($class->discriminatorColumn) {
2029 9
            $discrClass = $this->em->getClassMetadata($class->rootEntityName);
2030
        }
2031
2032 9
        if ($this->useSqlTableAliases) {
2033 9
            $sql .= $this->getSQLTableAlias($discrClass->getTableName(), $dqlAlias) . '.';
2034
        }
2035
2036 9
        $sql .= $class->discriminatorColumn['name'] . ($instanceOfExpr->not ? ' NOT IN ' : ' IN ');
2037
2038 9
        $sqlParameterList = [];
2039
2040 9
        foreach ($instanceOfExpr->value as $parameter) {
2041 9
            if ($parameter instanceof AST\InputParameter) {
2042 3
                $this->rsm->addMetadataParameterMapping($parameter->name, 'discriminatorValue');
2043
2044 3
                $sqlParameterList[] = $this->walkInputParameter($parameter);
2045
2046 3
                continue;
2047
            }
2048
2049
            // Get name from ClassMetadata to resolve aliases.
2050 6
            $entityClassName    = $this->em->getClassMetadata($parameter)->name;
2051 6
            $discriminatorValue = $class->discriminatorValue;
2052
2053 6
            if ($entityClassName !== $class->name) {
2054 5
                $discrMap = array_flip($class->discriminatorMap);
2055
2056 5
                if ( ! isset($discrMap[$entityClassName])) {
2057 1
                    throw QueryException::instanceOfUnrelatedClass($entityClassName, $class->rootEntityName);
2058
                }
2059
2060 4
                $discriminatorValue = $discrMap[$entityClassName];
2061
            }
2062
2063 5
            $sqlParameterList[] = $this->conn->quote($discriminatorValue);
2064
        }
2065
2066 8
        $sql .= '(' . implode(', ', $sqlParameterList) . ')';
2067
2068 8
        return $sql;
2069
    }
2070
2071
    /**
2072
     * {@inheritdoc}
2073
     */
2074 77
    public function walkInParameter($inParam)
2075
    {
2076 77
        return $inParam instanceof AST\InputParameter
2077 68
            ? $this->walkInputParameter($inParam)
2078 77
            : $this->walkLiteral($inParam);
2079
    }
2080
2081
    /**
2082
     * {@inheritdoc}
2083
     */
2084 148
    public function walkLiteral($literal)
2085
    {
2086 148
        switch ($literal->type) {
2087 148
            case AST\Literal::STRING:
2088 50
                return $this->conn->quote($literal->value);
2089
2090 111
            case AST\Literal::BOOLEAN:
2091 8
                return $this->conn->getDatabasePlatform()->convertBooleans('true' === strtolower($literal->value));
2092
2093 104
            case AST\Literal::NUMERIC:
2094 104
                return $literal->value;
2095
2096
            default:
2097
                throw QueryException::invalidLiteral($literal);
2098
        }
2099
    }
2100
2101
    /**
2102
     * {@inheritdoc}
2103
     */
2104 6
    public function walkBetweenExpression($betweenExpr)
2105
    {
2106 6
        $sql = $this->walkArithmeticExpression($betweenExpr->expression);
2107
2108 6
        if ($betweenExpr->not) {
2109 2
            $sql .= ' NOT';
2110
        }
2111
2112 6
        $sql .= ' BETWEEN ' . $this->walkArithmeticExpression($betweenExpr->leftBetweenExpression)
2113 6
            . ' AND ' . $this->walkArithmeticExpression($betweenExpr->rightBetweenExpression);
2114
2115 6
        return $sql;
2116
    }
2117
2118
    /**
2119
     * {@inheritdoc}
2120
     */
2121 9
    public function walkLikeExpression($likeExpr)
2122
    {
2123 9
        $stringExpr = $likeExpr->stringExpression;
2124 9
        $leftExpr   = (is_string($stringExpr) && isset($this->queryComponents[$stringExpr]['resultVariable']))
2125 1
            ? $this->walkResultVariable($stringExpr)
2126 9
            : $stringExpr->dispatch($this);
2127
2128 9
        $sql = $leftExpr . ($likeExpr->not ? ' NOT' : '') . ' LIKE ';
2129
2130 9
        if ($likeExpr->stringPattern instanceof AST\InputParameter) {
2131 4
            $sql .= $this->walkInputParameter($likeExpr->stringPattern);
2132 6
        } elseif ($likeExpr->stringPattern instanceof AST\Functions\FunctionNode) {
2133 2
            $sql .= $this->walkFunction($likeExpr->stringPattern);
2134 6
        } elseif ($likeExpr->stringPattern instanceof AST\PathExpression) {
2135 2
            $sql .= $this->walkPathExpression($likeExpr->stringPattern);
2136
        } else {
2137 6
            $sql .= $this->walkLiteral($likeExpr->stringPattern);
2138
        }
2139
2140 9
        if ($likeExpr->escapeChar) {
2141 1
            $sql .= ' ESCAPE ' . $this->walkLiteral($likeExpr->escapeChar);
2142
        }
2143
2144 9
        return $sql;
2145
    }
2146
2147
    /**
2148
     * {@inheritdoc}
2149
     */
2150 5
    public function walkStateFieldPathExpression($stateFieldPathExpression)
2151
    {
2152 5
        return $this->walkPathExpression($stateFieldPathExpression);
2153
    }
2154
2155
    /**
2156
     * {@inheritdoc}
2157
     */
2158 262
    public function walkComparisonExpression($compExpr)
2159
    {
2160 262
        $leftExpr  = $compExpr->leftExpression;
2161 262
        $rightExpr = $compExpr->rightExpression;
2162 262
        $sql       = '';
2163
2164 262
        $sql .= ($leftExpr instanceof AST\Node)
2165 262
            ? $leftExpr->dispatch($this)
2166 261
            : (is_numeric($leftExpr) ? $leftExpr : $this->conn->quote($leftExpr));
2167
2168 261
        $sql .= ' ' . $compExpr->operator . ' ';
2169
2170 261
        $sql .= ($rightExpr instanceof AST\Node)
2171 259
            ? $rightExpr->dispatch($this)
2172 261
            : (is_numeric($rightExpr) ? $rightExpr : $this->conn->quote($rightExpr));
2173
2174 261
        return $sql;
2175
    }
2176
2177
    /**
2178
     * {@inheritdoc}
2179
     */
2180 217
    public function walkInputParameter($inputParam)
2181
    {
2182 217
        $this->parserResult->addParameterMapping($inputParam->name, $this->sqlParamIndex++);
2183
2184 217
        $parameter = $this->query->getParameter($inputParam->name);
2185
2186 217
        if ($parameter && Type::hasType($type = $parameter->getType())) {
2187 56
            return Type::getType($type)->convertToDatabaseValueSQL('?', $this->platform);
2188
        }
2189
2190 167
        return '?';
2191
    }
2192
2193
    /**
2194
     * {@inheritdoc}
2195
     */
2196 328
    public function walkArithmeticExpression($arithmeticExpr)
2197
    {
2198 328
        return ($arithmeticExpr->isSimpleArithmeticExpression())
2199 328
            ? $this->walkSimpleArithmeticExpression($arithmeticExpr->simpleArithmeticExpression)
2200 326
            : '(' . $this->walkSubselect($arithmeticExpr->subselect) . ')';
2201
    }
2202
2203
    /**
2204
     * {@inheritdoc}
2205
     */
2206 391
    public function walkSimpleArithmeticExpression($simpleArithmeticExpr)
2207
    {
2208 391
        if ( ! ($simpleArithmeticExpr instanceof AST\SimpleArithmeticExpression)) {
2209 341
            return $this->walkArithmeticTerm($simpleArithmeticExpr);
2210
        }
2211
2212 75
        return implode(' ', array_map([$this, 'walkArithmeticTerm'], $simpleArithmeticExpr->arithmeticTerms));
2213
    }
2214
2215
    /**
2216
     * {@inheritdoc}
2217
     */
2218 412
    public function walkArithmeticTerm($term)
2219
    {
2220 412 View Code Duplication
        if (is_string($term)) {
2221 21
            return (isset($this->queryComponents[$term]))
2222 6
                ? $this->walkResultVariable($this->queryComponents[$term]['token']['value'])
2223 21
                : $term;
2224
        }
2225
2226
        // Phase 2 AST optimization: Skip processing of ArithmeticTerm
2227
        // if only one ArithmeticFactor is defined
2228 411
        if ( ! ($term instanceof AST\ArithmeticTerm)) {
2229 389
            return $this->walkArithmeticFactor($term);
2230
        }
2231
2232 47
        return implode(' ', array_map([$this, 'walkArithmeticFactor'], $term->arithmeticFactors));
2233
    }
2234
2235
    /**
2236
     * {@inheritdoc}
2237
     */
2238 412
    public function walkArithmeticFactor($factor)
2239
    {
2240 412 View Code Duplication
        if (is_string($factor)) {
2241 47
            return (isset($this->queryComponents[$factor]))
2242 2
                ? $this->walkResultVariable($this->queryComponents[$factor]['token']['value'])
2243 47
                : $factor;
2244
        }
2245
2246
        // Phase 2 AST optimization: Skip processing of ArithmeticFactor
2247
        // if only one ArithmeticPrimary is defined
2248 412
        if ( ! ($factor instanceof AST\ArithmeticFactor)) {
2249 411
            return $this->walkArithmeticPrimary($factor);
2250
        }
2251
2252 3
        $sign = $factor->isNegativeSigned() ? '-' : ($factor->isPositiveSigned() ? '+' : '');
2253
2254 3
        return $sign . $this->walkArithmeticPrimary($factor->arithmeticPrimary);
2255
    }
2256
2257
    /**
2258
     * Walks down an ArithmeticPrimary that represents an AST node, thereby generating the appropriate SQL.
2259
     *
2260
     * @param mixed $primary
2261
     *
2262
     * @return string The SQL.
2263
     */
2264 412
    public function walkArithmeticPrimary($primary)
2265
    {
2266 412
        if ($primary instanceof AST\SimpleArithmeticExpression) {
2267
            return '(' . $this->walkSimpleArithmeticExpression($primary) . ')';
2268
        }
2269
2270 412
        if ($primary instanceof AST\Node) {
2271 412
            return $primary->dispatch($this);
2272
        }
2273
2274
        return $this->walkEntityIdentificationVariable($primary);
2275
    }
2276
2277
    /**
2278
     * {@inheritdoc}
2279
     */
2280 18
    public function walkStringPrimary($stringPrimary)
2281
    {
2282 18
        return (is_string($stringPrimary))
2283
            ? $this->conn->quote($stringPrimary)
2284 18
            : $stringPrimary->dispatch($this);
2285
    }
2286
2287
    /**
2288
     * {@inheritdoc}
2289
     */
2290 30
    public function walkResultVariable($resultVariable)
2291
    {
2292 30
        $resultAlias = $this->scalarResultAliasMap[$resultVariable];
2293
2294 30
        if (is_array($resultAlias)) {
2295 1
            return implode(', ', $resultAlias);
2296
        }
2297
2298 29
        return $resultAlias;
2299
    }
2300
}
2301