Passed
Pull Request — master (#3157)
by Sergei
16:37
created

AbstractPlatform::modifyLimitQuery()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 7.4572

Importance

Changes 0
Metric Value
dl 0
loc 23
ccs 7
cts 13
cp 0.5385
rs 8.5906
c 0
b 0
f 0
cc 5
eloc 12
nc 6
nop 3
crap 7.4572
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\DBAL\Platforms;
21
22
use Doctrine\Common\EventManager;
23
use Doctrine\DBAL\DBALException;
24
use Doctrine\DBAL\Event\SchemaAlterTableAddColumnEventArgs;
25
use Doctrine\DBAL\Event\SchemaAlterTableChangeColumnEventArgs;
26
use Doctrine\DBAL\Event\SchemaAlterTableEventArgs;
27
use Doctrine\DBAL\Event\SchemaAlterTableRemoveColumnEventArgs;
28
use Doctrine\DBAL\Event\SchemaAlterTableRenameColumnEventArgs;
29
use Doctrine\DBAL\Event\SchemaCreateTableColumnEventArgs;
30
use Doctrine\DBAL\Event\SchemaCreateTableEventArgs;
31
use Doctrine\DBAL\Event\SchemaDropTableEventArgs;
32
use Doctrine\DBAL\Events;
33
use Doctrine\DBAL\Schema\Column;
34
use Doctrine\DBAL\Schema\ColumnDiff;
35
use Doctrine\DBAL\Schema\Constraint;
36
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
37
use Doctrine\DBAL\Schema\Identifier;
38
use Doctrine\DBAL\Schema\Index;
39
use Doctrine\DBAL\Schema\Sequence;
40
use Doctrine\DBAL\Schema\Table;
41
use Doctrine\DBAL\Schema\TableDiff;
42
use Doctrine\DBAL\TransactionIsolationLevel;
43
use Doctrine\DBAL\Types;
44
use Doctrine\DBAL\Types\Type;
45
use function addcslashes;
46
use function array_map;
47
use function array_merge;
48
use function array_unique;
49
use function array_values;
50
use function count;
51
use function explode;
52
use function func_get_arg;
53
use function func_get_args;
54
use function func_num_args;
55
use function get_class;
56
use function implode;
57
use function in_array;
58
use function is_array;
59
use function is_bool;
60
use function is_int;
61
use function is_string;
62
use function join;
63
use function preg_quote;
64
use function preg_replace;
65
use function sprintf;
66
use function str_replace;
67
use function strlen;
68
use function strpos;
69
use function strtolower;
70
use function strtoupper;
71
72
/**
73
 * Base class for all DatabasePlatforms. The DatabasePlatforms are the central
74
 * point of abstraction of platform-specific behaviors, features and SQL dialects.
75
 * They are a passive source of information.
76
 *
77
 * @link   www.doctrine-project.org
78
 * @since  2.0
79
 * @author Guilherme Blanco <[email protected]>
80
 * @author Jonathan Wage <[email protected]>
81
 * @author Roman Borschel <[email protected]>
82
 * @author Lukas Smith <[email protected]> (PEAR MDB2 library)
83
 * @author Benjamin Eberlei <[email protected]>
84
 * @todo   Remove any unnecessary methods.
85
 */
86
abstract class AbstractPlatform
87
{
88
    /**
89
     * @var int
90
     */
91
    const CREATE_INDEXES = 1;
92
93
    /**
94
     * @var int
95
     */
96
    const CREATE_FOREIGNKEYS = 2;
97
98
    /**
99
     * @deprecated Use DateIntervalUnit::INTERVAL_UNIT_SECOND.
100
     */
101
    public const DATE_INTERVAL_UNIT_SECOND = DateIntervalUnit::SECOND;
102
103
    /**
104
     * @deprecated Use DateIntervalUnit::MINUTE.
105
     */
106
    public const DATE_INTERVAL_UNIT_MINUTE = DateIntervalUnit::MINUTE;
107
108
    /**
109
     * @deprecated Use DateIntervalUnit::HOUR.
110
     */
111
    public const DATE_INTERVAL_UNIT_HOUR = DateIntervalUnit::HOUR;
112
113
    /**
114
     * @deprecated Use DateIntervalUnit::DAY.
115
     */
116
    public const DATE_INTERVAL_UNIT_DAY = DateIntervalUnit::DAY;
117
118
    /**
119
     * @deprecated Use DateIntervalUnit::WEEK.
120
     */
121
    public const DATE_INTERVAL_UNIT_WEEK = DateIntervalUnit::WEEK;
122
123
    /**
124
     * @deprecated Use DateIntervalUnit::MONTH.
125
     */
126
    public const DATE_INTERVAL_UNIT_MONTH = DateIntervalUnit::MONTH;
127
128
    /**
129
     * @deprecated Use DateIntervalUnit::QUARTER.
130
     */
131
    public const DATE_INTERVAL_UNIT_QUARTER = DateIntervalUnit::QUARTER;
132
133
    /**
134
     * @deprecated Use DateIntervalUnit::QUARTER.
135
     */
136
    public const DATE_INTERVAL_UNIT_YEAR = DateIntervalUnit::YEAR;
137
138
    /**
139
     * @var int
140
     *
141
     * @deprecated Use TrimMode::UNSPECIFIED.
142
     */
143
    public const TRIM_UNSPECIFIED = TrimMode::UNSPECIFIED;
144
145
    /**
146
     * @var int
147
     *
148
     * @deprecated Use TrimMode::LEADING.
149
     */
150
    public const TRIM_LEADING = TrimMode::LEADING;
151
152
    /**
153
     * @var int
154
     *
155
     * @deprecated Use TrimMode::TRAILING.
156
     */
157
    public const TRIM_TRAILING = TrimMode::TRAILING;
158
159
    /**
160
     * @var int
161
     *
162
     * @deprecated Use TrimMode::BOTH.
163
     */
164
    public const TRIM_BOTH = TrimMode::BOTH;
165
166
    /**
167
     * @var array|null
168
     */
169
    protected $doctrineTypeMapping = null;
170
171
    /**
172
     * Contains a list of all columns that should generate parseable column comments for type-detection
173
     * in reverse engineering scenarios.
174
     *
175
     * @var array|null
176
     */
177
    protected $doctrineTypeComments = null;
178
179
    /**
180
     * @var \Doctrine\Common\EventManager
181
     */
182
    protected $_eventManager;
183
184
    /**
185
     * Holds the KeywordList instance for the current platform.
186
     *
187
     * @var \Doctrine\DBAL\Platforms\Keywords\KeywordList
188
     */
189
    protected $_keywords;
190
191
    /**
192
     * Constructor.
193
     */
194 63195
    public function __construct()
195
    {
196 63195
    }
197
198
    /**
199
     * Sets the EventManager used by the Platform.
200
     *
201
     * @param \Doctrine\Common\EventManager $eventManager
202
     */
203 1617
    public function setEventManager(EventManager $eventManager)
204
    {
205 1617
        $this->_eventManager = $eventManager;
206 1617
    }
207
208
    /**
209
     * Gets the EventManager used by the Platform.
210
     *
211
     * @return \Doctrine\Common\EventManager
212
     */
213 969
    public function getEventManager()
214
    {
215 969
        return $this->_eventManager;
216
    }
217
218
    /**
219
     * Returns the SQL snippet that declares a boolean column.
220
     *
221
     * @param array $columnDef
222
     *
223
     * @return string
224
     */
225
    abstract public function getBooleanTypeDeclarationSQL(array $columnDef);
226
227
    /**
228
     * Returns the SQL snippet that declares a 4 byte integer column.
229
     *
230
     * @param array $columnDef
231
     *
232
     * @return string
233
     */
234
    abstract public function getIntegerTypeDeclarationSQL(array $columnDef);
235
236
    /**
237
     * Returns the SQL snippet that declares an 8 byte integer column.
238
     *
239
     * @param array $columnDef
240
     *
241
     * @return string
242
     */
243
    abstract public function getBigIntTypeDeclarationSQL(array $columnDef);
244
245
    /**
246
     * Returns the SQL snippet that declares a 2 byte integer column.
247
     *
248
     * @param array $columnDef
249
     *
250
     * @return string
251
     */
252
    abstract public function getSmallIntTypeDeclarationSQL(array $columnDef);
253
254
    /**
255
     * Returns the SQL snippet that declares common properties of an integer column.
256
     *
257
     * @param array $columnDef
258
     *
259
     * @return string
260
     */
261
    abstract protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef);
262
263
    /**
264
     * Lazy load Doctrine Type Mappings.
265
     *
266
     * @return void
267
     */
268
    abstract protected function initializeDoctrineTypeMappings();
269
270
    /**
271
     * Initializes Doctrine Type Mappings with the platform defaults
272
     * and with all additional type mappings.
273
     *
274
     * @return void
275
     */
276 1782
    private function initializeAllDoctrineTypeMappings()
277
    {
278 1782
        $this->initializeDoctrineTypeMappings();
279
280 1782
        foreach (Type::getTypesMap() as $typeName => $className) {
281 1782
            foreach (Type::getType($typeName)->getMappedDatabaseTypes($this) as $dbType) {
282 1782
                $this->doctrineTypeMapping[$dbType] = $typeName;
283
            }
284
        }
285 1782
    }
286
287
    /**
288
     * Returns the SQL snippet used to declare a VARCHAR column type.
289
     *
290
     * @param array $field
291
     *
292
     * @return string
293
     */
294 6266
    public function getVarcharTypeDeclarationSQL(array $field)
295
    {
296 6266
        if ( !isset($field['length'])) {
297 1488
            $field['length'] = $this->getVarcharDefaultLength();
298
        }
299
300 6266
        $fixed = $field['fixed'] ?? false;
301
302 6266
        $maxLength = $fixed
303 889
            ? $this->getCharMaxLength()
304 6266
            : $this->getVarcharMaxLength();
305
306 6266
        if ($field['length'] > $maxLength) {
307
            return $this->getClobTypeDeclarationSQL($field);
308
        }
309
310 6266
        return $this->getVarcharTypeDeclarationSQLSnippet($field['length'], $fixed);
311
    }
312
313
    /**
314
     * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
315
     *
316
     * @param array $field The column definition.
317
     *
318
     * @return string
319
     */
320 358
    public function getBinaryTypeDeclarationSQL(array $field)
321
    {
322 358
        if ( ! isset($field['length'])) {
323 341
            $field['length'] = $this->getBinaryDefaultLength();
324
        }
325
326 358
        $fixed = $field['fixed'] ?? false;
327
328 358
        if ($field['length'] > $this->getBinaryMaxLength()) {
329 331
            return $this->getBlobTypeDeclarationSQL($field);
330
        }
331
332 351
        return $this->getBinaryTypeDeclarationSQLSnippet($field['length'], $fixed);
333
    }
334
335
    /**
336
     * Returns the SQL snippet to declare a GUID/UUID field.
337
     *
338
     * By default this maps directly to a CHAR(36) and only maps to more
339
     * special datatypes when the underlying databases support this datatype.
340
     *
341
     * @param array $field
342
     *
343
     * @return string
344
     */
345 115
    public function getGuidTypeDeclarationSQL(array $field)
346
    {
347 115
        $field['length'] = 36;
348 115
        $field['fixed']  = true;
349
350 115
        return $this->getVarcharTypeDeclarationSQL($field);
351
    }
352
353
    /**
354
     * Returns the SQL snippet to declare a JSON field.
355
     *
356
     * By default this maps directly to a CLOB and only maps to more
357
     * special datatypes when the underlying databases support this datatype.
358
     *
359
     * @param array $field
360
     *
361
     * @return string
362
     */
363 373
    public function getJsonTypeDeclarationSQL(array $field)
364
    {
365 373
        return $this->getClobTypeDeclarationSQL($field);
366
    }
367
368
    /**
369
     * @param int  $length
370
     * @param bool $fixed
371
     *
372
     * @return string
373
     *
374
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
375
     */
376
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
0 ignored issues
show
Unused Code introduced by
The parameter $fixed is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

376
    protected function getVarcharTypeDeclarationSQLSnippet($length, /** @scrutinizer ignore-unused */ $fixed)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
377
    {
378
        throw DBALException::notSupported('VARCHARs not supported by Platform.');
379
    }
380
381
    /**
382
     * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
383
     *
384
     * @param int  $length The length of the column.
385
     * @param bool $fixed  Whether the column length is fixed.
386
     *
387
     * @return string
388
     *
389
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
390
     */
391
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
0 ignored issues
show
Unused Code introduced by
The parameter $fixed is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

391
    protected function getBinaryTypeDeclarationSQLSnippet($length, /** @scrutinizer ignore-unused */ $fixed)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
392
    {
393
        throw DBALException::notSupported('BINARY/VARBINARY column types are not supported by this platform.');
394
    }
395
396
    /**
397
     * Returns the SQL snippet used to declare a CLOB column type.
398
     *
399
     * @param array $field
400
     *
401
     * @return string
402
     */
403
    abstract public function getClobTypeDeclarationSQL(array $field);
404
405
    /**
406
     * Returns the SQL Snippet used to declare a BLOB column type.
407
     *
408
     * @param array $field
409
     *
410
     * @return string
411
     */
412
    abstract public function getBlobTypeDeclarationSQL(array $field);
413
414
    /**
415
     * Gets the name of the platform.
416
     *
417
     * @return string
418
     */
419
    abstract public function getName();
420
421
    /**
422
     * Registers a doctrine type to be used in conjunction with a column type of this platform.
423
     *
424
     * @param string $dbType
425
     * @param string $doctrineType
426
     *
427
     * @throws \Doctrine\DBAL\DBALException If the type is not found.
428
     */
429 996
    public function registerDoctrineTypeMapping($dbType, $doctrineType)
430
    {
431 996
        if ($this->doctrineTypeMapping === null) {
432 990
            $this->initializeAllDoctrineTypeMappings();
433
        }
434
435 996
        if (!Types\Type::hasType($doctrineType)) {
436 324
            throw DBALException::typeNotFound($doctrineType);
437
        }
438
439 672
        $dbType = strtolower($dbType);
440 672
        $this->doctrineTypeMapping[$dbType] = $doctrineType;
441
442 672
        $doctrineType = Type::getType($doctrineType);
443
444 672
        if ($doctrineType->requiresSQLCommentHint($this)) {
445 324
            $this->markDoctrineTypeCommented($doctrineType);
446
        }
447 672
    }
448
449
    /**
450
     * Gets the Doctrine type that is mapped for the given database column type.
451
     *
452
     * @param string $dbType
453
     *
454
     * @return string
455
     *
456
     * @throws \Doctrine\DBAL\DBALException
457
     */
458 1960
    public function getDoctrineTypeMapping($dbType)
459
    {
460 1960
        if ($this->doctrineTypeMapping === null) {
461 378
            $this->initializeAllDoctrineTypeMappings();
462
        }
463
464 1960
        $dbType = strtolower($dbType);
465
466 1960
        if (!isset($this->doctrineTypeMapping[$dbType])) {
467 324
            throw new \Doctrine\DBAL\DBALException("Unknown database type ".$dbType." requested, " . get_class($this) . " may not support it.");
468
        }
469
470 1636
        return $this->doctrineTypeMapping[$dbType];
471
    }
472
473
    /**
474
     * Checks if a database type is currently supported by this platform.
475
     *
476
     * @param string $dbType
477
     *
478
     * @return bool
479
     */
480 456
    public function hasDoctrineTypeMappingFor($dbType)
481
    {
482 456
        if ($this->doctrineTypeMapping === null) {
483 414
            $this->initializeAllDoctrineTypeMappings();
484
        }
485
486 456
        $dbType = strtolower($dbType);
487
488 456
        return isset($this->doctrineTypeMapping[$dbType]);
489
    }
490
491
    /**
492
     * Initializes the Doctrine Type comments instance variable for in_array() checks.
493
     *
494
     * @return void
495
     */
496 15809
    protected function initializeCommentedDoctrineTypes()
497
    {
498 15809
        $this->doctrineTypeComments = [];
499
500 15809
        foreach (Type::getTypesMap() as $typeName => $className) {
501 15809
            $type = Type::getType($typeName);
502
503 15809
            if ($type->requiresSQLCommentHint($this)) {
504 15809
                $this->doctrineTypeComments[] = $typeName;
505
            }
506
        }
507 15809
    }
508
509
    /**
510
     * Is it necessary for the platform to add a parsable type comment to allow reverse engineering the given type?
511
     *
512
     * @param \Doctrine\DBAL\Types\Type $doctrineType
513
     *
514
     * @return bool
515
     */
516 18308
    public function isCommentedDoctrineType(Type $doctrineType)
517
    {
518 18308
        if ($this->doctrineTypeComments === null) {
519 15485
            $this->initializeCommentedDoctrineTypes();
520
        }
521
522 18308
        return in_array($doctrineType->getName(), $this->doctrineTypeComments);
523
    }
524
525
    /**
526
     * Marks this type as to be commented in ALTER TABLE and CREATE TABLE statements.
527
     *
528
     * @param string|\Doctrine\DBAL\Types\Type $doctrineType
529
     *
530
     * @return void
531
     */
532 324
    public function markDoctrineTypeCommented($doctrineType)
533
    {
534 324
        if ($this->doctrineTypeComments === null) {
535 324
            $this->initializeCommentedDoctrineTypes();
536
        }
537
538 324
        $this->doctrineTypeComments[] = $doctrineType instanceof Type ? $doctrineType->getName() : $doctrineType;
539 324
    }
540
541
    /**
542
     * Gets the comment to append to a column comment that helps parsing this type in reverse engineering.
543
     *
544
     * @param \Doctrine\DBAL\Types\Type $doctrineType
545
     *
546
     * @return string
547
     */
548 767
    public function getDoctrineTypeComment(Type $doctrineType)
549
    {
550 767
        return '(DC2Type:' . $doctrineType->getName() . ')';
551
    }
552
553
    /**
554
     * Gets the comment of a passed column modified by potential doctrine type comment hints.
555
     *
556
     * @param \Doctrine\DBAL\Schema\Column $column
557
     *
558
     * @return string
559
     */
560 9903
    protected function getColumnComment(Column $column)
561
    {
562 9903
        $comment = $column->getComment();
563
564 9903
        if ($this->isCommentedDoctrineType($column->getType())) {
565 767
            $comment .= $this->getDoctrineTypeComment($column->getType());
566
        }
567
568 9903
        return $comment;
569
    }
570
571
    /**
572
     * Gets the character used for identifier quoting.
573
     *
574
     * @return string
575
     */
576 5429
    public function getIdentifierQuoteCharacter()
577
    {
578 5429
        return '"';
579
    }
580
581
    /**
582
     * Gets the string portion that starts an SQL comment.
583
     *
584
     * @return string
585
     */
586
    public function getSqlCommentStartString()
587
    {
588
        return "--";
589
    }
590
591
    /**
592
     * Gets the string portion that ends an SQL comment.
593
     *
594
     * @return string
595
     */
596
    public function getSqlCommentEndString()
597
    {
598
        return "\n";
599
    }
600
601
    /**
602
     * Gets the maximum length of a char field.
603
     */
604 810
    public function getCharMaxLength() : int
605
    {
606 810
        return $this->getVarcharMaxLength();
607
    }
608
609
    /**
610
     * Gets the maximum length of a varchar field.
611
     *
612
     * @return int
613
     */
614 2412
    public function getVarcharMaxLength()
615
    {
616 2412
        return 4000;
617
    }
618
619
    /**
620
     * Gets the default length of a varchar field.
621
     *
622
     * @return int
623
     */
624 1272
    public function getVarcharDefaultLength()
625
    {
626 1272
        return 255;
627
    }
628
629
    /**
630
     * Gets the maximum length of a binary field.
631
     *
632
     * @return int
633
     */
634
    public function getBinaryMaxLength()
635
    {
636
        return 4000;
637
    }
638
639
    /**
640
     * Gets the default length of a binary field.
641
     *
642
     * @return int
643
     */
644 262
    public function getBinaryDefaultLength()
645
    {
646 262
        return 255;
647
    }
648
649
    /**
650
     * Gets all SQL wildcard characters of the platform.
651
     *
652
     * @return array
653
     */
654
    public function getWildcards()
655
    {
656
        return ['%', '_'];
657
    }
658
659
    /**
660
     * Returns the regular expression operator.
661
     *
662
     * @return string
663
     *
664
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
665
     */
666 90
    public function getRegexpExpression()
667
    {
668 90
        throw DBALException::notSupported(__METHOD__);
669
    }
670
671
    /**
672
     * Returns the global unique identifier expression.
673
     *
674
     * @return string
675
     *
676
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
677
     */
678
    public function getGuidExpression()
679
    {
680
        throw DBALException::notSupported(__METHOD__);
681
    }
682
683
    /**
684
     * Returns the SQL snippet to get the average value of a column.
685
     *
686
     * @param string $column The column to use.
687
     *
688
     * @return string Generated SQL including an AVG aggregate function.
689
     */
690
    public function getAvgExpression($column)
691
    {
692
        return 'AVG(' . $column . ')';
693
    }
694
695
    /**
696
     * Returns the SQL snippet to get the number of rows (without a NULL value) of a column.
697
     *
698
     * If a '*' is used instead of a column the number of selected rows is returned.
699
     *
700
     * @param string|integer $column The column to use.
701
     *
702
     * @return string Generated SQL including a COUNT aggregate function.
703
     */
704
    public function getCountExpression($column)
705
    {
706
        return 'COUNT(' . $column . ')';
707
    }
708
709
    /**
710
     * Returns the SQL snippet to get the highest value of a column.
711
     *
712
     * @param string $column The column to use.
713
     *
714
     * @return string Generated SQL including a MAX aggregate function.
715
     */
716
    public function getMaxExpression($column)
717
    {
718
        return 'MAX(' . $column . ')';
719
    }
720
721
    /**
722
     * Returns the SQL snippet to get the lowest value of a column.
723
     *
724
     * @param string $column The column to use.
725
     *
726
     * @return string Generated SQL including a MIN aggregate function.
727
     */
728
    public function getMinExpression($column)
729
    {
730
        return 'MIN(' . $column . ')';
731
    }
732
733
    /**
734
     * Returns the SQL snippet to get the total sum of a column.
735
     *
736
     * @param string $column The column to use.
737
     *
738
     * @return string Generated SQL including a SUM aggregate function.
739
     */
740
    public function getSumExpression($column)
741
    {
742
        return 'SUM(' . $column . ')';
743
    }
744
745
    // scalar functions
746
747
    /**
748
     * Returns the SQL snippet to get the md5 sum of a field.
749
     *
750
     * Note: Not SQL92, but common functionality.
751
     *
752
     * @param string $column
753
     *
754
     * @return string
755
     */
756
    public function getMd5Expression($column)
757
    {
758
        return 'MD5(' . $column . ')';
759
    }
760
761
    /**
762
     * Returns the SQL snippet to get the length of a text field.
763
     *
764
     * @param string $column
765
     *
766
     * @return string
767
     */
768
    public function getLengthExpression($column)
769
    {
770
        return 'LENGTH(' . $column . ')';
771
    }
772
773
    /**
774
     * Returns the SQL snippet to get the squared value of a column.
775
     *
776
     * @param string $column The column to use.
777
     *
778
     * @return string Generated SQL including an SQRT aggregate function.
779
     */
780
    public function getSqrtExpression($column)
781
    {
782
        return 'SQRT(' . $column . ')';
783
    }
784
785
    /**
786
     * Returns the SQL snippet to round a numeric field to the number of decimals specified.
787
     *
788
     * @param string $column
789
     * @param int    $decimals
790
     *
791
     * @return string
792
     */
793
    public function getRoundExpression($column, $decimals = 0)
794
    {
795
        return 'ROUND(' . $column . ', ' . $decimals . ')';
796
    }
797
798
    /**
799
     * Returns the SQL snippet to get the remainder of the division operation $expression1 / $expression2.
800
     *
801
     * @param string $expression1
802
     * @param string $expression2
803
     *
804
     * @return string
805
     */
806
    public function getModExpression($expression1, $expression2)
807
    {
808
        return 'MOD(' . $expression1 . ', ' . $expression2 . ')';
809
    }
810
811
    /**
812
     * Returns the SQL snippet to trim a string.
813
     *
814
     * @param string      $str  The expression to apply the trim to.
815
     * @param int         $mode The position of the trim (leading/trailing/both).
816
     * @param string|bool $char The char to trim, has to be quoted already. Defaults to space.
817
     *
818
     * @return string
819
     */
820 540
    public function getTrimExpression($str, $mode = TrimMode::UNSPECIFIED, $char = false)
821
    {
822 540
        $expression = '';
823
824 36
        switch ($mode) {
825 504
            case TrimMode::LEADING:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
826 135
                $expression = 'LEADING ';
827 135
                break;
828
829 378
            case TrimMode::TRAILING:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
830 135
                $expression = 'TRAILING ';
831 135
                break;
832
833 252
            case TrimMode::BOTH:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
834 135
                $expression = 'BOTH ';
835 135
                break;
836
        }
837
838 540
        if ($char !== false) {
839 420
            $expression .= $char . ' ';
0 ignored issues
show
Bug introduced by
Are you sure $char of type string|true can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

839
            $expression .= /** @scrutinizer ignore-type */ $char . ' ';
Loading history...
840
        }
841
842 540
        if ($mode || $char !== false) {
843 510
            $expression .= 'FROM ';
844
        }
845
846 540
        return 'TRIM(' . $expression . $str . ')';
847
    }
848
849
    /**
850
     * Returns the SQL snippet to trim trailing space characters from the expression.
851
     *
852
     * @param string $str Literal string or column name.
853
     *
854
     * @return string
855
     */
856 72
    public function getRtrimExpression($str)
857
    {
858 72
        return 'RTRIM(' . $str . ')';
859
    }
860
861
    /**
862
     * Returns the SQL snippet to trim leading space characters from the expression.
863
     *
864
     * @param string $str Literal string or column name.
865
     *
866
     * @return string
867
     */
868 72
    public function getLtrimExpression($str)
869
    {
870 72
        return 'LTRIM(' . $str . ')';
871
    }
872
873
    /**
874
     * Returns the SQL snippet to change all characters from the expression to uppercase,
875
     * according to the current character set mapping.
876
     *
877
     * @param string $str Literal string or column name.
878
     *
879
     * @return string
880
     */
881
    public function getUpperExpression($str)
882
    {
883
        return 'UPPER(' . $str . ')';
884
    }
885
886
    /**
887
     * Returns the SQL snippet to change all characters from the expression to lowercase,
888
     * according to the current character set mapping.
889
     *
890
     * @param string $str Literal string or column name.
891
     *
892
     * @return string
893
     */
894
    public function getLowerExpression($str)
895
    {
896
        return 'LOWER(' . $str . ')';
897
    }
898
899
    /**
900
     * Returns the SQL snippet to get the position of the first occurrence of substring $substr in string $str.
901
     *
902
     * @param string   $str      Literal string.
903
     * @param string   $substr   Literal string to find.
904
     * @param int|bool $startPos Position to start at, beginning of string by default.
905
     *
906
     * @return string
907
     *
908
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
909
     */
910
    public function getLocateExpression($str, $substr, $startPos = false)
0 ignored issues
show
Unused Code introduced by
The parameter $startPos is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

910
    public function getLocateExpression($str, $substr, /** @scrutinizer ignore-unused */ $startPos = false)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $substr is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

910
    public function getLocateExpression($str, /** @scrutinizer ignore-unused */ $substr, $startPos = false)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
911
    {
912
        throw DBALException::notSupported(__METHOD__);
913
    }
914
915
    /**
916
     * Returns the SQL snippet to get the current system date.
917
     *
918
     * @return string
919
     */
920
    public function getNowExpression()
921
    {
922
        return 'NOW()';
923
    }
924
925
    /**
926
     * Returns a SQL snippet to get a substring inside an SQL statement.
927
     *
928
     * Note: Not SQL92, but common functionality.
929
     *
930
     * SQLite only supports the 2 parameter variant of this function.
931
     *
932
     * @param string   $value  An sql string literal or column name/alias.
933
     * @param int      $from   Where to start the substring portion.
934
     * @param int|null $length The substring portion length.
935
     *
936
     * @return string
937
     */
938
    public function getSubstringExpression($value, $from, $length = null)
939
    {
940
        if ($length === null) {
941
            return 'SUBSTRING(' . $value . ' FROM ' . $from . ')';
942
        }
943
944
        return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $length . ')';
945
    }
946
947
    /**
948
     * Returns a SQL snippet to concatenate the given expressions.
949
     *
950
     * Accepts an arbitrary number of string parameters. Each parameter must contain an expression.
951
     *
952
     * @return string
953
     */
954 108
    public function getConcatExpression()
955
    {
956 108
        return join(' || ', func_get_args());
957
    }
958
959
    /**
960
     * Returns the SQL for a logical not.
961
     *
962
     * Example:
963
     * <code>
964
     * $q = new Doctrine_Query();
965
     * $e = $q->expr;
966
     * $q->select('*')->from('table')
967
     *   ->where($e->eq('id', $e->not('null'));
968
     * </code>
969
     *
970
     * @param string $expression
971
     *
972
     * @return string The logical expression.
973
     */
974
    public function getNotExpression($expression)
975
    {
976
        return 'NOT(' . $expression . ')';
977
    }
978
979
    /**
980
     * Returns the SQL that checks if an expression is null.
981
     *
982
     * @param string $expression The expression that should be compared to null.
983
     *
984
     * @return string The logical expression.
985
     */
986 72
    public function getIsNullExpression($expression)
987
    {
988 72
        return $expression . ' IS NULL';
989
    }
990
991
    /**
992
     * Returns the SQL that checks if an expression is not null.
993
     *
994
     * @param string $expression The expression that should be compared to null.
995
     *
996
     * @return string The logical expression.
997
     */
998
    public function getIsNotNullExpression($expression)
999
    {
1000
        return $expression . ' IS NOT NULL';
1001
    }
1002
1003
    /**
1004
     * Returns the SQL that checks if an expression evaluates to a value between two values.
1005
     *
1006
     * The parameter $expression is checked if it is between $value1 and $value2.
1007
     *
1008
     * Note: There is a slight difference in the way BETWEEN works on some databases.
1009
     * http://www.w3schools.com/sql/sql_between.asp. If you want complete database
1010
     * independence you should avoid using between().
1011
     *
1012
     * @param string $expression The value to compare to.
1013
     * @param string $value1     The lower value to compare with.
1014
     * @param string $value2     The higher value to compare with.
1015
     *
1016
     * @return string The logical expression.
1017
     */
1018
    public function getBetweenExpression($expression, $value1, $value2)
1019
    {
1020
        return $expression . ' BETWEEN ' .$value1 . ' AND ' . $value2;
1021
    }
1022
1023
    /**
1024
     * Returns the SQL to get the arccosine of a value.
1025
     *
1026
     * @param string $value
1027
     *
1028
     * @return string
1029
     */
1030
    public function getAcosExpression($value)
1031
    {
1032
        return 'ACOS(' . $value . ')';
1033
    }
1034
1035
    /**
1036
     * Returns the SQL to get the sine of a value.
1037
     *
1038
     * @param string $value
1039
     *
1040
     * @return string
1041
     */
1042
    public function getSinExpression($value)
1043
    {
1044
        return 'SIN(' . $value . ')';
1045
    }
1046
1047
    /**
1048
     * Returns the SQL to get the PI value.
1049
     *
1050
     * @return string
1051
     */
1052
    public function getPiExpression()
1053
    {
1054
        return 'PI()';
1055
    }
1056
1057
    /**
1058
     * Returns the SQL to get the cosine of a value.
1059
     *
1060
     * @param string $value
1061
     *
1062
     * @return string
1063
     */
1064
    public function getCosExpression($value)
1065
    {
1066
        return 'COS(' . $value . ')';
1067
    }
1068
1069
    /**
1070
     * Returns the SQL to calculate the difference in days between the two passed dates.
1071
     *
1072
     * Computes diff = date1 - date2.
1073
     *
1074
     * @param string $date1
1075
     * @param string $date2
1076
     *
1077
     * @return string
1078
     *
1079
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1080
     */
1081
    public function getDateDiffExpression($date1, $date2)
0 ignored issues
show
Unused Code introduced by
The parameter $date1 is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1081
    public function getDateDiffExpression(/** @scrutinizer ignore-unused */ $date1, $date2)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $date2 is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1081
    public function getDateDiffExpression($date1, /** @scrutinizer ignore-unused */ $date2)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1082
    {
1083
        throw DBALException::notSupported(__METHOD__);
1084
    }
1085
1086
    /**
1087
     * Returns the SQL to add the number of given seconds to a date.
1088
     *
1089
     * @param string $date
1090
     * @param int    $seconds
1091
     *
1092
     * @return string
1093
     *
1094
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1095
     */
1096 108
    public function getDateAddSecondsExpression($date, $seconds)
1097
    {
1098 108
        return $this->getDateArithmeticIntervalExpression($date, '+', $seconds, DateIntervalUnit::SECOND);
1099
    }
1100
1101
    /**
1102
     * Returns the SQL to subtract the number of given seconds from a date.
1103
     *
1104
     * @param string $date
1105
     * @param int    $seconds
1106
     *
1107
     * @return string
1108
     *
1109
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1110
     */
1111 108
    public function getDateSubSecondsExpression($date, $seconds)
1112
    {
1113 108
        return $this->getDateArithmeticIntervalExpression($date, '-', $seconds, DateIntervalUnit::SECOND);
1114
    }
1115
1116
    /**
1117
     * Returns the SQL to add the number of given minutes to a date.
1118
     *
1119
     * @param string $date
1120
     * @param int    $minutes
1121
     *
1122
     * @return string
1123
     *
1124
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1125
     */
1126 108
    public function getDateAddMinutesExpression($date, $minutes)
1127
    {
1128 108
        return $this->getDateArithmeticIntervalExpression($date, '+', $minutes, DateIntervalUnit::MINUTE);
1129
    }
1130
1131
    /**
1132
     * Returns the SQL to subtract the number of given minutes from a date.
1133
     *
1134
     * @param string $date
1135
     * @param int    $minutes
1136
     *
1137
     * @return string
1138
     *
1139
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1140
     */
1141 108
    public function getDateSubMinutesExpression($date, $minutes)
1142
    {
1143 108
        return $this->getDateArithmeticIntervalExpression($date, '-', $minutes, DateIntervalUnit::MINUTE);
1144
    }
1145
1146
    /**
1147
     * Returns the SQL to add the number of given hours to a date.
1148
     *
1149
     * @param string $date
1150
     * @param int    $hours
1151
     *
1152
     * @return string
1153
     *
1154
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1155
     */
1156 108
    public function getDateAddHourExpression($date, $hours)
1157
    {
1158 108
        return $this->getDateArithmeticIntervalExpression($date, '+', $hours, DateIntervalUnit::HOUR);
1159
    }
1160
1161
    /**
1162
     * Returns the SQL to subtract the number of given hours to a date.
1163
     *
1164
     * @param string $date
1165
     * @param int    $hours
1166
     *
1167
     * @return string
1168
     *
1169
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1170
     */
1171 108
    public function getDateSubHourExpression($date, $hours)
1172
    {
1173 108
        return $this->getDateArithmeticIntervalExpression($date, '-', $hours, DateIntervalUnit::HOUR);
1174
    }
1175
1176
    /**
1177
     * Returns the SQL to add the number of given days to a date.
1178
     *
1179
     * @param string $date
1180
     * @param int    $days
1181
     *
1182
     * @return string
1183
     *
1184
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1185
     */
1186 144
    public function getDateAddDaysExpression($date, $days)
1187
    {
1188 144
        return $this->getDateArithmeticIntervalExpression($date, '+', $days, DateIntervalUnit::DAY);
1189
    }
1190
1191
    /**
1192
     * Returns the SQL to subtract the number of given days to a date.
1193
     *
1194
     * @param string $date
1195
     * @param int    $days
1196
     *
1197
     * @return string
1198
     *
1199
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1200
     */
1201 108
    public function getDateSubDaysExpression($date, $days)
1202
    {
1203 108
        return $this->getDateArithmeticIntervalExpression($date, '-', $days, DateIntervalUnit::DAY);
1204
    }
1205
1206
    /**
1207
     * Returns the SQL to add the number of given weeks to a date.
1208
     *
1209
     * @param string $date
1210
     * @param int    $weeks
1211
     *
1212
     * @return string
1213
     *
1214
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1215
     */
1216 108
    public function getDateAddWeeksExpression($date, $weeks)
1217
    {
1218 108
        return $this->getDateArithmeticIntervalExpression($date, '+', $weeks, DateIntervalUnit::WEEK);
1219
    }
1220
1221
    /**
1222
     * Returns the SQL to subtract the number of given weeks from a date.
1223
     *
1224
     * @param string $date
1225
     * @param int    $weeks
1226
     *
1227
     * @return string
1228
     *
1229
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1230
     */
1231 108
    public function getDateSubWeeksExpression($date, $weeks)
1232
    {
1233 108
        return $this->getDateArithmeticIntervalExpression($date, '-', $weeks, DateIntervalUnit::WEEK);
1234
    }
1235
1236
    /**
1237
     * Returns the SQL to add the number of given months to a date.
1238
     *
1239
     * @param string $date
1240
     * @param int    $months
1241
     *
1242
     * @return string
1243
     *
1244
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1245
     */
1246 108
    public function getDateAddMonthExpression($date, $months)
1247
    {
1248 108
        return $this->getDateArithmeticIntervalExpression($date, '+', $months, DateIntervalUnit::MONTH);
1249
    }
1250
1251
    /**
1252
     * Returns the SQL to subtract the number of given months to a date.
1253
     *
1254
     * @param string $date
1255
     * @param int    $months
1256
     *
1257
     * @return string
1258
     *
1259
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1260
     */
1261 108
    public function getDateSubMonthExpression($date, $months)
1262
    {
1263 108
        return $this->getDateArithmeticIntervalExpression($date, '-', $months, DateIntervalUnit::MONTH);
1264
    }
1265
1266
    /**
1267
     * Returns the SQL to add the number of given quarters to a date.
1268
     *
1269
     * @param string $date
1270
     * @param int    $quarters
1271
     *
1272
     * @return string
1273
     *
1274
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1275
     */
1276 108
    public function getDateAddQuartersExpression($date, $quarters)
1277
    {
1278 108
        return $this->getDateArithmeticIntervalExpression($date, '+', $quarters, DateIntervalUnit::QUARTER);
1279
    }
1280
1281
    /**
1282
     * Returns the SQL to subtract the number of given quarters from a date.
1283
     *
1284
     * @param string $date
1285
     * @param int    $quarters
1286
     *
1287
     * @return string
1288
     *
1289
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1290
     */
1291 108
    public function getDateSubQuartersExpression($date, $quarters)
1292
    {
1293 108
        return $this->getDateArithmeticIntervalExpression($date, '-', $quarters, DateIntervalUnit::QUARTER);
1294
    }
1295
1296
    /**
1297
     * Returns the SQL to add the number of given years to a date.
1298
     *
1299
     * @param string $date
1300
     * @param int    $years
1301
     *
1302
     * @return string
1303
     *
1304
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1305
     */
1306 108
    public function getDateAddYearsExpression($date, $years)
1307
    {
1308 108
        return $this->getDateArithmeticIntervalExpression($date, '+', $years, DateIntervalUnit::YEAR);
1309
    }
1310
1311
    /**
1312
     * Returns the SQL to subtract the number of given years from a date.
1313
     *
1314
     * @param string $date
1315
     * @param int    $years
1316
     *
1317
     * @return string
1318
     *
1319
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1320
     */
1321 108
    public function getDateSubYearsExpression($date, $years)
1322
    {
1323 108
        return $this->getDateArithmeticIntervalExpression($date, '-', $years, DateIntervalUnit::YEAR);
1324
    }
1325
1326
    /**
1327
     * Returns the SQL for a date arithmetic expression.
1328
     *
1329
     * @param string $date     The column or literal representing a date to perform the arithmetic operation on.
1330
     * @param string $operator The arithmetic operator (+ or -).
1331
     * @param int    $interval The interval that shall be calculated into the date.
1332
     * @param string $unit     The unit of the interval that shall be calculated into the date.
1333
     *                         One of the DATE_INTERVAL_UNIT_* constants.
1334
     *
1335
     * @return string
1336
     *
1337
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1338
     */
1339
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
0 ignored issues
show
Unused Code introduced by
The parameter $unit is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1339
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, /** @scrutinizer ignore-unused */ $unit)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $operator is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1339
    protected function getDateArithmeticIntervalExpression($date, /** @scrutinizer ignore-unused */ $operator, $interval, $unit)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $interval is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1339
    protected function getDateArithmeticIntervalExpression($date, $operator, /** @scrutinizer ignore-unused */ $interval, $unit)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1340
    {
1341
        throw DBALException::notSupported(__METHOD__);
1342
    }
1343
1344
    /**
1345
     * Returns the SQL bit AND comparison expression.
1346
     *
1347
     * @param string $value1
1348
     * @param string $value2
1349
     *
1350
     * @return string
1351
     */
1352 304
    public function getBitAndComparisonExpression($value1, $value2)
1353
    {
1354 304
        return '(' . $value1 . ' & ' . $value2 . ')';
1355
    }
1356
1357
    /**
1358
     * Returns the SQL bit OR comparison expression.
1359
     *
1360
     * @param string $value1
1361
     * @param string $value2
1362
     *
1363
     * @return string
1364
     */
1365 304
    public function getBitOrComparisonExpression($value1, $value2)
1366
    {
1367 304
        return '(' . $value1 . ' | ' . $value2 . ')';
1368
    }
1369
1370
    /**
1371
     * Returns the FOR UPDATE expression.
1372
     *
1373
     * @return string
1374
     */
1375 28
    public function getForUpdateSQL()
1376
    {
1377 28
        return 'FOR UPDATE';
1378
    }
1379
1380
    /**
1381
     * Honors that some SQL vendors such as MsSql use table hints for locking instead of the ANSI SQL FOR UPDATE specification.
1382
     *
1383
     * @param string   $fromClause The FROM clause to append the hint for the given lock mode to.
1384
     * @param int|null $lockMode   One of the Doctrine\DBAL\LockMode::* constants. If null is given, nothing will
1385
     *                             be appended to the FROM clause.
1386
     *
1387
     * @return string
1388
     */
1389 30
    public function appendLockHint($fromClause, $lockMode)
0 ignored issues
show
Unused Code introduced by
The parameter $lockMode is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1389
    public function appendLockHint($fromClause, /** @scrutinizer ignore-unused */ $lockMode)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1390
    {
1391 30
        return $fromClause;
1392
    }
1393
1394
    /**
1395
     * Returns the SQL snippet to append to any SELECT statement which locks rows in shared read lock.
1396
     *
1397
     * This defaults to the ANSI SQL "FOR UPDATE", which is an exclusive lock (Write). Some database
1398
     * vendors allow to lighten this constraint up to be a real read lock.
1399
     *
1400
     * @return string
1401
     */
1402
    public function getReadLockSQL()
1403
    {
1404
        return $this->getForUpdateSQL();
1405
    }
1406
1407
    /**
1408
     * Returns the SQL snippet to append to any SELECT statement which obtains an exclusive lock on the rows.
1409
     *
1410
     * The semantics of this lock mode should equal the SELECT .. FOR UPDATE of the ANSI SQL standard.
1411
     *
1412
     * @return string
1413
     */
1414 34
    public function getWriteLockSQL()
1415
    {
1416 34
        return $this->getForUpdateSQL();
1417
    }
1418
1419
    /**
1420
     * Returns the SQL snippet to drop an existing database.
1421
     *
1422
     * @param string $database The name of the database that should be dropped.
1423
     *
1424
     * @return string
1425
     */
1426 102
    public function getDropDatabaseSQL($database)
1427
    {
1428 102
        return 'DROP DATABASE ' . $database;
1429
    }
1430
1431
    /**
1432
     * Returns the SQL snippet to drop an existing table.
1433
     *
1434
     * @param \Doctrine\DBAL\Schema\Table|string $table
1435
     *
1436
     * @return string
1437
     *
1438
     * @throws \InvalidArgumentException
1439
     */
1440 2776
    public function getDropTableSQL($table)
1441
    {
1442 2776
        $tableArg = $table;
1443
1444 2776
        if ($table instanceof Table) {
1445 265
            $table = $table->getQuotedName($this);
1446 2524
        } elseif (!is_string($table)) {
0 ignored issues
show
introduced by
The condition is_string($table) is always true.
Loading history...
1447
            throw new \InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.');
1448
        }
1449
1450 2776
        if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaDropTable)) {
1451 324
            $eventArgs = new SchemaDropTableEventArgs($tableArg, $this);
1452 324
            $this->_eventManager->dispatchEvent(Events::onSchemaDropTable, $eventArgs);
1453
1454 324
            if ($eventArgs->isDefaultPrevented()) {
1455
                return $eventArgs->getSql();
1456
            }
1457
        }
1458
1459 2776
        return 'DROP TABLE ' . $table;
1460
    }
1461
1462
    /**
1463
     * Returns the SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction.
1464
     *
1465
     * @param \Doctrine\DBAL\Schema\Table|string $table
1466
     *
1467
     * @return string
1468
     */
1469 20
    public function getDropTemporaryTableSQL($table)
1470
    {
1471 20
        return $this->getDropTableSQL($table);
1472
    }
1473
1474
    /**
1475
     * Returns the SQL to drop an index from a table.
1476
     *
1477
     * @param \Doctrine\DBAL\Schema\Index|string $index
1478
     * @param \Doctrine\DBAL\Schema\Table|string $table
1479
     *
1480
     * @return string
1481
     *
1482
     * @throws \InvalidArgumentException
1483
     */
1484 170
    public function getDropIndexSQL($index, $table = null)
1485
    {
1486 170
        if ($index instanceof Index) {
1487 161
            $index = $index->getQuotedName($this);
1488 9
        } elseif (!is_string($index)) {
0 ignored issues
show
introduced by
The condition is_string($index) is always true.
Loading history...
1489
            throw new \InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.');
1490
        }
1491
1492 170
        return 'DROP INDEX ' . $index;
1493
    }
1494
1495
    /**
1496
     * Returns the SQL to drop a constraint.
1497
     *
1498
     * @param \Doctrine\DBAL\Schema\Constraint|string $constraint
1499
     * @param \Doctrine\DBAL\Schema\Table|string      $table
1500
     *
1501
     * @return string
1502
     */
1503 749
    public function getDropConstraintSQL($constraint, $table)
1504
    {
1505 749
        if (! $constraint instanceof Constraint) {
1506 563
            $constraint = new Identifier($constraint);
1507
        }
1508
1509 749
        if (! $table instanceof Table) {
1510 749
            $table = new Identifier($table);
1511
        }
1512
1513 749
        $constraint = $constraint->getQuotedName($this);
1514 749
        $table = $table->getQuotedName($this);
1515
1516 749
        return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $constraint;
1517
    }
1518
1519
    /**
1520
     * Returns the SQL to drop a foreign key.
1521
     *
1522
     * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint|string $foreignKey
1523
     * @param \Doctrine\DBAL\Schema\Table|string                $table
1524
     *
1525
     * @return string
1526
     */
1527 391
    public function getDropForeignKeySQL($foreignKey, $table)
1528
    {
1529 391
        if (! $foreignKey instanceof ForeignKeyConstraint) {
1530 144
            $foreignKey = new Identifier($foreignKey);
1531
        }
1532
1533 391
        if (! $table instanceof Table) {
1534 391
            $table = new Identifier($table);
1535
        }
1536
1537 391
        $foreignKey = $foreignKey->getQuotedName($this);
1538 391
        $table = $table->getQuotedName($this);
1539
1540 391
        return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $foreignKey;
1541
    }
1542
1543
    /**
1544
     * Returns the SQL statement(s) to create a table with the specified name, columns and constraints
1545
     * on this platform.
1546
     *
1547
     * @param \Doctrine\DBAL\Schema\Table $table
1548
     * @param int                         $createFlags
1549
     *
1550
     * @return array The sequence of SQL statements.
1551
     *
1552
     * @throws \Doctrine\DBAL\DBALException
1553
     * @throws \InvalidArgumentException
1554
     */
1555 7779
    public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDEXES)
1556
    {
1557 7779
        if ( ! is_int($createFlags)) {
0 ignored issues
show
introduced by
The condition is_int($createFlags) is always true.
Loading history...
1558
            throw new \InvalidArgumentException("Second argument of AbstractPlatform::getCreateTableSQL() has to be integer.");
1559
        }
1560
1561 7779
        if (count($table->getColumns()) === 0) {
1562 324
            throw DBALException::noColumnsSpecifiedForTable($table->getName());
1563
        }
1564
1565 7455
        $tableName = $table->getQuotedName($this);
1566 7455
        $options = $table->getOptions();
1567 7455
        $options['uniqueConstraints'] = [];
1568 7455
        $options['indexes'] = [];
1569 7455
        $options['primary'] = [];
1570
1571 7455
        if (($createFlags&self::CREATE_INDEXES) > 0) {
1572 7041
            foreach ($table->getIndexes() as $index) {
1573
                /* @var $index Index */
1574 4748
                if ($index->isPrimary()) {
1575 3553
                    $options['primary']       = $index->getQuotedColumns($this);
1576 3553
                    $options['primary_index'] = $index;
1577
                } else {
1578 4748
                    $options['indexes'][$index->getQuotedName($this)] = $index;
1579
                }
1580
            }
1581
        }
1582
1583 7455
        $columnSql = [];
1584 7455
        $columns = [];
1585
1586 7455
        foreach ($table->getColumns() as $column) {
1587
            /* @var \Doctrine\DBAL\Schema\Column $column */
1588
1589 7455
            if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn)) {
1590 324
                $eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this);
1591 324
                $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs);
1592
1593 324
                $columnSql = array_merge($columnSql, $eventArgs->getSql());
1594
1595 324
                if ($eventArgs->isDefaultPrevented()) {
1596
                    continue;
1597
                }
1598
            }
1599
1600 7455
            $columnData = $column->toArray();
1601 7455
            $columnData['name'] = $column->getQuotedName($this);
1602 7455
            $columnData['version'] = $column->hasPlatformOption("version") ? $column->getPlatformOption('version') : false;
1603 7455
            $columnData['comment'] = $this->getColumnComment($column);
1604
1605 7455
            if ($columnData['type'] instanceof Types\StringType && $columnData['length'] === null) {
1606 2814
                $columnData['length'] = 255;
1607
            }
1608
1609 7455
            if (in_array($column->getName(), $options['primary'])) {
1610 3193
                $columnData['primary'] = true;
1611
            }
1612
1613 7455
            $columns[$columnData['name']] = $columnData;
1614
        }
1615
1616 7455
        if (($createFlags&self::CREATE_FOREIGNKEYS) > 0) {
1617 3443
            $options['foreignKeys'] = [];
1618 3443
            foreach ($table->getForeignKeys() as $fkConstraint) {
1619 692
                $options['foreignKeys'][] = $fkConstraint;
1620
            }
1621
        }
1622
1623 7455
        if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) {
1624 324
            $eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this);
1625 324
            $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs);
1626
1627 324
            if ($eventArgs->isDefaultPrevented()) {
1628
                return array_merge($eventArgs->getSql(), $columnSql);
1629
            }
1630
        }
1631
1632 7455
        $sql = $this->_getCreateTableSQL($tableName, $columns, $options);
1633 7455
        if ($this->supportsCommentOnStatement()) {
1634 3739
            foreach ($table->getColumns() as $column) {
1635 3739
                $comment = $this->getColumnComment($column);
1636
1637 3739
                if (null !== $comment && '' !== $comment) {
1638 3739
                    $sql[] = $this->getCommentOnColumnSQL($tableName, $column->getQuotedName($this), $comment);
1639
                }
1640
            }
1641
        }
1642
1643 7455
        return array_merge($sql, $columnSql);
1644
    }
1645
1646
    /**
1647
     * @param string $tableName
1648
     * @param string $columnName
1649
     * @param string $comment
1650
     *
1651
     * @return string
1652
     */
1653 676
    public function getCommentOnColumnSQL($tableName, $columnName, $comment)
1654
    {
1655 676
        $tableName = new Identifier($tableName);
1656 676
        $columnName = new Identifier($columnName);
1657 676
        $comment = $this->quoteStringLiteral($comment);
1658
1659 676
        return "COMMENT ON COLUMN " . $tableName->getQuotedName($this) . "." . $columnName->getQuotedName($this) .
1660 676
            " IS " . $comment;
1661
    }
1662
1663
    /**
1664
     * Returns the SQL to create inline comment on a column.
1665
     *
1666
     * @param string $comment
1667
     *
1668
     * @return string
1669
     *
1670
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1671
     */
1672 972
    public function getInlineColumnCommentSQL($comment)
1673
    {
1674 972
        if (! $this->supportsInlineColumnComments()) {
1675 252
            throw DBALException::notSupported(__METHOD__);
1676
        }
1677
1678 720
        return "COMMENT " . $this->quoteStringLiteral($comment);
1679
    }
1680
1681
    /**
1682
     * Returns the SQL used to create a table.
1683
     *
1684
     * @param string $tableName
1685
     * @param array  $columns
1686
     * @param array  $options
1687
     *
1688
     * @return array
1689
     */
1690 671
    protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
1691
    {
1692 671
        $columnListSql = $this->getColumnDeclarationListSQL($columns);
1693
1694 671
        if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
1695
            foreach ($options['uniqueConstraints'] as $name => $definition) {
1696
                $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
1697
            }
1698
        }
1699
1700 671
        if (isset($options['primary']) && ! empty($options['primary'])) {
1701 351
            $columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')';
1702
        }
1703
1704 671
        if (isset($options['indexes']) && ! empty($options['indexes'])) {
1705
            foreach ($options['indexes'] as $index => $definition) {
1706
                $columnListSql .= ', ' . $this->getIndexDeclarationSQL($index, $definition);
1707
            }
1708
        }
1709
1710 671
        $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql;
1711
1712 671
        $check = $this->getCheckDeclarationSQL($columns);
1713 671
        if ( ! empty($check)) {
1714 18
            $query .= ', ' . $check;
1715
        }
1716 671
        $query .= ')';
1717
1718 671
        $sql[] = $query;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$sql was never initialized. Although not strictly required by PHP, it is generally a good practice to add $sql = array(); before regardless.
Loading history...
1719
1720 671
        if (isset($options['foreignKeys'])) {
1721 300
            foreach ((array) $options['foreignKeys'] as $definition) {
1722 67
                $sql[] = $this->getCreateForeignKeySQL($definition, $tableName);
1723
            }
1724
        }
1725
1726 671
        return $sql;
1727
    }
1728
1729
    /**
1730
     * @return string
1731
     */
1732 28
    public function getCreateTemporaryTableSnippetSQL()
1733
    {
1734 28
        return "CREATE TEMPORARY TABLE";
1735
    }
1736
1737
    /**
1738
     * Returns the SQL to create a sequence on this platform.
1739
     *
1740
     * @param \Doctrine\DBAL\Schema\Sequence $sequence
1741
     *
1742
     * @return string
1743
     *
1744
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1745
     */
1746
    public function getCreateSequenceSQL(Sequence $sequence)
0 ignored issues
show
Unused Code introduced by
The parameter $sequence is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1746
    public function getCreateSequenceSQL(/** @scrutinizer ignore-unused */ Sequence $sequence)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1747
    {
1748
        throw DBALException::notSupported(__METHOD__);
1749
    }
1750
1751
    /**
1752
     * Returns the SQL to change a sequence on this platform.
1753
     *
1754
     * @param \Doctrine\DBAL\Schema\Sequence $sequence
1755
     *
1756
     * @return string
1757
     *
1758
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1759
     */
1760
    public function getAlterSequenceSQL(Sequence $sequence)
0 ignored issues
show
Unused Code introduced by
The parameter $sequence is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1760
    public function getAlterSequenceSQL(/** @scrutinizer ignore-unused */ Sequence $sequence)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1761
    {
1762
        throw DBALException::notSupported(__METHOD__);
1763
    }
1764
1765
    /**
1766
     * Returns the SQL to create a constraint on a table on this platform.
1767
     *
1768
     * @param \Doctrine\DBAL\Schema\Constraint   $constraint
1769
     * @param \Doctrine\DBAL\Schema\Table|string $table
1770
     *
1771
     * @return string
1772
     *
1773
     * @throws \InvalidArgumentException
1774
     */
1775 312
    public function getCreateConstraintSQL(Constraint $constraint, $table)
1776
    {
1777 312
        if ($table instanceof Table) {
1778
            $table = $table->getQuotedName($this);
1779
        }
1780
1781 312
        $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this);
1782
1783 312
        $columnList = '('. implode(', ', $constraint->getQuotedColumns($this)) . ')';
1784
1785 312
        $referencesClause = '';
1786 312
        if ($constraint instanceof Index) {
1787 312
            if ($constraint->isPrimary()) {
1788 312
                $query .= ' PRIMARY KEY';
1789 234
            } elseif ($constraint->isUnique()) {
1790 234
                $query .= ' UNIQUE';
1791
            } else {
1792
                throw new \InvalidArgumentException(
1793 312
                    'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().'
1794
                );
1795
            }
1796 234
        } elseif ($constraint instanceof ForeignKeyConstraint) {
1797 234
            $query .= ' FOREIGN KEY';
1798
1799 234
            $referencesClause = ' REFERENCES ' . $constraint->getQuotedForeignTableName($this) .
1800 234
                ' (' . implode(', ', $constraint->getQuotedForeignColumns($this)) . ')';
1801
        }
1802 312
        $query .= ' '.$columnList.$referencesClause;
1803
1804 312
        return $query;
1805
    }
1806
1807
    /**
1808
     * Returns the SQL to create an index on a table on this platform.
1809
     *
1810
     * @param \Doctrine\DBAL\Schema\Index        $index
1811
     * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table on which the index is to be created.
1812
     *
1813
     * @return string
1814
     *
1815
     * @throws \InvalidArgumentException
1816
     */
1817 2880
    public function getCreateIndexSQL(Index $index, $table)
1818
    {
1819 2880
        if ($table instanceof Table) {
1820 18
            $table = $table->getQuotedName($this);
1821
        }
1822 2880
        $name = $index->getQuotedName($this);
1823 2880
        $columns = $index->getQuotedColumns($this);
1824
1825 2880
        if (count($columns) == 0) {
1826
            throw new \InvalidArgumentException("Incomplete definition. 'columns' required.");
1827
        }
1828
1829 2880
        if ($index->isPrimary()) {
1830 378
            return $this->getCreatePrimaryKeySQL($index, $table);
1831
        }
1832
1833 2538
        $query = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table;
1834 2538
        $query .= ' (' . $this->getIndexFieldDeclarationListSQL($columns) . ')' . $this->getPartialIndexSQL($index);
1835
1836 2538
        return $query;
1837
    }
1838
1839
    /**
1840
     * Adds condition for partial index.
1841
     *
1842
     * @param \Doctrine\DBAL\Schema\Index $index
1843
     *
1844
     * @return string
1845
     */
1846 3477
    protected function getPartialIndexSQL(Index $index)
1847
    {
1848 3477
        if ($this->supportsPartialIndexes() && $index->hasOption('where')) {
1849 96
            return  ' WHERE ' . $index->getOption('where');
1850
        }
1851
1852 3381
        return '';
1853
    }
1854
1855
    /**
1856
     * Adds additional flags for index generation.
1857
     *
1858
     * @param \Doctrine\DBAL\Schema\Index $index
1859
     *
1860
     * @return string
1861
     */
1862 1252
    protected function getCreateIndexSQLFlags(Index $index)
1863
    {
1864 1252
        return $index->isUnique() ? 'UNIQUE ' : '';
1865
    }
1866
1867
    /**
1868
     * Returns the SQL to create an unnamed primary key constraint.
1869
     *
1870
     * @param \Doctrine\DBAL\Schema\Index        $index
1871
     * @param \Doctrine\DBAL\Schema\Table|string $table
1872
     *
1873
     * @return string
1874
     */
1875 306
    public function getCreatePrimaryKeySQL(Index $index, $table)
1876
    {
1877 306
        return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index->getQuotedColumns($this)) . ')';
0 ignored issues
show
Bug introduced by
Are you sure $table of type Doctrine\DBAL\Schema\Table|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1877
        return 'ALTER TABLE ' . /** @scrutinizer ignore-type */ $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index->getQuotedColumns($this)) . ')';
Loading history...
1878
    }
1879
1880
    /**
1881
     * Returns the SQL to create a named schema.
1882
     *
1883
     * @param string $schemaName
1884
     *
1885
     * @return string
1886
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1887
     */
1888 180
    public function getCreateSchemaSQL($schemaName)
0 ignored issues
show
Unused Code introduced by
The parameter $schemaName is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1888
    public function getCreateSchemaSQL(/** @scrutinizer ignore-unused */ $schemaName)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1889
    {
1890 180
        throw DBALException::notSupported(__METHOD__);
1891
    }
1892
1893
    /**
1894
     * Quotes a string so that it can be safely used as a table or column name,
1895
     * even if it is a reserved word of the platform. This also detects identifier
1896
     * chains separated by dot and quotes them independently.
1897
     *
1898
     * NOTE: Just because you CAN use quoted identifiers doesn't mean
1899
     * you SHOULD use them. In general, they end up causing way more
1900
     * problems than they solve.
1901
     *
1902
     * @param string $str The identifier name to be quoted.
1903
     *
1904
     * @return string The quoted identifier string.
1905
     */
1906 7177
    public function quoteIdentifier($str)
1907
    {
1908 7177
        if (strpos($str, ".") !== false) {
1909 354
            $parts = array_map([$this, "quoteSingleIdentifier"], explode(".", $str));
1910
1911 354
            return implode(".", $parts);
1912
        }
1913
1914 7177
        return $this->quoteSingleIdentifier($str);
1915
    }
1916
1917
    /**
1918
     * Quotes a single identifier (no dot chain separation).
1919
     *
1920
     * @param string $str The identifier name to be quoted.
1921
     *
1922
     * @return string The quoted identifier string.
1923
     */
1924 6667
    public function quoteSingleIdentifier($str)
1925
    {
1926 6667
        $c = $this->getIdentifierQuoteCharacter();
1927
1928 6667
        return $c . str_replace($c, $c.$c, $str) . $c;
1929
    }
1930
1931
    /**
1932
     * Returns the SQL to create a new foreign key.
1933
     *
1934
     * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey The foreign key constraint.
1935
     * @param \Doctrine\DBAL\Schema\Table|string         $table      The name of the table on which the foreign key is to be created.
1936
     *
1937
     * @return string
1938
     */
1939 1606
    public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table)
1940
    {
1941 1606
        if ($table instanceof Table) {
1942 18
            $table = $table->getQuotedName($this);
1943
        }
1944
1945 1606
        $query = 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey);
1946
1947 1606
        return $query;
1948
    }
1949
1950
    /**
1951
     * Gets the SQL statements for altering an existing table.
1952
     *
1953
     * This method returns an array of SQL statements, since some platforms need several statements.
1954
     *
1955
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
1956
     *
1957
     * @return array
1958
     *
1959
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1960
     */
1961
    public function getAlterTableSQL(TableDiff $diff)
1962
    {
1963
        throw DBALException::notSupported(__METHOD__);
1964
    }
1965
1966
    /**
1967
     * @param \Doctrine\DBAL\Schema\Column    $column
1968
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
1969
     * @param array                           $columnSql
1970
     *
1971
     * @return bool
1972
     */
1973 1786
    protected function onSchemaAlterTableAddColumn(Column $column, TableDiff $diff, &$columnSql)
1974
    {
1975 1786
        if (null === $this->_eventManager) {
1976 1440
            return false;
1977
        }
1978
1979 346
        if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableAddColumn)) {
1980 22
            return false;
1981
        }
1982
1983 324
        $eventArgs = new SchemaAlterTableAddColumnEventArgs($column, $diff, $this);
1984 324
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn, $eventArgs);
1985
1986 324
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1987
1988 324
        return $eventArgs->isDefaultPrevented();
1989
    }
1990
1991
    /**
1992
     * @param \Doctrine\DBAL\Schema\Column    $column
1993
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
1994
     * @param array                           $columnSql
1995
     *
1996
     * @return bool
1997
     */
1998 1374
    protected function onSchemaAlterTableRemoveColumn(Column $column, TableDiff $diff, &$columnSql)
1999
    {
2000 1374
        if (null === $this->_eventManager) {
2001 1026
            return false;
2002
        }
2003
2004 348
        if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRemoveColumn)) {
2005 24
            return false;
2006
        }
2007
2008 324
        $eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column, $diff, $this);
2009 324
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn, $eventArgs);
2010
2011 324
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
2012
2013 324
        return $eventArgs->isDefaultPrevented();
2014
    }
2015
2016
    /**
2017
     * @param \Doctrine\DBAL\Schema\ColumnDiff $columnDiff
2018
     * @param \Doctrine\DBAL\Schema\TableDiff  $diff
2019
     * @param array                            $columnSql
2020
     *
2021
     * @return bool
2022
     */
2023 3472
    protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiff, TableDiff $diff, &$columnSql)
2024
    {
2025 3472
        if (null === $this->_eventManager) {
2026 2898
            return false;
2027
        }
2028
2029 574
        if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) {
2030 250
            return false;
2031
        }
2032
2033 324
        $eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff, $diff, $this);
2034 324
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn, $eventArgs);
2035
2036 324
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
2037
2038 324
        return $eventArgs->isDefaultPrevented();
2039
    }
2040
2041
    /**
2042
     * @param string                          $oldColumnName
2043
     * @param \Doctrine\DBAL\Schema\Column    $column
2044
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
2045
     * @param array                           $columnSql
2046
     *
2047
     * @return bool
2048
     */
2049 1389
    protected function onSchemaAlterTableRenameColumn($oldColumnName, Column $column, TableDiff $diff, &$columnSql)
2050
    {
2051 1389
        if (null === $this->_eventManager) {
2052 1044
            return false;
2053
        }
2054
2055 345
        if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRenameColumn)) {
2056 21
            return false;
2057
        }
2058
2059 324
        $eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName, $column, $diff, $this);
2060 324
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn, $eventArgs);
2061
2062 324
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
2063
2064 324
        return $eventArgs->isDefaultPrevented();
2065
    }
2066
2067
    /**
2068
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
2069
     * @param array                           $sql
2070
     *
2071
     * @return bool
2072
     */
2073 6596
    protected function onSchemaAlterTable(TableDiff $diff, &$sql)
2074
    {
2075 6596
        if (null === $this->_eventManager) {
2076 5940
            return false;
2077
        }
2078
2079 656
        if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTable)) {
2080 332
            return false;
2081
        }
2082
2083 324
        $eventArgs = new SchemaAlterTableEventArgs($diff, $this);
2084 324
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTable, $eventArgs);
2085
2086 324
        $sql = array_merge($sql, $eventArgs->getSql());
2087
2088 324
        return $eventArgs->isDefaultPrevented();
2089
    }
2090
2091
    /**
2092
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
2093
     *
2094
     * @return array
2095
     */
2096 6343
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
2097
    {
2098 6343
        $tableName = $diff->getName($this)->getQuotedName($this);
2099
2100 6343
        $sql = [];
2101 6343
        if ($this->supportsForeignKeyConstraints()) {
2102 6343
            foreach ($diff->removedForeignKeys as $foreignKey) {
2103 467
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
2104
            }
2105 6343
            foreach ($diff->changedForeignKeys as $foreignKey) {
2106 360
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
2107
            }
2108
        }
2109
2110 6343
        foreach ($diff->removedIndexes as $index) {
2111 229
            $sql[] = $this->getDropIndexSQL($index, $tableName);
2112
        }
2113 6343
        foreach ($diff->changedIndexes as $index) {
2114 322
            $sql[] = $this->getDropIndexSQL($index, $tableName);
2115
        }
2116
2117 6343
        return $sql;
2118
    }
2119
2120
    /**
2121
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
2122
     *
2123
     * @return array
2124
     */
2125 6343
    protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff)
2126
    {
2127 6343
        $tableName = (false !== $diff->newName)
2128 615
            ? $diff->getNewName()->getQuotedName($this)
2129 6343
            : $diff->getName($this)->getQuotedName($this);
2130
2131 6343
        $sql = [];
2132
2133 6343
        if ($this->supportsForeignKeyConstraints()) {
2134 6343
            foreach ($diff->addedForeignKeys as $foreignKey) {
2135 394
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
2136
            }
2137
2138 6343
            foreach ($diff->changedForeignKeys as $foreignKey) {
2139 360
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
2140
            }
2141
        }
2142
2143 6343
        foreach ($diff->addedIndexes as $index) {
2144 71
            $sql[] = $this->getCreateIndexSQL($index, $tableName);
2145
        }
2146
2147 6343
        foreach ($diff->changedIndexes as $index) {
2148 322
            $sql[] = $this->getCreateIndexSQL($index, $tableName);
2149
        }
2150
2151 6343
        foreach ($diff->renamedIndexes as $oldIndexName => $index) {
2152 1564
            $oldIndexName = new Identifier($oldIndexName);
2153 1564
            $sql          = array_merge(
2154 1564
                $sql,
2155 1564
                $this->getRenameIndexSQL($oldIndexName->getQuotedName($this), $index, $tableName)
2156
            );
2157
        }
2158
2159 6343
        return $sql;
2160
    }
2161
2162
    /**
2163
     * Returns the SQL for renaming an index on a table.
2164
     *
2165
     * @param string                      $oldIndexName The name of the index to rename from.
2166
     * @param \Doctrine\DBAL\Schema\Index $index        The definition of the index to rename to.
2167
     * @param string                      $tableName    The table to rename the given index on.
2168
     *
2169
     * @return array The sequence of SQL statements for renaming the given index.
2170
     */
2171 190
    protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
2172
    {
2173
        return [
2174 190
            $this->getDropIndexSQL($oldIndexName, $tableName),
2175 190
            $this->getCreateIndexSQL($index, $tableName)
2176
        ];
2177
    }
2178
2179
    /**
2180
     * Common code for alter table statement generation that updates the changed Index and Foreign Key definitions.
2181
     *
2182
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
2183
     *
2184
     * @return array
2185
     */
2186
    protected function _getAlterTableIndexForeignKeySQL(TableDiff $diff)
2187
    {
2188
        return array_merge($this->getPreAlterTableIndexForeignKeySQL($diff), $this->getPostAlterTableIndexForeignKeySQL($diff));
2189
    }
2190
2191
    /**
2192
     * Gets declaration of a number of fields in bulk.
2193
     *
2194
     * @param array $fields A multidimensional associative array.
2195
     *                      The first dimension determines the field name, while the second
2196
     *                      dimension is keyed with the name of the properties
2197
     *                      of the field being declared as array indexes. Currently, the types
2198
     *                      of supported field properties are as follows:
2199
     *
2200
     *      length
2201
     *          Integer value that determines the maximum length of the text
2202
     *          field. If this argument is missing the field should be
2203
     *          declared to have the longest length allowed by the DBMS.
2204
     *
2205
     *      default
2206
     *          Text value to be used as default for this field.
2207
     *
2208
     *      notnull
2209
     *          Boolean flag that indicates whether this field is constrained
2210
     *          to not be set to null.
2211
     *      charset
2212
     *          Text value with the default CHARACTER SET for this field.
2213
     *      collation
2214
     *          Text value with the default COLLATION for this field.
2215
     *      unique
2216
     *          unique constraint
2217
     *
2218
     * @return string
2219
     */
2220 7455
    public function getColumnDeclarationListSQL(array $fields)
2221
    {
2222 7455
        $queryFields = [];
2223
2224 7455
        foreach ($fields as $fieldName => $field) {
2225 7455
            $queryFields[] = $this->getColumnDeclarationSQL($fieldName, $field);
2226
        }
2227
2228 7455
        return implode(', ', $queryFields);
2229
    }
2230
2231
    /**
2232
     * Obtains DBMS specific SQL code portion needed to declare a generic type
2233
     * field to be used in statements like CREATE TABLE.
2234
     *
2235
     * @param string $name  The name the field to be declared.
2236
     * @param array  $field An associative array with the name of the properties
2237
     *                      of the field being declared as array indexes. Currently, the types
2238
     *                      of supported field properties are as follows:
2239
     *
2240
     *      length
2241
     *          Integer value that determines the maximum length of the text
2242
     *          field. If this argument is missing the field should be
2243
     *          declared to have the longest length allowed by the DBMS.
2244
     *
2245
     *      default
2246
     *          Text value to be used as default for this field.
2247
     *
2248
     *      notnull
2249
     *          Boolean flag that indicates whether this field is constrained
2250
     *          to not be set to null.
2251
     *      charset
2252
     *          Text value with the default CHARACTER SET for this field.
2253
     *      collation
2254
     *          Text value with the default COLLATION for this field.
2255
     *      unique
2256
     *          unique constraint
2257
     *      check
2258
     *          column check constraint
2259
     *      columnDefinition
2260
     *          a string that defines the complete column
2261
     *
2262
     * @return string DBMS specific SQL code portion that should be used to declare the column.
2263
     */
2264 7874
    public function getColumnDeclarationSQL($name, array $field)
2265
    {
2266 7874
        if (isset($field['columnDefinition'])) {
2267 260
            $columnDef = $this->getCustomTypeDeclarationSQL($field);
2268
        } else {
2269 7622
            $default = $this->getDefaultValueDeclarationSQL($field);
2270
2271 7622
            $charset = (isset($field['charset']) && $field['charset']) ?
2272 7622
                ' ' . $this->getColumnCharsetDeclarationSQL($field['charset']) : '';
2273
2274 7622
            $collation = (isset($field['collation']) && $field['collation']) ?
2275 7622
                ' ' . $this->getColumnCollationDeclarationSQL($field['collation']) : '';
2276
2277 7622
            $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : '';
2278
2279 7622
            $unique = (isset($field['unique']) && $field['unique']) ?
2280 7622
                ' ' . $this->getUniqueFieldDeclarationSQL() : '';
2281
2282 7622
            $check = (isset($field['check']) && $field['check']) ?
2283 7622
                ' ' . $field['check'] : '';
2284
2285 7622
            $typeDecl = $field['type']->getSQLDeclaration($field, $this);
2286 7622
            $columnDef = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation;
2287
2288 7622
            if ($this->supportsInlineColumnComments() && isset($field['comment']) && $field['comment'] !== '') {
2289 644
                $columnDef .= ' ' . $this->getInlineColumnCommentSQL($field['comment']);
2290
            }
2291
        }
2292
2293 7874
        return $name . ' ' . $columnDef;
2294
    }
2295
2296
    /**
2297
     * Returns the SQL snippet that declares a floating point column of arbitrary precision.
2298
     *
2299
     * @param array $columnDef
2300
     *
2301
     * @return string
2302
     */
2303 2416
    public function getDecimalTypeDeclarationSQL(array $columnDef)
2304
    {
2305 2416
        $columnDef['precision'] = ( ! isset($columnDef['precision']) || empty($columnDef['precision']))
2306 2416
            ? 10 : $columnDef['precision'];
2307 2416
        $columnDef['scale'] = ( ! isset($columnDef['scale']) || empty($columnDef['scale']))
2308 2416
            ? 0 : $columnDef['scale'];
2309
2310 2416
        return 'NUMERIC(' . $columnDef['precision'] . ', ' . $columnDef['scale'] . ')';
2311
    }
2312
2313
    /**
2314
     * Obtains DBMS specific SQL code portion needed to set a default value
2315
     * declaration to be used in statements like CREATE TABLE.
2316
     *
2317
     * @param array $field The field definition array.
2318
     *
2319
     * @return string DBMS specific SQL code portion needed to set a default value.
2320
     */
2321 8670
    public function getDefaultValueDeclarationSQL($field)
2322
    {
2323 8670
        if ( ! isset($field['default'])) {
2324 7461
            return empty($field['notnull']) ? ' DEFAULT NULL' : '';
2325
        }
2326
2327 1628
        $default = $field['default'];
2328
2329 1628
        if ( ! isset($field['type'])) {
2330
            return " DEFAULT '" . $default . "'";
2331
        }
2332
2333 1628
        $type = $field['type'];
2334
2335 1628
        if ($type instanceof Types\PhpIntegerMappingType) {
2336 443
            return ' DEFAULT ' . $default;
2337
        }
2338
2339 1230
        if ($type instanceof Types\PhpDateTimeMappingType && $default === $this->getCurrentTimestampSQL()) {
2340 286
            return ' DEFAULT ' . $this->getCurrentTimestampSQL();
2341
        }
2342
2343 953
        if ($type instanceof Types\TimeType && $default === $this->getCurrentTimeSQL()) {
2344 2
            return ' DEFAULT ' . $this->getCurrentTimeSQL();
2345
        }
2346
2347 953
        if ($type instanceof Types\DateType && $default === $this->getCurrentDateSQL()) {
2348 272
            return ' DEFAULT ' . $this->getCurrentDateSQL();
2349
        }
2350
2351 681
        if ($type instanceof Types\BooleanType) {
2352 276
            return " DEFAULT '" . $this->convertBooleans($default) . "'";
2353
        }
2354
2355 675
        return " DEFAULT '" . $default . "'";
2356
    }
2357
2358
    /**
2359
     * Obtains DBMS specific SQL code portion needed to set a CHECK constraint
2360
     * declaration to be used in statements like CREATE TABLE.
2361
     *
2362
     * @param array $definition The check definition.
2363
     *
2364
     * @return string DBMS specific SQL code portion needed to set a CHECK constraint.
2365
     */
2366 2725
    public function getCheckDeclarationSQL(array $definition)
2367
    {
2368 2725
        $constraints = [];
2369 2725
        foreach ($definition as $field => $def) {
2370 2725
            if (is_string($def)) {
2371
                $constraints[] = 'CHECK (' . $def . ')';
2372
            } else {
2373 2725
                if (isset($def['min'])) {
2374 90
                    $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')';
2375
                }
2376
2377 2725
                if (isset($def['max'])) {
2378 2725
                    $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')';
2379
                }
2380
            }
2381
        }
2382
2383 2725
        return implode(', ', $constraints);
2384
    }
2385
2386
    /**
2387
     * Obtains DBMS specific SQL code portion needed to set a unique
2388
     * constraint declaration to be used in statements like CREATE TABLE.
2389
     *
2390
     * @param string                      $name  The name of the unique constraint.
2391
     * @param \Doctrine\DBAL\Schema\Index $index The index definition.
2392
     *
2393
     * @return string DBMS specific SQL code portion needed to set a constraint.
2394
     *
2395
     * @throws \InvalidArgumentException
2396
     */
2397 504
    public function getUniqueConstraintDeclarationSQL($name, Index $index)
2398
    {
2399 504
        $columns = $index->getQuotedColumns($this);
2400 504
        $name = new Identifier($name);
2401
2402 504
        if (count($columns) === 0) {
2403
            throw new \InvalidArgumentException("Incomplete definition. 'columns' required.");
2404
        }
2405
2406 504
        return 'CONSTRAINT ' . $name->getQuotedName($this) . ' UNIQUE ('
2407 504
            . $this->getIndexFieldDeclarationListSQL($columns)
2408 504
            . ')' . $this->getPartialIndexSQL($index);
2409
    }
2410
2411
    /**
2412
     * Obtains DBMS specific SQL code portion needed to set an index
2413
     * declaration to be used in statements like CREATE TABLE.
2414
     *
2415
     * @param string                      $name  The name of the index.
2416
     * @param \Doctrine\DBAL\Schema\Index $index The index definition.
2417
     *
2418
     * @return string DBMS specific SQL code portion needed to set an index.
2419
     *
2420
     * @throws \InvalidArgumentException
2421
     */
2422 940
    public function getIndexDeclarationSQL($name, Index $index)
2423
    {
2424 940
        $columns = $index->getQuotedColumns($this);
2425 940
        $name = new Identifier($name);
2426
2427 940
        if (count($columns) === 0) {
2428
            throw new \InvalidArgumentException("Incomplete definition. 'columns' required.");
2429
        }
2430
2431 940
        return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name->getQuotedName($this) . ' ('
2432 940
            . $this->getIndexFieldDeclarationListSQL($columns)
2433 940
            . ')' . $this->getPartialIndexSQL($index);
2434
    }
2435
2436
    /**
2437
     * Obtains SQL code portion needed to create a custom column,
2438
     * e.g. when a field has the "columnDefinition" keyword.
2439
     * Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate.
2440
     *
2441
     * @param array $columnDef
2442
     *
2443
     * @return string
2444
     */
2445 332
    public function getCustomTypeDeclarationSQL(array $columnDef)
2446
    {
2447 332
        return $columnDef['columnDefinition'];
2448
    }
2449
2450
    /**
2451
     * Obtains DBMS specific SQL code portion needed to set an index
2452
     * declaration to be used in statements like CREATE TABLE.
2453
     *
2454
     * @param array $fields
2455
     *
2456
     * @return string
2457
     */
2458 4737
    public function getIndexFieldDeclarationListSQL(array $fields)
2459
    {
2460 4737
        $ret = [];
2461
2462 4737
        foreach ($fields as $field => $definition) {
2463 4737
            if (is_array($definition)) {
2464
                $ret[] = $field;
2465
            } else {
2466 4737
                $ret[] = $definition;
2467
            }
2468
        }
2469
2470 4737
        return implode(', ', $ret);
2471
    }
2472
2473
    /**
2474
     * Returns the required SQL string that fits between CREATE ... TABLE
2475
     * to create the table as a temporary table.
2476
     *
2477
     * Should be overridden in driver classes to return the correct string for the
2478
     * specific database type.
2479
     *
2480
     * The default is to return the string "TEMPORARY" - this will result in a
2481
     * SQL error for any database that does not support temporary tables, or that
2482
     * requires a different SQL command from "CREATE TEMPORARY TABLE".
2483
     *
2484
     * @return string The string required to be placed between "CREATE" and "TABLE"
2485
     *                to generate a temporary table, if possible.
2486
     */
2487
    public function getTemporaryTableSQL()
2488
    {
2489
        return 'TEMPORARY';
2490
    }
2491
2492
    /**
2493
     * Some vendors require temporary table names to be qualified specially.
2494
     *
2495
     * @param string $tableName
2496
     *
2497
     * @return string
2498
     */
2499 26
    public function getTemporaryTableName($tableName)
2500
    {
2501 26
        return $tableName;
2502
    }
2503
2504
    /**
2505
     * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2506
     * of a field declaration to be used in statements like CREATE TABLE.
2507
     *
2508
     * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey
2509
     *
2510
     * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2511
     *                of a field declaration.
2512
     */
2513 2219
    public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey)
2514
    {
2515 2219
        $sql  = $this->getForeignKeyBaseDeclarationSQL($foreignKey);
2516 2003
        $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey);
2517
2518 2003
        return $sql;
2519
    }
2520
2521
    /**
2522
     * Returns the FOREIGN KEY query section dealing with non-standard options
2523
     * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
2524
     *
2525
     * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey The foreign key definition.
2526
     *
2527
     * @return string
2528
     */
2529 1919
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
2530
    {
2531 1919
        $query = '';
2532 1919
        if ($this->supportsForeignKeyOnUpdate() && $foreignKey->hasOption('onUpdate')) {
2533 72
            $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate'));
2534
        }
2535 1919
        if ($foreignKey->hasOption('onDelete')) {
2536 178
            $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
2537
        }
2538
2539 1919
        return $query;
2540
    }
2541
2542
    /**
2543
     * Returns the given referential action in uppercase if valid, otherwise throws an exception.
2544
     *
2545
     * @param string $action The foreign key referential action.
2546
     *
2547
     * @return string
2548
     *
2549
     * @throws \InvalidArgumentException if unknown referential action given
2550
     */
2551 2194
    public function getForeignKeyReferentialActionSQL($action)
2552
    {
2553 2194
        $upper = strtoupper($action);
2554 121
        switch ($upper) {
2555 2194
            case 'CASCADE':
2556 1482
            case 'SET NULL':
2557 1104
            case 'NO ACTION':
2558 870
            case 'RESTRICT':
2559 618
            case 'SET DEFAULT':
2560 1888
                return $upper;
2561
            default:
2562 306
                throw new \InvalidArgumentException('Invalid foreign key action: ' . $upper);
2563
        }
2564
    }
2565
2566
    /**
2567
     * Obtains DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2568
     * of a field declaration to be used in statements like CREATE TABLE.
2569
     *
2570
     * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey
2571
     *
2572
     * @return string
2573
     *
2574
     * @throws \InvalidArgumentException
2575
     */
2576 1499
    public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey)
2577
    {
2578 1499
        $sql = '';
2579 1499
        if (strlen($foreignKey->getName())) {
2580 1247
            $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' ';
2581
        }
2582 1499
        $sql .= 'FOREIGN KEY (';
2583
2584 1499
        if (count($foreignKey->getLocalColumns()) === 0) {
2585
            throw new \InvalidArgumentException("Incomplete definition. 'local' required.");
2586
        }
2587 1499
        if (count($foreignKey->getForeignColumns()) === 0) {
2588
            throw new \InvalidArgumentException("Incomplete definition. 'foreign' required.");
2589
        }
2590 1499
        if (strlen($foreignKey->getForeignTableName()) === 0) {
2591
            throw new \InvalidArgumentException("Incomplete definition. 'foreignTable' required.");
2592
        }
2593
2594 1499
        $sql .= implode(', ', $foreignKey->getQuotedLocalColumns($this))
2595 1499
            . ') REFERENCES '
2596 1499
            . $foreignKey->getQuotedForeignTableName($this) . ' ('
2597 1499
            . implode(', ', $foreignKey->getQuotedForeignColumns($this)) . ')';
2598
2599 1499
        return $sql;
2600
    }
2601
2602
    /**
2603
     * Obtains DBMS specific SQL code portion needed to set the UNIQUE constraint
2604
     * of a field declaration to be used in statements like CREATE TABLE.
2605
     *
2606
     * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint
2607
     *                of a field declaration.
2608
     */
2609
    public function getUniqueFieldDeclarationSQL()
2610
    {
2611
        return 'UNIQUE';
2612
    }
2613
2614
    /**
2615
     * Obtains DBMS specific SQL code portion needed to set the CHARACTER SET
2616
     * of a field declaration to be used in statements like CREATE TABLE.
2617
     *
2618
     * @param string $charset The name of the charset.
2619
     *
2620
     * @return string DBMS specific SQL code portion needed to set the CHARACTER SET
2621
     *                of a field declaration.
2622
     */
2623
    public function getColumnCharsetDeclarationSQL($charset)
0 ignored issues
show
Unused Code introduced by
The parameter $charset is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

2623
    public function getColumnCharsetDeclarationSQL(/** @scrutinizer ignore-unused */ $charset)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2624
    {
2625
        return '';
2626
    }
2627
2628
    /**
2629
     * Obtains DBMS specific SQL code portion needed to set the COLLATION
2630
     * of a field declaration to be used in statements like CREATE TABLE.
2631
     *
2632
     * @param string $collation The name of the collation.
2633
     *
2634
     * @return string DBMS specific SQL code portion needed to set the COLLATION
2635
     *                of a field declaration.
2636
     */
2637 262
    public function getColumnCollationDeclarationSQL($collation)
2638
    {
2639 262
        return $this->supportsColumnCollation() ? 'COLLATE ' . $collation : '';
2640
    }
2641
2642
    /**
2643
     * Whether the platform prefers sequences for ID generation.
2644
     * Subclasses should override this method to return TRUE if they prefer sequences.
2645
     *
2646
     * @return bool
2647
     */
2648 90
    public function prefersSequences()
2649
    {
2650 90
        return false;
2651
    }
2652
2653
    /**
2654
     * Whether the platform prefers identity columns (eg. autoincrement) for ID generation.
2655
     * Subclasses should override this method to return TRUE if they prefer identity columns.
2656
     *
2657
     * @return bool
2658
     */
2659 108
    public function prefersIdentityColumns()
2660
    {
2661 108
        return false;
2662
    }
2663
2664
    /**
2665
     * Some platforms need the boolean values to be converted.
2666
     *
2667
     * The default conversion in this implementation converts to integers (false => 0, true => 1).
2668
     *
2669
     * Note: if the input is not a boolean the original input might be returned.
2670
     *
2671
     * There are two contexts when converting booleans: Literals and Prepared Statements.
2672
     * This method should handle the literal case
2673
     *
2674
     * @param mixed $item A boolean or an array of them.
2675
     *
2676
     * @return mixed A boolean database value or an array of them.
2677
     */
2678 398
    public function convertBooleans($item)
2679
    {
2680 398
        if (is_array($item)) {
2681
            foreach ($item as $k => $value) {
2682
                if (is_bool($value)) {
2683
                    $item[$k] = (int) $value;
2684
                }
2685
            }
2686 398
        } elseif (is_bool($item)) {
2687 380
            $item = (int) $item;
2688
        }
2689
2690 398
        return $item;
2691
    }
2692
2693
    /**
2694
     * Some platforms have boolean literals that needs to be correctly converted
2695
     *
2696
     * The default conversion tries to convert value into bool "(bool)$item"
2697
     *
2698
     * @param mixed $item
2699
     *
2700
     * @return bool|null
2701
     */
2702 882
    public function convertFromBoolean($item)
2703
    {
2704 882
        return null === $item ? null: (bool) $item ;
2705
    }
2706
2707
    /**
2708
     * This method should handle the prepared statements case. When there is no
2709
     * distinction, it's OK to use the same method.
2710
     *
2711
     * Note: if the input is not a boolean the original input might be returned.
2712
     *
2713
     * @param mixed $item A boolean or an array of them.
2714
     *
2715
     * @return mixed A boolean database value or an array of them.
2716
     */
2717 132
    public function convertBooleansToDatabaseValue($item)
2718
    {
2719 132
        return $this->convertBooleans($item);
2720
    }
2721
2722
    /**
2723
     * Returns the SQL specific for the platform to get the current date.
2724
     *
2725
     * @return string
2726
     */
2727 211
    public function getCurrentDateSQL()
2728
    {
2729 211
        return 'CURRENT_DATE';
2730
    }
2731
2732
    /**
2733
     * Returns the SQL specific for the platform to get the current time.
2734
     *
2735
     * @return string
2736
     */
2737 6
    public function getCurrentTimeSQL()
2738
    {
2739 6
        return 'CURRENT_TIME';
2740
    }
2741
2742
    /**
2743
     * Returns the SQL specific for the platform to get the current timestamp
2744
     *
2745
     * @return string
2746
     */
2747 310
    public function getCurrentTimestampSQL()
2748
    {
2749 310
        return 'CURRENT_TIMESTAMP';
2750
    }
2751
2752
    /**
2753
     * Returns the SQL for a given transaction isolation level Connection constant.
2754
     *
2755
     * @param int $level
2756
     *
2757
     * @return string
2758
     *
2759
     * @throws \InvalidArgumentException
2760
     */
2761 198
    protected function _getTransactionIsolationLevelSQL($level)
2762
    {
2763 11
        switch ($level) {
2764 187
            case TransactionIsolationLevel::READ_UNCOMMITTED:
2765 198
                return 'READ UNCOMMITTED';
2766 187
            case TransactionIsolationLevel::READ_COMMITTED:
2767 198
                return 'READ COMMITTED';
2768 187
            case TransactionIsolationLevel::REPEATABLE_READ:
2769 198
                return 'REPEATABLE READ';
2770 187
            case TransactionIsolationLevel::SERIALIZABLE:
2771 198
                return 'SERIALIZABLE';
2772
            default:
2773
                throw new \InvalidArgumentException('Invalid isolation level:' . $level);
2774
        }
2775
    }
2776
2777
    /**
2778
     * @return string
2779
     *
2780
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2781
     */
2782 1
    public function getListDatabasesSQL()
2783
    {
2784 1
        throw DBALException::notSupported(__METHOD__);
2785
    }
2786
2787
    /**
2788
     * Returns the SQL statement for retrieving the namespaces defined in the database.
2789
     *
2790
     * @return string
2791
     *
2792
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2793
     */
2794
    public function getListNamespacesSQL()
2795
    {
2796
        throw DBALException::notSupported(__METHOD__);
2797
    }
2798
2799
    /**
2800
     * @param string $database
2801
     *
2802
     * @return string
2803
     *
2804
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2805
     */
2806
    public function getListSequencesSQL($database)
2807
    {
2808
        throw DBALException::notSupported(__METHOD__);
2809
    }
2810
2811
    /**
2812
     * @param string $table
2813
     *
2814
     * @return string
2815
     *
2816
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2817
     */
2818
    public function getListTableConstraintsSQL($table)
2819
    {
2820
        throw DBALException::notSupported(__METHOD__);
2821
    }
2822
2823
    /**
2824
     * @param string      $table
2825
     * @param string|null $database
2826
     *
2827
     * @return string
2828
     *
2829
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2830
     */
2831
    public function getListTableColumnsSQL($table, $database = null)
2832
    {
2833
        throw DBALException::notSupported(__METHOD__);
2834
    }
2835
2836
    /**
2837
     * @return string
2838
     *
2839
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2840
     */
2841
    public function getListTablesSQL()
2842
    {
2843
        throw DBALException::notSupported(__METHOD__);
2844
    }
2845
2846
    /**
2847
     * @return string
2848
     *
2849
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2850
     */
2851
    public function getListUsersSQL()
2852
    {
2853
        throw DBALException::notSupported(__METHOD__);
2854
    }
2855
2856
    /**
2857
     * Returns the SQL to list all views of a database or user.
2858
     *
2859
     * @param string $database
2860
     *
2861
     * @return string
2862
     *
2863
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2864
     */
2865
    public function getListViewsSQL($database)
2866
    {
2867
        throw DBALException::notSupported(__METHOD__);
2868
    }
2869
2870
    /**
2871
     * Returns the list of indexes for the current database.
2872
     *
2873
     * The current database parameter is optional but will always be passed
2874
     * when using the SchemaManager API and is the database the given table is in.
2875
     *
2876
     * Attention: Some platforms only support currentDatabase when they
2877
     * are connected with that database. Cross-database information schema
2878
     * requests may be impossible.
2879
     *
2880
     * @param string $table
2881
     * @param string $currentDatabase
2882
     *
2883
     * @return string
2884
     *
2885
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2886
     */
2887
    public function getListTableIndexesSQL($table, $currentDatabase = null)
2888
    {
2889
        throw DBALException::notSupported(__METHOD__);
2890
    }
2891
2892
    /**
2893
     * @param string $table
2894
     *
2895
     * @return string
2896
     *
2897
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2898
     */
2899
    public function getListTableForeignKeysSQL($table)
2900
    {
2901
        throw DBALException::notSupported(__METHOD__);
2902
    }
2903
2904
    /**
2905
     * @param string $name
2906
     * @param string $sql
2907
     *
2908
     * @return string
2909
     *
2910
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2911
     */
2912
    public function getCreateViewSQL($name, $sql)
2913
    {
2914
        throw DBALException::notSupported(__METHOD__);
2915
    }
2916
2917
    /**
2918
     * @param string $name
2919
     *
2920
     * @return string
2921
     *
2922
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2923
     */
2924
    public function getDropViewSQL($name)
2925
    {
2926
        throw DBALException::notSupported(__METHOD__);
2927
    }
2928
2929
    /**
2930
     * Returns the SQL snippet to drop an existing sequence.
2931
     *
2932
     * @param Sequence|string $sequence
2933
     *
2934
     * @return string
2935
     *
2936
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2937
     */
2938
    public function getDropSequenceSQL($sequence)
0 ignored issues
show
Unused Code introduced by
The parameter $sequence is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

2938
    public function getDropSequenceSQL(/** @scrutinizer ignore-unused */ $sequence)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2939
    {
2940
        throw DBALException::notSupported(__METHOD__);
2941
    }
2942
2943
    /**
2944
     * @param string $sequenceName
2945
     *
2946
     * @return string
2947
     *
2948
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2949
     */
2950
    public function getSequenceNextValSQL($sequenceName)
0 ignored issues
show
Unused Code introduced by
The parameter $sequenceName is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

2950
    public function getSequenceNextValSQL(/** @scrutinizer ignore-unused */ $sequenceName)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2951
    {
2952
        throw DBALException::notSupported(__METHOD__);
2953
    }
2954
2955
    /**
2956
     * Returns the SQL to create a new database.
2957
     *
2958
     * @param string $database The name of the database that should be created.
2959
     *
2960
     * @return string
2961
     *
2962
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2963
     */
2964 18
    public function getCreateDatabaseSQL($database)
2965
    {
2966 18
        throw DBALException::notSupported(__METHOD__);
2967
    }
2968
2969
    /**
2970
     * Returns the SQL to set the transaction isolation level.
2971
     *
2972
     * @param int $level
2973
     *
2974
     * @return string
2975
     *
2976
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2977
     */
2978
    public function getSetTransactionIsolationSQL($level)
2979
    {
2980
        throw DBALException::notSupported(__METHOD__);
2981
    }
2982
2983
    /**
2984
     * Obtains DBMS specific SQL to be used to create datetime fields in
2985
     * statements like CREATE TABLE.
2986
     *
2987
     * @param array $fieldDeclaration
2988
     *
2989
     * @return string
2990
     *
2991
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2992
     */
2993
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
2994
    {
2995
        throw DBALException::notSupported(__METHOD__);
2996
    }
2997
2998
    /**
2999
     * Obtains DBMS specific SQL to be used to create datetime with timezone offset fields.
3000
     *
3001
     * @param array $fieldDeclaration
3002
     *
3003
     * @return string
3004
     */
3005 135
    public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration)
3006
    {
3007 135
        return $this->getDateTimeTypeDeclarationSQL($fieldDeclaration);
3008
    }
3009
3010
3011
    /**
3012
     * Obtains DBMS specific SQL to be used to create date fields in statements
3013
     * like CREATE TABLE.
3014
     *
3015
     * @param array $fieldDeclaration
3016
     *
3017
     * @return string
3018
     *
3019
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
3020
     */
3021
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
3022
    {
3023
        throw DBALException::notSupported(__METHOD__);
3024
    }
3025
3026
    /**
3027
     * Obtains DBMS specific SQL to be used to create time fields in statements
3028
     * like CREATE TABLE.
3029
     *
3030
     * @param array $fieldDeclaration
3031
     *
3032
     * @return string
3033
     *
3034
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
3035
     */
3036
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
3037
    {
3038
        throw DBALException::notSupported(__METHOD__);
3039
    }
3040
3041
    /**
3042
     * @param array $fieldDeclaration
3043
     *
3044
     * @return string
3045
     */
3046 1791
    public function getFloatDeclarationSQL(array $fieldDeclaration)
3047
    {
3048 1791
        return 'DOUBLE PRECISION';
3049
    }
3050
3051
    /**
3052
     * Gets the default transaction isolation level of the platform.
3053
     *
3054
     * @return int The default isolation level.
3055
     *
3056
     * @see TransactionIsolationLevel
3057
     */
3058
    public function getDefaultTransactionIsolationLevel()
3059
    {
3060
        return TransactionIsolationLevel::READ_COMMITTED;
3061
    }
3062
3063
    /* supports*() methods */
3064
3065
    /**
3066
     * Whether the platform supports sequences.
3067
     *
3068
     * @return bool
3069
     */
3070 99
    public function supportsSequences()
3071
    {
3072 99
        return false;
3073
    }
3074
3075
    /**
3076
     * Whether the platform supports identity columns.
3077
     *
3078
     * Identity columns are columns that receive an auto-generated value from the
3079
     * database on insert of a row.
3080
     *
3081
     * @return bool
3082
     */
3083 20
    public function supportsIdentityColumns()
3084
    {
3085 20
        return false;
3086
    }
3087
3088
    /**
3089
     * Whether the platform emulates identity columns through sequences.
3090
     *
3091
     * Some platforms that do not support identity columns natively
3092
     * but support sequences can emulate identity columns by using
3093
     * sequences.
3094
     *
3095
     * @return bool
3096
     */
3097 227
    public function usesSequenceEmulatedIdentityColumns()
3098
    {
3099 227
        return false;
3100
    }
3101
3102
    /**
3103
     * Returns the name of the sequence for a particular identity column in a particular table.
3104
     *
3105
     * @param string $tableName  The name of the table to return the sequence name for.
3106
     * @param string $columnName The name of the identity column in the table to return the sequence name for.
3107
     *
3108
     * @return string
3109
     *
3110
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
3111
     *
3112
     * @see    usesSequenceEmulatedIdentityColumns
3113
     */
3114 216
    public function getIdentitySequenceName($tableName, $columnName)
3115
    {
3116 216
        throw DBALException::notSupported(__METHOD__);
3117
    }
3118
3119
    /**
3120
     * Whether the platform supports indexes.
3121
     *
3122
     * @return bool
3123
     */
3124 72
    public function supportsIndexes()
3125
    {
3126 72
        return true;
3127
    }
3128
3129
    /**
3130
     * Whether the platform supports partial indexes.
3131
     *
3132
     * @return bool
3133
     */
3134 2649
    public function supportsPartialIndexes()
3135
    {
3136 2649
        return false;
3137
    }
3138
3139
    /**
3140
     * Whether the platform supports altering tables.
3141
     *
3142
     * @return bool
3143
     */
3144 90
    public function supportsAlterTable()
3145
    {
3146 90
        return true;
3147
    }
3148
3149
    /**
3150
     * Whether the platform supports transactions.
3151
     *
3152
     * @return bool
3153
     */
3154 72
    public function supportsTransactions()
3155
    {
3156 72
        return true;
3157
    }
3158
3159
    /**
3160
     * Whether the platform supports savepoints.
3161
     *
3162
     * @return bool
3163
     */
3164 394
    public function supportsSavepoints()
3165
    {
3166 394
        return true;
3167
    }
3168
3169
    /**
3170
     * Whether the platform supports releasing savepoints.
3171
     *
3172
     * @return bool
3173
     */
3174 86
    public function supportsReleaseSavepoints()
3175
    {
3176 86
        return $this->supportsSavepoints();
3177
    }
3178
3179
    /**
3180
     * Whether the platform supports primary key constraints.
3181
     *
3182
     * @return bool
3183
     */
3184 72
    public function supportsPrimaryConstraints()
3185
    {
3186 72
        return true;
3187
    }
3188
3189
    /**
3190
     * Whether the platform supports foreign key constraints.
3191
     *
3192
     * @return bool
3193
     */
3194 8499
    public function supportsForeignKeyConstraints()
3195
    {
3196 8499
        return true;
3197
    }
3198
3199
    /**
3200
     * Whether this platform supports onUpdate in foreign key constraints.
3201
     *
3202
     * @return bool
3203
     */
3204 1991
    public function supportsForeignKeyOnUpdate()
3205
    {
3206 1991
        return ($this->supportsForeignKeyConstraints() && true);
3207
    }
3208
3209
    /**
3210
     * Whether the platform supports database schemas.
3211
     *
3212
     * @return bool
3213
     */
3214 172
    public function supportsSchemas()
3215
    {
3216 172
        return false;
3217
    }
3218
3219
    /**
3220
     * Whether this platform can emulate schemas.
3221
     *
3222
     * Platforms that either support or emulate schemas don't automatically
3223
     * filter a schema for the namespaced elements in {@link
3224
     * AbstractManager#createSchema}.
3225
     *
3226
     * @return bool
3227
     */
3228 72
    public function canEmulateSchemas()
3229
    {
3230 72
        return false;
3231
    }
3232
3233
    /**
3234
     * Returns the default schema name.
3235
     *
3236
     * @return string
3237
     *
3238
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
3239
     */
3240
    public function getDefaultSchemaName()
3241
    {
3242
        throw DBALException::notSupported(__METHOD__);
3243
    }
3244
3245
    /**
3246
     * Whether this platform supports create database.
3247
     *
3248
     * Some databases don't allow to create and drop databases at all or only with certain tools.
3249
     *
3250
     * @return bool
3251
     */
3252 99
    public function supportsCreateDropDatabase()
3253
    {
3254 99
        return true;
3255
    }
3256
3257
    /**
3258
     * Whether the platform supports getting the affected rows of a recent update/delete type query.
3259
     *
3260
     * @return bool
3261
     */
3262 72
    public function supportsGettingAffectedRows()
3263
    {
3264 72
        return true;
3265
    }
3266
3267
    /**
3268
     * Whether this platform support to add inline column comments as postfix.
3269
     *
3270
     * @return bool
3271
     */
3272 4631
    public function supportsInlineColumnComments()
3273
    {
3274 4631
        return false;
3275
    }
3276
3277
    /**
3278
     * Whether this platform support the proprietary syntax "COMMENT ON asset".
3279
     *
3280
     * @return bool
3281
     */
3282 3842
    public function supportsCommentOnStatement()
3283
    {
3284 3842
        return false;
3285
    }
3286
3287
    /**
3288
     * Does this platform have native guid type.
3289
     *
3290
     * @return bool
3291
     */
3292 5378
    public function hasNativeGuidType()
3293
    {
3294 5378
        return false;
3295
    }
3296
3297
    /**
3298
     * Does this platform have native JSON type.
3299
     *
3300
     * @return bool
3301
     */
3302 11471
    public function hasNativeJsonType()
3303
    {
3304 11471
        return false;
3305
    }
3306
3307
    /**
3308
     * @deprecated
3309
     * @todo Remove in 3.0
3310
     */
3311
    public function getIdentityColumnNullInsertSQL()
3312
    {
3313
        return "";
3314
    }
3315
3316
    /**
3317
     * Whether this platform supports views.
3318
     *
3319
     * @return bool
3320
     */
3321 18
    public function supportsViews()
3322
    {
3323 18
        return true;
3324
    }
3325
3326
    /**
3327
     * Does this platform support column collation?
3328
     *
3329
     * @return bool
3330
     */
3331
    public function supportsColumnCollation()
3332
    {
3333
        return false;
3334
    }
3335
3336
    /**
3337
     * Gets the format string, as accepted by the date() function, that describes
3338
     * the format of a stored datetime value of this platform.
3339
     *
3340
     * @return string The format string.
3341
     */
3342 302
    public function getDateTimeFormatString()
3343
    {
3344 302
        return 'Y-m-d H:i:s';
3345
    }
3346
3347
    /**
3348
     * Gets the format string, as accepted by the date() function, that describes
3349
     * the format of a stored datetime with timezone value of this platform.
3350
     *
3351
     * @return string The format string.
3352
     */
3353 135
    public function getDateTimeTzFormatString()
3354
    {
3355 135
        return 'Y-m-d H:i:s';
3356
    }
3357
3358
    /**
3359
     * Gets the format string, as accepted by the date() function, that describes
3360
     * the format of a stored date value of this platform.
3361
     *
3362
     * @return string The format string.
3363
     */
3364 123
    public function getDateFormatString()
3365
    {
3366 123
        return 'Y-m-d';
3367
    }
3368
3369
    /**
3370
     * Gets the format string, as accepted by the date() function, that describes
3371
     * the format of a stored time value of this platform.
3372
     *
3373
     * @return string The format string.
3374
     */
3375 105
    public function getTimeFormatString()
3376
    {
3377 105
        return 'H:i:s';
3378
    }
3379
3380
    /**
3381
     * Adds an driver-specific LIMIT clause to the query.
3382
     *
3383
     * @param string   $query
3384
     * @param int|null $limit
3385
     * @param int|null $offset
3386
     *
3387
     * @return string
3388
     *
3389
     * @throws DBALException
3390
     */
3391 2448
    final public function modifyLimitQuery($query, $limit, $offset = null)
3392
    {
3393 2448
        if ($limit !== null) {
3394 2016
            $limit = (int) $limit;
3395
        }
3396
3397 2448
        $offset = (int) $offset;
3398
3399 2448
        if ($offset < 0) {
3400
            throw new DBALException(sprintf(
3401
                'Offset must be a positive integer of zero, %d given',
3402
                $offset
3403
            ));
3404
        }
3405
3406 2448
        if ($offset > 0 && ! $this->supportsLimitOffset()) {
3407
            throw new DBALException(sprintf(
3408
                'Platform %s does not support offset values in limit queries.',
3409
                $this->getName()
3410
            ));
3411
        }
3412
3413 2448
        return $this->doModifyLimitQuery($query, $limit, $offset);
3414
    }
3415
3416
    /**
3417
     * Adds an platform-specific LIMIT clause to the query.
3418
     *
3419
     * @param string   $query
3420
     * @param int|null $limit
3421
     * @param int|null $offset
3422
     *
3423
     * @return string
3424
     */
3425 380
    protected function doModifyLimitQuery($query, $limit, $offset)
3426
    {
3427 380
        if ($limit !== null) {
3428 265
            $query .= ' LIMIT ' . $limit;
3429
        }
3430
3431 380
        if ($offset > 0) {
3432 49
            $query .= ' OFFSET ' . $offset;
3433
        }
3434
3435 380
        return $query;
3436
    }
3437
3438
    /**
3439
     * Whether the database platform support offsets in modify limit clauses.
3440
     *
3441
     * @return bool
3442
     */
3443 310
    public function supportsLimitOffset()
3444
    {
3445 310
        return true;
3446
    }
3447
3448
    /**
3449
     * Gets the character casing of a column in an SQL result set of this platform.
3450
     *
3451
     * @param string $column The column name for which to get the correct character casing.
3452
     *
3453
     * @return string The column name in the character casing used in SQL result sets.
3454
     */
3455
    public function getSQLResultCasing($column)
3456
    {
3457
        return $column;
3458
    }
3459
3460
    /**
3461
     * Makes any fixes to a name of a schema element (table, sequence, ...) that are required
3462
     * by restrictions of the platform, like a maximum length.
3463
     *
3464
     * @param string $schemaElementName
3465
     *
3466
     * @return string
3467
     */
3468
    public function fixSchemaElementName($schemaElementName)
3469
    {
3470
        return $schemaElementName;
3471
    }
3472
3473
    /**
3474
     * Maximum length of any given database identifier, like tables or column names.
3475
     *
3476
     * @return int
3477
     */
3478 304
    public function getMaxIdentifierLength()
3479
    {
3480 304
        return 63;
3481
    }
3482
3483
    /**
3484
     * Returns the insert SQL for an empty insert statement.
3485
     *
3486
     * @param string $tableName
3487
     * @param string $identifierColumnName
3488
     *
3489
     * @return string
3490
     */
3491 9
    public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName)
3492
    {
3493 9
        return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (null)';
3494
    }
3495
3496
    /**
3497
     * Generates a Truncate Table SQL statement for a given table.
3498
     *
3499
     * Cascade is not supported on many platforms but would optionally cascade the truncate by
3500
     * following the foreign keys.
3501
     *
3502
     * @param string $tableName
3503
     * @param bool   $cascade
3504
     *
3505
     * @return string
3506
     */
3507 117
    public function getTruncateTableSQL($tableName, $cascade = false)
0 ignored issues
show
Unused Code introduced by
The parameter $cascade is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

3507
    public function getTruncateTableSQL($tableName, /** @scrutinizer ignore-unused */ $cascade = false)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
3508
    {
3509 117
        $tableIdentifier = new Identifier($tableName);
3510
3511 117
        return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this);
3512
    }
3513
3514
    /**
3515
     * This is for test reasons, many vendors have special requirements for dummy statements.
3516
     *
3517
     * @return string
3518
     */
3519 125
    public function getDummySelectSQL()
3520
    {
3521 125
        $expression = func_num_args() > 0 ? func_get_arg(0) : '1';
3522
3523 125
        return sprintf('SELECT %s', $expression);
3524
    }
3525
3526
    /**
3527
     * Returns the SQL to create a new savepoint.
3528
     *
3529
     * @param string $savepoint
3530
     *
3531
     * @return string
3532
     */
3533 15
    public function createSavePoint($savepoint)
3534
    {
3535 15
        return 'SAVEPOINT ' . $savepoint;
3536
    }
3537
3538
    /**
3539
     * Returns the SQL to release a savepoint.
3540
     *
3541
     * @param string $savepoint
3542
     *
3543
     * @return string
3544
     */
3545 14
    public function releaseSavePoint($savepoint)
3546
    {
3547 14
        return 'RELEASE SAVEPOINT ' . $savepoint;
3548
    }
3549
3550
    /**
3551
     * Returns the SQL to rollback a savepoint.
3552
     *
3553
     * @param string $savepoint
3554
     *
3555
     * @return string
3556
     */
3557 15
    public function rollbackSavePoint($savepoint)
3558
    {
3559 15
        return 'ROLLBACK TO SAVEPOINT ' . $savepoint;
3560
    }
3561
3562
    /**
3563
     * Returns the keyword list instance of this platform.
3564
     *
3565
     * @return \Doctrine\DBAL\Platforms\Keywords\KeywordList
3566
     *
3567
     * @throws \Doctrine\DBAL\DBALException If no keyword list is specified.
3568
     */
3569 20219
    final public function getReservedKeywordsList()
3570
    {
3571
        // Check for an existing instantiation of the keywords class.
3572 20219
        if ($this->_keywords) {
3573 19013
            return $this->_keywords;
3574
        }
3575
3576 17501
        $class = $this->getReservedKeywordsClass();
3577 17501
        $keywords = new $class;
3578 17501
        if ( ! $keywords instanceof \Doctrine\DBAL\Platforms\Keywords\KeywordList) {
3579
            throw DBALException::notSupported(__METHOD__);
3580
        }
3581
3582
        // Store the instance so it doesn't need to be generated on every request.
3583 17501
        $this->_keywords = $keywords;
3584
3585 17501
        return $keywords;
3586
    }
3587
3588
    /**
3589
     * Returns the class name of the reserved keywords list.
3590
     *
3591
     * @return string
3592
     *
3593
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
3594
     */
3595
    protected function getReservedKeywordsClass()
3596
    {
3597
        throw DBALException::notSupported(__METHOD__);
3598
    }
3599
3600
    /**
3601
     * Quotes a literal string.
3602
     * This method is NOT meant to fix SQL injections!
3603
     * It is only meant to escape this platform's string literal
3604
     * quote character inside the given literal string.
3605
     *
3606
     * @param string $str The literal string to be quoted.
3607
     *
3608
     * @return string The quoted literal string.
3609
     */
3610 6688
    public function quoteStringLiteral($str)
3611
    {
3612 6688
        $c = $this->getStringLiteralQuoteCharacter();
3613
3614 6688
        return $c . str_replace($c, $c . $c, $str) . $c;
3615
    }
3616
3617
    /**
3618
     * Gets the character used for string literal quoting.
3619
     *
3620
     * @return string
3621
     */
3622 7012
    public function getStringLiteralQuoteCharacter()
3623
    {
3624 7012
        return "'";
3625
    }
3626
3627
    /**
3628
     * Escapes metacharacters in a string intended to be used with a LIKE
3629
     * operator.
3630
     *
3631
     * @param string $inputString a literal, unquoted string
3632
     * @param string $escapeChar  should be reused by the caller in the LIKE
3633
     *                            expression.
3634
     */
3635 342
    final public function escapeStringForLike(string $inputString, string $escapeChar) : string
3636
    {
3637 342
        return preg_replace(
3638 342
            '~([' . preg_quote($this->getLikeWildcardCharacters() . $escapeChar, '~') . '])~u',
3639 342
            addcslashes($escapeChar, '\\') . '$1',
3640 342
            $inputString
3641
        );
3642
    }
3643
3644 342
    protected function getLikeWildcardCharacters() : string
3645
    {
3646 342
        return '%_';
3647
    }
3648
}
3649