Completed
Pull Request — develop (#3524)
by Jonathan
95:41 queued 92:25
created

AbstractPlatform::getCosExpression()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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

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

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

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

968
    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

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

1193
    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...
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

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

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

1625
    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...
1626
    {
1627
        throw DBALException::notSupported(__METHOD__);
1628
    }
1629
1630
    /**
1631
     * Returns the SQL to change a sequence on this platform.
1632
     *
1633
     * @return string
1634
     *
1635
     * @throws DBALException If not supported on this platform.
1636
     */
1637
    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

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

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

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

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

2807
    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...
2808
    {
2809
        throw DBALException::notSupported(__METHOD__);
2810
    }
2811
2812
    /**
2813
     * @param string $sequenceName
2814
     *
2815
     * @return string
2816
     *
2817
     * @throws DBALException If not supported on this platform.
2818
     */
2819
    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

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

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