Completed
Push — master ( 7f79d0...1c7523 )
by Sergei
25:19 queued 22:43
created

AbstractPlatform::getCreateIndexSQL()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4.0092

Importance

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

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

341
    protected function getVarcharTypeDeclarationSQLSnippet($length, /** @scrutinizer ignore-unused */ $fixed)

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

Loading history...
342
    {
343
        throw DBALException::notSupported('VARCHARs not supported by Platform.');
344
    }
345
346
    /**
347
     * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
348
     *
349
     * @param int  $length The length of the column.
350
     * @param bool $fixed  Whether the column length is fixed.
351
     *
352
     * @return string
353
     *
354
     * @throws DBALException If not supported on this platform.
355
     */
356
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
0 ignored issues
show
Unused Code introduced by
The parameter $fixed is not used and could be removed. ( Ignorable by Annotation )

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

356
    protected function getBinaryTypeDeclarationSQLSnippet($length, /** @scrutinizer ignore-unused */ $fixed)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
1305
    {
1306
        throw DBALException::notSupported(__METHOD__);
1307
    }
1308
1309
    /**
1310
     * Returns the SQL bit AND comparison expression.
1311
     *
1312
     * @param string $value1
1313
     * @param string $value2
1314
     *
1315
     * @return string
1316
     */
1317 456
    public function getBitAndComparisonExpression($value1, $value2)
1318
    {
1319 456
        return '(' . $value1 . ' & ' . $value2 . ')';
1320
    }
1321
1322
    /**
1323
     * Returns the SQL bit OR comparison expression.
1324
     *
1325
     * @param string $value1
1326
     * @param string $value2
1327
     *
1328
     * @return string
1329
     */
1330 456
    public function getBitOrComparisonExpression($value1, $value2)
1331
    {
1332 456
        return '(' . $value1 . ' | ' . $value2 . ')';
1333
    }
1334
1335
    /**
1336
     * Returns the FOR UPDATE expression.
1337
     *
1338
     * @return string
1339
     */
1340 46
    public function getForUpdateSQL()
1341
    {
1342 46
        return 'FOR UPDATE';
1343
    }
1344
1345
    /**
1346
     * Honors that some SQL vendors such as MsSql use table hints for locking instead of the ANSI SQL FOR UPDATE specification.
1347
     *
1348
     * @param string   $fromClause The FROM clause to append the hint for the given lock mode to.
1349
     * @param int|null $lockMode   One of the Doctrine\DBAL\LockMode::* constants. If null is given, nothing will
1350
     *                             be appended to the FROM clause.
1351
     *
1352
     * @return string
1353
     */
1354 48
    public function appendLockHint($fromClause, $lockMode)
0 ignored issues
show
Unused Code introduced by
The parameter $lockMode is not used and could be removed. ( Ignorable by Annotation )

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

1354
    public function appendLockHint($fromClause, /** @scrutinizer ignore-unused */ $lockMode)

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

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

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

1713
    public function getCreateSequenceSQL(/** @scrutinizer ignore-unused */ Sequence $sequence)

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

Loading history...
1714
    {
1715
        throw DBALException::notSupported(__METHOD__);
1716
    }
1717
1718
    /**
1719
     * Returns the SQL to change a sequence on this platform.
1720
     *
1721
     * @return string
1722
     *
1723
     * @throws DBALException If not supported on this platform.
1724
     */
1725
    public function getAlterSequenceSQL(Sequence $sequence)
0 ignored issues
show
Unused Code introduced by
The parameter $sequence is not used and could be removed. ( Ignorable by Annotation )

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

1725
    public function getAlterSequenceSQL(/** @scrutinizer ignore-unused */ Sequence $sequence)

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

Loading history...
1726
    {
1727
        throw DBALException::notSupported(__METHOD__);
1728
    }
1729
1730
    /**
1731
     * Returns the SQL to create a constraint on a table on this platform.
1732
     *
1733
     * @param Table|string $table
1734
     *
1735
     * @return string
1736
     *
1737
     * @throws InvalidArgumentException
1738
     */
1739 476
    public function getCreateConstraintSQL(Constraint $constraint, $table)
1740
    {
1741 476
        if ($table instanceof Table) {
1742
            $table = $table->getQuotedName($this);
1743
        }
1744
1745 476
        $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this);
1746
1747 476
        $columnList = '(' . implode(', ', $constraint->getQuotedColumns($this)) . ')';
1748
1749 476
        $referencesClause = '';
1750 476
        if ($constraint instanceof Index) {
1751 476
            if ($constraint->isPrimary()) {
1752 476
                $query .= ' PRIMARY KEY';
1753 351
            } elseif ($constraint->isUnique()) {
1754 351
                $query .= ' UNIQUE';
1755
            } else {
1756
                throw new InvalidArgumentException(
1757 476
                    'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().'
1758
                );
1759
            }
1760 351
        } elseif ($constraint instanceof ForeignKeyConstraint) {
1761 351
            $query .= ' FOREIGN KEY';
1762
1763 351
            $referencesClause = ' REFERENCES ' . $constraint->getQuotedForeignTableName($this) .
1764 351
                ' (' . implode(', ', $constraint->getQuotedForeignColumns($this)) . ')';
1765
        }
1766 476
        $query .= ' ' . $columnList . $referencesClause;
1767
1768 476
        return $query;
1769
    }
1770
1771
    /**
1772
     * Returns the SQL to create an index on a table on this platform.
1773
     *
1774
     * @param Table|string $table The name of the table on which the index is to be created.
1775
     *
1776
     * @return string
1777
     *
1778
     * @throws InvalidArgumentException
1779
     */
1780 4289
    public function getCreateIndexSQL(Index $index, $table)
1781
    {
1782 4289
        if ($table instanceof Table) {
1783 27
            $table = $table->getQuotedName($this);
1784
        }
1785 4289
        $name    = $index->getQuotedName($this);
1786 4289
        $columns = $index->getColumns();
1787
1788 4289
        if (count($columns) === 0) {
1789
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
1790
        }
1791
1792 4289
        if ($index->isPrimary()) {
1793 567
            return $this->getCreatePrimaryKeySQL($index, $table);
1794
        }
1795
1796 3776
        $query  = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table;
1797 3776
        $query .= ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')' . $this->getPartialIndexSQL($index);
1798
1799 3776
        return $query;
1800
    }
1801
1802
    /**
1803
     * Adds condition for partial index.
1804
     *
1805
     * @return string
1806
     */
1807 5259
    protected function getPartialIndexSQL(Index $index)
1808
    {
1809 5259
        if ($this->supportsPartialIndexes() && $index->hasOption('where')) {
1810 142
            return ' WHERE ' . $index->getOption('where');
1811
        }
1812
1813 5117
        return '';
1814
    }
1815
1816
    /**
1817
     * Adds additional flags for index generation.
1818
     *
1819
     * @return string
1820
     */
1821 1843
    protected function getCreateIndexSQLFlags(Index $index)
1822
    {
1823 1843
        return $index->isUnique() ? 'UNIQUE ' : '';
1824
    }
1825
1826
    /**
1827
     * Returns the SQL to create an unnamed primary key constraint.
1828
     *
1829
     * @param Table|string $table
1830
     *
1831
     * @return string
1832
     */
1833 459
    public function getCreatePrimaryKeySQL(Index $index, $table)
1834
    {
1835 459
        if ($table instanceof Table) {
1836
            $table = $table->getQuotedName($this);
1837
        }
1838
1839 459
        return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index) . ')';
1840
    }
1841
1842
    /**
1843
     * Returns the SQL to create a named schema.
1844
     *
1845
     * @param string $schemaName
1846
     *
1847
     * @return string
1848
     *
1849
     * @throws DBALException If not supported on this platform.
1850
     */
1851 270
    public function getCreateSchemaSQL($schemaName)
0 ignored issues
show
Unused Code introduced by
The parameter $schemaName is not used and could be removed. ( Ignorable by Annotation )

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

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

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

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

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

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

2570
    public function getColumnCharsetDeclarationSQL(/** @scrutinizer ignore-unused */ $charset)

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

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

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

2887
    public function getDropSequenceSQL(/** @scrutinizer ignore-unused */ $sequence)

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

Loading history...
2888
    {
2889
        throw DBALException::notSupported(__METHOD__);
2890
    }
2891
2892
    /**
2893
     * @param string $sequenceName
2894
     *
2895
     * @return string
2896
     *
2897
     * @throws DBALException If not supported on this platform.
2898
     */
2899
    public function getSequenceNextValSQL($sequenceName)
0 ignored issues
show
Unused Code introduced by
The parameter $sequenceName is not used and could be removed. ( Ignorable by Annotation )

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

2899
    public function getSequenceNextValSQL(/** @scrutinizer ignore-unused */ $sequenceName)

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

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

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

3465
    public function getTruncateTableSQL($tableName, /** @scrutinizer ignore-unused */ $cascade = false)

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

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