Failed Conditions
Pull Request — develop (#3518)
by Michael
29:00 queued 25:29
created

AbstractPlatform::supportsReleaseSavepoints()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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

267
    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...
268
    {
269
        throw DBALException::notSupported('VARCHARs not supported by Platform.');
270
    }
271
272
    /**
273
     * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
274
     *
275
     * @param int  $length The length of the column.
276
     * @param bool $fixed  Whether the column length is fixed.
277
     *
278
     * @return string
279
     *
280
     * @throws DBALException If not supported on this platform.
281
     */
282
    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

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

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

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

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

Loading history...
774
    {
775
        throw DBALException::notSupported(__METHOD__);
776
    }
777
778
    /**
779
     * Returns the SQL snippet to get the current system date.
780
     */
781
    public function getNowExpression() : string
782
    {
783
        return 'NOW()';
784
    }
785
786
    /**
787
     * Returns an SQL snippet to get a substring inside the string.
788
     *
789
     * Note: Not SQL92, but common functionality.
790
     *
791
     * @param string      $string SQL expression producing the string from which a substring should be extracted.
792
     * @param string      $start  SQL expression producing the position to start at,
793
     * @param string|null $length SQL expression producing the length of the substring portion to be returned.
794
     *                            By default, the entire substring is returned.
795
     */
796 48516
    public function getSubstringExpression(string $string, string $start, ?string $length = null) : string
797
    {
798 48516
        if ($length === null) {
799 48516
            return sprintf('SUBSTRING(%s FROM %s)', $string, $start);
800
        }
801
802 48490
        return sprintf('SUBSTRING(%s FROM %s FOR %s)', $string, $start, $length);
803
    }
804
805
    /**
806
     * Returns a SQL snippet to concatenate the given strings.
807
     *
808
     * @param string[] ...$string
809
     */
810 34970
    public function getConcatExpression(string ...$string) : string
811
    {
812 34970
        return implode(' || ', $string);
813
    }
814
815
    /**
816
     * Returns the SQL for a logical not.
817
     *
818
     * @param string $value SQL expression producing the value to negate.
819
     */
820
    public function getNotExpression(string $value) : string
821
    {
822
        return 'NOT(' . $value . ')';
823
    }
824
825
    /**
826
     * Returns the SQL that checks if an expression is null.
827
     *
828
     * @param string $value SQL expression producing the to be compared to null.
829
     */
830 56171
    public function getIsNullExpression(string $value) : string
831
    {
832 56171
        return $value . ' IS NULL';
833
    }
834
835
    /**
836
     * Returns the SQL that checks if an expression is not null.
837
     *
838
     * @param string $value SQL expression producing the to be compared to null.
839
     */
840
    public function getIsNotNullExpression(string $value) : string
841
    {
842
        return $value . ' IS NOT NULL';
843
    }
844
845
    /**
846
     * Returns the SQL that checks if an expression evaluates to a value between two values.
847
     *
848
     * The parameter $value is checked if it is between $min and $max.
849
     *
850
     * Note: There is a slight difference in the way BETWEEN works on some databases.
851
     * http://www.w3schools.com/sql/sql_between.asp. If you want complete database
852
     * independence you should avoid using between().
853
     *
854
     * @param string $value SQL expression producing the value to compare.
855
     * @param string $min   SQL expression producing the lower value to compare with.
856
     * @param string $max   SQL expression producing the higher value to compare with.
857
     */
858
    public function getBetweenExpression(string $value, string $min, string $max) : string
859
    {
860
        return $value . ' BETWEEN ' . $min . ' AND ' . $max;
861
    }
862
863
    /**
864
     * Returns the SQL to get the arccosine of a value.
865
     *
866
     * @param string $number SQL expression producing the number.
867
     */
868
    public function getAcosExpression(string $number) : string
869
    {
870
        return 'ACOS(' . $number . ')';
871
    }
872
873
    /**
874
     * Returns the SQL to get the sine of a value.
875
     *
876
     * @param string $number SQL expression producing the number.
877
     */
878
    public function getSinExpression(string $number) : string
879
    {
880
        return 'SIN(' . $number . ')';
881
    }
882
883
    /**
884
     * Returns the SQL to get the PI value.
885
     */
886
    public function getPiExpression() : string
887
    {
888
        return 'PI()';
889
    }
890
891
    /**
892
     * Returns the SQL to get the cosine of a value.
893
     *
894
     * @param string $number SQL expression producing the number.
895
     */
896
    public function getCosExpression(string $number) : string
897
    {
898
        return 'COS(' . $number . ')';
899
    }
900
901
    /**
902
     * Returns the SQL to calculate the difference in days between the two passed dates.
903
     *
904
     * Computes diff = date1 - date2.
905
     *
906
     * @throws DBALException If not supported on this platform.
907
     */
908
    public function getDateDiffExpression(string $date1, string $date2) : string
0 ignored issues
show
Unused Code introduced by
The parameter $date1 is not used and could be removed. ( Ignorable by Annotation )

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

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

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

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

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

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

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

Loading history...
909
    {
910
        throw DBALException::notSupported(__METHOD__);
911
    }
912
913
    /**
914
     * Returns the SQL to add the number of given seconds to a date.
915
     *
916
     * @param string $date    SQL expression producing the date.
917
     * @param string $seconds SQL expression producing the number of seconds.
918
     *
919
     * @throws DBALException If not supported on this platform.
920
     */
921 54244
    public function getDateAddSecondsExpression(string $date, string $seconds) : string
922
    {
923 54244
        return $this->getDateArithmeticIntervalExpression($date, '+', $seconds, DateIntervalUnit::SECOND);
924
    }
925
926
    /**
927
     * Returns the SQL to subtract the number of given seconds from a date.
928
     *
929
     * @param string $date    SQL expression producing the date.
930
     * @param string $seconds SQL expression producing the number of seconds.
931
     *
932
     * @throws DBALException If not supported on this platform.
933
     */
934 54192
    public function getDateSubSecondsExpression(string $date, string $seconds) : string
935
    {
936 54192
        return $this->getDateArithmeticIntervalExpression($date, '-', $seconds, DateIntervalUnit::SECOND);
937
    }
938
939
    /**
940
     * Returns the SQL to add the number of given minutes to a date.
941
     *
942
     * @param string $date    SQL expression producing the date.
943
     * @param string $minutes SQL expression producing the number of minutes.
944
     *
945
     * @throws DBALException If not supported on this platform.
946
     */
947 54140
    public function getDateAddMinutesExpression(string $date, string $minutes) : string
948
    {
949 54140
        return $this->getDateArithmeticIntervalExpression($date, '+', $minutes, DateIntervalUnit::MINUTE);
950
    }
951
952
    /**
953
     * Returns the SQL to subtract the number of given minutes from a date.
954
     *
955
     * @param string $date    SQL expression producing the date.
956
     * @param string $minutes SQL expression producing the number of minutes.
957
     *
958
     * @throws DBALException If not supported on this platform.
959
     */
960 54088
    public function getDateSubMinutesExpression(string $date, string $minutes) : string
961
    {
962 54088
        return $this->getDateArithmeticIntervalExpression($date, '-', $minutes, DateIntervalUnit::MINUTE);
963
    }
964
965
    /**
966
     * Returns the SQL to add the number of given hours to a date.
967
     *
968
     * @param string $date  SQL expression producing the date.
969
     * @param string $hours SQL expression producing the number of hours.
970
     *
971
     * @throws DBALException If not supported on this platform.
972
     */
973 54036
    public function getDateAddHourExpression(string $date, string $hours) : string
974
    {
975 54036
        return $this->getDateArithmeticIntervalExpression($date, '+', $hours, DateIntervalUnit::HOUR);
976
    }
977
978
    /**
979
     * Returns the SQL to subtract the number of given hours to a date.
980
     *
981
     * @param string $date  SQL expression producing the date.
982
     * @param string $hours SQL expression producing the number of hours.
983
     *
984
     * @throws DBALException If not supported on this platform.
985
     */
986 53984
    public function getDateSubHourExpression(string $date, string $hours) : string
987
    {
988 53984
        return $this->getDateArithmeticIntervalExpression($date, '-', $hours, DateIntervalUnit::HOUR);
989
    }
990
991
    /**
992
     * Returns the SQL to add the number of given days to a date.
993
     *
994
     * @param string $date SQL expression producing the date.
995
     * @param string $days SQL expression producing the number of days.
996
     *
997
     * @throws DBALException If not supported on this platform.
998
     */
999 53932
    public function getDateAddDaysExpression(string $date, string $days) : string
1000
    {
1001 53932
        return $this->getDateArithmeticIntervalExpression($date, '+', $days, DateIntervalUnit::DAY);
1002
    }
1003
1004
    /**
1005
     * Returns the SQL to subtract the number of given days to a date.
1006
     *
1007
     * @param string $date SQL expression producing the date.
1008
     * @param string $days SQL expression producing the number of days.
1009
     *
1010
     * @throws DBALException If not supported on this platform.
1011
     */
1012 53880
    public function getDateSubDaysExpression(string $date, string $days) : string
1013
    {
1014 53880
        return $this->getDateArithmeticIntervalExpression($date, '-', $days, DateIntervalUnit::DAY);
1015
    }
1016
1017
    /**
1018
     * Returns the SQL to add the number of given weeks to a date.
1019
     *
1020
     * @param string $date  SQL expression producing the date.
1021
     * @param string $weeks SQL expression producing the number of weeks.
1022
     *
1023
     * @throws DBALException If not supported on this platform.
1024
     */
1025 53828
    public function getDateAddWeeksExpression(string $date, string $weeks) : string
1026
    {
1027 53828
        return $this->getDateArithmeticIntervalExpression($date, '+', $weeks, DateIntervalUnit::WEEK);
1028
    }
1029
1030
    /**
1031
     * Returns the SQL to subtract the number of given weeks from a date.
1032
     *
1033
     * @param string $date  SQL expression producing the date.
1034
     * @param string $weeks SQL expression producing the number of weeks.
1035
     *
1036
     * @throws DBALException If not supported on this platform.
1037
     */
1038 53776
    public function getDateSubWeeksExpression(string $date, string $weeks) : string
1039
    {
1040 53776
        return $this->getDateArithmeticIntervalExpression($date, '-', $weeks, DateIntervalUnit::WEEK);
1041
    }
1042
1043
    /**
1044
     * Returns the SQL to add the number of given months to a date.
1045
     *
1046
     * @param string $date   SQL expression producing the date.
1047
     * @param string $months SQL expression producing the number of months.
1048
     *
1049
     * @throws DBALException If not supported on this platform.
1050
     */
1051 53724
    public function getDateAddMonthExpression(string $date, string $months) : string
1052
    {
1053 53724
        return $this->getDateArithmeticIntervalExpression($date, '+', $months, DateIntervalUnit::MONTH);
1054
    }
1055
1056
    /**
1057
     * Returns the SQL to subtract the number of given months to a date.
1058
     *
1059
     * @param string $date   SQL expression producing the date.
1060
     * @param string $months SQL expression producing the number of months.
1061
     *
1062
     * @throws DBALException If not supported on this platform.
1063
     */
1064 53672
    public function getDateSubMonthExpression(string $date, string $months) : string
1065
    {
1066 53672
        return $this->getDateArithmeticIntervalExpression($date, '-', $months, DateIntervalUnit::MONTH);
1067
    }
1068
1069
    /**
1070
     * Returns the SQL to add the number of given quarters to a date.
1071
     *
1072
     * @param string $date     SQL expression producing the date.
1073
     * @param string $quarters SQL expression producing the number of quarters.
1074
     *
1075
     * @throws DBALException If not supported on this platform.
1076
     */
1077 53620
    public function getDateAddQuartersExpression(string $date, string $quarters) : string
1078
    {
1079 53620
        return $this->getDateArithmeticIntervalExpression($date, '+', $quarters, DateIntervalUnit::QUARTER);
1080
    }
1081
1082
    /**
1083
     * Returns the SQL to subtract the number of given quarters from a date.
1084
     *
1085
     * @param string $date     SQL expression producing the date.
1086
     * @param string $quarters SQL expression producing the number of quarters.
1087
     *
1088
     * @throws DBALException If not supported on this platform.
1089
     */
1090 53568
    public function getDateSubQuartersExpression(string $date, string $quarters) : string
1091
    {
1092 53568
        return $this->getDateArithmeticIntervalExpression($date, '-', $quarters, DateIntervalUnit::QUARTER);
1093
    }
1094
1095
    /**
1096
     * Returns the SQL to add the number of given years to a date.
1097
     *
1098
     * @param string $date  SQL expression producing the date.
1099
     * @param string $years SQL expression producing the number of years.
1100
     *
1101
     * @throws DBALException If not supported on this platform.
1102
     */
1103 53516
    public function getDateAddYearsExpression(string $date, string $years) : string
1104
    {
1105 53516
        return $this->getDateArithmeticIntervalExpression($date, '+', $years, DateIntervalUnit::YEAR);
1106
    }
1107
1108
    /**
1109
     * Returns the SQL to subtract the number of given years from a date.
1110
     *
1111
     * @param string $date  SQL expression producing the date.
1112
     * @param string $years SQL expression producing the number of years.
1113
     *
1114
     * @throws DBALException If not supported on this platform.
1115
     */
1116 53464
    public function getDateSubYearsExpression(string $date, string $years) : string
1117
    {
1118 53464
        return $this->getDateArithmeticIntervalExpression($date, '-', $years, DateIntervalUnit::YEAR);
1119
    }
1120
1121
    /**
1122
     * Returns the SQL for a date arithmetic expression.
1123
     *
1124
     * @param string $date     SQL expression representing a date to perform the arithmetic operation on.
1125
     * @param string $operator The arithmetic operator (+ or -).
1126
     * @param string $interval SQL expression representing the value of the interval that shall be calculated
1127
     *                         into the date.
1128
     * @param string $unit     The unit of the interval that shall be calculated into the date.
1129
     *                         One of the DATE_INTERVAL_UNIT_* constants.
1130
     *
1131
     * @throws DBALException If not supported on this platform.
1132
     */
1133
    protected function getDateArithmeticIntervalExpression(string $date, string $operator, string $interval, string $unit) : string
0 ignored issues
show
Unused Code introduced by
The parameter $unit is not used and could be removed. ( Ignorable by Annotation )

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

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

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

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

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

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

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

Loading history...
1134
    {
1135
        throw DBALException::notSupported(__METHOD__);
1136
    }
1137
1138
    /**
1139
     * Generates the SQL expression which represents the given date interval multiplied by a number
1140
     *
1141
     * @param string $interval   SQL expression describing the interval value
1142
     * @param int    $multiplier Interval multiplier
1143
     *
1144
     * @throws DBALException
1145
     */
1146 50229
    protected function multiplyInterval(string $interval, int $multiplier) : string
1147
    {
1148 50229
        return sprintf('(%s * %d)', $interval, $multiplier);
1149
    }
1150
1151
    /**
1152
     * Returns the SQL bit AND comparison expression.
1153
     *
1154
     * @param string $value1 SQL expression producing the first value.
1155
     * @param string $value2 SQL expression producing the second value.
1156
     */
1157 52684
    public function getBitAndComparisonExpression(string $value1, string $value2) : string
1158
    {
1159 52684
        return '(' . $value1 . ' & ' . $value2 . ')';
1160
    }
1161
1162
    /**
1163
     * Returns the SQL bit OR comparison expression.
1164
     *
1165
     * @param string $value1 SQL expression producing the first value.
1166
     * @param string $value2 SQL expression producing the second value.
1167
     */
1168 52682
    public function getBitOrComparisonExpression(string $value1, string $value2) : string
1169
    {
1170 52682
        return '(' . $value1 . ' | ' . $value2 . ')';
1171
    }
1172
1173
    /**
1174
     * Returns the FOR UPDATE expression.
1175
     *
1176
     * @return string
1177
     */
1178 38420
    public function getForUpdateSQL()
1179
    {
1180 38420
        return 'FOR UPDATE';
1181
    }
1182
1183
    /**
1184
     * Honors that some SQL vendors such as MsSql use table hints for locking instead of the ANSI SQL FOR UPDATE specification.
1185
     *
1186
     * @param string   $fromClause The FROM clause to append the hint for the given lock mode to.
1187
     * @param int|null $lockMode   One of the Doctrine\DBAL\LockMode::* constants. If null is given, nothing will
1188
     *                             be appended to the FROM clause.
1189
     *
1190
     * @return string
1191
     */
1192 42262
    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

1192
    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...
1193
    {
1194 42262
        return $fromClause;
1195
    }
1196
1197
    /**
1198
     * Returns the SQL snippet to append to any SELECT statement which locks rows in shared read lock.
1199
     *
1200
     * This defaults to the ANSI SQL "FOR UPDATE", which is an exclusive lock (Write). Some database
1201
     * vendors allow to lighten this constraint up to be a real read lock.
1202
     *
1203
     * @return string
1204
     */
1205
    public function getReadLockSQL()
1206
    {
1207
        return $this->getForUpdateSQL();
1208
    }
1209
1210
    /**
1211
     * Returns the SQL snippet to append to any SELECT statement which obtains an exclusive lock on the rows.
1212
     *
1213
     * The semantics of this lock mode should equal the SELECT .. FOR UPDATE of the ANSI SQL standard.
1214
     *
1215
     * @return string
1216
     */
1217 49948
    public function getWriteLockSQL()
1218
    {
1219 49948
        return $this->getForUpdateSQL();
1220
    }
1221
1222
    /**
1223
     * Returns the SQL snippet to drop an existing database.
1224
     *
1225
     * @param string $database The name of the database that should be dropped.
1226
     *
1227
     * @return string
1228
     */
1229 37063
    public function getDropDatabaseSQL($database)
1230
    {
1231 37063
        return 'DROP DATABASE ' . $database;
1232
    }
1233
1234
    /**
1235
     * Returns the SQL snippet to drop an existing table.
1236
     *
1237
     * @param Table|string $table
1238
     *
1239
     * @return string
1240
     *
1241
     * @throws InvalidArgumentException
1242
     */
1243 55351
    public function getDropTableSQL($table)
1244
    {
1245 55351
        $tableArg = $table;
1246
1247 55351
        if ($table instanceof Table) {
1248 5616
            $table = $table->getQuotedName($this);
1249
        }
1250
1251 55351
        if (! is_string($table)) {
0 ignored issues
show
introduced by
The condition is_string($table) is always true.
Loading history...
1252
            throw new InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.');
1253
        }
1254
1255 55351
        if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaDropTable)) {
1256 47164
            $eventArgs = new SchemaDropTableEventArgs($tableArg, $this);
1257 47164
            $this->_eventManager->dispatchEvent(Events::onSchemaDropTable, $eventArgs);
1258
1259 47164
            if ($eventArgs->isDefaultPrevented()) {
1260
                $sql = $eventArgs->getSql();
1261
1262
                if ($sql === null) {
1263
                    throw new UnexpectedValueException('Default implementation of DROP TABLE was overridden with NULL');
1264
                }
1265
1266
                return $sql;
1267
            }
1268
        }
1269
1270 55351
        return 'DROP TABLE ' . $table;
1271
    }
1272
1273
    /**
1274
     * Returns the SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction.
1275
     *
1276
     * @param Table|string $table
1277
     *
1278
     * @return string
1279
     */
1280 23039
    public function getDropTemporaryTableSQL($table)
1281
    {
1282 23039
        return $this->getDropTableSQL($table);
1283
    }
1284
1285
    /**
1286
     * Returns the SQL to drop an index from a table.
1287
     *
1288
     * @param Index|string $index
1289
     * @param Table|string $table
1290
     *
1291
     * @return string
1292
     *
1293
     * @throws InvalidArgumentException
1294
     */
1295 36580
    public function getDropIndexSQL($index, $table = null)
1296
    {
1297 36580
        if ($index instanceof Index) {
1298 36554
            $index = $index->getQuotedName($this);
1299 15808
        } elseif (! is_string($index)) {
0 ignored issues
show
introduced by
The condition is_string($index) is always true.
Loading history...
1300
            throw new InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.');
1301
        }
1302
1303 36580
        return 'DROP INDEX ' . $index;
1304
    }
1305
1306
    /**
1307
     * Returns the SQL to drop a constraint.
1308
     *
1309
     * @param Constraint|string $constraint
1310
     * @param Table|string      $table
1311
     *
1312
     * @return string
1313
     */
1314 47496
    public function getDropConstraintSQL($constraint, $table)
1315
    {
1316 47496
        if (! $constraint instanceof Constraint) {
1317 46384
            $constraint = new Identifier($constraint);
1318
        }
1319
1320 47496
        if (! $table instanceof Table) {
1321 47496
            $table = new Identifier($table);
1322
        }
1323
1324 47496
        $constraint = $constraint->getQuotedName($this);
1325 47496
        $table      = $table->getQuotedName($this);
1326
1327 47496
        return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $constraint;
1328
    }
1329
1330
    /**
1331
     * Returns the SQL to drop a foreign key.
1332
     *
1333
     * @param ForeignKeyConstraint|string $foreignKey
1334
     * @param Table|string                $table
1335
     *
1336
     * @return string
1337
     */
1338 49304
    public function getDropForeignKeySQL($foreignKey, $table)
1339
    {
1340 49304
        if (! $foreignKey instanceof ForeignKeyConstraint) {
1341 46410
            $foreignKey = new Identifier($foreignKey);
1342
        }
1343
1344 49304
        if (! $table instanceof Table) {
1345 49304
            $table = new Identifier($table);
1346
        }
1347
1348 49304
        $foreignKey = $foreignKey->getQuotedName($this);
1349 49304
        $table      = $table->getQuotedName($this);
1350
1351 49304
        return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $foreignKey;
1352
    }
1353
1354
    /**
1355
     * Returns the SQL statement(s) to create a table with the specified name, columns and constraints
1356
     * on this platform.
1357
     *
1358
     * @param int $createFlags
1359
     *
1360
     * @return string[] The sequence of SQL statements.
1361
     *
1362
     * @throws DBALException
1363
     * @throws InvalidArgumentException
1364
     */
1365 55351
    public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDEXES)
1366
    {
1367 55351
        if (! is_int($createFlags)) {
0 ignored issues
show
introduced by
The condition is_int($createFlags) is always true.
Loading history...
1368
            throw new InvalidArgumentException('Second argument of AbstractPlatform::getCreateTableSQL() has to be integer.');
1369
        }
1370
1371 55351
        if (count($table->getColumns()) === 0) {
1372 47476
            throw DBALException::noColumnsSpecifiedForTable($table->getName());
1373
        }
1374
1375 55351
        $tableName                    = $table->getQuotedName($this);
1376 55351
        $options                      = $table->getOptions();
1377 55351
        $options['uniqueConstraints'] = [];
1378 55351
        $options['indexes']           = [];
1379 55351
        $options['primary']           = [];
1380
1381 55351
        if (($createFlags & self::CREATE_INDEXES) > 0) {
1382 55351
            foreach ($table->getIndexes() as $index) {
1383
                /** @var $index Index */
1384 55351
                if (! $index->isPrimary()) {
1385 53010
                    $options['indexes'][$index->getQuotedName($this)] = $index;
1386
1387 53010
                    continue;
1388
                }
1389
1390 55351
                $options['primary']       = $index->getQuotedColumns($this);
1391 55351
                $options['primary_index'] = $index;
1392
            }
1393
1394 55351
            foreach ($table->getUniqueConstraints() as $uniqueConstraint) {
1395
                /** @var UniqueConstraint $uniqueConstraint */
1396
                $options['uniqueConstraints'][$uniqueConstraint->getQuotedName($this)] = $uniqueConstraint;
1397
            }
1398
        }
1399
1400 55351
        if (($createFlags & self::CREATE_FOREIGNKEYS) > 0) {
1401 55351
            $options['foreignKeys'] = [];
1402
1403 55351
            foreach ($table->getForeignKeys() as $fkConstraint) {
1404 52986
                $options['foreignKeys'][] = $fkConstraint;
1405
            }
1406
        }
1407
1408 55351
        $columnSql = [];
1409 55351
        $columns   = [];
1410
1411 55351
        foreach ($table->getColumns() as $column) {
1412 55351
            if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn)) {
1413 47190
                $eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this);
1414 47190
                $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs);
1415
1416 47190
                $columnSql = array_merge($columnSql, $eventArgs->getSql());
1417
1418 47190
                if ($eventArgs->isDefaultPrevented()) {
1419
                    continue;
1420
                }
1421
            }
1422
1423 55351
            $columnData            = $column->toArray();
1424 55351
            $columnData['name']    = $column->getQuotedName($this);
1425 55351
            $columnData['version'] = $column->hasPlatformOption('version') ? $column->getPlatformOption('version') : false;
1426 55351
            $columnData['comment'] = $this->getColumnComment($column);
1427
1428 55351
            if ($columnData['type'] instanceof Types\StringType && $columnData['length'] === null) {
1429 55022
                $columnData['length'] = 255;
1430
            }
1431
1432 55351
            if (in_array($column->getName(), $options['primary'])) {
1433 55351
                $columnData['primary'] = true;
1434
            }
1435
1436 55351
            $columns[$columnData['name']] = $columnData;
1437
        }
1438
1439 55351
        if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) {
1440 47190
            $eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this);
1441 47190
            $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs);
1442
1443 47190
            if ($eventArgs->isDefaultPrevented()) {
1444
                return array_merge($eventArgs->getSql(), $columnSql);
1445
            }
1446
        }
1447
1448 55351
        $sql = $this->_getCreateTableSQL($tableName, $columns, $options);
1449 55351
        if ($this->supportsCommentOnStatement()) {
1450 50913
            foreach ($table->getColumns() as $column) {
1451 50913
                $comment = $this->getColumnComment($column);
1452
1453 50913
                if ($comment === null || $comment === '') {
1454 50913
                    continue;
1455
                }
1456
1457 48382
                $sql[] = $this->getCommentOnColumnSQL($tableName, $column->getQuotedName($this), $comment);
1458
            }
1459
        }
1460
1461 55351
        return array_merge($sql, $columnSql);
1462
    }
1463
1464
    /**
1465
     * @param string      $tableName
1466
     * @param string      $columnName
1467
     * @param string|null $comment
1468
     *
1469
     * @return string
1470
     */
1471 47450
    public function getCommentOnColumnSQL($tableName, $columnName, $comment)
1472
    {
1473 47450
        $tableName  = new Identifier($tableName);
1474 47450
        $columnName = new Identifier($columnName);
1475
1476 47450
        return sprintf(
1477
            'COMMENT ON COLUMN %s.%s IS %s',
1478 47450
            $tableName->getQuotedName($this),
1479 47450
            $columnName->getQuotedName($this),
1480 47450
            $this->quoteStringLiteral((string) $comment)
1481
        );
1482
    }
1483
1484
    /**
1485
     * Returns the SQL to create inline comment on a column.
1486
     *
1487
     * @param string $comment
1488
     *
1489
     * @return string
1490
     *
1491
     * @throws DBALException If not supported on this platform.
1492
     */
1493 49268
    public function getInlineColumnCommentSQL($comment)
1494
    {
1495 49268
        if (! $this->supportsInlineColumnComments()) {
1496 46280
            throw DBALException::notSupported(__METHOD__);
1497
        }
1498
1499 48056
        return 'COMMENT ' . $this->quoteStringLiteral($comment);
1500
    }
1501
1502
    /**
1503
     * Returns the SQL used to create a table.
1504
     *
1505
     * @param string    $tableName
1506
     * @param mixed[][] $columns
1507
     * @param mixed[]   $options
1508
     *
1509
     * @return string[]
1510
     */
1511 49346
    protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
1512
    {
1513 49346
        $columnListSql = $this->getColumnDeclarationListSQL($columns);
1514
1515 49346
        if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
1516
            foreach ($options['uniqueConstraints'] as $name => $definition) {
1517
                $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
1518
            }
1519
        }
1520
1521 49346
        if (isset($options['primary']) && ! empty($options['primary'])) {
1522 49346
            $columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')';
1523
        }
1524
1525 49346
        if (isset($options['indexes']) && ! empty($options['indexes'])) {
1526
            foreach ($options['indexes'] as $index => $definition) {
1527
                $columnListSql .= ', ' . $this->getIndexDeclarationSQL($index, $definition);
1528
            }
1529
        }
1530
1531 49346
        $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql;
1532
1533 49346
        $check = $this->getCheckDeclarationSQL($columns);
1534 49346
        if (! empty($check)) {
1535 48880
            $query .= ', ' . $check;
1536
        }
1537 49346
        $query .= ')';
1538
1539 49346
        $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...
1540
1541 49346
        if (isset($options['foreignKeys'])) {
1542 49322
            foreach ((array) $options['foreignKeys'] as $definition) {
1543 49086
                $sql[] = $this->getCreateForeignKeySQL($definition, $tableName);
1544
            }
1545
        }
1546
1547 49346
        return $sql;
1548
    }
1549
1550
    /**
1551
     * @return string
1552
     */
1553 38380
    public function getCreateTemporaryTableSnippetSQL()
1554
    {
1555 38380
        return 'CREATE TEMPORARY TABLE';
1556
    }
1557
1558
    /**
1559
     * Returns the SQL to create a sequence on this platform.
1560
     *
1561
     * @return string
1562
     *
1563
     * @throws DBALException If not supported on this platform.
1564
     */
1565
    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

1565
    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...
1566
    {
1567
        throw DBALException::notSupported(__METHOD__);
1568
    }
1569
1570
    /**
1571
     * Returns the SQL to change a sequence on this platform.
1572
     *
1573
     * @return string
1574
     *
1575
     * @throws DBALException If not supported on this platform.
1576
     */
1577
    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

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

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

2151
            return " DEFAULT '" . /** @scrutinizer ignore-type */ $this->convertBooleans($default) . "'";
Loading history...
2152
        }
2153
2154 51249
        return " DEFAULT '" . $default . "'";
2155
    }
2156
2157
    /**
2158
     * Obtains DBMS specific SQL code portion needed to set a CHECK constraint
2159
     * declaration to be used in statements like CREATE TABLE.
2160
     *
2161
     * @param string[]|mixed[][] $definition The check definition.
2162
     *
2163
     * @return string DBMS specific SQL code portion needed to set a CHECK constraint.
2164
     */
2165 50130
    public function getCheckDeclarationSQL(array $definition)
2166
    {
2167 50130
        $constraints = [];
2168 50130
        foreach ($definition as $field => $def) {
2169 50130
            if (is_string($def)) {
2170
                $constraints[] = 'CHECK (' . $def . ')';
2171
            } else {
2172 50130
                if (isset($def['min'])) {
2173 48880
                    $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')';
2174
                }
2175
2176 50130
                if (isset($def['max'])) {
2177 48880
                    $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')';
2178
                }
2179
            }
2180
        }
2181
2182 50130
        return implode(', ', $constraints);
2183
    }
2184
2185
    /**
2186
     * Obtains DBMS specific SQL code portion needed to set a unique
2187
     * constraint declaration to be used in statements like CREATE TABLE.
2188
     *
2189
     * @param string           $name       The name of the unique constraint.
2190
     * @param UniqueConstraint $constraint The unique constraint definition.
2191
     *
2192
     * @return string DBMS specific SQL code portion needed to set a constraint.
2193
     *
2194
     * @throws InvalidArgumentException
2195
     */
2196 47372
    public function getUniqueConstraintDeclarationSQL($name, UniqueConstraint $constraint)
2197
    {
2198 47372
        $columns = $constraint->getColumns();
2199 47372
        $name    = new Identifier($name);
2200
2201 47372
        if (count($columns) === 0) {
2202 17992
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
2203
        }
2204
2205 47372
        $flags = ['UNIQUE'];
2206
2207 47372
        if ($constraint->hasFlag('clustered')) {
2208 18018
            $flags[] = 'CLUSTERED';
2209
        }
2210
2211 47372
        $constraintName  = $name->getQuotedName($this);
2212 47372
        $constraintName  = ! empty($constraintName) ? $constraintName . ' ' : '';
2213 47372
        $columnListNames = $this->getIndexFieldDeclarationListSQL($columns);
2214
2215 47372
        return sprintf('CONSTRAINT %s%s (%s)', $constraintName, implode(' ', $flags), $columnListNames);
2216
    }
2217
2218
    /**
2219
     * Obtains DBMS specific SQL code portion needed to set an index
2220
     * declaration to be used in statements like CREATE TABLE.
2221
     *
2222
     * @param string $name  The name of the index.
2223
     * @param Index  $index The index definition.
2224
     *
2225
     * @return string DBMS specific SQL code portion needed to set an index.
2226
     *
2227
     * @throws InvalidArgumentException
2228
     */
2229 49781
    public function getIndexDeclarationSQL($name, Index $index)
2230
    {
2231 49781
        $columns = $index->getColumns();
2232 49781
        $name    = new Identifier($name);
2233
2234 49781
        if (count($columns) === 0) {
2235
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
2236
        }
2237
2238 49781
        return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name->getQuotedName($this) . ' ('
2239 49781
            . $this->getIndexFieldDeclarationListSQL($index)
2240 49781
            . ')' . $this->getPartialIndexSQL($index);
2241
    }
2242
2243
    /**
2244
     * Obtains SQL code portion needed to create a custom column,
2245
     * e.g. when a field has the "columnDefinition" keyword.
2246
     * Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate.
2247
     *
2248
     * @param mixed[] $columnDef
2249
     *
2250
     * @return string
2251
     */
2252 49164
    public function getCustomTypeDeclarationSQL(array $columnDef)
2253
    {
2254 49164
        return $columnDef['columnDefinition'];
2255
    }
2256
2257
    /**
2258
     * Obtains DBMS specific SQL code portion needed to set an index
2259
     * declaration to be used in statements like CREATE TABLE.
2260
     *
2261
     * @param mixed[]|Index $columnsOrIndex array declaration is deprecated, prefer passing Index to this method
2262
     */
2263 53010
    public function getIndexFieldDeclarationListSQL($columnsOrIndex) : string
2264
    {
2265 53010
        if ($columnsOrIndex instanceof Index) {
2266 53010
            return implode(', ', $columnsOrIndex->getQuotedColumns($this));
2267
        }
2268
2269 47372
        if (! is_array($columnsOrIndex)) {
0 ignored issues
show
introduced by
The condition is_array($columnsOrIndex) is always true.
Loading history...
2270
            throw new InvalidArgumentException('Fields argument should be an Index or array.');
2271
        }
2272
2273 47372
        $ret = [];
2274
2275 47372
        foreach ($columnsOrIndex as $column => $definition) {
2276 47372
            if (is_array($definition)) {
2277
                $ret[] = $column;
2278
            } else {
2279 47372
                $ret[] = $definition;
2280
            }
2281
        }
2282
2283 47372
        return implode(', ', $ret);
2284
    }
2285
2286
    /**
2287
     * Returns the required SQL string that fits between CREATE ... TABLE
2288
     * to create the table as a temporary table.
2289
     *
2290
     * Should be overridden in driver classes to return the correct string for the
2291
     * specific database type.
2292
     *
2293
     * The default is to return the string "TEMPORARY" - this will result in a
2294
     * SQL error for any database that does not support temporary tables, or that
2295
     * requires a different SQL command from "CREATE TEMPORARY TABLE".
2296
     *
2297
     * @return string The string required to be placed between "CREATE" and "TABLE"
2298
     *                to generate a temporary table, if possible.
2299
     */
2300
    public function getTemporaryTableSQL()
2301
    {
2302
        return 'TEMPORARY';
2303
    }
2304
2305
    /**
2306
     * Some vendors require temporary table names to be qualified specially.
2307
     *
2308
     * @param string $tableName
2309
     *
2310
     * @return string
2311
     */
2312 38380
    public function getTemporaryTableName($tableName)
2313
    {
2314 38380
        return $tableName;
2315
    }
2316
2317
    /**
2318
     * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2319
     * of a field declaration to be used in statements like CREATE TABLE.
2320
     *
2321
     * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2322
     *                of a field declaration.
2323
     */
2324 52986
    public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey)
2325
    {
2326 52986
        $sql  = $this->getForeignKeyBaseDeclarationSQL($foreignKey);
2327 52986
        $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey);
2328
2329 52986
        return $sql;
2330
    }
2331
2332
    /**
2333
     * Returns the FOREIGN KEY query section dealing with non-standard options
2334
     * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
2335
     *
2336
     * @param ForeignKeyConstraint $foreignKey The foreign key definition.
2337
     *
2338
     * @return string
2339
     */
2340 52986
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
2341
    {
2342 52986
        $query = '';
2343 52986
        if ($this->supportsForeignKeyOnUpdate() && $foreignKey->hasOption('onUpdate')) {
2344 17966
            $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate'));
2345
        }
2346 52986
        if ($foreignKey->hasOption('onDelete')) {
2347 51479
            $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
2348
        }
2349
2350 52986
        return $query;
2351
    }
2352
2353
    /**
2354
     * Returns the given referential action in uppercase if valid, otherwise throws an exception.
2355
     *
2356
     * @param string $action The foreign key referential action.
2357
     *
2358
     * @return string
2359
     *
2360
     * @throws InvalidArgumentException If unknown referential action given.
2361
     */
2362 51479
    public function getForeignKeyReferentialActionSQL($action)
2363
    {
2364 51479
        $upper = strtoupper($action);
2365 51479
        switch ($upper) {
2366
            case 'CASCADE':
2367
            case 'SET NULL':
2368
            case 'NO ACTION':
2369
            case 'RESTRICT':
2370
            case 'SET DEFAULT':
2371 51479
                return $upper;
2372
            default:
2373 48048
                throw new InvalidArgumentException('Invalid foreign key action: ' . $upper);
2374
        }
2375
    }
2376
2377
    /**
2378
     * Obtains DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2379
     * of a field declaration to be used in statements like CREATE TABLE.
2380
     *
2381
     * @return string
2382
     *
2383
     * @throws InvalidArgumentException
2384
     */
2385 52986
    public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey)
2386
    {
2387 52986
        $sql = '';
2388 52986
        if (strlen($foreignKey->getName())) {
2389 52986
            $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' ';
2390
        }
2391 52986
        $sql .= 'FOREIGN KEY (';
2392
2393 52986
        if (count($foreignKey->getLocalColumns()) === 0) {
2394
            throw new InvalidArgumentException("Incomplete definition. 'local' required.");
2395
        }
2396 52986
        if (count($foreignKey->getForeignColumns()) === 0) {
2397
            throw new InvalidArgumentException("Incomplete definition. 'foreign' required.");
2398
        }
2399 52986
        if (strlen($foreignKey->getForeignTableName()) === 0) {
2400
            throw new InvalidArgumentException("Incomplete definition. 'foreignTable' required.");
2401
        }
2402
2403 52986
        return $sql . implode(', ', $foreignKey->getQuotedLocalColumns($this))
2404 52986
            . ') REFERENCES '
2405 52986
            . $foreignKey->getQuotedForeignTableName($this) . ' ('
2406 52986
            . implode(', ', $foreignKey->getQuotedForeignColumns($this)) . ')';
2407
    }
2408
2409
    /**
2410
     * Obtains DBMS specific SQL code portion needed to set the UNIQUE constraint
2411
     * of a field declaration to be used in statements like CREATE TABLE.
2412
     *
2413
     * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint
2414
     *                of a field declaration.
2415
     */
2416
    public function getUniqueFieldDeclarationSQL()
2417
    {
2418
        return 'UNIQUE';
2419
    }
2420
2421
    /**
2422
     * Obtains DBMS specific SQL code portion needed to set the CHARACTER SET
2423
     * of a field declaration to be used in statements like CREATE TABLE.
2424
     *
2425
     * @param string $charset The name of the charset.
2426
     *
2427
     * @return string DBMS specific SQL code portion needed to set the CHARACTER SET
2428
     *                of a field declaration.
2429
     */
2430
    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

2430
    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...
2431
    {
2432
        return '';
2433
    }
2434
2435
    /**
2436
     * Obtains DBMS specific SQL code portion needed to set the COLLATION
2437
     * of a field declaration to be used in statements like CREATE TABLE.
2438
     *
2439
     * @param string $collation The name of the collation.
2440
     *
2441
     * @return string DBMS specific SQL code portion needed to set the COLLATION
2442
     *                of a field declaration.
2443
     */
2444 49696
    public function getColumnCollationDeclarationSQL($collation)
2445
    {
2446 49696
        return $this->supportsColumnCollation() ? 'COLLATE ' . $collation : '';
2447
    }
2448
2449
    /**
2450
     * Whether the platform prefers sequences for ID generation.
2451
     * Subclasses should override this method to return TRUE if they prefer sequences.
2452
     *
2453
     * @return bool
2454
     */
2455 17290
    public function prefersSequences()
2456
    {
2457 17290
        return false;
2458
    }
2459
2460
    /**
2461
     * Whether the platform prefers identity columns (eg. autoincrement) for ID generation.
2462
     * Subclasses should override this method to return TRUE if they prefer identity columns.
2463
     *
2464
     * @return bool
2465
     */
2466 38100
    public function prefersIdentityColumns()
2467
    {
2468 38100
        return false;
2469
    }
2470
2471
    /**
2472
     * Some platforms need the boolean values to be converted.
2473
     *
2474
     * The default conversion in this implementation converts to integers (false => 0, true => 1).
2475
     *
2476
     * Note: if the input is not a boolean the original input might be returned.
2477
     *
2478
     * There are two contexts when converting booleans: Literals and Prepared Statements.
2479
     * This method should handle the literal case
2480
     *
2481
     * @param mixed $item A boolean or an array of them.
2482
     *
2483
     * @return mixed A boolean database value or an array of them.
2484
     */
2485 50232
    public function convertBooleans($item)
2486
    {
2487 50232
        if (is_array($item)) {
2488
            foreach ($item as $k => $value) {
2489
                if (! is_bool($value)) {
2490
                    continue;
2491
                }
2492
2493
                $item[$k] = (int) $value;
2494
            }
2495 50232
        } elseif (is_bool($item)) {
2496 50232
            $item = (int) $item;
2497
        }
2498
2499 50232
        return $item;
2500
    }
2501
2502
    /**
2503
     * Some platforms have boolean literals that needs to be correctly converted
2504
     *
2505
     * The default conversion tries to convert value into bool "(bool)$item"
2506
     *
2507
     * @param mixed $item
2508
     *
2509
     * @return bool|null
2510
     */
2511 49806
    public function convertFromBoolean($item)
2512
    {
2513 49806
        return $item === null ? null: (bool) $item;
2514
    }
2515
2516
    /**
2517
     * This method should handle the prepared statements case. When there is no
2518
     * distinction, it's OK to use the same method.
2519
     *
2520
     * Note: if the input is not a boolean the original input might be returned.
2521
     *
2522
     * @param mixed $item A boolean or an array of them.
2523
     *
2524
     * @return mixed A boolean database value or an array of them.
2525
     */
2526 45321
    public function convertBooleansToDatabaseValue($item)
2527
    {
2528 45321
        return $this->convertBooleans($item);
2529
    }
2530
2531
    /**
2532
     * Returns the SQL specific for the platform to get the current date.
2533
     *
2534
     * @return string
2535
     */
2536 47914
    public function getCurrentDateSQL()
2537
    {
2538 47914
        return 'CURRENT_DATE';
2539
    }
2540
2541
    /**
2542
     * Returns the SQL specific for the platform to get the current time.
2543
     *
2544
     * @return string
2545
     */
2546 7951
    public function getCurrentTimeSQL()
2547
    {
2548 7951
        return 'CURRENT_TIME';
2549
    }
2550
2551
    /**
2552
     * Returns the SQL specific for the platform to get the current timestamp
2553
     *
2554
     * @return string
2555
     */
2556 49224
    public function getCurrentTimestampSQL()
2557
    {
2558 49224
        return 'CURRENT_TIMESTAMP';
2559
    }
2560
2561
    /**
2562
     * Returns the SQL for a given transaction isolation level Connection constant.
2563
     *
2564
     * @param int $level
2565
     *
2566
     * @return string
2567
     *
2568
     * @throws InvalidArgumentException
2569
     */
2570 45708
    protected function _getTransactionIsolationLevelSQL($level)
2571
    {
2572
        switch ($level) {
2573 45708
            case TransactionIsolationLevel::READ_UNCOMMITTED:
2574 45708
                return 'READ UNCOMMITTED';
2575 45708
            case TransactionIsolationLevel::READ_COMMITTED:
2576 45708
                return 'READ COMMITTED';
2577 45708
            case TransactionIsolationLevel::REPEATABLE_READ:
2578 45708
                return 'REPEATABLE READ';
2579 45708
            case TransactionIsolationLevel::SERIALIZABLE:
2580 45708
                return 'SERIALIZABLE';
2581
            default:
2582
                throw new InvalidArgumentException('Invalid isolation level:' . $level);
2583
        }
2584
    }
2585
2586
    /**
2587
     * @return string
2588
     *
2589
     * @throws DBALException If not supported on this platform.
2590
     */
2591
    public function getListDatabasesSQL()
2592
    {
2593
        throw DBALException::notSupported(__METHOD__);
2594
    }
2595
2596
    /**
2597
     * Returns the SQL statement for retrieving the namespaces defined in the database.
2598
     *
2599
     * @return string
2600
     *
2601
     * @throws DBALException If not supported on this platform.
2602
     */
2603
    public function getListNamespacesSQL()
2604
    {
2605
        throw DBALException::notSupported(__METHOD__);
2606
    }
2607
2608
    /**
2609
     * @param string $database
2610
     *
2611
     * @return string
2612
     *
2613
     * @throws DBALException If not supported on this platform.
2614
     */
2615
    public function getListSequencesSQL($database)
2616
    {
2617
        throw DBALException::notSupported(__METHOD__);
2618
    }
2619
2620
    /**
2621
     * @param string $table
2622
     *
2623
     * @return string
2624
     *
2625
     * @throws DBALException If not supported on this platform.
2626
     */
2627
    public function getListTableConstraintsSQL($table)
2628
    {
2629
        throw DBALException::notSupported(__METHOD__);
2630
    }
2631
2632
    /**
2633
     * @param string      $table
2634
     * @param string|null $database
2635
     *
2636
     * @return string
2637
     *
2638
     * @throws DBALException If not supported on this platform.
2639
     */
2640
    public function getListTableColumnsSQL($table, $database = null)
2641
    {
2642
        throw DBALException::notSupported(__METHOD__);
2643
    }
2644
2645
    /**
2646
     * @return string
2647
     *
2648
     * @throws DBALException If not supported on this platform.
2649
     */
2650
    public function getListTablesSQL()
2651
    {
2652
        throw DBALException::notSupported(__METHOD__);
2653
    }
2654
2655
    /**
2656
     * @return string
2657
     *
2658
     * @throws DBALException If not supported on this platform.
2659
     */
2660
    public function getListUsersSQL()
2661
    {
2662
        throw DBALException::notSupported(__METHOD__);
2663
    }
2664
2665
    /**
2666
     * Returns the SQL to list all views of a database or user.
2667
     *
2668
     * @param string $database
2669
     *
2670
     * @return string
2671
     *
2672
     * @throws DBALException If not supported on this platform.
2673
     */
2674
    public function getListViewsSQL($database)
2675
    {
2676
        throw DBALException::notSupported(__METHOD__);
2677
    }
2678
2679
    /**
2680
     * Returns the list of indexes for the current database.
2681
     *
2682
     * The current database parameter is optional but will always be passed
2683
     * when using the SchemaManager API and is the database the given table is in.
2684
     *
2685
     * Attention: Some platforms only support currentDatabase when they
2686
     * are connected with that database. Cross-database information schema
2687
     * requests may be impossible.
2688
     *
2689
     * @param string $table
2690
     * @param string $currentDatabase
2691
     *
2692
     * @return string
2693
     *
2694
     * @throws DBALException If not supported on this platform.
2695
     */
2696
    public function getListTableIndexesSQL($table, $currentDatabase = null)
0 ignored issues
show
Unused Code introduced by
The parameter $currentDatabase is not used and could be removed. ( Ignorable by Annotation )

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

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

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

Loading history...
2697
    {
2698
        throw DBALException::notSupported(__METHOD__);
2699
    }
2700
2701
    /**
2702
     * @param string $table
2703
     *
2704
     * @return string
2705
     *
2706
     * @throws DBALException If not supported on this platform.
2707
     */
2708
    public function getListTableForeignKeysSQL($table)
2709
    {
2710
        throw DBALException::notSupported(__METHOD__);
2711
    }
2712
2713
    /**
2714
     * @param string $name
2715
     * @param string $sql
2716
     *
2717
     * @return string
2718
     *
2719
     * @throws DBALException If not supported on this platform.
2720
     */
2721
    public function getCreateViewSQL($name, $sql)
2722
    {
2723
        throw DBALException::notSupported(__METHOD__);
2724
    }
2725
2726
    /**
2727
     * @param string $name
2728
     *
2729
     * @return string
2730
     *
2731
     * @throws DBALException If not supported on this platform.
2732
     */
2733
    public function getDropViewSQL($name)
2734
    {
2735
        throw DBALException::notSupported(__METHOD__);
2736
    }
2737
2738
    /**
2739
     * Returns the SQL snippet to drop an existing sequence.
2740
     *
2741
     * @param Sequence|string $sequence
2742
     *
2743
     * @return string
2744
     *
2745
     * @throws DBALException If not supported on this platform.
2746
     */
2747
    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

2747
    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...
2748
    {
2749
        throw DBALException::notSupported(__METHOD__);
2750
    }
2751
2752
    /**
2753
     * @param string $sequenceName
2754
     *
2755
     * @return string
2756
     *
2757
     * @throws DBALException If not supported on this platform.
2758
     */
2759
    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

2759
    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...
2760
    {
2761
        throw DBALException::notSupported(__METHOD__);
2762
    }
2763
2764
    /**
2765
     * Returns the SQL to create a new database.
2766
     *
2767
     * @param string $database The name of the database that should be created.
2768
     *
2769
     * @return string
2770
     *
2771
     * @throws DBALException If not supported on this platform.
2772
     */
2773 34918
    public function getCreateDatabaseSQL($database)
2774
    {
2775 34918
        throw DBALException::notSupported(__METHOD__);
2776
    }
2777
2778
    /**
2779
     * Returns the SQL to set the transaction isolation level.
2780
     *
2781
     * @param int $level
2782
     *
2783
     * @return string
2784
     *
2785
     * @throws DBALException If not supported on this platform.
2786
     */
2787
    public function getSetTransactionIsolationSQL($level)
2788
    {
2789
        throw DBALException::notSupported(__METHOD__);
2790
    }
2791
2792
    /**
2793
     * Obtains DBMS specific SQL to be used to create datetime fields in
2794
     * statements like CREATE TABLE.
2795
     *
2796
     * @param mixed[] $fieldDeclaration
2797
     *
2798
     * @return string
2799
     *
2800
     * @throws DBALException If not supported on this platform.
2801
     */
2802
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
2803
    {
2804
        throw DBALException::notSupported(__METHOD__);
2805
    }
2806
2807
    /**
2808
     * Obtains DBMS specific SQL to be used to create datetime with timezone offset fields.
2809
     *
2810
     * @param mixed[] $fieldDeclaration
2811
     *
2812
     * @return string
2813
     */
2814 30647
    public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration)
2815
    {
2816 30647
        return $this->getDateTimeTypeDeclarationSQL($fieldDeclaration);
2817
    }
2818
2819
    /**
2820
     * Obtains DBMS specific SQL to be used to create date fields in statements
2821
     * like CREATE TABLE.
2822
     *
2823
     * @param mixed[] $fieldDeclaration
2824
     *
2825
     * @return string
2826
     *
2827
     * @throws DBALException If not supported on this platform.
2828
     */
2829
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
2830
    {
2831
        throw DBALException::notSupported(__METHOD__);
2832
    }
2833
2834
    /**
2835
     * Obtains DBMS specific SQL to be used to create time fields in statements
2836
     * like CREATE TABLE.
2837
     *
2838
     * @param mixed[] $fieldDeclaration
2839
     *
2840
     * @return string
2841
     *
2842
     * @throws DBALException If not supported on this platform.
2843
     */
2844
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
2845
    {
2846
        throw DBALException::notSupported(__METHOD__);
2847
    }
2848
2849
    /**
2850
     * @param mixed[] $fieldDeclaration
2851
     *
2852
     * @return string
2853
     */
2854 48256
    public function getFloatDeclarationSQL(array $fieldDeclaration)
2855
    {
2856 48256
        return 'DOUBLE PRECISION';
2857
    }
2858
2859
    /**
2860
     * Gets the default transaction isolation level of the platform.
2861
     *
2862
     * @see TransactionIsolationLevel
2863
     *
2864
     * @return int The default isolation level.
2865
     */
2866
    public function getDefaultTransactionIsolationLevel()
2867
    {
2868
        return TransactionIsolationLevel::READ_COMMITTED;
2869
    }
2870
2871
    /* supports*() methods */
2872
2873
    /**
2874
     * Whether the platform supports sequences.
2875
     *
2876
     * @return bool
2877
     */
2878 33393
    public function supportsSequences()
2879
    {
2880 33393
        return false;
2881
    }
2882
2883
    /**
2884
     * Whether the platform supports identity columns.
2885
     *
2886
     * Identity columns are columns that receive an auto-generated value from the
2887
     * database on insert of a row.
2888
     *
2889
     * @return bool
2890
     */
2891 34762
    public function supportsIdentityColumns()
2892
    {
2893 34762
        return false;
2894
    }
2895
2896
    /**
2897
     * Whether the platform emulates identity columns through sequences.
2898
     *
2899
     * Some platforms that do not support identity columns natively
2900
     * but support sequences can emulate identity columns by using
2901
     * sequences.
2902
     *
2903
     * @return bool
2904
     */
2905 48484
    public function usesSequenceEmulatedIdentityColumns()
2906
    {
2907 48484
        return false;
2908
    }
2909
2910
    /**
2911
     * Gets the sequence name prefix based on table information.
2912
     *
2913
     * @param string      $tableName
2914
     * @param string|null $schemaName
2915
     *
2916
     * @return string
2917
     */
2918
    public function getSequencePrefix($tableName, $schemaName = null)
2919
    {
2920
        if (! $schemaName) {
2921
            return $tableName;
2922
        }
2923
2924
        // Prepend the schema name to the table name if there is one
2925
        return ! $this->supportsSchemas() && $this->canEmulateSchemas()
2926
            ? $schemaName . '__' . $tableName
2927
            : $schemaName . '.' . $tableName;
2928
    }
2929
2930
    /**
2931
     * Returns the name of the sequence for a particular identity column in a particular table.
2932
     *
2933
     * @see    usesSequenceEmulatedIdentityColumns
2934
     *
2935
     * @param string $tableName  The name of the table to return the sequence name for.
2936
     * @param string $columnName The name of the identity column in the table to return the sequence name for.
2937
     *
2938
     * @return string
2939
     *
2940
     * @throws DBALException If not supported on this platform.
2941
     */
2942 46618
    public function getIdentitySequenceName($tableName, $columnName)
2943
    {
2944 46618
        throw DBALException::notSupported(__METHOD__);
2945
    }
2946
2947
    /**
2948
     * Whether the platform supports indexes.
2949
     *
2950
     * @return bool
2951
     */
2952 17160
    public function supportsIndexes()
2953
    {
2954 17160
        return true;
2955
    }
2956
2957
    /**
2958
     * Whether the platform supports partial indexes.
2959
     *
2960
     * @return bool
2961
     */
2962 51971
    public function supportsPartialIndexes()
2963
    {
2964 51971
        return false;
2965
    }
2966
2967
    /**
2968
     * Whether the platform supports indexes with column length definitions.
2969
     */
2970 51697
    public function supportsColumnLengthIndexes() : bool
2971
    {
2972 51697
        return false;
2973
    }
2974
2975
    /**
2976
     * Whether the platform supports altering tables.
2977
     *
2978
     * @return bool
2979
     */
2980 51260
    public function supportsAlterTable()
2981
    {
2982 51260
        return true;
2983
    }
2984
2985
    /**
2986
     * Whether the platform supports transactions.
2987
     *
2988
     * @return bool
2989
     */
2990 17186
    public function supportsTransactions()
2991
    {
2992 17186
        return true;
2993
    }
2994
2995
    /**
2996
     * Whether the platform supports savepoints.
2997
     *
2998
     * @return bool
2999
     */
3000 54535
    public function supportsSavepoints()
3001
    {
3002 54535
        return true;
3003
    }
3004
3005
    /**
3006
     * Whether the platform supports releasing savepoints.
3007
     *
3008
     * @return bool
3009
     */
3010 46689
    public function supportsReleaseSavepoints()
3011
    {
3012 46689
        return $this->supportsSavepoints();
3013
    }
3014
3015
    /**
3016
     * Whether the platform supports primary key constraints.
3017
     *
3018
     * @return bool
3019
     */
3020 17264
    public function supportsPrimaryConstraints()
3021
    {
3022 17264
        return true;
3023
    }
3024
3025
    /**
3026
     * Whether the platform supports foreign key constraints.
3027
     *
3028
     * @return bool
3029
     */
3030 53088
    public function supportsForeignKeyConstraints()
3031
    {
3032 53088
        return true;
3033
    }
3034
3035
    /**
3036
     * Whether this platform supports onUpdate in foreign key constraints.
3037
     *
3038
     * @return bool
3039
     */
3040 52986
    public function supportsForeignKeyOnUpdate()
3041
    {
3042 52986
        return $this->supportsForeignKeyConstraints();
3043
    }
3044
3045
    /**
3046
     * Whether the platform supports database schemas.
3047
     *
3048
     * @return bool
3049
     */
3050 39173
    public function supportsSchemas()
3051
    {
3052 39173
        return false;
3053
    }
3054
3055
    /**
3056
     * Whether this platform can emulate schemas.
3057
     *
3058
     * Platforms that either support or emulate schemas don't automatically
3059
     * filter a schema for the namespaced elements in {@link
3060
     * AbstractManager#createSchema}.
3061
     *
3062
     * @return bool
3063
     */
3064 16978
    public function canEmulateSchemas()
3065
    {
3066 16978
        return false;
3067
    }
3068
3069
    /**
3070
     * Returns the default schema name.
3071
     *
3072
     * @return string
3073
     *
3074
     * @throws DBALException If not supported on this platform.
3075
     */
3076
    public function getDefaultSchemaName()
3077
    {
3078
        throw DBALException::notSupported(__METHOD__);
3079
    }
3080
3081
    /**
3082
     * Whether this platform supports create database.
3083
     *
3084
     * Some databases don't allow to create and drop databases at all or only with certain tools.
3085
     *
3086
     * @return bool
3087
     */
3088 47097
    public function supportsCreateDropDatabase()
3089
    {
3090 47097
        return true;
3091
    }
3092
3093
    /**
3094
     * Whether the platform supports getting the affected rows of a recent update/delete type query.
3095
     *
3096
     * @return bool
3097
     */
3098 17056
    public function supportsGettingAffectedRows()
3099
    {
3100 17056
        return true;
3101
    }
3102
3103
    /**
3104
     * Whether this platform support to add inline column comments as postfix.
3105
     *
3106
     * @return bool
3107
     */
3108 51253
    public function supportsInlineColumnComments()
3109
    {
3110 51253
        return false;
3111
    }
3112
3113
    /**
3114
     * Whether this platform support the proprietary syntax "COMMENT ON asset".
3115
     *
3116
     * @return bool
3117
     */
3118 52394
    public function supportsCommentOnStatement()
3119
    {
3120 52394
        return false;
3121
    }
3122
3123
    /**
3124
     * Does this platform have native guid type.
3125
     *
3126
     * @return bool
3127
     */
3128 53000
    public function hasNativeGuidType()
3129
    {
3130 53000
        return false;
3131
    }
3132
3133
    /**
3134
     * Does this platform have native JSON type.
3135
     *
3136
     * @return bool
3137
     */
3138 52736
    public function hasNativeJsonType()
3139
    {
3140 52736
        return false;
3141
    }
3142
3143
    /**
3144
     * Whether this platform supports views.
3145
     *
3146
     * @return bool
3147
     */
3148 51224
    public function supportsViews()
3149
    {
3150 51224
        return true;
3151
    }
3152
3153
    /**
3154
     * Does this platform support column collation?
3155
     *
3156
     * @return bool
3157
     */
3158
    public function supportsColumnCollation()
3159
    {
3160
        return false;
3161
    }
3162
3163
    /**
3164
     * Gets the format string, as accepted by the date() function, that describes
3165
     * the format of a stored datetime value of this platform.
3166
     *
3167
     * @return string The format string.
3168
     */
3169 46910
    public function getDateTimeFormatString()
3170
    {
3171 46910
        return 'Y-m-d H:i:s';
3172
    }
3173
3174
    /**
3175
     * Gets the format string, as accepted by the date() function, that describes
3176
     * the format of a stored datetime with timezone value of this platform.
3177
     *
3178
     * @return string The format string.
3179
     */
3180 30944
    public function getDateTimeTzFormatString()
3181
    {
3182 30944
        return 'Y-m-d H:i:s';
3183
    }
3184
3185
    /**
3186
     * Gets the format string, as accepted by the date() function, that describes
3187
     * the format of a stored date value of this platform.
3188
     *
3189
     * @return string The format string.
3190
     */
3191 42119
    public function getDateFormatString()
3192
    {
3193 42119
        return 'Y-m-d';
3194
    }
3195
3196
    /**
3197
     * Gets the format string, as accepted by the date() function, that describes
3198
     * the format of a stored time value of this platform.
3199
     *
3200
     * @return string The format string.
3201
     */
3202 41901
    public function getTimeFormatString()
3203
    {
3204 41901
        return 'H:i:s';
3205
    }
3206
3207
    /**
3208
     * Adds an driver-specific LIMIT clause to the query.
3209
     *
3210
     * @throws DBALException
3211
     */
3212 52884
    final public function modifyLimitQuery(string $query, ?int $limit, int $offset = 0) : string
3213
    {
3214 52884
        if ($offset < 0) {
3215
            throw new DBALException(sprintf(
3216
                'Offset must be a positive integer or zero, %d given',
3217
                $offset
3218
            ));
3219
        }
3220
3221 52884
        if ($offset > 0 && ! $this->supportsLimitOffset()) {
3222
            throw new DBALException(sprintf(
3223
                'Platform %s does not support offset values in limit queries.',
3224
                $this->getName()
3225
            ));
3226
        }
3227
3228 52884
        return $this->doModifyLimitQuery($query, $limit, $offset);
3229
    }
3230
3231
    /**
3232
     * Adds an platform-specific LIMIT clause to the query.
3233
     */
3234 36099
    protected function doModifyLimitQuery(string $query, ?int $limit, int $offset) : string
3235
    {
3236 36099
        if ($limit !== null) {
3237 36099
            $query .= sprintf(' LIMIT %d', $limit);
3238
        }
3239
3240 36099
        if ($offset > 0) {
3241 16699
            $query .= sprintf(' OFFSET %d', $offset);
3242
        }
3243
3244 36099
        return $query;
3245
    }
3246
3247
    /**
3248
     * Whether the database platform support offsets in modify limit clauses.
3249
     *
3250
     * @return bool
3251
     */
3252 52284
    public function supportsLimitOffset()
3253
    {
3254 52284
        return true;
3255
    }
3256
3257
    /**
3258
     * Gets the character casing of a column in an SQL result set of this platform.
3259
     *
3260
     * @param string $column The column name for which to get the correct character casing.
3261
     *
3262
     * @return string The column name in the character casing used in SQL result sets.
3263
     */
3264
    public function getSQLResultCasing($column)
3265
    {
3266
        return $column;
3267
    }
3268
3269
    /**
3270
     * Makes any fixes to a name of a schema element (table, sequence, ...) that are required
3271
     * by restrictions of the platform, like a maximum length.
3272
     *
3273
     * @param string $schemaElementName
3274
     *
3275
     * @return string
3276
     */
3277
    public function fixSchemaElementName($schemaElementName)
3278
    {
3279
        return $schemaElementName;
3280
    }
3281
3282
    /**
3283
     * Maximum length of any given database identifier, like tables or column names.
3284
     *
3285
     * @return int
3286
     */
3287 52528
    public function getMaxIdentifierLength()
3288
    {
3289 52528
        return 63;
3290
    }
3291
3292
    /**
3293
     * Returns the insert SQL for an empty insert statement.
3294
     *
3295
     * @param string $tableName
3296
     * @param string $identifierColumnName
3297
     *
3298
     * @return string
3299
     */
3300 26404
    public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName)
3301
    {
3302 26404
        return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (null)';
3303
    }
3304
3305
    /**
3306
     * Generates a Truncate Table SQL statement for a given table.
3307
     *
3308
     * Cascade is not supported on many platforms but would optionally cascade the truncate by
3309
     * following the foreign keys.
3310
     *
3311
     * @param string $tableName
3312
     * @param bool   $cascade
3313
     *
3314
     * @return string
3315
     */
3316 48755
    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

3316
    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...
3317
    {
3318 48755
        $tableIdentifier = new Identifier($tableName);
3319
3320 48755
        return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this);
3321
    }
3322
3323
    /**
3324
     * This is for test reasons, many vendors have special requirements for dummy statements.
3325
     */
3326 50981
    public function getDummySelectSQL(string $expression = '1') : string
3327
    {
3328 50981
        return sprintf('SELECT %s', $expression);
3329
    }
3330
3331
    /**
3332
     * Returns the SQL to create a new savepoint.
3333
     *
3334
     * @param string $savepoint
3335
     *
3336
     * @return string
3337
     */
3338 42741
    public function createSavePoint($savepoint)
3339
    {
3340 42741
        return 'SAVEPOINT ' . $savepoint;
3341
    }
3342
3343
    /**
3344
     * Returns the SQL to release a savepoint.
3345
     *
3346
     * @param string $savepoint
3347
     *
3348
     * @return string
3349
     */
3350 42741
    public function releaseSavePoint($savepoint)
3351
    {
3352 42741
        return 'RELEASE SAVEPOINT ' . $savepoint;
3353
    }
3354
3355
    /**
3356
     * Returns the SQL to rollback a savepoint.
3357
     *
3358
     * @param string $savepoint
3359
     *
3360
     * @return string
3361
     */
3362 42741
    public function rollbackSavePoint($savepoint)
3363
    {
3364 42741
        return 'ROLLBACK TO SAVEPOINT ' . $savepoint;
3365
    }
3366
3367
    /**
3368
     * Returns the keyword list instance of this platform.
3369
     *
3370
     * @return KeywordList
3371
     *
3372
     * @throws DBALException If no keyword list is specified.
3373
     */
3374 55351
    final public function getReservedKeywordsList()
3375
    {
3376
        // Check for an existing instantiation of the keywords class.
3377 55351
        if ($this->_keywords) {
3378 55351
            return $this->_keywords;
3379
        }
3380
3381 55351
        $class    = $this->getReservedKeywordsClass();
3382 55351
        $keywords = new $class();
3383 55351
        if (! $keywords instanceof KeywordList) {
3384
            throw DBALException::notSupported(__METHOD__);
3385
        }
3386
3387
        // Store the instance so it doesn't need to be generated on every request.
3388 55351
        $this->_keywords = $keywords;
3389
3390 55351
        return $keywords;
3391
    }
3392
3393
    /**
3394
     * Returns the class name of the reserved keywords list.
3395
     *
3396
     * @return string
3397
     *
3398
     * @throws DBALException If not supported on this platform.
3399
     */
3400
    protected function getReservedKeywordsClass()
3401
    {
3402
        throw DBALException::notSupported(__METHOD__);
3403
    }
3404
3405
    /**
3406
     * Quotes a literal string.
3407
     * This method is NOT meant to fix SQL injections!
3408
     * It is only meant to escape this platform's string literal
3409
     * quote character inside the given literal string.
3410
     *
3411
     * @param string $str The literal string to be quoted.
3412
     *
3413
     * @return string The quoted literal string.
3414
     */
3415 52602
    public function quoteStringLiteral($str)
3416
    {
3417 52602
        $c = $this->getStringLiteralQuoteCharacter();
3418
3419 52602
        return $c . str_replace($c, $c . $c, $str) . $c;
3420
    }
3421
3422
    /**
3423
     * Gets the character used for string literal quoting.
3424
     *
3425
     * @return string
3426
     */
3427 52602
    public function getStringLiteralQuoteCharacter()
3428
    {
3429 52602
        return "'";
3430
    }
3431
3432
    /**
3433
     * Escapes metacharacters in a string intended to be used with a LIKE
3434
     * operator.
3435
     *
3436
     * @param string $inputString a literal, unquoted string
3437
     * @param string $escapeChar  should be reused by the caller in the LIKE
3438
     *                            expression.
3439
     */
3440 53084
    final public function escapeStringForLike(string $inputString, string $escapeChar) : string
3441
    {
3442 53084
        return preg_replace(
3443 53084
            '~([' . preg_quote($this->getLikeWildcardCharacters() . $escapeChar, '~') . '])~u',
3444 53084
            addcslashes($escapeChar, '\\') . '$1',
3445 53084
            $inputString
3446
        );
3447
    }
3448
3449 53084
    protected function getLikeWildcardCharacters() : string
3450
    {
3451 53084
        return '%_';
3452
    }
3453
}
3454