Completed
Pull Request — develop (#3445)
by Evgeniy
64:46
created

AbstractPlatform::escapeStringForObject()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

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

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

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

788
            $expression .= /** @scrutinizer ignore-type */ $char . ' ';
Loading history...
789
        }
790 23
791
        if ($mode || $char !== false) {
792 23
            $expression .= 'FROM ';
793
        }
794
795
        return 'TRIM(' . $expression . $str . ')';
796
    }
797
798
    /**
799
     * Returns the SQL snippet to trim trailing space characters from the expression.
800
     *
801
     * @param string $str Literal string or column name.
802 23
     *
803
     * @return string
804 23
     */
805
    public function getRtrimExpression($str)
806
    {
807
        return 'RTRIM(' . $str . ')';
808
    }
809
810
    /**
811
     * Returns the SQL snippet to trim leading space characters from the expression.
812
     *
813
     * @param string $str Literal string or column name.
814
     *
815
     * @return string
816
     */
817
    public function getLtrimExpression($str)
818
    {
819
        return 'LTRIM(' . $str . ')';
820
    }
821
822
    /**
823
     * Returns the SQL snippet to change all characters from the expression to uppercase,
824
     * according to the current character set mapping.
825
     *
826
     * @param string $str Literal string or column name.
827
     *
828
     * @return string
829
     */
830
    public function getUpperExpression($str)
831
    {
832
        return 'UPPER(' . $str . ')';
833
    }
834
835
    /**
836
     * Returns the SQL snippet to change all characters from the expression to lowercase,
837
     * according to the current character set mapping.
838
     *
839
     * @param string $str Literal string or column name.
840
     *
841
     * @return string
842
     */
843
    public function getLowerExpression($str)
844
    {
845
        return 'LOWER(' . $str . ')';
846
    }
847
848
    /**
849
     * Returns the SQL snippet to get the position of the first occurrence of substring $substr in string $str.
850
     *
851
     * @param string   $str      Literal string.
852
     * @param string   $substr   Literal string to find.
853
     * @param int|bool $startPos Position to start at, beginning of string by default.
854
     *
855
     * @return string
856
     *
857
     * @throws DBALException If not supported on this platform.
858
     */
859
    public function getLocateExpression($str, $substr, $startPos = false)
0 ignored issues
show
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

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

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

1030
    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

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

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

1288
    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 $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

1288
    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 $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

1288
    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...
1289
    {
1290
        throw DBALException::notSupported(__METHOD__);
1291
    }
1292
1293
    /**
1294
     * Returns the SQL bit AND comparison expression.
1295
     *
1296
     * @param string $value1
1297
     * @param string $value2
1298
     *
1299 251
     * @return string
1300
     */
1301 251
    public function getBitAndComparisonExpression($value1, $value2)
1302
    {
1303
        return '(' . $value1 . ' & ' . $value2 . ')';
1304
    }
1305
1306
    /**
1307
     * Returns the SQL bit OR comparison expression.
1308
     *
1309 38
     * @param string $value1
1310
     * @param string $value2
1311 38
     *
1312
     * @return string
1313
     */
1314
    public function getBitOrComparisonExpression($value1, $value2)
1315
    {
1316
        return '(' . $value1 . ' | ' . $value2 . ')';
1317
    }
1318
1319
    /**
1320
     * Returns the FOR UPDATE expression.
1321
     *
1322
     * @return string
1323 40
     */
1324
    public function getForUpdateSQL()
1325 40
    {
1326
        return 'FOR UPDATE';
1327
    }
1328
1329
    /**
1330
     * Honors that some SQL vendors such as MsSql use table hints for locking instead of the ANSI SQL FOR UPDATE specification.
1331
     *
1332
     * @param string   $fromClause The FROM clause to append the hint for the given lock mode to.
1333
     * @param int|null $lockMode   One of the Doctrine\DBAL\LockMode::* constants. If null is given, nothing will
1334
     *                             be appended to the FROM clause.
1335
     *
1336
     * @return string
1337
     */
1338
    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

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

1706
    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...
1707
    {
1708
        throw DBALException::notSupported(__METHOD__);
1709
    }
1710
1711
    /**
1712
     * Returns the SQL to change a sequence on this platform.
1713
     *
1714
     * @return string
1715
     *
1716
     * @throws DBALException If not supported on this platform.
1717
     */
1718 323
    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

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

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

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

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

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

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

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

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

Loading history...
2831
    {
2832
        throw DBALException::notSupported(__METHOD__);
2833
    }
2834
2835
    /**
2836
     * @param string $table
2837
     *
2838
     * @return string
2839
     *
2840
     * @throws DBALException If not supported on this platform.
2841
     */
2842
    public function getListTableForeignKeysSQL($table)
2843
    {
2844
        throw DBALException::notSupported(__METHOD__);
2845
    }
2846
2847
    /**
2848
     * @param string $name
2849
     * @param string $sql
2850
     *
2851
     * @return string
2852
     *
2853
     * @throws DBALException If not supported on this platform.
2854
     */
2855
    public function getCreateViewSQL($name, $sql)
2856
    {
2857
        throw DBALException::notSupported(__METHOD__);
2858
    }
2859
2860
    /**
2861
     * @param string $name
2862
     *
2863
     * @return string
2864
     *
2865
     * @throws DBALException If not supported on this platform.
2866
     */
2867
    public function getDropViewSQL($name)
2868
    {
2869
        throw DBALException::notSupported(__METHOD__);
2870
    }
2871
2872
    /**
2873
     * Returns the SQL snippet to drop an existing sequence.
2874
     *
2875
     * @param Sequence|string $sequence
2876
     *
2877
     * @return string
2878
     *
2879
     * @throws DBALException If not supported on this platform.
2880
     */
2881
    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

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

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

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