Failed Conditions
Pull Request — develop (#3536)
by Jonathan
11:35
created

AbstractPlatform::supportsPrimaryConstraints()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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

330
    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...
331
    {
332
        throw NotSupported::new('VARCHARs not supported by Platform.');
333
    }
334
335
    /**
336
     * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
337
     *
338
     * @param int  $length The length of the column.
339
     * @param bool $fixed  Whether the column length is fixed.
340
     *
341
     * @return string
342
     *
343
     * @throws DBALException If not supported on this platform.
344
     */
345
    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

345
    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...
346
    {
347
        throw NotSupported::new('BINARY/VARBINARY column types are not supported by this platform.');
348
    }
349
350
    /**
351
     * Returns the SQL snippet used to declare a CLOB column type.
352
     *
353
     * @param mixed[] $field
354
     *
355
     * @return string
356
     */
357
    abstract public function getClobTypeDeclarationSQL(array $field);
358
359
    /**
360
     * Returns the SQL Snippet used to declare a BLOB column type.
361
     *
362
     * @param mixed[] $field
363
     *
364
     * @return string
365
     */
366
    abstract public function getBlobTypeDeclarationSQL(array $field);
367
368
    /**
369
     * Gets the name of the platform.
370
     *
371
     * @return string
372
     */
373
    abstract public function getName();
374
375
    /**
376
     * Registers a doctrine type to be used in conjunction with a column type of this platform.
377
     *
378
     * @param string $dbType
379
     * @param string $doctrineType
380
     *
381
     * @throws DBALException If the type is not found.
382
     */
383 52573
    public function registerDoctrineTypeMapping($dbType, $doctrineType)
384
    {
385 52573
        if ($this->doctrineTypeMapping === null) {
386 52091
            $this->initializeAllDoctrineTypeMappings();
387
        }
388
389 52573
        if (! Types\Type::hasType($doctrineType)) {
390 50124
            throw TypeNotFound::new($doctrineType);
391
        }
392
393 52561
        $dbType                             = strtolower($dbType);
394 52561
        $this->doctrineTypeMapping[$dbType] = $doctrineType;
395
396 52561
        $doctrineType = Type::getType($doctrineType);
397
398 52561
        if (! $doctrineType->requiresSQLCommentHint($this)) {
399 52549
            return;
400
        }
401
402 50097
        $this->markDoctrineTypeCommented($doctrineType);
403 50097
    }
404
405
    /**
406
     * Gets the Doctrine type that is mapped for the given database column type.
407
     *
408
     * @param string $dbType
409
     *
410
     * @return string
411
     *
412
     * @throws DBALException
413
     */
414 55067
    public function getDoctrineTypeMapping($dbType)
415
    {
416 55067
        if ($this->doctrineTypeMapping === null) {
417 55005
            $this->initializeAllDoctrineTypeMappings();
418
        }
419
420 55067
        $dbType = strtolower($dbType);
421
422 55067
        if (! isset($this->doctrineTypeMapping[$dbType])) {
423 50178
            throw new DBALException(sprintf(
424 12
                'Unknown database type "%s" requested, "%s" may not support it.',
425 50178
                $dbType,
426 50178
                static::class
427
            ));
428
        }
429
430 55055
        return $this->doctrineTypeMapping[$dbType];
431
    }
432
433
    /**
434
     * Checks if a database type is currently supported by this platform.
435
     *
436
     * @param string $dbType
437
     *
438
     * @return bool
439
     */
440 51700
    public function hasDoctrineTypeMappingFor($dbType)
441
    {
442 51700
        if ($this->doctrineTypeMapping === null) {
443 47887
            $this->initializeAllDoctrineTypeMappings();
444
        }
445
446 51700
        $dbType = strtolower($dbType);
447
448 51700
        return isset($this->doctrineTypeMapping[$dbType]);
449
    }
450
451
    /**
452
     * Initializes the Doctrine Type comments instance variable for in_array() checks.
453
     *
454
     * @return void
455
     */
456 58828
    protected function initializeCommentedDoctrineTypes()
457
    {
458 58828
        $this->doctrineTypeComments = [];
459
460 58828
        foreach (Type::getTypesMap() as $typeName => $className) {
461 58828
            $type = Type::getType($typeName);
462
463 58828
            if (! $type->requiresSQLCommentHint($this)) {
464 58828
                continue;
465
            }
466
467 58828
            $this->doctrineTypeComments[] = $typeName;
468
        }
469 58828
    }
470
471
    /**
472
     * Is it necessary for the platform to add a parsable type comment to allow reverse engineering the given type?
473
     *
474
     * @return bool
475
     */
476 58958
    public function isCommentedDoctrineType(Type $doctrineType)
477
    {
478 58958
        if ($this->doctrineTypeComments === null) {
479 58816
            $this->initializeCommentedDoctrineTypes();
480
        }
481
482 58958
        assert(is_array($this->doctrineTypeComments));
483
484 58958
        return in_array($doctrineType->getName(), $this->doctrineTypeComments);
485
    }
486
487
    /**
488
     * Marks this type as to be commented in ALTER TABLE and CREATE TABLE statements.
489
     *
490
     * @param string|Type $doctrineType
491
     *
492
     * @return void
493
     */
494 50097
    public function markDoctrineTypeCommented($doctrineType)
495
    {
496 50097
        if ($this->doctrineTypeComments === null) {
497 50097
            $this->initializeCommentedDoctrineTypes();
498
        }
499
500 50097
        assert(is_array($this->doctrineTypeComments));
501
502 50097
        $this->doctrineTypeComments[] = $doctrineType instanceof Type ? $doctrineType->getName() : $doctrineType;
503 50097
    }
504
505
    /**
506
     * Gets the comment to append to a column comment that helps parsing this type in reverse engineering.
507
     *
508
     * @return string
509
     */
510 54186
    public function getDoctrineTypeComment(Type $doctrineType)
511
    {
512 54186
        return '(DC2Type:' . $doctrineType->getName() . ')';
513
    }
514
515
    /**
516
     * Gets the comment of a passed column modified by potential doctrine type comment hints.
517
     *
518
     * @return string|null
519
     */
520 58647
    protected function getColumnComment(Column $column)
521
    {
522 58647
        $comment = $column->getComment();
523
524 58647
        if ($this->isCommentedDoctrineType($column->getType())) {
525 54186
            $comment .= $this->getDoctrineTypeComment($column->getType());
526
        }
527
528 58647
        return $comment;
529
    }
530
531
    /**
532
     * Gets the character used for identifier quoting.
533
     *
534
     * @return string
535
     */
536 52216
    public function getIdentifierQuoteCharacter()
537
    {
538 52216
        return '"';
539
    }
540
541
    /**
542
     * Gets the string portion that starts an SQL comment.
543
     *
544
     * @return string
545
     */
546
    public function getSqlCommentStartString()
547
    {
548
        return '--';
549
    }
550
551
    /**
552
     * Gets the string portion that ends an SQL comment.
553
     *
554
     * @return string
555
     */
556
    public function getSqlCommentEndString()
557
    {
558
        return "\n";
559
    }
560
561
    /**
562
     * Gets the maximum length of a char field.
563
     */
564 55078
    public function getCharMaxLength() : int
565
    {
566 55078
        return $this->getVarcharMaxLength();
567
    }
568
569
    /**
570
     * Gets the maximum length of a varchar field.
571
     *
572
     * @return int
573
     */
574 51471
    public function getVarcharMaxLength()
575
    {
576 51471
        return 4000;
577
    }
578
579
    /**
580
     * Gets the default length of a varchar field.
581
     *
582
     * @return int
583
     */
584 53312
    public function getVarcharDefaultLength()
585
    {
586 53312
        return 255;
587
    }
588
589
    /**
590
     * Gets the maximum length of a binary field.
591
     *
592
     * @return int
593
     */
594
    public function getBinaryMaxLength()
595
    {
596
        return 4000;
597
    }
598
599
    /**
600
     * Gets the default length of a binary field.
601
     *
602
     * @return int
603
     */
604 51891
    public function getBinaryDefaultLength()
605
    {
606 51891
        return 255;
607
    }
608
609
    /**
610
     * Gets all SQL wildcard characters of the platform.
611
     *
612
     * @return string[]
613
     */
614
    public function getWildcards()
615
    {
616
        return ['%', '_'];
617
    }
618
619
    /**
620
     * Returns the regular expression operator.
621
     *
622
     * @throws DBALException If not supported on this platform.
623
     */
624 36561
    public function getRegexpExpression() : string
625
    {
626 36561
        throw NotSupported::new(__METHOD__);
627
    }
628
629
    /**
630
     * Returns the SQL snippet to get the average value of a column.
631
     *
632
     * @param string $value SQL expression producing the value.
633
     */
634
    public function getAvgExpression(string $value) : string
635
    {
636
        return 'AVG(' . $value . ')';
637
    }
638
639
    /**
640
     * Returns the SQL snippet to get the number of rows (without a NULL value) of a column.
641
     *
642
     * If a '*' is used instead of a column the number of selected rows is returned.
643
     *
644
     * @param string $expression The expression to count.
645
     */
646
    public function getCountExpression(string $expression) : string
647
    {
648
        return 'COUNT(' . $expression . ')';
649
    }
650
651
    /**
652
     * Returns the SQL snippet to get the maximum value in a set of values.
653
     *
654
     * @param string $value SQL expression producing the value.
655
     */
656
    public function getMaxExpression(string $value) : string
657
    {
658
        return 'MAX(' . $value . ')';
659
    }
660
661
    /**
662
     * Returns the SQL snippet to get the minimum value in a set of values.
663
     *
664
     * @param string $value SQL expression producing the value.
665
     */
666
    public function getMinExpression(string $value) : string
667
    {
668
        return 'MIN(' . $value . ')';
669
    }
670
671
    /**
672
     * Returns the SQL snippet to get the total sum of the values in a set.
673
     *
674
     * @param string $value SQL expression producing the value.
675
     */
676
    public function getSumExpression(string $value) : string
677
    {
678
        return 'SUM(' . $value . ')';
679
    }
680
681
    // scalar functions
682
683
    /**
684
     * Returns the SQL snippet to get the md5 sum of the value.
685
     *
686
     * Note: Not SQL92, but common functionality.
687
     *
688
     * @param string $string SQL expression producing the string.
689
     */
690
    public function getMd5Expression(string $string) : string
691
    {
692
        return 'MD5(' . $string . ')';
693
    }
694
695
    /**
696
     * Returns the SQL snippet to get the length of a text field.
697
     *
698
     * @param string $string SQL expression producing the string.
699
     */
700
    public function getLengthExpression(string $string) : string
701
    {
702
        return 'LENGTH(' . $string . ')';
703
    }
704
705
    /**
706
     * Returns the SQL snippet to get the square root of the value.
707
     *
708
     * @param string $number SQL expression producing the number.
709
     */
710
    public function getSqrtExpression(string $number) : string
711
    {
712
        return 'SQRT(' . $number . ')';
713
    }
714
715
    /**
716
     * Returns the SQL snippet to round a number to the number of decimals specified.
717
     *
718
     * @param string $number   SQL expression producing the number to round.
719
     * @param string $decimals SQL expression producing the number of decimals.
720
     */
721
    public function getRoundExpression(string $number, string $decimals = '0') : string
722
    {
723
        return 'ROUND(' . $number . ', ' . $decimals . ')';
724
    }
725
726
    /**
727
     * Returns the SQL snippet to get the remainder of the operation of division of dividend by divisor.
728
     *
729
     * @param string $dividend SQL expression producing the dividend.
730
     * @param string $divisor  SQL expression producing the divisor.
731
     */
732
    public function getModExpression(string $dividend, string $divisor) : string
733
    {
734
        return 'MOD(' . $dividend . ', ' . $divisor . ')';
735
    }
736
737
    /**
738
     * Returns the SQL snippet to trim a string.
739
     *
740
     * @param string      $str  The expression to apply the trim to.
741
     * @param int         $mode The position of the trim (leading/trailing/both).
742
     * @param string|null $char The char to trim, has to be quoted already. Defaults to space.
743
     *
744
     * @throws InvalidArgumentException
745
     */
746 55569
    public function getTrimExpression(string $str, int $mode = TrimMode::UNSPECIFIED, ?string $char = null) : string
747
    {
748 55569
        $tokens = [];
749
750 55569
        switch ($mode) {
751
            case TrimMode::UNSPECIFIED:
752 55541
                break;
753
754
            case TrimMode::LEADING:
755 55515
                $tokens[] = 'LEADING';
756 55515
                break;
757
758
            case TrimMode::TRAILING:
759 55489
                $tokens[] = 'TRAILING';
760 55489
                break;
761
762
            case TrimMode::BOTH:
763 55489
                $tokens[] = 'BOTH';
764 55489
                break;
765
766
            default:
767 54883
                throw new InvalidArgumentException(
768 54883
                    sprintf(
769 1
                        'The value of $mode is expected to be one of the TrimMode constants, "%d" given.',
770 54883
                        $mode
771
                    )
772
                );
773
        }
774
775 55568
        if ($char !== null) {
776 55482
            $tokens[] = $char;
777
        }
778
779 55568
        if (count($tokens) > 0) {
780 55540
            $tokens[] = 'FROM';
781
        }
782
783 55568
        $tokens[] = $str;
784
785 55568
        return sprintf('TRIM(%s)', implode(' ', $tokens));
786
    }
787
788
    /**
789
     * Returns the SQL snippet to trim trailing space characters from the string.
790
     *
791
     * @param string $string SQL expression producing the string.
792
     */
793 18388
    public function getRtrimExpression(string $string) : string
794
    {
795 18388
        return 'RTRIM(' . $string . ')';
796
    }
797
798
    /**
799
     * Returns the SQL snippet to trim leading space characters from the string.
800
     *
801
     * @param string $string SQL expression producing the string.
802
     */
803 18388
    public function getLtrimExpression(string $string) : string
804
    {
805 18388
        return 'LTRIM(' . $string . ')';
806
    }
807
808
    /**
809
     * Returns the SQL snippet to change all characters from the string to uppercase,
810
     * according to the current character set mapping.
811
     *
812
     * @param string $string SQL expression producing the string.
813
     */
814
    public function getUpperExpression(string $string) : string
815
    {
816
        return 'UPPER(' . $string . ')';
817
    }
818
819
    /**
820
     * Returns the SQL snippet to change all characters from the string to lowercase,
821
     * according to the current character set mapping.
822
     *
823
     * @param string $string SQL expression producing the string.
824
     */
825
    public function getLowerExpression(string $string) : string
826
    {
827
        return 'LOWER(' . $string . ')';
828
    }
829
830
    /**
831
     * Returns the SQL snippet to get the position of the first occurrence of the substring in the string.
832
     *
833
     * @param string      $string    SQL expression producing the string to locate the substring in.
834
     * @param string      $substring SQL expression producing the substring to locate.
835
     * @param string|null $start     SQL expression producing the position to start at.
836
     *                               Defaults to the beginning of the string.
837
     *
838
     * @throws DBALException If not supported on this platform.
839
     */
840
    public function getLocateExpression(string $string, string $substring, ?string $start = null) : string
0 ignored issues
show
Unused Code introduced by
The parameter $substring 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

840
    public function getLocateExpression(string $string, /** @scrutinizer ignore-unused */ string $substring, ?string $start = null) : string

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...
841
    {
842
        throw NotSupported::new(__METHOD__);
843
    }
844
845
    /**
846
     * Returns the SQL snippet to get the current system date.
847
     */
848
    public function getNowExpression() : string
849
    {
850
        return 'NOW()';
851
    }
852
853
    /**
854
     * Returns an SQL snippet to get a substring inside the string.
855
     *
856
     * Note: Not SQL92, but common functionality.
857
     *
858
     * @param string      $string SQL expression producing the string from which a substring should be extracted.
859
     * @param string      $start  SQL expression producing the position to start at,
860
     * @param string|null $length SQL expression producing the length of the substring portion to be returned.
861
     *                            By default, the entire substring is returned.
862
     */
863 55242
    public function getSubstringExpression(string $string, string $start, ?string $length = null) : string
864
    {
865 55242
        if ($length === null) {
866 55242
            return sprintf('SUBSTRING(%s FROM %s)', $string, $start);
867
        }
868
869 55211
        return sprintf('SUBSTRING(%s FROM %s FOR %s)', $string, $start, $length);
870
    }
871
872
    /**
873
     * Returns a SQL snippet to concatenate the given strings.
874
     *
875
     * @param string[] ...$string
876
     */
877 37264
    public function getConcatExpression(string ...$string) : string
878
    {
879 37264
        return implode(' || ', $string);
880
    }
881
882
    /**
883
     * Returns the SQL for a logical not.
884
     *
885
     * @param string $value SQL expression producing the value to negate.
886
     */
887
    public function getNotExpression(string $value) : string
888
    {
889
        return 'NOT(' . $value . ')';
890
    }
891
892
    /**
893
     * Returns the SQL that checks if an expression is null.
894
     *
895
     * @param string $value SQL expression producing the to be compared to null.
896
     */
897 59074
    public function getIsNullExpression(string $value) : string
898
    {
899 59074
        return $value . ' IS NULL';
900
    }
901
902
    /**
903
     * Returns the SQL that checks if an expression is not null.
904
     *
905
     * @param string $value SQL expression producing the to be compared to null.
906
     */
907
    public function getIsNotNullExpression(string $value) : string
908
    {
909
        return $value . ' IS NOT NULL';
910
    }
911
912
    /**
913
     * Returns the SQL that checks if an expression evaluates to a value between two values.
914
     *
915
     * The parameter $value is checked if it is between $min and $max.
916
     *
917
     * Note: There is a slight difference in the way BETWEEN works on some databases.
918
     * http://www.w3schools.com/sql/sql_between.asp. If you want complete database
919
     * independence you should avoid using between().
920
     *
921
     * @param string $value SQL expression producing the value to compare.
922
     * @param string $min   SQL expression producing the lower value to compare with.
923
     * @param string $max   SQL expression producing the higher value to compare with.
924
     */
925
    public function getBetweenExpression(string $value, string $min, string $max) : string
926
    {
927
        return $value . ' BETWEEN ' . $min . ' AND ' . $max;
928
    }
929
930
    /**
931
     * Returns the SQL to get the arccosine of a value.
932
     *
933
     * @param string $number SQL expression producing the number.
934
     */
935
    public function getAcosExpression(string $number) : string
936
    {
937
        return 'ACOS(' . $number . ')';
938
    }
939
940
    /**
941
     * Returns the SQL to get the sine of a value.
942
     *
943
     * @param string $number SQL expression producing the number.
944
     */
945
    public function getSinExpression(string $number) : string
946
    {
947
        return 'SIN(' . $number . ')';
948
    }
949
950
    /**
951
     * Returns the SQL to get the PI value.
952
     */
953
    public function getPiExpression() : string
954
    {
955
        return 'PI()';
956
    }
957
958
    /**
959
     * Returns the SQL to get the cosine of a value.
960
     *
961
     * @param string $number SQL expression producing the number.
962
     */
963
    public function getCosExpression(string $number) : string
964
    {
965
        return 'COS(' . $number . ')';
966
    }
967
968
    /**
969
     * Returns the SQL to calculate the difference in days between the two passed dates.
970
     *
971
     * Computes diff = date1 - date2.
972
     *
973
     * @throws DBALException If not supported on this platform.
974
     */
975
    public function getDateDiffExpression(string $date1, string $date2) : string
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

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

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

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

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...
976
    {
977
        throw NotSupported::new(__METHOD__);
978
    }
979
980
    /**
981
     * Returns the SQL to add the number of given seconds to a date.
982
     *
983
     * @param string $date    SQL expression producing the date.
984
     * @param string $seconds SQL expression producing the number of seconds.
985
     *
986
     * @throws DBALException If not supported on this platform.
987
     */
988 56941
    public function getDateAddSecondsExpression(string $date, string $seconds) : string
989
    {
990 56941
        return $this->getDateArithmeticIntervalExpression($date, '+', $seconds, DateIntervalUnit::SECOND);
991
    }
992
993
    /**
994
     * Returns the SQL to subtract the number of given seconds from a date.
995
     *
996
     * @param string $date    SQL expression producing the date.
997
     * @param string $seconds SQL expression producing the number of seconds.
998
     *
999
     * @throws DBALException If not supported on this platform.
1000
     */
1001 56887
    public function getDateSubSecondsExpression(string $date, string $seconds) : string
1002
    {
1003 56887
        return $this->getDateArithmeticIntervalExpression($date, '-', $seconds, DateIntervalUnit::SECOND);
1004
    }
1005
1006
    /**
1007
     * Returns the SQL to add the number of given minutes to a date.
1008
     *
1009
     * @param string $date    SQL expression producing the date.
1010
     * @param string $minutes SQL expression producing the number of minutes.
1011
     *
1012
     * @throws DBALException If not supported on this platform.
1013
     */
1014 56833
    public function getDateAddMinutesExpression(string $date, string $minutes) : string
1015
    {
1016 56833
        return $this->getDateArithmeticIntervalExpression($date, '+', $minutes, DateIntervalUnit::MINUTE);
1017
    }
1018
1019
    /**
1020
     * Returns the SQL to subtract the number of given minutes from a date.
1021
     *
1022
     * @param string $date    SQL expression producing the date.
1023
     * @param string $minutes SQL expression producing the number of minutes.
1024
     *
1025
     * @throws DBALException If not supported on this platform.
1026
     */
1027 56779
    public function getDateSubMinutesExpression(string $date, string $minutes) : string
1028
    {
1029 56779
        return $this->getDateArithmeticIntervalExpression($date, '-', $minutes, DateIntervalUnit::MINUTE);
1030
    }
1031
1032
    /**
1033
     * Returns the SQL to add the number of given hours to a date.
1034
     *
1035
     * @param string $date  SQL expression producing the date.
1036
     * @param string $hours SQL expression producing the number of hours.
1037
     *
1038
     * @throws DBALException If not supported on this platform.
1039
     */
1040 56725
    public function getDateAddHourExpression(string $date, string $hours) : string
1041
    {
1042 56725
        return $this->getDateArithmeticIntervalExpression($date, '+', $hours, DateIntervalUnit::HOUR);
1043
    }
1044
1045
    /**
1046
     * Returns the SQL to subtract the number of given hours to a date.
1047
     *
1048
     * @param string $date  SQL expression producing the date.
1049
     * @param string $hours SQL expression producing the number of hours.
1050
     *
1051
     * @throws DBALException If not supported on this platform.
1052
     */
1053 56671
    public function getDateSubHourExpression(string $date, string $hours) : string
1054
    {
1055 56671
        return $this->getDateArithmeticIntervalExpression($date, '-', $hours, DateIntervalUnit::HOUR);
1056
    }
1057
1058
    /**
1059
     * Returns the SQL to add the number of given days to a date.
1060
     *
1061
     * @param string $date SQL expression producing the date.
1062
     * @param string $days SQL expression producing the number of days.
1063
     *
1064
     * @throws DBALException If not supported on this platform.
1065
     */
1066 56619
    public function getDateAddDaysExpression(string $date, string $days) : string
1067
    {
1068 56619
        return $this->getDateArithmeticIntervalExpression($date, '+', $days, DateIntervalUnit::DAY);
1069
    }
1070
1071
    /**
1072
     * Returns the SQL to subtract the number of given days to a date.
1073
     *
1074
     * @param string $date SQL expression producing the date.
1075
     * @param string $days SQL expression producing the number of days.
1076
     *
1077
     * @throws DBALException If not supported on this platform.
1078
     */
1079 56563
    public function getDateSubDaysExpression(string $date, string $days) : string
1080
    {
1081 56563
        return $this->getDateArithmeticIntervalExpression($date, '-', $days, DateIntervalUnit::DAY);
1082
    }
1083
1084
    /**
1085
     * Returns the SQL to add the number of given weeks to a date.
1086
     *
1087
     * @param string $date  SQL expression producing the date.
1088
     * @param string $weeks SQL expression producing the number of weeks.
1089
     *
1090
     * @throws DBALException If not supported on this platform.
1091
     */
1092 56509
    public function getDateAddWeeksExpression(string $date, string $weeks) : string
1093
    {
1094 56509
        return $this->getDateArithmeticIntervalExpression($date, '+', $weeks, DateIntervalUnit::WEEK);
1095
    }
1096
1097
    /**
1098
     * Returns the SQL to subtract the number of given weeks from a date.
1099
     *
1100
     * @param string $date  SQL expression producing the date.
1101
     * @param string $weeks SQL expression producing the number of weeks.
1102
     *
1103
     * @throws DBALException If not supported on this platform.
1104
     */
1105 56455
    public function getDateSubWeeksExpression(string $date, string $weeks) : string
1106
    {
1107 56455
        return $this->getDateArithmeticIntervalExpression($date, '-', $weeks, DateIntervalUnit::WEEK);
1108
    }
1109
1110
    /**
1111
     * Returns the SQL to add the number of given months to a date.
1112
     *
1113
     * @param string $date   SQL expression producing the date.
1114
     * @param string $months SQL expression producing the number of months.
1115
     *
1116
     * @throws DBALException If not supported on this platform.
1117
     */
1118 56401
    public function getDateAddMonthExpression(string $date, string $months) : string
1119
    {
1120 56401
        return $this->getDateArithmeticIntervalExpression($date, '+', $months, DateIntervalUnit::MONTH);
1121
    }
1122
1123
    /**
1124
     * Returns the SQL to subtract the number of given months to a date.
1125
     *
1126
     * @param string $date   SQL expression producing the date.
1127
     * @param string $months SQL expression producing the number of months.
1128
     *
1129
     * @throws DBALException If not supported on this platform.
1130
     */
1131 56347
    public function getDateSubMonthExpression(string $date, string $months) : string
1132
    {
1133 56347
        return $this->getDateArithmeticIntervalExpression($date, '-', $months, DateIntervalUnit::MONTH);
1134
    }
1135
1136
    /**
1137
     * Returns the SQL to add the number of given quarters to a date.
1138
     *
1139
     * @param string $date     SQL expression producing the date.
1140
     * @param string $quarters SQL expression producing the number of quarters.
1141
     *
1142
     * @throws DBALException If not supported on this platform.
1143
     */
1144 56293
    public function getDateAddQuartersExpression(string $date, string $quarters) : string
1145
    {
1146 56293
        return $this->getDateArithmeticIntervalExpression($date, '+', $quarters, DateIntervalUnit::QUARTER);
1147
    }
1148
1149
    /**
1150
     * Returns the SQL to subtract the number of given quarters from a date.
1151
     *
1152
     * @param string $date     SQL expression producing the date.
1153
     * @param string $quarters SQL expression producing the number of quarters.
1154
     *
1155
     * @throws DBALException If not supported on this platform.
1156
     */
1157 56239
    public function getDateSubQuartersExpression(string $date, string $quarters) : string
1158
    {
1159 56239
        return $this->getDateArithmeticIntervalExpression($date, '-', $quarters, DateIntervalUnit::QUARTER);
1160
    }
1161
1162
    /**
1163
     * Returns the SQL to add the number of given years to a date.
1164
     *
1165
     * @param string $date  SQL expression producing the date.
1166
     * @param string $years SQL expression producing the number of years.
1167
     *
1168
     * @throws DBALException If not supported on this platform.
1169
     */
1170 56185
    public function getDateAddYearsExpression(string $date, string $years) : string
1171
    {
1172 56185
        return $this->getDateArithmeticIntervalExpression($date, '+', $years, DateIntervalUnit::YEAR);
1173
    }
1174
1175
    /**
1176
     * Returns the SQL to subtract the number of given years from a date.
1177
     *
1178
     * @param string $date  SQL expression producing the date.
1179
     * @param string $years SQL expression producing the number of years.
1180
     *
1181
     * @throws DBALException If not supported on this platform.
1182
     */
1183 56131
    public function getDateSubYearsExpression(string $date, string $years) : string
1184
    {
1185 56131
        return $this->getDateArithmeticIntervalExpression($date, '-', $years, DateIntervalUnit::YEAR);
1186
    }
1187
1188
    /**
1189
     * Returns the SQL for a date arithmetic expression.
1190
     *
1191
     * @param string $date     SQL expression representing a date to perform the arithmetic operation on.
1192
     * @param string $operator The arithmetic operator (+ or -).
1193
     * @param string $interval SQL expression representing the value of the interval that shall be calculated
1194
     *                         into the date.
1195
     * @param string $unit     The unit of the interval that shall be calculated into the date.
1196
     *                         One of the DATE_INTERVAL_UNIT_* constants.
1197
     *
1198
     * @throws DBALException If not supported on this platform.
1199
     */
1200
    protected function getDateArithmeticIntervalExpression(string $date, string $operator, string $interval, string $unit) : string
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

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

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

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

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...
1201
    {
1202
        throw NotSupported::new(__METHOD__);
1203
    }
1204
1205
    /**
1206
     * Generates the SQL expression which represents the given date interval multiplied by a number
1207
     *
1208
     * @param string $interval   SQL expression describing the interval value
1209
     * @param int    $multiplier Interval multiplier
1210
     *
1211
     * @throws DBALException
1212
     */
1213 52108
    protected function multiplyInterval(string $interval, int $multiplier) : string
1214
    {
1215 52108
        return sprintf('(%s * %d)', $interval, $multiplier);
1216
    }
1217
1218
    /**
1219
     * Returns the SQL bit AND comparison expression.
1220
     *
1221
     * @param string $value1 SQL expression producing the first value.
1222
     * @param string $value2 SQL expression producing the second value.
1223
     */
1224 55995
    public function getBitAndComparisonExpression(string $value1, string $value2) : string
1225
    {
1226 55995
        return '(' . $value1 . ' & ' . $value2 . ')';
1227
    }
1228
1229
    /**
1230
     * Returns the SQL bit OR comparison expression.
1231
     *
1232
     * @param string $value1 SQL expression producing the first value.
1233
     * @param string $value2 SQL expression producing the second value.
1234
     */
1235 55995
    public function getBitOrComparisonExpression(string $value1, string $value2) : string
1236
    {
1237 55995
        return '(' . $value1 . ' | ' . $value2 . ')';
1238
    }
1239
1240
    /**
1241
     * Returns the FOR UPDATE expression.
1242
     *
1243
     * @return string
1244
     */
1245 50237
    public function getForUpdateSQL()
1246
    {
1247 50237
        return 'FOR UPDATE';
1248
    }
1249
1250
    /**
1251
     * Honors that some SQL vendors such as MsSql use table hints for locking instead of the ANSI SQL FOR UPDATE specification.
1252
     *
1253
     * @param string   $fromClause The FROM clause to append the hint for the given lock mode to.
1254
     * @param int|null $lockMode   One of the Doctrine\DBAL\LockMode::* constants. If null is given, nothing will
1255
     *                             be appended to the FROM clause.
1256
     *
1257
     * @return string
1258
     */
1259 50237
    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

1259
    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...
1260
    {
1261 50237
        return $fromClause;
1262
    }
1263
1264
    /**
1265
     * Returns the SQL snippet to append to any SELECT statement which locks rows in shared read lock.
1266
     *
1267
     * This defaults to the ANSI SQL "FOR UPDATE", which is an exclusive lock (Write). Some database
1268
     * vendors allow to lighten this constraint up to be a real read lock.
1269
     *
1270
     * @return string
1271
     */
1272
    public function getReadLockSQL()
1273
    {
1274
        return $this->getForUpdateSQL();
1275
    }
1276
1277
    /**
1278
     * Returns the SQL snippet to append to any SELECT statement which obtains an exclusive lock on the rows.
1279
     *
1280
     * The semantics of this lock mode should equal the SELECT .. FOR UPDATE of the ANSI SQL standard.
1281
     *
1282
     * @return string
1283
     */
1284 50237
    public function getWriteLockSQL()
1285
    {
1286 50237
        return $this->getForUpdateSQL();
1287
    }
1288
1289
    /**
1290
     * Returns the SQL snippet to drop an existing database.
1291
     *
1292
     * @param string $database The name of the database that should be dropped.
1293
     *
1294
     * @return string
1295
     */
1296 37549
    public function getDropDatabaseSQL($database)
1297
    {
1298 37549
        return 'DROP DATABASE ' . $database;
1299
    }
1300
1301
    /**
1302
     * Returns the SQL snippet to drop an existing table.
1303
     *
1304
     * @param Table|string $table
1305
     *
1306
     * @return string
1307
     *
1308
     * @throws InvalidArgumentException
1309
     */
1310 58362
    public function getDropTableSQL($table)
1311
    {
1312 58362
        $tableArg = $table;
1313
1314 58362
        if ($table instanceof Table) {
1315 7633
            $table = $table->getQuotedName($this);
1316
        }
1317
1318 58362
        if (! is_string($table)) {
0 ignored issues
show
introduced by
The condition is_string($table) is always true.
Loading history...
1319
            throw new InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.');
1320
        }
1321
1322 58362
        if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaDropTable)) {
1323 49287
            $eventArgs = new SchemaDropTableEventArgs($tableArg, $this);
1324 49287
            $this->_eventManager->dispatchEvent(Events::onSchemaDropTable, $eventArgs);
1325
1326 49287
            if ($eventArgs->isDefaultPrevented()) {
1327
                $sql = $eventArgs->getSql();
1328
1329
                if ($sql === null) {
1330
                    throw new UnexpectedValueException('Default implementation of DROP TABLE was overridden with NULL.');
1331
                }
1332
1333
                return $sql;
1334
            }
1335
        }
1336
1337 58362
        return 'DROP TABLE ' . $table;
1338
    }
1339
1340
    /**
1341
     * Returns the SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction.
1342
     *
1343
     * @param Table|string $table
1344
     *
1345
     * @return string
1346
     */
1347 11588
    public function getDropTemporaryTableSQL($table)
1348
    {
1349 11588
        return $this->getDropTableSQL($table);
1350
    }
1351
1352
    /**
1353
     * Returns the SQL to drop an index from a table.
1354
     *
1355
     * @param Index|string $index
1356
     * @param Table|string $table
1357
     *
1358
     * @return string
1359
     *
1360
     * @throws InvalidArgumentException
1361
     */
1362 36321
    public function getDropIndexSQL($index, $table = null)
1363
    {
1364 36321
        if ($index instanceof Index) {
1365 36303
            $index = $index->getQuotedName($this);
1366 11932
        } elseif (! is_string($index)) {
0 ignored issues
show
introduced by
The condition is_string($index) is always true.
Loading history...
1367
            throw new InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.');
1368
        }
1369
1370 36321
        return 'DROP INDEX ' . $index;
1371
    }
1372
1373
    /**
1374
     * Returns the SQL to drop a constraint.
1375
     *
1376
     * @param Constraint|string $constraint
1377
     * @param Table|string      $table
1378
     *
1379
     * @return string
1380
     */
1381 49541
    public function getDropConstraintSQL($constraint, $table)
1382
    {
1383 49541
        if (! $constraint instanceof Constraint) {
1384 48598
            $constraint = new Identifier($constraint);
1385
        }
1386
1387 49541
        if (! $table instanceof Table) {
1388 49541
            $table = new Identifier($table);
1389
        }
1390
1391 49541
        $constraint = $constraint->getQuotedName($this);
1392 49541
        $table      = $table->getQuotedName($this);
1393
1394 49541
        return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $constraint;
1395
    }
1396
1397
    /**
1398
     * Returns the SQL to drop a foreign key.
1399
     *
1400
     * @param ForeignKeyConstraint|string $foreignKey
1401
     * @param Table|string                $table
1402
     *
1403
     * @return string
1404
     */
1405 52376
    public function getDropForeignKeySQL($foreignKey, $table)
1406
    {
1407 52376
        if (! $foreignKey instanceof ForeignKeyConstraint) {
1408 48497
            $foreignKey = new Identifier($foreignKey);
1409
        }
1410
1411 52376
        if (! $table instanceof Table) {
1412 52376
            $table = new Identifier($table);
1413
        }
1414
1415 52376
        $foreignKey = $foreignKey->getQuotedName($this);
1416 52376
        $table      = $table->getQuotedName($this);
1417
1418 52376
        return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $foreignKey;
1419
    }
1420
1421
    /**
1422
     * Returns the SQL statement(s) to create a table with the specified name, columns and constraints
1423
     * on this platform.
1424
     *
1425
     * @param int $createFlags
1426
     *
1427
     * @return string[] The sequence of SQL statements.
1428
     *
1429
     * @throws DBALException
1430
     * @throws InvalidArgumentException
1431
     */
1432 58551
    public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDEXES)
1433
    {
1434 58551
        if (! is_int($createFlags)) {
0 ignored issues
show
introduced by
The condition is_int($createFlags) is always true.
Loading history...
1435
            throw new InvalidArgumentException('Second argument of AbstractPlatform::getCreateTableSQL() has to be an integer.');
1436
        }
1437
1438 58551
        if (count($table->getColumns()) === 0) {
1439 49611
            throw NoColumnsSpecifiedForTable::new($table->getName());
1440
        }
1441
1442 58539
        $tableName                    = $table->getQuotedName($this);
1443 58539
        $options                      = $table->getOptions();
1444 58539
        $options['uniqueConstraints'] = [];
1445 58539
        $options['indexes']           = [];
1446 58539
        $options['primary']           = [];
1447
1448 58539
        if (($createFlags & self::CREATE_INDEXES) > 0) {
1449 58525
            foreach ($table->getIndexes() as $index) {
1450
                /** @var $index Index */
1451 58421
                if (! $index->isPrimary()) {
1452 55963
                    $options['indexes'][$index->getQuotedName($this)] = $index;
1453
1454 55963
                    continue;
1455
                }
1456
1457 58373
                $options['primary']       = $index->getQuotedColumns($this);
1458 58373
                $options['primary_index'] = $index;
1459
            }
1460
1461 58525
            foreach ($table->getUniqueConstraints() as $uniqueConstraint) {
1462
                /** @var UniqueConstraint $uniqueConstraint */
1463
                $options['uniqueConstraints'][$uniqueConstraint->getQuotedName($this)] = $uniqueConstraint;
1464
            }
1465
        }
1466
1467 58539
        if (($createFlags & self::CREATE_FOREIGNKEYS) > 0) {
1468 58386
            $options['foreignKeys'] = [];
1469
1470 58386
            foreach ($table->getForeignKeys() as $fkConstraint) {
1471 55865
                $options['foreignKeys'][] = $fkConstraint;
1472
            }
1473
        }
1474
1475 58539
        $columnSql = [];
1476 58539
        $columns   = [];
1477
1478 58539
        foreach ($table->getColumns() as $column) {
1479 58539
            if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn)) {
1480 49314
                $eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this);
1481 49314
                $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs);
1482
1483 49314
                $columnSql = array_merge($columnSql, $eventArgs->getSql());
1484
1485 49314
                if ($eventArgs->isDefaultPrevented()) {
1486
                    continue;
1487
                }
1488
            }
1489
1490 58539
            $columnData            = $column->toArray();
1491 58539
            $columnData['name']    = $column->getQuotedName($this);
1492 58539
            $columnData['version'] = $column->hasPlatformOption('version') ? $column->getPlatformOption('version') : false;
1493 58539
            $columnData['comment'] = $this->getColumnComment($column);
1494
1495 58539
            if ($columnData['type'] instanceof Types\StringType && $columnData['length'] === null) {
1496 57981
                $columnData['length'] = 255;
1497
            }
1498
1499 58539
            if (in_array($column->getName(), $options['primary'])) {
1500 58359
                $columnData['primary'] = true;
1501
            }
1502
1503 58539
            $columns[$columnData['name']] = $columnData;
1504
        }
1505
1506 58539
        if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) {
1507 49314
            $eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this);
1508 49314
            $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs);
1509
1510 49314
            if ($eventArgs->isDefaultPrevented()) {
1511
                return array_merge($eventArgs->getSql(), $columnSql);
1512
            }
1513
        }
1514
1515 58539
        $sql = $this->_getCreateTableSQL($tableName, $columns, $options);
1516 58539
        if ($this->supportsCommentOnStatement()) {
1517 52655
            foreach ($table->getColumns() as $column) {
1518 52655
                $comment = $this->getColumnComment($column);
1519
1520 52655
                if ($comment === null || $comment === '') {
1521 52643
                    continue;
1522
                }
1523
1524 50172
                $sql[] = $this->getCommentOnColumnSQL($tableName, $column->getQuotedName($this), $comment);
1525
            }
1526
        }
1527
1528 58539
        return array_merge($sql, $columnSql);
1529
    }
1530
1531
    /**
1532
     * @param string      $tableName
1533
     * @param string      $columnName
1534
     * @param string|null $comment
1535
     *
1536
     * @return string
1537
     */
1538 49284
    public function getCommentOnColumnSQL($tableName, $columnName, $comment)
1539
    {
1540 49284
        $tableName  = new Identifier($tableName);
1541 49284
        $columnName = new Identifier($columnName);
1542
1543 49284
        return sprintf(
1544 63
            'COMMENT ON COLUMN %s.%s IS %s',
1545 49284
            $tableName->getQuotedName($this),
1546 49284
            $columnName->getQuotedName($this),
1547 49284
            $this->quoteStringLiteral((string) $comment)
1548
        );
1549
    }
1550
1551
    /**
1552
     * Returns the SQL to create inline comment on a column.
1553
     *
1554
     * @param string $comment
1555
     *
1556
     * @return string
1557
     *
1558
     * @throws DBALException If not supported on this platform.
1559
     */
1560 52937
    public function getInlineColumnCommentSQL($comment)
1561
    {
1562 52937
        if (! $this->supportsInlineColumnComments()) {
1563 48365
            throw NotSupported::new(__METHOD__);
1564
        }
1565
1566 52317
        return 'COMMENT ' . $this->quoteStringLiteral($comment);
1567
    }
1568
1569
    /**
1570
     * Returns the SQL used to create a table.
1571
     *
1572
     * @param string    $tableName
1573
     * @param mixed[][] $columns
1574
     * @param mixed[]   $options
1575
     *
1576
     * @return string[]
1577
     */
1578 51276
    protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
1579
    {
1580 51276
        $columnListSql = $this->getColumnDeclarationListSQL($columns);
1581
1582 51276
        if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
1583
            foreach ($options['uniqueConstraints'] as $name => $definition) {
1584
                $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
1585
            }
1586
        }
1587
1588 51276
        if (isset($options['primary']) && ! empty($options['primary'])) {
1589 51213
            $columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')';
1590
        }
1591
1592 51276
        if (isset($options['indexes']) && ! empty($options['indexes'])) {
1593
            foreach ($options['indexes'] as $index => $definition) {
1594
                $columnListSql .= ', ' . $this->getIndexDeclarationSQL($index, $definition);
1595
            }
1596
        }
1597
1598 51276
        $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql;
1599
1600 51276
        $check = $this->getCheckDeclarationSQL($columns);
1601 51276
        if (! empty($check)) {
1602 51058
            $query .= ', ' . $check;
1603
        }
1604 51276
        $query .= ')';
1605
1606 51276
        $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...
1607
1608 51276
        if (isset($options['foreignKeys'])) {
1609 51220
            foreach ((array) $options['foreignKeys'] as $definition) {
1610 51096
                $sql[] = $this->getCreateForeignKeySQL($definition, $tableName);
1611
            }
1612
        }
1613
1614 51276
        return $sql;
1615
    }
1616
1617
    /**
1618
     * @return string
1619
     */
1620 52113
    public function getCreateTemporaryTableSnippetSQL()
1621
    {
1622 52113
        return 'CREATE TEMPORARY TABLE';
1623
    }
1624
1625
    /**
1626
     * Returns the SQL to create a sequence on this platform.
1627
     *
1628
     * @return string
1629
     *
1630
     * @throws DBALException If not supported on this platform.
1631
     */
1632
    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

1632
    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...
1633
    {
1634
        throw NotSupported::new(__METHOD__);
1635
    }
1636
1637
    /**
1638
     * Returns the SQL to change a sequence on this platform.
1639
     *
1640
     * @return string
1641
     *
1642
     * @throws DBALException If not supported on this platform.
1643
     */
1644
    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

1644
    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...
1645
    {
1646
        throw NotSupported::new(__METHOD__);
1647
    }
1648
1649
    /**
1650
     * Returns the SQL to create a constraint on a table on this platform.
1651
     *
1652
     * @param Table|string $table
1653
     *
1654
     * @return string
1655
     *
1656
     * @throws InvalidArgumentException
1657
     */
1658 49474
    public function getCreateConstraintSQL(Constraint $constraint, $table)
1659
    {
1660 49474
        if ($table instanceof Table) {
1661
            $table = $table->getQuotedName($this);
1662
        }
1663
1664 49474
        $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this);
1665
1666 49474
        $columnList = '(' . implode(', ', $constraint->getQuotedColumns($this)) . ')';
1667
1668 49474
        $referencesClause = '';
1669 49474
        if ($constraint instanceof Index) {
1670 49474
            if ($constraint->isPrimary()) {
1671 49474
                $query .= ' PRIMARY KEY';
1672 49447
            } elseif ($constraint->isUnique()) {
1673 49447
                $query .= ' UNIQUE';
1674
            } else {
1675
                throw new InvalidArgumentException(
1676 49474
                    'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().'
1677
                );
1678
            }
1679 49447
        } elseif ($constraint instanceof ForeignKeyConstraint) {
1680 49447
            $query .= ' FOREIGN KEY';
1681
1682 49447
            $referencesClause = ' REFERENCES ' . $constraint->getQuotedForeignTableName($this) .
1683 49447
                ' (' . implode(', ', $constraint->getQuotedForeignColumns($this)) . ')';
1684
        }
1685 49474
        $query .= ' ' . $columnList . $referencesClause;
1686
1687 49474
        return $query;
1688
    }
1689
1690
    /**
1691
     * Returns the SQL to create an index on a table on this platform.
1692
     *
1693
     * @param Table|string $table The name of the table on which the index is to be created.
1694
     *
1695
     * @return string
1696
     *
1697
     * @throws InvalidArgumentException
1698
     */
1699 54257
    public function getCreateIndexSQL(Index $index, $table)
1700
    {
1701 54257
        if ($table instanceof Table) {
1702 53685
            $table = $table->getQuotedName($this);
1703
        }
1704 54257
        $name    = $index->getQuotedName($this);
1705 54257
        $columns = $index->getColumns();
1706
1707 54257
        if (count($columns) === 0) {
1708
            throw new InvalidArgumentException('Incomplete definition. "columns" required.');
1709
        }
1710
1711 54257
        if ($index->isPrimary()) {
1712 47404
            return $this->getCreatePrimaryKeySQL($index, $table);
1713
        }
1714
1715 54239
        $query  = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table;
1716 54239
        $query .= ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')' . $this->getPartialIndexSQL($index);
1717
1718 54239
        return $query;
1719
    }
1720
1721
    /**
1722
     * Adds condition for partial index.
1723
     *
1724
     * @return string
1725
     */
1726 56029
    protected function getPartialIndexSQL(Index $index)
1727
    {
1728 56029
        if ($this->supportsPartialIndexes() && $index->hasOption('where')) {
1729 34461
            return ' WHERE ' . $index->getOption('where');
1730
        }
1731
1732 56026
        return '';
1733
    }
1734
1735
    /**
1736
     * Adds additional flags for index generation.
1737
     *
1738
     * @return string
1739
     */
1740 52199
    protected function getCreateIndexSQLFlags(Index $index)
1741
    {
1742 52199
        return $index->isUnique() ? 'UNIQUE ' : '';
1743
    }
1744
1745
    /**
1746
     * Returns the SQL to create an unnamed primary key constraint.
1747
     *
1748
     * @param Table|string $table
1749
     *
1750
     * @return string
1751
     */
1752 50966
    public function getCreatePrimaryKeySQL(Index $index, $table)
1753
    {
1754 50966
        if ($table instanceof Table) {
1755
            $table = $table->getQuotedName($this);
1756
        }
1757
1758 50966
        return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index) . ')';
1759
    }
1760
1761
    /**
1762
     * Returns the SQL to create a named schema.
1763
     *
1764
     * @param string $schemaName
1765
     *
1766
     * @return string
1767
     *
1768
     * @throws DBALException If not supported on this platform.
1769
     */
1770 48796
    public function getCreateSchemaSQL($schemaName)
1771
    {
1772 48796
        throw NotSupported::new(__METHOD__);
1773
    }
1774
1775
    /**
1776
     * Quotes a string so that it can be safely used as a table or column name,
1777
     * even if it is a reserved word of the platform. This also detects identifier
1778
     * chains separated by dot and quotes them independently.
1779
     *
1780
     * NOTE: Just because you CAN use quoted identifiers doesn't mean
1781
     * you SHOULD use them. In general, they end up causing way more
1782
     * problems than they solve.
1783
     *
1784
     * @param string $str The identifier name to be quoted.
1785
     *
1786
     * @return string The quoted identifier string.
1787
     */
1788 58152
    public function quoteIdentifier($str)
1789
    {
1790 58152
        if (strpos($str, '.') !== false) {
1791 50913
            $parts = array_map([$this, 'quoteSingleIdentifier'], explode('.', $str));
1792
1793 50913
            return implode('.', $parts);
1794
        }
1795
1796 58152
        return $this->quoteSingleIdentifier($str);
1797
    }
1798
1799
    /**
1800
     * Quotes a single identifier (no dot chain separation).
1801
     *
1802
     * @param string $str The identifier name to be quoted.
1803
     *
1804
     * @return string The quoted identifier string.
1805
     */
1806 58147
    public function quoteSingleIdentifier($str)
1807
    {
1808 58147
        $c = $this->getIdentifierQuoteCharacter();
1809
1810 58147
        return $c . str_replace($c, $c . $c, $str) . $c;
1811
    }
1812
1813
    /**
1814
     * Returns the SQL to create a new foreign key.
1815
     *
1816
     * @param ForeignKeyConstraint $foreignKey The foreign key constraint.
1817
     * @param Table|string         $table      The name of the table on which the foreign key is to be created.
1818
     *
1819
     * @return string
1820
     */
1821 55816
    public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table)
1822
    {
1823 55816
        if ($table instanceof Table) {
1824 3214
            $table = $table->getQuotedName($this);
1825
        }
1826
1827 55816
        return 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey);
1828
    }
1829
1830
    /**
1831
     * Gets the SQL statements for altering an existing table.
1832
     *
1833
     * This method returns an array of SQL statements, since some platforms need several statements.
1834
     *
1835
     * @return string[]
1836
     *
1837
     * @throws DBALException If not supported on this platform.
1838
     */
1839
    public function getAlterTableSQL(TableDiff $diff)
1840
    {
1841
        throw NotSupported::new(__METHOD__);
1842
    }
1843
1844
    /**
1845
     * @param mixed[] $columnSql
1846
     *
1847
     * @return bool
1848
     */
1849 54824
    protected function onSchemaAlterTableAddColumn(Column $column, TableDiff $diff, &$columnSql)
1850
    {
1851 54824
        if ($this->_eventManager === null) {
1852 49412
            return false;
1853
        }
1854
1855 54768
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableAddColumn)) {
1856 54756
            return false;
1857
        }
1858
1859 49260
        $eventArgs = new SchemaAlterTableAddColumnEventArgs($column, $diff, $this);
1860 49260
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn, $eventArgs);
1861
1862 49260
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1863
1864 49260
        return $eventArgs->isDefaultPrevented();
1865
    }
1866
1867
    /**
1868
     * @param string[] $columnSql
1869
     *
1870
     * @return bool
1871
     */
1872 53632
    protected function onSchemaAlterTableRemoveColumn(Column $column, TableDiff $diff, &$columnSql)
1873
    {
1874 53632
        if ($this->_eventManager === null) {
1875 49392
            return false;
1876
        }
1877
1878 53596
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRemoveColumn)) {
1879 53584
            return false;
1880
        }
1881
1882 49260
        $eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column, $diff, $this);
1883 49260
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn, $eventArgs);
1884
1885 49260
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1886
1887 49260
        return $eventArgs->isDefaultPrevented();
1888
    }
1889
1890
    /**
1891
     * @param string[] $columnSql
1892
     *
1893
     * @return bool
1894
     */
1895 54499
    protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiff, TableDiff $diff, &$columnSql)
1896
    {
1897 54499
        if ($this->_eventManager === null) {
1898 50818
            return false;
1899
        }
1900
1901 54387
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) {
1902 54375
            return false;
1903
        }
1904
1905 49260
        $eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff, $diff, $this);
1906 49260
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn, $eventArgs);
1907
1908 49260
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1909
1910 49260
        return $eventArgs->isDefaultPrevented();
1911
    }
1912
1913
    /**
1914
     * @param string   $oldColumnName
1915
     * @param string[] $columnSql
1916
     *
1917
     * @return bool
1918
     */
1919 53371
    protected function onSchemaAlterTableRenameColumn($oldColumnName, Column $column, TableDiff $diff, &$columnSql)
1920
    {
1921 53371
        if ($this->_eventManager === null) {
1922 48610
            return false;
1923
        }
1924
1925 53334
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRenameColumn)) {
1926 51498
            return false;
1927
        }
1928
1929 49260
        $eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName, $column, $diff, $this);
1930 49260
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn, $eventArgs);
1931
1932 49260
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1933
1934 49260
        return $eventArgs->isDefaultPrevented();
1935
    }
1936
1937
    /**
1938
     * @param string[] $sql
1939
     *
1940
     * @return bool
1941
     */
1942 55220
    protected function onSchemaAlterTable(TableDiff $diff, &$sql)
1943
    {
1944 55220
        if ($this->_eventManager === null) {
1945 50942
            return false;
1946
        }
1947
1948 54984
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTable)) {
1949 54972
            return false;
1950
        }
1951
1952 49260
        $eventArgs = new SchemaAlterTableEventArgs($diff, $this);
1953 49260
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTable, $eventArgs);
1954
1955 49260
        $sql = array_merge($sql, $eventArgs->getSql());
1956
1957 49260
        return $eventArgs->isDefaultPrevented();
1958
    }
1959
1960
    /**
1961
     * @return string[]
1962
     */
1963 55086
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
1964
    {
1965 55086
        $tableName = $diff->getName($this)->getQuotedName($this);
1966
1967 55086
        $sql = [];
1968 55086
        if ($this->supportsForeignKeyConstraints()) {
1969 55086
            foreach ($diff->removedForeignKeys as $foreignKey) {
1970 53303
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
1971
            }
1972 55086
            foreach ($diff->changedForeignKeys as $foreignKey) {
1973 48290
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
1974
            }
1975
        }
1976
1977 55086
        foreach ($diff->removedIndexes as $index) {
1978 54023
            $sql[] = $this->getDropIndexSQL($index, $tableName);
1979
        }
1980 55086
        foreach ($diff->changedIndexes as $index) {
1981 53379
            $sql[] = $this->getDropIndexSQL($index, $tableName);
1982
        }
1983
1984 55086
        return $sql;
1985
    }
1986
1987
    /**
1988
     * @return string[]
1989
     */
1990 55086
    protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff)
1991
    {
1992 55086
        $sql     = [];
1993 55086
        $newName = $diff->getNewName();
1994
1995 55086
        if ($newName !== false) {
1996 49379
            $tableName = $newName->getQuotedName($this);
1997
        } else {
1998 55063
            $tableName = $diff->getName($this)->getQuotedName($this);
1999
        }
2000
2001 55086
        if ($this->supportsForeignKeyConstraints()) {
2002 55086
            foreach ($diff->addedForeignKeys as $foreignKey) {
2003 53410
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
2004
            }
2005
2006 55086
            foreach ($diff->changedForeignKeys as $foreignKey) {
2007 48290
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
2008
            }
2009
        }
2010
2011 55086
        foreach ($diff->addedIndexes as $index) {
2012 53373
            $sql[] = $this->getCreateIndexSQL($index, $tableName);
2013
        }
2014
2015 55086
        foreach ($diff->changedIndexes as $index) {
2016 53379
            $sql[] = $this->getCreateIndexSQL($index, $tableName);
2017
        }
2018
2019 55086
        foreach ($diff->renamedIndexes as $oldIndexName => $index) {
2020 53464
            $oldIndexName = new Identifier($oldIndexName);
2021 53464
            $sql          = array_merge(
2022 53464
                $sql,
2023 53464
                $this->getRenameIndexSQL($oldIndexName->getQuotedName($this), $index, $tableName)
2024
            );
2025
        }
2026
2027 55086
        return $sql;
2028
    }
2029
2030
    /**
2031
     * Returns the SQL for renaming an index on a table.
2032
     *
2033
     * @param string $oldIndexName The name of the index to rename from.
2034
     * @param Index  $index        The definition of the index to rename to.
2035
     * @param string $tableName    The table to rename the given index on.
2036
     *
2037
     * @return string[] The sequence of SQL statements for renaming the given index.
2038
     */
2039 49142
    protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
2040
    {
2041
        return [
2042 49142
            $this->getDropIndexSQL($oldIndexName, $tableName),
2043 49142
            $this->getCreateIndexSQL($index, $tableName),
2044
        ];
2045
    }
2046
2047
    /**
2048
     * Common code for alter table statement generation that updates the changed Index and Foreign Key definitions.
2049
     *
2050
     * @return string[]
2051
     */
2052
    protected function _getAlterTableIndexForeignKeySQL(TableDiff $diff)
2053
    {
2054
        return array_merge($this->getPreAlterTableIndexForeignKeySQL($diff), $this->getPostAlterTableIndexForeignKeySQL($diff));
2055
    }
2056
2057
    /**
2058
     * Gets declaration of a number of fields in bulk.
2059
     *
2060
     * @param mixed[][] $fields A multidimensional associative array.
2061
     *                          The first dimension determines the field name, while the second
2062
     *                          dimension is keyed with the name of the properties
2063
     *                          of the field being declared as array indexes. Currently, the types
2064
     *                          of supported field properties are as follows:
2065
     *
2066
     *      length
2067
     *          Integer value that determines the maximum length of the text
2068
     *          field. If this argument is missing the field should be
2069
     *          declared to have the longest length allowed by the DBMS.
2070
     *
2071
     *      default
2072
     *          Text value to be used as default for this field.
2073
     *
2074
     *      notnull
2075
     *          Boolean flag that indicates whether this field is constrained
2076
     *          to not be set to null.
2077
     *      charset
2078
     *          Text value with the default CHARACTER SET for this field.
2079
     *      collation
2080
     *          Text value with the default COLLATION for this field.
2081
     *      unique
2082
     *          unique constraint
2083
     *
2084
     * @return string
2085
     */
2086 58539
    public function getColumnDeclarationListSQL(array $fields)
2087
    {
2088 58539
        $queryFields = [];
2089
2090 58539
        foreach ($fields as $fieldName => $field) {
2091 58539
            $queryFields[] = $this->getColumnDeclarationSQL($fieldName, $field);
2092
        }
2093
2094 58539
        return implode(', ', $queryFields);
2095
    }
2096
2097
    /**
2098
     * Obtains DBMS specific SQL code portion needed to declare a generic type
2099
     * field to be used in statements like CREATE TABLE.
2100
     *
2101
     * @param string  $name  The name the field to be declared.
2102
     * @param mixed[] $field An associative array with the name of the properties
2103
     *                       of the field being declared as array indexes. Currently, the types
2104
     *                       of supported field properties are as follows:
2105
     *
2106
     *      length
2107
     *          Integer value that determines the maximum length of the text
2108
     *          field. If this argument is missing the field should be
2109
     *          declared to have the longest length allowed by the DBMS.
2110
     *
2111
     *      default
2112
     *          Text value to be used as default for this field.
2113
     *
2114
     *      notnull
2115
     *          Boolean flag that indicates whether this field is constrained
2116
     *          to not be set to null.
2117
     *      charset
2118
     *          Text value with the default CHARACTER SET for this field.
2119
     *      collation
2120
     *          Text value with the default COLLATION for this field.
2121
     *      unique
2122
     *          unique constraint
2123
     *      check
2124
     *          column check constraint
2125
     *      columnDefinition
2126
     *          a string that defines the complete column
2127
     *
2128
     * @return string DBMS specific SQL code portion that should be used to declare the column.
2129
     */
2130 58428
    public function getColumnDeclarationSQL($name, array $field)
2131
    {
2132 58428
        if (isset($field['columnDefinition'])) {
2133 52464
            $columnDef = $this->getCustomTypeDeclarationSQL($field);
2134
        } else {
2135 58419
            $default = $this->getDefaultValueDeclarationSQL($field);
2136
2137 58419
            $charset = isset($field['charset']) && $field['charset'] ?
2138 58419
                ' ' . $this->getColumnCharsetDeclarationSQL($field['charset']) : '';
2139
2140 58419
            $collation = isset($field['collation']) && $field['collation'] ?
2141 58419
                ' ' . $this->getColumnCollationDeclarationSQL($field['collation']) : '';
2142
2143 58419
            $notnull = isset($field['notnull']) && $field['notnull'] ? ' NOT NULL' : '';
2144
2145 58419
            $unique = isset($field['unique']) && $field['unique'] ?
2146 58419
                ' ' . $this->getUniqueFieldDeclarationSQL() : '';
2147
2148 58419
            $check = isset($field['check']) && $field['check'] ?
2149 58419
                ' ' . $field['check'] : '';
2150
2151 58419
            $typeDecl  = $field['type']->getSQLDeclaration($field, $this);
2152 58419
            $columnDef = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation;
2153
2154 58419
            if ($this->supportsInlineColumnComments() && isset($field['comment']) && $field['comment'] !== '') {
2155 52595
                $columnDef .= ' ' . $this->getInlineColumnCommentSQL($field['comment']);
2156
            }
2157
        }
2158
2159 58428
        return $name . ' ' . $columnDef;
2160
    }
2161
2162
    /**
2163
     * Returns the SQL snippet that declares a floating point column of arbitrary precision.
2164
     *
2165
     * @param mixed[] $columnDef
2166
     *
2167
     * @return string
2168
     */
2169 54262
    public function getDecimalTypeDeclarationSQL(array $columnDef)
2170
    {
2171 54262
        $columnDef['precision'] = ! isset($columnDef['precision']) || empty($columnDef['precision'])
2172 54262
            ? 10 : $columnDef['precision'];
2173 54262
        $columnDef['scale']     = ! isset($columnDef['scale']) || empty($columnDef['scale'])
2174 54262
            ? 0 : $columnDef['scale'];
2175
2176 54262
        return 'NUMERIC(' . $columnDef['precision'] . ', ' . $columnDef['scale'] . ')';
2177
    }
2178
2179
    /**
2180
     * Obtains DBMS specific SQL code portion needed to set a default value
2181
     * declaration to be used in statements like CREATE TABLE.
2182
     *
2183
     * @param mixed[] $field The field definition array.
2184
     *
2185
     * @return string DBMS specific SQL code portion needed to set a default value.
2186
     */
2187 58605
    public function getDefaultValueDeclarationSQL($field)
2188
    {
2189 58605
        if (! isset($field['default'])) {
2190 58557
            return empty($field['notnull']) ? ' DEFAULT NULL' : '';
2191
        }
2192
2193 54682
        $default = $field['default'];
2194
2195 54682
        if (! isset($field['type'])) {
2196
            return " DEFAULT '" . $default . "'";
2197
        }
2198
2199 54682
        $type = $field['type'];
2200
2201 54682
        if ($type instanceof Types\PhpIntegerMappingType) {
2202 54636
            return ' DEFAULT ' . $default;
2203
        }
2204
2205 54637
        if ($type instanceof Types\PhpDateTimeMappingType && $default === $this->getCurrentTimestampSQL()) {
2206 52996
            return ' DEFAULT ' . $this->getCurrentTimestampSQL();
2207
        }
2208
2209 54627
        if ($type instanceof Types\TimeType && $default === $this->getCurrentTimeSQL()) {
2210 8006
            return ' DEFAULT ' . $this->getCurrentTimeSQL();
2211
        }
2212
2213 54627
        if ($type instanceof Types\DateType && $default === $this->getCurrentDateSQL()) {
2214 49807
            return ' DEFAULT ' . $this->getCurrentDateSQL();
2215
        }
2216
2217 54617
        if ($type instanceof Types\BooleanType) {
2218 54600
            return " DEFAULT '" . $this->convertBooleans($default) . "'";
0 ignored issues
show
Bug introduced by
Are you sure $this->convertBooleans($default) of type array|integer|mixed 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

2218
            return " DEFAULT '" . /** @scrutinizer ignore-type */ $this->convertBooleans($default) . "'";
Loading history...
2219
        }
2220
2221 54148
        return " DEFAULT '" . $default . "'";
2222
    }
2223
2224
    /**
2225
     * Obtains DBMS specific SQL code portion needed to set a CHECK constraint
2226
     * declaration to be used in statements like CREATE TABLE.
2227
     *
2228
     * @param string[]|mixed[][] $definition The check definition.
2229
     *
2230
     * @return string DBMS specific SQL code portion needed to set a CHECK constraint.
2231
     */
2232 51323
    public function getCheckDeclarationSQL(array $definition)
2233
    {
2234 51323
        $constraints = [];
2235 51323
        foreach ($definition as $field => $def) {
2236 51323
            if (is_string($def)) {
2237
                $constraints[] = 'CHECK (' . $def . ')';
2238
            } else {
2239 51323
                if (isset($def['min'])) {
2240 51059
                    $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')';
2241
                }
2242
2243 51323
                if (isset($def['max'])) {
2244 51269
                    $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')';
2245
                }
2246
            }
2247
        }
2248
2249 51323
        return implode(', ', $constraints);
2250
    }
2251
2252
    /**
2253
     * Obtains DBMS specific SQL code portion needed to set a unique
2254
     * constraint declaration to be used in statements like CREATE TABLE.
2255
     *
2256
     * @param string           $name       The name of the unique constraint.
2257
     * @param UniqueConstraint $constraint The unique constraint definition.
2258
     *
2259
     * @return string DBMS specific SQL code portion needed to set a constraint.
2260
     *
2261
     * @throws InvalidArgumentException
2262
     */
2263 49517
    public function getUniqueConstraintDeclarationSQL($name, UniqueConstraint $constraint)
2264
    {
2265 49517
        $columns = $constraint->getColumns();
2266 49517
        $name    = new Identifier($name);
2267
2268 49517
        if (count($columns) === 0) {
2269 18793
            throw new InvalidArgumentException('Incomplete definition. "columns" required.');
2270
        }
2271
2272 49516
        $flags = ['UNIQUE'];
2273
2274 49516
        if ($constraint->hasFlag('clustered')) {
2275 18820
            $flags[] = 'CLUSTERED';
2276
        }
2277
2278 49516
        $constraintName  = $name->getQuotedName($this);
2279 49516
        $constraintName  = ! empty($constraintName) ? $constraintName . ' ' : '';
2280 49516
        $columnListNames = $this->getIndexFieldDeclarationListSQL($columns);
2281
2282 49516
        return sprintf('CONSTRAINT %s%s (%s)', $constraintName, implode(' ', $flags), $columnListNames);
2283
    }
2284
2285
    /**
2286
     * Obtains DBMS specific SQL code portion needed to set an index
2287
     * declaration to be used in statements like CREATE TABLE.
2288
     *
2289
     * @param string $name  The name of the index.
2290
     * @param Index  $index The index definition.
2291
     *
2292
     * @return string DBMS specific SQL code portion needed to set an index.
2293
     *
2294
     * @throws InvalidArgumentException
2295
     */
2296 54107
    public function getIndexDeclarationSQL($name, Index $index)
2297
    {
2298 54107
        $columns = $index->getColumns();
2299 54107
        $name    = new Identifier($name);
2300
2301 54107
        if (count($columns) === 0) {
2302
            throw new InvalidArgumentException('Incomplete definition. "columns" required.');
2303
        }
2304
2305 54107
        return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name->getQuotedName($this) . ' ('
2306 54107
            . $this->getIndexFieldDeclarationListSQL($index)
2307 54107
            . ')' . $this->getPartialIndexSQL($index);
2308
    }
2309
2310
    /**
2311
     * Obtains SQL code portion needed to create a custom column,
2312
     * e.g. when a field has the "columnDefinition" keyword.
2313
     * Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate.
2314
     *
2315
     * @param mixed[] $columnDef
2316
     *
2317
     * @return string
2318
     */
2319 52467
    public function getCustomTypeDeclarationSQL(array $columnDef)
2320
    {
2321 52467
        return $columnDef['columnDefinition'];
2322
    }
2323
2324
    /**
2325
     * Obtains DBMS specific SQL code portion needed to set an index
2326
     * declaration to be used in statements like CREATE TABLE.
2327
     *
2328
     * @param mixed[]|Index $columnsOrIndex array declaration is deprecated, prefer passing Index to this method
2329
     */
2330 56075
    public function getIndexFieldDeclarationListSQL($columnsOrIndex) : string
2331
    {
2332 56075
        if ($columnsOrIndex instanceof Index) {
2333 56053
            return implode(', ', $columnsOrIndex->getQuotedColumns($this));
2334
        }
2335
2336 49526
        if (! is_array($columnsOrIndex)) {
0 ignored issues
show
introduced by
The condition is_array($columnsOrIndex) is always true.
Loading history...
2337
            throw new InvalidArgumentException('Fields argument should be an Index or array.');
2338
        }
2339
2340 49526
        $ret = [];
2341
2342 49526
        foreach ($columnsOrIndex as $column => $definition) {
2343 49526
            if (is_array($definition)) {
2344
                $ret[] = $column;
2345
            } else {
2346 49526
                $ret[] = $definition;
2347
            }
2348
        }
2349
2350 49526
        return implode(', ', $ret);
2351
    }
2352
2353
    /**
2354
     * Returns the required SQL string that fits between CREATE ... TABLE
2355
     * to create the table as a temporary table.
2356
     *
2357
     * Should be overridden in driver classes to return the correct string for the
2358
     * specific database type.
2359
     *
2360
     * The default is to return the string "TEMPORARY" - this will result in a
2361
     * SQL error for any database that does not support temporary tables, or that
2362
     * requires a different SQL command from "CREATE TEMPORARY TABLE".
2363
     *
2364
     * @return string The string required to be placed between "CREATE" and "TABLE"
2365
     *                to generate a temporary table, if possible.
2366
     */
2367
    public function getTemporaryTableSQL()
2368
    {
2369
        return 'TEMPORARY';
2370
    }
2371
2372
    /**
2373
     * Some vendors require temporary table names to be qualified specially.
2374
     *
2375
     * @param string $tableName
2376
     *
2377
     * @return string
2378
     */
2379 50183
    public function getTemporaryTableName($tableName)
2380
    {
2381 50183
        return $tableName;
2382
    }
2383
2384
    /**
2385
     * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2386
     * of a field declaration to be used in statements like CREATE TABLE.
2387
     *
2388
     * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2389
     *                of a field declaration.
2390
     */
2391 55915
    public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey)
2392
    {
2393 55915
        $sql  = $this->getForeignKeyBaseDeclarationSQL($foreignKey);
2394 55912
        $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey);
2395
2396 55912
        return $sql;
2397
    }
2398
2399
    /**
2400
     * Returns the FOREIGN KEY query section dealing with non-standard options
2401
     * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
2402
     *
2403
     * @param ForeignKeyConstraint $foreignKey The foreign key definition.
2404
     *
2405
     * @return string
2406
     */
2407 55896
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
2408
    {
2409 55896
        $query = '';
2410 55896
        if ($this->supportsForeignKeyOnUpdate() && $foreignKey->hasOption('onUpdate')) {
2411 18766
            $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate'));
2412
        }
2413 55896
        if ($foreignKey->hasOption('onDelete')) {
2414 53021
            $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
2415
        }
2416
2417 55896
        return $query;
2418
    }
2419
2420
    /**
2421
     * Returns the given referential action in uppercase if valid, otherwise throws an exception.
2422
     *
2423
     * @param string $action The foreign key referential action.
2424
     *
2425
     * @return string
2426
     *
2427
     * @throws InvalidArgumentException If unknown referential action given.
2428
     */
2429 53742
    public function getForeignKeyReferentialActionSQL($action)
2430
    {
2431 53742
        $upper = strtoupper($action);
2432 53742
        switch ($upper) {
2433 78
            case 'CASCADE':
2434 53
            case 'SET NULL':
2435 41
            case 'NO ACTION':
2436 31
            case 'RESTRICT':
2437 22
            case 'SET DEFAULT':
2438 53731
                return $upper;
2439
            default:
2440 50204
                throw new InvalidArgumentException(sprintf('Invalid foreign key action "%s".', $upper));
2441
        }
2442
    }
2443
2444
    /**
2445
     * Obtains DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2446
     * of a field declaration to be used in statements like CREATE TABLE.
2447
     *
2448
     * @return string
2449
     *
2450
     * @throws InvalidArgumentException
2451
     */
2452 55905
    public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey)
2453
    {
2454 55905
        $sql = '';
2455 55905
        if (strlen($foreignKey->getName())) {
2456 55809
            $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' ';
2457
        }
2458 55905
        $sql .= 'FOREIGN KEY (';
2459
2460 55905
        if (count($foreignKey->getLocalColumns()) === 0) {
2461
            throw new InvalidArgumentException('Incomplete definition. "local" required.');
2462
        }
2463 55905
        if (count($foreignKey->getForeignColumns()) === 0) {
2464
            throw new InvalidArgumentException('Incomplete definition. "foreign" required.');
2465
        }
2466 55905
        if (strlen($foreignKey->getForeignTableName()) === 0) {
2467
            throw new InvalidArgumentException('Incomplete definition. "foreignTable" required.');
2468
        }
2469
2470 55905
        return $sql . implode(', ', $foreignKey->getQuotedLocalColumns($this))
2471 55905
            . ') REFERENCES '
2472 55905
            . $foreignKey->getQuotedForeignTableName($this) . ' ('
2473 55905
            . implode(', ', $foreignKey->getQuotedForeignColumns($this)) . ')';
2474
    }
2475
2476
    /**
2477
     * Obtains DBMS specific SQL code portion needed to set the UNIQUE constraint
2478
     * of a field declaration to be used in statements like CREATE TABLE.
2479
     *
2480
     * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint
2481
     *                of a field declaration.
2482
     */
2483
    public function getUniqueFieldDeclarationSQL()
2484
    {
2485
        return 'UNIQUE';
2486
    }
2487
2488
    /**
2489
     * Obtains DBMS specific SQL code portion needed to set the CHARACTER SET
2490
     * of a field declaration to be used in statements like CREATE TABLE.
2491
     *
2492
     * @param string $charset The name of the charset.
2493
     *
2494
     * @return string DBMS specific SQL code portion needed to set the CHARACTER SET
2495
     *                of a field declaration.
2496
     */
2497
    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

2497
    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...
2498
    {
2499
        return '';
2500
    }
2501
2502
    /**
2503
     * Obtains DBMS specific SQL code portion needed to set the COLLATION
2504
     * of a field declaration to be used in statements like CREATE TABLE.
2505
     *
2506
     * @param string $collation The name of the collation.
2507
     *
2508
     * @return string DBMS specific SQL code portion needed to set the COLLATION
2509
     *                of a field declaration.
2510
     */
2511 52877
    public function getColumnCollationDeclarationSQL($collation)
2512
    {
2513 52877
        return $this->supportsColumnCollation() ? 'COLLATE ' . $collation : '';
2514
    }
2515
2516
    /**
2517
     * Whether the platform prefers sequences for ID generation.
2518
     * Subclasses should override this method to return TRUE if they prefer sequences.
2519
     *
2520
     * @return bool
2521
     */
2522 18065
    public function prefersSequences()
2523
    {
2524 18065
        return false;
2525
    }
2526
2527
    /**
2528
     * Whether the platform prefers identity columns (eg. autoincrement) for ID generation.
2529
     * Subclasses should override this method to return TRUE if they prefer identity columns.
2530
     *
2531
     * @return bool
2532
     */
2533 39121
    public function prefersIdentityColumns()
2534
    {
2535 39121
        return false;
2536
    }
2537
2538
    /**
2539
     * Some platforms need the boolean values to be converted.
2540
     *
2541
     * The default conversion in this implementation converts to integers (false => 0, true => 1).
2542
     *
2543
     * Note: if the input is not a boolean the original input might be returned.
2544
     *
2545
     * There are two contexts when converting booleans: Literals and Prepared Statements.
2546
     * This method should handle the literal case
2547
     *
2548
     * @param mixed $item A boolean or an array of them.
2549
     *
2550
     * @return mixed A boolean database value or an array of them.
2551
     */
2552 53606
    public function convertBooleans($item)
2553
    {
2554 53606
        if (is_array($item)) {
2555
            foreach ($item as $k => $value) {
2556
                if (! is_bool($value)) {
2557
                    continue;
2558
                }
2559
2560
                $item[$k] = (int) $value;
2561
            }
2562 53606
        } elseif (is_bool($item)) {
2563 53605
            $item = (int) $item;
2564
        }
2565
2566 53606
        return $item;
2567
    }
2568
2569
    /**
2570
     * Some platforms have boolean literals that needs to be correctly converted
2571
     *
2572
     * The default conversion tries to convert value into bool "(bool)$item"
2573
     *
2574
     * @param mixed $item
2575
     *
2576
     * @return bool|null
2577
     */
2578 52052
    public function convertFromBoolean($item)
2579
    {
2580 52052
        return $item === null ? null: (bool) $item;
2581
    }
2582
2583
    /**
2584
     * This method should handle the prepared statements case. When there is no
2585
     * distinction, it's OK to use the same method.
2586
     *
2587
     * Note: if the input is not a boolean the original input might be returned.
2588
     *
2589
     * @param mixed $item A boolean or an array of them.
2590
     *
2591
     * @return mixed A boolean database value or an array of them.
2592
     */
2593 48269
    public function convertBooleansToDatabaseValue($item)
2594
    {
2595 48269
        return $this->convertBooleans($item);
2596
    }
2597
2598
    /**
2599
     * Returns the SQL specific for the platform to get the current date.
2600
     *
2601
     * @return string
2602
     */
2603 52158
    public function getCurrentDateSQL()
2604
    {
2605 52158
        return 'CURRENT_DATE';
2606
    }
2607
2608
    /**
2609
     * Returns the SQL specific for the platform to get the current time.
2610
     *
2611
     * @return string
2612
     */
2613 8006
    public function getCurrentTimeSQL()
2614
    {
2615 8006
        return 'CURRENT_TIME';
2616
    }
2617
2618
    /**
2619
     * Returns the SQL specific for the platform to get the current timestamp
2620
     *
2621
     * @return string
2622
     */
2623 52194
    public function getCurrentTimestampSQL()
2624
    {
2625 52194
        return 'CURRENT_TIMESTAMP';
2626
    }
2627
2628
    /**
2629
     * Returns the SQL for a given transaction isolation level Connection constant.
2630
     *
2631
     * @param int $level
2632
     *
2633
     * @return string
2634
     *
2635
     * @throws InvalidArgumentException
2636
     */
2637 47771
    protected function _getTransactionIsolationLevelSQL($level)
2638
    {
2639 8
        switch ($level) {
2640 47763
            case TransactionIsolationLevel::READ_UNCOMMITTED:
2641 47771
                return 'READ UNCOMMITTED';
2642 47763
            case TransactionIsolationLevel::READ_COMMITTED:
2643 47771
                return 'READ COMMITTED';
2644 47763
            case TransactionIsolationLevel::REPEATABLE_READ:
2645 47771
                return 'REPEATABLE READ';
2646 47763
            case TransactionIsolationLevel::SERIALIZABLE:
2647 47771
                return 'SERIALIZABLE';
2648
            default:
2649
                throw new InvalidArgumentException(sprintf('Invalid isolation level "%s".', $level));
2650
        }
2651
    }
2652
2653
    /**
2654
     * @return string
2655
     *
2656
     * @throws DBALException If not supported on this platform.
2657
     */
2658 2002
    public function getListDatabasesSQL()
2659
    {
2660 2002
        throw NotSupported::new(__METHOD__);
2661
    }
2662
2663
    /**
2664
     * Returns the SQL statement for retrieving the namespaces defined in the database.
2665
     *
2666
     * @return string
2667
     *
2668
     * @throws DBALException If not supported on this platform.
2669
     */
2670
    public function getListNamespacesSQL()
2671
    {
2672
        throw NotSupported::new(__METHOD__);
2673
    }
2674
2675
    /**
2676
     * @param string $database
2677
     *
2678
     * @return string
2679
     *
2680
     * @throws DBALException If not supported on this platform.
2681
     */
2682
    public function getListSequencesSQL($database)
2683
    {
2684
        throw NotSupported::new(__METHOD__);
2685
    }
2686
2687
    /**
2688
     * @param string $table
2689
     *
2690
     * @return string
2691
     *
2692
     * @throws DBALException If not supported on this platform.
2693
     */
2694
    public function getListTableConstraintsSQL($table)
2695
    {
2696
        throw NotSupported::new(__METHOD__);
2697
    }
2698
2699
    /**
2700
     * @param string      $table
2701
     * @param string|null $database
2702
     *
2703
     * @return string
2704
     *
2705
     * @throws DBALException If not supported on this platform.
2706
     */
2707
    public function getListTableColumnsSQL($table, $database = null)
2708
    {
2709
        throw NotSupported::new(__METHOD__);
2710
    }
2711
2712
    /**
2713
     * @return string
2714
     *
2715
     * @throws DBALException If not supported on this platform.
2716
     */
2717
    public function getListTablesSQL()
2718
    {
2719
        throw NotSupported::new(__METHOD__);
2720
    }
2721
2722
    /**
2723
     * @return string
2724
     *
2725
     * @throws DBALException If not supported on this platform.
2726
     */
2727
    public function getListUsersSQL()
2728
    {
2729
        throw NotSupported::new(__METHOD__);
2730
    }
2731
2732
    /**
2733
     * Returns the SQL to list all views of a database or user.
2734
     *
2735
     * @param string $database
2736
     *
2737
     * @return string
2738
     *
2739
     * @throws DBALException If not supported on this platform.
2740
     */
2741
    public function getListViewsSQL($database)
2742
    {
2743
        throw NotSupported::new(__METHOD__);
2744
    }
2745
2746
    /**
2747
     * Returns the list of indexes for the current database.
2748
     *
2749
     * The current database parameter is optional but will always be passed
2750
     * when using the SchemaManager API and is the database the given table is in.
2751
     *
2752
     * Attention: Some platforms only support currentDatabase when they
2753
     * are connected with that database. Cross-database information schema
2754
     * requests may be impossible.
2755
     *
2756
     * @param string $table
2757
     * @param string $currentDatabase
2758
     *
2759
     * @return string
2760
     *
2761
     * @throws DBALException If not supported on this platform.
2762
     */
2763
    public function getListTableIndexesSQL($table, $currentDatabase = null)
0 ignored issues
show
Unused Code introduced by
The parameter $currentDatabase 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

2763
    public function getListTableIndexesSQL($table, /** @scrutinizer ignore-unused */ $currentDatabase = null)

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...
2764
    {
2765
        throw NotSupported::new(__METHOD__);
2766
    }
2767
2768
    /**
2769
     * @param string $table
2770
     *
2771
     * @return string
2772
     *
2773
     * @throws DBALException If not supported on this platform.
2774
     */
2775
    public function getListTableForeignKeysSQL($table)
2776
    {
2777
        throw NotSupported::new(__METHOD__);
2778
    }
2779
2780
    /**
2781
     * @param string $name
2782
     * @param string $sql
2783
     *
2784
     * @return string
2785
     *
2786
     * @throws DBALException If not supported on this platform.
2787
     */
2788
    public function getCreateViewSQL($name, $sql)
2789
    {
2790
        throw NotSupported::new(__METHOD__);
2791
    }
2792
2793
    /**
2794
     * @param string $name
2795
     *
2796
     * @return string
2797
     *
2798
     * @throws DBALException If not supported on this platform.
2799
     */
2800
    public function getDropViewSQL($name)
2801
    {
2802
        throw NotSupported::new(__METHOD__);
2803
    }
2804
2805
    /**
2806
     * Returns the SQL snippet to drop an existing sequence.
2807
     *
2808
     * @param Sequence|string $sequence
2809
     *
2810
     * @return string
2811
     *
2812
     * @throws DBALException If not supported on this platform.
2813
     */
2814
    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

2814
    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...
2815
    {
2816
        throw NotSupported::new(__METHOD__);
2817
    }
2818
2819
    /**
2820
     * @param string $sequenceName
2821
     *
2822
     * @return string
2823
     *
2824
     * @throws DBALException If not supported on this platform.
2825
     */
2826
    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

2826
    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...
2827
    {
2828
        throw NotSupported::new(__METHOD__);
2829
    }
2830
2831
    /**
2832
     * Returns the SQL to create a new database.
2833
     *
2834
     * @param string $database The name of the database that should be created.
2835
     *
2836
     * @return string
2837
     *
2838
     * @throws DBALException If not supported on this platform.
2839
     */
2840 36478
    public function getCreateDatabaseSQL($database)
2841
    {
2842 36478
        throw NotSupported::new(__METHOD__);
2843
    }
2844
2845
    /**
2846
     * Returns the SQL to set the transaction isolation level.
2847
     *
2848
     * @param int $level
2849
     *
2850
     * @return string
2851
     *
2852
     * @throws DBALException If not supported on this platform.
2853
     */
2854
    public function getSetTransactionIsolationSQL($level)
2855
    {
2856
        throw NotSupported::new(__METHOD__);
2857
    }
2858
2859
    /**
2860
     * Obtains DBMS specific SQL to be used to create datetime fields in
2861
     * statements like CREATE TABLE.
2862
     *
2863
     * @param mixed[] $fieldDeclaration
2864
     *
2865
     * @return string
2866
     *
2867
     * @throws DBALException If not supported on this platform.
2868
     */
2869
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
2870
    {
2871
        throw NotSupported::new(__METHOD__);
2872
    }
2873
2874
    /**
2875
     * Obtains DBMS specific SQL to be used to create datetime with timezone offset fields.
2876
     *
2877
     * @param mixed[] $fieldDeclaration
2878
     *
2879
     * @return string
2880
     */
2881 42388
    public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration)
2882
    {
2883 42388
        return $this->getDateTimeTypeDeclarationSQL($fieldDeclaration);
2884
    }
2885
2886
    /**
2887
     * Obtains DBMS specific SQL to be used to create date fields in statements
2888
     * like CREATE TABLE.
2889
     *
2890
     * @param mixed[] $fieldDeclaration
2891
     *
2892
     * @return string
2893
     *
2894
     * @throws DBALException If not supported on this platform.
2895
     */
2896
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
2897
    {
2898
        throw NotSupported::new(__METHOD__);
2899
    }
2900
2901
    /**
2902
     * Obtains DBMS specific SQL to be used to create time fields in statements
2903
     * like CREATE TABLE.
2904
     *
2905
     * @param mixed[] $fieldDeclaration
2906
     *
2907
     * @return string
2908
     *
2909
     * @throws DBALException If not supported on this platform.
2910
     */
2911
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
2912
    {
2913
        throw NotSupported::new(__METHOD__);
2914
    }
2915
2916
    /**
2917
     * @param mixed[] $fieldDeclaration
2918
     *
2919
     * @return string
2920
     */
2921 49443
    public function getFloatDeclarationSQL(array $fieldDeclaration)
2922
    {
2923 49443
        return 'DOUBLE PRECISION';
2924
    }
2925
2926
    /**
2927
     * Gets the default transaction isolation level of the platform.
2928
     *
2929
     * @see TransactionIsolationLevel
2930
     *
2931
     * @return int The default isolation level.
2932
     */
2933
    public function getDefaultTransactionIsolationLevel()
2934
    {
2935
        return TransactionIsolationLevel::READ_COMMITTED;
2936
    }
2937
2938
    /* supports*() methods */
2939
2940
    /**
2941
     * Whether the platform supports sequences.
2942
     *
2943
     * @return bool
2944
     */
2945 45245
    public function supportsSequences()
2946
    {
2947 45245
        return false;
2948
    }
2949
2950
    /**
2951
     * Whether the platform supports identity columns.
2952
     *
2953
     * Identity columns are columns that receive an auto-generated value from the
2954
     * database on insert of a row.
2955
     *
2956
     * @return bool
2957
     */
2958 36318
    public function supportsIdentityColumns()
2959
    {
2960 36318
        return false;
2961
    }
2962
2963
    /**
2964
     * Whether the platform emulates identity columns through sequences.
2965
     *
2966
     * Some platforms that do not support identity columns natively
2967
     * but support sequences can emulate identity columns by using
2968
     * sequences.
2969
     *
2970
     * @return bool
2971
     */
2972 50767
    public function usesSequenceEmulatedIdentityColumns()
2973
    {
2974 50767
        return false;
2975
    }
2976
2977
    /**
2978
     * Gets the sequence name prefix based on table information.
2979
     *
2980
     * @param string      $tableName
2981
     * @param string|null $schemaName
2982
     *
2983
     * @return string
2984
     */
2985
    public function getSequencePrefix($tableName, $schemaName = null)
2986
    {
2987
        if (! $schemaName) {
2988
            return $tableName;
2989
        }
2990
2991
        // Prepend the schema name to the table name if there is one
2992
        return ! $this->supportsSchemas() && $this->canEmulateSchemas()
2993
            ? $schemaName . '__' . $tableName
2994
            : $schemaName . '.' . $tableName;
2995
    }
2996
2997
    /**
2998
     * Returns the name of the sequence for a particular identity column in a particular table.
2999
     *
3000
     * @see    usesSequenceEmulatedIdentityColumns
3001
     *
3002
     * @param string $tableName  The name of the table to return the sequence name for.
3003
     * @param string $columnName The name of the identity column in the table to return the sequence name for.
3004
     *
3005
     * @return string
3006
     *
3007
     * @throws DBALException If not supported on this platform.
3008
     */
3009 48716
    public function getIdentitySequenceName($tableName, $columnName)
3010
    {
3011 48716
        throw NotSupported::new(__METHOD__);
3012
    }
3013
3014
    /**
3015
     * Whether the platform supports indexes.
3016
     *
3017
     * @return bool
3018
     */
3019 17929
    public function supportsIndexes()
3020
    {
3021 17929
        return true;
3022
    }
3023
3024
    /**
3025
     * Whether the platform supports partial indexes.
3026
     *
3027
     * @return bool
3028
     */
3029 55128
    public function supportsPartialIndexes()
3030
    {
3031 55128
        return false;
3032
    }
3033
3034
    /**
3035
     * Whether the platform supports indexes with column length definitions.
3036
     */
3037 52879
    public function supportsColumnLengthIndexes() : bool
3038
    {
3039 52879
        return false;
3040
    }
3041
3042
    /**
3043
     * Whether the platform supports altering tables.
3044
     *
3045
     * @return bool
3046
     */
3047 53585
    public function supportsAlterTable()
3048
    {
3049 53585
        return true;
3050
    }
3051
3052
    /**
3053
     * Whether the platform supports transactions.
3054
     *
3055
     * @return bool
3056
     */
3057 17956
    public function supportsTransactions()
3058
    {
3059 17956
        return true;
3060
    }
3061
3062
    /**
3063
     * Whether the platform supports savepoints.
3064
     *
3065
     * @return bool
3066
     */
3067 58063
    public function supportsSavepoints()
3068
    {
3069 58063
        return true;
3070
    }
3071
3072
    /**
3073
     * Whether the platform supports releasing savepoints.
3074
     *
3075
     * @return bool
3076
     */
3077 58051
    public function supportsReleaseSavepoints()
3078
    {
3079 58051
        return $this->supportsSavepoints();
3080
    }
3081
3082
    /**
3083
     * Whether the platform supports primary key constraints.
3084
     *
3085
     * @return bool
3086
     */
3087 18037
    public function supportsPrimaryConstraints()
3088
    {
3089 18037
        return true;
3090
    }
3091
3092
    /**
3093
     * Whether the platform supports foreign key constraints.
3094
     *
3095
     * @return bool
3096
     */
3097 56075
    public function supportsForeignKeyConstraints()
3098
    {
3099 56075
        return true;
3100
    }
3101
3102
    /**
3103
     * Whether this platform supports onUpdate in foreign key constraints.
3104
     *
3105
     * @return bool
3106
     */
3107 55897
    public function supportsForeignKeyOnUpdate()
3108
    {
3109 55897
        return $this->supportsForeignKeyConstraints();
3110
    }
3111
3112
    /**
3113
     * Whether the platform supports database schemas.
3114
     *
3115
     * @return bool
3116
     */
3117 48159
    public function supportsSchemas()
3118
    {
3119 48159
        return false;
3120
    }
3121
3122
    /**
3123
     * Whether this platform can emulate schemas.
3124
     *
3125
     * Platforms that either support or emulate schemas don't automatically
3126
     * filter a schema for the namespaced elements in {@link
3127
     * AbstractManager#createSchema}.
3128
     *
3129
     * @return bool
3130
     */
3131 17740
    public function canEmulateSchemas()
3132
    {
3133 17740
        return false;
3134
    }
3135
3136
    /**
3137
     * Returns the default schema name.
3138
     *
3139
     * @return string
3140
     *
3141
     * @throws DBALException If not supported on this platform.
3142
     */
3143
    public function getDefaultSchemaName()
3144
    {
3145
        throw NotSupported::new(__METHOD__);
3146
    }
3147
3148
    /**
3149
     * Whether this platform supports create database.
3150
     *
3151
     * Some databases don't allow to create and drop databases at all or only with certain tools.
3152
     *
3153
     * @return bool
3154
     */
3155 57158
    public function supportsCreateDropDatabase()
3156
    {
3157 57158
        return true;
3158
    }
3159
3160
    /**
3161
     * Whether the platform supports getting the affected rows of a recent update/delete type query.
3162
     *
3163
     * @return bool
3164
     */
3165 17821
    public function supportsGettingAffectedRows()
3166
    {
3167 17821
        return true;
3168
    }
3169
3170
    /**
3171
     * Whether this platform support to add inline column comments as postfix.
3172
     *
3173
     * @return bool
3174
     */
3175 52546
    public function supportsInlineColumnComments()
3176
    {
3177 52546
        return false;
3178
    }
3179
3180
    /**
3181
     * Whether this platform support the proprietary syntax "COMMENT ON asset".
3182
     *
3183
     * @return bool
3184
     */
3185 56391
    public function supportsCommentOnStatement()
3186
    {
3187 56391
        return false;
3188
    }
3189
3190
    /**
3191
     * Does this platform have native guid type.
3192
     *
3193
     * @return bool
3194
     */
3195 57192
    public function hasNativeGuidType()
3196
    {
3197 57192
        return false;
3198
    }
3199
3200
    /**
3201
     * Does this platform have native JSON type.
3202
     *
3203
     * @return bool
3204
     */
3205 55168
    public function hasNativeJsonType()
3206
    {
3207 55168
        return false;
3208
    }
3209
3210
    /**
3211
     * @deprecated
3212
     *
3213
     * @todo Remove in 3.0
3214
     */
3215
    public function getIdentityColumnNullInsertSQL()
3216
    {
3217
        return '';
3218
    }
3219
3220
    /**
3221
     * Whether this platform supports views.
3222
     *
3223
     * @return bool
3224
     */
3225 53552
    public function supportsViews()
3226
    {
3227 53552
        return true;
3228
    }
3229
3230
    /**
3231
     * Does this platform support column collation?
3232
     *
3233
     * @return bool
3234
     */
3235
    public function supportsColumnCollation()
3236
    {
3237
        return false;
3238
    }
3239
3240
    /**
3241
     * Gets the format string, as accepted by the date() function, that describes
3242
     * the format of a stored datetime value of this platform.
3243
     *
3244
     * @return string The format string.
3245
     */
3246 57818
    public function getDateTimeFormatString()
3247
    {
3248 57818
        return 'Y-m-d H:i:s';
3249
    }
3250
3251
    /**
3252
     * Gets the format string, as accepted by the date() function, that describes
3253
     * the format of a stored datetime with timezone value of this platform.
3254
     *
3255
     * @return string The format string.
3256
     */
3257 42372
    public function getDateTimeTzFormatString()
3258
    {
3259 42372
        return 'Y-m-d H:i:s';
3260
    }
3261
3262
    /**
3263
     * Gets the format string, as accepted by the date() function, that describes
3264
     * the format of a stored date value of this platform.
3265
     *
3266
     * @return string The format string.
3267
     */
3268 51679
    public function getDateFormatString()
3269
    {
3270 51679
        return 'Y-m-d';
3271
    }
3272
3273
    /**
3274
     * Gets the format string, as accepted by the date() function, that describes
3275
     * the format of a stored time value of this platform.
3276
     *
3277
     * @return string The format string.
3278
     */
3279 51651
    public function getTimeFormatString()
3280
    {
3281 51651
        return 'H:i:s';
3282
    }
3283
3284
    /**
3285
     * Adds an driver-specific LIMIT clause to the query.
3286
     *
3287
     * @throws DBALException
3288
     */
3289 55486
    final public function modifyLimitQuery(string $query, ?int $limit, int $offset = 0) : string
3290
    {
3291 55486
        if ($offset < 0) {
3292
            throw new DBALException(sprintf(
3293
                'Offset must be a positive integer or zero, "%d" given.',
3294
                $offset
3295
            ));
3296
        }
3297
3298 55486
        if ($offset > 0 && ! $this->supportsLimitOffset()) {
3299
            throw new DBALException(sprintf(
3300
                'Platform "%s" does not support offset values in limit queries.',
3301
                $this->getName()
3302
            ));
3303
        }
3304
3305 55486
        return $this->doModifyLimitQuery($query, $limit, $offset);
3306
    }
3307
3308
    /**
3309
     * Adds an platform-specific LIMIT clause to the query.
3310
     */
3311 37508
    protected function doModifyLimitQuery(string $query, ?int $limit, int $offset) : string
3312
    {
3313 37508
        if ($limit !== null) {
3314 37504
            $query .= sprintf(' LIMIT %d', $limit);
3315
        }
3316
3317 37508
        if ($offset > 0) {
3318 17000
            $query .= sprintf(' OFFSET %d', $offset);
3319
        }
3320
3321 37508
        return $query;
3322
    }
3323
3324
    /**
3325
     * Whether the database platform support offsets in modify limit clauses.
3326
     *
3327
     * @return bool
3328
     */
3329 55404
    public function supportsLimitOffset()
3330
    {
3331 55404
        return true;
3332
    }
3333
3334
    /**
3335
     * Gets the character casing of a column in an SQL result set of this platform.
3336
     *
3337
     * @param string $column The column name for which to get the correct character casing.
3338
     *
3339
     * @return string The column name in the character casing used in SQL result sets.
3340
     */
3341
    public function getSQLResultCasing($column)
3342
    {
3343
        return $column;
3344
    }
3345
3346
    /**
3347
     * Makes any fixes to a name of a schema element (table, sequence, ...) that are required
3348
     * by restrictions of the platform, like a maximum length.
3349
     *
3350
     * @param string $schemaElementName
3351
     *
3352
     * @return string
3353
     */
3354
    public function fixSchemaElementName($schemaElementName)
3355
    {
3356
        return $schemaElementName;
3357
    }
3358
3359
    /**
3360
     * Maximum length of any given database identifier, like tables or column names.
3361
     *
3362
     * @return int
3363
     */
3364 54934
    public function getMaxIdentifierLength()
3365
    {
3366 54934
        return 63;
3367
    }
3368
3369
    /**
3370
     * Returns the insert SQL for an empty insert statement.
3371
     *
3372
     * @param string $tableName
3373
     * @param string $identifierColumnName
3374
     *
3375
     * @return string
3376
     */
3377 41735
    public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName)
3378
    {
3379 41735
        return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (null)';
3380
    }
3381
3382
    /**
3383
     * Generates a Truncate Table SQL statement for a given table.
3384
     *
3385
     * Cascade is not supported on many platforms but would optionally cascade the truncate by
3386
     * following the foreign keys.
3387
     *
3388
     * @param string $tableName
3389
     * @param bool   $cascade
3390
     *
3391
     * @return string
3392
     */
3393 53509
    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

3393
    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...
3394
    {
3395 53509
        $tableIdentifier = new Identifier($tableName);
3396
3397 53509
        return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this);
3398
    }
3399
3400
    /**
3401
     * This is for test reasons, many vendors have special requirements for dummy statements.
3402
     */
3403 57996
    public function getDummySelectSQL(string $expression = '1') : string
3404
    {
3405 57996
        return sprintf('SELECT %s', $expression);
3406
    }
3407
3408
    /**
3409
     * Returns the SQL to create a new savepoint.
3410
     *
3411
     * @param string $savepoint
3412
     *
3413
     * @return string
3414
     */
3415 58051
    public function createSavePoint($savepoint)
3416
    {
3417 58051
        return 'SAVEPOINT ' . $savepoint;
3418
    }
3419
3420
    /**
3421
     * Returns the SQL to release a savepoint.
3422
     *
3423
     * @param string $savepoint
3424
     *
3425
     * @return string
3426
     */
3427 58050
    public function releaseSavePoint($savepoint)
3428
    {
3429 58050
        return 'RELEASE SAVEPOINT ' . $savepoint;
3430
    }
3431
3432
    /**
3433
     * Returns the SQL to rollback a savepoint.
3434
     *
3435
     * @param string $savepoint
3436
     *
3437
     * @return string
3438
     */
3439 58051
    public function rollbackSavePoint($savepoint)
3440
    {
3441 58051
        return 'ROLLBACK TO SAVEPOINT ' . $savepoint;
3442
    }
3443
3444
    /**
3445
     * Returns the keyword list instance of this platform.
3446
     *
3447
     * @return KeywordList
3448
     *
3449
     * @throws DBALException If no keyword list is specified.
3450
     */
3451 59018
    final public function getReservedKeywordsList()
3452
    {
3453
        // Check for an existing instantiation of the keywords class.
3454 59018
        if ($this->_keywords) {
3455 58958
            return $this->_keywords;
3456
        }
3457
3458 58870
        $class    = $this->getReservedKeywordsClass();
3459 58870
        $keywords = new $class();
3460 58870
        if (! $keywords instanceof KeywordList) {
3461
            throw NotSupported::new(__METHOD__);
3462
        }
3463
3464
        // Store the instance so it doesn't need to be generated on every request.
3465 58870
        $this->_keywords = $keywords;
3466
3467 58870
        return $keywords;
3468
    }
3469
3470
    /**
3471
     * Returns the class name of the reserved keywords list.
3472
     *
3473
     * @return string
3474
     *
3475
     * @throws DBALException If not supported on this platform.
3476
     */
3477
    protected function getReservedKeywordsClass()
3478
    {
3479
        throw NotSupported::new(__METHOD__);
3480
    }
3481
3482
    /**
3483
     * Quotes a literal string.
3484
     * This method is NOT meant to fix SQL injections!
3485
     * It is only meant to escape this platform's string literal
3486
     * quote character inside the given literal string.
3487
     *
3488
     * @param string $str The literal string to be quoted.
3489
     *
3490
     * @return string The quoted literal string.
3491
     */
3492 55427
    public function quoteStringLiteral($str)
3493
    {
3494 55427
        $c = $this->getStringLiteralQuoteCharacter();
3495
3496 55427
        return $c . str_replace($c, $c . $c, $str) . $c;
3497
    }
3498
3499
    /**
3500
     * Gets the character used for string literal quoting.
3501
     *
3502
     * @return string
3503
     */
3504 55439
    public function getStringLiteralQuoteCharacter()
3505
    {
3506 55439
        return "'";
3507
    }
3508
3509
    /**
3510
     * Escapes metacharacters in a string intended to be used with a LIKE
3511
     * operator.
3512
     *
3513
     * @param string $inputString a literal, unquoted string
3514
     * @param string $escapeChar  should be reused by the caller in the LIKE
3515
     *                            expression.
3516
     */
3517 55671
    final public function escapeStringForLike(string $inputString, string $escapeChar) : string
3518
    {
3519 55671
        return preg_replace(
3520 55671
            '~([' . preg_quote($this->getLikeWildcardCharacters() . $escapeChar, '~') . '])~u',
3521 55671
            addcslashes($escapeChar, '\\') . '$1',
3522 55671
            $inputString
3523
        );
3524
    }
3525
3526 55671
    protected function getLikeWildcardCharacters() : string
3527
    {
3528 55671
        return '%_';
3529
    }
3530
}
3531