AbstractPlatform::getColumnDeclarationSQL()   F
last analyzed

Complexity

Conditions 15
Paths 2049

Size

Total Lines 30
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 15

Importance

Changes 0
Metric Value
eloc 18
dl 0
loc 30
ccs 18
cts 18
cp 1
rs 1.7499
c 0
b 0
f 0
cc 15
nc 2049
nop 2
crap 15

How to fix   Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1714
    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...
1715
    {
1716
        throw DBALException::notSupported(__METHOD__);
1717
    }
1718
1719
    /**
1720
     * Returns the SQL to change a sequence on this platform.
1721
     *
1722
     * @return string
1723
     *
1724
     * @throws DBALException If not supported on this platform.
1725
     */
1726
    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

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

1836
        return 'ALTER TABLE ' . /** @scrutinizer ignore-type */ $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index) . ')';
Loading history...
1837
    }
1838
1839
    /**
1840
     * Returns the SQL to create a named schema.
1841
     *
1842
     * @param string $schemaName
1843
     *
1844
     * @return string
1845
     *
1846
     * @throws DBALException If not supported on this platform.
1847
     */
1848 270
    public function getCreateSchemaSQL($schemaName)
0 ignored issues
show
Unused Code introduced by
The parameter $schemaName is not used and could be removed. ( Ignorable by Annotation )

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

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

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

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

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

2293
            return " DEFAULT '" . /** @scrutinizer ignore-type */ $this->convertBooleans($default) . "'";
Loading history...
2294
        }
2295
2296 1017
        return " DEFAULT '" . $default . "'";
2297
    }
2298
2299
    /**
2300
     * Obtains DBMS specific SQL code portion needed to set a CHECK constraint
2301
     * declaration to be used in statements like CREATE TABLE.
2302
     *
2303
     * @param mixed[][] $definition The check definition.
2304
     *
2305
     * @return string DBMS specific SQL code portion needed to set a CHECK constraint.
2306
     */
2307 3981
    public function getCheckDeclarationSQL(array $definition)
2308
    {
2309 3981
        $constraints = [];
2310 3981
        foreach ($definition as $field => $def) {
2311 3981
            if (is_string($def)) {
2312
                $constraints[] = 'CHECK (' . $def . ')';
2313
            } else {
2314 3981
                if (isset($def['min'])) {
2315 135
                    $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')';
2316
                }
2317
2318 3981
                if (isset($def['max'])) {
2319 3981
                    $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')';
2320
                }
2321
            }
2322
        }
2323
2324 3981
        return implode(', ', $constraints);
2325
    }
2326
2327
    /**
2328
     * Obtains DBMS specific SQL code portion needed to set a unique
2329
     * constraint declaration to be used in statements like CREATE TABLE.
2330
     *
2331
     * @param string $name  The name of the unique constraint.
2332
     * @param Index  $index The index definition.
2333
     *
2334
     * @return string DBMS specific SQL code portion needed to set a constraint.
2335
     *
2336
     * @throws InvalidArgumentException
2337
     */
2338 756
    public function getUniqueConstraintDeclarationSQL($name, Index $index)
2339
    {
2340 756
        $columns = $index->getColumns();
2341 756
        $name    = new Identifier($name);
2342
2343 756
        if (count($columns) === 0) {
2344
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
2345
        }
2346
2347 756
        return 'CONSTRAINT ' . $name->getQuotedName($this) . ' UNIQUE ('
2348 756
            . $this->getIndexFieldDeclarationListSQL($index)
2349 756
            . ')' . $this->getPartialIndexSQL($index);
2350
    }
2351
2352
    /**
2353
     * Obtains DBMS specific SQL code portion needed to set an index
2354
     * 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
     *
2359
     * @return string DBMS specific SQL code portion needed to set an index.
2360
     *
2361
     * @throws InvalidArgumentException
2362
     */
2363 1480
    public function getIndexDeclarationSQL($name, Index $index)
2364
    {
2365 1480
        $columns = $index->getColumns();
2366 1480
        $name    = new Identifier($name);
2367
2368 1480
        if (count($columns) === 0) {
2369
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
2370
        }
2371
2372 1480
        return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name->getQuotedName($this) . ' ('
2373 1480
            . $this->getIndexFieldDeclarationListSQL($index)
2374 1480
            . ')' . $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
     *
2384
     * @return string
2385
     */
2386 501
    public function getCustomTypeDeclarationSQL(array $columnDef)
2387
    {
2388 501
        return $columnDef['columnDefinition'];
2389
    }
2390
2391
    /**
2392
     * Obtains DBMS specific SQL code portion needed to set an index
2393
     * declaration to be used in statements like CREATE TABLE.
2394
     *
2395
     * @param mixed[]|Index $columnsOrIndex array declaration is deprecated, prefer passing Index to this method
2396
     */
2397 7135
    public function getIndexFieldDeclarationListSQL($columnsOrIndex) : string
2398
    {
2399 7135
        if ($columnsOrIndex instanceof Index) {
2400 5947
            return implode(', ', $columnsOrIndex->getQuotedColumns($this));
2401
        }
2402
2403 1350
        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 1350
        $ret = [];
2408
2409 1350
        foreach ($columnsOrIndex as $column => $definition) {
2410 1350
            if (is_array($definition)) {
2411
                $ret[] = $column;
2412
            } else {
2413 1350
                $ret[] = $definition;
2414
            }
2415
        }
2416
2417 1350
        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
     *                to generate a temporary table, if possible.
2433
     */
2434
    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
     * @return string
2445
     */
2446 42
    public function getTemporaryTableName($tableName)
2447
    {
2448 42
        return $tableName;
2449
    }
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 3331
    public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey)
2459
    {
2460 3331
        $sql  = $this->getForeignKeyBaseDeclarationSQL($foreignKey);
2461 3007
        $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey);
2462
2463 3007
        return $sql;
2464
    }
2465
2466
    /**
2467
     * Returns the FOREIGN KEY query section dealing with non-standard options
2468
     * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
2469
     *
2470
     * @param ForeignKeyConstraint $foreignKey The foreign key definition.
2471
     *
2472
     * @return string
2473
     */
2474 2875
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
2475
    {
2476 2875
        $query = '';
2477 2875
        if ($this->supportsForeignKeyOnUpdate() && $foreignKey->hasOption('onUpdate')) {
2478 108
            $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate'));
2479
        }
2480 2875
        if ($foreignKey->hasOption('onDelete')) {
2481 267
            $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
2482
        }
2483
2484 2875
        return $query;
2485
    }
2486
2487
    /**
2488
     * Returns the given referential action in uppercase if valid, otherwise throws an exception.
2489
     *
2490
     * @param string $action The foreign key referential action.
2491
     *
2492
     * @return string
2493
     *
2494
     * @throws InvalidArgumentException If unknown referential action given.
2495
     */
2496 3291
    public function getForeignKeyReferentialActionSQL($action)
2497
    {
2498 3291
        $upper = strtoupper($action);
2499 3291
        switch ($upper) {
2500 3291
            case 'CASCADE':
2501 2221
            case 'SET NULL':
2502 1654
            case 'NO ACTION':
2503 1303
            case 'RESTRICT':
2504 925
            case 'SET DEFAULT':
2505 2832
                return $upper;
2506
            default:
2507 459
                throw new InvalidArgumentException('Invalid foreign key action: ' . $upper);
2508
        }
2509
    }
2510
2511
    /**
2512
     * Obtains DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2513
     * of a field declaration to be used in statements like CREATE TABLE.
2514
     *
2515
     * @return string
2516
     *
2517
     * @throws InvalidArgumentException
2518
     */
2519 2251
    public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey)
2520
    {
2521 2251
        $sql = '';
2522 2251
        if (strlen($foreignKey->getName())) {
2523 1873
            $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' ';
2524
        }
2525 2251
        $sql .= 'FOREIGN KEY (';
2526
2527 2251
        if (count($foreignKey->getLocalColumns()) === 0) {
2528
            throw new InvalidArgumentException("Incomplete definition. 'local' required.");
2529
        }
2530 2251
        if (count($foreignKey->getForeignColumns()) === 0) {
2531
            throw new InvalidArgumentException("Incomplete definition. 'foreign' required.");
2532
        }
2533 2251
        if (strlen($foreignKey->getForeignTableName()) === 0) {
2534
            throw new InvalidArgumentException("Incomplete definition. 'foreignTable' required.");
2535
        }
2536
2537 2251
        return $sql . implode(', ', $foreignKey->getQuotedLocalColumns($this))
2538 2251
            . ') REFERENCES '
2539 2251
            . $foreignKey->getQuotedForeignTableName($this) . ' ('
2540 2251
            . 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
    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
        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
     * @return string DBMS specific SQL code portion needed to set the COLLATION
2576
     *                of a field declaration.
2577
     */
2578 395
    public function getColumnCollationDeclarationSQL($collation)
2579
    {
2580 395
        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
     *
2587
     * @return bool
2588
     */
2589 135
    public function prefersSequences()
2590
    {
2591 135
        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 162
    public function prefersIdentityColumns()
2601
    {
2602 162
        return false;
2603
    }
2604
2605
    /**
2606
     * Some platforms need the boolean values to be converted.
2607
     *
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
     * @param mixed $item A boolean or an array of them.
2616
     *
2617
     * @return mixed A boolean database value or an array of them.
2618
     */
2619 621
    public function convertBooleans($item)
2620
    {
2621 621
        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 621
        } elseif (is_bool($item)) {
2630 594
            $item = (int) $item;
2631
        }
2632
2633 621
        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 1323
    public function convertFromBoolean($item)
2646
    {
2647 1323
        return $item === null ? null: (bool) $item;
2648
    }
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
     * @param mixed $item A boolean or an array of them.
2657
     *
2658
     * @return mixed A boolean database value or an array of them.
2659
     */
2660 202
    public function convertBooleansToDatabaseValue($item)
2661
    {
2662 202
        return $this->convertBooleans($item);
2663
    }
2664
2665
    /**
2666
     * Returns the SQL specific for the platform to get the current date.
2667
     *
2668
     * @return string
2669
     */
2670 323
    public function getCurrentDateSQL()
2671
    {
2672 323
        return 'CURRENT_DATE';
2673
    }
2674
2675
    /**
2676
     * Returns the SQL specific for the platform to get the current time.
2677
     *
2678
     * @return string
2679
     */
2680 12
    public function getCurrentTimeSQL()
2681
    {
2682 12
        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 474
    public function getCurrentTimestampSQL()
2691
    {
2692 474
        return 'CURRENT_TIMESTAMP';
2693
    }
2694
2695
    /**
2696
     * Returns the SQL for a given transaction isolation level Connection constant.
2697
     *
2698
     * @param int $level
2699
     *
2700
     * @return string
2701
     *
2702
     * @throws InvalidArgumentException
2703
     */
2704 297
    protected function _getTransactionIsolationLevelSQL($level)
2705
    {
2706 297
        switch ($level) {
2707
            case TransactionIsolationLevel::READ_UNCOMMITTED:
2708 297
                return 'READ UNCOMMITTED';
2709
            case TransactionIsolationLevel::READ_COMMITTED:
2710 297
                return 'READ COMMITTED';
2711
            case TransactionIsolationLevel::REPEATABLE_READ:
2712 297
                return 'REPEATABLE READ';
2713
            case TransactionIsolationLevel::SERIALIZABLE:
2714 297
                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 1
    public function getListDatabasesSQL()
2726
    {
2727 1
        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)
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
    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
        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 27
    public function getCreateDatabaseSQL($database)
2908
    {
2909 27
        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
     * @throws DBALException If not supported on this platform.
2935
     */
2936
    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 240
    public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration)
2949
    {
2950 240
        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
     * @return string
2976
     *
2977
     * @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 2631
    public function getFloatDeclarationSQL(array $fieldDeclaration)
2990
    {
2991 2631
        return 'DOUBLE PRECISION';
2992
    }
2993
2994
    /**
2995
     * Gets the default transaction isolation level of the platform.
2996
     *
2997
     * @see TransactionIsolationLevel
2998
     *
2999
     * @return int The default isolation level.
3000
     */
3001
    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
     */
3013 151
    public function supportsSequences()
3014
    {
3015 151
        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 29
    public function supportsIdentityColumns()
3027
    {
3028 29
        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 342
    public function usesSequenceEmulatedIdentityColumns()
3041
    {
3042 342
        return false;
3043
    }
3044
3045
    /**
3046
     * Returns the name of the sequence for a particular identity column in a particular table.
3047
     *
3048
     * @see    usesSequenceEmulatedIdentityColumns
3049
     *
3050
     * @param string $tableName  The name of the table to return the sequence name for.
3051
     * @param string $columnName The name of the identity column in the table to return the sequence name for.
3052
     *
3053
     * @return string
3054
     *
3055
     * @throws DBALException If not supported on this platform.
3056
     */
3057 324
    public function getIdentitySequenceName($tableName, $columnName)
3058
    {
3059 324
        throw DBALException::notSupported(__METHOD__);
3060
    }
3061
3062
    /**
3063
     * Whether the platform supports indexes.
3064
     *
3065
     * @return bool
3066
     */
3067 108
    public function supportsIndexes()
3068
    {
3069 108
        return true;
3070
    }
3071
3072
    /**
3073
     * Whether the platform supports partial indexes.
3074
     *
3075
     * @return bool
3076
     */
3077 4039
    public function supportsPartialIndexes()
3078
    {
3079 4039
        return false;
3080
    }
3081
3082
    /**
3083
     * Whether the platform supports indexes with column length definitions.
3084
     */
3085 8149
    public function supportsColumnLengthIndexes() : bool
3086
    {
3087 8149
        return false;
3088
    }
3089
3090
    /**
3091
     * Whether the platform supports altering tables.
3092
     *
3093
     * @return bool
3094
     */
3095 135
    public function supportsAlterTable()
3096
    {
3097 135
        return true;
3098
    }
3099
3100
    /**
3101
     * Whether the platform supports transactions.
3102
     *
3103
     * @return bool
3104
     */
3105 108
    public function supportsTransactions()
3106
    {
3107 108
        return true;
3108
    }
3109
3110
    /**
3111
     * Whether the platform supports savepoints.
3112
     *
3113
     * @return bool
3114
     */
3115 592
    public function supportsSavepoints()
3116
    {
3117 592
        return true;
3118
    }
3119
3120
    /**
3121
     * Whether the platform supports releasing savepoints.
3122
     *
3123
     * @return bool
3124
     */
3125 130
    public function supportsReleaseSavepoints()
3126
    {
3127 130
        return $this->supportsSavepoints();
3128
    }
3129
3130
    /**
3131
     * Whether the platform supports primary key constraints.
3132
     *
3133
     * @return bool
3134
     */
3135 108
    public function supportsPrimaryConstraints()
3136
    {
3137 108
        return true;
3138
    }
3139
3140
    /**
3141
     * Whether the platform supports foreign key constraints.
3142
     *
3143
     * @return bool
3144
     */
3145 12986
    public function supportsForeignKeyConstraints()
3146
    {
3147 12986
        return true;
3148
    }
3149
3150
    /**
3151
     * Whether this platform supports onUpdate in foreign key constraints.
3152
     *
3153
     * @return bool
3154
     */
3155 2983
    public function supportsForeignKeyOnUpdate()
3156
    {
3157 2983
        return $this->supportsForeignKeyConstraints() && true;
3158
    }
3159
3160
    /**
3161
     * Whether the platform supports database schemas.
3162
     *
3163
     * @return bool
3164
     */
3165 261
    public function supportsSchemas()
3166
    {
3167 261
        return false;
3168
    }
3169
3170
    /**
3171
     * Whether this platform can emulate schemas.
3172
     *
3173
     * Platforms that either support or emulate schemas don't automatically
3174
     * filter a schema for the namespaced elements in {@link
3175
     * AbstractManager#createSchema}.
3176
     *
3177
     * @return bool
3178
     */
3179 108
    public function canEmulateSchemas()
3180
    {
3181 108
        return false;
3182
    }
3183
3184
    /**
3185
     * Returns the default schema name.
3186
     *
3187
     * @return string
3188
     *
3189
     * @throws DBALException If not supported on this platform.
3190
     */
3191
    public function getDefaultSchemaName()
3192
    {
3193
        throw DBALException::notSupported(__METHOD__);
3194
    }
3195
3196
    /**
3197
     * Whether this platform supports create database.
3198
     *
3199
     * Some databases don't allow to create and drop databases at all or only with certain tools.
3200
     *
3201
     * @return bool
3202
     */
3203 152
    public function supportsCreateDropDatabase()
3204
    {
3205 152
        return true;
3206
    }
3207
3208
    /**
3209
     * Whether the platform supports getting the affected rows of a recent update/delete type query.
3210
     *
3211
     * @return bool
3212
     */
3213 108
    public function supportsGettingAffectedRows()
3214
    {
3215 108
        return true;
3216
    }
3217
3218
    /**
3219
     * Whether this platform support to add inline column comments as postfix.
3220
     *
3221
     * @return bool
3222
     */
3223 6612
    public function supportsInlineColumnComments()
3224
    {
3225 6612
        return false;
3226
    }
3227
3228
    /**
3229
     * Whether this platform support the proprietary syntax "COMMENT ON asset".
3230
     *
3231
     * @return bool
3232
     */
3233 6212
    public function supportsCommentOnStatement()
3234
    {
3235 6212
        return false;
3236
    }
3237
3238
    /**
3239
     * Does this platform have native guid type.
3240
     *
3241
     * @return bool
3242
     */
3243 8105
    public function hasNativeGuidType()
3244
    {
3245 8105
        return false;
3246
    }
3247
3248
    /**
3249
     * Does this platform have native JSON type.
3250
     *
3251
     * @return bool
3252
     */
3253 18928
    public function hasNativeJsonType()
3254
    {
3255 18928
        return false;
3256
    }
3257
3258
    /**
3259
     * @deprecated
3260
     *
3261
     * @todo Remove in 3.0
3262
     */
3263
    public function getIdentityColumnNullInsertSQL()
3264
    {
3265
        return '';
3266
    }
3267
3268
    /**
3269
     * Whether this platform supports views.
3270
     *
3271
     * @return bool
3272
     */
3273 27
    public function supportsViews()
3274
    {
3275 27
        return true;
3276
    }
3277
3278
    /**
3279
     * Does this platform support column collation?
3280
     *
3281
     * @return bool
3282
     */
3283
    public function supportsColumnCollation()
3284
    {
3285
        return false;
3286
    }
3287
3288
    /**
3289
     * Gets the format string, as accepted by the date() function, that describes
3290
     * the format of a stored datetime value of this platform.
3291
     *
3292
     * @return string The format string.
3293
     */
3294 468
    public function getDateTimeFormatString()
3295
    {
3296 468
        return 'Y-m-d H:i:s';
3297
    }
3298
3299
    /**
3300
     * Gets the format string, as accepted by the date() function, that describes
3301
     * the format of a stored datetime with timezone value of this platform.
3302
     *
3303
     * @return string The format string.
3304
     */
3305 205
    public function getDateTimeTzFormatString()
3306
    {
3307 205
        return 'Y-m-d H:i:s';
3308
    }
3309
3310
    /**
3311
     * Gets the format string, as accepted by the date() function, that describes
3312
     * the format of a stored date value of this platform.
3313
     *
3314
     * @return string The format string.
3315
     */
3316 185
    public function getDateFormatString()
3317
    {
3318 185
        return 'Y-m-d';
3319
    }
3320
3321
    /**
3322
     * Gets the format string, as accepted by the date() function, that describes
3323
     * the format of a stored time value of this platform.
3324
     *
3325
     * @return string The format string.
3326
     */
3327 158
    public function getTimeFormatString()
3328
    {
3329 158
        return 'H:i:s';
3330
    }
3331
3332
    /**
3333
     * Adds an driver-specific LIMIT clause to the query.
3334
     *
3335
     * @param string   $query
3336
     * @param int|null $limit
3337
     * @param int|null $offset
3338
     *
3339
     * @return string
3340
     *
3341
     * @throws DBALException
3342
     */
3343 3780
    final public function modifyLimitQuery($query, $limit, $offset = null)
3344
    {
3345 3780
        if ($limit !== null) {
3346 3024
            $limit = (int) $limit;
3347
        }
3348
3349 3780
        $offset = (int) $offset;
3350
3351 3780
        if ($offset < 0) {
3352
            throw new DBALException(sprintf(
3353
                'Offset must be a positive integer or zero, %d given',
3354
                $offset
3355
            ));
3356
        }
3357
3358 3780
        if ($offset > 0 && ! $this->supportsLimitOffset()) {
3359
            throw new DBALException(sprintf(
3360
                'Platform %s does not support offset values in limit queries.',
3361
                $this->getName()
3362
            ));
3363
        }
3364
3365 3780
        return $this->doModifyLimitQuery($query, $limit, $offset);
3366
    }
3367
3368
    /**
3369
     * Adds an platform-specific LIMIT clause to the query.
3370
     *
3371
     * @param string   $query
3372
     * @param int|null $limit
3373
     * @param int|null $offset
3374
     *
3375
     * @return string
3376
     */
3377 550
    protected function doModifyLimitQuery($query, $limit, $offset)
3378
    {
3379 550
        if ($limit !== null) {
3380 380
            $query .= ' LIMIT ' . $limit;
3381
        }
3382
3383 550
        if ($offset > 0) {
3384 56
            $query .= ' OFFSET ' . $offset;
3385
        }
3386
3387 550
        return $query;
3388
    }
3389
3390
    /**
3391
     * Whether the database platform support offsets in modify limit clauses.
3392
     *
3393
     * @return bool
3394
     */
3395 580
    public function supportsLimitOffset()
3396
    {
3397 580
        return true;
3398
    }
3399
3400
    /**
3401
     * Gets the character casing of a column in an SQL result set of this platform.
3402
     *
3403
     * @param string $column The column name for which to get the correct character casing.
3404
     *
3405
     * @return string The column name in the character casing used in SQL result sets.
3406
     */
3407
    public function getSQLResultCasing($column)
3408
    {
3409
        return $column;
3410
    }
3411
3412
    /**
3413
     * Makes any fixes to a name of a schema element (table, sequence, ...) that are required
3414
     * by restrictions of the platform, like a maximum length.
3415
     *
3416
     * @param string $schemaElementName
3417
     *
3418
     * @return string
3419
     */
3420
    public function fixSchemaElementName($schemaElementName)
3421
    {
3422
        return $schemaElementName;
3423
    }
3424
3425
    /**
3426
     * Maximum length of any given database identifier, like tables or column names.
3427
     *
3428
     * @return int
3429
     */
3430 446
    public function getMaxIdentifierLength()
3431
    {
3432 446
        return 63;
3433
    }
3434
3435
    /**
3436
     * Returns the insert SQL for an empty insert statement.
3437
     *
3438
     * @param string $tableName
3439
     * @param string $identifierColumnName
3440
     *
3441
     * @return string
3442
     */
3443 16
    public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName)
3444
    {
3445 16
        return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (null)';
3446
    }
3447
3448
    /**
3449
     * Generates a Truncate Table SQL statement for a given table.
3450
     *
3451
     * Cascade is not supported on many platforms but would optionally cascade the truncate by
3452
     * following the foreign keys.
3453
     *
3454
     * @param string $tableName
3455
     * @param bool   $cascade
3456
     *
3457
     * @return string
3458
     */
3459 207
    public function getTruncateTableSQL($tableName, $cascade = false)
0 ignored issues
show
Unused Code introduced by
The parameter $cascade is not used and could be removed. ( Ignorable by Annotation )

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

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