Completed
Pull Request — master (#3610)
by Sergei
06:29
created

AbstractPlatform::getCreateConstraintSQL()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 6.042

Importance

Changes 0
Metric Value
dl 0
loc 31
ccs 17
cts 19
cp 0.8947
rs 8.8017
c 0
b 0
f 0
cc 6
nc 10
nop 2
crap 6.042
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 UnexpectedValueException;
31
use const E_USER_DEPRECATED;
32
use function addcslashes;
33
use function array_map;
34
use function array_merge;
35
use function array_unique;
36
use function array_values;
37
use function assert;
38
use function count;
39
use function explode;
40
use function func_get_arg;
41
use function func_get_args;
42
use function func_num_args;
43
use function implode;
44
use function in_array;
45
use function is_array;
46
use function is_bool;
47
use function is_int;
48
use function is_string;
49
use function preg_quote;
50
use function preg_replace;
51
use function sprintf;
52
use function str_replace;
53
use function strlen;
54
use function strpos;
55
use function strtolower;
56
use function strtoupper;
57
use function trigger_error;
58
59
/**
60
 * Base class for all DatabasePlatforms. The DatabasePlatforms are the central
61
 * point of abstraction of platform-specific behaviors, features and SQL dialects.
62
 * They are a passive source of information.
63
 *
64
 * @todo Remove any unnecessary methods.
65
 */
66
abstract class AbstractPlatform
67
{
68
    public const CREATE_INDEXES = 1;
69
70
    public const CREATE_FOREIGNKEYS = 2;
71
72
    /**
73
     * @deprecated Use DateIntervalUnit::INTERVAL_UNIT_SECOND.
74
     */
75
    public const DATE_INTERVAL_UNIT_SECOND = DateIntervalUnit::SECOND;
76
77
    /**
78
     * @deprecated Use DateIntervalUnit::MINUTE.
79
     */
80
    public const DATE_INTERVAL_UNIT_MINUTE = DateIntervalUnit::MINUTE;
81
82
    /**
83
     * @deprecated Use DateIntervalUnit::HOUR.
84
     */
85
    public const DATE_INTERVAL_UNIT_HOUR = DateIntervalUnit::HOUR;
86
87
    /**
88
     * @deprecated Use DateIntervalUnit::DAY.
89
     */
90
    public const DATE_INTERVAL_UNIT_DAY = DateIntervalUnit::DAY;
91
92
    /**
93
     * @deprecated Use DateIntervalUnit::WEEK.
94
     */
95
    public const DATE_INTERVAL_UNIT_WEEK = DateIntervalUnit::WEEK;
96
97
    /**
98
     * @deprecated Use DateIntervalUnit::MONTH.
99
     */
100
    public const DATE_INTERVAL_UNIT_MONTH = DateIntervalUnit::MONTH;
101
102
    /**
103
     * @deprecated Use DateIntervalUnit::QUARTER.
104
     */
105
    public const DATE_INTERVAL_UNIT_QUARTER = DateIntervalUnit::QUARTER;
106
107
    /**
108
     * @deprecated Use DateIntervalUnit::QUARTER.
109
     */
110
    public const DATE_INTERVAL_UNIT_YEAR = DateIntervalUnit::YEAR;
111
112
    /**
113
     * @deprecated Use TrimMode::UNSPECIFIED.
114
     */
115
    public const TRIM_UNSPECIFIED = TrimMode::UNSPECIFIED;
116
117
    /**
118
     * @deprecated Use TrimMode::LEADING.
119
     */
120
    public const TRIM_LEADING = TrimMode::LEADING;
121
122
    /**
123
     * @deprecated Use TrimMode::TRAILING.
124
     */
125
    public const TRIM_TRAILING = TrimMode::TRAILING;
126
127
    /**
128
     * @deprecated Use TrimMode::BOTH.
129
     */
130
    public const TRIM_BOTH = TrimMode::BOTH;
131
132
    /** @var string[]|null */
133
    protected $doctrineTypeMapping = null;
134
135
    /**
136
     * Contains a list of all columns that should generate parseable column comments for type-detection
137
     * in reverse engineering scenarios.
138
     *
139
     * @var string[]|null
140
     */
141
    protected $doctrineTypeComments = null;
142
143
    /** @var EventManager */
144
    protected $_eventManager;
145
146
    /**
147
     * Holds the KeywordList instance for the current platform.
148
     *
149
     * @var KeywordList|null
150
     */
151
    protected $_keywords;
152
153 71002
    public function __construct()
154
    {
155 71002
    }
156
157
    /**
158
     * Sets the EventManager used by the Platform.
159
     */
160 64180
    public function setEventManager(EventManager $eventManager)
161
    {
162 64180
        $this->_eventManager = $eventManager;
163 64180
    }
164
165
    /**
166
     * Gets the EventManager used by the Platform.
167
     *
168
     * @return EventManager
169
     */
170 61364
    public function getEventManager()
171
    {
172 61364
        return $this->_eventManager;
173
    }
174
175
    /**
176
     * Returns the SQL snippet that declares a boolean column.
177
     *
178
     * @param mixed[] $columnDef
179
     *
180
     * @return string
181
     */
182
    abstract public function getBooleanTypeDeclarationSQL(array $columnDef);
183
184
    /**
185
     * Returns the SQL snippet that declares a 4 byte integer column.
186
     *
187
     * @param mixed[] $columnDef
188
     *
189
     * @return string
190
     */
191
    abstract public function getIntegerTypeDeclarationSQL(array $columnDef);
192
193
    /**
194
     * Returns the SQL snippet that declares an 8 byte integer column.
195
     *
196
     * @param mixed[] $columnDef
197
     *
198
     * @return string
199
     */
200
    abstract public function getBigIntTypeDeclarationSQL(array $columnDef);
201
202
    /**
203
     * Returns the SQL snippet that declares a 2 byte integer column.
204
     *
205
     * @param mixed[] $columnDef
206
     *
207
     * @return string
208
     */
209
    abstract public function getSmallIntTypeDeclarationSQL(array $columnDef);
210
211
    /**
212
     * Returns the SQL snippet that declares common properties of an integer column.
213
     *
214
     * @param mixed[] $columnDef
215
     *
216
     * @return string
217
     */
218
    abstract protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef);
219
220
    /**
221
     * Lazy load Doctrine Type Mappings.
222
     *
223
     * @return void
224
     */
225
    abstract protected function initializeDoctrineTypeMappings();
226
227
    /**
228
     * Initializes Doctrine Type Mappings with the platform defaults
229
     * and with all additional type mappings.
230
     *
231
     * @return void
232
     */
233 61444
    private function initializeAllDoctrineTypeMappings()
234
    {
235 61444
        $this->initializeDoctrineTypeMappings();
236
237 61444
        foreach (Type::getTypesMap() as $typeName => $className) {
238 61444
            foreach (Type::getType($typeName)->getMappedDatabaseTypes($this) as $dbType) {
239 34252
                $this->doctrineTypeMapping[$dbType] = $typeName;
240
            }
241
        }
242 61444
    }
243
244
    /**
245
     * Returns the SQL snippet used to declare a VARCHAR column type.
246
     *
247
     * @param mixed[] $field
248
     *
249
     * @return string
250
     */
251 63503
    public function getVarcharTypeDeclarationSQL(array $field)
252
    {
253 63503
        if (! isset($field['length'])) {
254 59802
            $field['length'] = $this->getVarcharDefaultLength();
255
        }
256
257 63503
        $fixed = $field['fixed'] ?? false;
258
259 63503
        $maxLength = $fixed
260 61490
            ? $this->getCharMaxLength()
261 63503
            : $this->getVarcharMaxLength();
262
263 63503
        if ($field['length'] > $maxLength) {
264
            return $this->getClobTypeDeclarationSQL($field);
265
        }
266
267 63503
        return $this->getVarcharTypeDeclarationSQLSnippet($field['length'], $fixed);
268
    }
269
270
    /**
271
     * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
272
     *
273
     * @param mixed[] $field The column definition.
274
     *
275
     * @return string
276
     */
277 59964
    public function getBinaryTypeDeclarationSQL(array $field)
278
    {
279 59964
        if (! isset($field['length'])) {
280 59909
            $field['length'] = $this->getBinaryDefaultLength();
281
        }
282
283 59964
        $fixed = $field['fixed'] ?? false;
284
285 59964
        $maxLength = $this->getBinaryMaxLength();
286
287 59964
        if ($field['length'] > $maxLength) {
288 57659
            if ($maxLength > 0) {
289 57399
                @trigger_error(sprintf(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
290 24
                    'Binary field length %d is greater than supported by the platform (%d). Reduce the field length or use a BLOB field instead.',
291 57399
                    $field['length'],
292 57399
                    $maxLength
293 57399
                ), E_USER_DEPRECATED);
294
            }
295
296 57659
            return $this->getBlobTypeDeclarationSQL($field);
297
        }
298
299 59940
        return $this->getBinaryTypeDeclarationSQLSnippet($field['length'], $fixed);
300
    }
301
302
    /**
303
     * Returns the SQL snippet to declare a GUID/UUID field.
304
     *
305
     * By default this maps directly to a CHAR(36) and only maps to more
306
     * special datatypes when the underlying databases support this datatype.
307
     *
308
     * @param mixed[] $field
309
     *
310
     * @return string
311
     */
312 59136
    public function getGuidTypeDeclarationSQL(array $field)
313
    {
314 59136
        $field['length'] = 36;
315 59136
        $field['fixed']  = true;
316
317 59136
        return $this->getVarcharTypeDeclarationSQL($field);
318
    }
319
320
    /**
321
     * Returns the SQL snippet to declare a JSON field.
322
     *
323
     * By default this maps directly to a CLOB and only maps to more
324
     * special datatypes when the underlying databases support this datatype.
325
     *
326
     * @param mixed[] $field
327
     *
328
     * @return string
329
     */
330 57561
    public function getJsonTypeDeclarationSQL(array $field)
331
    {
332 57561
        return $this->getClobTypeDeclarationSQL($field);
333
    }
334
335
    /**
336
     * @param int  $length
337
     * @param bool $fixed
338
     *
339
     * @return string
340
     *
341
     * @throws DBALException If not supported on this platform.
342
     */
343
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
344
    {
345
        throw DBALException::notSupported('VARCHARs not supported by Platform.');
346
    }
347
348
    /**
349
     * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
350
     *
351
     * @param int  $length The length of the column.
352
     * @param bool $fixed  Whether the column length is fixed.
353
     *
354
     * @return string
355
     *
356
     * @throws DBALException If not supported on this platform.
357
     */
358
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
359
    {
360
        throw DBALException::notSupported('BINARY/VARBINARY column types are not supported by this platform.');
361
    }
362
363
    /**
364
     * Returns the SQL snippet used to declare a CLOB column type.
365
     *
366
     * @param mixed[] $field
367
     *
368
     * @return string
369
     */
370
    abstract public function getClobTypeDeclarationSQL(array $field);
371
372
    /**
373
     * Returns the SQL Snippet used to declare a BLOB column type.
374
     *
375
     * @param mixed[] $field
376
     *
377
     * @return string
378
     */
379
    abstract public function getBlobTypeDeclarationSQL(array $field);
380
381
    /**
382
     * Gets the name of the platform.
383
     *
384
     * @return string
385
     */
386
    abstract public function getName();
387
388
    /**
389
     * Registers a doctrine type to be used in conjunction with a column type of this platform.
390
     *
391
     * @param string $dbType
392
     * @param string $doctrineType
393
     *
394
     * @throws DBALException If the type is not found.
395
     */
396 59331
    public function registerDoctrineTypeMapping($dbType, $doctrineType)
397
    {
398 59331
        if ($this->doctrineTypeMapping === null) {
399 58660
            $this->initializeAllDoctrineTypeMappings();
400
        }
401
402 59331
        if (! Types\Type::hasType($doctrineType)) {
403 56886
            throw DBALException::typeNotFound($doctrineType);
404
        }
405
406 59295
        $dbType                             = strtolower($dbType);
407 59295
        $this->doctrineTypeMapping[$dbType] = $doctrineType;
408
409 59295
        $doctrineType = Type::getType($doctrineType);
410
411 59295
        if (! $doctrineType->requiresSQLCommentHint($this)) {
412 59259
            return;
413
        }
414
415 56861
        $this->markDoctrineTypeCommented($doctrineType);
416 56861
    }
417
418
    /**
419
     * Gets the Doctrine type that is mapped for the given database column type.
420
     *
421
     * @param string $dbType
422
     *
423
     * @return string
424
     *
425
     * @throws DBALException
426
     */
427 61476
    public function getDoctrineTypeMapping($dbType)
428
    {
429 61476
        if ($this->doctrineTypeMapping === null) {
430 61288
            $this->initializeAllDoctrineTypeMappings();
431
        }
432
433 61476
        $dbType = strtolower($dbType);
434
435 61476
        if (! isset($this->doctrineTypeMapping[$dbType])) {
436 56936
            throw new DBALException('Unknown database type ' . $dbType . ' requested, ' . static::class . ' may not support it.');
437
        }
438
439 61440
        return $this->doctrineTypeMapping[$dbType];
440
    }
441
442
    /**
443
     * Checks if a database type is currently supported by this platform.
444
     *
445
     * @param string $dbType
446
     *
447
     * @return bool
448
     */
449 58571
    public function hasDoctrineTypeMappingFor($dbType)
450
    {
451 58571
        if ($this->doctrineTypeMapping === null) {
452 55271
            $this->initializeAllDoctrineTypeMappings();
453
        }
454
455 58571
        $dbType = strtolower($dbType);
456
457 58571
        return isset($this->doctrineTypeMapping[$dbType]);
458
    }
459
460
    /**
461
     * Initializes the Doctrine Type comments instance variable for in_array() checks.
462
     *
463
     * @return void
464
     */
465 65013
    protected function initializeCommentedDoctrineTypes()
466
    {
467 65013
        $this->doctrineTypeComments = [];
468
469 65013
        foreach (Type::getTypesMap() as $typeName => $className) {
470 65013
            $type = Type::getType($typeName);
471
472 65013
            if (! $type->requiresSQLCommentHint($this)) {
473 65013
                continue;
474
            }
475
476 65013
            $this->doctrineTypeComments[] = $typeName;
477
        }
478 65013
    }
479
480
    /**
481
     * Is it necessary for the platform to add a parsable type comment to allow reverse engineering the given type?
482
     *
483
     * @return bool
484
     */
485 65266
    public function isCommentedDoctrineType(Type $doctrineType)
486
    {
487 65266
        if ($this->doctrineTypeComments === null) {
488 64977
            $this->initializeCommentedDoctrineTypes();
489
        }
490
491 65266
        assert(is_array($this->doctrineTypeComments));
492
493 65266
        return in_array($doctrineType->getName(), $this->doctrineTypeComments);
494
    }
495
496
    /**
497
     * Marks this type as to be commented in ALTER TABLE and CREATE TABLE statements.
498
     *
499
     * @param string|Type $doctrineType
500
     *
501
     * @return void
502
     */
503 56861
    public function markDoctrineTypeCommented($doctrineType)
504
    {
505 56861
        if ($this->doctrineTypeComments === null) {
506 56861
            $this->initializeCommentedDoctrineTypes();
507
        }
508
509 56861
        assert(is_array($this->doctrineTypeComments));
510
511 56861
        $this->doctrineTypeComments[] = $doctrineType instanceof Type ? $doctrineType->getName() : $doctrineType;
512 56861
    }
513
514
    /**
515
     * Gets the comment to append to a column comment that helps parsing this type in reverse engineering.
516
     *
517
     * @return string
518
     */
519 60525
    public function getDoctrineTypeComment(Type $doctrineType)
520
    {
521 60525
        return '(DC2Type:' . $doctrineType->getName() . ')';
522
    }
523
524
    /**
525
     * Gets the comment of a passed column modified by potential doctrine type comment hints.
526
     *
527
     * @return string|null
528
     */
529 64332
    protected function getColumnComment(Column $column)
530
    {
531 64332
        $comment = $column->getComment();
532
533 64332
        if ($this->isCommentedDoctrineType($column->getType())) {
534 60525
            $comment .= $this->getDoctrineTypeComment($column->getType());
535
        }
536
537 64332
        return $comment;
538
    }
539
540
    /**
541
     * Gets the character used for identifier quoting.
542
     *
543
     * @return string
544
     */
545 59745
    public function getIdentifierQuoteCharacter()
546
    {
547 59745
        return '"';
548
    }
549
550
    /**
551
     * Gets the string portion that starts an SQL comment.
552
     *
553
     * @return string
554
     */
555
    public function getSqlCommentStartString()
556
    {
557
        return '--';
558
    }
559
560
    /**
561
     * Gets the string portion that ends an SQL comment.
562
     *
563
     * @return string
564
     */
565
    public function getSqlCommentEndString()
566
    {
567
        return "\n";
568
    }
569
570
    /**
571
     * Gets the maximum length of a char field.
572
     */
573 61251
    public function getCharMaxLength() : int
574
    {
575 61251
        return $this->getVarcharMaxLength();
576
    }
577
578
    /**
579
     * Gets the maximum length of a varchar field.
580
     *
581
     * @return int
582
     */
583 58694
    public function getVarcharMaxLength()
584
    {
585 58694
        return 4000;
586
    }
587
588
    /**
589
     * Gets the default length of a varchar field.
590
     *
591
     * @return int
592
     */
593 59778
    public function getVarcharDefaultLength()
594
    {
595 59778
        return 255;
596
    }
597
598
    /**
599
     * Gets the maximum length of a binary field.
600
     *
601
     * @return int
602
     */
603
    public function getBinaryMaxLength()
604
    {
605
        return 4000;
606
    }
607
608
    /**
609
     * Gets the default length of a binary field.
610
     *
611
     * @return int
612
     */
613 57846
    public function getBinaryDefaultLength()
614
    {
615 57846
        return 255;
616
    }
617
618
    /**
619
     * Gets all SQL wildcard characters of the platform.
620
     *
621
     * @return string[]
622
     */
623
    public function getWildcards()
624
    {
625
        return ['%', '_'];
626
    }
627
628
    /**
629
     * Returns the regular expression operator.
630
     *
631
     * @return string
632
     *
633
     * @throws DBALException If not supported on this platform.
634
     */
635 46510
    public function getRegexpExpression()
636
    {
637 46510
        throw DBALException::notSupported(__METHOD__);
638
    }
639
640
    /**
641
     * Returns the global unique identifier expression.
642
     *
643
     * @deprecated Use application-generated UUIDs instead
644
     *
645
     * @return string
646
     *
647
     * @throws DBALException If not supported on this platform.
648
     */
649
    public function getGuidExpression()
650
    {
651
        throw DBALException::notSupported(__METHOD__);
652
    }
653
654
    /**
655
     * Returns the SQL snippet to get the average value of a column.
656
     *
657
     * @param string $column The column to use.
658
     *
659
     * @return string Generated SQL including an AVG aggregate function.
660
     */
661
    public function getAvgExpression($column)
662
    {
663
        return 'AVG(' . $column . ')';
664
    }
665
666
    /**
667
     * Returns the SQL snippet to get the number of rows (without a NULL value) of a column.
668
     *
669
     * If a '*' is used instead of a column the number of selected rows is returned.
670
     *
671
     * @param string|int $column The column to use.
672
     *
673
     * @return string Generated SQL including a COUNT aggregate function.
674
     */
675
    public function getCountExpression($column)
676
    {
677
        return 'COUNT(' . $column . ')';
678
    }
679
680
    /**
681
     * Returns the SQL snippet to get the highest value of a column.
682
     *
683
     * @param string $column The column to use.
684
     *
685
     * @return string Generated SQL including a MAX aggregate function.
686
     */
687
    public function getMaxExpression($column)
688
    {
689
        return 'MAX(' . $column . ')';
690
    }
691
692
    /**
693
     * Returns the SQL snippet to get the lowest value of a column.
694
     *
695
     * @param string $column The column to use.
696
     *
697
     * @return string Generated SQL including a MIN aggregate function.
698
     */
699
    public function getMinExpression($column)
700
    {
701
        return 'MIN(' . $column . ')';
702
    }
703
704
    /**
705
     * Returns the SQL snippet to get the total sum of a column.
706
     *
707
     * @param string $column The column to use.
708
     *
709
     * @return string Generated SQL including a SUM aggregate function.
710
     */
711
    public function getSumExpression($column)
712
    {
713
        return 'SUM(' . $column . ')';
714
    }
715
716
    // scalar functions
717
718
    /**
719
     * Returns the SQL snippet to get the md5 sum of a field.
720
     *
721
     * Note: Not SQL92, but common functionality.
722
     *
723
     * @param string $column
724
     *
725
     * @return string
726
     */
727
    public function getMd5Expression($column)
728
    {
729
        return 'MD5(' . $column . ')';
730
    }
731
732
    /**
733
     * Returns the SQL snippet to get the length of a text field.
734
     *
735
     * @param string $column
736
     *
737
     * @return string
738
     */
739
    public function getLengthExpression($column)
740
    {
741
        return 'LENGTH(' . $column . ')';
742
    }
743
744
    /**
745
     * Returns the SQL snippet to get the squared value of a column.
746
     *
747
     * @param string $column The column to use.
748
     *
749
     * @return string Generated SQL including an SQRT aggregate function.
750
     */
751
    public function getSqrtExpression($column)
752
    {
753
        return 'SQRT(' . $column . ')';
754
    }
755
756
    /**
757
     * Returns the SQL snippet to round a numeric field to the number of decimals specified.
758
     *
759
     * @param string $column
760
     * @param int    $decimals
761
     *
762
     * @return string
763
     */
764
    public function getRoundExpression($column, $decimals = 0)
765
    {
766
        return 'ROUND(' . $column . ', ' . $decimals . ')';
767
    }
768
769
    /**
770
     * Returns the SQL snippet to get the remainder of the division operation $expression1 / $expression2.
771
     *
772
     * @param string $expression1
773
     * @param string $expression2
774
     *
775
     * @return string
776
     */
777
    public function getModExpression($expression1, $expression2)
778
    {
779
        return 'MOD(' . $expression1 . ', ' . $expression2 . ')';
780
    }
781
782
    /**
783
     * Returns the SQL snippet to trim a string.
784
     *
785
     * @param string      $str  The expression to apply the trim to.
786
     * @param int         $mode The position of the trim (leading/trailing/both).
787
     * @param string|bool $char The char to trim, has to be quoted already. Defaults to space.
788
     *
789
     * @return string
790
     */
791 55284
    public function getTrimExpression($str, $mode = TrimMode::UNSPECIFIED, $char = false)
792
    {
793 55284
        $expression = '';
794
795 55284
        switch ($mode) {
796
            case TrimMode::LEADING:
797 55208
                $expression = 'LEADING ';
798 55208
                break;
799
800
            case TrimMode::TRAILING:
801 55186
                $expression = 'TRAILING ';
802 55186
                break;
803
804
            case TrimMode::BOTH:
805 55164
                $expression = 'BOTH ';
806 55164
                break;
807
        }
808
809 55284
        if ($char !== false) {
810 55180
            $expression .= $char . ' ';
811
        }
812
813 55284
        if ($mode || $char !== false) {
814 55258
            $expression .= 'FROM ';
815
        }
816
817 55284
        return 'TRIM(' . $expression . $str . ')';
818
    }
819
820
    /**
821
     * Returns the SQL snippet to trim trailing space characters from the expression.
822
     *
823
     * @param string $str Literal string or column name.
824
     *
825
     * @return string
826
     */
827 27608
    public function getRtrimExpression($str)
828
    {
829 27608
        return 'RTRIM(' . $str . ')';
830
    }
831
832
    /**
833
     * Returns the SQL snippet to trim leading space characters from the expression.
834
     *
835
     * @param string $str Literal string or column name.
836
     *
837
     * @return string
838
     */
839 27608
    public function getLtrimExpression($str)
840
    {
841 27608
        return 'LTRIM(' . $str . ')';
842
    }
843
844
    /**
845
     * Returns the SQL snippet to change all characters from the expression to uppercase,
846
     * according to the current character set mapping.
847
     *
848
     * @param string $str Literal string or column name.
849
     *
850
     * @return string
851
     */
852
    public function getUpperExpression($str)
853
    {
854
        return 'UPPER(' . $str . ')';
855
    }
856
857
    /**
858
     * Returns the SQL snippet to change all characters from the expression to lowercase,
859
     * according to the current character set mapping.
860
     *
861
     * @param string $str Literal string or column name.
862
     *
863
     * @return string
864
     */
865
    public function getLowerExpression($str)
866
    {
867
        return 'LOWER(' . $str . ')';
868
    }
869
870
    /**
871
     * Returns the SQL snippet to get the position of the first occurrence of substring $substr in string $str.
872
     *
873
     * @param string    $str      Literal string.
874
     * @param string    $substr   Literal string to find.
875
     * @param int|false $startPos Position to start at, beginning of string by default.
876
     *
877
     * @return string
878
     *
879
     * @throws DBALException If not supported on this platform.
880
     */
881
    public function getLocateExpression($str, $substr, $startPos = false)
882
    {
883
        throw DBALException::notSupported(__METHOD__);
884
    }
885
886
    /**
887
     * Returns the SQL snippet to get the current system date.
888
     *
889
     * @return string
890
     */
891
    public function getNowExpression()
892
    {
893
        return 'NOW()';
894
    }
895
896
    /**
897
     * Returns a SQL snippet to get a substring inside an SQL statement.
898
     *
899
     * Note: Not SQL92, but common functionality.
900
     *
901
     * SQLite only supports the 2 parameter variant of this function.
902
     *
903
     * @param string   $value  An sql string literal or column name/alias.
904
     * @param int      $from   Where to start the substring portion.
905
     * @param int|null $length The substring portion length.
906
     *
907
     * @return string
908
     */
909
    public function getSubstringExpression($value, $from, $length = null)
910
    {
911
        if ($length === null) {
912
            return 'SUBSTRING(' . $value . ' FROM ' . $from . ')';
913
        }
914
915
        return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $length . ')';
916
    }
917
918
    /**
919
     * Returns a SQL snippet to concatenate the given expressions.
920
     *
921
     * Accepts an arbitrary number of string parameters. Each parameter must contain an expression.
922
     *
923
     * @return string
924
     */
925 46487
    public function getConcatExpression()
926
    {
927 46487
        return implode(' || ', func_get_args());
928
    }
929
930
    /**
931
     * Returns the SQL for a logical not.
932
     *
933
     * Example:
934
     * <code>
935
     * $q = new Doctrine_Query();
936
     * $e = $q->expr;
937
     * $q->select('*')->from('table')
938
     *   ->where($e->eq('id', $e->not('null'));
939
     * </code>
940
     *
941
     * @param string $expression
942
     *
943
     * @return string The logical expression.
944
     */
945
    public function getNotExpression($expression)
946
    {
947
        return 'NOT(' . $expression . ')';
948
    }
949
950
    /**
951
     * Returns the SQL that checks if an expression is null.
952
     *
953
     * @param string $expression The expression that should be compared to null.
954
     *
955
     * @return string The logical expression.
956
     */
957 64072
    public function getIsNullExpression($expression)
958
    {
959 64072
        return $expression . ' IS NULL';
960
    }
961
962
    /**
963
     * Returns the SQL that checks if an expression is not null.
964
     *
965
     * @param string $expression The expression that should be compared to null.
966
     *
967
     * @return string The logical expression.
968
     */
969
    public function getIsNotNullExpression($expression)
970
    {
971
        return $expression . ' IS NOT NULL';
972
    }
973
974
    /**
975
     * Returns the SQL that checks if an expression evaluates to a value between two values.
976
     *
977
     * The parameter $expression is checked if it is between $value1 and $value2.
978
     *
979
     * Note: There is a slight difference in the way BETWEEN works on some databases.
980
     * http://www.w3schools.com/sql/sql_between.asp. If you want complete database
981
     * independence you should avoid using between().
982
     *
983
     * @param string $expression The value to compare to.
984
     * @param string $value1     The lower value to compare with.
985
     * @param string $value2     The higher value to compare with.
986
     *
987
     * @return string The logical expression.
988
     */
989
    public function getBetweenExpression($expression, $value1, $value2)
990
    {
991
        return $expression . ' BETWEEN ' . $value1 . ' AND ' . $value2;
992
    }
993
994
    /**
995
     * Returns the SQL to get the arccosine of a value.
996
     *
997
     * @param string $value
998
     *
999
     * @return string
1000
     */
1001
    public function getAcosExpression($value)
1002
    {
1003
        return 'ACOS(' . $value . ')';
1004
    }
1005
1006
    /**
1007
     * Returns the SQL to get the sine of a value.
1008
     *
1009
     * @param string $value
1010
     *
1011
     * @return string
1012
     */
1013
    public function getSinExpression($value)
1014
    {
1015
        return 'SIN(' . $value . ')';
1016
    }
1017
1018
    /**
1019
     * Returns the SQL to get the PI value.
1020
     *
1021
     * @return string
1022
     */
1023
    public function getPiExpression()
1024
    {
1025
        return 'PI()';
1026
    }
1027
1028
    /**
1029
     * Returns the SQL to get the cosine of a value.
1030
     *
1031
     * @param string $value
1032
     *
1033
     * @return string
1034
     */
1035
    public function getCosExpression($value)
1036
    {
1037
        return 'COS(' . $value . ')';
1038
    }
1039
1040
    /**
1041
     * Returns the SQL to calculate the difference in days between the two passed dates.
1042
     *
1043
     * Computes diff = date1 - date2.
1044
     *
1045
     * @param string $date1
1046
     * @param string $date2
1047
     *
1048
     * @return string
1049
     *
1050
     * @throws DBALException If not supported on this platform.
1051
     */
1052
    public function getDateDiffExpression($date1, $date2)
1053
    {
1054
        throw DBALException::notSupported(__METHOD__);
1055
    }
1056
1057
    /**
1058
     * Returns the SQL to add the number of given seconds to a date.
1059
     *
1060
     * @param string $date
1061
     * @param int    $seconds
1062
     *
1063
     * @return string
1064
     *
1065
     * @throws DBALException If not supported on this platform.
1066
     */
1067 62258
    public function getDateAddSecondsExpression($date, $seconds)
1068
    {
1069 62258
        return $this->getDateArithmeticIntervalExpression($date, '+', $seconds, DateIntervalUnit::SECOND);
1070
    }
1071
1072
    /**
1073
     * Returns the SQL to subtract the number of given seconds from a date.
1074
     *
1075
     * @param string $date
1076
     * @param int    $seconds
1077
     *
1078
     * @return string
1079
     *
1080
     * @throws DBALException If not supported on this platform.
1081
     */
1082 62258
    public function getDateSubSecondsExpression($date, $seconds)
1083
    {
1084 62258
        return $this->getDateArithmeticIntervalExpression($date, '-', $seconds, DateIntervalUnit::SECOND);
1085
    }
1086
1087
    /**
1088
     * Returns the SQL to add the number of given minutes to a date.
1089
     *
1090
     * @param string $date
1091
     * @param int    $minutes
1092
     *
1093
     * @return string
1094
     *
1095
     * @throws DBALException If not supported on this platform.
1096
     */
1097 62258
    public function getDateAddMinutesExpression($date, $minutes)
1098
    {
1099 62258
        return $this->getDateArithmeticIntervalExpression($date, '+', $minutes, DateIntervalUnit::MINUTE);
1100
    }
1101
1102
    /**
1103
     * Returns the SQL to subtract the number of given minutes from a date.
1104
     *
1105
     * @param string $date
1106
     * @param int    $minutes
1107
     *
1108
     * @return string
1109
     *
1110
     * @throws DBALException If not supported on this platform.
1111
     */
1112 62258
    public function getDateSubMinutesExpression($date, $minutes)
1113
    {
1114 62258
        return $this->getDateArithmeticIntervalExpression($date, '-', $minutes, DateIntervalUnit::MINUTE);
1115
    }
1116
1117
    /**
1118
     * Returns the SQL to add the number of given hours to a date.
1119
     *
1120
     * @param string $date
1121
     * @param int    $hours
1122
     *
1123
     * @return string
1124
     *
1125
     * @throws DBALException If not supported on this platform.
1126
     */
1127 62258
    public function getDateAddHourExpression($date, $hours)
1128
    {
1129 62258
        return $this->getDateArithmeticIntervalExpression($date, '+', $hours, DateIntervalUnit::HOUR);
1130
    }
1131
1132
    /**
1133
     * Returns the SQL to subtract the number of given hours to a date.
1134
     *
1135
     * @param string $date
1136
     * @param int    $hours
1137
     *
1138
     * @return string
1139
     *
1140
     * @throws DBALException If not supported on this platform.
1141
     */
1142 62258
    public function getDateSubHourExpression($date, $hours)
1143
    {
1144 62258
        return $this->getDateArithmeticIntervalExpression($date, '-', $hours, DateIntervalUnit::HOUR);
1145
    }
1146
1147
    /**
1148
     * Returns the SQL to add the number of given days to a date.
1149
     *
1150
     * @param string $date
1151
     * @param int    $days
1152
     *
1153
     * @return string
1154
     *
1155
     * @throws DBALException If not supported on this platform.
1156
     */
1157 62262
    public function getDateAddDaysExpression($date, $days)
1158
    {
1159 62262
        return $this->getDateArithmeticIntervalExpression($date, '+', $days, DateIntervalUnit::DAY);
1160
    }
1161
1162
    /**
1163
     * Returns the SQL to subtract the number of given days to a date.
1164
     *
1165
     * @param string $date
1166
     * @param int    $days
1167
     *
1168
     * @return string
1169
     *
1170
     * @throws DBALException If not supported on this platform.
1171
     */
1172 62258
    public function getDateSubDaysExpression($date, $days)
1173
    {
1174 62258
        return $this->getDateArithmeticIntervalExpression($date, '-', $days, DateIntervalUnit::DAY);
1175
    }
1176
1177
    /**
1178
     * Returns the SQL to add the number of given weeks to a date.
1179
     *
1180
     * @param string $date
1181
     * @param int    $weeks
1182
     *
1183
     * @return string
1184
     *
1185
     * @throws DBALException If not supported on this platform.
1186
     */
1187 62258
    public function getDateAddWeeksExpression($date, $weeks)
1188
    {
1189 62258
        return $this->getDateArithmeticIntervalExpression($date, '+', $weeks, DateIntervalUnit::WEEK);
1190
    }
1191
1192
    /**
1193
     * Returns the SQL to subtract the number of given weeks from a date.
1194
     *
1195
     * @param string $date
1196
     * @param int    $weeks
1197
     *
1198
     * @return string
1199
     *
1200
     * @throws DBALException If not supported on this platform.
1201
     */
1202 62258
    public function getDateSubWeeksExpression($date, $weeks)
1203
    {
1204 62258
        return $this->getDateArithmeticIntervalExpression($date, '-', $weeks, DateIntervalUnit::WEEK);
1205
    }
1206
1207
    /**
1208
     * Returns the SQL to add the number of given months to a date.
1209
     *
1210
     * @param string $date
1211
     * @param int    $months
1212
     *
1213
     * @return string
1214
     *
1215
     * @throws DBALException If not supported on this platform.
1216
     */
1217 62258
    public function getDateAddMonthExpression($date, $months)
1218
    {
1219 62258
        return $this->getDateArithmeticIntervalExpression($date, '+', $months, DateIntervalUnit::MONTH);
1220
    }
1221
1222
    /**
1223
     * Returns the SQL to subtract the number of given months to a date.
1224
     *
1225
     * @param string $date
1226
     * @param int    $months
1227
     *
1228
     * @return string
1229
     *
1230
     * @throws DBALException If not supported on this platform.
1231
     */
1232 62258
    public function getDateSubMonthExpression($date, $months)
1233
    {
1234 62258
        return $this->getDateArithmeticIntervalExpression($date, '-', $months, DateIntervalUnit::MONTH);
1235
    }
1236
1237
    /**
1238
     * Returns the SQL to add the number of given quarters to a date.
1239
     *
1240
     * @param string $date
1241
     * @param int    $quarters
1242
     *
1243
     * @return string
1244
     *
1245
     * @throws DBALException If not supported on this platform.
1246
     */
1247 62258
    public function getDateAddQuartersExpression($date, $quarters)
1248
    {
1249 62258
        return $this->getDateArithmeticIntervalExpression($date, '+', $quarters, DateIntervalUnit::QUARTER);
1250
    }
1251
1252
    /**
1253
     * Returns the SQL to subtract the number of given quarters from a date.
1254
     *
1255
     * @param string $date
1256
     * @param int    $quarters
1257
     *
1258
     * @return string
1259
     *
1260
     * @throws DBALException If not supported on this platform.
1261
     */
1262 62258
    public function getDateSubQuartersExpression($date, $quarters)
1263
    {
1264 62258
        return $this->getDateArithmeticIntervalExpression($date, '-', $quarters, DateIntervalUnit::QUARTER);
1265
    }
1266
1267
    /**
1268
     * Returns the SQL to add the number of given years to a date.
1269
     *
1270
     * @param string $date
1271
     * @param int    $years
1272
     *
1273
     * @return string
1274
     *
1275
     * @throws DBALException If not supported on this platform.
1276
     */
1277 62258
    public function getDateAddYearsExpression($date, $years)
1278
    {
1279 62258
        return $this->getDateArithmeticIntervalExpression($date, '+', $years, DateIntervalUnit::YEAR);
1280
    }
1281
1282
    /**
1283
     * Returns the SQL to subtract the number of given years from a date.
1284
     *
1285
     * @param string $date
1286
     * @param int    $years
1287
     *
1288
     * @return string
1289
     *
1290
     * @throws DBALException If not supported on this platform.
1291
     */
1292 62258
    public function getDateSubYearsExpression($date, $years)
1293
    {
1294 62258
        return $this->getDateArithmeticIntervalExpression($date, '-', $years, DateIntervalUnit::YEAR);
1295
    }
1296
1297
    /**
1298
     * Returns the SQL for a date arithmetic expression.
1299
     *
1300
     * @param string $date     The column or literal representing a date to perform the arithmetic operation on.
1301
     * @param string $operator The arithmetic operator (+ or -).
1302
     * @param int    $interval The interval that shall be calculated into the date.
1303
     * @param string $unit     The unit of the interval that shall be calculated into the date.
1304
     *                         One of the DATE_INTERVAL_UNIT_* constants.
1305
     *
1306
     * @return string
1307
     *
1308
     * @throws DBALException If not supported on this platform.
1309
     */
1310
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
1311
    {
1312
        throw DBALException::notSupported(__METHOD__);
1313
    }
1314
1315
    /**
1316
     * Returns the SQL bit AND comparison expression.
1317
     *
1318
     * @param string $value1
1319
     * @param string $value2
1320
     *
1321
     * @return string
1322
     */
1323 61953
    public function getBitAndComparisonExpression($value1, $value2)
1324
    {
1325 61953
        return '(' . $value1 . ' & ' . $value2 . ')';
1326
    }
1327
1328
    /**
1329
     * Returns the SQL bit OR comparison expression.
1330
     *
1331
     * @param string $value1
1332
     * @param string $value2
1333
     *
1334
     * @return string
1335
     */
1336 59804
    public function getBitOrComparisonExpression($value1, $value2)
1337
    {
1338 59804
        return '(' . $value1 . ' | ' . $value2 . ')';
1339
    }
1340
1341
    /**
1342
     * Returns the FOR UPDATE expression.
1343
     *
1344
     * @return string
1345
     */
1346 49277
    public function getForUpdateSQL()
1347
    {
1348 49277
        return 'FOR UPDATE';
1349
    }
1350
1351
    /**
1352
     * Honors that some SQL vendors such as MsSql use table hints for locking instead of the ANSI SQL FOR UPDATE specification.
1353
     *
1354
     * @param string   $fromClause The FROM clause to append the hint for the given lock mode to.
1355
     * @param int|null $lockMode   One of the Doctrine\DBAL\LockMode::* constants. If null is given, nothing will
1356
     *                             be appended to the FROM clause.
1357
     *
1358
     * @return string
1359
     */
1360 51622
    public function appendLockHint($fromClause, $lockMode)
1361
    {
1362 51622
        return $fromClause;
1363
    }
1364
1365
    /**
1366
     * Returns the SQL snippet to append to any SELECT statement which locks rows in shared read lock.
1367
     *
1368
     * This defaults to the ANSI SQL "FOR UPDATE", which is an exclusive lock (Write). Some database
1369
     * vendors allow to lighten this constraint up to be a real read lock.
1370
     *
1371
     * @return string
1372
     */
1373
    public function getReadLockSQL()
1374
    {
1375
        return $this->getForUpdateSQL();
1376
    }
1377
1378
    /**
1379
     * Returns the SQL snippet to append to any SELECT statement which obtains an exclusive lock on the rows.
1380
     *
1381
     * The semantics of this lock mode should equal the SELECT .. FOR UPDATE of the ANSI SQL standard.
1382
     *
1383
     * @return string
1384
     */
1385 56315
    public function getWriteLockSQL()
1386
    {
1387 56315
        return $this->getForUpdateSQL();
1388
    }
1389
1390
    /**
1391
     * Returns the SQL snippet to drop an existing database.
1392
     *
1393
     * @param string $database The name of the database that should be dropped.
1394
     *
1395
     * @return string
1396
     */
1397 49194
    public function getDropDatabaseSQL($database)
1398
    {
1399 49194
        return 'DROP DATABASE ' . $database;
1400
    }
1401
1402
    /**
1403
     * Returns the SQL snippet to drop an existing table.
1404
     *
1405
     * @param Table|string $table
1406
     *
1407
     * @return string
1408
     *
1409
     * @throws InvalidArgumentException
1410
     */
1411 63512
    public function getDropTableSQL($table)
1412
    {
1413 63512
        $tableArg = $table;
1414
1415 63512
        if ($table instanceof Table) {
1416 7000
            $table = $table->getQuotedName($this);
1417
        }
1418
1419 63512
        if (! is_string($table)) {
1420
            throw new InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.');
1421
        }
1422
1423 63512
        if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaDropTable)) {
1424 56461
            $eventArgs = new SchemaDropTableEventArgs($tableArg, $this);
1425 56461
            $this->_eventManager->dispatchEvent(Events::onSchemaDropTable, $eventArgs);
1426
1427 56461
            if ($eventArgs->isDefaultPrevented()) {
1428
                $sql = $eventArgs->getSql();
1429
1430
                if ($sql === null) {
1431
                    throw new UnexpectedValueException('Default implementation of DROP TABLE was overridden with NULL');
1432
                }
1433
1434
                return $sql;
1435
            }
1436
        }
1437
1438 63512
        return 'DROP TABLE ' . $table;
1439
    }
1440
1441
    /**
1442
     * Returns the SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction.
1443
     *
1444
     * @param Table|string $table
1445
     *
1446
     * @return string
1447
     */
1448 25795
    public function getDropTemporaryTableSQL($table)
1449
    {
1450 25795
        return $this->getDropTableSQL($table);
1451
    }
1452
1453
    /**
1454
     * Returns the SQL to drop an index from a table.
1455
     *
1456
     * @param Index|string $index
1457
     * @param Table|string $table
1458
     *
1459
     * @return string
1460
     *
1461
     * @throws InvalidArgumentException
1462
     */
1463 49007
    public function getDropIndexSQL($index, $table = null)
1464
    {
1465 49007
        if ($index instanceof Index) {
1466 48978
            $index = $index->getQuotedName($this);
1467 21627
        } elseif (! is_string($index)) {
1468
            throw new InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.');
1469
        }
1470
1471 49007
        return 'DROP INDEX ' . $index;
1472
    }
1473
1474
    /**
1475
     * Returns the SQL to drop a constraint.
1476
     *
1477
     * @param Constraint|string $constraint
1478
     * @param Table|string      $table
1479
     *
1480
     * @return string
1481
     */
1482 57154
    public function getDropConstraintSQL($constraint, $table)
1483
    {
1484 57154
        if (! $constraint instanceof Constraint) {
1485 55951
            $constraint = new Identifier($constraint);
1486
        }
1487
1488 57154
        if (! $table instanceof Table) {
1489 57154
            $table = new Identifier($table);
1490
        }
1491
1492 57154
        $constraint = $constraint->getQuotedName($this);
1493 57154
        $table      = $table->getQuotedName($this);
1494
1495 57154
        return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $constraint;
1496
    }
1497
1498
    /**
1499
     * Returns the SQL to drop a foreign key.
1500
     *
1501
     * @param ForeignKeyConstraint|string $foreignKey
1502
     * @param Table|string                $table
1503
     *
1504
     * @return string
1505
     */
1506 58243
    public function getDropForeignKeySQL($foreignKey, $table)
1507
    {
1508 58243
        if (! $foreignKey instanceof ForeignKeyConstraint) {
1509 55716
            $foreignKey = new Identifier($foreignKey);
1510
        }
1511
1512 58243
        if (! $table instanceof Table) {
1513 58243
            $table = new Identifier($table);
1514
        }
1515
1516 58243
        $foreignKey = $foreignKey->getQuotedName($this);
1517 58243
        $table      = $table->getQuotedName($this);
1518
1519 58243
        return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $foreignKey;
1520
    }
1521
1522
    /**
1523
     * Returns the SQL statement(s) to create a table with the specified name, columns and constraints
1524
     * on this platform.
1525
     *
1526
     * @param int $createFlags
1527
     *
1528
     * @return string[] The sequence of SQL statements.
1529
     *
1530
     * @throws DBALException
1531
     * @throws InvalidArgumentException
1532
     */
1533 64046
    public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDEXES)
1534
    {
1535 64046
        if (! is_int($createFlags)) {
1536
            throw new InvalidArgumentException('Second argument of AbstractPlatform::getCreateTableSQL() has to be integer.');
1537
        }
1538
1539 64046
        if (count($table->getColumns()) === 0) {
1540 56761
            throw DBALException::noColumnsSpecifiedForTable($table->getName());
1541
        }
1542
1543 64010
        $tableName                    = $table->getQuotedName($this);
1544 64010
        $options                      = $table->getOptions();
1545 64010
        $options['uniqueConstraints'] = [];
1546 64010
        $options['indexes']           = [];
1547 64010
        $options['primary']           = [];
1548
1549 64010
        if (($createFlags&self::CREATE_INDEXES) > 0) {
1550 63964
            foreach ($table->getIndexes() as $index) {
1551
                /** @var $index Index */
1552 63712
                if ($index->isPrimary()) {
1553 63580
                    $options['primary']       = $index->getQuotedColumns($this);
1554 63580
                    $options['primary_index'] = $index;
1555
                } else {
1556 62510
                    $options['indexes'][$index->getQuotedName($this)] = $index;
1557
                }
1558
            }
1559
        }
1560
1561 64010
        $columnSql = [];
1562 64010
        $columns   = [];
1563
1564 64010
        foreach ($table->getColumns() as $column) {
1565 64010
            if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn)) {
1566 56486
                $eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this);
1567 56486
                $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs);
1568
1569 56486
                $columnSql = array_merge($columnSql, $eventArgs->getSql());
1570
1571 56486
                if ($eventArgs->isDefaultPrevented()) {
1572
                    continue;
1573
                }
1574
            }
1575
1576 64010
            $columnData            = $column->toArray();
1577 64010
            $columnData['name']    = $column->getQuotedName($this);
1578 64010
            $columnData['version'] = $column->hasPlatformOption('version') ? $column->getPlatformOption('version') : false;
1579 64010
            $columnData['comment'] = $this->getColumnComment($column);
1580
1581 64010
            if ($columnData['type'] instanceof Types\StringType && $columnData['length'] === null) {
1582 63121
                $columnData['length'] = 255;
1583
            }
1584
1585 64010
            if (in_array($column->getName(), $options['primary'])) {
1586 63540
                $columnData['primary'] = true;
1587
            }
1588
1589 64010
            $columns[$columnData['name']] = $columnData;
1590
        }
1591
1592 64010
        if (($createFlags&self::CREATE_FOREIGNKEYS) > 0) {
1593 63558
            $options['foreignKeys'] = [];
1594 63558
            foreach ($table->getForeignKeys() as $fkConstraint) {
1595 61999
                $options['foreignKeys'][] = $fkConstraint;
1596
            }
1597
        }
1598
1599 64010
        if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) {
1600 56486
            $eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this);
1601 56486
            $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs);
1602
1603 56486
            if ($eventArgs->isDefaultPrevented()) {
1604
                return array_merge($eventArgs->getSql(), $columnSql);
1605
            }
1606
        }
1607
1608 64010
        $sql = $this->_getCreateTableSQL($tableName, $columns, $options);
1609 64010
        if ($this->supportsCommentOnStatement()) {
1610 60008
            foreach ($table->getColumns() as $column) {
1611 60008
                $comment = $this->getColumnComment($column);
1612
1613 60008
                if ($comment === null || $comment === '') {
1614 59974
                    continue;
1615
                }
1616
1617 57766
                $sql[] = $this->getCommentOnColumnSQL($tableName, $column->getQuotedName($this), $comment);
1618
            }
1619
        }
1620
1621 64010
        return array_merge($sql, $columnSql);
1622
    }
1623
1624
    /**
1625
     * @param string      $tableName
1626
     * @param string      $columnName
1627
     * @param string|null $comment
1628
     *
1629
     * @return string
1630
     */
1631 56667
    public function getCommentOnColumnSQL($tableName, $columnName, $comment)
1632
    {
1633 56667
        $tableName  = new Identifier($tableName);
1634 56667
        $columnName = new Identifier($columnName);
1635
1636 56667
        return sprintf(
1637 131
            'COMMENT ON COLUMN %s.%s IS %s',
1638 56667
            $tableName->getQuotedName($this),
1639 56667
            $columnName->getQuotedName($this),
1640 56667
            $this->quoteStringLiteral((string) $comment)
1641
        );
1642
    }
1643
1644
    /**
1645
     * Returns the SQL to create inline comment on a column.
1646
     *
1647
     * @param string $comment
1648
     *
1649
     * @return string
1650
     *
1651
     * @throws DBALException If not supported on this platform.
1652
     */
1653 58425
    public function getInlineColumnCommentSQL($comment)
1654
    {
1655 58425
        if (! $this->supportsInlineColumnComments()) {
1656 55603
            throw DBALException::notSupported(__METHOD__);
1657
        }
1658
1659 57517
        return 'COMMENT ' . $this->quoteStringLiteral($comment);
1660
    }
1661
1662
    /**
1663
     * Returns the SQL used to create a table.
1664
     *
1665
     * @param string    $tableName
1666
     * @param mixed[][] $columns
1667
     * @param mixed[]   $options
1668
     *
1669
     * @return string[]
1670
     */
1671 58178
    protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
1672
    {
1673 58178
        $columnListSql = $this->getColumnDeclarationListSQL($columns);
1674
1675 58178
        if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
1676
            foreach ($options['uniqueConstraints'] as $name => $definition) {
1677
                $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
1678
            }
1679
        }
1680
1681 58178
        if (isset($options['primary']) && ! empty($options['primary'])) {
1682 58052
            $columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')';
1683
        }
1684
1685 58178
        if (isset($options['indexes']) && ! empty($options['indexes'])) {
1686
            foreach ($options['indexes'] as $index => $definition) {
1687
                $columnListSql .= ', ' . $this->getIndexDeclarationSQL($index, $definition);
1688
            }
1689
        }
1690
1691 58178
        $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql;
1692
1693 58178
        $check = $this->getCheckDeclarationSQL($columns);
1694 58178
        if (! empty($check)) {
1695 57627
            $query .= ', ' . $check;
1696
        }
1697 58178
        $query .= ')';
1698
1699 58178
        $sql[] = $query;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$sql was never initialized. Although not strictly required by PHP, it is generally a good practice to add $sql = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
1700
1701 58178
        if (isset($options['foreignKeys'])) {
1702 58096
            foreach ((array) $options['foreignKeys'] as $definition) {
1703 57774
                $sql[] = $this->getCreateForeignKeySQL($definition, $tableName);
1704
            }
1705
        }
1706
1707 58178
        return $sql;
1708
    }
1709
1710
    /**
1711
     * @return string
1712
     */
1713 51575
    public function getCreateTemporaryTableSnippetSQL()
1714
    {
1715 51575
        return 'CREATE TEMPORARY TABLE';
1716
    }
1717
1718
    /**
1719
     * Returns the SQL to create a sequence on this platform.
1720
     *
1721
     * @return string
1722
     *
1723
     * @throws DBALException If not supported on this platform.
1724
     */
1725
    public function getCreateSequenceSQL(Sequence $sequence)
1726
    {
1727
        throw DBALException::notSupported(__METHOD__);
1728
    }
1729
1730
    /**
1731
     * Returns the SQL to change a sequence on this platform.
1732
     *
1733
     * @return string
1734
     *
1735
     * @throws DBALException If not supported on this platform.
1736
     */
1737
    public function getAlterSequenceSQL(Sequence $sequence)
1738
    {
1739
        throw DBALException::notSupported(__METHOD__);
1740
    }
1741
1742
    /**
1743
     * Returns the SQL to create a constraint on a table on this platform.
1744
     *
1745
     * @param Table|string $table
1746
     *
1747
     * @return string
1748
     *
1749
     * @throws InvalidArgumentException
1750
     */
1751 56651
    public function getCreateConstraintSQL(Constraint $constraint, $table)
1752
    {
1753 56651
        if ($table instanceof Table) {
1754
            $table = $table->getQuotedName($this);
1755
        }
1756
1757 56651
        $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this);
1758
1759 56651
        $columnList = '(' . implode(', ', $constraint->getQuotedColumns($this)) . ')';
1760
1761 56651
        $referencesClause = '';
1762 56651
        if ($constraint instanceof Index) {
1763 56651
            if ($constraint->isPrimary()) {
1764 56651
                $query .= ' PRIMARY KEY';
1765 56601
            } elseif ($constraint->isUnique()) {
1766 56601
                $query .= ' UNIQUE';
1767
            } else {
1768
                throw new InvalidArgumentException(
1769 56651
                    'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().'
1770
                );
1771
            }
1772 56601
        } elseif ($constraint instanceof ForeignKeyConstraint) {
1773 56601
            $query .= ' FOREIGN KEY';
1774
1775 56601
            $referencesClause = ' REFERENCES ' . $constraint->getQuotedForeignTableName($this) .
1776 56601
                ' (' . implode(', ', $constraint->getQuotedForeignColumns($this)) . ')';
1777
        }
1778 56651
        $query .= ' ' . $columnList . $referencesClause;
1779
1780 56651
        return $query;
1781
    }
1782
1783
    /**
1784
     * Returns the SQL to create an index on a table on this platform.
1785
     *
1786
     * @param Table|string $table The name of the table on which the index is to be created.
1787
     *
1788
     * @return string
1789
     *
1790
     * @throws InvalidArgumentException
1791
     */
1792 61061
    public function getCreateIndexSQL(Index $index, $table)
1793
    {
1794 61061
        if ($table instanceof Table) {
1795 60070
            $table = $table->getQuotedName($this);
1796
        }
1797 61061
        $name    = $index->getQuotedName($this);
1798 61061
        $columns = $index->getColumns();
1799
1800 61061
        if (count($columns) === 0) {
1801
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
1802
        }
1803
1804 61061
        if ($index->isPrimary()) {
1805 54817
            return $this->getCreatePrimaryKeySQL($index, $table);
1806
        }
1807
1808 61023
        $query  = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table;
1809 61023
        $query .= ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')' . $this->getPartialIndexSQL($index);
1810
1811 61023
        return $query;
1812
    }
1813
1814
    /**
1815
     * Adds condition for partial index.
1816
     *
1817
     * @return string
1818
     */
1819 62384
    protected function getPartialIndexSQL(Index $index)
1820
    {
1821 62384
        if ($this->supportsPartialIndexes() && $index->hasOption('where')) {
1822 47174
            return ' WHERE ' . $index->getOption('where');
1823
        }
1824
1825 62374
        return '';
1826
    }
1827
1828
    /**
1829
     * Adds additional flags for index generation.
1830
     *
1831
     * @return string
1832
     */
1833 59348
    protected function getCreateIndexSQLFlags(Index $index)
1834
    {
1835 59348
        return $index->isUnique() ? 'UNIQUE ' : '';
1836
    }
1837
1838
    /**
1839
     * Returns the SQL to create an unnamed primary key constraint.
1840
     *
1841
     * @param Table|string $table
1842
     *
1843
     * @return string
1844
     */
1845 57559
    public function getCreatePrimaryKeySQL(Index $index, $table)
1846
    {
1847 57559
        if ($table instanceof Table) {
1848
            $table = $table->getQuotedName($this);
1849
        }
1850
1851 57559
        return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index) . ')';
1852
    }
1853
1854
    /**
1855
     * Returns the SQL to create a named schema.
1856
     *
1857
     * @param string $schemaName
1858
     *
1859
     * @return string
1860
     *
1861
     * @throws DBALException If not supported on this platform.
1862
     */
1863 55995
    public function getCreateSchemaSQL($schemaName)
1864
    {
1865 55995
        throw DBALException::notSupported(__METHOD__);
1866
    }
1867
1868
    /**
1869
     * Quotes a string so that it can be safely used as a table or column name,
1870
     * even if it is a reserved word of the platform. This also detects identifier
1871
     * chains separated by dot and quotes them independently.
1872
     *
1873
     * NOTE: Just because you CAN use quoted identifiers doesn't mean
1874
     * you SHOULD use them. In general, they end up causing way more
1875
     * problems than they solve.
1876
     *
1877
     * @param string $str The identifier name to be quoted.
1878
     *
1879
     * @return string The quoted identifier string.
1880
     */
1881 63681
    public function quoteIdentifier($str)
1882
    {
1883 63681
        if (strpos($str, '.') !== false) {
1884 57710
            $parts = array_map([$this, 'quoteSingleIdentifier'], explode('.', $str));
1885
1886 57710
            return implode('.', $parts);
1887
        }
1888
1889 63681
        return $this->quoteSingleIdentifier($str);
1890
    }
1891
1892
    /**
1893
     * Quotes a single identifier (no dot chain separation).
1894
     *
1895
     * @param string $str The identifier name to be quoted.
1896
     *
1897
     * @return string The quoted identifier string.
1898
     */
1899 63254
    public function quoteSingleIdentifier($str)
1900
    {
1901 63254
        $c = $this->getIdentifierQuoteCharacter();
1902
1903 63254
        return $c . str_replace($c, $c . $c, $str) . $c;
1904
    }
1905
1906
    /**
1907
     * Returns the SQL to create a new foreign key.
1908
     *
1909
     * @param ForeignKeyConstraint $foreignKey The foreign key constraint.
1910
     * @param Table|string         $table      The name of the table on which the foreign key is to be created.
1911
     *
1912
     * @return string
1913
     */
1914 62019
    public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table)
1915
    {
1916 62019
        if ($table instanceof Table) {
1917 2927
            $table = $table->getQuotedName($this);
1918
        }
1919
1920 62019
        return 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey);
1921
    }
1922
1923
    /**
1924
     * Gets the SQL statements for altering an existing table.
1925
     *
1926
     * This method returns an array of SQL statements, since some platforms need several statements.
1927
     *
1928
     * @return string[]
1929
     *
1930
     * @throws DBALException If not supported on this platform.
1931
     */
1932
    public function getAlterTableSQL(TableDiff $diff)
1933
    {
1934
        throw DBALException::notSupported(__METHOD__);
1935
    }
1936
1937
    /**
1938
     * @param mixed[] $columnSql
1939
     *
1940
     * @return bool
1941
     */
1942 61072
    protected function onSchemaAlterTableAddColumn(Column $column, TableDiff $diff, &$columnSql)
1943
    {
1944 61072
        if ($this->_eventManager === null) {
1945 56660
            return false;
1946
        }
1947
1948 60912
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableAddColumn)) {
1949 60876
            return false;
1950
        }
1951
1952 56436
        $eventArgs = new SchemaAlterTableAddColumnEventArgs($column, $diff, $this);
1953 56436
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn, $eventArgs);
1954
1955 56436
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1956
1957 56436
        return $eventArgs->isDefaultPrevented();
1958
    }
1959
1960
    /**
1961
     * @param string[] $columnSql
1962
     *
1963
     * @return bool
1964
     */
1965 60171
    protected function onSchemaAlterTableRemoveColumn(Column $column, TableDiff $diff, &$columnSql)
1966
    {
1967 60171
        if ($this->_eventManager === null) {
1968 56614
            return false;
1969
        }
1970
1971 60057
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRemoveColumn)) {
1972 60021
            return false;
1973
        }
1974
1975 56436
        $eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column, $diff, $this);
1976 56436
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn, $eventArgs);
1977
1978 56436
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1979
1980 56436
        return $eventArgs->isDefaultPrevented();
1981
    }
1982
1983
    /**
1984
     * @param string[] $columnSql
1985
     *
1986
     * @return bool
1987
     */
1988 61035
    protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiff, TableDiff $diff, &$columnSql)
1989
    {
1990 61035
        if ($this->_eventManager === null) {
1991 57657
            return false;
1992
        }
1993
1994 60703
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) {
1995 60667
            return false;
1996
        }
1997
1998 56436
        $eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff, $diff, $this);
1999 56436
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn, $eventArgs);
2000
2001 56436
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
2002
2003 56436
        return $eventArgs->isDefaultPrevented();
2004
    }
2005
2006
    /**
2007
     * @param string   $oldColumnName
2008
     * @param string[] $columnSql
2009
     *
2010
     * @return bool
2011
     */
2012 59942
    protected function onSchemaAlterTableRenameColumn($oldColumnName, Column $column, TableDiff $diff, &$columnSql)
2013
    {
2014 59942
        if ($this->_eventManager === null) {
2015 55891
            return false;
2016
        }
2017
2018 59826
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRenameColumn)) {
2019 57534
            return false;
2020
        }
2021
2022 56436
        $eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName, $column, $diff, $this);
2023 56436
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn, $eventArgs);
2024
2025 56436
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
2026
2027 56436
        return $eventArgs->isDefaultPrevented();
2028
    }
2029
2030
    /**
2031
     * @param string[] $sql
2032
     *
2033
     * @return bool
2034
     */
2035 61890
    protected function onSchemaAlterTable(TableDiff $diff, &$sql)
2036
    {
2037 61890
        if ($this->_eventManager === null) {
2038 57995
            return false;
2039
        }
2040
2041 61220
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTable)) {
2042 61184
            return false;
2043
        }
2044
2045 56436
        $eventArgs = new SchemaAlterTableEventArgs($diff, $this);
2046 56436
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTable, $eventArgs);
2047
2048 56436
        $sql = array_merge($sql, $eventArgs->getSql());
2049
2050 56436
        return $eventArgs->isDefaultPrevented();
2051
    }
2052
2053
    /**
2054
     * @return string[]
2055
     */
2056 61747
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
2057
    {
2058 61747
        $tableName = $diff->getName($this)->getQuotedName($this);
2059
2060 61747
        $sql = [];
2061 61747
        if ($this->supportsForeignKeyConstraints()) {
2062 61747
            foreach ($diff->removedForeignKeys as $foreignKey) {
2063 59756
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
2064
            }
2065 61747
            foreach ($diff->changedForeignKeys as $foreignKey) {
2066 55540
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
2067
            }
2068
        }
2069
2070 61747
        foreach ($diff->removedIndexes as $index) {
2071 60225
            $sql[] = $this->getDropIndexSQL($index, $tableName);
2072
        }
2073 61747
        foreach ($diff->changedIndexes as $index) {
2074 59816
            $sql[] = $this->getDropIndexSQL($index, $tableName);
2075
        }
2076
2077 61747
        return $sql;
2078
    }
2079
2080
    /**
2081
     * @return string[]
2082
     */
2083 61747
    protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff)
2084
    {
2085 61747
        $sql     = [];
2086 61747
        $newName = $diff->getNewName();
2087
2088 61747
        if ($newName !== false) {
2089 56889
            $tableName = $newName->getQuotedName($this);
2090
        } else {
2091 61677
            $tableName = $diff->getName($this)->getQuotedName($this);
2092
        }
2093
2094 61747
        if ($this->supportsForeignKeyConstraints()) {
2095 61747
            foreach ($diff->addedForeignKeys as $foreignKey) {
2096 59853
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
2097
            }
2098
2099 61747
            foreach ($diff->changedForeignKeys as $foreignKey) {
2100 55540
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
2101
            }
2102
        }
2103
2104 61747
        foreach ($diff->addedIndexes as $index) {
2105 59797
            $sql[] = $this->getCreateIndexSQL($index, $tableName);
2106
        }
2107
2108 61747
        foreach ($diff->changedIndexes as $index) {
2109 59816
            $sql[] = $this->getCreateIndexSQL($index, $tableName);
2110
        }
2111
2112 61747
        foreach ($diff->renamedIndexes as $oldIndexName => $index) {
2113 59996
            $oldIndexName = new Identifier($oldIndexName);
2114 59996
            $sql          = array_merge(
2115 59996
                $sql,
2116 59996
                $this->getRenameIndexSQL($oldIndexName->getQuotedName($this), $index, $tableName)
2117
            );
2118
        }
2119
2120 61747
        return $sql;
2121
    }
2122
2123
    /**
2124
     * Returns the SQL for renaming an index on a table.
2125
     *
2126
     * @param string $oldIndexName The name of the index to rename from.
2127
     * @param Index  $index        The definition of the index to rename to.
2128
     * @param string $tableName    The table to rename the given index on.
2129
     *
2130
     * @return string[] The sequence of SQL statements for renaming the given index.
2131
     */
2132 55822
    protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
2133
    {
2134
        return [
2135 55822
            $this->getDropIndexSQL($oldIndexName, $tableName),
2136 55822
            $this->getCreateIndexSQL($index, $tableName),
2137
        ];
2138
    }
2139
2140
    /**
2141
     * Common code for alter table statement generation that updates the changed Index and Foreign Key definitions.
2142
     *
2143
     * @deprecated
2144
     *
2145
     * @return string[]
2146
     */
2147
    protected function _getAlterTableIndexForeignKeySQL(TableDiff $diff)
2148
    {
2149
        return array_merge($this->getPreAlterTableIndexForeignKeySQL($diff), $this->getPostAlterTableIndexForeignKeySQL($diff));
2150
    }
2151
2152
    /**
2153
     * Gets declaration of a number of fields in bulk.
2154
     *
2155
     * @param mixed[][] $fields A multidimensional associative array.
2156
     *                          The first dimension determines the field name, while the second
2157
     *                          dimension is keyed with the name of the properties
2158
     *                          of the field being declared as array indexes. Currently, the types
2159
     *                          of supported field properties are as follows:
2160
     *
2161
     *      length
2162
     *          Integer value that determines the maximum length of the text
2163
     *          field. If this argument is missing the field should be
2164
     *          declared to have the longest length allowed by the DBMS.
2165
     *
2166
     *      default
2167
     *          Text value to be used as default for this field.
2168
     *
2169
     *      notnull
2170
     *          Boolean flag that indicates whether this field is constrained
2171
     *          to not be set to null.
2172
     *      charset
2173
     *          Text value with the default CHARACTER SET for this field.
2174
     *      collation
2175
     *          Text value with the default COLLATION for this field.
2176
     *      unique
2177
     *          unique constraint
2178
     *
2179
     * @return string
2180
     */
2181 64010
    public function getColumnDeclarationListSQL(array $fields)
2182
    {
2183 64010
        $queryFields = [];
2184
2185 64010
        foreach ($fields as $fieldName => $field) {
2186 64010
            $queryFields[] = $this->getColumnDeclarationSQL($fieldName, $field);
2187
        }
2188
2189 64010
        return implode(', ', $queryFields);
2190
    }
2191
2192
    /**
2193
     * Obtains DBMS specific SQL code portion needed to declare a generic type
2194
     * field to be used in statements like CREATE TABLE.
2195
     *
2196
     * @param string  $name  The name the field to be declared.
2197
     * @param mixed[] $field An associative array with the name of the properties
2198
     *                       of the field being declared as array indexes. Currently, the types
2199
     *                       of supported field properties are as follows:
2200
     *
2201
     *      length
2202
     *          Integer value that determines the maximum length of the text
2203
     *          field. If this argument is missing the field should be
2204
     *          declared to have the longest length allowed by the DBMS.
2205
     *
2206
     *      default
2207
     *          Text value to be used as default for this field.
2208
     *
2209
     *      notnull
2210
     *          Boolean flag that indicates whether this field is constrained
2211
     *          to not be set to null.
2212
     *      charset
2213
     *          Text value with the default CHARACTER SET for this field.
2214
     *      collation
2215
     *          Text value with the default COLLATION for this field.
2216
     *      unique
2217
     *          unique constraint
2218
     *      check
2219
     *          column check constraint
2220
     *      columnDefinition
2221
     *          a string that defines the complete column
2222
     *
2223
     * @return string DBMS specific SQL code portion that should be used to declare the column.
2224
     */
2225 63477
    public function getColumnDeclarationSQL($name, array $field)
2226
    {
2227 63477
        if (isset($field['columnDefinition'])) {
2228 58363
            $columnDef = $this->getCustomTypeDeclarationSQL($field);
2229
        } else {
2230 63449
            $default = $this->getDefaultValueDeclarationSQL($field);
2231
2232 63449
            $charset = isset($field['charset']) && $field['charset'] ?
2233 63449
                ' ' . $this->getColumnCharsetDeclarationSQL($field['charset']) : '';
2234
2235 63449
            $collation = isset($field['collation']) && $field['collation'] ?
2236 63449
                ' ' . $this->getColumnCollationDeclarationSQL($field['collation']) : '';
2237
2238 63449
            $notnull = isset($field['notnull']) && $field['notnull'] ? ' NOT NULL' : '';
2239
2240 63449
            $unique = isset($field['unique']) && $field['unique'] ?
2241 63449
                ' ' . $this->getUniqueFieldDeclarationSQL() : '';
2242
2243 63449
            $check = isset($field['check']) && $field['check'] ?
2244 63449
                ' ' . $field['check'] : '';
2245
2246 63449
            $typeDecl  = $field['type']->getSQLDeclaration($field, $this);
2247 63449
            $columnDef = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation;
2248
2249 63449
            if ($this->supportsInlineColumnComments() && isset($field['comment']) && $field['comment'] !== '') {
2250 57746
                $columnDef .= ' ' . $this->getInlineColumnCommentSQL($field['comment']);
2251
            }
2252
        }
2253
2254 63477
        return $name . ' ' . $columnDef;
2255
    }
2256
2257
    /**
2258
     * Returns the SQL snippet that declares a floating point column of arbitrary precision.
2259
     *
2260
     * @param mixed[] $columnDef
2261
     *
2262
     * @return string
2263
     */
2264 60754
    public function getDecimalTypeDeclarationSQL(array $columnDef)
2265
    {
2266 60754
        $columnDef['precision'] = ! isset($columnDef['precision']) || empty($columnDef['precision'])
2267 60754
            ? 10 : $columnDef['precision'];
2268 60754
        $columnDef['scale']     = ! isset($columnDef['scale']) || empty($columnDef['scale'])
2269 60754
            ? 0 : $columnDef['scale'];
2270
2271 60754
        return 'NUMERIC(' . $columnDef['precision'] . ', ' . $columnDef['scale'] . ')';
2272
    }
2273
2274
    /**
2275
     * Obtains DBMS specific SQL code portion needed to set a default value
2276
     * declaration to be used in statements like CREATE TABLE.
2277
     *
2278
     * @param mixed[] $field The field definition array.
2279
     *
2280
     * @return string DBMS specific SQL code portion needed to set a default value.
2281
     */
2282 64227
    public function getDefaultValueDeclarationSQL($field)
2283
    {
2284 64227
        if (! isset($field['default'])) {
2285 63697
            return empty($field['notnull']) ? ' DEFAULT NULL' : '';
2286
        }
2287
2288 61780
        $default = $field['default'];
2289
2290 61780
        if (! isset($field['type'])) {
2291 14224
            return " DEFAULT '" . $default . "'";
2292
        }
2293
2294 61756
        $type = $field['type'];
2295
2296 61756
        if ($type instanceof Types\PhpIntegerMappingType) {
2297 61069
            return ' DEFAULT ' . $default;
2298
        }
2299
2300 61698
        if ($type instanceof Types\PhpDateTimeMappingType && $default === $this->getCurrentTimestampSQL()) {
2301 61494
            return ' DEFAULT ' . $this->getCurrentTimestampSQL();
2302
        }
2303
2304 61387
        if ($type instanceof Types\TimeType && $default === $this->getCurrentTimeSQL()) {
2305 36673
            return ' DEFAULT ' . $this->getCurrentTimeSQL();
2306
        }
2307
2308 61387
        if ($type instanceof Types\DateType && $default === $this->getCurrentDateSQL()) {
2309 59212
            return ' DEFAULT ' . $this->getCurrentDateSQL();
2310
        }
2311
2312 61120
        if ($type instanceof Types\BooleanType) {
2313 61014
            return " DEFAULT '" . $this->convertBooleans($default) . "'";
2314
        }
2315
2316 61091
        return ' DEFAULT ' . $this->quoteStringLiteral($default);
2317
    }
2318
2319
    /**
2320
     * Obtains DBMS specific SQL code portion needed to set a CHECK constraint
2321
     * declaration to be used in statements like CREATE TABLE.
2322
     *
2323
     * @param string[]|mixed[][] $definition The check definition.
2324
     *
2325
     * @return string DBMS specific SQL code portion needed to set a CHECK constraint.
2326
     */
2327 58725
    public function getCheckDeclarationSQL(array $definition)
2328
    {
2329 58725
        $constraints = [];
2330 58725
        foreach ($definition as $field => $def) {
2331 58725
            if (is_string($def)) {
2332
                $constraints[] = 'CHECK (' . $def . ')';
2333
            } else {
2334 58725
                if (isset($def['min'])) {
2335 57635
                    $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')';
2336
                }
2337
2338 58725
                if (isset($def['max'])) {
2339 58147
                    $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')';
2340
                }
2341
            }
2342
        }
2343
2344 58725
        return implode(', ', $constraints);
2345
    }
2346
2347
    /**
2348
     * Obtains DBMS specific SQL code portion needed to set a unique
2349
     * constraint declaration to be used in statements like CREATE TABLE.
2350
     *
2351
     * @param string $name  The name of the unique constraint.
2352
     * @param Index  $index The index definition.
2353
     *
2354
     * @return string DBMS specific SQL code portion needed to set a constraint.
2355
     *
2356
     * @throws InvalidArgumentException
2357
     */
2358 56681
    public function getUniqueConstraintDeclarationSQL($name, Index $index)
2359
    {
2360 56681
        $columns = $index->getColumns();
2361 56681
        $name    = new Identifier($name);
2362
2363 56681
        if (count($columns) === 0) {
2364
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
2365
        }
2366
2367 56681
        return 'CONSTRAINT ' . $name->getQuotedName($this) . ' UNIQUE ('
2368 56681
            . $this->getIndexFieldDeclarationListSQL($index)
2369 56681
            . ')' . $this->getPartialIndexSQL($index);
2370
    }
2371
2372
    /**
2373
     * Obtains DBMS specific SQL code portion needed to set an index
2374
     * declaration to be used in statements like CREATE TABLE.
2375
     *
2376
     * @param string $name  The name of the index.
2377
     * @param Index  $index The index definition.
2378
     *
2379
     * @return string DBMS specific SQL code portion needed to set an index.
2380
     *
2381
     * @throws InvalidArgumentException
2382
     */
2383 59138
    public function getIndexDeclarationSQL($name, Index $index)
2384
    {
2385 59138
        $columns = $index->getColumns();
2386 59138
        $name    = new Identifier($name);
2387
2388 59138
        if (count($columns) === 0) {
2389
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
2390
        }
2391
2392 59138
        return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name->getQuotedName($this) . ' ('
2393 59138
            . $this->getIndexFieldDeclarationListSQL($index)
2394 59138
            . ')' . $this->getPartialIndexSQL($index);
2395
    }
2396
2397
    /**
2398
     * Obtains SQL code portion needed to create a custom column,
2399
     * e.g. when a field has the "columnDefinition" keyword.
2400
     * Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate.
2401
     *
2402
     * @param mixed[] $columnDef
2403
     *
2404
     * @return string
2405
     */
2406 58371
    public function getCustomTypeDeclarationSQL(array $columnDef)
2407
    {
2408 58371
        return $columnDef['columnDefinition'];
2409
    }
2410
2411
    /**
2412
     * Obtains DBMS specific SQL code portion needed to set an index
2413
     * declaration to be used in statements like CREATE TABLE.
2414
     *
2415
     * @param mixed[]|Index $columnsOrIndex array declaration is deprecated, prefer passing Index to this method
2416
     */
2417 62524
    public function getIndexFieldDeclarationListSQL($columnsOrIndex) : string
2418
    {
2419 62524
        if ($columnsOrIndex instanceof Index) {
2420 62436
            return implode(', ', $columnsOrIndex->getQuotedColumns($this));
2421
        }
2422
2423 28475
        if (! is_array($columnsOrIndex)) {
2424
            throw new InvalidArgumentException('Fields argument should be an Index or array.');
2425
        }
2426
2427 28475
        $ret = [];
2428
2429 28475
        foreach ($columnsOrIndex as $column => $definition) {
2430 28475
            if (is_array($definition)) {
2431
                $ret[] = $column;
2432
            } else {
2433 28475
                $ret[] = $definition;
2434
            }
2435
        }
2436
2437 28475
        return implode(', ', $ret);
2438
    }
2439
2440
    /**
2441
     * Returns the required SQL string that fits between CREATE ... TABLE
2442
     * to create the table as a temporary table.
2443
     *
2444
     * Should be overridden in driver classes to return the correct string for the
2445
     * specific database type.
2446
     *
2447
     * The default is to return the string "TEMPORARY" - this will result in a
2448
     * SQL error for any database that does not support temporary tables, or that
2449
     * requires a different SQL command from "CREATE TEMPORARY TABLE".
2450
     *
2451
     * @return string The string required to be placed between "CREATE" and "TABLE"
2452
     *                to generate a temporary table, if possible.
2453
     */
2454
    public function getTemporaryTableSQL()
2455
    {
2456
        return 'TEMPORARY';
2457
    }
2458
2459
    /**
2460
     * Some vendors require temporary table names to be qualified specially.
2461
     *
2462
     * @param string $tableName
2463
     *
2464
     * @return string
2465
     */
2466 49231
    public function getTemporaryTableName($tableName)
2467
    {
2468 49231
        return $tableName;
2469
    }
2470
2471
    /**
2472
     * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2473
     * of a field declaration to be used in statements like CREATE TABLE.
2474
     *
2475
     * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2476
     *                of a field declaration.
2477
     */
2478 62169
    public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey)
2479
    {
2480 62169
        $sql  = $this->getForeignKeyBaseDeclarationSQL($foreignKey);
2481 62145
        $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey);
2482
2483 62145
        return $sql;
2484
    }
2485
2486
    /**
2487
     * Returns the FOREIGN KEY query section dealing with non-standard options
2488
     * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
2489
     *
2490
     * @param ForeignKeyConstraint $foreignKey The foreign key definition.
2491
     *
2492
     * @return string
2493
     */
2494 62113
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
2495
    {
2496 62113
        $query = '';
2497 62113
        if ($this->supportsForeignKeyOnUpdate() && $foreignKey->hasOption('onUpdate')) {
2498 27933
            $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate'));
2499
        }
2500 62113
        if ($foreignKey->hasOption('onDelete')) {
2501 59598
            $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
2502
        }
2503
2504 62113
        return $query;
2505
    }
2506
2507
    /**
2508
     * Returns the given referential action in uppercase if valid, otherwise throws an exception.
2509
     *
2510
     * @param string $action The foreign key referential action.
2511
     *
2512
     * @return string
2513
     *
2514
     * @throws InvalidArgumentException If unknown referential action given.
2515
     */
2516 60356
    public function getForeignKeyReferentialActionSQL($action)
2517
    {
2518 60356
        $upper = strtoupper($action);
2519 60356
        switch ($upper) {
2520 242
            case 'CASCADE':
2521 164
            case 'SET NULL':
2522 122
            case 'NO ACTION':
2523 96
            case 'RESTRICT':
2524 68
            case 'SET DEFAULT':
2525 60322
                return $upper;
2526
            default:
2527 56959
                throw new InvalidArgumentException('Invalid foreign key action: ' . $upper);
2528
        }
2529
    }
2530
2531
    /**
2532
     * Obtains DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2533
     * of a field declaration to be used in statements like CREATE TABLE.
2534
     *
2535
     * @return string
2536
     *
2537
     * @throws InvalidArgumentException
2538
     */
2539 62089
    public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey)
2540
    {
2541 62089
        $sql = '';
2542 62089
        if (strlen($foreignKey->getName())) {
2543 61979
            $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' ';
2544
        }
2545 62089
        $sql .= 'FOREIGN KEY (';
2546
2547 62089
        if (count($foreignKey->getLocalColumns()) === 0) {
2548
            throw new InvalidArgumentException("Incomplete definition. 'local' required.");
2549
        }
2550 62089
        if (count($foreignKey->getForeignColumns()) === 0) {
2551
            throw new InvalidArgumentException("Incomplete definition. 'foreign' required.");
2552
        }
2553 62089
        if (strlen($foreignKey->getForeignTableName()) === 0) {
2554
            throw new InvalidArgumentException("Incomplete definition. 'foreignTable' required.");
2555
        }
2556
2557 62089
        return $sql . implode(', ', $foreignKey->getQuotedLocalColumns($this))
2558 62089
            . ') REFERENCES '
2559 62089
            . $foreignKey->getQuotedForeignTableName($this) . ' ('
2560 62089
            . implode(', ', $foreignKey->getQuotedForeignColumns($this)) . ')';
2561
    }
2562
2563
    /**
2564
     * Obtains DBMS specific SQL code portion needed to set the UNIQUE constraint
2565
     * of a field declaration to be used in statements like CREATE TABLE.
2566
     *
2567
     * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint
2568
     *                of a field declaration.
2569
     */
2570
    public function getUniqueFieldDeclarationSQL()
2571
    {
2572
        return 'UNIQUE';
2573
    }
2574
2575
    /**
2576
     * Obtains DBMS specific SQL code portion needed to set the CHARACTER SET
2577
     * of a field declaration to be used in statements like CREATE TABLE.
2578
     *
2579
     * @param string $charset The name of the charset.
2580
     *
2581
     * @return string DBMS specific SQL code portion needed to set the CHARACTER SET
2582
     *                of a field declaration.
2583
     */
2584
    public function getColumnCharsetDeclarationSQL($charset)
2585
    {
2586
        return '';
2587
    }
2588
2589
    /**
2590
     * Obtains DBMS specific SQL code portion needed to set the COLLATION
2591
     * of a field declaration to be used in statements like CREATE TABLE.
2592
     *
2593
     * @param string $collation The name of the collation.
2594
     *
2595
     * @return string DBMS specific SQL code portion needed to set the COLLATION
2596
     *                of a field declaration.
2597
     */
2598 58547
    public function getColumnCollationDeclarationSQL($collation)
2599
    {
2600 58547
        return $this->supportsColumnCollation() ? 'COLLATE ' . $collation : '';
2601
    }
2602
2603
    /**
2604
     * Whether the platform prefers sequences for ID generation.
2605
     * Subclasses should override this method to return TRUE if they prefer sequences.
2606
     *
2607
     * @return bool
2608
     */
2609 10
    public function prefersSequences()
2610
    {
2611 10
        return false;
2612
    }
2613
2614
    /**
2615
     * Whether the platform prefers identity columns (eg. autoincrement) for ID generation.
2616
     * Subclasses should override this method to return TRUE if they prefer identity columns.
2617
     *
2618
     * @return bool
2619
     */
2620 49553
    public function prefersIdentityColumns()
2621
    {
2622 49553
        return false;
2623
    }
2624
2625
    /**
2626
     * Some platforms need the boolean values to be converted.
2627
     *
2628
     * The default conversion in this implementation converts to integers (false => 0, true => 1).
2629
     *
2630
     * Note: if the input is not a boolean the original input might be returned.
2631
     *
2632
     * There are two contexts when converting booleans: Literals and Prepared Statements.
2633
     * This method should handle the literal case
2634
     *
2635
     * @param mixed $item A boolean or an array of them.
2636
     *
2637
     * @return mixed A boolean database value or an array of them.
2638
     */
2639 59400
    public function convertBooleans($item)
2640
    {
2641 59400
        if (is_array($item)) {
2642
            foreach ($item as $k => $value) {
2643
                if (! is_bool($value)) {
2644
                    continue;
2645
                }
2646
2647
                $item[$k] = (int) $value;
2648
            }
2649 59400
        } elseif (is_bool($item)) {
2650 59398
            $item = (int) $item;
2651
        }
2652
2653 59400
        return $item;
2654
    }
2655
2656
    /**
2657
     * Some platforms have boolean literals that needs to be correctly converted
2658
     *
2659
     * The default conversion tries to convert value into bool "(bool)$item"
2660
     *
2661
     * @param mixed $item
2662
     *
2663
     * @return bool|null
2664
     */
2665 58615
    public function convertFromBoolean($item)
2666
    {
2667 58615
        return $item === null ? null: (bool) $item;
2668
    }
2669
2670
    /**
2671
     * This method should handle the prepared statements case. When there is no
2672
     * distinction, it's OK to use the same method.
2673
     *
2674
     * Note: if the input is not a boolean the original input might be returned.
2675
     *
2676
     * @param mixed $item A boolean or an array of them.
2677
     *
2678
     * @return mixed A boolean database value or an array of them.
2679
     */
2680 54168
    public function convertBooleansToDatabaseValue($item)
2681
    {
2682 54168
        return $this->convertBooleans($item);
2683
    }
2684
2685
    /**
2686
     * Returns the SQL specific for the platform to get the current date.
2687
     *
2688
     * @return string
2689
     */
2690 59949
    public function getCurrentDateSQL()
2691
    {
2692 59949
        return 'CURRENT_DATE';
2693
    }
2694
2695
    /**
2696
     * Returns the SQL specific for the platform to get the current time.
2697
     *
2698
     * @return string
2699
     */
2700 29345
    public function getCurrentTimeSQL()
2701
    {
2702 29345
        return 'CURRENT_TIME';
2703
    }
2704
2705
    /**
2706
     * Returns the SQL specific for the platform to get the current timestamp
2707
     *
2708
     * @return string
2709
     */
2710 61194
    public function getCurrentTimestampSQL()
2711
    {
2712 61194
        return 'CURRENT_TIMESTAMP';
2713
    }
2714
2715
    /**
2716
     * Returns the SQL for a given transaction isolation level Connection constant.
2717
     *
2718
     * @param int $level
2719
     *
2720
     * @return string
2721
     *
2722
     * @throws InvalidArgumentException
2723
     */
2724 55147
    protected function _getTransactionIsolationLevelSQL($level)
2725
    {
2726 22
        switch ($level) {
2727 55125
            case TransactionIsolationLevel::READ_UNCOMMITTED:
2728 55147
                return 'READ UNCOMMITTED';
2729 55125
            case TransactionIsolationLevel::READ_COMMITTED:
2730 55147
                return 'READ COMMITTED';
2731 55125
            case TransactionIsolationLevel::REPEATABLE_READ:
2732 55147
                return 'REPEATABLE READ';
2733 55125
            case TransactionIsolationLevel::SERIALIZABLE:
2734 55147
                return 'SERIALIZABLE';
2735
            default:
2736
                throw new InvalidArgumentException('Invalid isolation level:' . $level);
2737
        }
2738
    }
2739
2740
    /**
2741
     * @return string
2742
     *
2743
     * @throws DBALException If not supported on this platform.
2744
     */
2745 2413
    public function getListDatabasesSQL()
2746
    {
2747 2413
        throw DBALException::notSupported(__METHOD__);
2748
    }
2749
2750
    /**
2751
     * Returns the SQL statement for retrieving the namespaces defined in the database.
2752
     *
2753
     * @return string
2754
     *
2755
     * @throws DBALException If not supported on this platform.
2756
     */
2757
    public function getListNamespacesSQL()
2758
    {
2759
        throw DBALException::notSupported(__METHOD__);
2760
    }
2761
2762
    /**
2763
     * @param string $database
2764
     *
2765
     * @return string
2766
     *
2767
     * @throws DBALException If not supported on this platform.
2768
     */
2769
    public function getListSequencesSQL($database)
2770
    {
2771
        throw DBALException::notSupported(__METHOD__);
2772
    }
2773
2774
    /**
2775
     * @param string $table
2776
     *
2777
     * @return string
2778
     *
2779
     * @throws DBALException If not supported on this platform.
2780
     */
2781
    public function getListTableConstraintsSQL($table)
2782
    {
2783
        throw DBALException::notSupported(__METHOD__);
2784
    }
2785
2786
    /**
2787
     * @param string      $table
2788
     * @param string|null $database
2789
     *
2790
     * @return string
2791
     *
2792
     * @throws DBALException If not supported on this platform.
2793
     */
2794
    public function getListTableColumnsSQL($table, $database = null)
2795
    {
2796
        throw DBALException::notSupported(__METHOD__);
2797
    }
2798
2799
    /**
2800
     * @return string
2801
     *
2802
     * @throws DBALException If not supported on this platform.
2803
     */
2804
    public function getListTablesSQL()
2805
    {
2806
        throw DBALException::notSupported(__METHOD__);
2807
    }
2808
2809
    /**
2810
     * @return string
2811
     *
2812
     * @throws DBALException If not supported on this platform.
2813
     */
2814
    public function getListUsersSQL()
2815
    {
2816
        throw DBALException::notSupported(__METHOD__);
2817
    }
2818
2819
    /**
2820
     * Returns the SQL to list all views of a database or user.
2821
     *
2822
     * @param string $database
2823
     *
2824
     * @return string
2825
     *
2826
     * @throws DBALException If not supported on this platform.
2827
     */
2828
    public function getListViewsSQL($database)
2829
    {
2830
        throw DBALException::notSupported(__METHOD__);
2831
    }
2832
2833
    /**
2834
     * Returns the list of indexes for the current database.
2835
     *
2836
     * The current database parameter is optional but will always be passed
2837
     * when using the SchemaManager API and is the database the given table is in.
2838
     *
2839
     * Attention: Some platforms only support currentDatabase when they
2840
     * are connected with that database. Cross-database information schema
2841
     * requests may be impossible.
2842
     *
2843
     * @param string $table
2844
     * @param string $currentDatabase
2845
     *
2846
     * @return string
2847
     *
2848
     * @throws DBALException If not supported on this platform.
2849
     */
2850
    public function getListTableIndexesSQL($table, $currentDatabase = null)
2851
    {
2852
        throw DBALException::notSupported(__METHOD__);
2853
    }
2854
2855
    /**
2856
     * @param string $table
2857
     *
2858
     * @return string
2859
     *
2860
     * @throws DBALException If not supported on this platform.
2861
     */
2862
    public function getListTableForeignKeysSQL($table)
2863
    {
2864
        throw DBALException::notSupported(__METHOD__);
2865
    }
2866
2867
    /**
2868
     * @param string $name
2869
     * @param string $sql
2870
     *
2871
     * @return string
2872
     *
2873
     * @throws DBALException If not supported on this platform.
2874
     */
2875
    public function getCreateViewSQL($name, $sql)
2876
    {
2877
        throw DBALException::notSupported(__METHOD__);
2878
    }
2879
2880
    /**
2881
     * @param string $name
2882
     *
2883
     * @return string
2884
     *
2885
     * @throws DBALException If not supported on this platform.
2886
     */
2887
    public function getDropViewSQL($name)
2888
    {
2889
        throw DBALException::notSupported(__METHOD__);
2890
    }
2891
2892
    /**
2893
     * Returns the SQL snippet to drop an existing sequence.
2894
     *
2895
     * @param Sequence|string $sequence
2896
     *
2897
     * @return string
2898
     *
2899
     * @throws DBALException If not supported on this platform.
2900
     */
2901
    public function getDropSequenceSQL($sequence)
2902
    {
2903
        throw DBALException::notSupported(__METHOD__);
2904
    }
2905
2906
    /**
2907
     * @param string $sequenceName
2908
     *
2909
     * @return string
2910
     *
2911
     * @throws DBALException If not supported on this platform.
2912
     */
2913
    public function getSequenceNextValSQL($sequenceName)
2914
    {
2915
        throw DBALException::notSupported(__METHOD__);
2916
    }
2917
2918
    /**
2919
     * Returns the SQL to create a new database.
2920
     *
2921
     * @param string $database The name of the database that should be created.
2922
     *
2923
     * @return string
2924
     *
2925
     * @throws DBALException If not supported on this platform.
2926
     */
2927 46427
    public function getCreateDatabaseSQL($database)
2928
    {
2929 46427
        throw DBALException::notSupported(__METHOD__);
2930
    }
2931
2932
    /**
2933
     * Returns the SQL to set the transaction isolation level.
2934
     *
2935
     * @param int $level
2936
     *
2937
     * @return string
2938
     *
2939
     * @throws DBALException If not supported on this platform.
2940
     */
2941
    public function getSetTransactionIsolationSQL($level)
2942
    {
2943
        throw DBALException::notSupported(__METHOD__);
2944
    }
2945
2946
    /**
2947
     * Obtains DBMS specific SQL to be used to create datetime fields in
2948
     * statements like CREATE TABLE.
2949
     *
2950
     * @param mixed[] $fieldDeclaration
2951
     *
2952
     * @return string
2953
     *
2954
     * @throws DBALException If not supported on this platform.
2955
     */
2956
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
2957
    {
2958
        throw DBALException::notSupported(__METHOD__);
2959
    }
2960
2961
    /**
2962
     * Obtains DBMS specific SQL to be used to create datetime with timezone offset fields.
2963
     *
2964
     * @param mixed[] $fieldDeclaration
2965
     *
2966
     * @return string
2967
     */
2968 37450
    public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration)
2969
    {
2970 37450
        return $this->getDateTimeTypeDeclarationSQL($fieldDeclaration);
2971
    }
2972
2973
    /**
2974
     * Obtains DBMS specific SQL to be used to create date fields in statements
2975
     * like CREATE TABLE.
2976
     *
2977
     * @param mixed[] $fieldDeclaration
2978
     *
2979
     * @return string
2980
     *
2981
     * @throws DBALException If not supported on this platform.
2982
     */
2983
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
0 ignored issues
show
Unused Code introduced by
The parameter $fieldDeclaration is not used and could be removed.

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

Loading history...
2984
    {
2985
        throw DBALException::notSupported(__METHOD__);
2986
    }
2987
2988
    /**
2989
     * Obtains DBMS specific SQL to be used to create time fields in statements
2990
     * like CREATE TABLE.
2991
     *
2992
     * @param mixed[] $fieldDeclaration
2993
     *
2994
     * @return string
2995
     *
2996
     * @throws DBALException If not supported on this platform.
2997
     */
2998
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
0 ignored issues
show
Unused Code introduced by
The parameter $fieldDeclaration is not used and could be removed.

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

Loading history...
2999
    {
3000
        throw DBALException::notSupported(__METHOD__);
3001
    }
3002
3003
    /**
3004
     * @param mixed[] $fieldDeclaration
3005
     *
3006
     * @return string
3007
     */
3008 57529
    public function getFloatDeclarationSQL(array $fieldDeclaration)
3009
    {
3010 57529
        return 'DOUBLE PRECISION';
3011
    }
3012
3013
    /**
3014
     * Gets the default transaction isolation level of the platform.
3015
     *
3016
     * @see TransactionIsolationLevel
3017
     *
3018
     * @return int The default isolation level.
3019
     */
3020
    public function getDefaultTransactionIsolationLevel()
3021
    {
3022
        return TransactionIsolationLevel::READ_COMMITTED;
3023
    }
3024
3025
    /* supports*() methods */
3026
3027
    /**
3028
     * Whether the platform supports sequences.
3029
     *
3030
     * @return bool
3031
     */
3032 40114
    public function supportsSequences()
3033
    {
3034 40114
        return false;
3035
    }
3036
3037
    /**
3038
     * Whether the platform supports identity columns.
3039
     *
3040
     * Identity columns are columns that receive an auto-generated value from the
3041
     * database on insert of a row.
3042
     *
3043
     * @return bool
3044
     */
3045 4
    public function supportsIdentityColumns()
3046
    {
3047 4
        return false;
3048
    }
3049
3050
    /**
3051
     * Whether the platform emulates identity columns through sequences.
3052
     *
3053
     * Some platforms that do not support identity columns natively
3054
     * but support sequences can emulate identity columns by using
3055
     * sequences.
3056
     *
3057
     * @return bool
3058
     */
3059 57281
    public function usesSequenceEmulatedIdentityColumns()
3060
    {
3061 57281
        return false;
3062
    }
3063
3064
    /**
3065
     * Returns the name of the sequence for a particular identity column in a particular table.
3066
     *
3067
     * @see    usesSequenceEmulatedIdentityColumns
3068
     *
3069
     * @param string $tableName  The name of the table to return the sequence name for.
3070
     * @param string $columnName The name of the identity column in the table to return the sequence name for.
3071
     *
3072
     * @return string
3073
     *
3074
     * @throws DBALException If not supported on this platform.
3075
     */
3076 55924
    public function getIdentitySequenceName($tableName, $columnName)
3077
    {
3078 55924
        throw DBALException::notSupported(__METHOD__);
3079
    }
3080
3081
    /**
3082
     * Whether the platform supports indexes.
3083
     *
3084
     * @return bool
3085
     */
3086 8
    public function supportsIndexes()
3087
    {
3088 8
        return true;
3089
    }
3090
3091
    /**
3092
     * Whether the platform supports partial indexes.
3093
     *
3094
     * @return bool
3095
     */
3096 61034
    public function supportsPartialIndexes()
3097
    {
3098 61034
        return false;
3099
    }
3100
3101
    /**
3102
     * Whether the platform supports indexes with column length definitions.
3103
     */
3104 60665
    public function supportsColumnLengthIndexes() : bool
3105
    {
3106 60665
        return false;
3107
    }
3108
3109
    /**
3110
     * Whether the platform supports altering tables.
3111
     *
3112
     * @return bool
3113
     */
3114 59987
    public function supportsAlterTable()
3115
    {
3116 59987
        return true;
3117
    }
3118
3119
    /**
3120
     * Whether the platform supports transactions.
3121
     *
3122
     * @return bool
3123
     */
3124 8
    public function supportsTransactions()
3125
    {
3126 8
        return true;
3127
    }
3128
3129
    /**
3130
     * Whether the platform supports savepoints.
3131
     *
3132
     * @return bool
3133
     */
3134 61703
    public function supportsSavepoints()
3135
    {
3136 61703
        return true;
3137
    }
3138
3139
    /**
3140
     * Whether the platform supports releasing savepoints.
3141
     *
3142
     * @return bool
3143
     */
3144 58900
    public function supportsReleaseSavepoints()
3145
    {
3146 58900
        return $this->supportsSavepoints();
3147
    }
3148
3149
    /**
3150
     * Whether the platform supports primary key constraints.
3151
     *
3152
     * @return bool
3153
     */
3154 8
    public function supportsPrimaryConstraints()
3155
    {
3156 8
        return true;
3157
    }
3158
3159
    /**
3160
     * Whether the platform supports foreign key constraints.
3161
     *
3162
     * @return bool
3163
     */
3164 62874
    public function supportsForeignKeyConstraints()
3165
    {
3166 62874
        return true;
3167
    }
3168
3169
    /**
3170
     * Whether this platform supports onUpdate in foreign key constraints.
3171
     *
3172
     * @return bool
3173
     */
3174 62121
    public function supportsForeignKeyOnUpdate()
3175
    {
3176 62121
        return $this->supportsForeignKeyConstraints();
3177
    }
3178
3179
    /**
3180
     * Whether the platform supports database schemas.
3181
     *
3182
     * @return bool
3183
     */
3184 40124
    public function supportsSchemas()
3185
    {
3186 40124
        return false;
3187
    }
3188
3189
    /**
3190
     * Whether this platform can emulate schemas.
3191
     *
3192
     * Platforms that either support or emulate schemas don't automatically
3193
     * filter a schema for the namespaced elements in {@link
3194
     * AbstractManager#createSchema}.
3195
     *
3196
     * @return bool
3197
     */
3198 8
    public function canEmulateSchemas()
3199
    {
3200 8
        return false;
3201
    }
3202
3203
    /**
3204
     * Returns the default schema name.
3205
     *
3206
     * @return string
3207
     *
3208
     * @throws DBALException If not supported on this platform.
3209
     */
3210
    public function getDefaultSchemaName()
3211
    {
3212
        throw DBALException::notSupported(__METHOD__);
3213
    }
3214
3215
    /**
3216
     * Whether this platform supports create database.
3217
     *
3218
     * Some databases don't allow to create and drop databases at all or only with certain tools.
3219
     *
3220
     * @return bool
3221
     */
3222 57964
    public function supportsCreateDropDatabase()
3223
    {
3224 57964
        return true;
3225
    }
3226
3227
    /**
3228
     * Whether the platform supports getting the affected rows of a recent update/delete type query.
3229
     *
3230
     * @return bool
3231
     */
3232 8
    public function supportsGettingAffectedRows()
3233
    {
3234 8
        return true;
3235
    }
3236
3237
    /**
3238
     * Whether this platform support to add inline column comments as postfix.
3239
     *
3240
     * @return bool
3241
     */
3242 60041
    public function supportsInlineColumnComments()
3243
    {
3244 60041
        return false;
3245
    }
3246
3247
    /**
3248
     * Whether this platform support the proprietary syntax "COMMENT ON asset".
3249
     *
3250
     * @return bool
3251
     */
3252 60891
    public function supportsCommentOnStatement()
3253
    {
3254 60891
        return false;
3255
    }
3256
3257
    /**
3258
     * Does this platform have native guid type.
3259
     *
3260
     * @return bool
3261
     */
3262 61870
    public function hasNativeGuidType()
3263
    {
3264 61870
        return false;
3265
    }
3266
3267
    /**
3268
     * Does this platform have native JSON type.
3269
     *
3270
     * @return bool
3271
     */
3272 62091
    public function hasNativeJsonType()
3273
    {
3274 62091
        return false;
3275
    }
3276
3277
    /**
3278
     * @deprecated
3279
     *
3280
     * @todo Remove in 3.0
3281
     */
3282
    public function getIdentityColumnNullInsertSQL()
3283
    {
3284
        return '';
3285
    }
3286
3287
    /**
3288
     * Whether this platform supports views.
3289
     *
3290
     * @return bool
3291
     */
3292 59945
    public function supportsViews()
3293
    {
3294 59945
        return true;
3295
    }
3296
3297
    /**
3298
     * Does this platform support column collation?
3299
     *
3300
     * @return bool
3301
     */
3302
    public function supportsColumnCollation()
3303
    {
3304
        return false;
3305
    }
3306
3307
    /**
3308
     * Gets the format string, as accepted by the date() function, that describes
3309
     * the format of a stored datetime value of this platform.
3310
     *
3311
     * @return string The format string.
3312
     */
3313 57968
    public function getDateTimeFormatString()
3314
    {
3315 57968
        return 'Y-m-d H:i:s';
3316
    }
3317
3318
    /**
3319
     * Gets the format string, as accepted by the date() function, that describes
3320
     * the format of a stored datetime with timezone value of this platform.
3321
     *
3322
     * @return string The format string.
3323
     */
3324 37696
    public function getDateTimeTzFormatString()
3325
    {
3326 37696
        return 'Y-m-d H:i:s';
3327
    }
3328
3329
    /**
3330
     * Gets the format string, as accepted by the date() function, that describes
3331
     * the format of a stored date value of this platform.
3332
     *
3333
     * @return string The format string.
3334
     */
3335 53686
    public function getDateFormatString()
3336
    {
3337 53686
        return 'Y-m-d';
3338
    }
3339
3340
    /**
3341
     * Gets the format string, as accepted by the date() function, that describes
3342
     * the format of a stored time value of this platform.
3343
     *
3344
     * @return string The format string.
3345
     */
3346 44331
    public function getTimeFormatString()
3347
    {
3348 44331
        return 'H:i:s';
3349
    }
3350
3351
    /**
3352
     * Adds an driver-specific LIMIT clause to the query.
3353
     *
3354
     * @param string   $query
3355
     * @param int|null $limit
3356
     * @param int|null $offset
3357
     *
3358
     * @return string
3359
     *
3360
     * @throws DBALException
3361
     */
3362 62031
    final public function modifyLimitQuery($query, $limit, $offset = null)
3363
    {
3364 62031
        if ($limit !== null) {
3365 61975
            $limit = (int) $limit;
3366
        }
3367
3368 62031
        $offset = (int) $offset;
3369
3370 62031
        if ($offset < 0) {
3371
            throw new DBALException(sprintf(
3372
                'Offset must be a positive integer or zero, %d given',
3373
                $offset
3374
            ));
3375
        }
3376
3377 62031
        if ($offset > 0 && ! $this->supportsLimitOffset()) {
3378
            throw new DBALException(sprintf(
3379
                'Platform %s does not support offset values in limit queries.',
3380
                $this->getName()
3381
            ));
3382
        }
3383
3384 62031
        return $this->doModifyLimitQuery($query, $limit, $offset);
3385
    }
3386
3387
    /**
3388
     * Adds an platform-specific LIMIT clause to the query.
3389
     *
3390
     * @param string   $query
3391
     * @param int|null $limit
3392
     * @param int|null $offset
3393
     *
3394
     * @return string
3395
     */
3396 49211
    protected function doModifyLimitQuery($query, $limit, $offset)
3397
    {
3398 49211
        if ($limit !== null) {
3399 49199
            $query .= ' LIMIT ' . $limit;
3400
        }
3401
3402 49211
        if ($offset > 0) {
3403 19782
            $query .= ' OFFSET ' . $offset;
3404
        }
3405
3406 49211
        return $query;
3407
    }
3408
3409
    /**
3410
     * Whether the database platform support offsets in modify limit clauses.
3411
     *
3412
     * @return bool
3413
     */
3414 61478
    public function supportsLimitOffset()
3415
    {
3416 61478
        return true;
3417
    }
3418
3419
    /**
3420
     * Gets the character casing of a column in an SQL result set of this platform.
3421
     *
3422
     * @param string $column The column name for which to get the correct character casing.
3423
     *
3424
     * @return string The column name in the character casing used in SQL result sets.
3425
     */
3426
    public function getSQLResultCasing($column)
3427
    {
3428
        return $column;
3429
    }
3430
3431
    /**
3432
     * Makes any fixes to a name of a schema element (table, sequence, ...) that are required
3433
     * by restrictions of the platform, like a maximum length.
3434
     *
3435
     * @param string $schemaElementName
3436
     *
3437
     * @return string
3438
     */
3439
    public function fixSchemaElementName($schemaElementName)
3440
    {
3441
        return $schemaElementName;
3442
    }
3443
3444
    /**
3445
     * Maximum length of any given database identifier, like tables or column names.
3446
     *
3447
     * @return int
3448
     */
3449 61203
    public function getMaxIdentifierLength()
3450
    {
3451 61203
        return 63;
3452
    }
3453
3454
    /**
3455
     * Returns the insert SQL for an empty insert statement.
3456
     *
3457
     * @param string $tableName
3458
     * @param string $identifierColumnName
3459
     *
3460
     * @return string
3461
     */
3462 34666
    public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName)
3463
    {
3464 34666
        return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (null)';
3465
    }
3466
3467
    /**
3468
     * Generates a Truncate Table SQL statement for a given table.
3469
     *
3470
     * Cascade is not supported on many platforms but would optionally cascade the truncate by
3471
     * following the foreign keys.
3472
     *
3473
     * @param string $tableName
3474
     * @param bool   $cascade
3475
     *
3476
     * @return string
3477
     */
3478 58295
    public function getTruncateTableSQL($tableName, $cascade = false)
3479
    {
3480 58295
        $tableIdentifier = new Identifier($tableName);
3481
3482 58295
        return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this);
3483
    }
3484
3485
    /**
3486
     * This is for test reasons, many vendors have special requirements for dummy statements.
3487
     *
3488
     * @return string
3489
     */
3490 62997
    public function getDummySelectSQL()
3491
    {
3492 62997
        $expression = func_num_args() > 0 ? func_get_arg(0) : '1';
3493
3494 62997
        return sprintf('SELECT %s', $expression);
3495
    }
3496
3497
    /**
3498
     * Returns the SQL to create a new savepoint.
3499
     *
3500
     * @param string $savepoint
3501
     *
3502
     * @return string
3503
     */
3504 55621
    public function createSavePoint($savepoint)
3505
    {
3506 55621
        return 'SAVEPOINT ' . $savepoint;
3507
    }
3508
3509
    /**
3510
     * Returns the SQL to release a savepoint.
3511
     *
3512
     * @param string $savepoint
3513
     *
3514
     * @return string
3515
     */
3516 55619
    public function releaseSavePoint($savepoint)
3517
    {
3518 55619
        return 'RELEASE SAVEPOINT ' . $savepoint;
3519
    }
3520
3521
    /**
3522
     * Returns the SQL to rollback a savepoint.
3523
     *
3524
     * @param string $savepoint
3525
     *
3526
     * @return string
3527
     */
3528 55621
    public function rollbackSavePoint($savepoint)
3529
    {
3530 55621
        return 'ROLLBACK TO SAVEPOINT ' . $savepoint;
3531
    }
3532
3533
    /**
3534
     * Returns the keyword list instance of this platform.
3535
     *
3536
     * @return KeywordList
3537
     *
3538
     * @throws DBALException If no keyword list is specified.
3539
     */
3540 65482
    final public function getReservedKeywordsList()
3541
    {
3542
        // Check for an existing instantiation of the keywords class.
3543 65482
        if ($this->_keywords) {
3544 65348
            return $this->_keywords;
3545
        }
3546
3547 65161
        $class    = $this->getReservedKeywordsClass();
3548 65161
        $keywords = new $class();
3549 65161
        if (! $keywords instanceof KeywordList) {
3550
            throw DBALException::notSupported(__METHOD__);
3551
        }
3552
3553
        // Store the instance so it doesn't need to be generated on every request.
3554 65161
        $this->_keywords = $keywords;
3555
3556 65161
        return $keywords;
3557
    }
3558
3559
    /**
3560
     * Returns the class name of the reserved keywords list.
3561
     *
3562
     * @return string
3563
     *
3564
     * @throws DBALException If not supported on this platform.
3565
     */
3566
    protected function getReservedKeywordsClass()
3567
    {
3568
        throw DBALException::notSupported(__METHOD__);
3569
    }
3570
3571
    /**
3572
     * Quotes a literal string.
3573
     * This method is NOT meant to fix SQL injections!
3574
     * It is only meant to escape this platform's string literal
3575
     * quote character inside the given literal string.
3576
     *
3577
     * @param string $str The literal string to be quoted.
3578
     *
3579
     * @return string The quoted literal string.
3580
     */
3581 62345
    public function quoteStringLiteral($str)
3582
    {
3583 62345
        $c = $this->getStringLiteralQuoteCharacter();
3584
3585 62345
        return $c . str_replace($c, $c . $c, $str) . $c;
3586
    }
3587
3588
    /**
3589
     * Gets the character used for string literal quoting.
3590
     *
3591
     * @return string
3592
     */
3593 62381
    public function getStringLiteralQuoteCharacter()
3594
    {
3595 62381
        return "'";
3596
    }
3597
3598
    /**
3599
     * Escapes metacharacters in a string intended to be used with a LIKE
3600
     * operator.
3601
     *
3602
     * @param string $inputString a literal, unquoted string
3603
     * @param string $escapeChar  should be reused by the caller in the LIKE
3604
     *                            expression.
3605
     */
3606 61988
    final public function escapeStringForLike(string $inputString, string $escapeChar) : string
3607
    {
3608 61988
        return preg_replace(
3609 61988
            '~([' . preg_quote($this->getLikeWildcardCharacters() . $escapeChar, '~') . '])~u',
3610 61988
            addcslashes($escapeChar, '\\') . '$1',
3611 61988
            $inputString
3612
        );
3613
    }
3614
3615 61988
    protected function getLikeWildcardCharacters() : string
3616
    {
3617 61988
        return '%_';
3618
    }
3619
}
3620