Failed Conditions
Pull Request — develop (#3581)
by Jonathan
12:44
created

AbstractPlatform::getCreateTableSQL()   F

Complexity

Conditions 23
Paths 361

Size

Total Lines 96
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 51
CRAP Score 23.0909

Importance

Changes 0
Metric Value
eloc 53
dl 0
loc 96
ccs 51
cts 54
cp 0.9444
rs 1.3208
c 0
b 0
f 0
cc 23
nc 361
nop 2
crap 23.0909

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

245
    protected function getVarcharTypeDeclarationSQLSnippet(int $length, /** @scrutinizer ignore-unused */ bool $fixed) : 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...
246
    {
247
        throw NotSupported::new('VARCHARs not supported by Platform.');
248
    }
249
250
    /**
251
     * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
252
     *
253
     * @param int  $length The length of the column.
254
     * @param bool $fixed  Whether the column length is fixed.
255
     *
256
     * @throws DBALException If not supported on this platform.
257
     */
258
    protected function getBinaryTypeDeclarationSQLSnippet(int $length, bool $fixed) : string
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

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

712
    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...
713
    {
714
        throw NotSupported::new(__METHOD__);
715
    }
716
717
    /**
718
     * Returns the SQL snippet to get the current system date.
719
     */
720
    public function getNowExpression() : string
721
    {
722
        return 'NOW()';
723
    }
724
725
    /**
726
     * Returns an SQL snippet to get a substring inside the string.
727
     *
728
     * Note: Not SQL92, but common functionality.
729
     *
730
     * @param string      $string SQL expression producing the string from which a substring should be extracted.
731
     * @param string      $start  SQL expression producing the position to start at,
732
     * @param string|null $length SQL expression producing the length of the substring portion to be returned.
733
     *                            By default, the entire substring is returned.
734
     */
735 38487
    public function getSubstringExpression(string $string, string $start, ?string $length = null) : string
736
    {
737 38487
        if ($length === null) {
738 38487
            return sprintf('SUBSTRING(%s FROM %s)', $string, $start);
739
        }
740
741 38461
        return sprintf('SUBSTRING(%s FROM %s FOR %s)', $string, $start, $length);
742
    }
743
744
    /**
745
     * Returns a SQL snippet to concatenate the given strings.
746
     *
747
     * @param string[] ...$string
748
     */
749 27810
    public function getConcatExpression(string ...$string) : string
750
    {
751 27810
        return implode(' || ', $string);
752
    }
753
754
    /**
755
     * Returns the SQL for a logical not.
756
     *
757
     * @param string $value SQL expression producing the value to negate.
758
     */
759
    public function getNotExpression(string $value) : string
760
    {
761
        return 'NOT(' . $value . ')';
762
    }
763
764
    /**
765
     * Returns the SQL that checks if an expression is null.
766
     *
767
     * @param string $value SQL expression producing the to be compared to null.
768
     */
769 43873
    public function getIsNullExpression(string $value) : string
770
    {
771 43873
        return $value . ' IS NULL';
772
    }
773
774
    /**
775
     * Returns the SQL that checks if an expression is not null.
776
     *
777
     * @param string $value SQL expression producing the to be compared to null.
778
     */
779
    public function getIsNotNullExpression(string $value) : string
780
    {
781
        return $value . ' IS NOT NULL';
782
    }
783
784
    /**
785
     * Returns the SQL that checks if an expression evaluates to a value between two values.
786
     *
787
     * The parameter $value is checked if it is between $min and $max.
788
     *
789
     * Note: There is a slight difference in the way BETWEEN works on some databases.
790
     * http://www.w3schools.com/sql/sql_between.asp. If you want complete database
791
     * independence you should avoid using between().
792
     *
793
     * @param string $value SQL expression producing the value to compare.
794
     * @param string $min   SQL expression producing the lower value to compare with.
795
     * @param string $max   SQL expression producing the higher value to compare with.
796
     */
797
    public function getBetweenExpression(string $value, string $min, string $max) : string
798
    {
799
        return $value . ' BETWEEN ' . $min . ' AND ' . $max;
800
    }
801
802
    /**
803
     * Returns the SQL to get the arccosine of a value.
804
     *
805
     * @param string $number SQL expression producing the number.
806
     */
807
    public function getAcosExpression(string $number) : string
808
    {
809
        return 'ACOS(' . $number . ')';
810
    }
811
812
    /**
813
     * Returns the SQL to get the sine of a value.
814
     *
815
     * @param string $number SQL expression producing the number.
816
     */
817
    public function getSinExpression(string $number) : string
818
    {
819
        return 'SIN(' . $number . ')';
820
    }
821
822
    /**
823
     * Returns the SQL to get the PI value.
824
     */
825
    public function getPiExpression() : string
826
    {
827
        return 'PI()';
828
    }
829
830
    /**
831
     * Returns the SQL to get the cosine of a value.
832
     *
833
     * @param string $number SQL expression producing the number.
834
     */
835
    public function getCosExpression(string $number) : string
836
    {
837
        return 'COS(' . $number . ')';
838
    }
839
840
    /**
841
     * Returns the SQL to calculate the difference in days between the two passed dates.
842
     *
843
     * Computes diff = date1 - date2.
844
     *
845
     * @throws DBALException If not supported on this platform.
846
     */
847
    public function getDateDiffExpression(string $date1, string $date2) : string
0 ignored issues
show
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

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

847
    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...
848
    {
849
        throw NotSupported::new(__METHOD__);
850
    }
851
852
    /**
853
     * Returns the SQL to add the number of given seconds to a date.
854
     *
855
     * @param string $date    SQL expression producing the date.
856
     * @param string $seconds SQL expression producing the number of seconds.
857
     *
858
     * @throws DBALException If not supported on this platform.
859
     */
860 42811
    public function getDateAddSecondsExpression(string $date, string $seconds) : string
861
    {
862 42811
        return $this->getDateArithmeticIntervalExpression($date, '+', $seconds, DateIntervalUnit::SECOND);
863
    }
864
865
    /**
866
     * Returns the SQL to subtract the number of given seconds from a date.
867
     *
868
     * @param string $date    SQL expression producing the date.
869
     * @param string $seconds SQL expression producing the number of seconds.
870
     *
871
     * @throws DBALException If not supported on this platform.
872
     */
873 42761
    public function getDateSubSecondsExpression(string $date, string $seconds) : string
874
    {
875 42761
        return $this->getDateArithmeticIntervalExpression($date, '-', $seconds, DateIntervalUnit::SECOND);
876
    }
877
878
    /**
879
     * Returns the SQL to add the number of given minutes to a date.
880
     *
881
     * @param string $date    SQL expression producing the date.
882
     * @param string $minutes SQL expression producing the number of minutes.
883
     *
884
     * @throws DBALException If not supported on this platform.
885
     */
886 42711
    public function getDateAddMinutesExpression(string $date, string $minutes) : string
887
    {
888 42711
        return $this->getDateArithmeticIntervalExpression($date, '+', $minutes, DateIntervalUnit::MINUTE);
889
    }
890
891
    /**
892
     * Returns the SQL to subtract the number of given minutes from a date.
893
     *
894
     * @param string $date    SQL expression producing the date.
895
     * @param string $minutes SQL expression producing the number of minutes.
896
     *
897
     * @throws DBALException If not supported on this platform.
898
     */
899 42661
    public function getDateSubMinutesExpression(string $date, string $minutes) : string
900
    {
901 42661
        return $this->getDateArithmeticIntervalExpression($date, '-', $minutes, DateIntervalUnit::MINUTE);
902
    }
903
904
    /**
905
     * Returns the SQL to add the number of given hours to a date.
906
     *
907
     * @param string $date  SQL expression producing the date.
908
     * @param string $hours SQL expression producing the number of hours.
909
     *
910
     * @throws DBALException If not supported on this platform.
911
     */
912 42611
    public function getDateAddHourExpression(string $date, string $hours) : string
913
    {
914 42611
        return $this->getDateArithmeticIntervalExpression($date, '+', $hours, DateIntervalUnit::HOUR);
915
    }
916
917
    /**
918
     * Returns the SQL to subtract the number of given hours to a date.
919
     *
920
     * @param string $date  SQL expression producing the date.
921
     * @param string $hours SQL expression producing the number of hours.
922
     *
923
     * @throws DBALException If not supported on this platform.
924
     */
925 42561
    public function getDateSubHourExpression(string $date, string $hours) : string
926
    {
927 42561
        return $this->getDateArithmeticIntervalExpression($date, '-', $hours, DateIntervalUnit::HOUR);
928
    }
929
930
    /**
931
     * Returns the SQL to add the number of given days to a date.
932
     *
933
     * @param string $date SQL expression producing the date.
934
     * @param string $days SQL expression producing the number of days.
935
     *
936
     * @throws DBALException If not supported on this platform.
937
     */
938 42513
    public function getDateAddDaysExpression(string $date, string $days) : string
939
    {
940 42513
        return $this->getDateArithmeticIntervalExpression($date, '+', $days, DateIntervalUnit::DAY);
941
    }
942
943
    /**
944
     * Returns the SQL to subtract the number of given days to a date.
945
     *
946
     * @param string $date SQL expression producing the date.
947
     * @param string $days SQL expression producing the number of days.
948
     *
949
     * @throws DBALException If not supported on this platform.
950
     */
951 42461
    public function getDateSubDaysExpression(string $date, string $days) : string
952
    {
953 42461
        return $this->getDateArithmeticIntervalExpression($date, '-', $days, DateIntervalUnit::DAY);
954
    }
955
956
    /**
957
     * Returns the SQL to add the number of given weeks to a date.
958
     *
959
     * @param string $date  SQL expression producing the date.
960
     * @param string $weeks SQL expression producing the number of weeks.
961
     *
962
     * @throws DBALException If not supported on this platform.
963
     */
964 42411
    public function getDateAddWeeksExpression(string $date, string $weeks) : string
965
    {
966 42411
        return $this->getDateArithmeticIntervalExpression($date, '+', $weeks, DateIntervalUnit::WEEK);
967
    }
968
969
    /**
970
     * Returns the SQL to subtract the number of given weeks from a date.
971
     *
972
     * @param string $date  SQL expression producing the date.
973
     * @param string $weeks SQL expression producing the number of weeks.
974
     *
975
     * @throws DBALException If not supported on this platform.
976
     */
977 42361
    public function getDateSubWeeksExpression(string $date, string $weeks) : string
978
    {
979 42361
        return $this->getDateArithmeticIntervalExpression($date, '-', $weeks, DateIntervalUnit::WEEK);
980
    }
981
982
    /**
983
     * Returns the SQL to add the number of given months to a date.
984
     *
985
     * @param string $date   SQL expression producing the date.
986
     * @param string $months SQL expression producing the number of months.
987
     *
988
     * @throws DBALException If not supported on this platform.
989
     */
990 42311
    public function getDateAddMonthExpression(string $date, string $months) : string
991
    {
992 42311
        return $this->getDateArithmeticIntervalExpression($date, '+', $months, DateIntervalUnit::MONTH);
993
    }
994
995
    /**
996
     * Returns the SQL to subtract the number of given months to a date.
997
     *
998
     * @param string $date   SQL expression producing the date.
999
     * @param string $months SQL expression producing the number of months.
1000
     *
1001
     * @throws DBALException If not supported on this platform.
1002
     */
1003 42261
    public function getDateSubMonthExpression(string $date, string $months) : string
1004
    {
1005 42261
        return $this->getDateArithmeticIntervalExpression($date, '-', $months, DateIntervalUnit::MONTH);
1006
    }
1007
1008
    /**
1009
     * Returns the SQL to add the number of given quarters to a date.
1010
     *
1011
     * @param string $date     SQL expression producing the date.
1012
     * @param string $quarters SQL expression producing the number of quarters.
1013
     *
1014
     * @throws DBALException If not supported on this platform.
1015
     */
1016 42211
    public function getDateAddQuartersExpression(string $date, string $quarters) : string
1017
    {
1018 42211
        return $this->getDateArithmeticIntervalExpression($date, '+', $quarters, DateIntervalUnit::QUARTER);
1019
    }
1020
1021
    /**
1022
     * Returns the SQL to subtract the number of given quarters from a date.
1023
     *
1024
     * @param string $date     SQL expression producing the date.
1025
     * @param string $quarters SQL expression producing the number of quarters.
1026
     *
1027
     * @throws DBALException If not supported on this platform.
1028
     */
1029 42161
    public function getDateSubQuartersExpression(string $date, string $quarters) : string
1030
    {
1031 42161
        return $this->getDateArithmeticIntervalExpression($date, '-', $quarters, DateIntervalUnit::QUARTER);
1032
    }
1033
1034
    /**
1035
     * Returns the SQL to add the number of given years to a date.
1036
     *
1037
     * @param string $date  SQL expression producing the date.
1038
     * @param string $years SQL expression producing the number of years.
1039
     *
1040
     * @throws DBALException If not supported on this platform.
1041
     */
1042 42111
    public function getDateAddYearsExpression(string $date, string $years) : string
1043
    {
1044 42111
        return $this->getDateArithmeticIntervalExpression($date, '+', $years, DateIntervalUnit::YEAR);
1045
    }
1046
1047
    /**
1048
     * Returns the SQL to subtract the number of given years from a date.
1049
     *
1050
     * @param string $date  SQL expression producing the date.
1051
     * @param string $years SQL expression producing the number of years.
1052
     *
1053
     * @throws DBALException If not supported on this platform.
1054
     */
1055 42061
    public function getDateSubYearsExpression(string $date, string $years) : string
1056
    {
1057 42061
        return $this->getDateArithmeticIntervalExpression($date, '-', $years, DateIntervalUnit::YEAR);
1058
    }
1059
1060
    /**
1061
     * Returns the SQL for a date arithmetic expression.
1062
     *
1063
     * @param string $date     SQL expression representing a date to perform the arithmetic operation on.
1064
     * @param string $operator The arithmetic operator (+ or -).
1065
     * @param string $interval SQL expression representing the value of the interval that shall be calculated
1066
     *                         into the date.
1067
     * @param string $unit     The unit of the interval that shall be calculated into the date.
1068
     *                         One of the DATE_INTERVAL_UNIT_* constants.
1069
     *
1070
     * @throws DBALException If not supported on this platform.
1071
     */
1072
    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

1072
    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

1072
    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...
1073
    {
1074
        throw NotSupported::new(__METHOD__);
1075
    }
1076
1077
    /**
1078
     * Generates the SQL expression which represents the given date interval multiplied by a number
1079
     *
1080
     * @param string $interval   SQL expression describing the interval value
1081
     * @param int    $multiplier Interval multiplier
1082
     *
1083
     * @throws DBALException
1084
     */
1085 39087
    protected function multiplyInterval(string $interval, int $multiplier) : string
1086
    {
1087 39087
        return sprintf('(%s * %d)', $interval, $multiplier);
1088
    }
1089
1090
    /**
1091
     * Returns the SQL bit AND comparison expression.
1092
     *
1093
     * @param string $value1 SQL expression producing the first value.
1094
     * @param string $value2 SQL expression producing the second value.
1095
     */
1096 41679
    public function getBitAndComparisonExpression(string $value1, string $value2) : string
1097
    {
1098 41679
        return '(' . $value1 . ' & ' . $value2 . ')';
1099
    }
1100
1101
    /**
1102
     * Returns the SQL bit OR comparison expression.
1103
     *
1104
     * @param string $value1 SQL expression producing the first value.
1105
     * @param string $value2 SQL expression producing the second value.
1106
     */
1107 40331
    public function getBitOrComparisonExpression(string $value1, string $value2) : string
1108
    {
1109 40331
        return '(' . $value1 . ' | ' . $value2 . ')';
1110
    }
1111
1112
    /**
1113
     * Returns the FOR UPDATE expression.
1114
     */
1115 30827
    public function getForUpdateSQL() : string
1116
    {
1117 30827
        return 'FOR UPDATE';
1118
    }
1119
1120
    /**
1121
     * Honors that some SQL vendors such as MsSql use table hints for locking instead of the ANSI SQL FOR UPDATE specification.
1122
     *
1123
     * @param string   $fromClause The FROM clause to append the hint for the given lock mode to.
1124
     * @param int|null $lockMode   One of the Doctrine\DBAL\LockMode::* constants. If null is given, nothing will
1125
     *                             be appended to the FROM clause.
1126
     */
1127 32368
    public function appendLockHint(string $fromClause, ?int $lockMode) : string
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

1127
    public function appendLockHint(string $fromClause, /** @scrutinizer ignore-unused */ ?int $lockMode) : 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...
1128
    {
1129 32368
        return $fromClause;
1130
    }
1131
1132
    /**
1133
     * Returns the SQL snippet to append to any SELECT statement which locks rows in shared read lock.
1134
     *
1135
     * This defaults to the ANSI SQL "FOR UPDATE", which is an exclusive lock (Write). Some database
1136
     * vendors allow to lighten this constraint up to be a real read lock.
1137
     */
1138
    public function getReadLockSQL() : string
1139
    {
1140
        return $this->getForUpdateSQL();
1141
    }
1142
1143
    /**
1144
     * Returns the SQL snippet to append to any SELECT statement which obtains an exclusive lock on the rows.
1145
     *
1146
     * The semantics of this lock mode should equal the SELECT .. FOR UPDATE of the ANSI SQL standard.
1147
     */
1148 35451
    public function getWriteLockSQL() : string
1149
    {
1150 35451
        return $this->getForUpdateSQL();
1151
    }
1152
1153
    /**
1154
     * Returns the SQL snippet to drop an existing database.
1155
     *
1156
     * @param string $database The name of the database that should be dropped.
1157
     */
1158 28438
    public function getDropDatabaseSQL(string $database) : string
1159
    {
1160 28438
        return 'DROP DATABASE ' . $database;
1161
    }
1162
1163
    /**
1164
     * Returns the SQL snippet to drop an existing table.
1165
     *
1166
     * @param Table|string $table
1167
     *
1168
     * @throws InvalidArgumentException
1169
     */
1170 43644
    public function getDropTableSQL($table) : string
1171
    {
1172 43644
        $tableArg = $table;
1173
1174 43644
        if ($table instanceof Table) {
1175 7337
            $table = $table->getQuotedName($this);
1176
        }
1177
1178 43644
        if (! is_string($table)) {
0 ignored issues
show
introduced by
The condition is_string($table) is always true.
Loading history...
1179
            throw new InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.');
1180
        }
1181
1182 43644
        if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaDropTable)) {
1183 36362
            $eventArgs = new SchemaDropTableEventArgs($tableArg, $this);
1184 36362
            $this->_eventManager->dispatchEvent(Events::onSchemaDropTable, $eventArgs);
1185
1186 36362
            if ($eventArgs->isDefaultPrevented()) {
1187
                $sql = $eventArgs->getSql();
1188
1189
                if ($sql === null) {
1190
                    throw new UnexpectedValueException('Default implementation of DROP TABLE was overridden with NULL.');
1191
                }
1192
1193
                return $sql;
1194
            }
1195
        }
1196
1197 43644
        return 'DROP TABLE ' . $table;
1198
    }
1199
1200
    /**
1201
     * Returns the SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction.
1202
     *
1203
     * @param Table|string $table
1204
     */
1205 16938
    public function getDropTemporaryTableSQL($table) : string
1206
    {
1207 16938
        return $this->getDropTableSQL($table);
1208
    }
1209
1210
    /**
1211
     * Returns the SQL to drop an index from a table.
1212
     *
1213
     * @param Index|string $index
1214
     * @param Table|string $table
1215
     *
1216
     * @throws InvalidArgumentException
1217
     */
1218 29038
    public function getDropIndexSQL($index, $table = null) : string
1219
    {
1220 29038
        if ($index instanceof Index) {
1221 29011
            $index = $index->getQuotedName($this);
1222 14326
        } elseif (! is_string($index)) {
0 ignored issues
show
introduced by
The condition is_string($index) is always true.
Loading history...
1223
            throw new InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.');
1224
        }
1225
1226 29038
        return 'DROP INDEX ' . $index;
1227
    }
1228
1229
    /**
1230
     * Returns the SQL to drop a constraint.
1231
     *
1232
     * @param Constraint|string $constraint
1233
     * @param Table|string      $table
1234
     */
1235 36721
    public function getDropConstraintSQL($constraint, $table) : string
1236
    {
1237 36721
        if (! $constraint instanceof Constraint) {
1238 35734
            $constraint = new Identifier($constraint);
1239
        }
1240
1241 36721
        if (! $table instanceof Table) {
1242 36721
            $table = new Identifier($table);
1243
        }
1244
1245 36721
        $constraint = $constraint->getQuotedName($this);
1246 36721
        $table      = $table->getQuotedName($this);
1247
1248 36721
        return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $constraint;
1249
    }
1250
1251
    /**
1252
     * Returns the SQL to drop a foreign key.
1253
     *
1254
     * @param ForeignKeyConstraint|string $foreignKey
1255
     * @param Table|string                $table
1256
     */
1257 38031
    public function getDropForeignKeySQL($foreignKey, $table) : string
1258
    {
1259 38031
        if (! $foreignKey instanceof ForeignKeyConstraint) {
1260 35630
            $foreignKey = new Identifier($foreignKey);
1261
        }
1262
1263 38031
        if (! $table instanceof Table) {
1264 38031
            $table = new Identifier($table);
1265
        }
1266
1267 38031
        $foreignKey = $foreignKey->getQuotedName($this);
1268 38031
        $table      = $table->getQuotedName($this);
1269
1270 38031
        return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $foreignKey;
1271
    }
1272
1273
    /**
1274
     * Returns the SQL statement(s) to create a table with the specified name, columns and constraints
1275
     * on this platform.
1276
     *
1277
     * @return array<int, string> The sequence of SQL statements.
1278
     *
1279
     * @throws DBALException
1280
     */
1281 43833
    public function getCreateTableSQL(Table $table, int $createFlags = self::CREATE_INDEXES) : array
1282
    {
1283 43833
        if (count($table->getColumns()) === 0) {
1284 36662
            throw NoColumnsSpecifiedForTable::new($table->getName());
1285
        }
1286
1287 43821
        $tableName                    = $table->getQuotedName($this);
1288 43821
        $options                      = $table->getOptions();
1289 43821
        $options['uniqueConstraints'] = [];
1290 43821
        $options['indexes']           = [];
1291 43821
        $options['primary']           = [];
1292
1293 43821
        if (($createFlags & self::CREATE_INDEXES) > 0) {
1294 43807
            foreach ($table->getIndexes() as $index) {
1295
                /** @var $index Index */
1296 43702
                if (! $index->isPrimary()) {
1297 41794
                    $options['indexes'][$index->getQuotedName($this)] = $index;
1298
1299 41794
                    continue;
1300
                }
1301
1302 43654
                $options['primary']       = $index->getQuotedColumns($this);
1303 43654
                $options['primary_index'] = $index;
1304
            }
1305
1306 43807
            foreach ($table->getUniqueConstraints() as $uniqueConstraint) {
1307
                /** @var UniqueConstraint $uniqueConstraint */
1308
                $options['uniqueConstraints'][$uniqueConstraint->getQuotedName($this)] = $uniqueConstraint;
1309
            }
1310
        }
1311
1312 43821
        if (($createFlags & self::CREATE_FOREIGNKEYS) > 0) {
1313 43668
            $options['foreignKeys'] = [];
1314
1315 43668
            foreach ($table->getForeignKeys() as $fkConstraint) {
1316 41609
                $options['foreignKeys'][] = $fkConstraint;
1317
            }
1318
        }
1319
1320 43821
        $columnSql = [];
1321 43821
        $columns   = [];
1322
1323 43821
        foreach ($table->getColumns() as $column) {
1324 43821
            if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn)) {
1325 36387
                $eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this);
1326 36387
                $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs);
1327
1328 36387
                $columnSql = array_merge($columnSql, $eventArgs->getSql());
1329
1330 36387
                if ($eventArgs->isDefaultPrevented()) {
1331
                    continue;
1332
                }
1333
            }
1334
1335 43821
            $columnData            = $column->toArray();
1336 43821
            $columnData['name']    = $column->getQuotedName($this);
1337 43821
            $columnData['version'] = $column->hasPlatformOption('version') ? $column->getPlatformOption('version') : false;
1338 43821
            $columnData['comment'] = $this->getColumnComment($column);
1339
1340 43821
            if ($columnData['type'] instanceof Types\StringType && $columnData['length'] === null) {
1341 43291
                $columnData['length'] = 255;
1342
            }
1343
1344 43821
            if (in_array($column->getName(), $options['primary'])) {
1345 43640
                $columnData['primary'] = true;
1346
            }
1347
1348 43821
            $columnName = $columnData['name'];
1349 43821
            assert(is_string($columnName));
1350
1351 43821
            $columns[$columnName] = $columnData;
1352
        }
1353
1354 43821
        if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) {
1355 36387
            $eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this);
1356 36387
            $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs);
1357
1358 36387
            if ($eventArgs->isDefaultPrevented()) {
1359
                return array_merge($eventArgs->getSql(), $columnSql);
1360
            }
1361
        }
1362
1363 43821
        $sql = $this->_getCreateTableSQL($tableName, $columns, $options);
1364 43821
        if ($this->supportsCommentOnStatement()) {
1365 39431
            foreach ($table->getColumns() as $column) {
1366 39431
                $comment = $this->getColumnComment($column);
1367
1368 39431
                if ($comment === null || $comment === '') {
1369 39421
                    continue;
1370
                }
1371
1372 37288
                $sql[] = $this->getCommentOnColumnSQL($tableName, $column->getQuotedName($this), $comment);
1373
            }
1374
        }
1375
1376 43821
        return array_merge($sql, $columnSql);
1377
    }
1378
1379 36506
    public function getCommentOnColumnSQL(string $tableName, string $columnName, ?string $comment) : string
1380
    {
1381 36506
        $tableName  = new Identifier($tableName);
1382 36506
        $columnName = new Identifier($columnName);
1383
1384 36506
        return sprintf(
1385 61
            'COMMENT ON COLUMN %s.%s IS %s',
1386 36506
            $tableName->getQuotedName($this),
1387 36506
            $columnName->getQuotedName($this),
1388 36506
            $this->quoteStringLiteral((string) $comment)
1389
        );
1390
    }
1391
1392
    /**
1393
     * Returns the SQL to create inline comment on a column.
1394
     *
1395
     * @throws DBALException If not supported on this platform.
1396
     */
1397 38223
    public function getInlineColumnCommentSQL(?string $comment) : string
1398
    {
1399 38223
        if (! $this->supportsInlineColumnComments()) {
1400 35508
            throw NotSupported::new(__METHOD__);
1401
        }
1402
1403 37357
        return 'COMMENT ' . $this->quoteStringLiteral((string) $comment);
1404
    }
1405
1406
    /**
1407
     * Returns the SQL used to create a table.
1408
     *
1409
     * @param mixed[][] $columns
1410
     * @param mixed[]   $options
1411
     *
1412
     * @return array<int, string>
1413
     */
1414 37902
    protected function _getCreateTableSQL(string $tableName, array $columns, array $options = []) : array
1415
    {
1416 37902
        $columnListSql = $this->getColumnDeclarationListSQL($columns);
1417
1418 37902
        if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
1419
            foreach ($options['uniqueConstraints'] as $name => $definition) {
1420
                $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
1421
            }
1422
        }
1423
1424 37902
        if (isset($options['primary']) && ! empty($options['primary'])) {
1425 37838
            $columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')';
1426
        }
1427
1428 37902
        if (isset($options['indexes']) && ! empty($options['indexes'])) {
1429
            foreach ($options['indexes'] as $index => $definition) {
1430
                $columnListSql .= ', ' . $this->getIndexDeclarationSQL($index, $definition);
1431
            }
1432
        }
1433
1434 37902
        $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql;
1435
1436 37902
        $check = $this->getCheckDeclarationSQL($columns);
1437 37902
        if (! empty($check)) {
1438 37501
            $query .= ', ' . $check;
1439
        }
1440 37902
        $query .= ')';
1441
1442 37902
        $sql = [$query];
1443
1444 37902
        if (isset($options['foreignKeys'])) {
1445 37849
            foreach ((array) $options['foreignKeys'] as $definition) {
1446 37623
                $sql[] = $this->getCreateForeignKeySQL($definition, $tableName);
1447
            }
1448
        }
1449
1450 37902
        return $sql;
1451
    }
1452
1453 33863
    public function getCreateTemporaryTableSnippetSQL() : string
1454
    {
1455 33863
        return 'CREATE TEMPORARY TABLE';
1456
    }
1457
1458
    /**
1459
     * Returns the SQL to create a sequence on this platform.
1460
     *
1461
     * @throws DBALException If not supported on this platform.
1462
     */
1463
    public function getCreateSequenceSQL(Sequence $sequence) : string
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

1463
    public function getCreateSequenceSQL(/** @scrutinizer ignore-unused */ Sequence $sequence) : 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...
1464
    {
1465
        throw NotSupported::new(__METHOD__);
1466
    }
1467
1468
    /**
1469
     * Returns the SQL to change a sequence on this platform.
1470
     *
1471
     * @throws DBALException If not supported on this platform.
1472
     */
1473
    public function getAlterSequenceSQL(Sequence $sequence) : string
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

1473
    public function getAlterSequenceSQL(/** @scrutinizer ignore-unused */ Sequence $sequence) : 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...
1474
    {
1475
        throw NotSupported::new(__METHOD__);
1476
    }
1477
1478
    /**
1479
     * Returns the SQL to create a constraint on a table on this platform.
1480
     *
1481
     * @param Table|string $table
1482
     *
1483
     * @throws InvalidArgumentException
1484
     */
1485 36537
    public function getCreateConstraintSQL(Constraint $constraint, $table) : string
1486
    {
1487 36537
        if ($table instanceof Table) {
1488
            $table = $table->getQuotedName($this);
1489
        }
1490
1491 36537
        $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this);
1492
1493 36537
        $columnList = '(' . implode(', ', $constraint->getQuotedColumns($this)) . ')';
1494
1495 36537
        $referencesClause = '';
1496 36537
        if ($constraint instanceof Index) {
1497 36537
            if ($constraint->isPrimary()) {
1498 36537
                $query .= ' PRIMARY KEY';
1499 36510
            } elseif ($constraint->isUnique()) {
1500 36510
                $query .= ' UNIQUE';
1501
            } else {
1502
                throw new InvalidArgumentException(
1503 36537
                    'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().'
1504
                );
1505
            }
1506 36510
        } elseif ($constraint instanceof ForeignKeyConstraint) {
1507 36510
            $query .= ' FOREIGN KEY';
1508
1509 36510
            $referencesClause = ' REFERENCES ' . $constraint->getQuotedForeignTableName($this) .
1510 36510
                ' (' . implode(', ', $constraint->getQuotedForeignColumns($this)) . ')';
1511
        }
1512 36537
        $query .= ' ' . $columnList . $referencesClause;
1513
1514 36537
        return $query;
1515
    }
1516
1517
    /**
1518
     * Returns the SQL to create an index on a table on this platform.
1519
     *
1520
     * @param Table|string $table The name of the table on which the index is to be created.
1521
     *
1522
     * @throws InvalidArgumentException
1523
     */
1524 40577
    public function getCreateIndexSQL(Index $index, $table) : string
1525
    {
1526 40577
        if ($table instanceof Table) {
1527 39816
            $table = $table->getQuotedName($this);
1528
        }
1529 40577
        $name    = $index->getQuotedName($this);
1530 40577
        $columns = $index->getColumns();
1531
1532 40577
        if (count($columns) === 0) {
1533
            throw new InvalidArgumentException('Incomplete definition. "columns" required.');
1534
        }
1535
1536 40577
        if ($index->isPrimary()) {
1537 34744
            return $this->getCreatePrimaryKeySQL($index, $table);
1538
        }
1539
1540 40559
        $query  = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table;
1541 40559
        $query .= ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')' . $this->getPartialIndexSQL($index);
1542
1543 40559
        return $query;
1544
    }
1545
1546
    /**
1547
     * Adds condition for partial index.
1548
     */
1549 41860
    protected function getPartialIndexSQL(Index $index) : string
1550
    {
1551 41860
        if ($this->supportsPartialIndexes() && $index->hasOption('where')) {
1552 26554
            return ' WHERE ' . $index->getOption('where');
1553
        }
1554
1555 41857
        return '';
1556
    }
1557
1558
    /**
1559
     * Adds additional flags for index generation.
1560
     */
1561 39055
    protected function getCreateIndexSQLFlags(Index $index) : string
1562
    {
1563 39055
        return $index->isUnique() ? 'UNIQUE ' : '';
1564
    }
1565
1566
    /**
1567
     * Returns the SQL to create an unnamed primary key constraint.
1568
     *
1569
     * @param Table|string $table
1570
     */
1571 37417
    public function getCreatePrimaryKeySQL(Index $index, $table) : string
1572
    {
1573 37417
        if ($table instanceof Table) {
1574
            $table = $table->getQuotedName($this);
1575
        }
1576
1577 37417
        return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index) . ')';
1578
    }
1579
1580
    /**
1581
     * Returns the SQL to create a named schema.
1582
     *
1583
     * @throws DBALException If not supported on this platform.
1584
     */
1585 35907
    public function getCreateSchemaSQL(string $schemaName) : string
1586
    {
1587 35907
        throw NotSupported::new(__METHOD__);
1588
    }
1589
1590
    /**
1591
     * Quotes a string so that it can be safely used as a table or column name,
1592
     * even if it is a reserved word of the platform. This also detects identifier
1593
     * chains separated by dot and quotes them independently.
1594
     *
1595
     * NOTE: Just because you CAN use quoted identifiers doesn't mean
1596
     * you SHOULD use them. In general, they end up causing way more
1597
     * problems than they solve.
1598
     *
1599
     * @param string $identifier The identifier name to be quoted.
1600
     *
1601
     * @return string The quoted identifier string.
1602
     */
1603 43420
    public function quoteIdentifier(string $identifier) : string
1604
    {
1605 43420
        if (strpos($identifier, '.') !== false) {
1606 37457
            $parts = array_map([$this, 'quoteSingleIdentifier'], explode('.', $identifier));
1607
1608 37457
            return implode('.', $parts);
1609
        }
1610
1611 43420
        return $this->quoteSingleIdentifier($identifier);
1612
    }
1613
1614
    /**
1615
     * Quotes a single identifier (no dot chain separation).
1616
     *
1617
     * @param string $str The identifier name to be quoted.
1618
     *
1619
     * @return string The quoted identifier string.
1620
     */
1621 42994
    public function quoteSingleIdentifier(string $str) : string
1622
    {
1623 42994
        $c = $this->getIdentifierQuoteCharacter();
1624
1625 42994
        return $c . str_replace($c, $c . $c, $str) . $c;
1626
    }
1627
1628
    /**
1629
     * Returns the SQL to create a new foreign key.
1630
     *
1631
     * @param ForeignKeyConstraint $foreignKey The foreign key constraint.
1632
     * @param Table|string         $table      The name of the table on which the foreign key is to be created.
1633
     */
1634 41487
    public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) : string
1635
    {
1636 41487
        if ($table instanceof Table) {
1637 2676
            $table = $table->getQuotedName($this);
1638
        }
1639
1640 41487
        return 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey);
1641
    }
1642
1643
    /**
1644
     * Gets the SQL statements for altering an existing table.
1645
     *
1646
     * This method returns an array of SQL statements, since some platforms need several statements.
1647
     *
1648
     * @return array<int, string>
1649
     *
1650
     * @throws DBALException If not supported on this platform.
1651
     */
1652
    public function getAlterTableSQL(TableDiff $diff) : array
1653
    {
1654
        throw NotSupported::new(__METHOD__);
1655
    }
1656
1657
    /**
1658
     * @param mixed[] $columnSql
1659
     */
1660 40695
    protected function onSchemaAlterTableAddColumn(Column $column, TableDiff $diff, array &$columnSql) : bool
1661
    {
1662 40695
        if ($this->_eventManager === null) {
1663 36481
            return false;
1664
        }
1665
1666 40639
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableAddColumn)) {
1667 40627
            return false;
1668
        }
1669
1670 36337
        $eventArgs = new SchemaAlterTableAddColumnEventArgs($column, $diff, $this);
1671 36337
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn, $eventArgs);
1672
1673 36337
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1674
1675 36337
        return $eventArgs->isDefaultPrevented();
1676
    }
1677
1678
    /**
1679
     * @param string[] $columnSql
1680
     */
1681 39816
    protected function onSchemaAlterTableRemoveColumn(Column $column, TableDiff $diff, array &$columnSql) : bool
1682
    {
1683 39816
        if ($this->_eventManager === null) {
1684 36461
            return false;
1685
        }
1686
1687 39780
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRemoveColumn)) {
1688 39768
            return false;
1689
        }
1690
1691 36337
        $eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column, $diff, $this);
1692 36337
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn, $eventArgs);
1693
1694 36337
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1695
1696 36337
        return $eventArgs->isDefaultPrevented();
1697
    }
1698
1699
    /**
1700
     * @param string[] $columnSql
1701
     */
1702 40497
    protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiff, TableDiff $diff, array &$columnSql) : bool
1703
    {
1704 40497
        if ($this->_eventManager === null) {
1705 37337
            return false;
1706
        }
1707
1708 40385
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) {
1709 40373
            return false;
1710
        }
1711
1712 36337
        $eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff, $diff, $this);
1713 36337
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn, $eventArgs);
1714
1715 36337
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1716
1717 36337
        return $eventArgs->isDefaultPrevented();
1718
    }
1719
1720
    /**
1721
     * @param string[] $columnSql
1722
     */
1723 39469
    protected function onSchemaAlterTableRenameColumn(string $oldColumnName, Column $column, TableDiff $diff, array &$columnSql) : bool
1724
    {
1725 39469
        if ($this->_eventManager === null) {
1726 35737
            return false;
1727
        }
1728
1729 39432
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRenameColumn)) {
1730 36514
            return false;
1731
        }
1732
1733 36337
        $eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName, $column, $diff, $this);
1734 36337
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn, $eventArgs);
1735
1736 36337
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1737
1738 36337
        return $eventArgs->isDefaultPrevented();
1739
    }
1740
1741
    /**
1742
     * @param string[] $sql
1743
     */
1744 41150
    protected function onSchemaAlterTable(TableDiff $diff, array &$sql) : bool
1745
    {
1746 41150
        if ($this->_eventManager === null) {
1747 37461
            return false;
1748
        }
1749
1750 40914
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTable)) {
1751 40902
            return false;
1752
        }
1753
1754 36337
        $eventArgs = new SchemaAlterTableEventArgs($diff, $this);
1755 36337
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTable, $eventArgs);
1756
1757 36337
        $sql = array_merge($sql, $eventArgs->getSql());
1758
1759 36337
        return $eventArgs->isDefaultPrevented();
1760
    }
1761
1762
    /**
1763
     * @return string[]
1764
     */
1765 40911
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) : array
1766
    {
1767 40911
        $tableName = $diff->getName($this)->getQuotedName($this);
1768
1769 40911
        $sql = [];
1770 40911
        if ($this->supportsForeignKeyConstraints()) {
1771 40911
            foreach ($diff->removedForeignKeys as $foreignKey) {
1772 39317
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
1773
            }
1774 40911
            foreach ($diff->changedForeignKeys as $foreignKey) {
1775 35439
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
1776
            }
1777
        }
1778
1779 40911
        foreach ($diff->removedIndexes as $index) {
1780 39789
            $sql[] = $this->getDropIndexSQL($index, $tableName);
1781
        }
1782 40911
        foreach ($diff->changedIndexes as $index) {
1783 39361
            $sql[] = $this->getDropIndexSQL($index, $tableName);
1784
        }
1785
1786 40911
        return $sql;
1787
    }
1788
1789
    /**
1790
     * @return string[]
1791
     */
1792 40911
    protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) : array
1793
    {
1794 40911
        $sql     = [];
1795 40911
        $newName = $diff->getNewName();
1796
1797 40911
        if ($newName !== null) {
1798 36756
            $tableName = $newName->getQuotedName($this);
1799
        } else {
1800 40888
            $tableName = $diff->getName($this)->getQuotedName($this);
1801
        }
1802
1803 40911
        if ($this->supportsForeignKeyConstraints()) {
1804 40911
            foreach ($diff->addedForeignKeys as $foreignKey) {
1805 39415
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
1806
            }
1807
1808 40911
            foreach ($diff->changedForeignKeys as $foreignKey) {
1809 35439
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
1810
            }
1811
        }
1812
1813 40911
        foreach ($diff->addedIndexes as $index) {
1814 39363
            $sql[] = $this->getCreateIndexSQL($index, $tableName);
1815
        }
1816
1817 40911
        foreach ($diff->changedIndexes as $index) {
1818 39361
            $sql[] = $this->getCreateIndexSQL($index, $tableName);
1819
        }
1820
1821 40911
        foreach ($diff->renamedIndexes as $oldIndexName => $index) {
1822 39482
            $oldIndexName = new Identifier($oldIndexName);
1823 39482
            $sql          = array_merge(
1824 39482
                $sql,
1825 39482
                $this->getRenameIndexSQL($oldIndexName->getQuotedName($this), $index, $tableName)
1826
            );
1827
        }
1828
1829 40911
        return $sql;
1830
    }
1831
1832
    /**
1833
     * Returns the SQL for renaming an index on a table.
1834
     *
1835
     * @param string $oldIndexName The name of the index to rename from.
1836
     * @param Index  $index        The definition of the index to rename to.
1837
     * @param string $tableName    The table to rename the given index on.
1838
     *
1839
     * @return string[] The sequence of SQL statements for renaming the given index.
1840
     */
1841 35710
    protected function getRenameIndexSQL(string $oldIndexName, Index $index, string $tableName) : array
1842
    {
1843
        return [
1844 35710
            $this->getDropIndexSQL($oldIndexName, $tableName),
1845 35710
            $this->getCreateIndexSQL($index, $tableName),
1846
        ];
1847
    }
1848
1849
    /**
1850
     * Common code for alter table statement generation that updates the changed Index and Foreign Key definitions.
1851
     *
1852
     * @return string[]
1853
     */
1854
    protected function _getAlterTableIndexForeignKeySQL(TableDiff $diff) : array
1855
    {
1856
        return array_merge($this->getPreAlterTableIndexForeignKeySQL($diff), $this->getPostAlterTableIndexForeignKeySQL($diff));
1857
    }
1858
1859
    /**
1860
     * Gets declaration of a number of fields in bulk.
1861
     *
1862
     * @param mixed[][] $fields A multidimensional associative array.
1863
     *                          The first dimension determines the field name, while the second
1864
     *                          dimension is keyed with the name of the properties
1865
     *                          of the field being declared as array indexes. Currently, the types
1866
     *                          of supported field properties are as follows:
1867
     *
1868
     *      length
1869
     *          Integer value that determines the maximum length of the text
1870
     *          field. If this argument is missing the field should be
1871
     *          declared to have the longest length allowed by the DBMS.
1872
     *
1873
     *      default
1874
     *          Text value to be used as default for this field.
1875
     *
1876
     *      notnull
1877
     *          Boolean flag that indicates whether this field is constrained
1878
     *          to not be set to null.
1879
     *      charset
1880
     *          Text value with the default CHARACTER SET for this field.
1881
     *      collation
1882
     *          Text value with the default COLLATION for this field.
1883
     *      unique
1884
     *          unique constraint
1885
     */
1886 43821
    public function getColumnDeclarationListSQL(array $fields) : string
1887
    {
1888 43821
        $queryFields = [];
1889
1890 43821
        foreach ($fields as $fieldName => $field) {
1891 43821
            $queryFields[] = $this->getColumnDeclarationSQL($fieldName, $field);
1892
        }
1893
1894 43821
        return implode(', ', $queryFields);
1895
    }
1896
1897
    /**
1898
     * Obtains DBMS specific SQL code portion needed to declare a generic type
1899
     * field to be used in statements like CREATE TABLE.
1900
     *
1901
     * @param string  $name  The name the field to be declared.
1902
     * @param mixed[] $field An associative array with the name of the properties
1903
     *                       of the field being declared as array indexes. Currently, the types
1904
     *                       of supported field properties are as follows:
1905
     *
1906
     *      length
1907
     *          Integer value that determines the maximum length of the text
1908
     *          field. If this argument is missing the field should be
1909
     *          declared to have the longest length allowed by the DBMS.
1910
     *
1911
     *      default
1912
     *          Text value to be used as default for this field.
1913
     *
1914
     *      notnull
1915
     *          Boolean flag that indicates whether this field is constrained
1916
     *          to not be set to null.
1917
     *      charset
1918
     *          Text value with the default CHARACTER SET for this field.
1919
     *      collation
1920
     *          Text value with the default COLLATION for this field.
1921
     *      unique
1922
     *          unique constraint
1923
     *      check
1924
     *          column check constraint
1925
     *      columnDefinition
1926
     *          a string that defines the complete column
1927
     *
1928
     * @return string DBMS specific SQL code portion that should be used to declare the column.
1929
     */
1930 43310
    public function getColumnDeclarationSQL(string $name, array $field) : string
1931
    {
1932 43310
        if (isset($field['columnDefinition'])) {
1933 38283
            $columnDef = $this->getCustomTypeDeclarationSQL($field);
1934
        } else {
1935 43301
            $default = $this->getDefaultValueDeclarationSQL($field);
1936
1937 43301
            $charset = isset($field['charset']) && $field['charset'] ?
1938 43301
                ' ' . $this->getColumnCharsetDeclarationSQL($field['charset']) : '';
1939
1940 43301
            $collation = isset($field['collation']) && $field['collation'] ?
1941 43301
                ' ' . $this->getColumnCollationDeclarationSQL($field['collation']) : '';
1942
1943 43301
            $notnull = isset($field['notnull']) && $field['notnull'] ? ' NOT NULL' : '';
1944
1945 43301
            $unique = isset($field['unique']) && $field['unique'] ?
1946 43301
                ' ' . $this->getUniqueFieldDeclarationSQL() : '';
1947
1948 43301
            $check = isset($field['check']) && $field['check'] ?
1949 43301
                ' ' . $field['check'] : '';
1950
1951 43301
            $typeDecl  = $field['type']->getSQLDeclaration($field, $this);
1952 43301
            $columnDef = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation;
1953
1954 43301
            if ($this->supportsInlineColumnComments() && isset($field['comment']) && $field['comment'] !== '') {
1955 37819
                $columnDef .= ' ' . $this->getInlineColumnCommentSQL($field['comment']);
1956
            }
1957
        }
1958
1959 43310
        return $name . ' ' . $columnDef;
1960
    }
1961
1962
    /**
1963
     * Returns the SQL snippet that declares a floating point column of arbitrary precision.
1964
     *
1965
     * @param mixed[] $columnDef
1966
     */
1967 40332
    public function getDecimalTypeDeclarationSQL(array $columnDef) : string
1968
    {
1969 40332
        $columnDef['precision'] = ! isset($columnDef['precision']) || empty($columnDef['precision'])
1970 40332
            ? 10 : $columnDef['precision'];
1971 40332
        $columnDef['scale']     = ! isset($columnDef['scale']) || empty($columnDef['scale'])
1972 40332
            ? 0 : $columnDef['scale'];
1973
1974 40332
        return 'NUMERIC(' . $columnDef['precision'] . ', ' . $columnDef['scale'] . ')';
1975
    }
1976
1977
    /**
1978
     * Obtains DBMS specific SQL code portion needed to set a default value
1979
     * declaration to be used in statements like CREATE TABLE.
1980
     *
1981
     * @param mixed[] $field The field definition array.
1982
     *
1983
     * @return string DBMS specific SQL code portion needed to set a default value.
1984
     */
1985 43798
    public function getDefaultValueDeclarationSQL(array $field) : string
1986
    {
1987 43798
        if (! isset($field['default'])) {
1988 43440
            return empty($field['notnull']) ? ' DEFAULT NULL' : '';
1989
        }
1990
1991 41319
        $default = $field['default'];
1992
1993 41319
        if (! isset($field['type'])) {
1994 10658
            return " DEFAULT '" . $default . "'";
1995
        }
1996
1997 41311
        $type = $field['type'];
1998
1999 41311
        if ($type instanceof Types\PhpIntegerMappingType) {
2000 40762
            return ' DEFAULT ' . $default;
2001
        }
2002
2003 41289
        if ($type instanceof Types\PhpDateTimeMappingType && $default === $this->getCurrentTimestampSQL()) {
2004 41203
            return ' DEFAULT ' . $this->getCurrentTimestampSQL();
2005
        }
2006
2007 41006
        if ($type instanceof Types\TimeType && $default === $this->getCurrentTimeSQL()) {
2008 24509
            return ' DEFAULT ' . $this->getCurrentTimeSQL();
2009
        }
2010
2011 41006
        if ($type instanceof Types\DateType && $default === $this->getCurrentDateSQL()) {
2012 38993
            return ' DEFAULT ' . $this->getCurrentDateSQL();
2013
        }
2014
2015 40762
        if ($type instanceof Types\BooleanType) {
2016 40724
            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

2016
            return " DEFAULT '" . /** @scrutinizer ignore-type */ $this->convertBooleans($default) . "'";
Loading history...
2017
        }
2018
2019 40734
        if (is_int($default) || is_float($default)) {
2020 35411
            return ' DEFAULT ' . $default;
2021
        }
2022
2023 40734
        return ' DEFAULT ' . $this->quoteStringLiteral($default);
2024
    }
2025
2026
    /**
2027
     * Obtains DBMS specific SQL code portion needed to set a CHECK constraint
2028
     * declaration to be used in statements like CREATE TABLE.
2029
     *
2030
     * @param string[]|mixed[][] $definition The check definition.
2031
     *
2032
     * @return string DBMS specific SQL code portion needed to set a CHECK constraint.
2033
     */
2034 38348
    public function getCheckDeclarationSQL(array $definition) : string
2035
    {
2036 38348
        $constraints = [];
2037 38348
        foreach ($definition as $field => $def) {
2038 38348
            if (is_string($def)) {
2039
                $constraints[] = 'CHECK (' . $def . ')';
2040
            } else {
2041 38348
                if (isset($def['min'])) {
2042 37502
                    $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')';
2043
                }
2044
2045 38348
                if (! isset($def['max'])) {
2046 38348
                    continue;
2047
                }
2048
2049 37502
                $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')';
2050
            }
2051
        }
2052
2053 38348
        return implode(', ', $constraints);
2054
    }
2055
2056
    /**
2057
     * Obtains DBMS specific SQL code portion needed to set a unique
2058
     * constraint declaration to be used in statements like CREATE TABLE.
2059
     *
2060
     * @param string           $name       The name of the unique constraint.
2061
     * @param UniqueConstraint $constraint The unique constraint definition.
2062
     *
2063
     * @return string DBMS specific SQL code portion needed to set a constraint.
2064
     *
2065
     * @throws InvalidArgumentException
2066
     */
2067 36576
    public function getUniqueConstraintDeclarationSQL(string $name, UniqueConstraint $constraint) : string
2068
    {
2069 36576
        $columns = $constraint->getColumns();
2070
2071 36576
        if (count($columns) === 0) {
2072 14526
            throw new InvalidArgumentException('Incomplete definition. "columns" required.');
2073
        }
2074
2075 36575
        $chunks = ['CONSTRAINT'];
2076
2077 36575
        if ($name !== '') {
2078 36575
            $chunks[] = (new Identifier($name))->getQuotedName($this);
2079
        }
2080
2081 36575
        $chunks[] = 'UNIQUE';
2082
2083 36575
        if ($constraint->hasFlag('clustered')) {
2084 14551
            $chunks[] = 'CLUSTERED';
2085
        }
2086
2087 36575
        $chunks[] = sprintf('(%s)', $this->getIndexFieldDeclarationListSQL($columns));
2088
2089 36575
        return implode(' ', $chunks);
2090
    }
2091
2092
    /**
2093
     * Obtains DBMS specific SQL code portion needed to set an index
2094
     * declaration to be used in statements like CREATE TABLE.
2095
     *
2096
     * @param string $name  The name of the index.
2097
     * @param Index  $index The index definition.
2098
     *
2099
     * @return string DBMS specific SQL code portion needed to set an index.
2100
     *
2101
     * @throws InvalidArgumentException
2102
     */
2103 38932
    public function getIndexDeclarationSQL(string $name, Index $index) : string
2104
    {
2105 38932
        $columns = $index->getColumns();
2106 38932
        $name    = new Identifier($name);
2107
2108 38932
        if (count($columns) === 0) {
2109
            throw new InvalidArgumentException('Incomplete definition. "columns" required.');
2110
        }
2111
2112 38932
        return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name->getQuotedName($this) . ' ('
2113 38932
            . $this->getIndexFieldDeclarationListSQL($index)
2114 38932
            . ')' . $this->getPartialIndexSQL($index);
2115
    }
2116
2117
    /**
2118
     * Obtains SQL code portion needed to create a custom column,
2119
     * e.g. when a field has the "columnDefinition" keyword.
2120
     * Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate.
2121
     *
2122
     * @param mixed[] $columnDef
2123
     */
2124 38286
    public function getCustomTypeDeclarationSQL(array $columnDef) : string
2125
    {
2126 38286
        return $columnDef['columnDefinition'];
2127
    }
2128
2129
    /**
2130
     * Obtains DBMS specific SQL code portion needed to set an index
2131
     * declaration to be used in statements like CREATE TABLE.
2132
     *
2133
     * @param mixed[]|Index $columnsOrIndex array declaration is deprecated, prefer passing Index to this method
2134
     */
2135 41906
    public function getIndexFieldDeclarationListSQL($columnsOrIndex) : string
2136
    {
2137 41906
        if ($columnsOrIndex instanceof Index) {
2138 41884
            return implode(', ', $columnsOrIndex->getQuotedColumns($this));
2139
        }
2140
2141 36585
        if (! is_array($columnsOrIndex)) {
0 ignored issues
show
introduced by
The condition is_array($columnsOrIndex) is always true.
Loading history...
2142
            throw new InvalidArgumentException('Fields argument should be an Index or array.');
2143
        }
2144
2145 36585
        $ret = [];
2146
2147 36585
        foreach ($columnsOrIndex as $column => $definition) {
2148 36585
            if (is_array($definition)) {
2149
                $ret[] = $column;
2150
            } else {
2151 36585
                $ret[] = $definition;
2152
            }
2153
        }
2154
2155 36585
        return implode(', ', $ret);
2156
    }
2157
2158
    /**
2159
     * Returns the required SQL string that fits between CREATE ... TABLE
2160
     * to create the table as a temporary table.
2161
     *
2162
     * Should be overridden in driver classes to return the correct string for the
2163
     * specific database type.
2164
     *
2165
     * The default is to return the string "TEMPORARY" - this will result in a
2166
     * SQL error for any database that does not support temporary tables, or that
2167
     * requires a different SQL command from "CREATE TEMPORARY TABLE".
2168
     *
2169
     * @return string The string required to be placed between "CREATE" and "TABLE"
2170
     *                to generate a temporary table, if possible.
2171
     */
2172
    public function getTemporaryTableSQL() : string
2173
    {
2174
        return 'TEMPORARY';
2175
    }
2176
2177
    /**
2178
     * Some vendors require temporary table names to be qualified specially.
2179
     */
2180 30785
    public function getTemporaryTableName(string $tableName) : string
2181
    {
2182 30785
        return $tableName;
2183
    }
2184
2185
    /**
2186
     * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2187
     * of a field declaration to be used in statements like CREATE TABLE.
2188
     *
2189
     * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2190
     *                of a field declaration.
2191
     */
2192 41659
    public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey) : string
2193
    {
2194 41659
        $sql  = $this->getForeignKeyBaseDeclarationSQL($foreignKey);
2195 41656
        $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey);
2196
2197 41656
        return $sql;
2198
    }
2199
2200
    /**
2201
     * Returns the FOREIGN KEY query section dealing with non-standard options
2202
     * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
2203
     *
2204
     * @param ForeignKeyConstraint $foreignKey The foreign key definition.
2205
     */
2206 41640
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) : string
2207
    {
2208 41640
        $query = '';
2209 41640
        if ($this->supportsForeignKeyOnUpdate() && $foreignKey->hasOption('onUpdate')) {
2210 14501
            $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate'));
2211
        }
2212 41640
        if ($foreignKey->hasOption('onDelete')) {
2213 38676
            $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
2214
        }
2215
2216 41640
        return $query;
2217
    }
2218
2219
    /**
2220
     * Returns the given referential action in uppercase if valid, otherwise throws an exception.
2221
     *
2222
     * @param string $action The foreign key referential action.
2223
     *
2224
     * @throws InvalidArgumentException If unknown referential action given.
2225
     */
2226 39806
    public function getForeignKeyReferentialActionSQL(string $action) : string
2227
    {
2228 39806
        $upper = strtoupper($action);
2229 39806
        switch ($upper) {
2230 78
            case 'CASCADE':
2231 53
            case 'SET NULL':
2232 41
            case 'NO ACTION':
2233 31
            case 'RESTRICT':
2234 22
            case 'SET DEFAULT':
2235 39795
                return $upper;
2236
            default:
2237 36861
                throw new InvalidArgumentException(sprintf('Invalid foreign key action "%s".', $upper));
2238
        }
2239
    }
2240
2241
    /**
2242
     * Obtains DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2243
     * of a field declaration to be used in statements like CREATE TABLE.
2244
     *
2245
     * @throws InvalidArgumentException
2246
     */
2247 41649
    public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey) : string
2248
    {
2249 41649
        $sql = '';
2250 41649
        if ($foreignKey->getName() !== '') {
2251 41480
            $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' ';
2252
        }
2253 41649
        $sql .= 'FOREIGN KEY (';
2254
2255 41649
        if (count($foreignKey->getLocalColumns()) === 0) {
2256
            throw new InvalidArgumentException('Incomplete definition. "local" required.');
2257
        }
2258 41649
        if (count($foreignKey->getForeignColumns()) === 0) {
2259
            throw new InvalidArgumentException('Incomplete definition. "foreign" required.');
2260
        }
2261 41649
        if (strlen($foreignKey->getForeignTableName()) === 0) {
2262
            throw new InvalidArgumentException('Incomplete definition. "foreignTable" required.');
2263
        }
2264
2265 41649
        return $sql . implode(', ', $foreignKey->getQuotedLocalColumns($this))
2266 41649
            . ') REFERENCES '
2267 41649
            . $foreignKey->getQuotedForeignTableName($this) . ' ('
2268 41649
            . implode(', ', $foreignKey->getQuotedForeignColumns($this)) . ')';
2269
    }
2270
2271
    /**
2272
     * Obtains DBMS specific SQL code portion needed to set the UNIQUE constraint
2273
     * of a field declaration to be used in statements like CREATE TABLE.
2274
     *
2275
     * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint
2276
     *                of a field declaration.
2277
     */
2278
    public function getUniqueFieldDeclarationSQL() : string
2279
    {
2280
        return 'UNIQUE';
2281
    }
2282
2283
    /**
2284
     * Obtains DBMS specific SQL code portion needed to set the CHARACTER SET
2285
     * of a field declaration to be used in statements like CREATE TABLE.
2286
     *
2287
     * @param string $charset The name of the charset.
2288
     *
2289
     * @return string DBMS specific SQL code portion needed to set the CHARACTER SET
2290
     *                of a field declaration.
2291
     */
2292
    public function getColumnCharsetDeclarationSQL(string $charset) : string
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

2292
    public function getColumnCharsetDeclarationSQL(/** @scrutinizer ignore-unused */ string $charset) : 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...
2293
    {
2294
        return '';
2295
    }
2296
2297
    /**
2298
     * Obtains DBMS specific SQL code portion needed to set the COLLATION
2299
     * of a field declaration to be used in statements like CREATE TABLE.
2300
     *
2301
     * @param string $collation The name of the collation.
2302
     *
2303
     * @return string DBMS specific SQL code portion needed to set the COLLATION
2304
     *                of a field declaration.
2305
     */
2306 38608
    public function getColumnCollationDeclarationSQL(string $collation) : string
2307
    {
2308 38608
        return $this->supportsColumnCollation() ? 'COLLATE ' . $collation : '';
2309
    }
2310
2311
    /**
2312
     * Whether the platform prefers sequences for ID generation.
2313
     * Subclasses should override this method to return TRUE if they prefer sequences.
2314
     */
2315 2
    public function prefersSequences() : bool
2316
    {
2317 2
        return false;
2318
    }
2319
2320
    /**
2321
     * Whether the platform prefers identity columns (eg. autoincrement) for ID generation.
2322
     * Subclasses should override this method to return TRUE if they prefer identity columns.
2323
     */
2324 29109
    public function prefersIdentityColumns() : bool
2325
    {
2326 29109
        return false;
2327
    }
2328
2329
    /**
2330
     * Some platforms need the boolean values to be converted.
2331
     *
2332
     * The default conversion in this implementation converts to integers (false => 0, true => 1).
2333
     *
2334
     * Note: if the input is not a boolean the original input might be returned.
2335
     *
2336
     * There are two contexts when converting booleans: Literals and Prepared Statements.
2337
     * This method should handle the literal case
2338
     *
2339
     * @param mixed $item A boolean or an array of them.
2340
     *
2341
     * @return mixed A boolean database value or an array of them.
2342
     */
2343 39341
    public function convertBooleans($item)
2344
    {
2345 39341
        if (is_array($item)) {
2346
            foreach ($item as $k => $value) {
2347
                if (! is_bool($value)) {
2348
                    continue;
2349
                }
2350
2351
                $item[$k] = (int) $value;
2352
            }
2353 39341
        } elseif (is_bool($item)) {
2354 39340
            $item = (int) $item;
2355
        }
2356
2357 39341
        return $item;
2358
    }
2359
2360
    /**
2361
     * Some platforms have boolean literals that needs to be correctly converted
2362
     *
2363
     * The default conversion tries to convert value into bool "(bool)$item"
2364
     *
2365
     * @param mixed $item
2366
     */
2367 38414
    public function convertFromBoolean($item) : ?bool
2368
    {
2369 38414
        if ($item === null) {
2370 1426
            return null;
2371
        }
2372
2373 38413
        return (bool) $item;
2374
    }
2375
2376
    /**
2377
     * This method should handle the prepared statements case. When there is no
2378
     * distinction, it's OK to use the same method.
2379
     *
2380
     * Note: if the input is not a boolean the original input might be returned.
2381
     *
2382
     * @param mixed $item A boolean or an array of them.
2383
     *
2384
     * @return mixed A boolean database value or an array of them.
2385
     */
2386 34736
    public function convertBooleansToDatabaseValue($item)
2387
    {
2388 34736
        return $this->convertBooleans($item);
2389
    }
2390
2391
    /**
2392
     * Returns the SQL specific for the platform to get the current date.
2393
     */
2394 39713
    public function getCurrentDateSQL() : string
2395
    {
2396 39713
        return 'CURRENT_DATE';
2397
    }
2398
2399
    /**
2400
     * Returns the SQL specific for the platform to get the current time.
2401
     */
2402 19618
    public function getCurrentTimeSQL() : string
2403
    {
2404 19618
        return 'CURRENT_TIME';
2405
    }
2406
2407
    /**
2408
     * Returns the SQL specific for the platform to get the current timestamp
2409
     */
2410 40925
    public function getCurrentTimestampSQL() : string
2411
    {
2412 40925
        return 'CURRENT_TIMESTAMP';
2413
    }
2414
2415
    /**
2416
     * Returns the SQL for a given transaction isolation level Connection constant.
2417
     *
2418
     * @throws InvalidArgumentException
2419
     */
2420 35058
    protected function _getTransactionIsolationLevelSQL(int $level) : string
2421
    {
2422
        switch ($level) {
2423 35058
            case TransactionIsolationLevel::READ_UNCOMMITTED:
2424 35058
                return 'READ UNCOMMITTED';
2425 35058
            case TransactionIsolationLevel::READ_COMMITTED:
2426 35058
                return 'READ COMMITTED';
2427 35058
            case TransactionIsolationLevel::REPEATABLE_READ:
2428 35058
                return 'REPEATABLE READ';
2429 35058
            case TransactionIsolationLevel::SERIALIZABLE:
2430 35058
                return 'SERIALIZABLE';
2431
            default:
2432
                throw new InvalidArgumentException(sprintf('Invalid isolation level "%s".', $level));
2433
        }
2434
    }
2435
2436
    /**
2437
     * @throws DBALException If not supported on this platform.
2438
     */
2439 3210
    public function getListDatabasesSQL() : string
2440
    {
2441 3210
        throw NotSupported::new(__METHOD__);
2442
    }
2443
2444
    /**
2445
     * Returns the SQL statement for retrieving the namespaces defined in the database.
2446
     *
2447
     * @throws DBALException If not supported on this platform.
2448
     */
2449
    public function getListNamespacesSQL() : string
2450
    {
2451
        throw NotSupported::new(__METHOD__);
2452
    }
2453
2454
    /**
2455
     * @throws DBALException If not supported on this platform.
2456
     */
2457
    public function getListSequencesSQL(string $database) : string
2458
    {
2459
        throw NotSupported::new(__METHOD__);
2460
    }
2461
2462
    /**
2463
     * @throws DBALException If not supported on this platform.
2464
     */
2465
    public function getListTableConstraintsSQL(string $table) : string
2466
    {
2467
        throw NotSupported::new(__METHOD__);
2468
    }
2469
2470
    /**
2471
     * @throws DBALException If not supported on this platform.
2472
     */
2473
    public function getListTableColumnsSQL(string $table, ?string $database = null) : string
2474
    {
2475
        throw NotSupported::new(__METHOD__);
2476
    }
2477
2478
    /**
2479
     * @throws DBALException If not supported on this platform.
2480
     */
2481
    public function getListTablesSQL() : string
2482
    {
2483
        throw NotSupported::new(__METHOD__);
2484
    }
2485
2486
    /**
2487
     * @throws DBALException If not supported on this platform.
2488
     */
2489
    public function getListUsersSQL() : string
2490
    {
2491
        throw NotSupported::new(__METHOD__);
2492
    }
2493
2494
    /**
2495
     * Returns the SQL to list all views of a database or user.
2496
     *
2497
     * @throws DBALException If not supported on this platform.
2498
     */
2499
    public function getListViewsSQL(string $database) : string
2500
    {
2501
        throw NotSupported::new(__METHOD__);
2502
    }
2503
2504
    /**
2505
     * Returns the list of indexes for the current database.
2506
     *
2507
     * The current database parameter is optional but will always be passed
2508
     * when using the SchemaManager API and is the database the given table is in.
2509
     *
2510
     * Attention: Some platforms only support currentDatabase when they
2511
     * are connected with that database. Cross-database information schema
2512
     * requests may be impossible.
2513
     *
2514
     * @throws DBALException If not supported on this platform.
2515
     */
2516
    public function getListTableIndexesSQL(string $table, ?string $currentDatabase = null) : string
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

2516
    public function getListTableIndexesSQL(string $table, /** @scrutinizer ignore-unused */ ?string $currentDatabase = 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...
2517
    {
2518
        throw NotSupported::new(__METHOD__);
2519
    }
2520
2521
    /**
2522
     * @throws DBALException If not supported on this platform.
2523
     */
2524
    public function getListTableForeignKeysSQL(string $table, ?string $database = null) : string
2525
    {
2526
        throw NotSupported::new(__METHOD__);
2527
    }
2528
2529
    /**
2530
     * @throws DBALException If not supported on this platform.
2531
     */
2532
    public function getCreateViewSQL(string $name, string $sql) : string
2533
    {
2534
        throw NotSupported::new(__METHOD__);
2535
    }
2536
2537
    /**
2538
     * @throws DBALException If not supported on this platform.
2539
     */
2540
    public function getDropViewSQL(string $name) : string
2541
    {
2542
        throw NotSupported::new(__METHOD__);
2543
    }
2544
2545
    /**
2546
     * Returns the SQL snippet to drop an existing sequence.
2547
     *
2548
     * @param Sequence|string $sequence
2549
     *
2550
     * @throws DBALException If not supported on this platform.
2551
     */
2552
    public function getDropSequenceSQL($sequence) : string
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

2552
    public function getDropSequenceSQL(/** @scrutinizer ignore-unused */ $sequence) : 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...
2553
    {
2554
        throw NotSupported::new(__METHOD__);
2555
    }
2556
2557
    /**
2558
     * @throws DBALException If not supported on this platform.
2559
     */
2560
    public function getSequenceNextValSQL(string $sequenceName) : string
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

2560
    public function getSequenceNextValSQL(/** @scrutinizer ignore-unused */ string $sequenceName) : 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...
2561
    {
2562
        throw NotSupported::new(__METHOD__);
2563
    }
2564
2565
    /**
2566
     * Returns the SQL to create a new database.
2567
     *
2568
     * @param string $database The name of the database that should be created.
2569
     *
2570
     * @throws DBALException If not supported on this platform.
2571
     */
2572 26501
    public function getCreateDatabaseSQL(string $database) : string
2573
    {
2574 26501
        throw NotSupported::new(__METHOD__);
2575
    }
2576
2577
    /**
2578
     * Returns the SQL to set the transaction isolation level.
2579
     *
2580
     * @throws DBALException If not supported on this platform.
2581
     */
2582
    public function getSetTransactionIsolationSQL(int $level) : string
2583
    {
2584
        throw NotSupported::new(__METHOD__);
2585
    }
2586
2587
    /**
2588
     * Obtains DBMS specific SQL to be used to create datetime fields in
2589
     * statements like CREATE TABLE.
2590
     *
2591
     * @param mixed[] $fieldDeclaration
2592
     *
2593
     * @throws DBALException If not supported on this platform.
2594
     */
2595
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) : string
2596
    {
2597
        throw NotSupported::new(__METHOD__);
2598
    }
2599
2600
    /**
2601
     * Obtains DBMS specific SQL to be used to create datetime with timezone offset fields.
2602
     *
2603
     * @param mixed[] $fieldDeclaration
2604
     */
2605 26107
    public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) : string
2606
    {
2607 26107
        return $this->getDateTimeTypeDeclarationSQL($fieldDeclaration);
2608
    }
2609
2610
    /**
2611
     * Obtains DBMS specific SQL to be used to create date fields in statements
2612
     * like CREATE TABLE.
2613
     *
2614
     * @param mixed[] $fieldDeclaration
2615
     *
2616
     * @throws DBALException If not supported on this platform.
2617
     */
2618
    public function getDateTypeDeclarationSQL(array $fieldDeclaration) : string
2619
    {
2620
        throw NotSupported::new(__METHOD__);
2621
    }
2622
2623
    /**
2624
     * Obtains DBMS specific SQL to be used to create time fields in statements
2625
     * like CREATE TABLE.
2626
     *
2627
     * @param mixed[] $fieldDeclaration
2628
     *
2629
     * @throws DBALException If not supported on this platform.
2630
     */
2631
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration) : string
2632
    {
2633
        throw NotSupported::new(__METHOD__);
2634
    }
2635
2636
    /**
2637
     * @param mixed[] $fieldDeclaration
2638
     */
2639 37189
    public function getFloatDeclarationSQL(array $fieldDeclaration) : string
2640
    {
2641 37189
        return 'DOUBLE PRECISION';
2642
    }
2643
2644
    /**
2645
     * Gets the default transaction isolation level of the platform.
2646
     *
2647
     * @see TransactionIsolationLevel
2648
     *
2649
     * @return int The default isolation level.
2650
     */
2651
    public function getDefaultTransactionIsolationLevel() : int
2652
    {
2653
        return TransactionIsolationLevel::READ_COMMITTED;
2654
    }
2655
2656
    /* supports*() methods */
2657
2658
    /**
2659
     * Whether the platform supports sequences.
2660
     */
2661 28576
    public function supportsSequences() : bool
2662
    {
2663 28576
        return false;
2664
    }
2665
2666
    /**
2667
     * Whether the platform supports identity columns.
2668
     *
2669
     * Identity columns are columns that receive an auto-generated value from the
2670
     * database on insert of a row.
2671
     */
2672 3
    public function supportsIdentityColumns() : bool
2673
    {
2674 3
        return false;
2675
    }
2676
2677
    /**
2678
     * Whether the platform emulates identity columns through sequences.
2679
     *
2680
     * Some platforms that do not support identity columns natively
2681
     * but support sequences can emulate identity columns by using
2682
     * sequences.
2683
     */
2684 37226
    public function usesSequenceEmulatedIdentityColumns() : bool
2685
    {
2686 37226
        return false;
2687
    }
2688
2689
    /**
2690
     * Gets the sequence name prefix based on table information.
2691
     */
2692
    public function getSequencePrefix(string $tableName, ?string $schemaName = null) : string
2693
    {
2694
        if (! $schemaName) {
2695
            return $tableName;
2696
        }
2697
2698
        // Prepend the schema name to the table name if there is one
2699
        return ! $this->supportsSchemas() && $this->canEmulateSchemas()
2700
            ? $schemaName . '__' . $tableName
2701
            : $schemaName . '.' . $tableName;
2702
    }
2703
2704
    /**
2705
     * Returns the name of the sequence for a particular identity column in a particular table.
2706
     *
2707
     * @see    usesSequenceEmulatedIdentityColumns
2708
     *
2709
     * @param string $tableName  The name of the table to return the sequence name for.
2710
     * @param string $columnName The name of the identity column in the table to return the sequence name for.
2711
     *
2712
     * @throws DBALException If not supported on this platform.
2713
     */
2714 35833
    public function getIdentitySequenceName(string $tableName, string $columnName) : string
2715
    {
2716 35833
        throw NotSupported::new(__METHOD__);
2717
    }
2718
2719
    /**
2720
     * Whether the platform supports indexes.
2721
     */
2722 1
    public function supportsIndexes() : bool
2723
    {
2724 1
        return true;
2725
    }
2726
2727
    /**
2728
     * Whether the platform supports partial indexes.
2729
     */
2730 40778
    public function supportsPartialIndexes() : bool
2731
    {
2732 40778
        return false;
2733
    }
2734
2735
    /**
2736
     * Whether the platform supports indexes with column length definitions.
2737
     */
2738 40262
    public function supportsColumnLengthIndexes() : bool
2739
    {
2740 40262
        return false;
2741
    }
2742
2743
    /**
2744
     * Whether the platform supports altering tables.
2745
     */
2746 39727
    public function supportsAlterTable() : bool
2747
    {
2748 39727
        return true;
2749
    }
2750
2751
    /**
2752
     * Whether the platform supports transactions.
2753
     */
2754 1
    public function supportsTransactions() : bool
2755
    {
2756 1
        return true;
2757
    }
2758
2759
    /**
2760
     * Whether the platform supports savepoints.
2761
     */
2762 42227
    public function supportsSavepoints() : bool
2763
    {
2764 42227
        return true;
2765
    }
2766
2767
    /**
2768
     * Whether the platform supports releasing savepoints.
2769
     */
2770 39930
    public function supportsReleaseSavepoints() : bool
2771
    {
2772 39930
        return $this->supportsSavepoints();
2773
    }
2774
2775
    /**
2776
     * Whether the platform supports primary key constraints.
2777
     */
2778 1
    public function supportsPrimaryConstraints() : bool
2779
    {
2780 1
        return true;
2781
    }
2782
2783
    /**
2784
     * Whether the platform supports foreign key constraints.
2785
     */
2786 41818
    public function supportsForeignKeyConstraints() : bool
2787
    {
2788 41818
        return true;
2789
    }
2790
2791
    /**
2792
     * Whether this platform supports onUpdate in foreign key constraints.
2793
     */
2794 41641
    public function supportsForeignKeyOnUpdate() : bool
2795
    {
2796 41641
        return $this->supportsForeignKeyConstraints();
2797
    }
2798
2799
    /**
2800
     * Whether the platform supports database schemas.
2801
     */
2802 28580
    public function supportsSchemas() : bool
2803
    {
2804 28580
        return false;
2805
    }
2806
2807
    /**
2808
     * Whether this platform can emulate schemas.
2809
     *
2810
     * Platforms that either support or emulate schemas don't automatically
2811
     * filter a schema for the namespaced elements in {@link
2812
     * AbstractManager#createSchema}.
2813
     */
2814 1
    public function canEmulateSchemas() : bool
2815
    {
2816 1
        return false;
2817
    }
2818
2819
    /**
2820
     * Returns the default schema name.
2821
     *
2822
     * @throws DBALException If not supported on this platform.
2823
     */
2824
    public function getDefaultSchemaName() : string
2825
    {
2826
        throw NotSupported::new(__METHOD__);
2827
    }
2828
2829
    /**
2830
     * Whether this platform supports create database.
2831
     *
2832
     * Some databases don't allow to create and drop databases at all or only with certain tools.
2833
     */
2834 37768
    public function supportsCreateDropDatabase() : bool
2835
    {
2836 37768
        return true;
2837
    }
2838
2839
    /**
2840
     * Whether the platform supports getting the affected rows of a recent update/delete type query.
2841
     */
2842 1
    public function supportsGettingAffectedRows() : bool
2843
    {
2844 1
        return true;
2845
    }
2846
2847
    /**
2848
     * Whether this platform support to add inline column comments as postfix.
2849
     */
2850 39487
    public function supportsInlineColumnComments() : bool
2851
    {
2852 39487
        return false;
2853
    }
2854
2855
    /**
2856
     * Whether this platform support the proprietary syntax "COMMENT ON asset".
2857
     */
2858 41260
    public function supportsCommentOnStatement() : bool
2859
    {
2860 41260
        return false;
2861
    }
2862
2863
    /**
2864
     * Does this platform have native guid type.
2865
     */
2866 41918
    public function hasNativeGuidType() : bool
2867
    {
2868 41918
        return false;
2869
    }
2870
2871
    /**
2872
     * Does this platform have native JSON type.
2873
     */
2874 41427
    public function hasNativeJsonType() : bool
2875
    {
2876 41427
        return false;
2877
    }
2878
2879
    /**
2880
     * Whether this platform supports views.
2881
     */
2882 39693
    public function supportsViews() : bool
2883
    {
2884 39693
        return true;
2885
    }
2886
2887
    /**
2888
     * Does this platform support column collation?
2889
     */
2890
    public function supportsColumnCollation() : bool
2891
    {
2892
        return false;
2893
    }
2894
2895
    /**
2896
     * Gets the format string, as accepted by the date() function, that describes
2897
     * the format of a stored datetime value of this platform.
2898
     *
2899
     * @return string The format string.
2900
     */
2901 39832
    public function getDateTimeFormatString() : string
2902
    {
2903 39832
        return 'Y-m-d H:i:s';
2904
    }
2905
2906
    /**
2907
     * Gets the format string, as accepted by the date() function, that describes
2908
     * the format of a stored datetime with timezone value of this platform.
2909
     *
2910
     * @return string The format string.
2911
     */
2912 26208
    public function getDateTimeTzFormatString() : string
2913
    {
2914 26208
        return 'Y-m-d H:i:s';
2915
    }
2916
2917
    /**
2918
     * Gets the format string, as accepted by the date() function, that describes
2919
     * the format of a stored date value of this platform.
2920
     *
2921
     * @return string The format string.
2922
     */
2923 35141
    public function getDateFormatString() : string
2924
    {
2925 35141
        return 'Y-m-d';
2926
    }
2927
2928
    /**
2929
     * Gets the format string, as accepted by the date() function, that describes
2930
     * the format of a stored time value of this platform.
2931
     *
2932
     * @return string The format string.
2933
     */
2934 26011
    public function getTimeFormatString() : string
2935
    {
2936 26011
        return 'H:i:s';
2937
    }
2938
2939
    /**
2940
     * Adds an driver-specific LIMIT clause to the query.
2941
     *
2942
     * @throws DBALException
2943
     */
2944 41583
    final public function modifyLimitQuery(string $query, ?int $limit, int $offset = 0) : string
2945
    {
2946 41583
        if ($offset < 0) {
2947
            throw new DBALException(sprintf(
2948
                'Offset must be a positive integer or zero, %d given.',
2949
                $offset
2950
            ));
2951
        }
2952
2953 41583
        if ($offset > 0 && ! $this->supportsLimitOffset()) {
2954
            throw new DBALException(sprintf(
2955
                'Platform "%s" does not support offset values in limit queries.',
2956
                $this->getName()
2957
            ));
2958
        }
2959
2960 41583
        return $this->doModifyLimitQuery($query, $limit, $offset);
2961
    }
2962
2963
    /**
2964
     * Adds an platform-specific LIMIT clause to the query.
2965
     */
2966 29142
    protected function doModifyLimitQuery(string $query, ?int $limit, int $offset) : string
2967
    {
2968 29142
        if ($limit !== null) {
2969 29138
            $query .= sprintf(' LIMIT %d', $limit);
2970
        }
2971
2972 29142
        if ($offset > 0) {
2973 16465
            $query .= sprintf(' OFFSET %d', $offset);
2974
        }
2975
2976 29142
        return $query;
2977
    }
2978
2979
    /**
2980
     * Whether the database platform support offsets in modify limit clauses.
2981
     */
2982 41192
    public function supportsLimitOffset() : bool
2983
    {
2984 41192
        return true;
2985
    }
2986
2987
    /**
2988
     * Gets the character casing of a column in an SQL result set of this platform.
2989
     *
2990
     * @param string $column The column name for which to get the correct character casing.
2991
     *
2992
     * @return string The column name in the character casing used in SQL result sets.
2993
     */
2994
    public function getSQLResultCasing(string $column) : string
2995
    {
2996
        return $column;
2997
    }
2998
2999
    /**
3000
     * Makes any fixes to a name of a schema element (table, sequence, ...) that are required
3001
     * by restrictions of the platform, like a maximum length.
3002
     */
3003
    public function fixSchemaElementName(string $schemaElementName) : string
3004
    {
3005
        return $schemaElementName;
3006
    }
3007
3008
    /**
3009
     * Maximum length of any given database identifier, like tables or column names.
3010
     */
3011 40943
    public function getMaxIdentifierLength() : int
3012
    {
3013 40943
        return 63;
3014
    }
3015
3016
    /**
3017
     * Returns the insert SQL for an empty insert statement.
3018
     */
3019 24097
    public function getEmptyIdentityInsertSQL(string $tableName, string $identifierColumnName) : string
3020
    {
3021 24097
        return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (null)';
3022
    }
3023
3024
    /**
3025
     * Generates a Truncate Table SQL statement for a given table.
3026
     *
3027
     * Cascade is not supported on many platforms but would optionally cascade the truncate by
3028
     * following the foreign keys.
3029
     */
3030 38149
    public function getTruncateTableSQL(string $tableName, bool $cascade = false) : string
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

3030
    public function getTruncateTableSQL(string $tableName, /** @scrutinizer ignore-unused */ bool $cascade = false) : 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...
3031
    {
3032 38149
        $tableIdentifier = new Identifier($tableName);
3033
3034 38149
        return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this);
3035
    }
3036
3037
    /**
3038
     * This is for test reasons, many vendors have special requirements for dummy statements.
3039
     */
3040 41613
    public function getDummySelectSQL(string $expression = '1') : string
3041
    {
3042 41613
        return sprintf('SELECT %s', $expression);
3043
    }
3044
3045
    /**
3046
     * Returns the SQL to create a new savepoint.
3047
     */
3048 38271
    public function createSavePoint(string $savepoint) : string
3049
    {
3050 38271
        return 'SAVEPOINT ' . $savepoint;
3051
    }
3052
3053
    /**
3054
     * Returns the SQL to release a savepoint.
3055
     */
3056 38270
    public function releaseSavePoint(string $savepoint) : string
3057
    {
3058 38270
        return 'RELEASE SAVEPOINT ' . $savepoint;
3059
    }
3060
3061
    /**
3062
     * Returns the SQL to rollback a savepoint.
3063
     */
3064 38271
    public function rollbackSavePoint(string $savepoint) : string
3065
    {
3066 38271
        return 'ROLLBACK TO SAVEPOINT ' . $savepoint;
3067
    }
3068
3069
    /**
3070
     * Returns the keyword list instance of this platform.
3071
     *
3072
     * @throws DBALException If no keyword list is specified.
3073
     */
3074 44313
    final public function getReservedKeywordsList() : KeywordList
3075
    {
3076
        // Check for an existing instantiation of the keywords class.
3077 44313
        if ($this->_keywords) {
3078 44252
            return $this->_keywords;
3079
        }
3080
3081 44148
        $class    = $this->getReservedKeywordsClass();
3082 44148
        $keywords = new $class();
3083 44148
        if (! $keywords instanceof KeywordList) {
3084
            throw NotSupported::new(__METHOD__);
3085
        }
3086
3087
        // Store the instance so it doesn't need to be generated on every request.
3088 44148
        $this->_keywords = $keywords;
3089
3090 44148
        return $keywords;
3091
    }
3092
3093
    /**
3094
     * Returns the class name of the reserved keywords list.
3095
     *
3096
     * @throws DBALException If not supported on this platform.
3097
     */
3098
    protected function getReservedKeywordsClass() : string
3099
    {
3100
        throw NotSupported::new(__METHOD__);
3101
    }
3102
3103
    /**
3104
     * Quotes a literal string.
3105
     * This method is NOT meant to fix SQL injections!
3106
     * It is only meant to escape this platform's string literal
3107
     * quote character inside the given literal string.
3108
     *
3109
     * @param string $str The literal string to be quoted.
3110
     *
3111
     * @return string The quoted literal string.
3112
     */
3113 41622
    public function quoteStringLiteral(string $str) : string
3114
    {
3115 41622
        $c = $this->getStringLiteralQuoteCharacter();
3116
3117 41622
        return $c . str_replace($c, $c . $c, $str) . $c;
3118
    }
3119
3120
    /**
3121
     * Gets the character used for string literal quoting.
3122
     */
3123 41634
    public function getStringLiteralQuoteCharacter() : string
3124
    {
3125 41634
        return "'";
3126
    }
3127
3128
    /**
3129
     * Escapes metacharacters in a string intended to be used with a LIKE
3130
     * operator.
3131
     *
3132
     * @param string $inputString a literal, unquoted string
3133
     * @param string $escapeChar  should be reused by the caller in the LIKE
3134
     *                            expression.
3135
     */
3136 41697
    final public function escapeStringForLike(string $inputString, string $escapeChar) : string
3137
    {
3138 41697
        return preg_replace(
3139 41697
            '~([' . preg_quote($this->getLikeWildcardCharacters() . $escapeChar, '~') . '])~u',
3140 41697
            addcslashes($escapeChar, '\\') . '$1',
3141 41697
            $inputString
3142
        );
3143
    }
3144
3145 41697
    protected function getLikeWildcardCharacters() : string
3146
    {
3147 41697
        return '%_';
3148
    }
3149
}
3150