Failed Conditions
Pull Request — master (#2412)
by Benoît
65:39
created

AbstractPlatform::supportsColumnLengthIndexes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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

341
    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...
342
    {
343
        throw DBALException::notSupported('VARCHARs not supported by Platform.');
344
    }
345
346
    /**
347
     * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
348
     *
349
     * @param int  $length The length of the column.
350
     * @param bool $fixed  Whether the column length is fixed.
351
     *
352
     * @return string
353
     *
354
     * @throws DBALException If not supported on this platform.
355
     */
356
    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

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

804
            $expression .= /** @scrutinizer ignore-type */ $char . ' ';
Loading history...
805
        }
806
807 576
        if ($mode || $char !== false) {
808 544
            $expression .= 'FROM ';
809
        }
810
811 576
        return 'TRIM(' . $expression . $str . ')';
812
    }
813
814
    /**
815
     * Returns the SQL snippet to trim trailing space characters from the expression.
816
     *
817
     * @param string $str Literal string or column name.
818
     *
819
     * @return string
820
     */
821 76
    public function getRtrimExpression($str)
822
    {
823 76
        return 'RTRIM(' . $str . ')';
824
    }
825
826
    /**
827
     * Returns the SQL snippet to trim leading space characters from the expression.
828
     *
829
     * @param string $str Literal string or column name.
830
     *
831
     * @return string
832
     */
833 76
    public function getLtrimExpression($str)
834
    {
835 76
        return 'LTRIM(' . $str . ')';
836
    }
837
838
    /**
839
     * Returns the SQL snippet to change all characters from the expression to uppercase,
840
     * according to the current character set mapping.
841
     *
842
     * @param string $str Literal string or column name.
843
     *
844
     * @return string
845
     */
846
    public function getUpperExpression($str)
847
    {
848
        return 'UPPER(' . $str . ')';
849
    }
850
851
    /**
852
     * Returns the SQL snippet to change all characters from the expression to lowercase,
853
     * according to the current character set mapping.
854
     *
855
     * @param string $str Literal string or column name.
856
     *
857
     * @return string
858
     */
859
    public function getLowerExpression($str)
860
    {
861
        return 'LOWER(' . $str . ')';
862
    }
863
864
    /**
865
     * Returns the SQL snippet to get the position of the first occurrence of substring $substr in string $str.
866
     *
867
     * @param string   $str      Literal string.
868
     * @param string   $substr   Literal string to find.
869
     * @param int|bool $startPos Position to start at, beginning of string by default.
870
     *
871
     * @return string
872
     *
873
     * @throws DBALException If not supported on this platform.
874
     */
875
    public function getLocateExpression($str, $substr, $startPos = false)
0 ignored issues
show
Unused Code introduced by
The parameter $startPos is not used and could be removed. ( Ignorable by Annotation )

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

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

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

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

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

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

1046
    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

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

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

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

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

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

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

1304
    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

1304
    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...
1305
    {
1306
        throw DBALException::notSupported(__METHOD__);
1307
    }
1308
1309
    /**
1310
     * Returns the SQL bit AND comparison expression.
1311
     *
1312
     * @param string $value1
1313
     * @param string $value2
1314
     *
1315
     * @return string
1316
     */
1317 321
    public function getBitAndComparisonExpression($value1, $value2)
1318
    {
1319 321
        return '(' . $value1 . ' & ' . $value2 . ')';
1320
    }
1321
1322
    /**
1323
     * Returns the SQL bit OR comparison expression.
1324
     *
1325
     * @param string $value1
1326
     * @param string $value2
1327
     *
1328
     * @return string
1329
     */
1330 321
    public function getBitOrComparisonExpression($value1, $value2)
1331
    {
1332 321
        return '(' . $value1 . ' | ' . $value2 . ')';
1333
    }
1334
1335
    /**
1336
     * Returns the FOR UPDATE expression.
1337
     *
1338
     * @return string
1339
     */
1340 30
    public function getForUpdateSQL()
1341
    {
1342 30
        return 'FOR UPDATE';
1343
    }
1344
1345
    /**
1346
     * Honors that some SQL vendors such as MsSql use table hints for locking instead of the ANSI SQL FOR UPDATE specification.
1347
     *
1348
     * @param string   $fromClause The FROM clause to append the hint for the given lock mode to.
1349
     * @param int|null $lockMode   One of the Doctrine\DBAL\LockMode::* constants. If null is given, nothing will
1350
     *                             be appended to the FROM clause.
1351
     *
1352
     * @return string
1353
     */
1354 32
    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

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

1716
    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...
1717
    {
1718
        throw DBALException::notSupported(__METHOD__);
1719
    }
1720
1721
    /**
1722
     * Returns the SQL to change a sequence on this platform.
1723
     *
1724
     * @return string
1725
     *
1726
     * @throws DBALException If not supported on this platform.
1727
     */
1728
    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

1728
    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...
1729
    {
1730
        throw DBALException::notSupported(__METHOD__);
1731
    }
1732
1733
    /**
1734
     * Returns the SQL to create a constraint on a table on this platform.
1735
     *
1736
     * @param Table|string $table
1737
     *
1738
     * @return string
1739
     *
1740 328
     * @throws InvalidArgumentException
1741
     */
1742 328
    public function getCreateConstraintSQL(Constraint $constraint, $table)
1743
    {
1744
        if ($table instanceof Table) {
1745
            $table = $table->getQuotedName($this);
1746 328
        }
1747
1748 328
        $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this);
1749
1750 328
        $columnList = '(' . implode(', ', $constraint->getQuotedColumns($this)) . ')';
1751 328
1752 328
        $referencesClause = '';
1753 328
        if ($constraint instanceof Index) {
1754 247
            if ($constraint->isPrimary()) {
1755 247
                $query .= ' PRIMARY KEY';
1756
            } elseif ($constraint->isUnique()) {
1757
                $query .= ' UNIQUE';
1758 328
            } else {
1759
                throw new InvalidArgumentException(
1760
                    'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().'
1761 247
                );
1762 247
            }
1763
        } elseif ($constraint instanceof ForeignKeyConstraint) {
1764 247
            $query .= ' FOREIGN KEY';
1765 247
1766
            $referencesClause = ' REFERENCES ' . $constraint->getQuotedForeignTableName($this) .
1767 328
                ' (' . implode(', ', $constraint->getQuotedForeignColumns($this)) . ')';
1768
        }
1769 328
        $query .= ' ' . $columnList . $referencesClause;
1770
1771
        return $query;
1772
    }
1773
1774
    /**
1775
     * Returns the SQL to create an index on a table on this platform.
1776
     *
1777
     * @param Table|string $table The name of the table on which the index is to be created.
1778
     *
1779
     * @return string
1780
     *
1781 3034
     * @throws InvalidArgumentException
1782
     */
1783 3034
    public function getCreateIndexSQL(Index $index, $table)
1784 19
    {
1785
        if ($table instanceof Table) {
1786 3034
            $table = $table->getQuotedName($this);
1787 3034
        }
1788
        $name    = $index->getQuotedName($this);
1789 3034
        $columns = $index->getColumns();
1790
1791
        if (count($columns) === 0) {
1792
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
1793 3034
        }
1794 399
1795
        if ($index->isPrimary()) {
1796
            return $this->getCreatePrimaryKeySQL($index, $table);
1797 2673
        }
1798 2673
1799
        $query  = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table;
1800 2673
        $query .= ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')' . $this->getPartialIndexSQL($index);
1801
1802
        return $query;
1803
    }
1804
1805
    /**
1806
     * Adds condition for partial index.
1807
     *
1808 3672
     * @return string
1809
     */
1810 3672
    protected function getPartialIndexSQL(Index $index)
1811 101
    {
1812
        if ($this->supportsPartialIndexes() && $index->hasOption('where')) {
1813
            return ' WHERE ' . $index->getOption('where');
1814 3571
        }
1815
1816
        return '';
1817
    }
1818
1819
    /**
1820
     * Adds additional flags for index generation.
1821
     *
1822 1314
     * @return string
1823
     */
1824 1314
    protected function getCreateIndexSQLFlags(Index $index)
1825
    {
1826
        return $index->isUnique() ? 'UNIQUE ' : '';
1827
    }
1828
1829
    /**
1830
     * Returns the SQL to create an unnamed primary key constraint.
1831
     *
1832
     * @param Table|string $table
1833
     *
1834 323
     * @return string
1835
     */
1836 323
    public function getCreatePrimaryKeySQL(Index $index, $table)
1837
    {
1838
        return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index) . ')';
0 ignored issues
show
Bug introduced by
Are you sure $table of type Doctrine\DBAL\Schema\Table|string can be used in concatenation? ( Ignorable by Annotation )

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

1838
        return 'ALTER TABLE ' . /** @scrutinizer ignore-type */ $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index) . ')';
Loading history...
1839
    }
1840
1841
    /**
1842
     * Returns the SQL to create a named schema.
1843
     *
1844
     * @param string $schemaName
1845
     *
1846
     * @return string
1847
     *
1848 190
     * @throws DBALException If not supported on this platform.
1849
     */
1850 190
    public function getCreateSchemaSQL($schemaName)
0 ignored issues
show
Unused Code introduced by
The parameter $schemaName is not used and could be removed. ( Ignorable by Annotation )

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

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

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

Loading history...
1851
    {
1852
        throw DBALException::notSupported(__METHOD__);
1853
    }
1854
1855
    /**
1856
     * Quotes a string so that it can be safely used as a table or column name,
1857
     * even if it is a reserved word of the platform. This also detects identifier
1858
     * chains separated by dot and quotes them independently.
1859
     *
1860
     * NOTE: Just because you CAN use quoted identifiers doesn't mean
1861
     * you SHOULD use them. In general, they end up causing way more
1862
     * problems than they solve.
1863
     *
1864
     * @param string $str The identifier name to be quoted.
1865
     *
1866 7570
     * @return string The quoted identifier string.
1867
     */
1868 7570
    public function quoteIdentifier($str)
1869 372
    {
1870
        if (strpos($str, '.') !== false) {
1871 372
            $parts = array_map([$this, 'quoteSingleIdentifier'], explode('.', $str));
1872
1873
            return implode('.', $parts);
1874 7570
        }
1875
1876
        return $this->quoteSingleIdentifier($str);
1877
    }
1878
1879
    /**
1880
     * Quotes a single identifier (no dot chain separation).
1881
     *
1882
     * @param string $str The identifier name to be quoted.
1883
     *
1884 7030
     * @return string The quoted identifier string.
1885
     */
1886 7030
    public function quoteSingleIdentifier($str)
1887
    {
1888 7030
        $c = $this->getIdentifierQuoteCharacter();
1889
1890
        return $c . str_replace($c, $c . $c, $str) . $c;
1891
    }
1892
1893
    /**
1894
     * Returns the SQL to create a new foreign key.
1895
     *
1896
     * @param ForeignKeyConstraint $foreignKey The foreign key constraint.
1897
     * @param Table|string         $table      The name of the table on which the foreign key is to be created.
1898
     *
1899 1695
     * @return string
1900
     */
1901 1695
    public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table)
1902 19
    {
1903
        if ($table instanceof Table) {
1904
            $table = $table->getQuotedName($this);
1905 1695
        }
1906
1907
        return 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey);
1908
    }
1909
1910
    /**
1911
     * Gets the SQL statements for altering an existing table.
1912
     *
1913
     * This method returns an array of SQL statements, since some platforms need several statements.
1914
     *
1915
     * @return string[]
1916
     *
1917
     * @throws DBALException If not supported on this platform.
1918
     */
1919
    public function getAlterTableSQL(TableDiff $diff)
1920
    {
1921
        throw DBALException::notSupported(__METHOD__);
1922
    }
1923
1924
    /**
1925
     * @param mixed[] $columnSql
1926
     *
1927 1885
     * @return bool
1928
     */
1929 1885
    protected function onSchemaAlterTableAddColumn(Column $column, TableDiff $diff, &$columnSql)
1930 1520
    {
1931
        if ($this->_eventManager === null) {
1932
            return false;
1933 365
        }
1934 23
1935
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableAddColumn)) {
1936
            return false;
1937 342
        }
1938 342
1939
        $eventArgs = new SchemaAlterTableAddColumnEventArgs($column, $diff, $this);
1940 342
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn, $eventArgs);
1941
1942 342
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1943
1944
        return $eventArgs->isDefaultPrevented();
1945
    }
1946
1947
    /**
1948
     * @param string[] $columnSql
1949
     *
1950 1450
     * @return bool
1951
     */
1952 1450
    protected function onSchemaAlterTableRemoveColumn(Column $column, TableDiff $diff, &$columnSql)
1953 1083
    {
1954
        if ($this->_eventManager === null) {
1955
            return false;
1956 367
        }
1957 25
1958
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRemoveColumn)) {
1959
            return false;
1960 342
        }
1961 342
1962
        $eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column, $diff, $this);
1963 342
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn, $eventArgs);
1964
1965 342
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1966
1967
        return $eventArgs->isDefaultPrevented();
1968
    }
1969
1970
    /**
1971
     * @param string[] $columnSql
1972
     *
1973 3664
     * @return bool
1974
     */
1975 3664
    protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiff, TableDiff $diff, &$columnSql)
1976 3059
    {
1977
        if ($this->_eventManager === null) {
1978
            return false;
1979 605
        }
1980 263
1981
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) {
1982
            return false;
1983 342
        }
1984 342
1985
        $eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff, $diff, $this);
1986 342
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn, $eventArgs);
1987
1988 342
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1989
1990
        return $eventArgs->isDefaultPrevented();
1991
    }
1992
1993
    /**
1994
     * @param string   $oldColumnName
1995
     * @param string[] $columnSql
1996
     *
1997 1466
     * @return bool
1998
     */
1999 1466
    protected function onSchemaAlterTableRenameColumn($oldColumnName, Column $column, TableDiff $diff, &$columnSql)
2000 1102
    {
2001
        if ($this->_eventManager === null) {
2002
            return false;
2003 364
        }
2004 22
2005
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRenameColumn)) {
2006
            return false;
2007 342
        }
2008 342
2009
        $eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName, $column, $diff, $this);
2010 342
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn, $eventArgs);
2011
2012 342
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
2013
2014
        return $eventArgs->isDefaultPrevented();
2015
    }
2016
2017
    /**
2018
     * @param string[] $sql
2019
     *
2020 6963
     * @return bool
2021
     */
2022 6963
    protected function onSchemaAlterTable(TableDiff $diff, &$sql)
2023 6270
    {
2024
        if ($this->_eventManager === null) {
2025
            return false;
2026 693
        }
2027 351
2028
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTable)) {
2029
            return false;
2030 342
        }
2031 342
2032
        $eventArgs = new SchemaAlterTableEventArgs($diff, $this);
2033 342
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTable, $eventArgs);
2034
2035 342
        $sql = array_merge($sql, $eventArgs->getSql());
2036
2037
        return $eventArgs->isDefaultPrevented();
2038
    }
2039
2040
    /**
2041 6697
     * @return string[]
2042
     */
2043 6697
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
2044
    {
2045 6697
        $tableName = $diff->getName($this)->getQuotedName($this);
2046 6697
2047 6697
        $sql = [];
2048 493
        if ($this->supportsForeignKeyConstraints()) {
2049
            foreach ($diff->removedForeignKeys as $foreignKey) {
2050 6697
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
2051 380
            }
2052
            foreach ($diff->changedForeignKeys as $foreignKey) {
2053
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
2054
            }
2055 6697
        }
2056 243
2057
        foreach ($diff->removedIndexes as $index) {
2058 6697
            $sql[] = $this->getDropIndexSQL($index, $tableName);
2059 340
        }
2060
        foreach ($diff->changedIndexes as $index) {
2061
            $sql[] = $this->getDropIndexSQL($index, $tableName);
2062 6697
        }
2063
2064
        return $sql;
2065
    }
2066
2067
    /**
2068 6697
     * @return string[]
2069
     */
2070 6697
    protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff)
2071 649
    {
2072 6697
        $tableName = $diff->newName !== false
2073
            ? $diff->getNewName()->getQuotedName($this)
2074 6697
            : $diff->getName($this)->getQuotedName($this);
2075
2076 6697
        $sql = [];
2077 6697
2078 416
        if ($this->supportsForeignKeyConstraints()) {
2079
            foreach ($diff->addedForeignKeys as $foreignKey) {
2080
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
2081 6697
            }
2082 380
2083
            foreach ($diff->changedForeignKeys as $foreignKey) {
2084
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
2085
            }
2086 6697
        }
2087 75
2088
        foreach ($diff->addedIndexes as $index) {
2089
            $sql[] = $this->getCreateIndexSQL($index, $tableName);
2090 6697
        }
2091 340
2092
        foreach ($diff->changedIndexes as $index) {
2093
            $sql[] = $this->getCreateIndexSQL($index, $tableName);
2094 6697
        }
2095 1651
2096 1651
        foreach ($diff->renamedIndexes as $oldIndexName => $index) {
2097 1651
            $oldIndexName = new Identifier($oldIndexName);
2098 1651
            $sql          = array_merge(
2099
                $sql,
2100
                $this->getRenameIndexSQL($oldIndexName->getQuotedName($this), $index, $tableName)
2101
            );
2102 6697
        }
2103
2104
        return $sql;
2105
    }
2106
2107
    /**
2108
     * Returns the SQL for renaming an index on a table.
2109
     *
2110
     * @param string $oldIndexName The name of the index to rename from.
2111
     * @param Index  $index        The definition of the index to rename to.
2112
     * @param string $tableName    The table to rename the given index on.
2113
     *
2114 202
     * @return string[] The sequence of SQL statements for renaming the given index.
2115
     */
2116
    protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
2117 202
    {
2118 202
        return [
2119
            $this->getDropIndexSQL($oldIndexName, $tableName),
2120
            $this->getCreateIndexSQL($index, $tableName),
2121
        ];
2122
    }
2123
2124
    /**
2125
     * Common code for alter table statement generation that updates the changed Index and Foreign Key definitions.
2126
     *
2127
     * @return string[]
2128
     */
2129
    protected function _getAlterTableIndexForeignKeySQL(TableDiff $diff)
2130
    {
2131
        return array_merge($this->getPreAlterTableIndexForeignKeySQL($diff), $this->getPostAlterTableIndexForeignKeySQL($diff));
2132
    }
2133
2134
    /**
2135
     * Gets declaration of a number of fields in bulk.
2136
     *
2137
     * @param mixed[][] $fields A multidimensional associative array.
2138
     *                          The first dimension determines the field name, while the second
2139
     *                          dimension is keyed with the name of the properties
2140
     *                          of the field being declared as array indexes. Currently, the types
2141
     *                          of supported field properties are as follows:
2142
     *
2143
     *      length
2144
     *          Integer value that determines the maximum length of the text
2145
     *          field. If this argument is missing the field should be
2146
     *          declared to have the longest length allowed by the DBMS.
2147
     *
2148
     *      default
2149
     *          Text value to be used as default for this field.
2150
     *
2151
     *      notnull
2152
     *          Boolean flag that indicates whether this field is constrained
2153
     *          to not be set to null.
2154
     *      charset
2155
     *          Text value with the default CHARACTER SET for this field.
2156
     *      collation
2157
     *          Text value with the default COLLATION for this field.
2158
     *      unique
2159
     *          unique constraint
2160
     *
2161 7927
     * @return string
2162
     */
2163 7927
    public function getColumnDeclarationListSQL(array $fields)
2164
    {
2165 7927
        $queryFields = [];
2166 7927
2167
        foreach ($fields as $fieldName => $field) {
2168
            $queryFields[] = $this->getColumnDeclarationSQL($fieldName, $field);
2169 7927
        }
2170
2171
        return implode(', ', $queryFields);
2172
    }
2173
2174
    /**
2175
     * Obtains DBMS specific SQL code portion needed to declare a generic type
2176
     * field to be used in statements like CREATE TABLE.
2177
     *
2178
     * @param string  $name  The name the field to be declared.
2179
     * @param mixed[] $field An associative array with the name of the properties
2180
     *                       of the field being declared as array indexes. Currently, the types
2181
     *                       of supported field properties are as follows:
2182
     *
2183
     *      length
2184
     *          Integer value that determines the maximum length of the text
2185
     *          field. If this argument is missing the field should be
2186
     *          declared to have the longest length allowed by the DBMS.
2187
     *
2188
     *      default
2189
     *          Text value to be used as default for this field.
2190
     *
2191
     *      notnull
2192
     *          Boolean flag that indicates whether this field is constrained
2193
     *          to not be set to null.
2194
     *      charset
2195
     *          Text value with the default CHARACTER SET for this field.
2196
     *      collation
2197
     *          Text value with the default COLLATION for this field.
2198
     *      unique
2199
     *          unique constraint
2200
     *      check
2201
     *          column check constraint
2202
     *      columnDefinition
2203
     *          a string that defines the complete column
2204
     *
2205 8387
     * @return string DBMS specific SQL code portion that should be used to declare the column.
2206
     */
2207 8387
    public function getColumnDeclarationSQL($name, array $field)
2208 275
    {
2209
        if (isset($field['columnDefinition'])) {
2210 8121
            $columnDef = $this->getCustomTypeDeclarationSQL($field);
2211
        } else {
2212 8121
            $default = $this->getDefaultValueDeclarationSQL($field);
2213 8121
2214
            $charset = isset($field['charset']) && $field['charset'] ?
2215 8121
                ' ' . $this->getColumnCharsetDeclarationSQL($field['charset']) : '';
2216 8121
2217
            $collation = isset($field['collation']) && $field['collation'] ?
2218 8121
                ' ' . $this->getColumnCollationDeclarationSQL($field['collation']) : '';
2219
2220 8121
            $notnull = isset($field['notnull']) && $field['notnull'] ? ' NOT NULL' : '';
2221 8121
2222
            $unique = isset($field['unique']) && $field['unique'] ?
2223 8121
                ' ' . $this->getUniqueFieldDeclarationSQL() : '';
2224 8121
2225
            $check = isset($field['check']) && $field['check'] ?
2226 8121
                ' ' . $field['check'] : '';
2227 8121
2228
            $typeDecl  = $field['type']->getSQLDeclaration($field, $this);
2229 8121
            $columnDef = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation;
2230 699
2231
            if ($this->supportsInlineColumnComments() && isset($field['comment']) && $field['comment'] !== '') {
2232
                $columnDef .= ' ' . $this->getInlineColumnCommentSQL($field['comment']);
2233
            }
2234 8387
        }
2235
2236
        return $name . ' ' . $columnDef;
2237
    }
2238
2239
    /**
2240
     * Returns the SQL snippet that declares a floating point column of arbitrary precision.
2241
     *
2242
     * @param mixed[] $columnDef
2243
     *
2244 2551
     * @return string
2245
     */
2246 2551
    public function getDecimalTypeDeclarationSQL(array $columnDef)
2247 2551
    {
2248 2551
        $columnDef['precision'] = ! isset($columnDef['precision']) || empty($columnDef['precision'])
2249 2551
            ? 10 : $columnDef['precision'];
2250
        $columnDef['scale']     = ! isset($columnDef['scale']) || empty($columnDef['scale'])
2251 2551
            ? 0 : $columnDef['scale'];
2252
2253
        return 'NUMERIC(' . $columnDef['precision'] . ', ' . $columnDef['scale'] . ')';
2254
    }
2255
2256
    /**
2257
     * Obtains DBMS specific SQL code portion needed to set a default value
2258
     * declaration to be used in statements like CREATE TABLE.
2259
     *
2260
     * @param mixed[] $field The field definition array.
2261
     *
2262 9224
     * @return string DBMS specific SQL code portion needed to set a default value.
2263
     */
2264 9224
    public function getDefaultValueDeclarationSQL($field)
2265 7947
    {
2266
        if (! isset($field['default'])) {
2267
            return empty($field['notnull']) ? ' DEFAULT NULL' : '';
2268 1720
        }
2269
2270 1720
        $default = $field['default'];
2271
2272
        if (! isset($field['type'])) {
2273
            return " DEFAULT '" . $default . "'";
2274 1720
        }
2275
2276 1720
        $type = $field['type'];
2277 469
2278
        if ($type instanceof Types\PhpIntegerMappingType) {
2279
            return ' DEFAULT ' . $default;
2280 1299
        }
2281 303
2282
        if ($type instanceof Types\PhpDateTimeMappingType && $default === $this->getCurrentTimestampSQL()) {
2283
            return ' DEFAULT ' . $this->getCurrentTimestampSQL();
2284 1006
        }
2285 2
2286
        if ($type instanceof Types\TimeType && $default === $this->getCurrentTimeSQL()) {
2287
            return ' DEFAULT ' . $this->getCurrentTimeSQL();
2288 1006
        }
2289 287
2290
        if ($type instanceof Types\DateType && $default === $this->getCurrentDateSQL()) {
2291
            return ' DEFAULT ' . $this->getCurrentDateSQL();
2292 719
        }
2293 291
2294
        if ($type instanceof Types\BooleanType) {
2295
            return " DEFAULT '" . $this->convertBooleans($default) . "'";
0 ignored issues
show
Bug introduced by
Are you sure $this->convertBooleans($default) of type integer|array|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

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

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

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

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

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