Failed Conditions
Push — develop ( 152bc9...e39bc0 )
by Sergei
102:42 queued 37:39
created

AbstractPlatform::getDropTableSQL()   B

Complexity

Conditions 7
Paths 10

Size

Total Lines 28
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 7.392

Importance

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

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

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

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

779
            $expression .= /** @scrutinizer ignore-type */ $char . ' ';
Loading history...
780 720
        }
781
782
        if ($mode || $char !== false) {
783
            $expression .= 'FROM ';
784
        }
785
786
        return 'TRIM(' . $expression . $str . ')';
787
    }
788
789
    /**
790 23
     * Returns the SQL snippet to trim trailing space characters from the expression.
791
     *
792 23
     * @param string $str Literal string or column name.
793
     *
794
     * @return string
795
     */
796
    public function getRtrimExpression($str)
797
    {
798
        return 'RTRIM(' . $str . ')';
799
    }
800
801
    /**
802 23
     * Returns the SQL snippet to trim leading space characters from the expression.
803
     *
804 23
     * @param string $str Literal string or column name.
805
     *
806
     * @return string
807
     */
808
    public function getLtrimExpression($str)
809
    {
810
        return 'LTRIM(' . $str . ')';
811
    }
812
813
    /**
814
     * Returns the SQL snippet to change all characters from the expression to uppercase,
815
     * according to the current character set mapping.
816
     *
817
     * @param string $str Literal string or column name.
818
     *
819
     * @return string
820
     */
821
    public function getUpperExpression($str)
822
    {
823
        return 'UPPER(' . $str . ')';
824
    }
825
826
    /**
827
     * Returns the SQL snippet to change all characters from the expression to lowercase,
828
     * according to the current character set mapping.
829
     *
830
     * @param string $str Literal string or column name.
831
     *
832
     * @return string
833
     */
834
    public function getLowerExpression($str)
835
    {
836
        return 'LOWER(' . $str . ')';
837
    }
838
839
    /**
840
     * Returns the SQL snippet to get the position of the first occurrence of substring $substr in string $str.
841
     *
842
     * @param string    $str      Literal string.
843
     * @param string    $substr   Literal string to find.
844
     * @param int|false $startPos Position to start at, beginning of string by default.
845
     *
846
     * @return string
847
     *
848
     * @throws DBALException If not supported on this platform.
849
     */
850
    public function getLocateExpression($str, $substr, $startPos = false)
0 ignored issues
show
Unused Code introduced by
The parameter $substr is not used and could be removed. ( Ignorable by Annotation )

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

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

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

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

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

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

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

Loading history...
851
    {
852
        throw DBALException::notSupported(__METHOD__);
853
    }
854
855
    /**
856
     * Returns the SQL snippet to get the current system date.
857
     *
858
     * @return string
859
     */
860
    public function getNowExpression()
861
    {
862
        return 'NOW()';
863
    }
864
865
    /**
866
     * Returns a SQL snippet to get a substring inside an SQL statement.
867
     *
868
     * Note: Not SQL92, but common functionality.
869
     *
870
     * SQLite only supports the 2 parameter variant of this function.
871
     *
872
     * @param string   $value  An sql string literal or column name/alias.
873
     * @param int      $from   Where to start the substring portion.
874
     * @param int|null $length The substring portion length.
875
     *
876
     * @return string
877
     */
878
    public function getSubstringExpression($value, $from, $length = null)
879
    {
880
        if ($length === null) {
881
            return 'SUBSTRING(' . $value . ' FROM ' . $from . ')';
882
        }
883
884
        return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $length . ')';
885
    }
886
887
    /**
888 92
     * Returns a SQL snippet to concatenate the given expressions.
889
     *
890 92
     * Accepts an arbitrary number of string parameters. Each parameter must contain an expression.
891
     *
892
     * @return string
893
     */
894
    public function getConcatExpression()
895
    {
896
        return implode(' || ', func_get_args());
897
    }
898
899
    /**
900
     * Returns the SQL for a logical not.
901
     *
902
     * Example:
903
     * <code>
904
     * $q = new Doctrine_Query();
905
     * $e = $q->expr;
906
     * $q->select('*')->from('table')
907
     *   ->where($e->eq('id', $e->not('null'));
908
     * </code>
909
     *
910
     * @param string $expression
911
     *
912
     * @return string The logical expression.
913
     */
914
    public function getNotExpression($expression)
915
    {
916
        return 'NOT(' . $expression . ')';
917
    }
918
919
    /**
920 92
     * Returns the SQL that checks if an expression is null.
921
     *
922 92
     * @param string $expression The expression that should be compared to null.
923
     *
924
     * @return string The logical expression.
925
     */
926
    public function getIsNullExpression($expression)
927
    {
928
        return $expression . ' IS NULL';
929
    }
930
931
    /**
932
     * Returns the SQL that checks if an expression is not null.
933
     *
934
     * @param string $expression The expression that should be compared to null.
935
     *
936
     * @return string The logical expression.
937
     */
938
    public function getIsNotNullExpression($expression)
939
    {
940
        return $expression . ' IS NOT NULL';
941
    }
942
943
    /**
944
     * Returns the SQL that checks if an expression evaluates to a value between two values.
945
     *
946
     * The parameter $expression is checked if it is between $value1 and $value2.
947
     *
948
     * Note: There is a slight difference in the way BETWEEN works on some databases.
949
     * http://www.w3schools.com/sql/sql_between.asp. If you want complete database
950
     * independence you should avoid using between().
951
     *
952
     * @param string $expression The value to compare to.
953
     * @param string $value1     The lower value to compare with.
954
     * @param string $value2     The higher value to compare with.
955
     *
956
     * @return string The logical expression.
957
     */
958
    public function getBetweenExpression($expression, $value1, $value2)
959
    {
960
        return $expression . ' BETWEEN ' . $value1 . ' AND ' . $value2;
961
    }
962
963
    /**
964
     * Returns the SQL to get the arccosine of a value.
965
     *
966
     * @param string $value
967
     *
968
     * @return string
969
     */
970
    public function getAcosExpression($value)
971
    {
972
        return 'ACOS(' . $value . ')';
973
    }
974
975
    /**
976
     * Returns the SQL to get the sine of a value.
977
     *
978
     * @param string $value
979
     *
980
     * @return string
981
     */
982
    public function getSinExpression($value)
983
    {
984
        return 'SIN(' . $value . ')';
985
    }
986
987
    /**
988
     * Returns the SQL to get the PI value.
989
     *
990
     * @return string
991
     */
992
    public function getPiExpression()
993
    {
994
        return 'PI()';
995
    }
996
997
    /**
998
     * Returns the SQL to get the cosine of a value.
999
     *
1000
     * @param string $value
1001
     *
1002
     * @return string
1003
     */
1004
    public function getCosExpression($value)
1005
    {
1006
        return 'COS(' . $value . ')';
1007
    }
1008
1009
    /**
1010
     * Returns the SQL to calculate the difference in days between the two passed dates.
1011
     *
1012
     * Computes diff = date1 - date2.
1013
     *
1014
     * @param string $date1
1015
     * @param string $date2
1016
     *
1017
     * @return string
1018
     *
1019
     * @throws DBALException If not supported on this platform.
1020
     */
1021
    public function getDateDiffExpression($date1, $date2)
0 ignored issues
show
Unused Code introduced by
The parameter $date1 is not used and could be removed. ( Ignorable by Annotation )

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

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

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

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

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

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

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

Loading history...
1022
    {
1023
        throw DBALException::notSupported(__METHOD__);
1024
    }
1025
1026
    /**
1027
     * Returns the SQL to add the number of given seconds to a date.
1028
     *
1029
     * @param string $date
1030 69
     * @param int    $seconds
1031
     *
1032 69
     * @return string
1033
     *
1034
     * @throws DBALException If not supported on this platform.
1035
     */
1036
    public function getDateAddSecondsExpression($date, $seconds)
1037
    {
1038
        return $this->getDateArithmeticIntervalExpression($date, '+', $seconds, DateIntervalUnit::SECOND);
1039
    }
1040
1041
    /**
1042
     * Returns the SQL to subtract the number of given seconds from a date.
1043
     *
1044
     * @param string $date
1045 69
     * @param int    $seconds
1046
     *
1047 69
     * @return string
1048
     *
1049
     * @throws DBALException If not supported on this platform.
1050
     */
1051
    public function getDateSubSecondsExpression($date, $seconds)
1052
    {
1053
        return $this->getDateArithmeticIntervalExpression($date, '-', $seconds, DateIntervalUnit::SECOND);
1054
    }
1055
1056
    /**
1057
     * Returns the SQL to add the number of given minutes to a date.
1058
     *
1059
     * @param string $date
1060 69
     * @param int    $minutes
1061
     *
1062 69
     * @return string
1063
     *
1064
     * @throws DBALException If not supported on this platform.
1065
     */
1066
    public function getDateAddMinutesExpression($date, $minutes)
1067
    {
1068
        return $this->getDateArithmeticIntervalExpression($date, '+', $minutes, DateIntervalUnit::MINUTE);
1069
    }
1070
1071
    /**
1072
     * Returns the SQL to subtract the number of given minutes from a date.
1073
     *
1074
     * @param string $date
1075 69
     * @param int    $minutes
1076
     *
1077 69
     * @return string
1078
     *
1079
     * @throws DBALException If not supported on this platform.
1080
     */
1081
    public function getDateSubMinutesExpression($date, $minutes)
1082
    {
1083
        return $this->getDateArithmeticIntervalExpression($date, '-', $minutes, DateIntervalUnit::MINUTE);
1084
    }
1085
1086
    /**
1087
     * Returns the SQL to add the number of given hours to a date.
1088
     *
1089
     * @param string $date
1090 69
     * @param int    $hours
1091
     *
1092 69
     * @return string
1093
     *
1094
     * @throws DBALException If not supported on this platform.
1095
     */
1096
    public function getDateAddHourExpression($date, $hours)
1097
    {
1098
        return $this->getDateArithmeticIntervalExpression($date, '+', $hours, DateIntervalUnit::HOUR);
1099
    }
1100
1101
    /**
1102
     * Returns the SQL to subtract the number of given hours to a date.
1103
     *
1104
     * @param string $date
1105 69
     * @param int    $hours
1106
     *
1107 69
     * @return string
1108
     *
1109
     * @throws DBALException If not supported on this platform.
1110
     */
1111
    public function getDateSubHourExpression($date, $hours)
1112
    {
1113
        return $this->getDateArithmeticIntervalExpression($date, '-', $hours, DateIntervalUnit::HOUR);
1114
    }
1115
1116
    /**
1117
     * Returns the SQL to add the number of given days to a date.
1118
     *
1119
     * @param string $date
1120 115
     * @param int    $days
1121
     *
1122 115
     * @return string
1123
     *
1124
     * @throws DBALException If not supported on this platform.
1125
     */
1126
    public function getDateAddDaysExpression($date, $days)
1127
    {
1128
        return $this->getDateArithmeticIntervalExpression($date, '+', $days, DateIntervalUnit::DAY);
1129
    }
1130
1131
    /**
1132
     * Returns the SQL to subtract the number of given days to a date.
1133
     *
1134
     * @param string $date
1135 70
     * @param int    $days
1136
     *
1137 70
     * @return string
1138
     *
1139
     * @throws DBALException If not supported on this platform.
1140
     */
1141
    public function getDateSubDaysExpression($date, $days)
1142
    {
1143
        return $this->getDateArithmeticIntervalExpression($date, '-', $days, DateIntervalUnit::DAY);
1144
    }
1145
1146
    /**
1147
     * Returns the SQL to add the number of given weeks to a date.
1148
     *
1149
     * @param string $date
1150 69
     * @param int    $weeks
1151
     *
1152 69
     * @return string
1153
     *
1154
     * @throws DBALException If not supported on this platform.
1155
     */
1156
    public function getDateAddWeeksExpression($date, $weeks)
1157
    {
1158
        return $this->getDateArithmeticIntervalExpression($date, '+', $weeks, DateIntervalUnit::WEEK);
1159
    }
1160
1161
    /**
1162
     * Returns the SQL to subtract the number of given weeks from a date.
1163
     *
1164
     * @param string $date
1165 69
     * @param int    $weeks
1166
     *
1167 69
     * @return string
1168
     *
1169
     * @throws DBALException If not supported on this platform.
1170
     */
1171
    public function getDateSubWeeksExpression($date, $weeks)
1172
    {
1173
        return $this->getDateArithmeticIntervalExpression($date, '-', $weeks, DateIntervalUnit::WEEK);
1174
    }
1175
1176
    /**
1177
     * Returns the SQL to add the number of given months to a date.
1178
     *
1179
     * @param string $date
1180 69
     * @param int    $months
1181
     *
1182 69
     * @return string
1183
     *
1184
     * @throws DBALException If not supported on this platform.
1185
     */
1186
    public function getDateAddMonthExpression($date, $months)
1187
    {
1188
        return $this->getDateArithmeticIntervalExpression($date, '+', $months, DateIntervalUnit::MONTH);
1189
    }
1190
1191
    /**
1192
     * Returns the SQL to subtract the number of given months to a date.
1193
     *
1194
     * @param string $date
1195 69
     * @param int    $months
1196
     *
1197 69
     * @return string
1198
     *
1199
     * @throws DBALException If not supported on this platform.
1200
     */
1201
    public function getDateSubMonthExpression($date, $months)
1202
    {
1203
        return $this->getDateArithmeticIntervalExpression($date, '-', $months, DateIntervalUnit::MONTH);
1204
    }
1205
1206
    /**
1207
     * Returns the SQL to add the number of given quarters to a date.
1208
     *
1209
     * @param string $date
1210 69
     * @param int    $quarters
1211
     *
1212 69
     * @return string
1213
     *
1214
     * @throws DBALException If not supported on this platform.
1215
     */
1216
    public function getDateAddQuartersExpression($date, $quarters)
1217
    {
1218
        return $this->getDateArithmeticIntervalExpression($date, '+', $quarters, DateIntervalUnit::QUARTER);
1219
    }
1220
1221
    /**
1222
     * Returns the SQL to subtract the number of given quarters from a date.
1223
     *
1224
     * @param string $date
1225 69
     * @param int    $quarters
1226
     *
1227 69
     * @return string
1228
     *
1229
     * @throws DBALException If not supported on this platform.
1230
     */
1231
    public function getDateSubQuartersExpression($date, $quarters)
1232
    {
1233
        return $this->getDateArithmeticIntervalExpression($date, '-', $quarters, DateIntervalUnit::QUARTER);
1234
    }
1235
1236
    /**
1237
     * Returns the SQL to add the number of given years to a date.
1238
     *
1239
     * @param string $date
1240 69
     * @param int    $years
1241
     *
1242 69
     * @return string
1243
     *
1244
     * @throws DBALException If not supported on this platform.
1245
     */
1246
    public function getDateAddYearsExpression($date, $years)
1247
    {
1248
        return $this->getDateArithmeticIntervalExpression($date, '+', $years, DateIntervalUnit::YEAR);
1249
    }
1250
1251
    /**
1252
     * Returns the SQL to subtract the number of given years from a date.
1253
     *
1254
     * @param string $date
1255 69
     * @param int    $years
1256
     *
1257 69
     * @return string
1258
     *
1259
     * @throws DBALException If not supported on this platform.
1260
     */
1261
    public function getDateSubYearsExpression($date, $years)
1262
    {
1263
        return $this->getDateArithmeticIntervalExpression($date, '-', $years, DateIntervalUnit::YEAR);
1264
    }
1265
1266
    /**
1267
     * Returns the SQL for a date arithmetic expression.
1268
     *
1269
     * @param string $date     The column or literal representing a date to perform the arithmetic operation on.
1270
     * @param string $operator The arithmetic operator (+ or -).
1271
     * @param int    $interval The interval that shall be calculated into the date.
1272
     * @param string $unit     The unit of the interval that shall be calculated into the date.
1273
     *                         One of the DATE_INTERVAL_UNIT_* constants.
1274
     *
1275
     * @return string
1276
     *
1277
     * @throws DBALException If not supported on this platform.
1278
     */
1279
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
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

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

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

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

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

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

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

Loading history...
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

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

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

Loading history...
1280
    {
1281
        throw DBALException::notSupported(__METHOD__);
1282
    }
1283
1284
    /**
1285
     * Returns the SQL bit AND comparison expression.
1286 251
     *
1287
     * @param string $value1
1288 251
     * @param string $value2
1289
     *
1290
     * @return string
1291
     */
1292
    public function getBitAndComparisonExpression($value1, $value2)
1293
    {
1294
        return '(' . $value1 . ' & ' . $value2 . ')';
1295
    }
1296
1297
    /**
1298
     * Returns the SQL bit OR comparison expression.
1299 251
     *
1300
     * @param string $value1
1301 251
     * @param string $value2
1302
     *
1303
     * @return string
1304
     */
1305
    public function getBitOrComparisonExpression($value1, $value2)
1306
    {
1307
        return '(' . $value1 . ' | ' . $value2 . ')';
1308
    }
1309 38
1310
    /**
1311 38
     * Returns the FOR UPDATE expression.
1312
     *
1313
     * @return string
1314
     */
1315
    public function getForUpdateSQL()
1316
    {
1317
        return 'FOR UPDATE';
1318
    }
1319
1320
    /**
1321
     * Honors that some SQL vendors such as MsSql use table hints for locking instead of the ANSI SQL FOR UPDATE specification.
1322
     *
1323 40
     * @param string   $fromClause The FROM clause to append the hint for the given lock mode to.
1324
     * @param int|null $lockMode   One of the Doctrine\DBAL\LockMode::* constants. If null is given, nothing will
1325 40
     *                             be appended to the FROM clause.
1326
     *
1327
     * @return string
1328
     */
1329
    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

1329
    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...
1330
    {
1331
        return $fromClause;
1332
    }
1333
1334
    /**
1335
     * Returns the SQL snippet to append to any SELECT statement which locks rows in shared read lock.
1336
     *
1337
     * This defaults to the ANSI SQL "FOR UPDATE", which is an exclusive lock (Write). Some database
1338
     * vendors allow to lighten this constraint up to be a real read lock.
1339
     *
1340
     * @return string
1341
     */
1342
    public function getReadLockSQL()
1343
    {
1344
        return $this->getForUpdateSQL();
1345
    }
1346
1347
    /**
1348 44
     * Returns the SQL snippet to append to any SELECT statement which obtains an exclusive lock on the rows.
1349
     *
1350 44
     * The semantics of this lock mode should equal the SELECT .. FOR UPDATE of the ANSI SQL standard.
1351
     *
1352
     * @return string
1353
     */
1354
    public function getWriteLockSQL()
1355
    {
1356
        return $this->getForUpdateSQL();
1357
    }
1358
1359
    /**
1360 81
     * Returns the SQL snippet to drop an existing database.
1361
     *
1362 81
     * @param string $database The name of the database that should be dropped.
1363
     *
1364
     * @return string
1365
     */
1366
    public function getDropDatabaseSQL($database)
1367
    {
1368
        return 'DROP DATABASE ' . $database;
1369
    }
1370
1371
    /**
1372
     * Returns the SQL snippet to drop an existing table.
1373
     *
1374 3453
     * @param Table|string $table
1375
     *
1376 3453
     * @return string
1377
     *
1378 3453
     * @throws InvalidArgumentException
1379 335
     */
1380
    public function getDropTableSQL($table)
1381
    {
1382 3453
        $tableArg = $table;
1383
1384
        if ($table instanceof Table) {
1385
            $table = $table->getQuotedName($this);
1386 3453
        }
1387 276
1388 276
        if (! is_string($table)) {
0 ignored issues
show
introduced by
The condition is_string($table) is always true.
Loading history...
1389
            throw new InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.');
1390 276
        }
1391
1392
        if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaDropTable)) {
1393
            $eventArgs = new SchemaDropTableEventArgs($tableArg, $this);
1394
            $this->_eventManager->dispatchEvent(Events::onSchemaDropTable, $eventArgs);
1395 3453
1396
            if ($eventArgs->isDefaultPrevented()) {
1397
                $sql = $eventArgs->getSql();
1398
1399
                if ($sql === null) {
1400
                    throw new UnexpectedValueException('Default implementation of DROP TABLE was overridden with NULL');
1401
                }
1402
1403
                return $sql;
1404
            }
1405 20
        }
1406
1407 20
        return 'DROP TABLE ' . $table;
1408
    }
1409
1410
    /**
1411
     * Returns the SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction.
1412
     *
1413
     * @param Table|string $table
1414
     *
1415
     * @return string
1416
     */
1417
    public function getDropTemporaryTableSQL($table)
1418
    {
1419
        return $this->getDropTableSQL($table);
1420 164
    }
1421
1422 164
    /**
1423 155
     * Returns the SQL to drop an index from a table.
1424 9
     *
1425
     * @param Index|string $index
1426
     * @param Table|string $table
1427
     *
1428 164
     * @return string
1429
     *
1430
     * @throws InvalidArgumentException
1431
     */
1432
    public function getDropIndexSQL($index, $table = null)
1433
    {
1434
        if ($index instanceof Index) {
1435
            $index = $index->getQuotedName($this);
1436
        } elseif (! is_string($index)) {
0 ignored issues
show
introduced by
The condition is_string($index) is always true.
Loading history...
1437
            throw new InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.');
1438
        }
1439 655
1440
        return 'DROP INDEX ' . $index;
1441 655
    }
1442 511
1443
    /**
1444
     * Returns the SQL to drop a constraint.
1445 655
     *
1446 655
     * @param Constraint|string $constraint
1447
     * @param Table|string      $table
1448
     *
1449 655
     * @return string
1450 655
     */
1451
    public function getDropConstraintSQL($constraint, $table)
1452 655
    {
1453
        if (! $constraint instanceof Constraint) {
1454
            $constraint = new Identifier($constraint);
1455
        }
1456
1457
        if (! $table instanceof Table) {
1458
            $table = new Identifier($table);
1459
        }
1460
1461
        $constraint = $constraint->getQuotedName($this);
1462
        $table      = $table->getQuotedName($this);
1463 368
1464
        return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $constraint;
1465 368
    }
1466 115
1467
    /**
1468
     * Returns the SQL to drop a foreign key.
1469 368
     *
1470 368
     * @param ForeignKeyConstraint|string $foreignKey
1471
     * @param Table|string                $table
1472
     *
1473 368
     * @return string
1474 368
     */
1475
    public function getDropForeignKeySQL($foreignKey, $table)
1476 368
    {
1477
        if (! $foreignKey instanceof ForeignKeyConstraint) {
1478
            $foreignKey = new Identifier($foreignKey);
1479
        }
1480
1481
        if (! $table instanceof Table) {
1482
            $table = new Identifier($table);
1483
        }
1484
1485
        $foreignKey = $foreignKey->getQuotedName($this);
1486
        $table      = $table->getQuotedName($this);
1487
1488
        return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $foreignKey;
1489
    }
1490 8074
1491
    /**
1492 8074
     * Returns the SQL statement(s) to create a table with the specified name, columns and constraints
1493
     * on this platform.
1494
     *
1495
     * @param int $createFlags
1496 8074
     *
1497 276
     * @return string[] The sequence of SQL statements.
1498
     *
1499
     * @throws DBALException
1500 7798
     * @throws InvalidArgumentException
1501 7798
     */
1502 7798
    public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDEXES)
1503 7798
    {
1504 7798
        if (! is_int($createFlags)) {
0 ignored issues
show
introduced by
The condition is_int($createFlags) is always true.
Loading history...
1505
            throw new InvalidArgumentException('Second argument of AbstractPlatform::getCreateTableSQL() has to be integer.');
1506 7798
        }
1507 7476
1508
        if (count($table->getColumns()) === 0) {
1509 5003
            throw DBALException::noColumnsSpecifiedForTable($table->getName());
1510 1567
        }
1511
1512 1567
        $tableName                    = $table->getQuotedName($this);
1513
        $options                      = $table->getOptions();
1514
        $options['uniqueConstraints'] = [];
1515 3870
        $options['indexes']           = [];
1516 3870
        $options['primary']           = [];
1517
1518
        if (($createFlags & self::CREATE_INDEXES) > 0) {
1519 7476
            foreach ($table->getIndexes() as $index) {
1520
                /** @var $index Index */
1521
                if (! $index->isPrimary()) {
1522
                    $options['indexes'][$index->getQuotedName($this)] = $index;
1523
1524
                    continue;
1525 7798
                }
1526 4345
1527
                $options['primary']       = $index->getQuotedColumns($this);
1528 4345
                $options['primary_index'] = $index;
1529 675
            }
1530
1531
            foreach ($table->getUniqueConstraints() as $uniqueConstraint) {
1532
                /** @var UniqueConstraint $uniqueConstraint */
1533 7798
                $options['uniqueConstraints'][$uniqueConstraint->getQuotedName($this)] = $uniqueConstraint;
1534 7798
            }
1535
        }
1536 7798
1537
        if (($createFlags & self::CREATE_FOREIGNKEYS) > 0) {
1538 7798
            $options['foreignKeys'] = [];
1539 276
1540 276
            foreach ($table->getForeignKeys() as $fkConstraint) {
1541
                $options['foreignKeys'][] = $fkConstraint;
1542 276
            }
1543
        }
1544 276
1545
        $columnSql = [];
1546
        $columns   = [];
1547
1548
        foreach ($table->getColumns() as $column) {
1549 7798
            if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn)) {
1550 7798
                $eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this);
1551 7798
                $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs);
1552 7798
1553
                $columnSql = array_merge($columnSql, $eventArgs->getSql());
1554 7798
1555 2881
                if ($eventArgs->isDefaultPrevented()) {
1556
                    continue;
1557
                }
1558 7798
            }
1559 3548
1560
            $columnData            = $column->toArray();
1561
            $columnData['name']    = $column->getQuotedName($this);
1562 7798
            $columnData['version'] = $column->hasPlatformOption('version') ? $column->getPlatformOption('version') : false;
1563
            $columnData['comment'] = $this->getColumnComment($column);
1564
1565 7798
            if ($columnData['type'] instanceof Types\StringType && $columnData['length'] === null) {
1566 276
                $columnData['length'] = 255;
1567 276
            }
1568
1569 276
            if (in_array($column->getName(), $options['primary'])) {
1570
                $columnData['primary'] = true;
1571
            }
1572
1573
            $columns[$columnData['name']] = $columnData;
1574 7798
        }
1575 7798
1576 2993
        if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) {
1577 2993
            $eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this);
1578
            $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs);
1579 2993
1580 2807
            if ($eventArgs->isDefaultPrevented()) {
1581
                return array_merge($eventArgs->getSql(), $columnSql);
1582
            }
1583 525
        }
1584
1585
        $sql = $this->_getCreateTableSQL($tableName, $columns, $options);
1586
        if ($this->supportsCommentOnStatement()) {
1587 7798
            foreach ($table->getColumns() as $column) {
1588
                $comment = $this->getColumnComment($column);
1589
1590
                if ($comment === null || $comment === '') {
1591
                    continue;
1592
                }
1593
1594
                $sql[] = $this->getCommentOnColumnSQL($tableName, $column->getQuotedName($this), $comment);
1595
            }
1596
        }
1597 777
1598
        return array_merge($sql, $columnSql);
1599 777
    }
1600 777
1601 777
    /**
1602
     * @param string      $tableName
1603 777
     * @param string      $columnName
1604 777
     * @param string|null $comment
1605 777
     *
1606 777
     * @return string
1607 777
     */
1608
    public function getCommentOnColumnSQL($tableName, $columnName, $comment)
1609
    {
1610
        $tableName  = new Identifier($tableName);
1611
        $columnName = new Identifier($columnName);
1612
1613
        return sprintf(
1614
            'COMMENT ON COLUMN %s.%s IS %s',
1615
            $tableName->getQuotedName($this),
1616
            $columnName->getQuotedName($this),
1617
            $this->quoteStringLiteral((string) $comment)
1618
        );
1619
    }
1620 1209
1621
    /**
1622 1209
     * Returns the SQL to create inline comment on a column.
1623 184
     *
1624
     * @param string $comment
1625
     *
1626 1025
     * @return string
1627
     *
1628
     * @throws DBALException If not supported on this platform.
1629
     */
1630
    public function getInlineColumnCommentSQL($comment)
1631
    {
1632
        if (! $this->supportsInlineColumnComments()) {
1633
            throw DBALException::notSupported(__METHOD__);
1634
        }
1635
1636
        return 'COMMENT ' . $this->quoteStringLiteral($comment);
1637
    }
1638 793
1639
    /**
1640 793
     * Returns the SQL used to create a table.
1641
     *
1642 793
     * @param string    $tableName
1643
     * @param mixed[][] $columns
1644
     * @param mixed[]   $options
1645
     *
1646
     * @return string[]
1647
     */
1648 793
    protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
1649 405
    {
1650
        $columnListSql = $this->getColumnDeclarationListSQL($columns);
1651
1652 793
        if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
1653
            foreach ($options['uniqueConstraints'] as $name => $definition) {
1654
                $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
1655
            }
1656
        }
1657
1658 793
        if (isset($options['primary']) && ! empty($options['primary'])) {
1659
            $columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')';
1660 793
        }
1661 793
1662 23
        if (isset($options['indexes']) && ! empty($options['indexes'])) {
1663
            foreach ($options['indexes'] as $index => $definition) {
1664 793
                $columnListSql .= ', ' . $this->getIndexDeclarationSQL($index, $definition);
1665
            }
1666 793
        }
1667
1668 793
        $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql;
1669 322
1670 82
        $check = $this->getCheckDeclarationSQL($columns);
1671
        if (! empty($check)) {
1672
            $query .= ', ' . $check;
1673
        }
1674 793
        $query .= ')';
1675
1676
        $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...
1677
1678
        if (isset($options['foreignKeys'])) {
1679
            foreach ((array) $options['foreignKeys'] as $definition) {
1680 38
                $sql[] = $this->getCreateForeignKeySQL($definition, $tableName);
1681
            }
1682 38
        }
1683
1684
        return $sql;
1685
    }
1686
1687
    /**
1688
     * @return string
1689
     */
1690
    public function getCreateTemporaryTableSnippetSQL()
1691
    {
1692
        return 'CREATE TEMPORARY TABLE';
1693
    }
1694
1695
    /**
1696
     * Returns the SQL to create a sequence on this platform.
1697
     *
1698
     * @return string
1699
     *
1700
     * @throws DBALException If not supported on this platform.
1701
     */
1702
    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

1702
    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...
1703
    {
1704
        throw DBALException::notSupported(__METHOD__);
1705
    }
1706
1707
    /**
1708
     * Returns the SQL to change a sequence on this platform.
1709
     *
1710
     * @return string
1711
     *
1712
     * @throws DBALException If not supported on this platform.
1713
     */
1714
    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

1714
    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...
1715
    {
1716
        throw DBALException::notSupported(__METHOD__);
1717
    }
1718 323
1719
    /**
1720 323
     * Returns the SQL to create a constraint on a table on this platform.
1721
     *
1722
     * @param Table|string $table
1723
     *
1724 323
     * @return string
1725
     *
1726 323
     * @throws InvalidArgumentException
1727
     */
1728 323
    public function getCreateConstraintSQL(Constraint $constraint, $table)
1729 323
    {
1730 323
        if ($table instanceof Table) {
1731 323
            $table = $table->getQuotedName($this);
1732 230
        }
1733 230
1734
        $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this);
1735
1736 323
        $columnList = '(' . implode(', ', $constraint->getQuotedColumns($this)) . ')';
1737
1738
        $referencesClause = '';
1739 230
        if ($constraint instanceof Index) {
1740 230
            if ($constraint->isPrimary()) {
1741
                $query .= ' PRIMARY KEY';
1742 230
            } elseif ($constraint->isUnique()) {
1743 230
                $query .= ' UNIQUE';
1744
            } else {
1745 323
                throw new InvalidArgumentException(
1746
                    'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().'
1747 323
                );
1748
            }
1749
        } elseif ($constraint instanceof ForeignKeyConstraint) {
1750
            $query .= ' FOREIGN KEY';
1751
1752
            $referencesClause = ' REFERENCES ' . $constraint->getQuotedForeignTableName($this) .
1753
                ' (' . implode(', ', $constraint->getQuotedForeignColumns($this)) . ')';
1754
        }
1755
        $query .= ' ' . $columnList . $referencesClause;
1756
1757
        return $query;
1758
    }
1759 2638
1760
    /**
1761 2638
     * Returns the SQL to create an index on a table on this platform.
1762 23
     *
1763
     * @param Table|string $table The name of the table on which the index is to be created.
1764 2638
     *
1765 2638
     * @return string
1766
     *
1767 2638
     * @throws InvalidArgumentException
1768
     */
1769
    public function getCreateIndexSQL(Index $index, $table)
1770
    {
1771 2638
        if ($table instanceof Table) {
1772 437
            $table = $table->getQuotedName($this);
1773
        }
1774
        $name    = $index->getQuotedName($this);
1775 2224
        $columns = $index->getColumns();
1776 2224
1777
        if (count($columns) === 0) {
1778 2224
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
1779
        }
1780
1781
        if ($index->isPrimary()) {
1782
            return $this->getCreatePrimaryKeySQL($index, $table);
1783
        }
1784
1785
        $query  = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table;
1786 3084
        $query .= ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')' . $this->getPartialIndexSQL($index);
1787
1788 3084
        return $query;
1789 75
    }
1790
1791
    /**
1792 3009
     * Adds condition for partial index.
1793
     *
1794
     * @return string
1795
     */
1796
    protected function getPartialIndexSQL(Index $index)
1797
    {
1798
        if ($this->supportsPartialIndexes() && $index->hasOption('where')) {
1799
            return ' WHERE ' . $index->getOption('where');
1800 1240
        }
1801
1802 1240
        return '';
1803
    }
1804
1805
    /**
1806
     * Adds additional flags for index generation.
1807
     *
1808
     * @return string
1809
     */
1810
    protected function getCreateIndexSQLFlags(Index $index)
1811
    {
1812 391
        return $index->isUnique() ? 'UNIQUE ' : '';
1813
    }
1814 391
1815
    /**
1816
     * Returns the SQL to create an unnamed primary key constraint.
1817
     *
1818
     * @param Table|string $table
1819
     *
1820
     * @return string
1821
     */
1822
    public function getCreatePrimaryKeySQL(Index $index, $table)
1823
    {
1824
        if ($table instanceof Table) {
1825
            $table = $table->getQuotedName($this);
1826 161
        }
1827
1828 161
        return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index) . ')';
1829
    }
1830
1831
    /**
1832
     * Returns the SQL to create a named schema.
1833
     *
1834
     * @param string $schemaName
1835
     *
1836
     * @return string
1837
     *
1838
     * @throws DBALException If not supported on this platform.
1839
     */
1840
    public function getCreateSchemaSQL($schemaName)
1841
    {
1842
        throw DBALException::notSupported(__METHOD__);
1843
    }
1844 6377
1845
    /**
1846 6377
     * Quotes a string so that it can be safely used as a table or column name,
1847 300
     * even if it is a reserved word of the platform. This also detects identifier
1848
     * chains separated by dot and quotes them independently.
1849 300
     *
1850
     * NOTE: Just because you CAN use quoted identifiers doesn't mean
1851
     * you SHOULD use them. In general, they end up causing way more
1852 6377
     * problems than they solve.
1853
     *
1854
     * @param string $str The identifier name to be quoted.
1855
     *
1856
     * @return string The quoted identifier string.
1857
     */
1858
    public function quoteIdentifier($str)
1859
    {
1860
        if (strpos($str, '.') !== false) {
1861
            $parts = array_map([$this, 'quoteSingleIdentifier'], explode('.', $str));
1862 6293
1863
            return implode('.', $parts);
1864 6293
        }
1865
1866 6293
        return $this->quoteSingleIdentifier($str);
1867
    }
1868
1869
    /**
1870
     * Quotes a single identifier (no dot chain separation).
1871
     *
1872
     * @param string $str The identifier name to be quoted.
1873
     *
1874
     * @return string The quoted identifier string.
1875
     */
1876
    public function quoteSingleIdentifier($str)
1877 1499
    {
1878
        $c = $this->getIdentifierQuoteCharacter();
1879 1499
1880 23
        return $c . str_replace($c, $c . $c, $str) . $c;
1881
    }
1882
1883 1499
    /**
1884
     * Returns the SQL to create a new foreign key.
1885
     *
1886
     * @param ForeignKeyConstraint $foreignKey The foreign key constraint.
1887
     * @param Table|string         $table      The name of the table on which the foreign key is to be created.
1888
     *
1889
     * @return string
1890
     */
1891
    public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table)
1892
    {
1893
        if ($table instanceof Table) {
1894
            $table = $table->getQuotedName($this);
1895
        }
1896
1897
        return 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey);
1898
    }
1899
1900
    /**
1901
     * Gets the SQL statements for altering an existing table.
1902
     *
1903
     * This method returns an array of SQL statements, since some platforms need several statements.
1904
     *
1905 1591
     * @return string[]
1906
     *
1907 1591
     * @throws DBALException If not supported on this platform.
1908 1288
     */
1909
    public function getAlterTableSQL(TableDiff $diff)
1910
    {
1911 303
        throw DBALException::notSupported(__METHOD__);
1912 27
    }
1913
1914
    /**
1915 276
     * @param mixed[] $columnSql
1916 276
     *
1917
     * @return bool
1918 276
     */
1919
    protected function onSchemaAlterTableAddColumn(Column $column, TableDiff $diff, &$columnSql)
1920 276
    {
1921
        if ($this->_eventManager === null) {
1922
            return false;
1923
        }
1924
1925
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableAddColumn)) {
1926
            return false;
1927
        }
1928 1179
1929
        $eventArgs = new SchemaAlterTableAddColumnEventArgs($column, $diff, $this);
1930 1179
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn, $eventArgs);
1931 874
1932
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1933
1934 305
        return $eventArgs->isDefaultPrevented();
1935 29
    }
1936
1937
    /**
1938 276
     * @param string[] $columnSql
1939 276
     *
1940
     * @return bool
1941 276
     */
1942
    protected function onSchemaAlterTableRemoveColumn(Column $column, TableDiff $diff, &$columnSql)
1943 276
    {
1944
        if ($this->_eventManager === null) {
1945
            return false;
1946
        }
1947
1948
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRemoveColumn)) {
1949
            return false;
1950
        }
1951 3165
1952
        $eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column, $diff, $this);
1953 3165
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn, $eventArgs);
1954 2576
1955
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1956
1957 589
        return $eventArgs->isDefaultPrevented();
1958 313
    }
1959
1960
    /**
1961 276
     * @param string[] $columnSql
1962 276
     *
1963
     * @return bool
1964 276
     */
1965
    protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiff, TableDiff $diff, &$columnSql)
1966 276
    {
1967
        if ($this->_eventManager === null) {
1968
            return false;
1969
        }
1970
1971
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) {
1972
            return false;
1973
        }
1974
1975 1199
        $eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff, $diff, $this);
1976
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn, $eventArgs);
1977 1199
1978 897
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1979
1980
        return $eventArgs->isDefaultPrevented();
1981 302
    }
1982 26
1983
    /**
1984
     * @param string   $oldColumnName
1985 276
     * @param string[] $columnSql
1986 276
     *
1987
     * @return bool
1988 276
     */
1989
    protected function onSchemaAlterTableRenameColumn($oldColumnName, Column $column, TableDiff $diff, &$columnSql)
1990 276
    {
1991
        if ($this->_eventManager === null) {
1992
            return false;
1993
        }
1994
1995
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRenameColumn)) {
1996
            return false;
1997
        }
1998 6129
1999
        $eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName, $column, $diff, $this);
2000 6129
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn, $eventArgs);
2001 5428
2002
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
2003
2004 701
        return $eventArgs->isDefaultPrevented();
2005 425
    }
2006
2007
    /**
2008 276
     * @param string[] $sql
2009 276
     *
2010
     * @return bool
2011 276
     */
2012
    protected function onSchemaAlterTable(TableDiff $diff, &$sql)
2013 276
    {
2014
        if ($this->_eventManager === null) {
2015
            return false;
2016
        }
2017
2018
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTable)) {
2019 5811
            return false;
2020
        }
2021 5811
2022
        $eventArgs = new SchemaAlterTableEventArgs($diff, $this);
2023 5811
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTable, $eventArgs);
2024 5811
2025 5811
        $sql = array_merge($sql, $eventArgs->getSql());
2026 413
2027
        return $eventArgs->isDefaultPrevented();
2028 5811
    }
2029 322
2030
    /**
2031
     * @return string[]
2032
     */
2033 5811
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
2034 253
    {
2035
        $tableName = $diff->getName($this)->getQuotedName($this);
2036 5811
2037 412
        $sql = [];
2038
        if ($this->supportsForeignKeyConstraints()) {
2039
            foreach ($diff->removedForeignKeys as $foreignKey) {
2040 5811
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
2041
            }
2042
            foreach ($diff->changedForeignKeys as $foreignKey) {
2043
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
2044
            }
2045
        }
2046 5811
2047
        foreach ($diff->removedIndexes as $index) {
2048 5811
            $sql[] = $this->getDropIndexSQL($index, $tableName);
2049 509
        }
2050 5811
        foreach ($diff->changedIndexes as $index) {
2051
            $sql[] = $this->getDropIndexSQL($index, $tableName);
2052 5811
        }
2053
2054 5811
        return $sql;
2055 5811
    }
2056 366
2057
    /**
2058
     * @return string[]
2059 5811
     */
2060 322
    protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff)
2061
    {
2062
        $sql     = [];
2063
        $newName = $diff->getNewName();
2064 5811
2065 91
        if ($newName !== false) {
2066
            $tableName = $newName->getQuotedName($this);
2067
        } else {
2068 5811
            $tableName = $diff->getName($this)->getQuotedName($this);
2069 412
        }
2070
2071
        if ($this->supportsForeignKeyConstraints()) {
2072 5811
            foreach ($diff->addedForeignKeys as $foreignKey) {
2073 1309
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
2074 1309
            }
2075 1309
2076 1309
            foreach ($diff->changedForeignKeys as $foreignKey) {
2077
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
2078
            }
2079
        }
2080 5811
2081
        foreach ($diff->addedIndexes as $index) {
2082
            $sql[] = $this->getCreateIndexSQL($index, $tableName);
2083
        }
2084
2085
        foreach ($diff->changedIndexes as $index) {
2086
            $sql[] = $this->getCreateIndexSQL($index, $tableName);
2087
        }
2088
2089
        foreach ($diff->renamedIndexes as $oldIndexName => $index) {
2090
            $oldIndexName = new Identifier($oldIndexName);
2091
            $sql          = array_merge(
2092 250
                $sql,
2093
                $this->getRenameIndexSQL($oldIndexName->getQuotedName($this), $index, $tableName)
2094
            );
2095 250
        }
2096 250
2097
        return $sql;
2098
    }
2099
2100
    /**
2101
     * Returns the SQL for renaming an index on a table.
2102
     *
2103
     * @param string $oldIndexName The name of the index to rename from.
2104
     * @param Index  $index        The definition of the index to rename to.
2105
     * @param string $tableName    The table to rename the given index on.
2106
     *
2107
     * @return string[] The sequence of SQL statements for renaming the given index.
2108
     */
2109
    protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
2110
    {
2111
        return [
2112
            $this->getDropIndexSQL($oldIndexName, $tableName),
2113
            $this->getCreateIndexSQL($index, $tableName),
2114
        ];
2115
    }
2116
2117
    /**
2118
     * Common code for alter table statement generation that updates the changed Index and Foreign Key definitions.
2119
     *
2120
     * @return string[]
2121
     */
2122
    protected function _getAlterTableIndexForeignKeySQL(TableDiff $diff)
2123
    {
2124
        return array_merge($this->getPreAlterTableIndexForeignKeySQL($diff), $this->getPostAlterTableIndexForeignKeySQL($diff));
2125
    }
2126
2127
    /**
2128
     * Gets declaration of a number of fields in bulk.
2129
     *
2130
     * @param mixed[][] $fields A multidimensional associative array.
2131
     *                          The first dimension determines the field name, while the second
2132
     *                          dimension is keyed with the name of the properties
2133
     *                          of the field being declared as array indexes. Currently, the types
2134
     *                          of supported field properties are as follows:
2135
     *
2136
     *      length
2137
     *          Integer value that determines the maximum length of the text
2138
     *          field. If this argument is missing the field should be
2139 7798
     *          declared to have the longest length allowed by the DBMS.
2140
     *
2141 7798
     *      default
2142
     *          Text value to be used as default for this field.
2143 7798
     *
2144 7798
     *      notnull
2145
     *          Boolean flag that indicates whether this field is constrained
2146
     *          to not be set to null.
2147 7798
     *      charset
2148
     *          Text value with the default CHARACTER SET for this field.
2149
     *      collation
2150
     *          Text value with the default COLLATION for this field.
2151
     *      unique
2152
     *          unique constraint
2153
     *
2154
     * @return string
2155
     */
2156
    public function getColumnDeclarationListSQL(array $fields)
2157
    {
2158
        $queryFields = [];
2159
2160
        foreach ($fields as $fieldName => $field) {
2161
            $queryFields[] = $this->getColumnDeclarationSQL($fieldName, $field);
2162
        }
2163
2164
        return implode(', ', $queryFields);
2165
    }
2166
2167
    /**
2168
     * Obtains DBMS specific SQL code portion needed to declare a generic type
2169
     * field to be used in statements like CREATE TABLE.
2170
     *
2171
     * @param string  $name  The name the field to be declared.
2172
     * @param mixed[] $field An associative array with the name of the properties
2173
     *                       of the field being declared as array indexes. Currently, the types
2174
     *                       of supported field properties are as follows:
2175
     *
2176
     *      length
2177
     *          Integer value that determines the maximum length of the text
2178
     *          field. If this argument is missing the field should be
2179
     *          declared to have the longest length allowed by the DBMS.
2180
     *
2181
     *      default
2182
     *          Text value to be used as default for this field.
2183 8106
     *
2184
     *      notnull
2185 8106
     *          Boolean flag that indicates whether this field is constrained
2186 220
     *          to not be set to null.
2187
     *      charset
2188 7899
     *          Text value with the default CHARACTER SET for this field.
2189
     *      collation
2190 7899
     *          Text value with the default COLLATION for this field.
2191 7899
     *      unique
2192
     *          unique constraint
2193 7899
     *      check
2194 7899
     *          column check constraint
2195
     *      columnDefinition
2196 7899
     *          a string that defines the complete column
2197
     *
2198 7899
     * @return string DBMS specific SQL code portion that should be used to declare the column.
2199 7899
     */
2200
    public function getColumnDeclarationSQL($name, array $field)
2201 7899
    {
2202 7899
        if (isset($field['columnDefinition'])) {
2203
            $columnDef = $this->getCustomTypeDeclarationSQL($field);
2204 7899
        } else {
2205 7899
            $default = $this->getDefaultValueDeclarationSQL($field);
2206
2207 7899
            $charset = isset($field['charset']) && $field['charset'] ?
2208 919
                ' ' . $this->getColumnCharsetDeclarationSQL($field['charset']) : '';
2209
2210
            $collation = isset($field['collation']) && $field['collation'] ?
2211
                ' ' . $this->getColumnCollationDeclarationSQL($field['collation']) : '';
2212 8106
2213
            $notnull = isset($field['notnull']) && $field['notnull'] ? ' NOT NULL' : '';
2214
2215
            $unique = isset($field['unique']) && $field['unique'] ?
2216
                ' ' . $this->getUniqueFieldDeclarationSQL() : '';
2217
2218
            $check = isset($field['check']) && $field['check'] ?
2219
                ' ' . $field['check'] : '';
2220
2221
            $typeDecl  = $field['type']->getSQLDeclaration($field, $this);
2222 2217
            $columnDef = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation;
2223
2224 2217
            if ($this->supportsInlineColumnComments() && isset($field['comment']) && $field['comment'] !== '') {
2225 2217
                $columnDef .= ' ' . $this->getInlineColumnCommentSQL($field['comment']);
2226 2217
            }
2227 2217
        }
2228
2229 2217
        return $name . ' ' . $columnDef;
2230
    }
2231
2232
    /**
2233
     * Returns the SQL snippet that declares a floating point column of arbitrary precision.
2234
     *
2235
     * @param mixed[] $columnDef
2236
     *
2237
     * @return string
2238
     */
2239
    public function getDecimalTypeDeclarationSQL(array $columnDef)
2240 8990
    {
2241
        $columnDef['precision'] = ! isset($columnDef['precision']) || empty($columnDef['precision'])
2242 8990
            ? 10 : $columnDef['precision'];
2243 7878
        $columnDef['scale']     = ! isset($columnDef['scale']) || empty($columnDef['scale'])
2244
            ? 0 : $columnDef['scale'];
2245
2246 1536
        return 'NUMERIC(' . $columnDef['precision'] . ', ' . $columnDef['scale'] . ')';
2247
    }
2248 1536
2249
    /**
2250
     * Obtains DBMS specific SQL code portion needed to set a default value
2251
     * declaration to be used in statements like CREATE TABLE.
2252 1536
     *
2253
     * @param mixed[] $field The field definition array.
2254 1536
     *
2255 479
     * @return string DBMS specific SQL code portion needed to set a default value.
2256
     */
2257
    public function getDefaultValueDeclarationSQL($field)
2258 1117
    {
2259 258
        if (! isset($field['default'])) {
2260
            return empty($field['notnull']) ? ' DEFAULT NULL' : '';
2261
        }
2262 875
2263 4
        $default = $field['default'];
2264
2265
        if (! isset($field['type'])) {
2266 875
            return " DEFAULT '" . $default . "'";
2267 234
        }
2268
2269
        $type = $field['type'];
2270 641
2271 236
        if ($type instanceof Types\PhpIntegerMappingType) {
2272
            return ' DEFAULT ' . $default;
2273
        }
2274 635
2275
        if ($type instanceof Types\PhpDateTimeMappingType && $default === $this->getCurrentTimestampSQL()) {
2276
            return ' DEFAULT ' . $this->getCurrentTimestampSQL();
2277
        }
2278
2279
        if ($type instanceof Types\TimeType && $default === $this->getCurrentTimeSQL()) {
2280
            return ' DEFAULT ' . $this->getCurrentTimeSQL();
2281
        }
2282
2283
        if ($type instanceof Types\DateType && $default === $this->getCurrentDateSQL()) {
2284
            return ' DEFAULT ' . $this->getCurrentDateSQL();
2285 2139
        }
2286
2287 2139
        if ($type instanceof Types\BooleanType) {
2288 2139
            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

2288
            return " DEFAULT '" . /** @scrutinizer ignore-type */ $this->convertBooleans($default) . "'";
Loading history...
2289 2139
        }
2290
2291
        return " DEFAULT '" . $default . "'";
2292 2139
    }
2293 46
2294
    /**
2295
     * Obtains DBMS specific SQL code portion needed to set a CHECK constraint
2296 2139
     * declaration to be used in statements like CREATE TABLE.
2297 2139
     *
2298
     * @param string[]|mixed[][] $definition The check definition.
2299
     *
2300
     * @return string DBMS specific SQL code portion needed to set a CHECK constraint.
2301
     */
2302 2139
    public function getCheckDeclarationSQL(array $definition)
2303
    {
2304
        $constraints = [];
2305
        foreach ($definition as $field => $def) {
2306
            if (is_string($def)) {
2307
                $constraints[] = 'CHECK (' . $def . ')';
2308
            } else {
2309
                if (isset($def['min'])) {
2310
                    $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')';
2311
                }
2312
2313
                if (isset($def['max'])) {
2314
                    $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')';
2315
                }
2316 598
            }
2317
        }
2318 598
2319 598
        return implode(', ', $constraints);
2320
    }
2321 598
2322 23
    /**
2323
     * Obtains DBMS specific SQL code portion needed to set a unique
2324
     * constraint declaration to be used in statements like CREATE TABLE.
2325 575
     *
2326
     * @param string           $name       The name of the unique constraint.
2327 575
     * @param UniqueConstraint $constraint The unique constraint definition.
2328 23
     *
2329
     * @return string DBMS specific SQL code portion needed to set a constraint.
2330
     *
2331 575
     * @throws InvalidArgumentException
2332 575
     */
2333 575
    public function getUniqueConstraintDeclarationSQL($name, UniqueConstraint $constraint)
2334
    {
2335 575
        $columns = $constraint->getColumns();
2336
        $name    = new Identifier($name);
2337
2338
        if (count($columns) === 0) {
2339
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
2340
        }
2341
2342
        $flags = ['UNIQUE'];
2343
2344
        if ($constraint->hasFlag('clustered')) {
2345
            $flags[] = 'CLUSTERED';
2346
        }
2347
2348
        $constraintName  = $name->getQuotedName($this);
2349 1124
        $constraintName  = ! empty($constraintName) ? $constraintName . ' ' : '';
2350
        $columnListNames = $this->getIndexFieldDeclarationListSQL($columns);
2351 1124
2352 1124
        return sprintf('CONSTRAINT %s%s (%s)', $constraintName, implode(' ', $flags), $columnListNames);
2353
    }
2354 1124
2355
    /**
2356
     * Obtains DBMS specific SQL code portion needed to set an index
2357
     * declaration to be used in statements like CREATE TABLE.
2358 1124
     *
2359 1124
     * @param string $name  The name of the index.
2360 1124
     * @param Index  $index The index definition.
2361
     *
2362
     * @return string DBMS specific SQL code portion needed to set an index.
2363
     *
2364
     * @throws InvalidArgumentException
2365
     */
2366
    public function getIndexDeclarationSQL($name, Index $index)
2367
    {
2368
        $columns = $index->getColumns();
2369
        $name    = new Identifier($name);
2370
2371
        if (count($columns) === 0) {
2372 289
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
2373
        }
2374 289
2375
        return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name->getQuotedName($this) . ' ('
2376
            . $this->getIndexFieldDeclarationListSQL($index)
2377
            . ')' . $this->getPartialIndexSQL($index);
2378
    }
2379
2380
    /**
2381
     * Obtains SQL code portion needed to create a custom column,
2382
     * e.g. when a field has the "columnDefinition" keyword.
2383 4142
     * Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate.
2384
     *
2385 4142
     * @param mixed[] $columnDef
2386 3636
     *
2387
     * @return string
2388
     */
2389 805
    public function getCustomTypeDeclarationSQL(array $columnDef)
2390
    {
2391
        return $columnDef['columnDefinition'];
2392
    }
2393 805
2394
    /**
2395 805
     * Obtains DBMS specific SQL code portion needed to set an index
2396 805
     * declaration to be used in statements like CREATE TABLE.
2397
     *
2398
     * @param mixed[]|Index $columnsOrIndex array declaration is deprecated, prefer passing Index to this method
2399 805
     */
2400
    public function getIndexFieldDeclarationListSQL($columnsOrIndex) : string
2401
    {
2402
        if ($columnsOrIndex instanceof Index) {
2403 805
            return implode(', ', $columnsOrIndex->getQuotedColumns($this));
2404
        }
2405
2406
        if (! is_array($columnsOrIndex)) {
0 ignored issues
show
introduced by
The condition is_array($columnsOrIndex) is always true.
Loading history...
2407
            throw new InvalidArgumentException('Fields argument should be an Index or array.');
2408
        }
2409
2410
        $ret = [];
2411
2412
        foreach ($columnsOrIndex as $column => $definition) {
2413
            if (is_array($definition)) {
2414
                $ret[] = $column;
2415
            } else {
2416
                $ret[] = $definition;
2417
            }
2418
        }
2419
2420
        return implode(', ', $ret);
2421
    }
2422
2423
    /**
2424
     * Returns the required SQL string that fits between CREATE ... TABLE
2425
     * to create the table as a temporary table.
2426
     *
2427
     * Should be overridden in driver classes to return the correct string for the
2428
     * specific database type.
2429
     *
2430
     * The default is to return the string "TEMPORARY" - this will result in a
2431
     * SQL error for any database that does not support temporary tables, or that
2432 36
     * requires a different SQL command from "CREATE TEMPORARY TABLE".
2433
     *
2434 36
     * @return string The string required to be placed between "CREATE" and "TABLE"
2435
     *                to generate a temporary table, if possible.
2436
     */
2437
    public function getTemporaryTableSQL()
2438
    {
2439
        return 'TEMPORARY';
2440
    }
2441
2442
    /**
2443
     * Some vendors require temporary table names to be qualified specially.
2444 1822
     *
2445
     * @param string $tableName
2446 1822
     *
2447 1753
     * @return string
2448
     */
2449 1753
    public function getTemporaryTableName($tableName)
2450
    {
2451
        return $tableName;
2452
    }
2453
2454
    /**
2455
     * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2456
     * of a field declaration to be used in statements like CREATE TABLE.
2457
     *
2458
     * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2459
     *                of a field declaration.
2460 1649
     */
2461
    public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey)
2462 1649
    {
2463 1649
        $sql  = $this->getForeignKeyBaseDeclarationSQL($foreignKey);
2464 23
        $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey);
2465
2466 1649
        return $sql;
2467 113
    }
2468
2469
    /**
2470 1649
     * Returns the FOREIGN KEY query section dealing with non-standard options
2471
     * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
2472
     *
2473
     * @param ForeignKeyConstraint $foreignKey The foreign key definition.
2474
     *
2475
     * @return string
2476
     */
2477
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
2478
    {
2479
        $query = '';
2480
        if ($this->supportsForeignKeyOnUpdate() && $foreignKey->hasOption('onUpdate')) {
2481
            $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate'));
2482 1815
        }
2483
        if ($foreignKey->hasOption('onDelete')) {
2484 1815
            $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
2485 1815
        }
2486 1815
2487 1225
        return $query;
2488 949
    }
2489 719
2490 512
    /**
2491 1562
     * Returns the given referential action in uppercase if valid, otherwise throws an exception.
2492
     *
2493 253
     * @param string $action The foreign key referential action.
2494
     *
2495
     * @return string
2496
     *
2497
     * @throws InvalidArgumentException If unknown referential action given.
2498
     */
2499
    public function getForeignKeyReferentialActionSQL($action)
2500
    {
2501
        $upper = strtoupper($action);
2502
        switch ($upper) {
2503
            case 'CASCADE':
2504
            case 'SET NULL':
2505 1592
            case 'NO ACTION':
2506
            case 'RESTRICT':
2507 1592
            case 'SET DEFAULT':
2508 1592
                return $upper;
2509 1339
            default:
2510
                throw new InvalidArgumentException('Invalid foreign key action: ' . $upper);
2511 1592
        }
2512
    }
2513 1592
2514
    /**
2515
     * Obtains DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2516 1592
     * of a field declaration to be used in statements like CREATE TABLE.
2517
     *
2518
     * @return string
2519 1592
     *
2520
     * @throws InvalidArgumentException
2521
     */
2522
    public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey)
2523 1592
    {
2524 1592
        $sql = '';
2525 1592
        if (strlen($foreignKey->getName())) {
2526 1592
            $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' ';
2527
        }
2528
        $sql .= 'FOREIGN KEY (';
2529
2530
        if (count($foreignKey->getLocalColumns()) === 0) {
2531
            throw new InvalidArgumentException("Incomplete definition. 'local' required.");
2532
        }
2533
        if (count($foreignKey->getForeignColumns()) === 0) {
2534
            throw new InvalidArgumentException("Incomplete definition. 'foreign' required.");
2535
        }
2536
        if (strlen($foreignKey->getForeignTableName()) === 0) {
2537
            throw new InvalidArgumentException("Incomplete definition. 'foreignTable' required.");
2538
        }
2539
2540
        return $sql . implode(', ', $foreignKey->getQuotedLocalColumns($this))
2541
            . ') REFERENCES '
2542
            . $foreignKey->getQuotedForeignTableName($this) . ' ('
2543
            . implode(', ', $foreignKey->getQuotedForeignColumns($this)) . ')';
2544
    }
2545
2546
    /**
2547
     * Obtains DBMS specific SQL code portion needed to set the UNIQUE constraint
2548
     * of a field declaration to be used in statements like CREATE TABLE.
2549
     *
2550
     * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint
2551
     *                of a field declaration.
2552
     */
2553
    public function getUniqueFieldDeclarationSQL()
2554
    {
2555
        return 'UNIQUE';
2556
    }
2557
2558
    /**
2559
     * Obtains DBMS specific SQL code portion needed to set the CHARACTER SET
2560
     * of a field declaration to be used in statements like CREATE TABLE.
2561
     *
2562
     * @param string $charset The name of the charset.
2563
     *
2564 291
     * @return string DBMS specific SQL code portion needed to set the CHARACTER SET
2565
     *                of a field declaration.
2566 291
     */
2567
    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

2567
    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...
2568
    {
2569
        return '';
2570
    }
2571
2572
    /**
2573
     * Obtains DBMS specific SQL code portion needed to set the COLLATION
2574
     * of a field declaration to be used in statements like CREATE TABLE.
2575 46
     *
2576
     * @param string $collation The name of the collation.
2577 46
     *
2578
     * @return string DBMS specific SQL code portion needed to set the COLLATION
2579
     *                of a field declaration.
2580
     */
2581
    public function getColumnCollationDeclarationSQL($collation)
2582
    {
2583
        return $this->supportsColumnCollation() ? 'COLLATE ' . $collation : '';
2584
    }
2585
2586 92
    /**
2587
     * Whether the platform prefers sequences for ID generation.
2588 92
     * Subclasses should override this method to return TRUE if they prefer sequences.
2589
     *
2590
     * @return bool
2591
     */
2592
    public function prefersSequences()
2593
    {
2594
        return false;
2595
    }
2596
2597
    /**
2598
     * Whether the platform prefers identity columns (eg. autoincrement) for ID generation.
2599
     * Subclasses should override this method to return TRUE if they prefer identity columns.
2600
     *
2601
     * @return bool
2602
     */
2603
    public function prefersIdentityColumns()
2604
    {
2605 352
        return false;
2606
    }
2607 352
2608
    /**
2609
     * Some platforms need the boolean values to be converted.
2610
     *
2611
     * The default conversion in this implementation converts to integers (false => 0, true => 1).
2612
     *
2613
     * Note: if the input is not a boolean the original input might be returned.
2614
     *
2615 352
     * There are two contexts when converting booleans: Literals and Prepared Statements.
2616 329
     * This method should handle the literal case
2617
     *
2618
     * @param mixed $item A boolean or an array of them.
2619 352
     *
2620
     * @return mixed A boolean database value or an array of them.
2621
     */
2622
    public function convertBooleans($item)
2623
    {
2624
        if (is_array($item)) {
2625
            foreach ($item as $k => $value) {
2626
                if (! is_bool($value)) {
2627
                    continue;
2628
                }
2629
2630
                $item[$k] = (int) $value;
2631 713
            }
2632
        } elseif (is_bool($item)) {
2633 713
            $item = (int) $item;
2634
        }
2635
2636
        return $item;
2637
    }
2638
2639
    /**
2640
     * Some platforms have boolean literals that needs to be correctly converted
2641
     *
2642
     * The default conversion tries to convert value into bool "(bool)$item"
2643
     *
2644
     * @param mixed $item
2645
     *
2646 126
     * @return bool|null
2647
     */
2648 126
    public function convertFromBoolean($item)
2649
    {
2650
        return $item === null ? null: (bool) $item;
2651
    }
2652
2653
    /**
2654
     * This method should handle the prepared statements case. When there is no
2655
     * distinction, it's OK to use the same method.
2656 231
     *
2657
     * Note: if the input is not a boolean the original input might be returned.
2658 231
     *
2659
     * @param mixed $item A boolean or an array of them.
2660
     *
2661
     * @return mixed A boolean database value or an array of them.
2662
     */
2663
    public function convertBooleansToDatabaseValue($item)
2664
    {
2665
        return $this->convertBooleans($item);
2666 12
    }
2667
2668 12
    /**
2669
     * Returns the SQL specific for the platform to get the current date.
2670
     *
2671
     * @return string
2672
     */
2673
    public function getCurrentDateSQL()
2674
    {
2675
        return 'CURRENT_DATE';
2676 314
    }
2677
2678 314
    /**
2679
     * Returns the SQL specific for the platform to get the current time.
2680
     *
2681
     * @return string
2682
     */
2683
    public function getCurrentTimeSQL()
2684
    {
2685
        return 'CURRENT_TIME';
2686
    }
2687
2688
    /**
2689
     * Returns the SQL specific for the platform to get the current timestamp
2690 184
     *
2691
     * @return string
2692 184
     */
2693
    public function getCurrentTimestampSQL()
2694 184
    {
2695
        return 'CURRENT_TIMESTAMP';
2696 184
    }
2697
2698 184
    /**
2699
     * Returns the SQL for a given transaction isolation level Connection constant.
2700 184
     *
2701
     * @param int $level
2702
     *
2703
     * @return string
2704
     *
2705
     * @throws InvalidArgumentException
2706
     */
2707
    protected function _getTransactionIsolationLevelSQL($level)
2708
    {
2709
        switch ($level) {
2710
            case TransactionIsolationLevel::READ_UNCOMMITTED:
2711 1
                return 'READ UNCOMMITTED';
2712
            case TransactionIsolationLevel::READ_COMMITTED:
2713 1
                return 'READ COMMITTED';
2714
            case TransactionIsolationLevel::REPEATABLE_READ:
2715
                return 'REPEATABLE READ';
2716
            case TransactionIsolationLevel::SERIALIZABLE:
2717
                return 'SERIALIZABLE';
2718
            default:
2719
                throw new InvalidArgumentException('Invalid isolation level:' . $level);
2720
        }
2721
    }
2722
2723
    /**
2724
     * @return string
2725
     *
2726
     * @throws DBALException If not supported on this platform.
2727
     */
2728
    public function getListDatabasesSQL()
2729
    {
2730
        throw DBALException::notSupported(__METHOD__);
2731
    }
2732
2733
    /**
2734
     * Returns the SQL statement for retrieving the namespaces defined in the database.
2735
     *
2736
     * @return string
2737
     *
2738
     * @throws DBALException If not supported on this platform.
2739
     */
2740
    public function getListNamespacesSQL()
2741
    {
2742
        throw DBALException::notSupported(__METHOD__);
2743
    }
2744
2745
    /**
2746
     * @param string $database
2747
     *
2748
     * @return string
2749
     *
2750
     * @throws DBALException If not supported on this platform.
2751
     */
2752
    public function getListSequencesSQL($database)
2753
    {
2754
        throw DBALException::notSupported(__METHOD__);
2755
    }
2756
2757
    /**
2758
     * @param string $table
2759
     *
2760
     * @return string
2761
     *
2762
     * @throws DBALException If not supported on this platform.
2763
     */
2764
    public function getListTableConstraintsSQL($table)
2765
    {
2766
        throw DBALException::notSupported(__METHOD__);
2767
    }
2768
2769
    /**
2770
     * @param string      $table
2771
     * @param string|null $database
2772
     *
2773
     * @return string
2774
     *
2775
     * @throws DBALException If not supported on this platform.
2776
     */
2777
    public function getListTableColumnsSQL($table, $database = null)
2778
    {
2779
        throw DBALException::notSupported(__METHOD__);
2780
    }
2781
2782
    /**
2783
     * @return string
2784
     *
2785
     * @throws DBALException If not supported on this platform.
2786
     */
2787
    public function getListTablesSQL()
2788
    {
2789
        throw DBALException::notSupported(__METHOD__);
2790
    }
2791
2792
    /**
2793
     * @return string
2794
     *
2795
     * @throws DBALException If not supported on this platform.
2796
     */
2797
    public function getListUsersSQL()
2798
    {
2799
        throw DBALException::notSupported(__METHOD__);
2800
    }
2801
2802
    /**
2803
     * Returns the SQL to list all views of a database or user.
2804
     *
2805
     * @param string $database
2806
     *
2807
     * @return string
2808
     *
2809
     * @throws DBALException If not supported on this platform.
2810
     */
2811
    public function getListViewsSQL($database)
2812
    {
2813
        throw DBALException::notSupported(__METHOD__);
2814
    }
2815
2816
    /**
2817
     * Returns the list of indexes for the current database.
2818
     *
2819
     * The current database parameter is optional but will always be passed
2820
     * when using the SchemaManager API and is the database the given table is in.
2821
     *
2822
     * Attention: Some platforms only support currentDatabase when they
2823
     * are connected with that database. Cross-database information schema
2824
     * requests may be impossible.
2825
     *
2826
     * @param string $table
2827
     * @param string $currentDatabase
2828
     *
2829
     * @return string
2830
     *
2831
     * @throws DBALException If not supported on this platform.
2832
     */
2833
    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

2833
    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...
2834
    {
2835
        throw DBALException::notSupported(__METHOD__);
2836
    }
2837
2838
    /**
2839
     * @param string $table
2840
     *
2841
     * @return string
2842
     *
2843
     * @throws DBALException If not supported on this platform.
2844
     */
2845
    public function getListTableForeignKeysSQL($table)
2846
    {
2847
        throw DBALException::notSupported(__METHOD__);
2848
    }
2849
2850
    /**
2851
     * @param string $name
2852
     * @param string $sql
2853
     *
2854
     * @return string
2855
     *
2856
     * @throws DBALException If not supported on this platform.
2857
     */
2858
    public function getCreateViewSQL($name, $sql)
2859
    {
2860
        throw DBALException::notSupported(__METHOD__);
2861
    }
2862
2863
    /**
2864
     * @param string $name
2865
     *
2866
     * @return string
2867
     *
2868
     * @throws DBALException If not supported on this platform.
2869
     */
2870
    public function getDropViewSQL($name)
2871
    {
2872
        throw DBALException::notSupported(__METHOD__);
2873
    }
2874
2875
    /**
2876
     * Returns the SQL snippet to drop an existing sequence.
2877
     *
2878
     * @param Sequence|string $sequence
2879
     *
2880
     * @return string
2881
     *
2882
     * @throws DBALException If not supported on this platform.
2883
     */
2884
    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

2884
    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...
2885
    {
2886
        throw DBALException::notSupported(__METHOD__);
2887
    }
2888
2889
    /**
2890
     * @param string $sequenceName
2891
     *
2892
     * @return string
2893 23
     *
2894
     * @throws DBALException If not supported on this platform.
2895 23
     */
2896
    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

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

3464
    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...
3465
    {
3466
        $tableIdentifier = new Identifier($tableName);
3467
3468
        return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this);
3469 20
    }
3470
3471 20
    /**
3472
     * This is for test reasons, many vendors have special requirements for dummy statements.
3473
     */
3474
    public function getDummySelectSQL(string $expression = '1') : string
3475
    {
3476
        return sprintf('SELECT %s', $expression);
3477
    }
3478
3479
    /**
3480
     * Returns the SQL to create a new savepoint.
3481 19
     *
3482
     * @param string $savepoint
3483 19
     *
3484
     * @return string
3485
     */
3486
    public function createSavePoint($savepoint)
3487
    {
3488
        return 'SAVEPOINT ' . $savepoint;
3489
    }
3490
3491
    /**
3492
     * Returns the SQL to release a savepoint.
3493 20
     *
3494
     * @param string $savepoint
3495 20
     *
3496
     * @return string
3497
     */
3498
    public function releaseSavePoint($savepoint)
3499
    {
3500
        return 'RELEASE SAVEPOINT ' . $savepoint;
3501
    }
3502
3503
    /**
3504
     * Returns the SQL to rollback a savepoint.
3505 18683
     *
3506
     * @param string $savepoint
3507
     *
3508 18683
     * @return string
3509 17349
     */
3510
    public function rollbackSavePoint($savepoint)
3511
    {
3512 15060
        return 'ROLLBACK TO SAVEPOINT ' . $savepoint;
3513 15060
    }
3514 15060
3515
    /**
3516
     * Returns the keyword list instance of this platform.
3517
     *
3518
     * @return KeywordList
3519 15060
     *
3520
     * @throws DBALException If no keyword list is specified.
3521 15060
     */
3522
    final public function getReservedKeywordsList()
3523
    {
3524
        // Check for an existing instantiation of the keywords class.
3525
        if ($this->_keywords) {
3526
            return $this->_keywords;
3527
        }
3528
3529
        $class    = $this->getReservedKeywordsClass();
3530
        $keywords = new $class();
3531
        if (! $keywords instanceof KeywordList) {
3532
            throw DBALException::notSupported(__METHOD__);
3533
        }
3534
3535
        // Store the instance so it doesn't need to be generated on every request.
3536
        $this->_keywords = $keywords;
3537
3538
        return $keywords;
3539
    }
3540
3541
    /**
3542
     * Returns the class name of the reserved keywords list.
3543
     *
3544
     * @return string
3545
     *
3546 6544
     * @throws DBALException If not supported on this platform.
3547
     */
3548 6544
    protected function getReservedKeywordsClass()
3549
    {
3550 6544
        throw DBALException::notSupported(__METHOD__);
3551
    }
3552
3553
    /**
3554
     * Quotes a literal string.
3555
     * This method is NOT meant to fix SQL injections!
3556
     * It is only meant to escape this platform's string literal
3557
     * quote character inside the given literal string.
3558 6820
     *
3559
     * @param string $str The literal string to be quoted.
3560 6820
     *
3561
     * @return string The quoted literal string.
3562
     */
3563
    public function quoteStringLiteral($str)
3564
    {
3565
        $c = $this->getStringLiteralQuoteCharacter();
3566
3567
        return $c . str_replace($c, $c . $c, $str) . $c;
3568
    }
3569
3570
    /**
3571 299
     * Gets the character used for string literal quoting.
3572
     *
3573 299
     * @return string
3574 299
     */
3575 299
    public function getStringLiteralQuoteCharacter()
3576 299
    {
3577
        return "'";
3578
    }
3579
3580 299
    /**
3581
     * Escapes metacharacters in a string intended to be used with a LIKE
3582 299
     * operator.
3583
     *
3584
     * @param string $inputString a literal, unquoted string
3585
     * @param string $escapeChar  should be reused by the caller in the LIKE
3586
     *                            expression.
3587
     */
3588
    final public function escapeStringForLike(string $inputString, string $escapeChar) : string
3589
    {
3590
        return preg_replace(
3591
            '~([' . preg_quote($this->getLikeWildcardCharacters() . $escapeChar, '~') . '])~u',
3592
            addcslashes($escapeChar, '\\') . '$1',
3593
            $inputString
3594
        );
3595
    }
3596
3597
    protected function getLikeWildcardCharacters() : string
3598
    {
3599
        return '%_';
3600
    }
3601
}
3602