Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 621 | Zur aktuellen Revision | Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Doctrine\DBAL\Platforms;
4
 
5
use Doctrine\Common\EventManager;
6
use Doctrine\DBAL\Connection;
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\Exception;
17
use Doctrine\DBAL\Exception\InvalidLockMode;
18
use Doctrine\DBAL\LockMode;
19
use Doctrine\DBAL\Platforms\Keywords\KeywordList;
20
use Doctrine\DBAL\Schema\AbstractSchemaManager;
21
use Doctrine\DBAL\Schema\Column;
22
use Doctrine\DBAL\Schema\ColumnDiff;
23
use Doctrine\DBAL\Schema\Constraint;
24
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
25
use Doctrine\DBAL\Schema\Identifier;
26
use Doctrine\DBAL\Schema\Index;
27
use Doctrine\DBAL\Schema\SchemaDiff;
28
use Doctrine\DBAL\Schema\Sequence;
29
use Doctrine\DBAL\Schema\Table;
30
use Doctrine\DBAL\Schema\TableDiff;
31
use Doctrine\DBAL\Schema\UniqueConstraint;
32
use Doctrine\DBAL\SQL\Parser;
33
use Doctrine\DBAL\TransactionIsolationLevel;
34
use Doctrine\DBAL\Types;
35
use Doctrine\DBAL\Types\Type;
36
use Doctrine\Deprecations\Deprecation;
37
use InvalidArgumentException;
38
use UnexpectedValueException;
39
 
40
use function addcslashes;
41
use function array_map;
42
use function array_merge;
43
use function array_unique;
44
use function array_values;
45
use function assert;
46
use function count;
47
use function explode;
48
use function func_get_arg;
49
use function func_get_args;
50
use function func_num_args;
51
use function implode;
52
use function in_array;
53
use function is_array;
54
use function is_bool;
55
use function is_int;
56
use function is_string;
57
use function preg_quote;
58
use function preg_replace;
59
use function sprintf;
60
use function str_replace;
61
use function strlen;
62
use function strpos;
63
use function strtolower;
64
use function strtoupper;
65
 
66
/**
67
 * Base class for all DatabasePlatforms. The DatabasePlatforms are the central
68
 * point of abstraction of platform-specific behaviors, features and SQL dialects.
69
 * They are a passive source of information.
70
 *
71
 * @todo Remove any unnecessary methods.
72
 */
73
abstract class AbstractPlatform
74
{
75
    public const CREATE_INDEXES = 1;
76
 
77
    public const CREATE_FOREIGNKEYS = 2;
78
 
79
    /** @var string[]|null */
80
    protected $doctrineTypeMapping;
81
 
82
    /**
83
     * Contains a list of all columns that should generate parseable column comments for type-detection
84
     * in reverse engineering scenarios.
85
     *
86
     * @deprecated This property is deprecated and will be removed in Doctrine DBAL 4.0.
87
     *
88
     * @var string[]|null
89
     */
90
    protected $doctrineTypeComments;
91
 
92
    /**
93
     * @deprecated
94
     *
95
     * @var EventManager|null
96
     */
97
    protected $_eventManager;
98
 
99
    /**
100
     * Holds the KeywordList instance for the current platform.
101
     *
102
     * @var KeywordList|null
103
     */
104
    protected $_keywords;
105
 
106
    /**
107
     * Sets the EventManager used by the Platform.
108
     *
109
     * @deprecated
110
     *
111
     * @return void
112
     */
113
    public function setEventManager(EventManager $eventManager)
114
    {
115
        Deprecation::triggerIfCalledFromOutside(
116
            'doctrine/dbal',
117
            'https://github.com/doctrine/dbal/issues/5784',
118
            '%s is deprecated.',
119
            __METHOD__,
120
        );
121
 
122
        $this->_eventManager = $eventManager;
123
    }
124
 
125
    /**
126
     * Gets the EventManager used by the Platform.
127
     *
128
     * @deprecated
129
     *
130
     * @return EventManager|null
131
     */
132
    public function getEventManager()
133
    {
134
        Deprecation::triggerIfCalledFromOutside(
135
            'doctrine/dbal',
136
            'https://github.com/doctrine/dbal/issues/5784',
137
            '%s is deprecated.',
138
            __METHOD__,
139
        );
140
 
141
        return $this->_eventManager;
142
    }
143
 
144
    /**
145
     * Returns the SQL snippet that declares a boolean column.
146
     *
147
     * @param mixed[] $column
148
     *
149
     * @return string
150
     */
151
    abstract public function getBooleanTypeDeclarationSQL(array $column);
152
 
153
    /**
154
     * Returns the SQL snippet that declares a 4 byte integer column.
155
     *
156
     * @param mixed[] $column
157
     *
158
     * @return string
159
     */
160
    abstract public function getIntegerTypeDeclarationSQL(array $column);
161
 
162
    /**
163
     * Returns the SQL snippet that declares an 8 byte integer column.
164
     *
165
     * @param mixed[] $column
166
     *
167
     * @return string
168
     */
169
    abstract public function getBigIntTypeDeclarationSQL(array $column);
170
 
171
    /**
172
     * Returns the SQL snippet that declares a 2 byte integer column.
173
     *
174
     * @param mixed[] $column
175
     *
176
     * @return string
177
     */
178
    abstract public function getSmallIntTypeDeclarationSQL(array $column);
179
 
180
    /**
181
     * Returns the SQL snippet that declares common properties of an integer column.
182
     *
183
     * @param mixed[] $column
184
     *
185
     * @return string
186
     */
187
    abstract protected function _getCommonIntegerTypeDeclarationSQL(array $column);
188
 
189
    /**
190
     * Lazy load Doctrine Type Mappings.
191
     *
192
     * @return void
193
     */
194
    abstract protected function initializeDoctrineTypeMappings();
195
 
196
    /**
197
     * Initializes Doctrine Type Mappings with the platform defaults
198
     * and with all additional type mappings.
199
     */
200
    private function initializeAllDoctrineTypeMappings(): void
201
    {
202
        $this->initializeDoctrineTypeMappings();
203
 
204
        foreach (Type::getTypesMap() as $typeName => $className) {
205
            foreach (Type::getType($typeName)->getMappedDatabaseTypes($this) as $dbType) {
206
                $this->doctrineTypeMapping[$dbType] = $typeName;
207
            }
208
        }
209
    }
210
 
211
    /**
212
     * Returns the SQL snippet used to declare a column that can
213
     * store characters in the ASCII character set
214
     *
215
     * @param mixed[] $column
216
     */
217
    public function getAsciiStringTypeDeclarationSQL(array $column): string
218
    {
219
        return $this->getStringTypeDeclarationSQL($column);
220
    }
221
 
222
    /**
223
     * Returns the SQL snippet used to declare a VARCHAR column type.
224
     *
225
     * @deprecated Use {@link getStringTypeDeclarationSQL()} instead.
226
     *
227
     * @param mixed[] $column
228
     *
229
     * @return string
230
     */
231
    public function getVarcharTypeDeclarationSQL(array $column)
232
    {
233
        if (isset($column['length'])) {
234
            $lengthOmitted = false;
235
        } else {
236
            $column['length'] = $this->getVarcharDefaultLength();
237
            $lengthOmitted    = true;
238
        }
239
 
240
        $fixed = $column['fixed'] ?? false;
241
 
242
        $maxLength = $fixed
243
            ? $this->getCharMaxLength()
244
            : $this->getVarcharMaxLength();
245
 
246
        if ($column['length'] > $maxLength) {
247
            return $this->getClobTypeDeclarationSQL($column);
248
        }
249
 
250
        return $this->getVarcharTypeDeclarationSQLSnippet($column['length'], $fixed, $lengthOmitted);
251
    }
252
 
253
    /**
254
     * Returns the SQL snippet used to declare a string column type.
255
     *
256
     * @param mixed[] $column
257
     *
258
     * @return string
259
     */
260
    public function getStringTypeDeclarationSQL(array $column)
261
    {
262
        return $this->getVarcharTypeDeclarationSQL($column);
263
    }
264
 
265
    /**
266
     * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
267
     *
268
     * @param mixed[] $column The column definition.
269
     *
270
     * @return string
271
     */
272
    public function getBinaryTypeDeclarationSQL(array $column)
273
    {
274
        if (isset($column['length'])) {
275
            $lengthOmitted = false;
276
        } else {
277
            $column['length'] = $this->getBinaryDefaultLength();
278
            $lengthOmitted    = true;
279
        }
280
 
281
        $fixed = $column['fixed'] ?? false;
282
 
283
        $maxLength = $this->getBinaryMaxLength();
284
 
285
        if ($column['length'] > $maxLength) {
286
            if ($maxLength > 0) {
287
                Deprecation::trigger(
288
                    'doctrine/dbal',
289
                    'https://github.com/doctrine/dbal/issues/3187',
290
                    'Binary column length %d is greater than supported by the platform (%d).'
291
                        . ' Reduce the column length or use a BLOB column instead.',
292
                    $column['length'],
293
                    $maxLength,
294
                );
295
            }
296
 
297
            return $this->getBlobTypeDeclarationSQL($column);
298
        }
299
 
300
        return $this->getBinaryTypeDeclarationSQLSnippet($column['length'], $fixed, $lengthOmitted);
301
    }
302
 
303
    /**
304
     * Returns the SQL snippet to declare a GUID/UUID column.
305
     *
306
     * By default this maps directly to a CHAR(36) and only maps to more
307
     * special datatypes when the underlying databases support this datatype.
308
     *
309
     * @param mixed[] $column
310
     *
311
     * @return string
312
     */
313
    public function getGuidTypeDeclarationSQL(array $column)
314
    {
315
        $column['length'] = 36;
316
        $column['fixed']  = true;
317
 
318
        return $this->getStringTypeDeclarationSQL($column);
319
    }
320
 
321
    /**
322
     * Returns the SQL snippet to declare a JSON column.
323
     *
324
     * By default this maps directly to a CLOB and only maps to more
325
     * special datatypes when the underlying databases support this datatype.
326
     *
327
     * @param mixed[] $column
328
     *
329
     * @return string
330
     */
331
    public function getJsonTypeDeclarationSQL(array $column)
332
    {
333
        return $this->getClobTypeDeclarationSQL($column);
334
    }
335
 
336
    /**
337
     * @param int|false $length
338
     * @param bool      $fixed
339
     *
340
     * @return string
341
     *
342
     * @throws Exception If not supported on this platform.
343
     */
344
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/)
345
    {
346
        throw Exception::notSupported('VARCHARs not supported by Platform.');
347
    }
348
 
349
    /**
350
     * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
351
     *
352
     * @param int|false $length The length of the column.
353
     * @param bool      $fixed  Whether the column length is fixed.
354
     *
355
     * @return string
356
     *
357
     * @throws Exception If not supported on this platform.
358
     */
359
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/)
360
    {
361
        throw Exception::notSupported('BINARY/VARBINARY column types are not supported by this platform.');
362
    }
363
 
364
    /**
365
     * Returns the SQL snippet used to declare a CLOB column type.
366
     *
367
     * @param mixed[] $column
368
     *
369
     * @return string
370
     */
371
    abstract public function getClobTypeDeclarationSQL(array $column);
372
 
373
    /**
374
     * Returns the SQL Snippet used to declare a BLOB column type.
375
     *
376
     * @param mixed[] $column
377
     *
378
     * @return string
379
     */
380
    abstract public function getBlobTypeDeclarationSQL(array $column);
381
 
382
    /**
383
     * Gets the name of the platform.
384
     *
385
     * @deprecated Identify platforms by their class.
386
     *
387
     * @return string
388
     */
389
    abstract public function getName();
390
 
391
    /**
392
     * Registers a doctrine type to be used in conjunction with a column type of this platform.
393
     *
394
     * @param string $dbType
395
     * @param string $doctrineType
396
     *
397
     * @return void
398
     *
399
     * @throws Exception If the type is not found.
400
     */
401
    public function registerDoctrineTypeMapping($dbType, $doctrineType)
402
    {
403
        if ($this->doctrineTypeMapping === null) {
404
            $this->initializeAllDoctrineTypeMappings();
405
        }
406
 
407
        if (! Types\Type::hasType($doctrineType)) {
408
            throw Exception::typeNotFound($doctrineType);
409
        }
410
 
411
        $dbType                             = strtolower($dbType);
412
        $this->doctrineTypeMapping[$dbType] = $doctrineType;
413
 
414
        $doctrineType = Type::getType($doctrineType);
415
 
416
        if (! $doctrineType->requiresSQLCommentHint($this)) {
417
            return;
418
        }
419
 
420
        $this->markDoctrineTypeCommented($doctrineType);
421
    }
422
 
423
    /**
424
     * Gets the Doctrine type that is mapped for the given database column type.
425
     *
426
     * @param string $dbType
427
     *
428
     * @return string
429
     *
430
     * @throws Exception
431
     */
432
    public function getDoctrineTypeMapping($dbType)
433
    {
434
        if ($this->doctrineTypeMapping === null) {
435
            $this->initializeAllDoctrineTypeMappings();
436
        }
437
 
438
        $dbType = strtolower($dbType);
439
 
440
        if (! isset($this->doctrineTypeMapping[$dbType])) {
441
            throw new Exception(
442
                'Unknown database type ' . $dbType . ' requested, ' . static::class . ' may not support it.',
443
            );
444
        }
445
 
446
        return $this->doctrineTypeMapping[$dbType];
447
    }
448
 
449
    /**
450
     * Checks if a database type is currently supported by this platform.
451
     *
452
     * @param string $dbType
453
     *
454
     * @return bool
455
     */
456
    public function hasDoctrineTypeMappingFor($dbType)
457
    {
458
        if ($this->doctrineTypeMapping === null) {
459
            $this->initializeAllDoctrineTypeMappings();
460
        }
461
 
462
        $dbType = strtolower($dbType);
463
 
464
        return isset($this->doctrineTypeMapping[$dbType]);
465
    }
466
 
467
    /**
468
     * Initializes the Doctrine Type comments instance variable for in_array() checks.
469
     *
470
     * @deprecated This API will be removed in Doctrine DBAL 4.0.
471
     *
472
     * @return void
473
     */
474
    protected function initializeCommentedDoctrineTypes()
475
    {
476
        Deprecation::triggerIfCalledFromOutside(
477
            'doctrine/dbal',
478
            'https://github.com/doctrine/dbal/pull/5058',
479
            '%s is deprecated and will be removed in Doctrine DBAL 4.0.',
480
            __METHOD__,
481
        );
482
 
483
        $this->doctrineTypeComments = [];
484
 
485
        foreach (Type::getTypesMap() as $typeName => $className) {
486
            $type = Type::getType($typeName);
487
 
488
            if (! $type->requiresSQLCommentHint($this)) {
489
                continue;
490
            }
491
 
492
            $this->doctrineTypeComments[] = $typeName;
493
        }
494
    }
495
 
496
    /**
497
     * Is it necessary for the platform to add a parsable type comment to allow reverse engineering the given type?
498
     *
499
     * @deprecated Use {@link Type::requiresSQLCommentHint()} instead.
500
     *
501
     * @return bool
502
     */
503
    public function isCommentedDoctrineType(Type $doctrineType)
504
    {
505
        Deprecation::triggerIfCalledFromOutside(
506
            'doctrine/dbal',
507
            'https://github.com/doctrine/dbal/pull/5058',
508
            '%s is deprecated and will be removed in Doctrine DBAL 4.0. Use Type::requiresSQLCommentHint() instead.',
509
            __METHOD__,
510
        );
511
 
512
        if ($this->doctrineTypeComments === null) {
513
            $this->initializeCommentedDoctrineTypes();
514
        }
515
 
516
        return $doctrineType->requiresSQLCommentHint($this);
517
    }
518
 
519
    /**
520
     * Marks this type as to be commented in ALTER TABLE and CREATE TABLE statements.
521
     *
522
     * @param string|Type $doctrineType
523
     *
524
     * @return void
525
     */
526
    public function markDoctrineTypeCommented($doctrineType)
527
    {
528
        Deprecation::triggerIfCalledFromOutside(
529
            'doctrine/dbal',
530
            'https://github.com/doctrine/dbal/pull/5058',
531
            '%s is deprecated and will be removed in Doctrine DBAL 4.0. Use Type::requiresSQLCommentHint() instead.',
532
            __METHOD__,
533
        );
534
 
535
        if ($this->doctrineTypeComments === null) {
536
            $this->initializeCommentedDoctrineTypes();
537
        }
538
 
539
        assert(is_array($this->doctrineTypeComments));
540
 
541
        $this->doctrineTypeComments[] = $doctrineType instanceof Type ? $doctrineType->getName() : $doctrineType;
542
    }
543
 
544
    /**
545
     * Gets the comment to append to a column comment that helps parsing this type in reverse engineering.
546
     *
547
     * @deprecated This method will be removed without replacement.
548
     *
549
     * @return string
550
     */
551
    public function getDoctrineTypeComment(Type $doctrineType)
552
    {
553
        Deprecation::triggerIfCalledFromOutside(
554
            'doctrine/dbal',
555
            'https://github.com/doctrine/dbal/pull/5107',
556
            '%s is deprecated and will be removed in Doctrine DBAL 4.0.',
557
            __METHOD__,
558
        );
559
 
560
        return '(DC2Type:' . $doctrineType->getName() . ')';
561
    }
562
 
563
    /**
564
     * Gets the comment of a passed column modified by potential doctrine type comment hints.
565
     *
566
     * @deprecated This method will be removed without replacement.
567
     *
568
     * @return string|null
569
     */
570
    protected function getColumnComment(Column $column)
571
    {
572
        Deprecation::triggerIfCalledFromOutside(
573
            'doctrine/dbal',
574
            'https://github.com/doctrine/dbal/pull/5107',
575
            '%s is deprecated and will be removed in Doctrine DBAL 4.0.',
576
            __METHOD__,
577
        );
578
 
579
        $comment = $column->getComment();
580
 
581
        if ($column->getType()->requiresSQLCommentHint($this)) {
582
            $comment .= $this->getDoctrineTypeComment($column->getType());
583
        }
584
 
585
        return $comment;
586
    }
587
 
588
    /**
589
     * Gets the character used for identifier quoting.
590
     *
591
     * @deprecated Use {@see quoteIdentifier()} to quote identifiers instead.
592
     *
593
     * @return string
594
     */
595
    public function getIdentifierQuoteCharacter()
596
    {
597
        Deprecation::triggerIfCalledFromOutside(
598
            'doctrine/dbal',
599
            'https://github.com/doctrine/dbal/pull/5388',
600
            'AbstractPlatform::getIdentifierQuoteCharacter() is deprecated. Use quoteIdentifier() instead.',
601
        );
602
 
603
        return '"';
604
    }
605
 
606
    /**
607
     * Gets the string portion that starts an SQL comment.
608
     *
609
     * @deprecated
610
     *
611
     * @return string
612
     */
613
    public function getSqlCommentStartString()
614
    {
615
        Deprecation::trigger(
616
            'doctrine/dbal',
617
            'https://github.com/doctrine/dbal/pull/4724',
618
            'AbstractPlatform::getSqlCommentStartString() is deprecated.',
619
        );
620
 
621
        return '--';
622
    }
623
 
624
    /**
625
     * Gets the string portion that ends an SQL comment.
626
     *
627
     * @deprecated
628
     *
629
     * @return string
630
     */
631
    public function getSqlCommentEndString()
632
    {
633
        Deprecation::trigger(
634
            'doctrine/dbal',
635
            'https://github.com/doctrine/dbal/pull/4724',
636
            'AbstractPlatform::getSqlCommentEndString() is deprecated.',
637
        );
638
 
639
        return "\n";
640
    }
641
 
642
    /**
643
     * Gets the maximum length of a char column.
644
     *
645
     * @deprecated
646
     */
647
    public function getCharMaxLength(): int
648
    {
649
        Deprecation::triggerIfCalledFromOutside(
650
            'doctrine/dbal',
651
            'https://github.com/doctrine/dbal/issues/3263',
652
            'AbstractPlatform::getCharMaxLength() is deprecated.',
653
        );
654
 
655
        return $this->getVarcharMaxLength();
656
    }
657
 
658
    /**
659
     * Gets the maximum length of a varchar column.
660
     *
661
     * @deprecated
662
     *
663
     * @return int
664
     */
665
    public function getVarcharMaxLength()
666
    {
667
        Deprecation::triggerIfCalledFromOutside(
668
            'doctrine/dbal',
669
            'https://github.com/doctrine/dbal/issues/3263',
670
            'AbstractPlatform::getVarcharMaxLength() is deprecated.',
671
        );
672
 
673
        return 4000;
674
    }
675
 
676
    /**
677
     * Gets the default length of a varchar column.
678
     *
679
     * @deprecated
680
     *
681
     * @return int
682
     */
683
    public function getVarcharDefaultLength()
684
    {
685
        Deprecation::triggerIfCalledFromOutside(
686
            'doctrine/dbal',
687
            'https://github.com/doctrine/dbal/issues/3263',
688
            'Relying on the default varchar column length is deprecated, specify the length explicitly.',
689
        );
690
 
691
        return 255;
692
    }
693
 
694
    /**
695
     * Gets the maximum length of a binary column.
696
     *
697
     * @deprecated
698
     *
699
     * @return int
700
     */
701
    public function getBinaryMaxLength()
702
    {
703
        Deprecation::triggerIfCalledFromOutside(
704
            'doctrine/dbal',
705
            'https://github.com/doctrine/dbal/issues/3263',
706
            'AbstractPlatform::getBinaryMaxLength() is deprecated.',
707
        );
708
 
709
        return 4000;
710
    }
711
 
712
    /**
713
     * Gets the default length of a binary column.
714
     *
715
     * @deprecated
716
     *
717
     * @return int
718
     */
719
    public function getBinaryDefaultLength()
720
    {
721
        Deprecation::trigger(
722
            'doctrine/dbal',
723
            'https://github.com/doctrine/dbal/issues/3263',
724
            'Relying on the default binary column length is deprecated, specify the length explicitly.',
725
        );
726
 
727
        return 255;
728
    }
729
 
730
    /**
731
     * Gets all SQL wildcard characters of the platform.
732
     *
733
     * @deprecated Use {@see AbstractPlatform::getLikeWildcardCharacters()} instead.
734
     *
735
     * @return string[]
736
     */
737
    public function getWildcards()
738
    {
739
        Deprecation::trigger(
740
            'doctrine/dbal',
741
            'https://github.com/doctrine/dbal/pull/4724',
742
            'AbstractPlatform::getWildcards() is deprecated.'
743
            . ' Use AbstractPlatform::getLikeWildcardCharacters() instead.',
744
        );
745
 
746
        return ['%', '_'];
747
    }
748
 
749
    /**
750
     * Returns the regular expression operator.
751
     *
752
     * @return string
753
     *
754
     * @throws Exception If not supported on this platform.
755
     */
756
    public function getRegexpExpression()
757
    {
758
        throw Exception::notSupported(__METHOD__);
759
    }
760
 
761
    /**
762
     * Returns the SQL snippet to get the average value of a column.
763
     *
764
     * @deprecated Use AVG() in SQL instead.
765
     *
766
     * @param string $column The column to use.
767
     *
768
     * @return string Generated SQL including an AVG aggregate function.
769
     */
770
    public function getAvgExpression($column)
771
    {
772
        Deprecation::trigger(
773
            'doctrine/dbal',
774
            'https://github.com/doctrine/dbal/pull/4724',
775
            'AbstractPlatform::getAvgExpression() is deprecated. Use AVG() in SQL instead.',
776
        );
777
 
778
        return 'AVG(' . $column . ')';
779
    }
780
 
781
    /**
782
     * Returns the SQL snippet to get the number of rows (without a NULL value) of a column.
783
     *
784
     * If a '*' is used instead of a column the number of selected rows is returned.
785
     *
786
     * @deprecated Use COUNT() in SQL instead.
787
     *
788
     * @param string|int $column The column to use.
789
     *
790
     * @return string Generated SQL including a COUNT aggregate function.
791
     */
792
    public function getCountExpression($column)
793
    {
794
        Deprecation::trigger(
795
            'doctrine/dbal',
796
            'https://github.com/doctrine/dbal/pull/4724',
797
            'AbstractPlatform::getCountExpression() is deprecated. Use COUNT() in SQL instead.',
798
        );
799
 
800
        return 'COUNT(' . $column . ')';
801
    }
802
 
803
    /**
804
     * Returns the SQL snippet to get the highest value of a column.
805
     *
806
     * @deprecated Use MAX() in SQL instead.
807
     *
808
     * @param string $column The column to use.
809
     *
810
     * @return string Generated SQL including a MAX aggregate function.
811
     */
812
    public function getMaxExpression($column)
813
    {
814
        Deprecation::trigger(
815
            'doctrine/dbal',
816
            'https://github.com/doctrine/dbal/pull/4724',
817
            'AbstractPlatform::getMaxExpression() is deprecated. Use MAX() in SQL instead.',
818
        );
819
 
820
        return 'MAX(' . $column . ')';
821
    }
822
 
823
    /**
824
     * Returns the SQL snippet to get the lowest value of a column.
825
     *
826
     * @deprecated Use MIN() in SQL instead.
827
     *
828
     * @param string $column The column to use.
829
     *
830
     * @return string Generated SQL including a MIN aggregate function.
831
     */
832
    public function getMinExpression($column)
833
    {
834
        Deprecation::trigger(
835
            'doctrine/dbal',
836
            'https://github.com/doctrine/dbal/pull/4724',
837
            'AbstractPlatform::getMinExpression() is deprecated. Use MIN() in SQL instead.',
838
        );
839
 
840
        return 'MIN(' . $column . ')';
841
    }
842
 
843
    /**
844
     * Returns the SQL snippet to get the total sum of a column.
845
     *
846
     * @deprecated Use SUM() in SQL instead.
847
     *
848
     * @param string $column The column to use.
849
     *
850
     * @return string Generated SQL including a SUM aggregate function.
851
     */
852
    public function getSumExpression($column)
853
    {
854
        Deprecation::trigger(
855
            'doctrine/dbal',
856
            'https://github.com/doctrine/dbal/pull/4724',
857
            'AbstractPlatform::getSumExpression() is deprecated. Use SUM() in SQL instead.',
858
        );
859
 
860
        return 'SUM(' . $column . ')';
861
    }
862
 
863
    // scalar functions
864
 
865
    /**
866
     * Returns the SQL snippet to get the md5 sum of a column.
867
     *
868
     * Note: Not SQL92, but common functionality.
869
     *
870
     * @deprecated
871
     *
872
     * @param string $column
873
     *
874
     * @return string
875
     */
876
    public function getMd5Expression($column)
877
    {
878
        Deprecation::trigger(
879
            'doctrine/dbal',
880
            'https://github.com/doctrine/dbal/pull/4724',
881
            'AbstractPlatform::getMd5Expression() is deprecated.',
882
        );
883
 
884
        return 'MD5(' . $column . ')';
885
    }
886
 
887
    /**
888
     * Returns the SQL snippet to get the length of a text column in characters.
889
     *
890
     * @param string $column
891
     *
892
     * @return string
893
     */
894
    public function getLengthExpression($column)
895
    {
896
        return 'LENGTH(' . $column . ')';
897
    }
898
 
899
    /**
900
     * Returns the SQL snippet to get the squared value of a column.
901
     *
902
     * @deprecated Use SQRT() in SQL instead.
903
     *
904
     * @param string $column The column to use.
905
     *
906
     * @return string Generated SQL including an SQRT aggregate function.
907
     */
908
    public function getSqrtExpression($column)
909
    {
910
        Deprecation::trigger(
911
            'doctrine/dbal',
912
            'https://github.com/doctrine/dbal/pull/4724',
913
            'AbstractPlatform::getSqrtExpression() is deprecated. Use SQRT() in SQL instead.',
914
        );
915
 
916
        return 'SQRT(' . $column . ')';
917
    }
918
 
919
    /**
920
     * Returns the SQL snippet to round a numeric column to the number of decimals specified.
921
     *
922
     * @deprecated Use ROUND() in SQL instead.
923
     *
924
     * @param string     $column
925
     * @param string|int $decimals
926
     *
927
     * @return string
928
     */
929
    public function getRoundExpression($column, $decimals = 0)
930
    {
931
        Deprecation::trigger(
932
            'doctrine/dbal',
933
            'https://github.com/doctrine/dbal/pull/4724',
934
            'AbstractPlatform::getRoundExpression() is deprecated. Use ROUND() in SQL instead.',
935
        );
936
 
937
        return 'ROUND(' . $column . ', ' . $decimals . ')';
938
    }
939
 
940
    /**
941
     * Returns the SQL snippet to get the remainder of the division operation $expression1 / $expression2.
942
     *
943
     * @param string $expression1
944
     * @param string $expression2
945
     *
946
     * @return string
947
     */
948
    public function getModExpression($expression1, $expression2)
949
    {
950
        return 'MOD(' . $expression1 . ', ' . $expression2 . ')';
951
    }
952
 
953
    /**
954
     * Returns the SQL snippet to trim a string.
955
     *
956
     * @param string      $str  The expression to apply the trim to.
957
     * @param int         $mode The position of the trim (leading/trailing/both).
958
     * @param string|bool $char The char to trim, has to be quoted already. Defaults to space.
959
     *
960
     * @return string
961
     */
962
    public function getTrimExpression($str, $mode = TrimMode::UNSPECIFIED, $char = false)
963
    {
964
        $expression = '';
965
 
966
        switch ($mode) {
967
            case TrimMode::LEADING:
968
                $expression = 'LEADING ';
969
                break;
970
 
971
            case TrimMode::TRAILING:
972
                $expression = 'TRAILING ';
973
                break;
974
 
975
            case TrimMode::BOTH:
976
                $expression = 'BOTH ';
977
                break;
978
        }
979
 
980
        if ($char !== false) {
981
            $expression .= $char . ' ';
982
        }
983
 
984
        if ($mode !== TrimMode::UNSPECIFIED || $char !== false) {
985
            $expression .= 'FROM ';
986
        }
987
 
988
        return 'TRIM(' . $expression . $str . ')';
989
    }
990
 
991
    /**
992
     * Returns the SQL snippet to trim trailing space characters from the expression.
993
     *
994
     * @deprecated Use RTRIM() in SQL instead.
995
     *
996
     * @param string $str Literal string or column name.
997
     *
998
     * @return string
999
     */
1000
    public function getRtrimExpression($str)
1001
    {
1002
        Deprecation::trigger(
1003
            'doctrine/dbal',
1004
            'https://github.com/doctrine/dbal/pull/4724',
1005
            'AbstractPlatform::getRtrimExpression() is deprecated. Use RTRIM() in SQL instead.',
1006
        );
1007
 
1008
        return 'RTRIM(' . $str . ')';
1009
    }
1010
 
1011
    /**
1012
     * Returns the SQL snippet to trim leading space characters from the expression.
1013
     *
1014
     * @deprecated Use LTRIM() in SQL instead.
1015
     *
1016
     * @param string $str Literal string or column name.
1017
     *
1018
     * @return string
1019
     */
1020
    public function getLtrimExpression($str)
1021
    {
1022
        Deprecation::trigger(
1023
            'doctrine/dbal',
1024
            'https://github.com/doctrine/dbal/pull/4724',
1025
            'AbstractPlatform::getLtrimExpression() is deprecated. Use LTRIM() in SQL instead.',
1026
        );
1027
 
1028
        return 'LTRIM(' . $str . ')';
1029
    }
1030
 
1031
    /**
1032
     * Returns the SQL snippet to change all characters from the expression to uppercase,
1033
     * according to the current character set mapping.
1034
     *
1035
     * @deprecated Use UPPER() in SQL instead.
1036
     *
1037
     * @param string $str Literal string or column name.
1038
     *
1039
     * @return string
1040
     */
1041
    public function getUpperExpression($str)
1042
    {
1043
        Deprecation::trigger(
1044
            'doctrine/dbal',
1045
            'https://github.com/doctrine/dbal/pull/4724',
1046
            'AbstractPlatform::getUpperExpression() is deprecated. Use UPPER() in SQL instead.',
1047
        );
1048
 
1049
        return 'UPPER(' . $str . ')';
1050
    }
1051
 
1052
    /**
1053
     * Returns the SQL snippet to change all characters from the expression to lowercase,
1054
     * according to the current character set mapping.
1055
     *
1056
     * @deprecated Use LOWER() in SQL instead.
1057
     *
1058
     * @param string $str Literal string or column name.
1059
     *
1060
     * @return string
1061
     */
1062
    public function getLowerExpression($str)
1063
    {
1064
        Deprecation::trigger(
1065
            'doctrine/dbal',
1066
            'https://github.com/doctrine/dbal/pull/4724',
1067
            'AbstractPlatform::getLowerExpression() is deprecated. Use LOWER() in SQL instead.',
1068
        );
1069
 
1070
        return 'LOWER(' . $str . ')';
1071
    }
1072
 
1073
    /**
1074
     * Returns the SQL snippet to get the position of the first occurrence of substring $substr in string $str.
1075
     *
1076
     * @param string           $str      Literal string.
1077
     * @param string           $substr   Literal string to find.
1078
     * @param string|int|false $startPos Position to start at, beginning of string by default.
1079
     *
1080
     * @return string
1081
     *
1082
     * @throws Exception If not supported on this platform.
1083
     */
1084
    public function getLocateExpression($str, $substr, $startPos = false)
1085
    {
1086
        throw Exception::notSupported(__METHOD__);
1087
    }
1088
 
1089
    /**
1090
     * Returns the SQL snippet to get the current system date.
1091
     *
1092
     * @deprecated Generate dates within the application.
1093
     *
1094
     * @return string
1095
     */
1096
    public function getNowExpression()
1097
    {
1098
        Deprecation::trigger(
1099
            'doctrine/dbal',
1100
            'https://github.com/doctrine/dbal/pull/4753',
1101
            'AbstractPlatform::getNowExpression() is deprecated. Generate dates within the application.',
1102
        );
1103
 
1104
        return 'NOW()';
1105
    }
1106
 
1107
    /**
1108
     * Returns a SQL snippet to get a substring inside an SQL statement.
1109
     *
1110
     * Note: Not SQL92, but common functionality.
1111
     *
1112
     * SQLite only supports the 2 parameter variant of this function.
1113
     *
1114
     * @param string          $string An sql string literal or column name/alias.
1115
     * @param string|int      $start  Where to start the substring portion.
1116
     * @param string|int|null $length The substring portion length.
1117
     *
1118
     * @return string
1119
     */
1120
    public function getSubstringExpression($string, $start, $length = null)
1121
    {
1122
        if ($length === null) {
1123
            return 'SUBSTRING(' . $string . ' FROM ' . $start . ')';
1124
        }
1125
 
1126
        return 'SUBSTRING(' . $string . ' FROM ' . $start . ' FOR ' . $length . ')';
1127
    }
1128
 
1129
    /**
1130
     * Returns a SQL snippet to concatenate the given expressions.
1131
     *
1132
     * Accepts an arbitrary number of string parameters. Each parameter must contain an expression.
1133
     *
1134
     * @return string
1135
     */
1136
    public function getConcatExpression()
1137
    {
1138
        return implode(' || ', func_get_args());
1139
    }
1140
 
1141
    /**
1142
     * Returns the SQL for a logical not.
1143
     *
1144
     * Example:
1145
     * <code>
1146
     * $q = new Doctrine_Query();
1147
     * $e = $q->expr;
1148
     * $q->select('*')->from('table')
1149
     *   ->where($e->eq('id', $e->not('null'));
1150
     * </code>
1151
     *
1152
     * @deprecated Use NOT() in SQL instead.
1153
     *
1154
     * @param string $expression
1155
     *
1156
     * @return string The logical expression.
1157
     */
1158
    public function getNotExpression($expression)
1159
    {
1160
        Deprecation::trigger(
1161
            'doctrine/dbal',
1162
            'https://github.com/doctrine/dbal/pull/4724',
1163
            'AbstractPlatform::getNotExpression() is deprecated. Use NOT() in SQL instead.',
1164
        );
1165
 
1166
        return 'NOT(' . $expression . ')';
1167
    }
1168
 
1169
    /**
1170
     * Returns the SQL that checks if an expression is null.
1171
     *
1172
     * @deprecated Use IS NULL in SQL instead.
1173
     *
1174
     * @param string $expression The expression that should be compared to null.
1175
     *
1176
     * @return string The logical expression.
1177
     */
1178
    public function getIsNullExpression($expression)
1179
    {
1180
        Deprecation::trigger(
1181
            'doctrine/dbal',
1182
            'https://github.com/doctrine/dbal/pull/4724',
1183
            'AbstractPlatform::getIsNullExpression() is deprecated. Use IS NULL in SQL instead.',
1184
        );
1185
 
1186
        return $expression . ' IS NULL';
1187
    }
1188
 
1189
    /**
1190
     * Returns the SQL that checks if an expression is not null.
1191
     *
1192
     * @deprecated Use IS NOT NULL in SQL instead.
1193
     *
1194
     * @param string $expression The expression that should be compared to null.
1195
     *
1196
     * @return string The logical expression.
1197
     */
1198
    public function getIsNotNullExpression($expression)
1199
    {
1200
        Deprecation::trigger(
1201
            'doctrine/dbal',
1202
            'https://github.com/doctrine/dbal/pull/4724',
1203
            'AbstractPlatform::getIsNotNullExpression() is deprecated. Use IS NOT NULL in SQL instead.',
1204
        );
1205
 
1206
        return $expression . ' IS NOT NULL';
1207
    }
1208
 
1209
    /**
1210
     * Returns the SQL that checks if an expression evaluates to a value between two values.
1211
     *
1212
     * The parameter $expression is checked if it is between $value1 and $value2.
1213
     *
1214
     * Note: There is a slight difference in the way BETWEEN works on some databases.
1215
     * http://www.w3schools.com/sql/sql_between.asp. If you want complete database
1216
     * independence you should avoid using between().
1217
     *
1218
     * @deprecated Use BETWEEN in SQL instead.
1219
     *
1220
     * @param string $expression The value to compare to.
1221
     * @param string $value1     The lower value to compare with.
1222
     * @param string $value2     The higher value to compare with.
1223
     *
1224
     * @return string The logical expression.
1225
     */
1226
    public function getBetweenExpression($expression, $value1, $value2)
1227
    {
1228
        Deprecation::trigger(
1229
            'doctrine/dbal',
1230
            'https://github.com/doctrine/dbal/pull/4724',
1231
            'AbstractPlatform::getBetweenExpression() is deprecated. Use BETWEEN in SQL instead.',
1232
        );
1233
 
1234
        return $expression . ' BETWEEN ' . $value1 . ' AND ' . $value2;
1235
    }
1236
 
1237
    /**
1238
     * Returns the SQL to get the arccosine of a value.
1239
     *
1240
     * @deprecated Use ACOS() in SQL instead.
1241
     *
1242
     * @param string $value
1243
     *
1244
     * @return string
1245
     */
1246
    public function getAcosExpression($value)
1247
    {
1248
        Deprecation::trigger(
1249
            'doctrine/dbal',
1250
            'https://github.com/doctrine/dbal/pull/4724',
1251
            'AbstractPlatform::getAcosExpression() is deprecated. Use ACOS() in SQL instead.',
1252
        );
1253
 
1254
        return 'ACOS(' . $value . ')';
1255
    }
1256
 
1257
    /**
1258
     * Returns the SQL to get the sine of a value.
1259
     *
1260
     * @deprecated Use SIN() in SQL instead.
1261
     *
1262
     * @param string $value
1263
     *
1264
     * @return string
1265
     */
1266
    public function getSinExpression($value)
1267
    {
1268
        Deprecation::trigger(
1269
            'doctrine/dbal',
1270
            'https://github.com/doctrine/dbal/pull/4724',
1271
            'AbstractPlatform::getSinExpression() is deprecated. Use SIN() in SQL instead.',
1272
        );
1273
 
1274
        return 'SIN(' . $value . ')';
1275
    }
1276
 
1277
    /**
1278
     * Returns the SQL to get the PI value.
1279
     *
1280
     * @deprecated Use PI() in SQL instead.
1281
     *
1282
     * @return string
1283
     */
1284
    public function getPiExpression()
1285
    {
1286
        Deprecation::trigger(
1287
            'doctrine/dbal',
1288
            'https://github.com/doctrine/dbal/pull/4724',
1289
            'AbstractPlatform::getPiExpression() is deprecated. Use PI() in SQL instead.',
1290
        );
1291
 
1292
        return 'PI()';
1293
    }
1294
 
1295
    /**
1296
     * Returns the SQL to get the cosine of a value.
1297
     *
1298
     * @deprecated Use COS() in SQL instead.
1299
     *
1300
     * @param string $value
1301
     *
1302
     * @return string
1303
     */
1304
    public function getCosExpression($value)
1305
    {
1306
        Deprecation::trigger(
1307
            'doctrine/dbal',
1308
            'https://github.com/doctrine/dbal/pull/4724',
1309
            'AbstractPlatform::getCosExpression() is deprecated. Use COS() in SQL instead.',
1310
        );
1311
 
1312
        return 'COS(' . $value . ')';
1313
    }
1314
 
1315
    /**
1316
     * Returns the SQL to calculate the difference in days between the two passed dates.
1317
     *
1318
     * Computes diff = date1 - date2.
1319
     *
1320
     * @param string $date1
1321
     * @param string $date2
1322
     *
1323
     * @return string
1324
     *
1325
     * @throws Exception If not supported on this platform.
1326
     */
1327
    public function getDateDiffExpression($date1, $date2)
1328
    {
1329
        throw Exception::notSupported(__METHOD__);
1330
    }
1331
 
1332
    /**
1333
     * Returns the SQL to add the number of given seconds to a date.
1334
     *
1335
     * @param string             $date
1336
     * @param int|numeric-string $seconds
1337
     *
1338
     * @return string
1339
     *
1340
     * @throws Exception If not supported on this platform.
1341
     */
1342
    public function getDateAddSecondsExpression($date, $seconds)
1343
    {
1344
        if (is_int($seconds)) {
1345
            Deprecation::trigger(
1346
                'doctrine/dbal',
1347
                'https://github.com/doctrine/dbal/pull/3498',
1348
                'Passing $seconds as an integer is deprecated. Pass it as a numeric string instead.',
1349
            );
1350
        }
1351
 
1352
        return $this->getDateArithmeticIntervalExpression($date, '+', $seconds, DateIntervalUnit::SECOND);
1353
    }
1354
 
1355
    /**
1356
     * Returns the SQL to subtract the number of given seconds from a date.
1357
     *
1358
     * @param string             $date
1359
     * @param int|numeric-string $seconds
1360
     *
1361
     * @return string
1362
     *
1363
     * @throws Exception If not supported on this platform.
1364
     */
1365
    public function getDateSubSecondsExpression($date, $seconds)
1366
    {
1367
        if (is_int($seconds)) {
1368
            Deprecation::trigger(
1369
                'doctrine/dbal',
1370
                'https://github.com/doctrine/dbal/pull/3498',
1371
                'Passing $seconds as an integer is deprecated. Pass it as a numeric string instead.',
1372
            );
1373
        }
1374
 
1375
        return $this->getDateArithmeticIntervalExpression($date, '-', $seconds, DateIntervalUnit::SECOND);
1376
    }
1377
 
1378
    /**
1379
     * Returns the SQL to add the number of given minutes to a date.
1380
     *
1381
     * @param string             $date
1382
     * @param int|numeric-string $minutes
1383
     *
1384
     * @return string
1385
     *
1386
     * @throws Exception If not supported on this platform.
1387
     */
1388
    public function getDateAddMinutesExpression($date, $minutes)
1389
    {
1390
        if (is_int($minutes)) {
1391
            Deprecation::trigger(
1392
                'doctrine/dbal',
1393
                'https://github.com/doctrine/dbal/pull/3498',
1394
                'Passing $minutes as an integer is deprecated. Pass it as a numeric string instead.',
1395
            );
1396
        }
1397
 
1398
        return $this->getDateArithmeticIntervalExpression($date, '+', $minutes, DateIntervalUnit::MINUTE);
1399
    }
1400
 
1401
    /**
1402
     * Returns the SQL to subtract the number of given minutes from a date.
1403
     *
1404
     * @param string             $date
1405
     * @param int|numeric-string $minutes
1406
     *
1407
     * @return string
1408
     *
1409
     * @throws Exception If not supported on this platform.
1410
     */
1411
    public function getDateSubMinutesExpression($date, $minutes)
1412
    {
1413
        if (is_int($minutes)) {
1414
            Deprecation::trigger(
1415
                'doctrine/dbal',
1416
                'https://github.com/doctrine/dbal/pull/3498',
1417
                'Passing $minutes as an integer is deprecated. Pass it as a numeric string instead.',
1418
            );
1419
        }
1420
 
1421
        return $this->getDateArithmeticIntervalExpression($date, '-', $minutes, DateIntervalUnit::MINUTE);
1422
    }
1423
 
1424
    /**
1425
     * Returns the SQL to add the number of given hours to a date.
1426
     *
1427
     * @param string             $date
1428
     * @param int|numeric-string $hours
1429
     *
1430
     * @return string
1431
     *
1432
     * @throws Exception If not supported on this platform.
1433
     */
1434
    public function getDateAddHourExpression($date, $hours)
1435
    {
1436
        if (is_int($hours)) {
1437
            Deprecation::trigger(
1438
                'doctrine/dbal',
1439
                'https://github.com/doctrine/dbal/pull/3498',
1440
                'Passing $hours as an integer is deprecated. Pass it as a numeric string instead.',
1441
            );
1442
        }
1443
 
1444
        return $this->getDateArithmeticIntervalExpression($date, '+', $hours, DateIntervalUnit::HOUR);
1445
    }
1446
 
1447
    /**
1448
     * Returns the SQL to subtract the number of given hours to a date.
1449
     *
1450
     * @param string             $date
1451
     * @param int|numeric-string $hours
1452
     *
1453
     * @return string
1454
     *
1455
     * @throws Exception If not supported on this platform.
1456
     */
1457
    public function getDateSubHourExpression($date, $hours)
1458
    {
1459
        if (is_int($hours)) {
1460
            Deprecation::trigger(
1461
                'doctrine/dbal',
1462
                'https://github.com/doctrine/dbal/pull/3498',
1463
                'Passing $hours as an integer is deprecated. Pass it as a numeric string instead.',
1464
            );
1465
        }
1466
 
1467
        return $this->getDateArithmeticIntervalExpression($date, '-', $hours, DateIntervalUnit::HOUR);
1468
    }
1469
 
1470
    /**
1471
     * Returns the SQL to add the number of given days to a date.
1472
     *
1473
     * @param string             $date
1474
     * @param int|numeric-string $days
1475
     *
1476
     * @return string
1477
     *
1478
     * @throws Exception If not supported on this platform.
1479
     */
1480
    public function getDateAddDaysExpression($date, $days)
1481
    {
1482
        if (is_int($days)) {
1483
            Deprecation::trigger(
1484
                'doctrine/dbal',
1485
                'https://github.com/doctrine/dbal/pull/3498',
1486
                'Passing $days as an integer is deprecated. Pass it as a numeric string instead.',
1487
            );
1488
        }
1489
 
1490
        return $this->getDateArithmeticIntervalExpression($date, '+', $days, DateIntervalUnit::DAY);
1491
    }
1492
 
1493
    /**
1494
     * Returns the SQL to subtract the number of given days to a date.
1495
     *
1496
     * @param string             $date
1497
     * @param int|numeric-string $days
1498
     *
1499
     * @return string
1500
     *
1501
     * @throws Exception If not supported on this platform.
1502
     */
1503
    public function getDateSubDaysExpression($date, $days)
1504
    {
1505
        if (is_int($days)) {
1506
            Deprecation::trigger(
1507
                'doctrine/dbal',
1508
                'https://github.com/doctrine/dbal/pull/3498',
1509
                'Passing $days as an integer is deprecated. Pass it as a numeric string instead.',
1510
            );
1511
        }
1512
 
1513
        return $this->getDateArithmeticIntervalExpression($date, '-', $days, DateIntervalUnit::DAY);
1514
    }
1515
 
1516
    /**
1517
     * Returns the SQL to add the number of given weeks to a date.
1518
     *
1519
     * @param string             $date
1520
     * @param int|numeric-string $weeks
1521
     *
1522
     * @return string
1523
     *
1524
     * @throws Exception If not supported on this platform.
1525
     */
1526
    public function getDateAddWeeksExpression($date, $weeks)
1527
    {
1528
        if (is_int($weeks)) {
1529
            Deprecation::trigger(
1530
                'doctrine/dbal',
1531
                'https://github.com/doctrine/dbal/pull/3498',
1532
                'Passing $weeks as an integer is deprecated. Pass it as a numeric string instead.',
1533
            );
1534
        }
1535
 
1536
        return $this->getDateArithmeticIntervalExpression($date, '+', $weeks, DateIntervalUnit::WEEK);
1537
    }
1538
 
1539
    /**
1540
     * Returns the SQL to subtract the number of given weeks from a date.
1541
     *
1542
     * @param string             $date
1543
     * @param int|numeric-string $weeks
1544
     *
1545
     * @return string
1546
     *
1547
     * @throws Exception If not supported on this platform.
1548
     */
1549
    public function getDateSubWeeksExpression($date, $weeks)
1550
    {
1551
        if (is_int($weeks)) {
1552
            Deprecation::trigger(
1553
                'doctrine/dbal',
1554
                'https://github.com/doctrine/dbal/pull/3498',
1555
                'Passing $weeks as an integer is deprecated. Pass it as a numeric string instead.',
1556
            );
1557
        }
1558
 
1559
        return $this->getDateArithmeticIntervalExpression($date, '-', $weeks, DateIntervalUnit::WEEK);
1560
    }
1561
 
1562
    /**
1563
     * Returns the SQL to add the number of given months to a date.
1564
     *
1565
     * @param string             $date
1566
     * @param int|numeric-string $months
1567
     *
1568
     * @return string
1569
     *
1570
     * @throws Exception If not supported on this platform.
1571
     */
1572
    public function getDateAddMonthExpression($date, $months)
1573
    {
1574
        if (is_int($months)) {
1575
            Deprecation::trigger(
1576
                'doctrine/dbal',
1577
                'https://github.com/doctrine/dbal/pull/3498',
1578
                'Passing $months as an integer is deprecated. Pass it as a numeric string instead.',
1579
            );
1580
        }
1581
 
1582
        return $this->getDateArithmeticIntervalExpression($date, '+', $months, DateIntervalUnit::MONTH);
1583
    }
1584
 
1585
    /**
1586
     * Returns the SQL to subtract the number of given months to a date.
1587
     *
1588
     * @param string             $date
1589
     * @param int|numeric-string $months
1590
     *
1591
     * @return string
1592
     *
1593
     * @throws Exception If not supported on this platform.
1594
     */
1595
    public function getDateSubMonthExpression($date, $months)
1596
    {
1597
        if (is_int($months)) {
1598
            Deprecation::trigger(
1599
                'doctrine/dbal',
1600
                'https://github.com/doctrine/dbal/pull/3498',
1601
                'Passing $months as an integer is deprecated. Pass it as a numeric string instead.',
1602
            );
1603
        }
1604
 
1605
        return $this->getDateArithmeticIntervalExpression($date, '-', $months, DateIntervalUnit::MONTH);
1606
    }
1607
 
1608
    /**
1609
     * Returns the SQL to add the number of given quarters to a date.
1610
     *
1611
     * @param string             $date
1612
     * @param int|numeric-string $quarters
1613
     *
1614
     * @return string
1615
     *
1616
     * @throws Exception If not supported on this platform.
1617
     */
1618
    public function getDateAddQuartersExpression($date, $quarters)
1619
    {
1620
        if (is_int($quarters)) {
1621
            Deprecation::trigger(
1622
                'doctrine/dbal',
1623
                'https://github.com/doctrine/dbal/pull/3498',
1624
                'Passing $quarters as an integer is deprecated. Pass it as a numeric string instead.',
1625
            );
1626
        }
1627
 
1628
        return $this->getDateArithmeticIntervalExpression($date, '+', $quarters, DateIntervalUnit::QUARTER);
1629
    }
1630
 
1631
    /**
1632
     * Returns the SQL to subtract the number of given quarters from a date.
1633
     *
1634
     * @param string             $date
1635
     * @param int|numeric-string $quarters
1636
     *
1637
     * @return string
1638
     *
1639
     * @throws Exception If not supported on this platform.
1640
     */
1641
    public function getDateSubQuartersExpression($date, $quarters)
1642
    {
1643
        if (is_int($quarters)) {
1644
            Deprecation::trigger(
1645
                'doctrine/dbal',
1646
                'https://github.com/doctrine/dbal/pull/3498',
1647
                'Passing $quarters as an integer is deprecated. Pass it as a numeric string instead.',
1648
            );
1649
        }
1650
 
1651
        return $this->getDateArithmeticIntervalExpression($date, '-', $quarters, DateIntervalUnit::QUARTER);
1652
    }
1653
 
1654
    /**
1655
     * Returns the SQL to add the number of given years to a date.
1656
     *
1657
     * @param string             $date
1658
     * @param int|numeric-string $years
1659
     *
1660
     * @return string
1661
     *
1662
     * @throws Exception If not supported on this platform.
1663
     */
1664
    public function getDateAddYearsExpression($date, $years)
1665
    {
1666
        if (is_int($years)) {
1667
            Deprecation::trigger(
1668
                'doctrine/dbal',
1669
                'https://github.com/doctrine/dbal/pull/3498',
1670
                'Passing $years as an integer is deprecated. Pass it as a numeric string instead.',
1671
            );
1672
        }
1673
 
1674
        return $this->getDateArithmeticIntervalExpression($date, '+', $years, DateIntervalUnit::YEAR);
1675
    }
1676
 
1677
    /**
1678
     * Returns the SQL to subtract the number of given years from a date.
1679
     *
1680
     * @param string             $date
1681
     * @param int|numeric-string $years
1682
     *
1683
     * @return string
1684
     *
1685
     * @throws Exception If not supported on this platform.
1686
     */
1687
    public function getDateSubYearsExpression($date, $years)
1688
    {
1689
        if (is_int($years)) {
1690
            Deprecation::trigger(
1691
                'doctrine/dbal',
1692
                'https://github.com/doctrine/dbal/pull/3498',
1693
                'Passing $years as an integer is deprecated. Pass it as a numeric string instead.',
1694
            );
1695
        }
1696
 
1697
        return $this->getDateArithmeticIntervalExpression($date, '-', $years, DateIntervalUnit::YEAR);
1698
    }
1699
 
1700
    /**
1701
     * Returns the SQL for a date arithmetic expression.
1702
     *
1703
     * @param string             $date     The column or literal representing a date
1704
     *                                     to perform the arithmetic operation on.
1705
     * @param string             $operator The arithmetic operator (+ or -).
1706
     * @param int|numeric-string $interval The interval that shall be calculated into the date.
1707
     * @param string             $unit     The unit of the interval that shall be calculated into the date.
1708
     *                                     One of the DATE_INTERVAL_UNIT_* constants.
1709
     *
1710
     * @return string
1711
     *
1712
     * @throws Exception If not supported on this platform.
1713
     */
1714
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
1715
    {
1716
        throw Exception::notSupported(__METHOD__);
1717
    }
1718
 
1719
    /**
1720
     * Returns the SQL bit AND comparison expression.
1721
     *
1722
     * @param string $value1
1723
     * @param string $value2
1724
     *
1725
     * @return string
1726
     */
1727
    public function getBitAndComparisonExpression($value1, $value2)
1728
    {
1729
        return '(' . $value1 . ' & ' . $value2 . ')';
1730
    }
1731
 
1732
    /**
1733
     * Returns the SQL bit OR comparison expression.
1734
     *
1735
     * @param string $value1
1736
     * @param string $value2
1737
     *
1738
     * @return string
1739
     */
1740
    public function getBitOrComparisonExpression($value1, $value2)
1741
    {
1742
        return '(' . $value1 . ' | ' . $value2 . ')';
1743
    }
1744
 
1745
    /**
1746
     * Returns the SQL expression which represents the currently selected database.
1747
     */
1748
    abstract public function getCurrentDatabaseExpression(): string;
1749
 
1750
    /**
1751
     * Returns the FOR UPDATE expression.
1752
     *
1753
     * @return string
1754
     */
1755
    public function getForUpdateSQL()
1756
    {
1757
        return 'FOR UPDATE';
1758
    }
1759
 
1760
    /**
1761
     * Honors that some SQL vendors such as MsSql use table hints for locking instead of the
1762
     * ANSI SQL FOR UPDATE specification.
1763
     *
1764
     * @param string $fromClause The FROM clause to append the hint for the given lock mode to
1765
     * @param int    $lockMode   One of the Doctrine\DBAL\LockMode::* constants
1766
     * @psalm-param LockMode::* $lockMode
1767
     */
1768
    public function appendLockHint(string $fromClause, int $lockMode): string
1769
    {
1770
        switch ($lockMode) {
1771
            case LockMode::NONE:
1772
            case LockMode::OPTIMISTIC:
1773
            case LockMode::PESSIMISTIC_READ:
1774
            case LockMode::PESSIMISTIC_WRITE:
1775
                return $fromClause;
1776
 
1777
            default:
1778
                throw InvalidLockMode::fromLockMode($lockMode);
1779
        }
1780
    }
1781
 
1782
    /**
1783
     * Returns the SQL snippet to append to any SELECT statement which locks rows in shared read lock.
1784
     *
1785
     * This defaults to the ANSI SQL "FOR UPDATE", which is an exclusive lock (Write). Some database
1786
     * vendors allow to lighten this constraint up to be a real read lock.
1787
     *
1788
     * @return string
1789
     */
1790
    public function getReadLockSQL()
1791
    {
1792
        return $this->getForUpdateSQL();
1793
    }
1794
 
1795
    /**
1796
     * Returns the SQL snippet to append to any SELECT statement which obtains an exclusive lock on the rows.
1797
     *
1798
     * The semantics of this lock mode should equal the SELECT .. FOR UPDATE of the ANSI SQL standard.
1799
     *
1800
     * @return string
1801
     */
1802
    public function getWriteLockSQL()
1803
    {
1804
        return $this->getForUpdateSQL();
1805
    }
1806
 
1807
    /**
1808
     * Returns the SQL snippet to drop an existing table.
1809
     *
1810
     * @param Table|string $table
1811
     *
1812
     * @return string
1813
     *
1814
     * @throws InvalidArgumentException
1815
     */
1816
    public function getDropTableSQL($table)
1817
    {
1818
        $tableArg = $table;
1819
 
1820
        if ($table instanceof Table) {
1821
            Deprecation::trigger(
1822
                'doctrine/dbal',
1823
                'https://github.com/doctrine/dbal/issues/4798',
1824
                'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.',
1825
                __METHOD__,
1826
            );
1827
 
1828
            $table = $table->getQuotedName($this);
1829
        }
1830
 
1831
        if (! is_string($table)) {
1832
            throw new InvalidArgumentException(
1833
                __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.',
1834
            );
1835
        }
1836
 
1837
        if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaDropTable)) {
1838
            Deprecation::trigger(
1839
                'doctrine/dbal',
1840
                'https://github.com/doctrine/dbal/issues/5784',
1841
                'Subscribing to %s events is deprecated.',
1842
                Events::onSchemaDropTable,
1843
            );
1844
 
1845
            $eventArgs = new SchemaDropTableEventArgs($tableArg, $this);
1846
            $this->_eventManager->dispatchEvent(Events::onSchemaDropTable, $eventArgs);
1847
 
1848
            if ($eventArgs->isDefaultPrevented()) {
1849
                $sql = $eventArgs->getSql();
1850
 
1851
                if ($sql === null) {
1852
                    throw new UnexpectedValueException('Default implementation of DROP TABLE was overridden with NULL');
1853
                }
1854
 
1855
                return $sql;
1856
            }
1857
        }
1858
 
1859
        return 'DROP TABLE ' . $table;
1860
    }
1861
 
1862
    /**
1863
     * Returns the SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction.
1864
     *
1865
     * @param Table|string $table
1866
     *
1867
     * @return string
1868
     */
1869
    public function getDropTemporaryTableSQL($table)
1870
    {
1871
        if ($table instanceof Table) {
1872
            Deprecation::trigger(
1873
                'doctrine/dbal',
1874
                'https://github.com/doctrine/dbal/issues/4798',
1875
                'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.',
1876
                __METHOD__,
1877
            );
1878
 
1879
            $table = $table->getQuotedName($this);
1880
        }
1881
 
1882
        return $this->getDropTableSQL($table);
1883
    }
1884
 
1885
    /**
1886
     * Returns the SQL to drop an index from a table.
1887
     *
1888
     * @param Index|string      $index
1889
     * @param Table|string|null $table
1890
     *
1891
     * @return string
1892
     *
1893
     * @throws InvalidArgumentException
1894
     */
1895
    public function getDropIndexSQL($index, $table = null)
1896
    {
1897
        if ($index instanceof Index) {
1898
            Deprecation::trigger(
1899
                'doctrine/dbal',
1900
                'https://github.com/doctrine/dbal/issues/4798',
1901
                'Passing $index as an Index object to %s is deprecated. Pass it as a quoted name instead.',
1902
                __METHOD__,
1903
            );
1904
 
1905
            $index = $index->getQuotedName($this);
1906
        } elseif (! is_string($index)) {
1907
            throw new InvalidArgumentException(
1908
                __METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.',
1909
            );
1910
        }
1911
 
1912
        return 'DROP INDEX ' . $index;
1913
    }
1914
 
1915
    /**
1916
     * Returns the SQL to drop a constraint.
1917
     *
1918
     * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy.
1919
     *
1920
     * @param Constraint|string $constraint
1921
     * @param Table|string      $table
1922
     *
1923
     * @return string
1924
     */
1925
    public function getDropConstraintSQL($constraint, $table)
1926
    {
1927
        if ($constraint instanceof Constraint) {
1928
            Deprecation::trigger(
1929
                'doctrine/dbal',
1930
                'https://github.com/doctrine/dbal/issues/4798',
1931
                'Passing $constraint as a Constraint object to %s is deprecated. Pass it as a quoted name instead.',
1932
                __METHOD__,
1933
            );
1934
        } else {
1935
            $constraint = new Identifier($constraint);
1936
        }
1937
 
1938
        if ($table instanceof Table) {
1939
            Deprecation::trigger(
1940
                'doctrine/dbal',
1941
                'https://github.com/doctrine/dbal/issues/4798',
1942
                'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.',
1943
                __METHOD__,
1944
            );
1945
        } else {
1946
            $table = new Identifier($table);
1947
        }
1948
 
1949
        $constraint = $constraint->getQuotedName($this);
1950
        $table      = $table->getQuotedName($this);
1951
 
1952
        return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $constraint;
1953
    }
1954
 
1955
    /**
1956
     * Returns the SQL to drop a foreign key.
1957
     *
1958
     * @param ForeignKeyConstraint|string $foreignKey
1959
     * @param Table|string                $table
1960
     *
1961
     * @return string
1962
     */
1963
    public function getDropForeignKeySQL($foreignKey, $table)
1964
    {
1965
        if ($foreignKey instanceof ForeignKeyConstraint) {
1966
            Deprecation::trigger(
1967
                'doctrine/dbal',
1968
                'https://github.com/doctrine/dbal/issues/4798',
1969
                'Passing $foreignKey as a ForeignKeyConstraint object to %s is deprecated.'
1970
                    . ' Pass it as a quoted name instead.',
1971
                __METHOD__,
1972
            );
1973
        } else {
1974
            $foreignKey = new Identifier($foreignKey);
1975
        }
1976
 
1977
        if ($table instanceof Table) {
1978
            Deprecation::trigger(
1979
                'doctrine/dbal',
1980
                'https://github.com/doctrine/dbal/issues/4798',
1981
                'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.',
1982
                __METHOD__,
1983
            );
1984
        } else {
1985
            $table = new Identifier($table);
1986
        }
1987
 
1988
        $foreignKey = $foreignKey->getQuotedName($this);
1989
        $table      = $table->getQuotedName($this);
1990
 
1991
        return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $foreignKey;
1992
    }
1993
 
1994
    /**
1995
     * Returns the SQL to drop a unique constraint.
1996
     */
1997
    public function getDropUniqueConstraintSQL(string $name, string $tableName): string
1998
    {
1999
        return $this->getDropConstraintSQL($name, $tableName);
2000
    }
2001
 
2002
    /**
2003
     * Returns the SQL statement(s) to create a table with the specified name, columns and constraints
2004
     * on this platform.
2005
     *
2006
     * @param int $createFlags
2007
     * @psalm-param int-mask-of<self::CREATE_*> $createFlags
2008
     *
2009
     * @return list<string> The list of SQL statements.
2010
     *
2011
     * @throws Exception
2012
     * @throws InvalidArgumentException
2013
     */
2014
    public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDEXES)
2015
    {
2016
        if (! is_int($createFlags)) {
2017
            throw new InvalidArgumentException(
2018
                'Second argument of AbstractPlatform::getCreateTableSQL() has to be integer.',
2019
            );
2020
        }
2021
 
2022
        if (($createFlags & self::CREATE_INDEXES) === 0) {
2023
            Deprecation::trigger(
2024
                'doctrine/dbal',
2025
                'https://github.com/doctrine/dbal/pull/5416',
2026
                'Unsetting the CREATE_INDEXES flag in AbstractPlatform::getCreateTableSQL() is deprecated.',
2027
            );
2028
        }
2029
 
2030
        if (($createFlags & self::CREATE_FOREIGNKEYS) === 0) {
2031
            Deprecation::trigger(
2032
                'doctrine/dbal',
2033
                'https://github.com/doctrine/dbal/pull/5416',
2034
                'Not setting the CREATE_FOREIGNKEYS flag in AbstractPlatform::getCreateTableSQL()'
2035
                    . ' is deprecated. In order to build the statements that create multiple tables'
2036
                    . ' referencing each other via foreign keys, use AbstractPlatform::getCreateTablesSQL().',
2037
            );
2038
        }
2039
 
2040
        return $this->buildCreateTableSQL(
2041
            $table,
2042
            ($createFlags & self::CREATE_INDEXES) > 0,
2043
            ($createFlags & self::CREATE_FOREIGNKEYS) > 0,
2044
        );
2045
    }
2046
 
2047
    /**
2048
     * @internal
2049
     *
2050
     * @return list<string>
2051
     *
2052
     * @throws Exception
2053
     */
2054
    final protected function getCreateTableWithoutForeignKeysSQL(Table $table): array
2055
    {
2056
        return $this->buildCreateTableSQL($table, true, false);
2057
    }
2058
 
2059
    /**
2060
     * @return list<string>
2061
     *
2062
     * @throws Exception
2063
     */
2064
    private function buildCreateTableSQL(Table $table, bool $createIndexes, bool $createForeignKeys): array
2065
    {
2066
        if (count($table->getColumns()) === 0) {
2067
            throw Exception::noColumnsSpecifiedForTable($table->getName());
2068
        }
2069
 
2070
        $tableName                    = $table->getQuotedName($this);
2071
        $options                      = $table->getOptions();
2072
        $options['uniqueConstraints'] = [];
2073
        $options['indexes']           = [];
2074
        $options['primary']           = [];
2075
 
2076
        if ($createIndexes) {
2077
            foreach ($table->getIndexes() as $index) {
2078
                if (! $index->isPrimary()) {
2079
                    $options['indexes'][$index->getQuotedName($this)] = $index;
2080
 
2081
                    continue;
2082
                }
2083
 
2084
                $options['primary']       = $index->getQuotedColumns($this);
2085
                $options['primary_index'] = $index;
2086
            }
2087
 
2088
            foreach ($table->getUniqueConstraints() as $uniqueConstraint) {
2089
                $options['uniqueConstraints'][$uniqueConstraint->getQuotedName($this)] = $uniqueConstraint;
2090
            }
2091
        }
2092
 
2093
        if ($createForeignKeys) {
2094
            $options['foreignKeys'] = [];
2095
 
2096
            foreach ($table->getForeignKeys() as $fkConstraint) {
2097
                $options['foreignKeys'][] = $fkConstraint;
2098
            }
2099
        }
2100
 
2101
        $columnSql = [];
2102
        $columns   = [];
2103
 
2104
        foreach ($table->getColumns() as $column) {
2105
            if (
2106
                $this->_eventManager !== null
2107
                && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn)
2108
            ) {
2109
                Deprecation::trigger(
2110
                    'doctrine/dbal',
2111
                    'https://github.com/doctrine/dbal/issues/5784',
2112
                    'Subscribing to %s events is deprecated.',
2113
                    Events::onSchemaCreateTableColumn,
2114
                );
2115
 
2116
                $eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this);
2117
 
2118
                $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs);
2119
 
2120
                $columnSql = array_merge($columnSql, $eventArgs->getSql());
2121
 
2122
                if ($eventArgs->isDefaultPrevented()) {
2123
                    continue;
2124
                }
2125
            }
2126
 
2127
            $columnData = $this->columnToArray($column);
2128
 
2129
            if (in_array($column->getName(), $options['primary'], true)) {
2130
                $columnData['primary'] = true;
2131
            }
2132
 
2133
            $columns[$columnData['name']] = $columnData;
2134
        }
2135
 
2136
        if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) {
2137
            Deprecation::trigger(
2138
                'doctrine/dbal',
2139
                'https://github.com/doctrine/dbal/issues/5784',
2140
                'Subscribing to %s events is deprecated.',
2141
                Events::onSchemaCreateTable,
2142
            );
2143
 
2144
            $eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this);
2145
 
2146
            $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs);
2147
 
2148
            if ($eventArgs->isDefaultPrevented()) {
2149
                return array_merge($eventArgs->getSql(), $columnSql);
2150
            }
2151
        }
2152
 
2153
        $sql = $this->_getCreateTableSQL($tableName, $columns, $options);
2154
 
2155
        if ($this->supportsCommentOnStatement()) {
2156
            if ($table->hasOption('comment')) {
2157
                $sql[] = $this->getCommentOnTableSQL($tableName, $table->getOption('comment'));
2158
            }
2159
 
2160
            foreach ($table->getColumns() as $column) {
2161
                $comment = $this->getColumnComment($column);
2162
 
2163
                if ($comment === null || $comment === '') {
2164
                    continue;
2165
                }
2166
 
2167
                $sql[] = $this->getCommentOnColumnSQL($tableName, $column->getQuotedName($this), $comment);
2168
            }
2169
        }
2170
 
2171
        return array_merge($sql, $columnSql);
2172
    }
2173
 
2174
    /**
2175
     * @param list<Table> $tables
2176
     *
2177
     * @return list<string>
2178
     *
2179
     * @throws Exception
2180
     */
2181
    public function getCreateTablesSQL(array $tables): array
2182
    {
2183
        $sql = [];
2184
 
2185
        foreach ($tables as $table) {
2186
            $sql = array_merge($sql, $this->getCreateTableWithoutForeignKeysSQL($table));
2187
        }
2188
 
2189
        foreach ($tables as $table) {
2190
            foreach ($table->getForeignKeys() as $foreignKey) {
2191
                $sql[] = $this->getCreateForeignKeySQL(
2192
                    $foreignKey,
2193
                    $table->getQuotedName($this),
2194
                );
2195
            }
2196
        }
2197
 
2198
        return $sql;
2199
    }
2200
 
2201
    /**
2202
     * @param list<Table> $tables
2203
     *
2204
     * @return list<string>
2205
     */
2206
    public function getDropTablesSQL(array $tables): array
2207
    {
2208
        $sql = [];
2209
 
2210
        foreach ($tables as $table) {
2211
            foreach ($table->getForeignKeys() as $foreignKey) {
2212
                $sql[] = $this->getDropForeignKeySQL(
2213
                    $foreignKey->getQuotedName($this),
2214
                    $table->getQuotedName($this),
2215
                );
2216
            }
2217
        }
2218
 
2219
        foreach ($tables as $table) {
2220
            $sql[] = $this->getDropTableSQL($table->getQuotedName($this));
2221
        }
2222
 
2223
        return $sql;
2224
    }
2225
 
2226
    protected function getCommentOnTableSQL(string $tableName, ?string $comment): string
2227
    {
2228
        $tableName = new Identifier($tableName);
2229
 
2230
        return sprintf(
2231
            'COMMENT ON TABLE %s IS %s',
2232
            $tableName->getQuotedName($this),
2233
            $this->quoteStringLiteral((string) $comment),
2234
        );
2235
    }
2236
 
2237
    /**
2238
     * @param string      $tableName
2239
     * @param string      $columnName
2240
     * @param string|null $comment
2241
     *
2242
     * @return string
2243
     */
2244
    public function getCommentOnColumnSQL($tableName, $columnName, $comment)
2245
    {
2246
        $tableName  = new Identifier($tableName);
2247
        $columnName = new Identifier($columnName);
2248
 
2249
        return sprintf(
2250
            'COMMENT ON COLUMN %s.%s IS %s',
2251
            $tableName->getQuotedName($this),
2252
            $columnName->getQuotedName($this),
2253
            $this->quoteStringLiteral((string) $comment),
2254
        );
2255
    }
2256
 
2257
    /**
2258
     * Returns the SQL to create inline comment on a column.
2259
     *
2260
     * @param string $comment
2261
     *
2262
     * @return string
2263
     *
2264
     * @throws Exception If not supported on this platform.
2265
     */
2266
    public function getInlineColumnCommentSQL($comment)
2267
    {
2268
        if (! $this->supportsInlineColumnComments()) {
2269
            throw Exception::notSupported(__METHOD__);
2270
        }
2271
 
2272
        return 'COMMENT ' . $this->quoteStringLiteral($comment);
2273
    }
2274
 
2275
    /**
2276
     * Returns the SQL used to create a table.
2277
     *
2278
     * @param string    $name
2279
     * @param mixed[][] $columns
2280
     * @param mixed[]   $options
2281
     *
2282
     * @return string[]
2283
     */
2284
    protected function _getCreateTableSQL($name, array $columns, array $options = [])
2285
    {
2286
        $columnListSql = $this->getColumnDeclarationListSQL($columns);
2287
 
2288
        if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
2289
            foreach ($options['uniqueConstraints'] as $index => $definition) {
2290
                $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($index, $definition);
2291
            }
2292
        }
2293
 
2294
        if (isset($options['primary']) && ! empty($options['primary'])) {
2295
            $columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')';
2296
        }
2297
 
2298
        if (isset($options['indexes']) && ! empty($options['indexes'])) {
2299
            foreach ($options['indexes'] as $index => $definition) {
2300
                $columnListSql .= ', ' . $this->getIndexDeclarationSQL($index, $definition);
2301
            }
2302
        }
2303
 
2304
        $query = 'CREATE TABLE ' . $name . ' (' . $columnListSql;
2305
        $check = $this->getCheckDeclarationSQL($columns);
2306
 
2307
        if (! empty($check)) {
2308
            $query .= ', ' . $check;
2309
        }
2310
 
2311
        $query .= ')';
2312
 
2313
        $sql = [$query];
2314
 
2315
        if (isset($options['foreignKeys'])) {
2316
            foreach ($options['foreignKeys'] as $definition) {
2317
                $sql[] = $this->getCreateForeignKeySQL($definition, $name);
2318
            }
2319
        }
2320
 
2321
        return $sql;
2322
    }
2323
 
2324
    /** @return string */
2325
    public function getCreateTemporaryTableSnippetSQL()
2326
    {
2327
        return 'CREATE TEMPORARY TABLE';
2328
    }
2329
 
2330
    /**
2331
     * Generates SQL statements that can be used to apply the diff.
2332
     *
2333
     * @return list<string>
2334
     */
2335
    public function getAlterSchemaSQL(SchemaDiff $diff): array
2336
    {
2337
        return $diff->toSql($this);
2338
    }
2339
 
2340
    /**
2341
     * Returns the SQL to create a sequence on this platform.
2342
     *
2343
     * @return string
2344
     *
2345
     * @throws Exception If not supported on this platform.
2346
     */
2347
    public function getCreateSequenceSQL(Sequence $sequence)
2348
    {
2349
        throw Exception::notSupported(__METHOD__);
2350
    }
2351
 
2352
    /**
2353
     * Returns the SQL to change a sequence on this platform.
2354
     *
2355
     * @return string
2356
     *
2357
     * @throws Exception If not supported on this platform.
2358
     */
2359
    public function getAlterSequenceSQL(Sequence $sequence)
2360
    {
2361
        throw Exception::notSupported(__METHOD__);
2362
    }
2363
 
2364
    /**
2365
     * Returns the SQL snippet to drop an existing sequence.
2366
     *
2367
     * @param Sequence|string $sequence
2368
     *
2369
     * @return string
2370
     *
2371
     * @throws Exception If not supported on this platform.
2372
     */
2373
    public function getDropSequenceSQL($sequence)
2374
    {
2375
        if (! $this->supportsSequences()) {
2376
            throw Exception::notSupported(__METHOD__);
2377
        }
2378
 
2379
        if ($sequence instanceof Sequence) {
2380
            Deprecation::trigger(
2381
                'doctrine/dbal',
2382
                'https://github.com/doctrine/dbal/issues/4798',
2383
                'Passing $sequence as a Sequence object to %s is deprecated. Pass it as a quoted name instead.',
2384
                __METHOD__,
2385
            );
2386
 
2387
            $sequence = $sequence->getQuotedName($this);
2388
        }
2389
 
2390
        return 'DROP SEQUENCE ' . $sequence;
2391
    }
2392
 
2393
    /**
2394
     * Returns the SQL to create a constraint on a table on this platform.
2395
     *
2396
     * @deprecated Use {@see getCreateIndexSQL()}, {@see getCreateForeignKeySQL()}
2397
     *             or {@see getCreateUniqueConstraintSQL()} instead.
2398
     *
2399
     * @param Table|string $table
2400
     *
2401
     * @return string
2402
     *
2403
     * @throws InvalidArgumentException
2404
     */
2405
    public function getCreateConstraintSQL(Constraint $constraint, $table)
2406
    {
2407
        if ($table instanceof Table) {
2408
            Deprecation::trigger(
2409
                'doctrine/dbal',
2410
                'https://github.com/doctrine/dbal/issues/4798',
2411
                'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.',
2412
                __METHOD__,
2413
            );
2414
 
2415
            $table = $table->getQuotedName($this);
2416
        }
2417
 
2418
        $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this);
2419
 
2420
        $columnList = '(' . implode(', ', $constraint->getQuotedColumns($this)) . ')';
2421
 
2422
        $referencesClause = '';
2423
        if ($constraint instanceof Index) {
2424
            if ($constraint->isPrimary()) {
2425
                $query .= ' PRIMARY KEY';
2426
            } elseif ($constraint->isUnique()) {
2427
                $query .= ' UNIQUE';
2428
            } else {
2429
                throw new InvalidArgumentException(
2430
                    'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().',
2431
                );
2432
            }
2433
        } elseif ($constraint instanceof UniqueConstraint) {
2434
            $query .= ' UNIQUE';
2435
        } elseif ($constraint instanceof ForeignKeyConstraint) {
2436
            $query .= ' FOREIGN KEY';
2437
 
2438
            $referencesClause = ' REFERENCES ' . $constraint->getQuotedForeignTableName($this) .
2439
                ' (' . implode(', ', $constraint->getQuotedForeignColumns($this)) . ')';
2440
        }
2441
 
2442
        $query .= ' ' . $columnList . $referencesClause;
2443
 
2444
        return $query;
2445
    }
2446
 
2447
    /**
2448
     * Returns the SQL to create an index on a table on this platform.
2449
     *
2450
     * @param Table|string $table The name of the table on which the index is to be created.
2451
     *
2452
     * @return string
2453
     *
2454
     * @throws InvalidArgumentException
2455
     */
2456
    public function getCreateIndexSQL(Index $index, $table)
2457
    {
2458
        if ($table instanceof Table) {
2459
            Deprecation::trigger(
2460
                'doctrine/dbal',
2461
                'https://github.com/doctrine/dbal/issues/4798',
2462
                'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.',
2463
                __METHOD__,
2464
            );
2465
 
2466
            $table = $table->getQuotedName($this);
2467
        }
2468
 
2469
        $name    = $index->getQuotedName($this);
2470
        $columns = $index->getColumns();
2471
 
2472
        if (count($columns) === 0) {
2473
            throw new InvalidArgumentException(sprintf(
2474
                'Incomplete or invalid index definition %s on table %s',
2475
                $name,
2476
                $table,
2477
            ));
2478
        }
2479
 
2480
        if ($index->isPrimary()) {
2481
            return $this->getCreatePrimaryKeySQL($index, $table);
2482
        }
2483
 
2484
        $query  = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table;
2485
        $query .= ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')' . $this->getPartialIndexSQL($index);
2486
 
2487
        return $query;
2488
    }
2489
 
2490
    /**
2491
     * Adds condition for partial index.
2492
     *
2493
     * @return string
2494
     */
2495
    protected function getPartialIndexSQL(Index $index)
2496
    {
2497
        if ($this->supportsPartialIndexes() && $index->hasOption('where')) {
2498
            return ' WHERE ' . $index->getOption('where');
2499
        }
2500
 
2501
        return '';
2502
    }
2503
 
2504
    /**
2505
     * Adds additional flags for index generation.
2506
     *
2507
     * @return string
2508
     */
2509
    protected function getCreateIndexSQLFlags(Index $index)
2510
    {
2511
        return $index->isUnique() ? 'UNIQUE ' : '';
2512
    }
2513
 
2514
    /**
2515
     * Returns the SQL to create an unnamed primary key constraint.
2516
     *
2517
     * @param Table|string $table
2518
     *
2519
     * @return string
2520
     */
2521
    public function getCreatePrimaryKeySQL(Index $index, $table)
2522
    {
2523
        if ($table instanceof Table) {
2524
            Deprecation::trigger(
2525
                'doctrine/dbal',
2526
                'https://github.com/doctrine/dbal/issues/4798',
2527
                'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.',
2528
                __METHOD__,
2529
            );
2530
 
2531
            $table = $table->getQuotedName($this);
2532
        }
2533
 
2534
        return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index) . ')';
2535
    }
2536
 
2537
    /**
2538
     * Returns the SQL to create a named schema.
2539
     *
2540
     * @param string $schemaName
2541
     *
2542
     * @return string
2543
     *
2544
     * @throws Exception If not supported on this platform.
2545
     */
2546
    public function getCreateSchemaSQL($schemaName)
2547
    {
2548
        if (! $this->supportsSchemas()) {
2549
            throw Exception::notSupported(__METHOD__);
2550
        }
2551
 
2552
        return 'CREATE SCHEMA ' . $schemaName;
2553
    }
2554
 
2555
    /**
2556
     * Returns the SQL to create a unique constraint on a table on this platform.
2557
     */
2558
    public function getCreateUniqueConstraintSQL(UniqueConstraint $constraint, string $tableName): string
2559
    {
2560
        return $this->getCreateConstraintSQL($constraint, $tableName);
2561
    }
2562
 
2563
    /**
2564
     * Returns the SQL snippet to drop a schema.
2565
     *
2566
     * @throws Exception If not supported on this platform.
2567
     */
2568
    public function getDropSchemaSQL(string $schemaName): string
2569
    {
2570
        if (! $this->supportsSchemas()) {
2571
            throw Exception::notSupported(__METHOD__);
2572
        }
2573
 
2574
        return 'DROP SCHEMA ' . $schemaName;
2575
    }
2576
 
2577
    /**
2578
     * Quotes a string so that it can be safely used as a table or column name,
2579
     * even if it is a reserved word of the platform. This also detects identifier
2580
     * chains separated by dot and quotes them independently.
2581
     *
2582
     * NOTE: Just because you CAN use quoted identifiers doesn't mean
2583
     * you SHOULD use them. In general, they end up causing way more
2584
     * problems than they solve.
2585
     *
2586
     * @param string $str The identifier name to be quoted.
2587
     *
2588
     * @return string The quoted identifier string.
2589
     */
2590
    public function quoteIdentifier($str)
2591
    {
2592
        if (strpos($str, '.') !== false) {
2593
            $parts = array_map([$this, 'quoteSingleIdentifier'], explode('.', $str));
2594
 
2595
            return implode('.', $parts);
2596
        }
2597
 
2598
        return $this->quoteSingleIdentifier($str);
2599
    }
2600
 
2601
    /**
2602
     * Quotes a single identifier (no dot chain separation).
2603
     *
2604
     * @param string $str The identifier name to be quoted.
2605
     *
2606
     * @return string The quoted identifier string.
2607
     */
2608
    public function quoteSingleIdentifier($str)
2609
    {
2610
        $c = $this->getIdentifierQuoteCharacter();
2611
 
2612
        return $c . str_replace($c, $c . $c, $str) . $c;
2613
    }
2614
 
2615
    /**
2616
     * Returns the SQL to create a new foreign key.
2617
     *
2618
     * @param ForeignKeyConstraint $foreignKey The foreign key constraint.
2619
     * @param Table|string         $table      The name of the table on which the foreign key is to be created.
2620
     *
2621
     * @return string
2622
     */
2623
    public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table)
2624
    {
2625
        if ($table instanceof Table) {
2626
            Deprecation::trigger(
2627
                'doctrine/dbal',
2628
                'https://github.com/doctrine/dbal/issues/4798',
2629
                'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.',
2630
                __METHOD__,
2631
            );
2632
 
2633
            $table = $table->getQuotedName($this);
2634
        }
2635
 
2636
        return 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey);
2637
    }
2638
 
2639
    /**
2640
     * Gets the SQL statements for altering an existing table.
2641
     *
2642
     * This method returns an array of SQL statements, since some platforms need several statements.
2643
     *
2644
     * @return list<string>
2645
     *
2646
     * @throws Exception If not supported on this platform.
2647
     */
2648
    public function getAlterTableSQL(TableDiff $diff)
2649
    {
2650
        throw Exception::notSupported(__METHOD__);
2651
    }
2652
 
2653
    /** @return list<string> */
2654
    public function getRenameTableSQL(string $oldName, string $newName): array
2655
    {
2656
        return [
2657
            sprintf('ALTER TABLE %s RENAME TO %s', $oldName, $newName),
2658
        ];
2659
    }
2660
 
2661
    /**
2662
     * @param mixed[] $columnSql
2663
     *
2664
     * @return bool
2665
     */
2666
    protected function onSchemaAlterTableAddColumn(Column $column, TableDiff $diff, &$columnSql)
2667
    {
2668
        if ($this->_eventManager === null) {
2669
            return false;
2670
        }
2671
 
2672
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableAddColumn)) {
2673
            return false;
2674
        }
2675
 
2676
        Deprecation::trigger(
2677
            'doctrine/dbal',
2678
            'https://github.com/doctrine/dbal/issues/5784',
2679
            'Subscribing to %s events is deprecated.',
2680
            Events::onSchemaAlterTableAddColumn,
2681
        );
2682
 
2683
        $eventArgs = new SchemaAlterTableAddColumnEventArgs($column, $diff, $this);
2684
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn, $eventArgs);
2685
 
2686
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
2687
 
2688
        return $eventArgs->isDefaultPrevented();
2689
    }
2690
 
2691
    /**
2692
     * @param string[] $columnSql
2693
     *
2694
     * @return bool
2695
     */
2696
    protected function onSchemaAlterTableRemoveColumn(Column $column, TableDiff $diff, &$columnSql)
2697
    {
2698
        if ($this->_eventManager === null) {
2699
            return false;
2700
        }
2701
 
2702
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRemoveColumn)) {
2703
            return false;
2704
        }
2705
 
2706
        Deprecation::trigger(
2707
            'doctrine/dbal',
2708
            'https://github.com/doctrine/dbal/issues/5784',
2709
            'Subscribing to %s events is deprecated.',
2710
            Events::onSchemaAlterTableRemoveColumn,
2711
        );
2712
 
2713
        $eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column, $diff, $this);
2714
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn, $eventArgs);
2715
 
2716
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
2717
 
2718
        return $eventArgs->isDefaultPrevented();
2719
    }
2720
 
2721
    /**
2722
     * @param string[] $columnSql
2723
     *
2724
     * @return bool
2725
     */
2726
    protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiff, TableDiff $diff, &$columnSql)
2727
    {
2728
        if ($this->_eventManager === null) {
2729
            return false;
2730
        }
2731
 
2732
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) {
2733
            return false;
2734
        }
2735
 
2736
        Deprecation::trigger(
2737
            'doctrine/dbal',
2738
            'https://github.com/doctrine/dbal/issues/5784',
2739
            'Subscribing to %s events is deprecated.',
2740
            Events::onSchemaAlterTableChangeColumn,
2741
        );
2742
 
2743
        $eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff, $diff, $this);
2744
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn, $eventArgs);
2745
 
2746
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
2747
 
2748
        return $eventArgs->isDefaultPrevented();
2749
    }
2750
 
2751
    /**
2752
     * @param string   $oldColumnName
2753
     * @param string[] $columnSql
2754
     *
2755
     * @return bool
2756
     */
2757
    protected function onSchemaAlterTableRenameColumn($oldColumnName, Column $column, TableDiff $diff, &$columnSql)
2758
    {
2759
        if ($this->_eventManager === null) {
2760
            return false;
2761
        }
2762
 
2763
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRenameColumn)) {
2764
            return false;
2765
        }
2766
 
2767
        Deprecation::trigger(
2768
            'doctrine/dbal',
2769
            'https://github.com/doctrine/dbal/issues/5784',
2770
            'Subscribing to %s events is deprecated.',
2771
            Events::onSchemaAlterTableRenameColumn,
2772
        );
2773
 
2774
        $eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName, $column, $diff, $this);
2775
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn, $eventArgs);
2776
 
2777
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
2778
 
2779
        return $eventArgs->isDefaultPrevented();
2780
    }
2781
 
2782
    /**
2783
     * @param string[] $sql
2784
     *
2785
     * @return bool
2786
     */
2787
    protected function onSchemaAlterTable(TableDiff $diff, &$sql)
2788
    {
2789
        if ($this->_eventManager === null) {
2790
            return false;
2791
        }
2792
 
2793
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTable)) {
2794
            return false;
2795
        }
2796
 
2797
        Deprecation::trigger(
2798
            'doctrine/dbal',
2799
            'https://github.com/doctrine/dbal/issues/5784',
2800
            'Subscribing to %s events is deprecated.',
2801
            Events::onSchemaAlterTable,
2802
        );
2803
 
2804
        $eventArgs = new SchemaAlterTableEventArgs($diff, $this);
2805
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTable, $eventArgs);
2806
 
2807
        $sql = array_merge($sql, $eventArgs->getSql());
2808
 
2809
        return $eventArgs->isDefaultPrevented();
2810
    }
2811
 
2812
    /** @return string[] */
2813
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
2814
    {
2815
        $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this);
2816
 
2817
        $sql = [];
2818
        if ($this->supportsForeignKeyConstraints()) {
2819
            foreach ($diff->getDroppedForeignKeys() as $foreignKey) {
2820
                if ($foreignKey instanceof ForeignKeyConstraint) {
2821
                    $foreignKey = $foreignKey->getQuotedName($this);
2822
                }
2823
 
2824
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableNameSQL);
2825
            }
2826
 
2827
            foreach ($diff->getModifiedForeignKeys() as $foreignKey) {
2828
                $sql[] = $this->getDropForeignKeySQL($foreignKey->getQuotedName($this), $tableNameSQL);
2829
            }
2830
        }
2831
 
2832
        foreach ($diff->getDroppedIndexes() as $index) {
2833
            $sql[] = $this->getDropIndexSQL($index->getQuotedName($this), $tableNameSQL);
2834
        }
2835
 
2836
        foreach ($diff->getModifiedIndexes() as $index) {
2837
            $sql[] = $this->getDropIndexSQL($index->getQuotedName($this), $tableNameSQL);
2838
        }
2839
 
2840
        return $sql;
2841
    }
2842
 
2843
    /** @return string[] */
2844
    protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff)
2845
    {
2846
        $sql     = [];
2847
        $newName = $diff->getNewName();
2848
 
2849
        if ($newName !== false) {
2850
            $tableNameSQL = $newName->getQuotedName($this);
2851
        } else {
2852
            $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this);
2853
        }
2854
 
2855
        if ($this->supportsForeignKeyConstraints()) {
2856
            foreach ($diff->getAddedForeignKeys() as $foreignKey) {
2857
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableNameSQL);
2858
            }
2859
 
2860
            foreach ($diff->getModifiedForeignKeys() as $foreignKey) {
2861
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableNameSQL);
2862
            }
2863
        }
2864
 
2865
        foreach ($diff->getAddedIndexes() as $index) {
2866
            $sql[] = $this->getCreateIndexSQL($index, $tableNameSQL);
2867
        }
2868
 
2869
        foreach ($diff->getModifiedIndexes() as $index) {
2870
            $sql[] = $this->getCreateIndexSQL($index, $tableNameSQL);
2871
        }
2872
 
2873
        foreach ($diff->getRenamedIndexes() as $oldIndexName => $index) {
2874
            $oldIndexName = new Identifier($oldIndexName);
2875
            $sql          = array_merge(
2876
                $sql,
2877
                $this->getRenameIndexSQL($oldIndexName->getQuotedName($this), $index, $tableNameSQL),
2878
            );
2879
        }
2880
 
2881
        return $sql;
2882
    }
2883
 
2884
    /**
2885
     * Returns the SQL for renaming an index on a table.
2886
     *
2887
     * @param string $oldIndexName The name of the index to rename from.
2888
     * @param Index  $index        The definition of the index to rename to.
2889
     * @param string $tableName    The table to rename the given index on.
2890
     *
2891
     * @return string[] The sequence of SQL statements for renaming the given index.
2892
     */
2893
    protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
2894
    {
2895
        return [
2896
            $this->getDropIndexSQL($oldIndexName, $tableName),
2897
            $this->getCreateIndexSQL($index, $tableName),
2898
        ];
2899
    }
2900
 
2901
    /**
2902
     * Gets declaration of a number of columns in bulk.
2903
     *
2904
     * @param mixed[][] $columns A multidimensional associative array.
2905
     *                           The first dimension determines the column name, while the second
2906
     *                           dimension is keyed with the name of the properties
2907
     *                           of the column being declared as array indexes. Currently, the types
2908
     *                           of supported column properties are as follows:
2909
     *
2910
     *      length
2911
     *          Integer value that determines the maximum length of the text
2912
     *          column. If this argument is missing the column should be
2913
     *          declared to have the longest length allowed by the DBMS.
2914
     *
2915
     *      default
2916
     *          Text value to be used as default for this column.
2917
     *
2918
     *      notnull
2919
     *          Boolean flag that indicates whether this column is constrained
2920
     *          to not be set to null.
2921
     *      charset
2922
     *          Text value with the default CHARACTER SET for this column.
2923
     *      collation
2924
     *          Text value with the default COLLATION for this column.
2925
     *      unique
2926
     *          unique constraint
2927
     *
2928
     * @return string
2929
     */
2930
    public function getColumnDeclarationListSQL(array $columns)
2931
    {
2932
        $declarations = [];
2933
 
2934
        foreach ($columns as $name => $column) {
2935
            $declarations[] = $this->getColumnDeclarationSQL($name, $column);
2936
        }
2937
 
2938
        return implode(', ', $declarations);
2939
    }
2940
 
2941
    /**
2942
     * Obtains DBMS specific SQL code portion needed to declare a generic type
2943
     * column to be used in statements like CREATE TABLE.
2944
     *
2945
     * @param string  $name   The name the column to be declared.
2946
     * @param mixed[] $column An associative array with the name of the properties
2947
     *                        of the column being declared as array indexes. Currently, the types
2948
     *                        of supported column properties are as follows:
2949
     *
2950
     *      length
2951
     *          Integer value that determines the maximum length of the text
2952
     *          column. If this argument is missing the column should be
2953
     *          declared to have the longest length allowed by the DBMS.
2954
     *
2955
     *      default
2956
     *          Text value to be used as default for this column.
2957
     *
2958
     *      notnull
2959
     *          Boolean flag that indicates whether this column is constrained
2960
     *          to not be set to null.
2961
     *      charset
2962
     *          Text value with the default CHARACTER SET for this column.
2963
     *      collation
2964
     *          Text value with the default COLLATION for this column.
2965
     *      unique
2966
     *          unique constraint
2967
     *      check
2968
     *          column check constraint
2969
     *      columnDefinition
2970
     *          a string that defines the complete column
2971
     *
2972
     * @return string DBMS specific SQL code portion that should be used to declare the column.
2973
     *
2974
     * @throws Exception
2975
     */
2976
    public function getColumnDeclarationSQL($name, array $column)
2977
    {
2978
        if (isset($column['columnDefinition'])) {
2979
            $declaration = $this->getCustomTypeDeclarationSQL($column);
2980
        } else {
2981
            $default = $this->getDefaultValueDeclarationSQL($column);
2982
 
2983
            $charset = ! empty($column['charset']) ?
2984
                ' ' . $this->getColumnCharsetDeclarationSQL($column['charset']) : '';
2985
 
2986
            $collation = ! empty($column['collation']) ?
2987
                ' ' . $this->getColumnCollationDeclarationSQL($column['collation']) : '';
2988
 
2989
            $notnull = ! empty($column['notnull']) ? ' NOT NULL' : '';
2990
 
2991
            if (! empty($column['unique'])) {
2992
                Deprecation::trigger(
2993
                    'doctrine/dbal',
2994
                    'https://github.com/doctrine/dbal/pull/5656',
2995
                    'The usage of the "unique" column property is deprecated. Use unique constraints instead.',
2996
                );
2997
 
2998
                $unique = ' ' . $this->getUniqueFieldDeclarationSQL();
2999
            } else {
3000
                $unique = '';
3001
            }
3002
 
3003
            if (! empty($column['check'])) {
3004
                Deprecation::trigger(
3005
                    'doctrine/dbal',
3006
                    'https://github.com/doctrine/dbal/pull/5656',
3007
                    'The usage of the "check" column property is deprecated.',
3008
                );
3009
 
3010
                $check = ' ' . $column['check'];
3011
            } else {
3012
                $check = '';
3013
            }
3014
 
3015
            $typeDecl    = $column['type']->getSQLDeclaration($column, $this);
3016
            $declaration = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation;
3017
 
3018
            if ($this->supportsInlineColumnComments() && isset($column['comment']) && $column['comment'] !== '') {
3019
                $declaration .= ' ' . $this->getInlineColumnCommentSQL($column['comment']);
3020
            }
3021
        }
3022
 
3023
        return $name . ' ' . $declaration;
3024
    }
3025
 
3026
    /**
3027
     * Returns the SQL snippet that declares a floating point column of arbitrary precision.
3028
     *
3029
     * @param mixed[] $column
3030
     *
3031
     * @return string
3032
     */
3033
    public function getDecimalTypeDeclarationSQL(array $column)
3034
    {
3035
        if (empty($column['precision'])) {
3036
            if (! isset($column['precision'])) {
3037
                Deprecation::trigger(
3038
                    'doctrine/dbal',
3039
                    'https://github.com/doctrine/dbal/pull/5637',
3040
                    'Relying on the default decimal column precision is deprecated'
3041
                        . ', specify the precision explicitly.',
3042
                );
3043
            }
3044
 
3045
            $precision = 10;
3046
        } else {
3047
            $precision = $column['precision'];
3048
        }
3049
 
3050
        if (empty($column['scale'])) {
3051
            if (! isset($column['scale'])) {
3052
                Deprecation::trigger(
3053
                    'doctrine/dbal',
3054
                    'https://github.com/doctrine/dbal/pull/5637',
3055
                    'Relying on the default decimal column scale is deprecated'
3056
                        . ', specify the scale explicitly.',
3057
                );
3058
            }
3059
 
3060
            $scale = 0;
3061
        } else {
3062
            $scale = $column['scale'];
3063
        }
3064
 
3065
        return 'NUMERIC(' . $precision . ', ' . $scale . ')';
3066
    }
3067
 
3068
    /**
3069
     * Obtains DBMS specific SQL code portion needed to set a default value
3070
     * declaration to be used in statements like CREATE TABLE.
3071
     *
3072
     * @param mixed[] $column The column definition array.
3073
     *
3074
     * @return string DBMS specific SQL code portion needed to set a default value.
3075
     */
3076
    public function getDefaultValueDeclarationSQL($column)
3077
    {
3078
        if (! isset($column['default'])) {
3079
            return empty($column['notnull']) ? ' DEFAULT NULL' : '';
3080
        }
3081
 
3082
        $default = $column['default'];
3083
 
3084
        if (! isset($column['type'])) {
3085
            return " DEFAULT '" . $default . "'";
3086
        }
3087
 
3088
        $type = $column['type'];
3089
 
3090
        if ($type instanceof Types\PhpIntegerMappingType) {
3091
            return ' DEFAULT ' . $default;
3092
        }
3093
 
3094
        if ($type instanceof Types\PhpDateTimeMappingType && $default === $this->getCurrentTimestampSQL()) {
3095
            return ' DEFAULT ' . $this->getCurrentTimestampSQL();
3096
        }
3097
 
3098
        if ($type instanceof Types\TimeType && $default === $this->getCurrentTimeSQL()) {
3099
            return ' DEFAULT ' . $this->getCurrentTimeSQL();
3100
        }
3101
 
3102
        if ($type instanceof Types\DateType && $default === $this->getCurrentDateSQL()) {
3103
            return ' DEFAULT ' . $this->getCurrentDateSQL();
3104
        }
3105
 
3106
        if ($type instanceof Types\BooleanType) {
3107
            return ' DEFAULT ' . $this->convertBooleans($default);
3108
        }
3109
 
3110
        return ' DEFAULT ' . $this->quoteStringLiteral($default);
3111
    }
3112
 
3113
    /**
3114
     * Obtains DBMS specific SQL code portion needed to set a CHECK constraint
3115
     * declaration to be used in statements like CREATE TABLE.
3116
     *
3117
     * @param string[]|mixed[][] $definition The check definition.
3118
     *
3119
     * @return string DBMS specific SQL code portion needed to set a CHECK constraint.
3120
     */
3121
    public function getCheckDeclarationSQL(array $definition)
3122
    {
3123
        $constraints = [];
3124
        foreach ($definition as $column => $def) {
3125
            if (is_string($def)) {
3126
                $constraints[] = 'CHECK (' . $def . ')';
3127
            } else {
3128
                if (isset($def['min'])) {
3129
                    $constraints[] = 'CHECK (' . $column . ' >= ' . $def['min'] . ')';
3130
                }
3131
 
3132
                if (isset($def['max'])) {
3133
                    $constraints[] = 'CHECK (' . $column . ' <= ' . $def['max'] . ')';
3134
                }
3135
            }
3136
        }
3137
 
3138
        return implode(', ', $constraints);
3139
    }
3140
 
3141
    /**
3142
     * Obtains DBMS specific SQL code portion needed to set a unique
3143
     * constraint declaration to be used in statements like CREATE TABLE.
3144
     *
3145
     * @param string           $name       The name of the unique constraint.
3146
     * @param UniqueConstraint $constraint The unique constraint definition.
3147
     *
3148
     * @return string DBMS specific SQL code portion needed to set a constraint.
3149
     *
3150
     * @throws InvalidArgumentException
3151
     */
3152
    public function getUniqueConstraintDeclarationSQL($name, UniqueConstraint $constraint)
3153
    {
3154
        $columns = $constraint->getQuotedColumns($this);
3155
        $name    = new Identifier($name);
3156
 
3157
        if (count($columns) === 0) {
3158
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
3159
        }
3160
 
3161
        $constraintFlags = array_merge(['UNIQUE'], array_map('strtoupper', $constraint->getFlags()));
3162
        $constraintName  = $name->getQuotedName($this);
3163
        $columnListNames = $this->getColumnsFieldDeclarationListSQL($columns);
3164
 
3165
        return sprintf('CONSTRAINT %s %s (%s)', $constraintName, implode(' ', $constraintFlags), $columnListNames);
3166
    }
3167
 
3168
    /**
3169
     * Obtains DBMS specific SQL code portion needed to set an index
3170
     * declaration to be used in statements like CREATE TABLE.
3171
     *
3172
     * @param string $name  The name of the index.
3173
     * @param Index  $index The index definition.
3174
     *
3175
     * @return string DBMS specific SQL code portion needed to set an index.
3176
     *
3177
     * @throws InvalidArgumentException
3178
     */
3179
    public function getIndexDeclarationSQL($name, Index $index)
3180
    {
3181
        $columns = $index->getColumns();
3182
        $name    = new Identifier($name);
3183
 
3184
        if (count($columns) === 0) {
3185
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
3186
        }
3187
 
3188
        return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name->getQuotedName($this)
3189
            . ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')' . $this->getPartialIndexSQL($index);
3190
    }
3191
 
3192
    /**
3193
     * Obtains SQL code portion needed to create a custom column,
3194
     * e.g. when a column has the "columnDefinition" keyword.
3195
     * Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate.
3196
     *
3197
     * @deprecated
3198
     *
3199
     * @param mixed[] $column
3200
     *
3201
     * @return string
3202
     */
3203
    public function getCustomTypeDeclarationSQL(array $column)
3204
    {
3205
        Deprecation::triggerIfCalledFromOutside(
3206
            'doctrine/dbal',
3207
            'https://github.com/doctrine/dbal/pull/5527',
3208
            '%s is deprecated.',
3209
            __METHOD__,
3210
        );
3211
 
3212
        return $column['columnDefinition'];
3213
    }
3214
 
3215
    /**
3216
     * Obtains DBMS specific SQL code portion needed to set an index
3217
     * declaration to be used in statements like CREATE TABLE.
3218
     *
3219
     * @deprecated
3220
     */
3221
    public function getIndexFieldDeclarationListSQL(Index $index): string
3222
    {
3223
        Deprecation::triggerIfCalledFromOutside(
3224
            'doctrine/dbal',
3225
            'https://github.com/doctrine/dbal/pull/5527',
3226
            '%s is deprecated.',
3227
            __METHOD__,
3228
        );
3229
 
3230
        return implode(', ', $index->getQuotedColumns($this));
3231
    }
3232
 
3233
    /**
3234
     * Obtains DBMS specific SQL code portion needed to set an index
3235
     * declaration to be used in statements like CREATE TABLE.
3236
     *
3237
     * @deprecated
3238
     *
3239
     * @param mixed[] $columns
3240
     */
3241
    public function getColumnsFieldDeclarationListSQL(array $columns): string
3242
    {
3243
        Deprecation::triggerIfCalledFromOutside(
3244
            'doctrine/dbal',
3245
            'https://github.com/doctrine/dbal/pull/5527',
3246
            '%s is deprecated.',
3247
            __METHOD__,
3248
        );
3249
 
3250
        $ret = [];
3251
 
3252
        foreach ($columns as $column => $definition) {
3253
            if (is_array($definition)) {
3254
                $ret[] = $column;
3255
            } else {
3256
                $ret[] = $definition;
3257
            }
3258
        }
3259
 
3260
        return implode(', ', $ret);
3261
    }
3262
 
3263
    /**
3264
     * Returns the required SQL string that fits between CREATE ... TABLE
3265
     * to create the table as a temporary table.
3266
     *
3267
     * Should be overridden in driver classes to return the correct string for the
3268
     * specific database type.
3269
     *
3270
     * The default is to return the string "TEMPORARY" - this will result in a
3271
     * SQL error for any database that does not support temporary tables, or that
3272
     * requires a different SQL command from "CREATE TEMPORARY TABLE".
3273
     *
3274
     * @deprecated
3275
     *
3276
     * @return string The string required to be placed between "CREATE" and "TABLE"
3277
     *                to generate a temporary table, if possible.
3278
     */
3279
    public function getTemporaryTableSQL()
3280
    {
3281
        Deprecation::trigger(
3282
            'doctrine/dbal',
3283
            'https://github.com/doctrine/dbal/pull/4724',
3284
            'AbstractPlatform::getTemporaryTableSQL() is deprecated.',
3285
        );
3286
 
3287
        return 'TEMPORARY';
3288
    }
3289
 
3290
    /**
3291
     * Some vendors require temporary table names to be qualified specially.
3292
     *
3293
     * @param string $tableName
3294
     *
3295
     * @return string
3296
     */
3297
    public function getTemporaryTableName($tableName)
3298
    {
3299
        return $tableName;
3300
    }
3301
 
3302
    /**
3303
     * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
3304
     * of a column declaration to be used in statements like CREATE TABLE.
3305
     *
3306
     * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
3307
     *                of a column declaration.
3308
     */
3309
    public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey)
3310
    {
3311
        $sql  = $this->getForeignKeyBaseDeclarationSQL($foreignKey);
3312
        $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey);
3313
 
3314
        return $sql;
3315
    }
3316
 
3317
    /**
3318
     * Returns the FOREIGN KEY query section dealing with non-standard options
3319
     * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
3320
     *
3321
     * @param ForeignKeyConstraint $foreignKey The foreign key definition.
3322
     *
3323
     * @return string
3324
     */
3325
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
3326
    {
3327
        $query = '';
3328
        if ($foreignKey->hasOption('onUpdate')) {
3329
            $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate'));
3330
        }
3331
 
3332
        if ($foreignKey->hasOption('onDelete')) {
3333
            $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
3334
        }
3335
 
3336
        return $query;
3337
    }
3338
 
3339
    /**
3340
     * Returns the given referential action in uppercase if valid, otherwise throws an exception.
3341
     *
3342
     * @param string $action The foreign key referential action.
3343
     *
3344
     * @return string
3345
     *
3346
     * @throws InvalidArgumentException If unknown referential action given.
3347
     */
3348
    public function getForeignKeyReferentialActionSQL($action)
3349
    {
3350
        $upper = strtoupper($action);
3351
        switch ($upper) {
3352
            case 'CASCADE':
3353
            case 'SET NULL':
3354
            case 'NO ACTION':
3355
            case 'RESTRICT':
3356
            case 'SET DEFAULT':
3357
                return $upper;
3358
 
3359
            default:
3360
                throw new InvalidArgumentException('Invalid foreign key action: ' . $upper);
3361
        }
3362
    }
3363
 
3364
    /**
3365
     * Obtains DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
3366
     * of a column declaration to be used in statements like CREATE TABLE.
3367
     *
3368
     * @return string
3369
     *
3370
     * @throws InvalidArgumentException
3371
     */
3372
    public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey)
3373
    {
3374
        $sql = '';
3375
        if (strlen($foreignKey->getName()) > 0) {
3376
            $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' ';
3377
        }
3378
 
3379
        $sql .= 'FOREIGN KEY (';
3380
 
3381
        if (count($foreignKey->getLocalColumns()) === 0) {
3382
            throw new InvalidArgumentException("Incomplete definition. 'local' required.");
3383
        }
3384
 
3385
        if (count($foreignKey->getForeignColumns()) === 0) {
3386
            throw new InvalidArgumentException("Incomplete definition. 'foreign' required.");
3387
        }
3388
 
3389
        if (strlen($foreignKey->getForeignTableName()) === 0) {
3390
            throw new InvalidArgumentException("Incomplete definition. 'foreignTable' required.");
3391
        }
3392
 
3393
        return $sql . implode(', ', $foreignKey->getQuotedLocalColumns($this))
3394
            . ') REFERENCES '
3395
            . $foreignKey->getQuotedForeignTableName($this) . ' ('
3396
            . implode(', ', $foreignKey->getQuotedForeignColumns($this)) . ')';
3397
    }
3398
 
3399
    /**
3400
     * Obtains DBMS specific SQL code portion needed to set the UNIQUE constraint
3401
     * of a column declaration to be used in statements like CREATE TABLE.
3402
     *
3403
     * @deprecated Use UNIQUE in SQL instead.
3404
     *
3405
     * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint
3406
     *                of a column declaration.
3407
     */
3408
    public function getUniqueFieldDeclarationSQL()
3409
    {
3410
        Deprecation::trigger(
3411
            'doctrine/dbal',
3412
            'https://github.com/doctrine/dbal/pull/4724',
3413
            'AbstractPlatform::getUniqueFieldDeclarationSQL() is deprecated. Use UNIQUE in SQL instead.',
3414
        );
3415
 
3416
        return 'UNIQUE';
3417
    }
3418
 
3419
    /**
3420
     * Obtains DBMS specific SQL code portion needed to set the CHARACTER SET
3421
     * of a column declaration to be used in statements like CREATE TABLE.
3422
     *
3423
     * @param string $charset The name of the charset.
3424
     *
3425
     * @return string DBMS specific SQL code portion needed to set the CHARACTER SET
3426
     *                of a column declaration.
3427
     */
3428
    public function getColumnCharsetDeclarationSQL($charset)
3429
    {
3430
        return '';
3431
    }
3432
 
3433
    /**
3434
     * Obtains DBMS specific SQL code portion needed to set the COLLATION
3435
     * of a column declaration to be used in statements like CREATE TABLE.
3436
     *
3437
     * @param string $collation The name of the collation.
3438
     *
3439
     * @return string DBMS specific SQL code portion needed to set the COLLATION
3440
     *                of a column declaration.
3441
     */
3442
    public function getColumnCollationDeclarationSQL($collation)
3443
    {
3444
        return $this->supportsColumnCollation() ? 'COLLATE ' . $this->quoteSingleIdentifier($collation) : '';
3445
    }
3446
 
3447
    /**
3448
     * Whether the platform prefers identity columns (eg. autoincrement) for ID generation.
3449
     * Subclasses should override this method to return TRUE if they prefer identity columns.
3450
     *
3451
     * @deprecated
3452
     *
3453
     * @return bool
3454
     */
3455
    public function prefersIdentityColumns()
3456
    {
3457
        Deprecation::trigger(
3458
            'doctrine/dbal',
3459
            'https://github.com/doctrine/dbal/pull/1519',
3460
            'AbstractPlatform::prefersIdentityColumns() is deprecated.',
3461
        );
3462
 
3463
        return false;
3464
    }
3465
 
3466
    /**
3467
     * Some platforms need the boolean values to be converted.
3468
     *
3469
     * The default conversion in this implementation converts to integers (false => 0, true => 1).
3470
     *
3471
     * Note: if the input is not a boolean the original input might be returned.
3472
     *
3473
     * There are two contexts when converting booleans: Literals and Prepared Statements.
3474
     * This method should handle the literal case
3475
     *
3476
     * @param mixed $item A boolean or an array of them.
3477
     *
3478
     * @return mixed A boolean database value or an array of them.
3479
     */
3480
    public function convertBooleans($item)
3481
    {
3482
        if (is_array($item)) {
3483
            foreach ($item as $k => $value) {
3484
                if (! is_bool($value)) {
3485
                    continue;
3486
                }
3487
 
3488
                $item[$k] = (int) $value;
3489
            }
3490
        } elseif (is_bool($item)) {
3491
            $item = (int) $item;
3492
        }
3493
 
3494
        return $item;
3495
    }
3496
 
3497
    /**
3498
     * Some platforms have boolean literals that needs to be correctly converted
3499
     *
3500
     * The default conversion tries to convert value into bool "(bool)$item"
3501
     *
3502
     * @param mixed $item
3503
     *
3504
     * @return bool|null
3505
     */
3506
    public function convertFromBoolean($item)
3507
    {
3508
        return $item === null ? null : (bool) $item;
3509
    }
3510
 
3511
    /**
3512
     * This method should handle the prepared statements case. When there is no
3513
     * distinction, it's OK to use the same method.
3514
     *
3515
     * Note: if the input is not a boolean the original input might be returned.
3516
     *
3517
     * @param mixed $item A boolean or an array of them.
3518
     *
3519
     * @return mixed A boolean database value or an array of them.
3520
     */
3521
    public function convertBooleansToDatabaseValue($item)
3522
    {
3523
        return $this->convertBooleans($item);
3524
    }
3525
 
3526
    /**
3527
     * Returns the SQL specific for the platform to get the current date.
3528
     *
3529
     * @return string
3530
     */
3531
    public function getCurrentDateSQL()
3532
    {
3533
        return 'CURRENT_DATE';
3534
    }
3535
 
3536
    /**
3537
     * Returns the SQL specific for the platform to get the current time.
3538
     *
3539
     * @return string
3540
     */
3541
    public function getCurrentTimeSQL()
3542
    {
3543
        return 'CURRENT_TIME';
3544
    }
3545
 
3546
    /**
3547
     * Returns the SQL specific for the platform to get the current timestamp
3548
     *
3549
     * @return string
3550
     */
3551
    public function getCurrentTimestampSQL()
3552
    {
3553
        return 'CURRENT_TIMESTAMP';
3554
    }
3555
 
3556
    /**
3557
     * Returns the SQL for a given transaction isolation level Connection constant.
3558
     *
3559
     * @param int $level
3560
     *
3561
     * @return string
3562
     *
3563
     * @throws InvalidArgumentException
3564
     */
3565
    protected function _getTransactionIsolationLevelSQL($level)
3566
    {
3567
        switch ($level) {
3568
            case TransactionIsolationLevel::READ_UNCOMMITTED:
3569
                return 'READ UNCOMMITTED';
3570
 
3571
            case TransactionIsolationLevel::READ_COMMITTED:
3572
                return 'READ COMMITTED';
3573
 
3574
            case TransactionIsolationLevel::REPEATABLE_READ:
3575
                return 'REPEATABLE READ';
3576
 
3577
            case TransactionIsolationLevel::SERIALIZABLE:
3578
                return 'SERIALIZABLE';
3579
 
3580
            default:
3581
                throw new InvalidArgumentException('Invalid isolation level:' . $level);
3582
        }
3583
    }
3584
 
3585
    /**
3586
     * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy.
3587
     *
3588
     * @return string
3589
     *
3590
     * @throws Exception If not supported on this platform.
3591
     */
3592
    public function getListDatabasesSQL()
3593
    {
3594
        throw Exception::notSupported(__METHOD__);
3595
    }
3596
 
3597
    /**
3598
     * Returns the SQL statement for retrieving the namespaces defined in the database.
3599
     *
3600
     * @deprecated Use {@see AbstractSchemaManager::listSchemaNames()} instead.
3601
     *
3602
     * @return string
3603
     *
3604
     * @throws Exception If not supported on this platform.
3605
     */
3606
    public function getListNamespacesSQL()
3607
    {
3608
        Deprecation::triggerIfCalledFromOutside(
3609
            'doctrine/dbal',
3610
            'https://github.com/doctrine/dbal/issues/4503',
3611
            'AbstractPlatform::getListNamespacesSQL() is deprecated,'
3612
                . ' use AbstractSchemaManager::listSchemaNames() instead.',
3613
        );
3614
 
3615
        throw Exception::notSupported(__METHOD__);
3616
    }
3617
 
3618
    /**
3619
     * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy.
3620
     *
3621
     * @param string $database
3622
     *
3623
     * @return string
3624
     *
3625
     * @throws Exception If not supported on this platform.
3626
     */
3627
    public function getListSequencesSQL($database)
3628
    {
3629
        throw Exception::notSupported(__METHOD__);
3630
    }
3631
 
3632
    /**
3633
     * @deprecated
3634
     *
3635
     * @param string $table
3636
     *
3637
     * @return string
3638
     *
3639
     * @throws Exception If not supported on this platform.
3640
     */
3641
    public function getListTableConstraintsSQL($table)
3642
    {
3643
        throw Exception::notSupported(__METHOD__);
3644
    }
3645
 
3646
    /**
3647
     * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon.
3648
     *
3649
     * @param string $table
3650
     * @param string $database
3651
     *
3652
     * @return string
3653
     *
3654
     * @throws Exception If not supported on this platform.
3655
     */
3656
    public function getListTableColumnsSQL($table, $database = null)
3657
    {
3658
        throw Exception::notSupported(__METHOD__);
3659
    }
3660
 
3661
    /**
3662
     * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon.
3663
     *
3664
     * @return string
3665
     *
3666
     * @throws Exception If not supported on this platform.
3667
     */
3668
    public function getListTablesSQL()
3669
    {
3670
        throw Exception::notSupported(__METHOD__);
3671
    }
3672
 
3673
    /**
3674
     * @deprecated
3675
     *
3676
     * @return string
3677
     *
3678
     * @throws Exception If not supported on this platform.
3679
     */
3680
    public function getListUsersSQL()
3681
    {
3682
        Deprecation::trigger(
3683
            'doctrine/dbal',
3684
            'https://github.com/doctrine/dbal/pull/4724',
3685
            'AbstractPlatform::getListUsersSQL() is deprecated.',
3686
        );
3687
 
3688
        throw Exception::notSupported(__METHOD__);
3689
    }
3690
 
3691
    /**
3692
     * Returns the SQL to list all views of a database or user.
3693
     *
3694
     * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy.
3695
     *
3696
     * @param string $database
3697
     *
3698
     * @return string
3699
     *
3700
     * @throws Exception If not supported on this platform.
3701
     */
3702
    public function getListViewsSQL($database)
3703
    {
3704
        throw Exception::notSupported(__METHOD__);
3705
    }
3706
 
3707
    /**
3708
     * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon.
3709
     *
3710
     * Returns the list of indexes for the current database.
3711
     *
3712
     * The current database parameter is optional but will always be passed
3713
     * when using the SchemaManager API and is the database the given table is in.
3714
     *
3715
     * Attention: Some platforms only support currentDatabase when they
3716
     * are connected with that database. Cross-database information schema
3717
     * requests may be impossible.
3718
     *
3719
     * @param string $table
3720
     * @param string $database
3721
     *
3722
     * @return string
3723
     *
3724
     * @throws Exception If not supported on this platform.
3725
     */
3726
    public function getListTableIndexesSQL($table, $database = null)
3727
    {
3728
        throw Exception::notSupported(__METHOD__);
3729
    }
3730
 
3731
    /**
3732
     * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon.
3733
     *
3734
     * @param string $table
3735
     *
3736
     * @return string
3737
     *
3738
     * @throws Exception If not supported on this platform.
3739
     */
3740
    public function getListTableForeignKeysSQL($table)
3741
    {
3742
        throw Exception::notSupported(__METHOD__);
3743
    }
3744
 
3745
    /**
3746
     * @param string $name
3747
     * @param string $sql
3748
     *
3749
     * @return string
3750
     */
3751
    public function getCreateViewSQL($name, $sql)
3752
    {
3753
        return 'CREATE VIEW ' . $name . ' AS ' . $sql;
3754
    }
3755
 
3756
    /**
3757
     * @param string $name
3758
     *
3759
     * @return string
3760
     */
3761
    public function getDropViewSQL($name)
3762
    {
3763
        return 'DROP VIEW ' . $name;
3764
    }
3765
 
3766
    /**
3767
     * @param string $sequence
3768
     *
3769
     * @return string
3770
     *
3771
     * @throws Exception If not supported on this platform.
3772
     */
3773
    public function getSequenceNextValSQL($sequence)
3774
    {
3775
        throw Exception::notSupported(__METHOD__);
3776
    }
3777
 
3778
    /**
3779
     * Returns the SQL to create a new database.
3780
     *
3781
     * @param string $name The name of the database that should be created.
3782
     *
3783
     * @return string
3784
     *
3785
     * @throws Exception If not supported on this platform.
3786
     */
3787
    public function getCreateDatabaseSQL($name)
3788
    {
3789
        if (! $this->supportsCreateDropDatabase()) {
3790
            throw Exception::notSupported(__METHOD__);
3791
        }
3792
 
3793
        return 'CREATE DATABASE ' . $name;
3794
    }
3795
 
3796
    /**
3797
     * Returns the SQL snippet to drop an existing database.
3798
     *
3799
     * @param string $name The name of the database that should be dropped.
3800
     *
3801
     * @return string
3802
     */
3803
    public function getDropDatabaseSQL($name)
3804
    {
3805
        if (! $this->supportsCreateDropDatabase()) {
3806
            throw Exception::notSupported(__METHOD__);
3807
        }
3808
 
3809
        return 'DROP DATABASE ' . $name;
3810
    }
3811
 
3812
    /**
3813
     * Returns the SQL to set the transaction isolation level.
3814
     *
3815
     * @param int $level
3816
     *
3817
     * @return string
3818
     *
3819
     * @throws Exception If not supported on this platform.
3820
     */
3821
    public function getSetTransactionIsolationSQL($level)
3822
    {
3823
        throw Exception::notSupported(__METHOD__);
3824
    }
3825
 
3826
    /**
3827
     * Obtains DBMS specific SQL to be used to create datetime columns in
3828
     * statements like CREATE TABLE.
3829
     *
3830
     * @param mixed[] $column
3831
     *
3832
     * @return string
3833
     *
3834
     * @throws Exception If not supported on this platform.
3835
     */
3836
    public function getDateTimeTypeDeclarationSQL(array $column)
3837
    {
3838
        throw Exception::notSupported(__METHOD__);
3839
    }
3840
 
3841
    /**
3842
     * Obtains DBMS specific SQL to be used to create datetime with timezone offset columns.
3843
     *
3844
     * @param mixed[] $column
3845
     *
3846
     * @return string
3847
     */
3848
    public function getDateTimeTzTypeDeclarationSQL(array $column)
3849
    {
3850
        return $this->getDateTimeTypeDeclarationSQL($column);
3851
    }
3852
 
3853
    /**
3854
     * Obtains DBMS specific SQL to be used to create date columns in statements
3855
     * like CREATE TABLE.
3856
     *
3857
     * @param mixed[] $column
3858
     *
3859
     * @return string
3860
     *
3861
     * @throws Exception If not supported on this platform.
3862
     */
3863
    public function getDateTypeDeclarationSQL(array $column)
3864
    {
3865
        throw Exception::notSupported(__METHOD__);
3866
    }
3867
 
3868
    /**
3869
     * Obtains DBMS specific SQL to be used to create time columns in statements
3870
     * like CREATE TABLE.
3871
     *
3872
     * @param mixed[] $column
3873
     *
3874
     * @return string
3875
     *
3876
     * @throws Exception If not supported on this platform.
3877
     */
3878
    public function getTimeTypeDeclarationSQL(array $column)
3879
    {
3880
        throw Exception::notSupported(__METHOD__);
3881
    }
3882
 
3883
    /**
3884
     * @param mixed[] $column
3885
     *
3886
     * @return string
3887
     */
3888
    public function getFloatDeclarationSQL(array $column)
3889
    {
3890
        return 'DOUBLE PRECISION';
3891
    }
3892
 
3893
    /**
3894
     * Gets the default transaction isolation level of the platform.
3895
     *
3896
     * @see TransactionIsolationLevel
3897
     *
3898
     * @return TransactionIsolationLevel::* The default isolation level.
3899
     */
3900
    public function getDefaultTransactionIsolationLevel()
3901
    {
3902
        return TransactionIsolationLevel::READ_COMMITTED;
3903
    }
3904
 
3905
    /* supports*() methods */
3906
 
3907
    /**
3908
     * Whether the platform supports sequences.
3909
     *
3910
     * @return bool
3911
     */
3912
    public function supportsSequences()
3913
    {
3914
        return false;
3915
    }
3916
 
3917
    /**
3918
     * Whether the platform supports identity columns.
3919
     *
3920
     * Identity columns are columns that receive an auto-generated value from the
3921
     * database on insert of a row.
3922
     *
3923
     * @return bool
3924
     */
3925
    public function supportsIdentityColumns()
3926
    {
3927
        return false;
3928
    }
3929
 
3930
    /**
3931
     * Whether the platform emulates identity columns through sequences.
3932
     *
3933
     * Some platforms that do not support identity columns natively
3934
     * but support sequences can emulate identity columns by using
3935
     * sequences.
3936
     *
3937
     * @deprecated
3938
     *
3939
     * @return bool
3940
     */
3941
    public function usesSequenceEmulatedIdentityColumns()
3942
    {
3943
        Deprecation::trigger(
3944
            'doctrine/dbal',
3945
            'https://github.com/doctrine/dbal/pull/5513',
3946
            '%s is deprecated.',
3947
            __METHOD__,
3948
        );
3949
 
3950
        return false;
3951
    }
3952
 
3953
    /**
3954
     * Returns the name of the sequence for a particular identity column in a particular table.
3955
     *
3956
     * @deprecated
3957
     *
3958
     * @see usesSequenceEmulatedIdentityColumns
3959
     *
3960
     * @param string $tableName  The name of the table to return the sequence name for.
3961
     * @param string $columnName The name of the identity column in the table to return the sequence name for.
3962
     *
3963
     * @return string
3964
     *
3965
     * @throws Exception If not supported on this platform.
3966
     */
3967
    public function getIdentitySequenceName($tableName, $columnName)
3968
    {
3969
        throw Exception::notSupported(__METHOD__);
3970
    }
3971
 
3972
    /**
3973
     * Whether the platform supports indexes.
3974
     *
3975
     * @deprecated
3976
     *
3977
     * @return bool
3978
     */
3979
    public function supportsIndexes()
3980
    {
3981
        Deprecation::trigger(
3982
            'doctrine/dbal',
3983
            'https://github.com/doctrine/dbal/pull/4724',
3984
            'AbstractPlatform::supportsIndexes() is deprecated.',
3985
        );
3986
 
3987
        return true;
3988
    }
3989
 
3990
    /**
3991
     * Whether the platform supports partial indexes.
3992
     *
3993
     * @return bool
3994
     */
3995
    public function supportsPartialIndexes()
3996
    {
3997
        return false;
3998
    }
3999
 
4000
    /**
4001
     * Whether the platform supports indexes with column length definitions.
4002
     */
4003
    public function supportsColumnLengthIndexes(): bool
4004
    {
4005
        return false;
4006
    }
4007
 
4008
    /**
4009
     * Whether the platform supports altering tables.
4010
     *
4011
     * @deprecated All platforms must implement altering tables.
4012
     *
4013
     * @return bool
4014
     */
4015
    public function supportsAlterTable()
4016
    {
4017
        Deprecation::trigger(
4018
            'doctrine/dbal',
4019
            'https://github.com/doctrine/dbal/pull/4724',
4020
            'AbstractPlatform::supportsAlterTable() is deprecated. All platforms must implement altering tables.',
4021
        );
4022
 
4023
        return true;
4024
    }
4025
 
4026
    /**
4027
     * Whether the platform supports transactions.
4028
     *
4029
     * @deprecated
4030
     *
4031
     * @return bool
4032
     */
4033
    public function supportsTransactions()
4034
    {
4035
        Deprecation::trigger(
4036
            'doctrine/dbal',
4037
            'https://github.com/doctrine/dbal/pull/4724',
4038
            'AbstractPlatform::supportsTransactions() is deprecated.',
4039
        );
4040
 
4041
        return true;
4042
    }
4043
 
4044
    /**
4045
     * Whether the platform supports savepoints.
4046
     *
4047
     * @return bool
4048
     */
4049
    public function supportsSavepoints()
4050
    {
4051
        return true;
4052
    }
4053
 
4054
    /**
4055
     * Whether the platform supports releasing savepoints.
4056
     *
4057
     * @return bool
4058
     */
4059
    public function supportsReleaseSavepoints()
4060
    {
4061
        return $this->supportsSavepoints();
4062
    }
4063
 
4064
    /**
4065
     * Whether the platform supports primary key constraints.
4066
     *
4067
     * @deprecated
4068
     *
4069
     * @return bool
4070
     */
4071
    public function supportsPrimaryConstraints()
4072
    {
4073
        Deprecation::trigger(
4074
            'doctrine/dbal',
4075
            'https://github.com/doctrine/dbal/pull/4724',
4076
            'AbstractPlatform::supportsPrimaryConstraints() is deprecated.',
4077
        );
4078
 
4079
        return true;
4080
    }
4081
 
4082
    /**
4083
     * Whether the platform supports foreign key constraints.
4084
     *
4085
     * @deprecated All platforms should support foreign key constraints.
4086
     *
4087
     * @return bool
4088
     */
4089
    public function supportsForeignKeyConstraints()
4090
    {
4091
        Deprecation::triggerIfCalledFromOutside(
4092
            'doctrine/dbal',
4093
            'https://github.com/doctrine/dbal/pull/5409',
4094
            'AbstractPlatform::supportsForeignKeyConstraints() is deprecated.',
4095
        );
4096
 
4097
        return true;
4098
    }
4099
 
4100
    /**
4101
     * Whether the platform supports database schemas.
4102
     *
4103
     * @return bool
4104
     */
4105
    public function supportsSchemas()
4106
    {
4107
        return false;
4108
    }
4109
 
4110
    /**
4111
     * Whether this platform can emulate schemas.
4112
     *
4113
     * @deprecated
4114
     *
4115
     * Platforms that either support or emulate schemas don't automatically
4116
     * filter a schema for the namespaced elements in {@see AbstractManager::introspectSchema()}.
4117
     *
4118
     * @return bool
4119
     */
4120
    public function canEmulateSchemas()
4121
    {
4122
        Deprecation::trigger(
4123
            'doctrine/dbal',
4124
            'https://github.com/doctrine/dbal/pull/4805',
4125
            'AbstractPlatform::canEmulateSchemas() is deprecated.',
4126
        );
4127
 
4128
        return false;
4129
    }
4130
 
4131
    /**
4132
     * Returns the default schema name.
4133
     *
4134
     * @deprecated
4135
     *
4136
     * @return string
4137
     *
4138
     * @throws Exception If not supported on this platform.
4139
     */
4140
    public function getDefaultSchemaName()
4141
    {
4142
        throw Exception::notSupported(__METHOD__);
4143
    }
4144
 
4145
    /**
4146
     * Whether this platform supports create database.
4147
     *
4148
     * Some databases don't allow to create and drop databases at all or only with certain tools.
4149
     *
4150
     * @deprecated
4151
     *
4152
     * @return bool
4153
     */
4154
    public function supportsCreateDropDatabase()
4155
    {
4156
        Deprecation::trigger(
4157
            'doctrine/dbal',
4158
            'https://github.com/doctrine/dbal/pull/5513',
4159
            '%s is deprecated.',
4160
            __METHOD__,
4161
        );
4162
 
4163
        return true;
4164
    }
4165
 
4166
    /**
4167
     * Whether the platform supports getting the affected rows of a recent update/delete type query.
4168
     *
4169
     * @deprecated
4170
     *
4171
     * @return bool
4172
     */
4173
    public function supportsGettingAffectedRows()
4174
    {
4175
        Deprecation::trigger(
4176
            'doctrine/dbal',
4177
            'https://github.com/doctrine/dbal/pull/4724',
4178
            'AbstractPlatform::supportsGettingAffectedRows() is deprecated.',
4179
        );
4180
 
4181
        return true;
4182
    }
4183
 
4184
    /**
4185
     * Whether this platform support to add inline column comments as postfix.
4186
     *
4187
     * @return bool
4188
     */
4189
    public function supportsInlineColumnComments()
4190
    {
4191
        return false;
4192
    }
4193
 
4194
    /**
4195
     * Whether this platform support the proprietary syntax "COMMENT ON asset".
4196
     *
4197
     * @return bool
4198
     */
4199
    public function supportsCommentOnStatement()
4200
    {
4201
        return false;
4202
    }
4203
 
4204
    /**
4205
     * Does this platform have native guid type.
4206
     *
4207
     * @deprecated
4208
     *
4209
     * @return bool
4210
     */
4211
    public function hasNativeGuidType()
4212
    {
4213
        Deprecation::triggerIfCalledFromOutside(
4214
            'doctrine/dbal',
4215
            'https://github.com/doctrine/dbal/pull/5509',
4216
            '%s is deprecated.',
4217
            __METHOD__,
4218
        );
4219
 
4220
        return false;
4221
    }
4222
 
4223
    /**
4224
     * Does this platform have native JSON type.
4225
     *
4226
     * @deprecated
4227
     *
4228
     * @return bool
4229
     */
4230
    public function hasNativeJsonType()
4231
    {
4232
        Deprecation::triggerIfCalledFromOutside(
4233
            'doctrine/dbal',
4234
            'https://github.com/doctrine/dbal/pull/5509',
4235
            '%s is deprecated.',
4236
            __METHOD__,
4237
        );
4238
 
4239
        return false;
4240
    }
4241
 
4242
    /**
4243
     * Whether this platform supports views.
4244
     *
4245
     * @deprecated All platforms must implement support for views.
4246
     *
4247
     * @return bool
4248
     */
4249
    public function supportsViews()
4250
    {
4251
        Deprecation::trigger(
4252
            'doctrine/dbal',
4253
            'https://github.com/doctrine/dbal/pull/4724',
4254
            'AbstractPlatform::supportsViews() is deprecated. All platforms must implement support for views.',
4255
        );
4256
 
4257
        return true;
4258
    }
4259
 
4260
    /**
4261
     * Does this platform support column collation?
4262
     *
4263
     * @return bool
4264
     */
4265
    public function supportsColumnCollation()
4266
    {
4267
        return false;
4268
    }
4269
 
4270
    /**
4271
     * Gets the format string, as accepted by the date() function, that describes
4272
     * the format of a stored datetime value of this platform.
4273
     *
4274
     * @return string The format string.
4275
     */
4276
    public function getDateTimeFormatString()
4277
    {
4278
        return 'Y-m-d H:i:s';
4279
    }
4280
 
4281
    /**
4282
     * Gets the format string, as accepted by the date() function, that describes
4283
     * the format of a stored datetime with timezone value of this platform.
4284
     *
4285
     * @return string The format string.
4286
     */
4287
    public function getDateTimeTzFormatString()
4288
    {
4289
        return 'Y-m-d H:i:s';
4290
    }
4291
 
4292
    /**
4293
     * Gets the format string, as accepted by the date() function, that describes
4294
     * the format of a stored date value of this platform.
4295
     *
4296
     * @return string The format string.
4297
     */
4298
    public function getDateFormatString()
4299
    {
4300
        return 'Y-m-d';
4301
    }
4302
 
4303
    /**
4304
     * Gets the format string, as accepted by the date() function, that describes
4305
     * the format of a stored time value of this platform.
4306
     *
4307
     * @return string The format string.
4308
     */
4309
    public function getTimeFormatString()
4310
    {
4311
        return 'H:i:s';
4312
    }
4313
 
4314
    /**
4315
     * Adds an driver-specific LIMIT clause to the query.
4316
     *
4317
     * @param string   $query
4318
     * @param int|null $limit
4319
     * @param int      $offset
4320
     *
4321
     * @throws Exception
4322
     */
4323
    final public function modifyLimitQuery($query, $limit, $offset = 0): string
4324
    {
4325
        if ($offset < 0) {
4326
            throw new Exception(sprintf(
4327
                'Offset must be a positive integer or zero, %d given',
4328
                $offset,
4329
            ));
4330
        }
4331
 
4332
        if ($offset > 0 && ! $this->supportsLimitOffset()) {
4333
            throw new Exception(sprintf(
4334
                'Platform %s does not support offset values in limit queries.',
4335
                $this->getName(),
4336
            ));
4337
        }
4338
 
4339
        if ($limit !== null) {
4340
            $limit = (int) $limit;
4341
        }
4342
 
4343
        return $this->doModifyLimitQuery($query, $limit, (int) $offset);
4344
    }
4345
 
4346
    /**
4347
     * Adds an platform-specific LIMIT clause to the query.
4348
     *
4349
     * @param string   $query
4350
     * @param int|null $limit
4351
     * @param int      $offset
4352
     *
4353
     * @return string
4354
     */
4355
    protected function doModifyLimitQuery($query, $limit, $offset)
4356
    {
4357
        if ($limit !== null) {
4358
            $query .= sprintf(' LIMIT %d', $limit);
4359
        }
4360
 
4361
        if ($offset > 0) {
4362
            $query .= sprintf(' OFFSET %d', $offset);
4363
        }
4364
 
4365
        return $query;
4366
    }
4367
 
4368
    /**
4369
     * Whether the database platform support offsets in modify limit clauses.
4370
     *
4371
     * @deprecated All platforms must implement support for offsets in modify limit clauses.
4372
     *
4373
     * @return bool
4374
     */
4375
    public function supportsLimitOffset()
4376
    {
4377
        Deprecation::triggerIfCalledFromOutside(
4378
            'doctrine/dbal',
4379
            'https://github.com/doctrine/dbal/pull/4724',
4380
            'AbstractPlatform::supportsViews() is deprecated.'
4381
            . ' All platforms must implement support for offsets in modify limit clauses.',
4382
        );
4383
 
4384
        return true;
4385
    }
4386
 
4387
    /**
4388
     * Maximum length of any given database identifier, like tables or column names.
4389
     *
4390
     * @return int
4391
     */
4392
    public function getMaxIdentifierLength()
4393
    {
4394
        return 63;
4395
    }
4396
 
4397
    /**
4398
     * Returns the insert SQL for an empty insert statement.
4399
     *
4400
     * @param string $quotedTableName
4401
     * @param string $quotedIdentifierColumnName
4402
     *
4403
     * @return string
4404
     */
4405
    public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName)
4406
    {
4407
        return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (null)';
4408
    }
4409
 
4410
    /**
4411
     * Generates a Truncate Table SQL statement for a given table.
4412
     *
4413
     * Cascade is not supported on many platforms but would optionally cascade the truncate by
4414
     * following the foreign keys.
4415
     *
4416
     * @param string $tableName
4417
     * @param bool   $cascade
4418
     *
4419
     * @return string
4420
     */
4421
    public function getTruncateTableSQL($tableName, $cascade = false)
4422
    {
4423
        $tableIdentifier = new Identifier($tableName);
4424
 
4425
        return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this);
4426
    }
4427
 
4428
    /**
4429
     * This is for test reasons, many vendors have special requirements for dummy statements.
4430
     *
4431
     * @return string
4432
     */
4433
    public function getDummySelectSQL()
4434
    {
4435
        $expression = func_num_args() > 0 ? func_get_arg(0) : '1';
4436
 
4437
        return sprintf('SELECT %s', $expression);
4438
    }
4439
 
4440
    /**
4441
     * Returns the SQL to create a new savepoint.
4442
     *
4443
     * @param string $savepoint
4444
     *
4445
     * @return string
4446
     */
4447
    public function createSavePoint($savepoint)
4448
    {
4449
        return 'SAVEPOINT ' . $savepoint;
4450
    }
4451
 
4452
    /**
4453
     * Returns the SQL to release a savepoint.
4454
     *
4455
     * @param string $savepoint
4456
     *
4457
     * @return string
4458
     */
4459
    public function releaseSavePoint($savepoint)
4460
    {
4461
        return 'RELEASE SAVEPOINT ' . $savepoint;
4462
    }
4463
 
4464
    /**
4465
     * Returns the SQL to rollback a savepoint.
4466
     *
4467
     * @param string $savepoint
4468
     *
4469
     * @return string
4470
     */
4471
    public function rollbackSavePoint($savepoint)
4472
    {
4473
        return 'ROLLBACK TO SAVEPOINT ' . $savepoint;
4474
    }
4475
 
4476
    /**
4477
     * Returns the keyword list instance of this platform.
4478
     *
4479
     * @throws Exception If no keyword list is specified.
4480
     */
4481
    final public function getReservedKeywordsList(): KeywordList
4482
    {
4483
        // Store the instance so it doesn't need to be generated on every request.
4484
        return $this->_keywords ??= $this->createReservedKeywordsList();
4485
    }
4486
 
4487
    /**
4488
     * Creates an instance of the reserved keyword list of this platform.
4489
     *
4490
     * This method will become @abstract in DBAL 4.0.0.
4491
     *
4492
     * @throws Exception
4493
     */
4494
    protected function createReservedKeywordsList(): KeywordList
4495
    {
4496
        $class    = $this->getReservedKeywordsClass();
4497
        $keywords = new $class();
4498
        if (! $keywords instanceof KeywordList) {
4499
            throw Exception::notSupported(__METHOD__);
4500
        }
4501
 
4502
        return $keywords;
4503
    }
4504
 
4505
    /**
4506
     * Returns the class name of the reserved keywords list.
4507
     *
4508
     * @deprecated Implement {@see createReservedKeywordsList()} instead.
4509
     *
4510
     * @return string
4511
     * @psalm-return class-string<KeywordList>
4512
     *
4513
     * @throws Exception If not supported on this platform.
4514
     */
4515
    protected function getReservedKeywordsClass()
4516
    {
4517
        Deprecation::triggerIfCalledFromOutside(
4518
            'doctrine/dbal',
4519
            'https://github.com/doctrine/dbal/issues/4510',
4520
            'AbstractPlatform::getReservedKeywordsClass() is deprecated,'
4521
                . ' use AbstractPlatform::createReservedKeywordsList() instead.',
4522
        );
4523
 
4524
        throw Exception::notSupported(__METHOD__);
4525
    }
4526
 
4527
    /**
4528
     * Quotes a literal string.
4529
     * This method is NOT meant to fix SQL injections!
4530
     * It is only meant to escape this platform's string literal
4531
     * quote character inside the given literal string.
4532
     *
4533
     * @param string $str The literal string to be quoted.
4534
     *
4535
     * @return string The quoted literal string.
4536
     */
4537
    public function quoteStringLiteral($str)
4538
    {
4539
        $c = $this->getStringLiteralQuoteCharacter();
4540
 
4541
        return $c . str_replace($c, $c . $c, $str) . $c;
4542
    }
4543
 
4544
    /**
4545
     * Gets the character used for string literal quoting.
4546
     *
4547
     * @deprecated Use {@see quoteStringLiteral()} to quote string literals instead.
4548
     *
4549
     * @return string
4550
     */
4551
    public function getStringLiteralQuoteCharacter()
4552
    {
4553
        Deprecation::triggerIfCalledFromOutside(
4554
            'doctrine/dbal',
4555
            'https://github.com/doctrine/dbal/pull/5388',
4556
            'AbstractPlatform::getStringLiteralQuoteCharacter() is deprecated.'
4557
                . ' Use quoteStringLiteral() instead.',
4558
        );
4559
 
4560
        return "'";
4561
    }
4562
 
4563
    /**
4564
     * Escapes metacharacters in a string intended to be used with a LIKE
4565
     * operator.
4566
     *
4567
     * @param string $inputString a literal, unquoted string
4568
     * @param string $escapeChar  should be reused by the caller in the LIKE
4569
     *                            expression.
4570
     */
4571
    final public function escapeStringForLike(string $inputString, string $escapeChar): string
4572
    {
4573
        return preg_replace(
4574
            '~([' . preg_quote($this->getLikeWildcardCharacters() . $escapeChar, '~') . '])~u',
4575
            addcslashes($escapeChar, '\\') . '$1',
4576
            $inputString,
4577
        );
4578
    }
4579
 
4580
    /**
4581
     * @return array<string,mixed> An associative array with the name of the properties
4582
     *                             of the column being declared as array indexes.
4583
     */
4584
    private function columnToArray(Column $column): array
4585
    {
4586
        $name = $column->getQuotedName($this);
4587
 
4588
        return array_merge($column->toArray(), [
4589
            'name' => $name,
4590
            'version' => $column->hasPlatformOption('version') ? $column->getPlatformOption('version') : false,
4591
            'comment' => $this->getColumnComment($column),
4592
        ]);
4593
    }
4594
 
4595
    /** @internal */
4596
    public function createSQLParser(): Parser
4597
    {
4598
        return new Parser(false);
4599
    }
4600
 
4601
    protected function getLikeWildcardCharacters(): string
4602
    {
4603
        return '%_';
4604
    }
4605
 
4606
    /**
4607
     * Compares the definitions of the given columns in the context of this platform.
4608
     *
4609
     * @throws Exception
4610
     */
4611
    public function columnsEqual(Column $column1, Column $column2): bool
4612
    {
4613
        $column1Array = $this->columnToArray($column1);
4614
        $column2Array = $this->columnToArray($column2);
4615
 
4616
        // ignore explicit columnDefinition since it's not set on the Column generated by the SchemaManager
4617
        unset($column1Array['columnDefinition']);
4618
        unset($column2Array['columnDefinition']);
4619
 
4620
        if (
4621
            $this->getColumnDeclarationSQL('', $column1Array)
4622
            !== $this->getColumnDeclarationSQL('', $column2Array)
4623
        ) {
4624
            return false;
4625
        }
4626
 
4627
        // If the platform supports inline comments, all comparison is already done above
4628
        if ($this->supportsInlineColumnComments()) {
4629
            return true;
4630
        }
4631
 
4632
        if ($column1->getComment() !== $column2->getComment()) {
4633
            return false;
4634
        }
4635
 
4636
        return $column1->getType() === $column2->getType();
4637
    }
4638
 
4639
    /**
4640
     * Creates the schema manager that can be used to inspect and change the underlying
4641
     * database schema according to the dialect of the platform.
4642
     *
4643
     * @throws Exception
4644
     *
4645
     * @abstract
4646
     */
4647
    public function createSchemaManager(Connection $connection): AbstractSchemaManager
4648
    {
4649
        throw Exception::notSupported(__METHOD__);
4650
    }
4651
}