Failed Conditions
Pull Request — develop (#3348)
by Sergei
38:24 queued 35:29
created

AbstractPlatform::convertNonNativeInterval()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

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

297
    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...
298 122
    {
299
        throw DBALException::notSupported('VARCHARs not supported by Platform.');
300 122
    }
301
302
    /**
303
     * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
304
     *
305
     * @param int|null $length The length of the column.
306
     * @param bool     $fixed  Whether the column length is fixed.
307
     *
308
     * @throws DBALException If not supported on this platform.
309
     */
310
    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

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

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

742
    public function getLocateExpression(string $str, string $substr, /** @scrutinizer ignore-unused */ $startPos = 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...
Unused Code introduced by
The parameter $substr is not used and could be removed. ( Ignorable by Annotation )

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

742
    public function getLocateExpression(string $str, /** @scrutinizer ignore-unused */ string $substr, $startPos = 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...
743
    {
744
        throw DBALException::notSupported(__METHOD__);
745
    }
746
747
    /**
748
     * Returns the SQL snippet to get the current system date.
749
     */
750
    public function getNowExpression() : string
751
    {
752
        return 'NOW()';
753
    }
754
755
    /**
756 540
     * Returns a SQL snippet to get a substring inside an SQL statement.
757
     *
758 540
     * Note: Not SQL92, but common functionality.
759
     *
760 540
     * SQLite only supports the 2 parameter variant of this function.
761
     *
762 135
     * @param string   $value  An sql string literal or column name/alias.
763 135
     * @param int      $from   Where to start the substring portion.
764
     * @param int|null $length The substring portion length.
765
     */
766 135
    public function getSubstringExpression(string $value, int $from, ?int $length = null) : string
767 135
    {
768
        if ($length === null) {
769
            return 'SUBSTRING(' . $value . ' FROM ' . $from . ')';
770 135
        }
771 135
772
        return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $length . ')';
773
    }
774 540
775 420
    /**
776
     * Returns a SQL snippet to concatenate the given expressions.
777
     *
778 540
     * @param string[] ...$expressions
779 510
     */
780
    public function getConcatExpression(string ...$expressions) : string
781
    {
782 540
        return implode(' || ', $expressions);
783
    }
784
785
    /**
786
     * Returns the SQL for a logical not.
787
     *
788
     * Example:
789
     * <code>
790
     * $q = new Doctrine_Query();
791
     * $e = $q->expr;
792 19
     * $q->select('*')->from('table')
793
     *   ->where($e->eq('id', $e->not('null'));
794 19
     * </code>
795
     *
796
     * @return string The logical expression.
797
     */
798
    public function getNotExpression(string $expression) : string
799
    {
800
        return 'NOT(' . $expression . ')';
801
    }
802
803
    /**
804 19
     * Returns the SQL that checks if an expression is null.
805
     *
806 19
     * @param string $expression The expression that should be compared to null.
807
     *
808
     * @return string The logical expression.
809
     */
810
    public function getIsNullExpression(string $expression) : string
811
    {
812
        return $expression . ' IS NULL';
813
    }
814
815
    /**
816
     * Returns the SQL that checks if an expression is not null.
817
     *
818
     * @param string $expression The expression that should be compared to null.
819
     *
820
     * @return string The logical expression.
821
     */
822
    public function getIsNotNullExpression(string $expression) : string
823
    {
824
        return $expression . ' IS NOT NULL';
825
    }
826
827
    /**
828
     * Returns the SQL that checks if an expression evaluates to a value between two values.
829
     *
830
     * The parameter $expression is checked if it is between $value1 and $value2.
831
     *
832
     * Note: There is a slight difference in the way BETWEEN works on some databases.
833
     * http://www.w3schools.com/sql/sql_between.asp. If you want complete database
834
     * independence you should avoid using between().
835
     *
836
     * @param string $expression The value to compare to.
837
     * @param string $value1     The lower value to compare with.
838
     * @param string $value2     The higher value to compare with.
839
     *
840
     * @return string The logical expression.
841
     */
842
    public function getBetweenExpression(string $expression, string $value1, string $value2) : string
843
    {
844
        return $expression . ' BETWEEN ' . $value1 . ' AND ' . $value2;
845
    }
846
847
    /**
848
     * Returns the SQL to get the arccosine of a value.
849
     */
850
    public function getAcosExpression(string $value) : string
851
    {
852
        return 'ACOS(' . $value . ')';
853
    }
854
855
    /**
856
     * Returns the SQL to get the sine of a value.
857
     */
858
    public function getSinExpression(string $value) : string
859
    {
860
        return 'SIN(' . $value . ')';
861
    }
862
863
    /**
864
     * Returns the SQL to get the PI value.
865
     */
866
    public function getPiExpression() : string
867
    {
868
        return 'PI()';
869
    }
870
871
    /**
872
     * Returns the SQL to get the cosine of a value.
873
     */
874
    public function getCosExpression(string $value) : string
875
    {
876
        return 'COS(' . $value . ')';
877
    }
878
879
    /**
880
     * Returns the SQL to calculate the difference in days between the two passed dates.
881
     *
882
     * Computes diff = date1 - date2.
883
     *
884
     * @throws DBALException If not supported on this platform.
885
     */
886
    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

886
    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

886
    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...
887
    {
888
        throw DBALException::notSupported(__METHOD__);
889
    }
890 76
891
    /**
892 76
     * Returns the SQL to add the number of given seconds to a date.
893
     *
894
     * @throws DBALException If not supported on this platform.
895
     */
896
    public function getDateAddSecondsExpression(string $date, string $seconds) : string
897
    {
898
        return $this->getDateArithmeticIntervalExpression($date, '+', $seconds, DateIntervalUnit::SECOND);
899
    }
900
901
    /**
902
     * Returns the SQL to subtract the number of given seconds from a date.
903
     *
904
     * @throws DBALException If not supported on this platform.
905
     */
906
    public function getDateSubSecondsExpression(string $date, string $seconds) : string
907
    {
908
        return $this->getDateArithmeticIntervalExpression($date, '-', $seconds, DateIntervalUnit::SECOND);
909
    }
910
911
    /**
912
     * Returns the SQL to add the number of given minutes to a date.
913
     *
914
     * @throws DBALException If not supported on this platform.
915
     */
916
    public function getDateAddMinutesExpression(string $date, string $minutes) : string
917
    {
918
        return $this->getDateArithmeticIntervalExpression($date, '+', $minutes, DateIntervalUnit::MINUTE);
919
    }
920
921
    /**
922 76
     * Returns the SQL to subtract the number of given minutes from a date.
923
     *
924 76
     * @throws DBALException If not supported on this platform.
925
     */
926
    public function getDateSubMinutesExpression(string $date, string $minutes) : string
927
    {
928
        return $this->getDateArithmeticIntervalExpression($date, '-', $minutes, DateIntervalUnit::MINUTE);
929
    }
930
931
    /**
932
     * Returns the SQL to add the number of given hours to a date.
933
     *
934
     * @throws DBALException If not supported on this platform.
935
     */
936
    public function getDateAddHourExpression(string $date, string $hours) : string
937
    {
938
        return $this->getDateArithmeticIntervalExpression($date, '+', $hours, DateIntervalUnit::HOUR);
939
    }
940
941
    /**
942
     * Returns the SQL to subtract the number of given hours to a date.
943
     *
944
     * @throws DBALException If not supported on this platform.
945
     */
946
    public function getDateSubHourExpression(string $date, string $hours) : string
947
    {
948
        return $this->getDateArithmeticIntervalExpression($date, '-', $hours, DateIntervalUnit::HOUR);
949
    }
950
951
    /**
952
     * Returns the SQL to add the number of given days to a date.
953
     *
954
     * @throws DBALException If not supported on this platform.
955
     */
956
    public function getDateAddDaysExpression(string $date, string $days) : string
957
    {
958
        return $this->getDateArithmeticIntervalExpression($date, '+', $days, DateIntervalUnit::DAY);
959
    }
960
961
    /**
962
     * Returns the SQL to subtract the number of given days to a date.
963
     *
964
     * @throws DBALException If not supported on this platform.
965
     */
966
    public function getDateSubDaysExpression(string $date, string $days) : string
967
    {
968
        return $this->getDateArithmeticIntervalExpression($date, '-', $days, DateIntervalUnit::DAY);
969
    }
970
971
    /**
972
     * Returns the SQL to add the number of given weeks to a date.
973
     *
974
     * @throws DBALException If not supported on this platform.
975
     */
976
    public function getDateAddWeeksExpression(string $date, string $weeks) : string
977
    {
978
        return $this->getDateArithmeticIntervalExpression($date, '+', $weeks, DateIntervalUnit::WEEK);
979
    }
980
981
    /**
982
     * Returns the SQL to subtract the number of given weeks from a date.
983
     *
984
     * @throws DBALException If not supported on this platform.
985
     */
986
    public function getDateSubWeeksExpression(string $date, string $weeks) : string
987
    {
988
        return $this->getDateArithmeticIntervalExpression($date, '-', $weeks, DateIntervalUnit::WEEK);
989
    }
990
991
    /**
992
     * Returns the SQL to add the number of given months to a date.
993
     *
994
     * @throws DBALException If not supported on this platform.
995
     */
996
    public function getDateAddMonthExpression(string $date, string $months) : string
997
    {
998
        return $this->getDateArithmeticIntervalExpression($date, '+', $months, DateIntervalUnit::MONTH);
999
    }
1000
1001
    /**
1002
     * Returns the SQL to subtract the number of given months to a date.
1003
     *
1004
     * @throws DBALException If not supported on this platform.
1005
     */
1006
    public function getDateSubMonthExpression(string $date, string $months) : string
1007
    {
1008
        return $this->getDateArithmeticIntervalExpression($date, '-', $months, DateIntervalUnit::MONTH);
1009
    }
1010
1011
    /**
1012
     * Returns the SQL to add the number of given quarters to a date.
1013
     *
1014
     * @throws DBALException If not supported on this platform.
1015
     */
1016
    public function getDateAddQuartersExpression(string $date, string $quarters) : string
1017
    {
1018
        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
     * @throws DBALException If not supported on this platform.
1025
     */
1026
    public function getDateSubQuartersExpression(string $date, string $quarters) : string
1027
    {
1028
        return $this->getDateArithmeticIntervalExpression($date, '-', $quarters, DateIntervalUnit::QUARTER);
1029
    }
1030
1031
    /**
1032 57
     * Returns the SQL to add the number of given years to a date.
1033
     *
1034 57
     * @throws DBALException If not supported on this platform.
1035
     */
1036
    public function getDateAddYearsExpression(string $date, string $years) : string
1037
    {
1038
        return $this->getDateArithmeticIntervalExpression($date, '+', $years, DateIntervalUnit::YEAR);
1039
    }
1040
1041
    /**
1042
     * Returns the SQL to subtract the number of given years from a date.
1043
     *
1044
     * @throws DBALException If not supported on this platform.
1045
     */
1046
    public function getDateSubYearsExpression(string $date, string $years) : string
1047 57
    {
1048
        return $this->getDateArithmeticIntervalExpression($date, '-', $years, DateIntervalUnit::YEAR);
1049 57
    }
1050
1051
    /**
1052
     * Returns the SQL for a date arithmetic expression.
1053
     *
1054
     * @param string $date     The column or literal representing a date to perform the arithmetic operation on.
1055
     * @param string $operator The arithmetic operator (+ or -).
1056
     * @param string $interval The interval that shall be calculated into the date.
1057
     * @param string $unit     The unit of the interval that shall be calculated into the date.
1058
     *                                 One of the DATE_INTERVAL_UNIT_* constants.
1059
     *
1060
     * @throws DBALException If not supported on this platform.
1061
     */
1062 57
    protected function getDateArithmeticIntervalExpression(string $date, string $operator, string $interval, string $unit) : string
0 ignored issues
show
Unused Code introduced by
The parameter $operator is not used and could be removed. ( Ignorable by Annotation )

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

1062
    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...
1063
    {
1064 57
        throw DBALException::notSupported(__METHOD__);
1065
    }
1066
1067
    /**
1068
     * Converts the value of a natively unsupported date interval to a value of a supported one
1069
     *
1070
     * @param string $interval   SQL expression describing the value of the interval
1071
     * @param string $unit       Interval unit
1072
     * @param int    $multiplier Interval multiplier
1073
     *
1074
     * @throws DBALException
1075
     */
1076
    protected function convertNonNativeInterval(string $interval, string $unit, int $multiplier) : string
1077 57
    {
1078
        if (! is_numeric($interval)) {
1079 57
            throw new DBALException(sprintf('Non-numeric value of a %s interval is not supported', $unit));
1080
        }
1081
1082
        return (string) ((int) $interval * $multiplier);
1083
    }
1084
1085
    /**
1086
     * Returns the SQL bit AND comparison expression.
1087
     */
1088
    public function getBitAndComparisonExpression(string $value1, string $value2) : string
1089
    {
1090
        return '(' . $value1 . ' & ' . $value2 . ')';
1091
    }
1092 57
1093
    /**
1094 57
     * Returns the SQL bit OR comparison expression.
1095
     */
1096
    public function getBitOrComparisonExpression(string $value1, string $value2) : string
1097
    {
1098
        return '(' . $value1 . ' | ' . $value2 . ')';
1099
    }
1100
1101
    /**
1102
     * Returns the FOR UPDATE expression.
1103
     */
1104
    public function getForUpdateSQL() : string
1105
    {
1106
        return 'FOR UPDATE';
1107 57
    }
1108
1109 57
    /**
1110
     * Honors that some SQL vendors such as MsSql use table hints for locking instead of the ANSI SQL FOR UPDATE specification.
1111
     *
1112
     * @param string   $fromClause The FROM clause to append the hint for the given lock mode to.
1113
     * @param int|null $lockMode   One of the Doctrine\DBAL\LockMode::* constants. If null is given, nothing will
1114
     *                             be appended to the FROM clause.
1115
     */
1116
    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

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

1446
    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...
1447
    {
1448
        throw DBALException::notSupported(__METHOD__);
1449 653
    }
1450 653
1451
    /**
1452 653
     * Returns the SQL to change a sequence on this platform.
1453
     *
1454
     * @throws DBALException If not supported on this platform.
1455
     */
1456
    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

1456
    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...
1457
    {
1458
        throw DBALException::notSupported(__METHOD__);
1459
    }
1460
1461
    /**
1462
     * Returns the SQL to create a constraint on a table on this platform.
1463 299
     *
1464
     * @param Table|string $table
1465 299
     *
1466 95
     * @throws InvalidArgumentException
1467
     */
1468
    public function getCreateConstraintSQL(Constraint $constraint, $table) : string
1469 299
    {
1470 299
        if ($table instanceof Table) {
1471
            $table = $table->getQuotedName($this);
1472
        }
1473 299
1474 299
        $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this);
1475
1476 299
        $columnList = '(' . implode(', ', $constraint->getQuotedColumns($this)) . ')';
1477
1478
        $referencesClause = '';
1479
        if ($constraint instanceof Index) {
1480
            if ($constraint->isPrimary()) {
1481
                $query .= ' PRIMARY KEY';
1482
            } elseif ($constraint->isUnique()) {
1483
                $query .= ' UNIQUE';
1484
            } else {
1485
                throw new InvalidArgumentException(
1486
                    'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().'
1487
                );
1488
            }
1489
        } elseif ($constraint instanceof ForeignKeyConstraint) {
1490 6595
            $query .= ' FOREIGN KEY';
1491
1492 6595
            $referencesClause = ' REFERENCES ' . $constraint->getQuotedForeignTableName($this) .
1493
                ' (' . implode(', ', $constraint->getQuotedForeignColumns($this)) . ')';
1494
        }
1495
        $query .= ' ' . $columnList . $referencesClause;
1496 6595
1497 228
        return $query;
1498
    }
1499
1500 6367
    /**
1501 6367
     * Returns the SQL to create an index on a table on this platform.
1502 6367
     *
1503 6367
     * @param Table|string $table The name of the table on which the index is to be created.
1504 6367
     *
1505
     * @throws InvalidArgumentException
1506 6367
     */
1507 6101
    public function getCreateIndexSQL(Index $index, $table) : string
1508
    {
1509 4099
        if ($table instanceof Table) {
1510 1274
            $table = $table->getQuotedName($this);
1511
        }
1512 1274
        $name    = $index->getQuotedName($this);
1513
        $columns = $index->getColumns();
1514
1515 3177
        if (count($columns) === 0) {
1516 3177
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
1517
        }
1518
1519 6101
        if ($index->isPrimary()) {
1520
            return $this->getCreatePrimaryKeySQL($index, $table);
1521
        }
1522
1523
        $query  = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table;
1524
        $query .= ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')' . $this->getPartialIndexSQL($index);
1525 6367
1526 3518
        return $query;
1527
    }
1528 3518
1529 553
    /**
1530
     * Adds condition for partial index.
1531
     */
1532
    protected function getPartialIndexSQL(Index $index) : string
1533 6367
    {
1534 6367
        if ($this->supportsPartialIndexes() && $index->hasOption('where')) {
1535
            return ' WHERE ' . $index->getOption('where');
1536 6367
        }
1537
1538 6367
        return '';
1539 228
    }
1540 228
1541
    /**
1542 228
     * Adds additional flags for index generation.
1543
     */
1544 228
    protected function getCreateIndexSQLFlags(Index $index) : string
1545
    {
1546
        return $index->isUnique() ? 'UNIQUE ' : '';
1547
    }
1548
1549 6367
    /**
1550 6367
     * Returns the SQL to create an unnamed primary key constraint.
1551 6367
     *
1552 6367
     * @param Table|string $table
1553
     */
1554 6367
    public function getCreatePrimaryKeySQL(Index $index, $table) : string
1555 2377
    {
1556
        return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index) . ')';
0 ignored issues
show
Bug introduced by
Are you sure $table of type Doctrine\DBAL\Schema\Table|string can be used in concatenation? ( Ignorable by Annotation )

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

1556
        return 'ALTER TABLE ' . /** @scrutinizer ignore-type */ $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index) . ')';
Loading history...
1557
    }
1558 6367
1559 2911
    /**
1560
     * Returns the SQL to create a named schema.
1561
     *
1562 6367
     * @throws DBALException If not supported on this platform.
1563
     */
1564
    public function getCreateSchemaSQL(string $schemaName) : string
1565 6367
    {
1566 228
        throw DBALException::notSupported(__METHOD__);
1567 228
    }
1568
1569 228
    /**
1570
     * Quotes a string so that it can be safely used as a table or column name,
1571
     * even if it is a reserved word of the platform. This also detects identifier
1572
     * chains separated by dot and quotes them independently.
1573
     *
1574 6367
     * NOTE: Just because you CAN use quoted identifiers doesn't mean
1575 6367
     * you SHOULD use them. In general, they end up causing way more
1576 2527
     * problems than they solve.
1577 2527
     *
1578
     * @param string $str The identifier name to be quoted.
1579 2527
     *
1580 2372
     * @return string The quoted identifier string.
1581
     */
1582
    public function quoteIdentifier(string $str) : string
1583 446
    {
1584
        if (strpos($str, '.') !== false) {
1585
            $parts = array_map([$this, 'quoteSingleIdentifier'], explode('.', $str));
1586
1587 6367
            return implode('.', $parts);
1588
        }
1589
1590
        return $this->quoteSingleIdentifier($str);
1591
    }
1592
1593
    /**
1594
     * Quotes a single identifier (no dot chain separation).
1595
     *
1596
     * @param string $str The identifier name to be quoted.
1597 653
     *
1598
     * @return string The quoted identifier string.
1599 653
     */
1600 653
    public function quoteSingleIdentifier(string $str) : string
1601 653
    {
1602
        $c = $this->getIdentifierQuoteCharacter();
1603 653
1604 653
        return $c . str_replace($c, $c . $c, $str) . $c;
1605 653
    }
1606 653
1607 653
    /**
1608
     * Returns the SQL to create a new foreign key.
1609
     *
1610
     * @param ForeignKeyConstraint $foreignKey The foreign key constraint.
1611
     * @param Table|string         $table      The name of the table on which the foreign key is to be created.
1612
     */
1613
    public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) : string
1614
    {
1615
        if ($table instanceof Table) {
1616
            $table = $table->getQuotedName($this);
1617
        }
1618
1619
        return 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey);
1620 933
    }
1621
1622 933
    /**
1623 152
     * Gets the SQL statements for altering an existing table.
1624
     *
1625
     * This method returns an array of SQL statements, since some platforms need several statements.
1626 781
     *
1627
     * @return string[]
1628
     *
1629
     * @throws DBALException If not supported on this platform.
1630
     */
1631
    public function getAlterTableSQL(TableDiff $diff)
1632
    {
1633
        throw DBALException::notSupported(__METHOD__);
1634
    }
1635
1636
    /**
1637
     * @param mixed[] $columnSql
1638 713
     */
1639
    protected function onSchemaAlterTableAddColumn(Column $column, TableDiff $diff, array &$columnSql) : bool
1640 713
    {
1641
        if ($this->_eventManager === null) {
1642 713
            return false;
1643
        }
1644
1645
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableAddColumn)) {
1646
            return false;
1647
        }
1648 713
1649 372
        $eventArgs = new SchemaAlterTableAddColumnEventArgs($column, $diff, $this);
1650
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn, $eventArgs);
1651
1652 713
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1653
1654
        return $eventArgs->isDefaultPrevented();
1655
    }
1656
1657
    /**
1658 713
     * @param string[] $columnSql
1659
     */
1660 713
    protected function onSchemaAlterTableRemoveColumn(Column $column, TableDiff $diff, array &$columnSql) : bool
1661 713
    {
1662 19
        if ($this->_eventManager === null) {
1663
            return false;
1664 713
        }
1665
1666 713
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRemoveColumn)) {
1667
            return false;
1668 713
        }
1669 315
1670 75
        $eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column, $diff, $this);
1671
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn, $eventArgs);
1672
1673
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1674 713
1675
        return $eventArgs->isDefaultPrevented();
1676
    }
1677
1678
    /**
1679
     * @param string[] $columnSql
1680 30
     */
1681
    protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiff, TableDiff $diff, array &$columnSql) : bool
1682 30
    {
1683
        if ($this->_eventManager === null) {
1684
            return false;
1685
        }
1686
1687
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) {
1688
            return false;
1689
        }
1690
1691
        $eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff, $diff, $this);
1692
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn, $eventArgs);
1693
1694
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1695
1696
        return $eventArgs->isDefaultPrevented();
1697
    }
1698
1699
    /**
1700
     * @param string[] $columnSql
1701
     */
1702
    protected function onSchemaAlterTableRenameColumn(string $oldColumnName, Column $column, TableDiff $diff, array &$columnSql) : bool
1703
    {
1704
        if ($this->_eventManager === null) {
1705
            return false;
1706
        }
1707
1708
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRenameColumn)) {
1709
            return false;
1710
        }
1711
1712
        $eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName, $column, $diff, $this);
1713
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn, $eventArgs);
1714
1715
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1716
1717
        return $eventArgs->isDefaultPrevented();
1718 295
    }
1719
1720 295
    /**
1721
     * @param string[] $sql
1722
     */
1723
    protected function onSchemaAlterTable(TableDiff $diff, array &$sql) : bool
1724 295
    {
1725
        if ($this->_eventManager === null) {
1726 295
            return false;
1727
        }
1728 295
1729 295
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTable)) {
1730 295
            return false;
1731 295
        }
1732 190
1733 190
        $eventArgs = new SchemaAlterTableEventArgs($diff, $this);
1734
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTable, $eventArgs);
1735
1736 295
        $sql = array_merge($sql, $eventArgs->getSql());
1737
1738
        return $eventArgs->isDefaultPrevented();
1739 190
    }
1740 190
1741
    /**
1742 190
     * @return string[]
1743 190
     */
1744
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) : array
1745 295
    {
1746
        $tableName = $diff->getName($this)->getQuotedName($this);
1747 295
1748
        $sql = [];
1749
        if ($this->supportsForeignKeyConstraints()) {
1750
            foreach ($diff->removedForeignKeys as $foreignKey) {
1751
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
1752
            }
1753
            foreach ($diff->changedForeignKeys as $foreignKey) {
1754
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
1755
            }
1756
        }
1757
1758
        foreach ($diff->removedIndexes as $index) {
1759 2192
            $sql[] = $this->getDropIndexSQL($index, $tableName);
1760
        }
1761 2192
        foreach ($diff->changedIndexes as $index) {
1762 19
            $sql[] = $this->getDropIndexSQL($index, $tableName);
1763
        }
1764 2192
1765 2192
        return $sql;
1766
    }
1767 2192
1768
    /**
1769
     * @return string[]
1770
     */
1771 2192
    protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) : array
1772 361
    {
1773
        $tableName = $diff->newName !== false
1774
            ? $diff->getNewName()->getQuotedName($this)
1775 1850
            : $diff->getName($this)->getQuotedName($this);
1776 1850
1777
        $sql = [];
1778 1850
1779
        if ($this->supportsForeignKeyConstraints()) {
1780
            foreach ($diff->addedForeignKeys as $foreignKey) {
1781
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
1782
            }
1783
1784
            foreach ($diff->changedForeignKeys as $foreignKey) {
1785
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
1786 2526
            }
1787
        }
1788 2526
1789 62
        foreach ($diff->addedIndexes as $index) {
1790
            $sql[] = $this->getCreateIndexSQL($index, $tableName);
1791
        }
1792 2464
1793
        foreach ($diff->changedIndexes as $index) {
1794
            $sql[] = $this->getCreateIndexSQL($index, $tableName);
1795
        }
1796
1797
        foreach ($diff->renamedIndexes as $oldIndexName => $index) {
1798
            $oldIndexName = new Identifier($oldIndexName);
1799
            $sql          = array_merge(
1800 1042
                $sql,
1801
                $this->getRenameIndexSQL($oldIndexName->getQuotedName($this), $index, $tableName)
1802 1042
            );
1803
        }
1804
1805
        return $sql;
1806
    }
1807
1808
    /**
1809
     * Returns the SQL for renaming an index on a table.
1810
     *
1811
     * @param string $oldIndexName The name of the index to rename from.
1812 323
     * @param Index  $index        The definition of the index to rename to.
1813
     * @param string $tableName    The table to rename the given index on.
1814 323
     *
1815
     * @return string[] The sequence of SQL statements for renaming the given index.
1816
     */
1817
    protected function getRenameIndexSQL(string $oldIndexName, Index $index, string $tableName) : array
1818
    {
1819
        return [
1820
            $this->getDropIndexSQL($oldIndexName, $tableName),
1821
            $this->getCreateIndexSQL($index, $tableName),
1822
        ];
1823
    }
1824
1825
    /**
1826 133
     * Common code for alter table statement generation that updates the changed Index and Foreign Key definitions.
1827
     *
1828 133
     * @return string[]
1829
     */
1830
    protected function _getAlterTableIndexForeignKeySQL(TableDiff $diff) : array
1831
    {
1832
        return array_merge($this->getPreAlterTableIndexForeignKeySQL($diff), $this->getPostAlterTableIndexForeignKeySQL($diff));
1833
    }
1834
1835
    /**
1836
     * Gets declaration of a number of fields in bulk.
1837
     *
1838
     * @param mixed[][] $fields A multidimensional associative array.
1839
     *                          The first dimension determines the field name, while the second
1840
     *                          dimension is keyed with the name of the properties
1841
     *                          of the field being declared as array indexes. Currently, the types
1842
     *                          of supported field properties are as follows:
1843
     *
1844 5274
     *      length
1845
     *          Integer value that determines the maximum length of the text
1846 5274
     *          field. If this argument is missing the field should be
1847 252
     *          declared to have the longest length allowed by the DBMS.
1848
     *
1849 252
     *      default
1850
     *          Text value to be used as default for this field.
1851
     *
1852 5274
     *      notnull
1853
     *          Boolean flag that indicates whether this field is constrained
1854
     *          to not be set to null.
1855
     *      charset
1856
     *          Text value with the default CHARACTER SET for this field.
1857
     *      collation
1858
     *          Text value with the default COLLATION for this field.
1859
     *      unique
1860
     *          unique constraint
1861
     */
1862 5247
    public function getColumnDeclarationListSQL(array $fields) : string
1863
    {
1864 5247
        $queryFields = [];
1865
1866 5247
        foreach ($fields as $fieldName => $field) {
1867
            $queryFields[] = $this->getColumnDeclarationSQL($fieldName, $field);
1868
        }
1869
1870
        return implode(', ', $queryFields);
1871
    }
1872
1873
    /**
1874
     * Obtains DBMS specific SQL code portion needed to declare a generic type
1875
     * field to be used in statements like CREATE TABLE.
1876
     *
1877 1229
     * @param string  $name  The name the field to be declared.
1878
     * @param mixed[] $field An associative array with the name of the properties
1879 1229
     *                       of the field being declared as array indexes. Currently, the types
1880 19
     *                       of supported field properties are as follows:
1881
     *
1882
     *      length
1883 1229
     *          Integer value that determines the maximum length of the text
1884
     *          field. If this argument is missing the field should be
1885
     *          declared to have the longest length allowed by the DBMS.
1886
     *
1887
     *      default
1888
     *          Text value to be used as default for this field.
1889
     *
1890
     *      notnull
1891
     *          Boolean flag that indicates whether this field is constrained
1892
     *          to not be set to null.
1893
     *      charset
1894
     *          Text value with the default CHARACTER SET for this field.
1895
     *      collation
1896
     *          Text value with the default COLLATION for this field.
1897
     *      unique
1898
     *          unique constraint
1899
     *      check
1900
     *          column check constraint
1901
     *      columnDefinition
1902
     *          a string that defines the complete column
1903
     *
1904
     * @return string DBMS specific SQL code portion that should be used to declare the column.
1905 1315
     */
1906
    public function getColumnDeclarationSQL(string $name, array $field) : string
1907 1315
    {
1908 1064
        if (isset($field['columnDefinition'])) {
1909
            $columnDef = $this->getCustomTypeDeclarationSQL($field);
1910
        } else {
1911 251
            $default = $this->getDefaultValueDeclarationSQL($field);
1912 23
1913
            $charset = isset($field['charset']) && $field['charset'] ?
1914
                ' ' . $this->getColumnCharsetDeclarationSQL($field['charset']) : '';
1915 228
1916 228
            $collation = isset($field['collation']) && $field['collation'] ?
1917
                ' ' . $this->getColumnCollationDeclarationSQL($field['collation']) : '';
1918 228
1919
            $notnull = isset($field['notnull']) && $field['notnull'] ? ' NOT NULL' : '';
1920 228
1921
            $unique = isset($field['unique']) && $field['unique'] ?
1922
                ' ' . $this->getUniqueFieldDeclarationSQL() : '';
1923
1924
            $check = isset($field['check']) && $field['check'] ?
1925
                ' ' . $field['check'] : '';
1926
1927
            $typeDecl  = $field['type']->getSQLDeclaration($field, $this);
1928 975
            $columnDef = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation;
1929
1930 975
            if ($this->supportsInlineColumnComments() && isset($field['comment']) && $field['comment'] !== '') {
1931 722
                $columnDef .= ' ' . $this->getInlineColumnCommentSQL($field['comment']);
1932
            }
1933
        }
1934 253
1935 25
        return $name . ' ' . $columnDef;
1936
    }
1937
1938 228
    /**
1939 228
     * Returns the SQL snippet that declares a floating point column of arbitrary precision.
1940
     *
1941 228
     * @param mixed[] $columnDef
1942
     */
1943 228
    public function getDecimalTypeDeclarationSQL(array $columnDef) : string
1944
    {
1945
        $columnDef['precision'] = ! isset($columnDef['precision']) || empty($columnDef['precision'])
1946
            ? 10 : $columnDef['precision'];
1947
        $columnDef['scale']     = ! isset($columnDef['scale']) || empty($columnDef['scale'])
1948
            ? 0 : $columnDef['scale'];
1949
1950
        return 'NUMERIC(' . $columnDef['precision'] . ', ' . $columnDef['scale'] . ')';
1951 2559
    }
1952
1953 2559
    /**
1954 2071
     * Obtains DBMS specific SQL code portion needed to set a default value
1955
     * declaration to be used in statements like CREATE TABLE.
1956
     *
1957 488
     * @param mixed[] $field The field definition array.
1958 260
     *
1959
     * @return string DBMS specific SQL code portion needed to set a default value.
1960
     */
1961 228
    public function getDefaultValueDeclarationSQL(array $field) : string
1962 228
    {
1963
        if (! isset($field['default'])) {
1964 228
            return empty($field['notnull']) ? ' DEFAULT NULL' : '';
1965
        }
1966 228
1967
        $default = $field['default'];
1968
1969
        if (! isset($field['type'])) {
1970
            return " DEFAULT '" . $default . "'";
1971
        }
1972
1973
        $type = $field['type'];
1974
1975 990
        if ($type instanceof Types\PhpIntegerMappingType) {
1976
            return ' DEFAULT ' . $default;
1977 990
        }
1978 741
1979
        if ($type instanceof Types\PhpDateTimeMappingType && $default === $this->getCurrentTimestampSQL()) {
1980
            return ' DEFAULT ' . $this->getCurrentTimestampSQL();
1981 249
        }
1982 21
1983
        if ($type instanceof Types\TimeType && $default === $this->getCurrentTimeSQL()) {
1984
            return ' DEFAULT ' . $this->getCurrentTimeSQL();
1985 228
        }
1986 228
1987
        if ($type instanceof Types\DateType && $default === $this->getCurrentDateSQL()) {
1988 228
            return ' DEFAULT ' . $this->getCurrentDateSQL();
1989
        }
1990 228
1991
        if ($type instanceof Types\BooleanType) {
1992
            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

1992
            return " DEFAULT '" . /** @scrutinizer ignore-type */ $this->convertBooleans($default) . "'";
Loading history...
1993
        }
1994
1995
        return " DEFAULT '" . $default . "'";
1996
    }
1997
1998 5008
    /**
1999
     * Obtains DBMS specific SQL code portion needed to set a CHECK constraint
2000 5008
     * declaration to be used in statements like CREATE TABLE.
2001 4427
     *
2002
     * @param mixed[][] $definition The check definition.
2003
     *
2004 581
     * @return string DBMS specific SQL code portion needed to set a CHECK constraint.
2005 353
     */
2006
    public function getCheckDeclarationSQL(array $definition) : string
2007
    {
2008 228
        $constraints = [];
2009 228
        foreach ($definition as $field => $def) {
2010
            if (is_string($def)) {
2011 228
                $constraints[] = 'CHECK (' . $def . ')';
2012
            } else {
2013 228
                if (isset($def['min'])) {
2014
                    $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')';
2015
                }
2016
2017
                if (isset($def['max'])) {
2018
                    $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')';
2019 4723
                }
2020
            }
2021 4723
        }
2022
2023 4723
        return implode(', ', $constraints);
2024 4723
    }
2025 4723
2026 340
    /**
2027
     * Obtains DBMS specific SQL code portion needed to set a unique
2028 4723
     * constraint declaration to be used in statements like CREATE TABLE.
2029 266
     *
2030
     * @param string|null      $name       The name of the unique constraint.
2031
     * @param UniqueConstraint $constraint The unique constraint definition.
2032
     *
2033 4723
     * @return string DBMS specific SQL code portion needed to set a constraint.
2034 204
     *
2035
     * @throws InvalidArgumentException
2036 4723
     */
2037 338
    public function getUniqueConstraintDeclarationSQL(?string $name, UniqueConstraint $constraint) : string
2038
    {
2039
        $columns = $constraint->getColumns();
2040 4723
        $name    = new Identifier($name);
2041
2042
        if (count($columns) === 0) {
2043
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
2044
        }
2045
2046 4723
        $flags = ['UNIQUE'];
2047
2048 4723
        if ($constraint->hasFlag('clustered')) {
2049 422
            $flags[] = 'CLUSTERED';
2050 4723
        }
2051
2052 4723
        $constraintName  = $name->getQuotedName($this);
2053
        $constraintName  = ! empty($constraintName) ? $constraintName . ' ' : '';
2054 4723
        $columnListNames = $this->getIndexFieldDeclarationListSQL($columns);
2055 4723
2056 300
        return sprintf('CONSTRAINT %s%s (%s)', $constraintName, implode(' ', $flags), $columnListNames);
2057
    }
2058
2059 4723
    /**
2060 266
     * Obtains DBMS specific SQL code portion needed to set an index
2061
     * declaration to be used in statements like CREATE TABLE.
2062
     *
2063
     * @param string $name  The name of the index.
2064 4723
     * @param Index  $index The index definition.
2065 74
     *
2066
     * @return string DBMS specific SQL code portion needed to set an index.
2067
     *
2068 4723
     * @throws InvalidArgumentException
2069 338
     */
2070
    public function getIndexDeclarationSQL(string $name, Index $index) : string
2071
    {
2072 4723
        $columns = $index->getColumns();
2073 1079
        $name    = new Identifier($name);
2074 1079
2075 1079
        if (count($columns) === 0) {
2076 1079
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
2077
        }
2078
2079
        return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name->getQuotedName($this) . ' ('
2080 4723
            . $this->getIndexFieldDeclarationListSQL($index)
2081
            . ')' . $this->getPartialIndexSQL($index);
2082
    }
2083
2084
    /**
2085
     * Obtains SQL code portion needed to create a custom column,
2086
     * e.g. when a field has the "columnDefinition" keyword.
2087
     * Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate.
2088
     *
2089
     * @param mixed[] $columnDef
2090
     */
2091
    public function getCustomTypeDeclarationSQL(array $columnDef) : string
2092 202
    {
2093
        return $columnDef['columnDefinition'];
2094
    }
2095 202
2096 202
    /**
2097
     * Obtains DBMS specific SQL code portion needed to set an index
2098
     * declaration to be used in statements like CREATE TABLE.
2099
     *
2100
     * @param mixed[][]|Index $columnsOrIndex array declaration is deprecated, prefer passing Index to this method
2101
     */
2102
    public function getIndexFieldDeclarationListSQL($columnsOrIndex) : string
2103
    {
2104
        if ($columnsOrIndex instanceof Index) {
2105
            return implode(', ', $columnsOrIndex->getQuotedColumns($this));
2106
        }
2107
2108
        if (! is_array($columnsOrIndex)) {
0 ignored issues
show
introduced by
The condition is_array($columnsOrIndex) is always true.
Loading history...
2109
            throw new InvalidArgumentException('Fields argument should be an Index or array.');
2110
        }
2111
2112
        $ret = [];
2113
2114
        foreach ($columnsOrIndex as $column => $definition) {
2115
            if (is_array($definition)) {
2116
                $ret[] = $column;
2117
            } else {
2118
                $ret[] = $definition;
2119
            }
2120
        }
2121
2122
        return implode(', ', $ret);
2123
    }
2124
2125
    /**
2126
     * Returns the required SQL string that fits between CREATE ... TABLE
2127
     * to create the table as a temporary table.
2128
     *
2129
     * Should be overridden in driver classes to return the correct string for the
2130
     * specific database type.
2131
     *
2132
     * The default is to return the string "TEMPORARY" - this will result in a
2133
     * SQL error for any database that does not support temporary tables, or that
2134
     * requires a different SQL command from "CREATE TEMPORARY TABLE".
2135
     *
2136
     * @return string The string required to be placed between "CREATE" and "TABLE"
2137
     *                to generate a temporary table, if possible.
2138
     */
2139 6367
    public function getTemporaryTableSQL() : string
2140
    {
2141 6367
        return 'TEMPORARY';
2142
    }
2143 6367
2144 6367
    /**
2145
     * Some vendors require temporary table names to be qualified specially.
2146
     */
2147 6367
    public function getTemporaryTableName(string $tableName) : string
2148
    {
2149
        return $tableName;
2150
    }
2151
2152
    /**
2153
     * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2154
     * of a field declaration to be used in statements like CREATE TABLE.
2155
     *
2156
     * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2157
     *                of a field declaration.
2158
     */
2159
    public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey) : string
2160
    {
2161
        $sql  = $this->getForeignKeyBaseDeclarationSQL($foreignKey);
2162
        $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey);
2163
2164
        return $sql;
2165
    }
2166
2167
    /**
2168
     * Returns the FOREIGN KEY query section dealing with non-standard options
2169
     * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
2170
     *
2171
     * @param ForeignKeyConstraint $foreignKey The foreign key definition.
2172
     */
2173
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) : string
2174
    {
2175
        $query = '';
2176
        if ($this->supportsForeignKeyOnUpdate() && $foreignKey->hasOption('onUpdate')) {
2177
            $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate'));
2178
        }
2179
        if ($foreignKey->hasOption('onDelete')) {
2180
            $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
2181
        }
2182
2183 6423
        return $query;
2184
    }
2185 6423
2186 181
    /**
2187
     * Returns the given referential action in uppercase if valid, otherwise throws an exception.
2188 6252
     *
2189
     * @param string $action The foreign key referential action.
2190 6252
     *
2191 6252
     * @throws InvalidArgumentException If unknown referential action given.
2192
     */
2193 6252
    public function getForeignKeyReferentialActionSQL(string $action) : string
2194 6252
    {
2195
        $upper = strtoupper($action);
2196 6252
        switch ($upper) {
2197
            case 'CASCADE':
2198 6252
            case 'SET NULL':
2199 6252
            case 'NO ACTION':
2200
            case 'RESTRICT':
2201 6252
            case 'SET DEFAULT':
2202 6252
                return $upper;
2203
            default:
2204 6252
                throw new InvalidArgumentException('Invalid foreign key action: ' . $upper);
2205 6252
        }
2206
    }
2207 6252
2208 731
    /**
2209
     * Obtains DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2210
     * of a field declaration to be used in statements like CREATE TABLE.
2211
     *
2212 6423
     * @throws InvalidArgumentException
2213
     */
2214
    public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey) : string
2215
    {
2216
        $sql = '';
2217
        if (strlen($foreignKey->getName())) {
2218
            $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' ';
2219
        }
2220
        $sql .= 'FOREIGN KEY (';
2221
2222 1828
        if (count($foreignKey->getLocalColumns()) === 0) {
2223
            throw new InvalidArgumentException("Incomplete definition. 'local' required.");
2224 1828
        }
2225 1828
        if (count($foreignKey->getForeignColumns()) === 0) {
2226 1828
            throw new InvalidArgumentException("Incomplete definition. 'foreign' required.");
2227 1828
        }
2228
        if (strlen($foreignKey->getForeignTableName()) === 0) {
2229 1828
            throw new InvalidArgumentException("Incomplete definition. 'foreignTable' required.");
2230
        }
2231
2232
        return $sql . implode(', ', $foreignKey->getQuotedLocalColumns($this))
2233
            . ') REFERENCES '
2234
            . $foreignKey->getQuotedForeignTableName($this) . ' ('
2235
            . implode(', ', $foreignKey->getQuotedForeignColumns($this)) . ')';
2236
    }
2237
2238
    /**
2239
     * Obtains DBMS specific SQL code portion needed to set the UNIQUE constraint
2240 7314
     * of a field declaration to be used in statements like CREATE TABLE.
2241
     *
2242 7314
     * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint
2243 6417
     *                of a field declaration.
2244
     */
2245
    public function getUniqueFieldDeclarationSQL() : string
2246 1241
    {
2247
        return 'UNIQUE';
2248 1241
    }
2249
2250
    /**
2251
     * Obtains DBMS specific SQL code portion needed to set the CHARACTER SET
2252 1241
     * of a field declaration to be used in statements like CREATE TABLE.
2253
     *
2254 1241
     * @param string $charset The name of the charset.
2255 372
     *
2256
     * @return string DBMS specific SQL code portion needed to set the CHARACTER SET
2257
     *                of a field declaration.
2258 916
     */
2259 208
    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

2259
    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...
2260
    {
2261
        return '';
2262 718
    }
2263 2
2264
    /**
2265
     * Obtains DBMS specific SQL code portion needed to set the COLLATION
2266 718
     * of a field declaration to be used in statements like CREATE TABLE.
2267 192
     *
2268
     * @param string $collation The name of the collation.
2269
     *
2270 526
     * @return string DBMS specific SQL code portion needed to set the COLLATION
2271 195
     *                of a field declaration.
2272
     */
2273
    public function getColumnCollationDeclarationSQL(string $collation) : string
2274 521
    {
2275
        return $this->supportsColumnCollation() ? 'COLLATE ' . $collation : '';
2276
    }
2277
2278
    /**
2279
     * Whether the platform prefers sequences for ID generation.
2280
     * Subclasses should override this method to return TRUE if they prefer sequences.
2281
     */
2282
    public function prefersSequences() : bool
2283
    {
2284
        return false;
2285 1863
    }
2286
2287 1863
    /**
2288 1863
     * Whether the platform prefers identity columns (eg. autoincrement) for ID generation.
2289 1863
     * Subclasses should override this method to return TRUE if they prefer identity columns.
2290
     */
2291
    public function prefersIdentityColumns() : bool
2292 1863
    {
2293 38
        return false;
2294
    }
2295
2296 1863
    /**
2297 1863
     * Some platforms need the boolean values to be converted.
2298
     *
2299
     * The default conversion in this implementation converts to integers (false => 0, true => 1).
2300
     *
2301
     * Note: if the input is not a boolean the original input might be returned.
2302 1863
     *
2303
     * There are two contexts when converting booleans: Literals and Prepared Statements.
2304
     * This method should handle the literal case
2305
     *
2306
     * @param mixed $item A boolean or an array of them.
2307
     *
2308
     * @return mixed A boolean database value or an array of them.
2309
     */
2310
    public function convertBooleans($item)
2311
    {
2312
        if (is_array($item)) {
2313
            foreach ($item as $k => $value) {
2314
                if (! is_bool($value)) {
2315
                    continue;
2316 494
                }
2317
2318 494
                $item[$k] = (int) $value;
2319 494
            }
2320
        } elseif (is_bool($item)) {
2321 494
            $item = (int) $item;
2322 19
        }
2323
2324
        return $item;
2325 475
    }
2326
2327 475
    /**
2328 19
     * Some platforms have boolean literals that needs to be correctly converted
2329
     *
2330
     * The default conversion tries to convert value into bool "(bool)$item"
2331 475
     *
2332 475
     * @param mixed $item
2333 475
     */
2334
    public function convertFromBoolean($item) : ?bool
2335 475
    {
2336
        return $item === null ? null: (bool) $item;
2337
    }
2338
2339
    /**
2340
     * This method should handle the prepared statements case. When there is no
2341
     * distinction, it's OK to use the same method.
2342
     *
2343
     * Note: if the input is not a boolean the original input might be returned.
2344
     *
2345
     * @param mixed $item A boolean or an array of them.
2346
     *
2347
     * @return mixed A boolean database value or an array of them.
2348
     */
2349 888
    public function convertBooleansToDatabaseValue($item)
2350
    {
2351 888
        return $this->convertBooleans($item);
2352 888
    }
2353
2354 888
    /**
2355
     * Returns the SQL specific for the platform to get the current date.
2356
     */
2357
    public function getCurrentDateSQL() : string
2358 888
    {
2359 888
        return 'CURRENT_DATE';
2360 888
    }
2361
2362
    /**
2363
     * Returns the SQL specific for the platform to get the current time.
2364
     */
2365
    public function getCurrentTimeSQL() : string
2366
    {
2367
        return 'CURRENT_TIME';
2368
    }
2369
2370
    /**
2371
     * Returns the SQL specific for the platform to get the current timestamp
2372 238
     */
2373
    public function getCurrentTimestampSQL() : string
2374 238
    {
2375
        return 'CURRENT_TIMESTAMP';
2376
    }
2377
2378
    /**
2379
     * Returns the SQL for a given transaction isolation level Connection constant.
2380
     *
2381
     * @throws InvalidArgumentException
2382
     */
2383
    protected function _getTransactionIsolationLevelSQL(int $level) : string
2384
    {
2385 3400
        switch ($level) {
2386
            case TransactionIsolationLevel::READ_UNCOMMITTED:
2387 3400
                return 'READ UNCOMMITTED';
2388
            case TransactionIsolationLevel::READ_COMMITTED:
2389 3400
                return 'READ COMMITTED';
2390 3400
            case TransactionIsolationLevel::REPEATABLE_READ:
2391
                return 'REPEATABLE READ';
2392
            case TransactionIsolationLevel::SERIALIZABLE:
2393 3400
                return 'SERIALIZABLE';
2394
            default:
2395
                throw new InvalidArgumentException('Invalid isolation level:' . $level);
2396
        }
2397 3400
    }
2398
2399
    /**
2400
     * @throws DBALException If not supported on this platform.
2401
     */
2402
    public function getListDatabasesSQL() : string
2403
    {
2404
        throw DBALException::notSupported(__METHOD__);
2405
    }
2406
2407
    /**
2408
     * Returns the SQL statement for retrieving the namespaces defined in the database.
2409
     *
2410
     * @throws DBALException If not supported on this platform.
2411
     */
2412
    public function getListNamespacesSQL() : string
2413
    {
2414
        throw DBALException::notSupported(__METHOD__);
2415
    }
2416
2417
    /**
2418
     * @throws DBALException If not supported on this platform.
2419
     */
2420
    public function getListSequencesSQL(?string $database) : ?string
2421
    {
2422
        throw DBALException::notSupported(__METHOD__);
2423
    }
2424
2425
    /**
2426 26
     * @throws DBALException If not supported on this platform.
2427
     */
2428 26
    public function getListTableConstraintsSQL(string $table) : string
2429
    {
2430
        throw DBALException::notSupported(__METHOD__);
2431
    }
2432
2433
    /**
2434
     * @throws DBALException If not supported on this platform.
2435
     */
2436
    public function getListTableColumnsSQL(string $table, ?string $database = null) : string
2437
    {
2438 1497
        throw DBALException::notSupported(__METHOD__);
2439
    }
2440 1497
2441 1440
    /**
2442
     * @throws DBALException If not supported on this platform.
2443 1440
     */
2444
    public function getListTablesSQL() : string
2445
    {
2446
        throw DBALException::notSupported(__METHOD__);
2447
    }
2448
2449
    /**
2450
     * @throws DBALException If not supported on this platform.
2451
     */
2452
    public function getListUsersSQL() : string
2453
    {
2454 1340
        throw DBALException::notSupported(__METHOD__);
2455
    }
2456 1340
2457 1340
    /**
2458 19
     * Returns the SQL to list all views of a database or user.
2459
     *
2460 1340
     * @throws DBALException If not supported on this platform.
2461 91
     */
2462
    public function getListViewsSQL(?string $database) : string
2463
    {
2464 1340
        throw DBALException::notSupported(__METHOD__);
2465
    }
2466
2467
    /**
2468
     * Returns the list of indexes for the current database.
2469
     *
2470
     * The current database parameter is optional but will always be passed
2471
     * when using the SchemaManager API and is the database the given table is in.
2472
     *
2473
     * Attention: Some platforms only support currentDatabase when they
2474
     * are connected with that database. Cross-database information schema
2475
     * requests may be impossible.
2476 1497
     *
2477
     * @throws DBALException If not supported on this platform.
2478 1497
     */
2479 1497
    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

2479
    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...
2480 1497
    {
2481 1012
        throw DBALException::notSupported(__METHOD__);
2482 784
    }
2483 594
2484 423
    /**
2485 1288
     * @throws DBALException If not supported on this platform.
2486
     */
2487 209
    public function getListTableForeignKeysSQL(string $table) : string
2488
    {
2489
        throw DBALException::notSupported(__METHOD__);
2490
    }
2491
2492
    /**
2493
     * @throws DBALException If not supported on this platform.
2494
     */
2495
    public function getCreateViewSQL(string $name, string $sql) : string
2496
    {
2497
        throw DBALException::notSupported(__METHOD__);
2498
    }
2499 1307
2500
    /**
2501 1307
     * @throws DBALException If not supported on this platform.
2502 1307
     */
2503 1098
    public function getDropViewSQL(string $name) : string
2504
    {
2505 1307
        throw DBALException::notSupported(__METHOD__);
2506
    }
2507 1307
2508
    /**
2509
     * Returns the SQL snippet to drop an existing sequence.
2510 1307
     *
2511
     * @param Sequence|string $sequence
2512
     *
2513 1307
     * @throws DBALException If not supported on this platform.
2514
     */
2515
    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

2515
    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...
2516
    {
2517 1307
        throw DBALException::notSupported(__METHOD__);
2518 1307
    }
2519 1307
2520 1307
    /**
2521
     * @throws DBALException If not supported on this platform.
2522
     */
2523
    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

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

3003
    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...
3004
    {
3005
        $tableIdentifier = new Identifier($tableName);
3006 23
3007
        return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this);
3008 23
    }
3009
3010
    /**
3011
     * This is for test reasons, many vendors have special requirements for dummy statements.
3012
     */
3013
    public function getDummySelectSQL(string $expression = '1') : string
3014
    {
3015
        return sprintf('SELECT %s', $expression);
3016
    }
3017
3018
    /**
3019
     * Returns the SQL to create a new savepoint.
3020 164
     */
3021
    public function createSavePoint(string $savepoint) : string
3022 164
    {
3023
        return 'SAVEPOINT ' . $savepoint;
3024
    }
3025
3026
    /**
3027
     * Returns the SQL to release a savepoint.
3028
     */
3029
    public function releaseSavePoint(string $savepoint) : string
3030
    {
3031
        return 'RELEASE SAVEPOINT ' . $savepoint;
3032
    }
3033
3034
    /**
3035
     * Returns the SQL to rollback a savepoint.
3036
     */
3037
    public function rollbackSavePoint(string $savepoint) : string
3038
    {
3039
        return 'ROLLBACK TO SAVEPOINT ' . $savepoint;
3040
    }
3041
3042
    /**
3043
     * Returns the keyword list instance of this platform.
3044
     *
3045
     * @throws DBALException If no keyword list is specified.
3046
     */
3047
    final public function getReservedKeywordsList() : KeywordList
3048
    {
3049
        // Check for an existing instantiation of the keywords class.
3050
        if ($this->_keywords) {
3051
            return $this->_keywords;
3052
        }
3053
3054
        $class    = $this->getReservedKeywordsClass();
3055
        $keywords = new $class();
3056
        if (! $keywords instanceof KeywordList) {
3057 152
            throw DBALException::notSupported(__METHOD__);
3058
        }
3059 152
3060
        // Store the instance so it doesn't need to be generated on every request.
3061
        $this->_keywords = $keywords;
3062
3063
        return $keywords;
3064
    }
3065
3066
    /**
3067 19
     * Returns the class name of the reserved keywords list.
3068
     *
3069 19
     * @return string
3070
     *
3071
     * @throws DBALException If not supported on this platform.
3072
     */
3073
    protected function getReservedKeywordsClass()
3074
    {
3075
        throw DBALException::notSupported(__METHOD__);
3076
    }
3077 2037
3078
    /**
3079 2037
     * Quotes a literal string.
3080
     * This method is NOT meant to fix SQL injections!
3081
     * It is only meant to escape this platform's string literal
3082
     * quote character inside the given literal string.
3083
     *
3084
     * @param string $str The literal string to be quoted.
3085
     *
3086
     * @return string The quoted literal string.
3087 38
     */
3088
    public function quoteStringLiteral(string $str) : string
3089 38
    {
3090
        $c = $this->getStringLiteralQuoteCharacter();
3091
3092
        return $c . str_replace($c, $c . $c, $str) . $c;
3093
    }
3094
3095
    /**
3096
     * Gets the character used for string literal quoting.
3097 19
     */
3098
    public function getStringLiteralQuoteCharacter() : string
3099 19
    {
3100
        return "'";
3101
    }
3102
3103
    /**
3104
     * Escapes metacharacters in a string intended to be used with a LIKE
3105
     * operator.
3106
     *
3107 247
     * @param string $inputString a literal, unquoted string
3108
     * @param string $escapeChar  should be reused by the caller in the LIKE
3109 247
     *                            expression.
3110
     */
3111
    final public function escapeStringForLike(string $inputString, string $escapeChar) : string
3112
    {
3113
        return preg_replace(
3114
            '~([' . preg_quote($this->getLikeWildcardCharacters() . $escapeChar, '~') . '])~u',
3115
            addcslashes($escapeChar, '\\') . '$1',
3116
            $inputString
3117 34
        );
3118
    }
3119 34
3120
    protected function getLikeWildcardCharacters() : string
3121
    {
3122
        return '%_';
3123
    }
3124
}
3125