Completed
Pull Request — master (#3133)
by Michael
63:30
created

AbstractPlatform::getCharMaxLength()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 1
cts 1
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 0
crap 1
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\DBAL\Platforms;
21
22
use Doctrine\Common\EventManager;
23
use Doctrine\DBAL\DBALException;
24
use Doctrine\DBAL\Event\SchemaAlterTableAddColumnEventArgs;
25
use Doctrine\DBAL\Event\SchemaAlterTableChangeColumnEventArgs;
26
use Doctrine\DBAL\Event\SchemaAlterTableEventArgs;
27
use Doctrine\DBAL\Event\SchemaAlterTableRemoveColumnEventArgs;
28
use Doctrine\DBAL\Event\SchemaAlterTableRenameColumnEventArgs;
29
use Doctrine\DBAL\Event\SchemaCreateTableColumnEventArgs;
30
use Doctrine\DBAL\Event\SchemaCreateTableEventArgs;
31
use Doctrine\DBAL\Event\SchemaDropTableEventArgs;
32
use Doctrine\DBAL\Events;
33
use Doctrine\DBAL\Schema\Column;
34
use Doctrine\DBAL\Schema\ColumnDiff;
35
use Doctrine\DBAL\Schema\Constraint;
36
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
37
use Doctrine\DBAL\Schema\Identifier;
38
use Doctrine\DBAL\Schema\Index;
39
use Doctrine\DBAL\Schema\Sequence;
40
use Doctrine\DBAL\Schema\Table;
41
use Doctrine\DBAL\Schema\TableDiff;
42
use Doctrine\DBAL\TransactionIsolationLevel;
43
use Doctrine\DBAL\Types;
44
use Doctrine\DBAL\Types\Type;
45
use function addcslashes;
46
use function array_map;
47
use function array_merge;
48
use function array_unique;
49
use function array_values;
50
use function count;
51
use function explode;
52
use function func_get_arg;
53
use function func_get_args;
54
use function func_num_args;
55
use function get_class;
56
use function implode;
57
use function in_array;
58
use function is_array;
59
use function is_bool;
60
use function is_int;
61
use function is_string;
62
use function join;
63
use function preg_quote;
64
use function preg_replace;
65
use function sprintf;
66
use function str_replace;
67
use function strlen;
68
use function strpos;
69
use function strtolower;
70
use function strtoupper;
71
72
/**
73
 * Base class for all DatabasePlatforms. The DatabasePlatforms are the central
74
 * point of abstraction of platform-specific behaviors, features and SQL dialects.
75
 * They are a passive source of information.
76
 *
77
 * @link   www.doctrine-project.org
78
 * @since  2.0
79
 * @author Guilherme Blanco <[email protected]>
80
 * @author Jonathan Wage <[email protected]>
81
 * @author Roman Borschel <[email protected]>
82
 * @author Lukas Smith <[email protected]> (PEAR MDB2 library)
83
 * @author Benjamin Eberlei <[email protected]>
84
 * @todo   Remove any unnecessary methods.
85
 */
86
abstract class AbstractPlatform
87
{
88
    /**
89
     * @var int
90
     */
91
    const CREATE_INDEXES = 1;
92
93
    /**
94
     * @var int
95
     */
96
    const CREATE_FOREIGNKEYS = 2;
97
98
    /**
99
     * @deprecated Use DateIntervalUnit::INTERVAL_UNIT_SECOND.
100
     */
101
    public const DATE_INTERVAL_UNIT_SECOND = DateIntervalUnit::SECOND;
102
103
    /**
104
     * @deprecated Use DateIntervalUnit::MINUTE.
105
     */
106
    public const DATE_INTERVAL_UNIT_MINUTE = DateIntervalUnit::MINUTE;
107
108
    /**
109
     * @deprecated Use DateIntervalUnit::HOUR.
110
     */
111
    public const DATE_INTERVAL_UNIT_HOUR = DateIntervalUnit::HOUR;
112
113
    /**
114
     * @deprecated Use DateIntervalUnit::DAY.
115
     */
116
    public const DATE_INTERVAL_UNIT_DAY = DateIntervalUnit::DAY;
117
118
    /**
119
     * @deprecated Use DateIntervalUnit::WEEK.
120
     */
121
    public const DATE_INTERVAL_UNIT_WEEK = DateIntervalUnit::WEEK;
122
123
    /**
124
     * @deprecated Use DateIntervalUnit::MONTH.
125
     */
126
    public const DATE_INTERVAL_UNIT_MONTH = DateIntervalUnit::MONTH;
127
128
    /**
129
     * @deprecated Use DateIntervalUnit::QUARTER.
130
     */
131
    public const DATE_INTERVAL_UNIT_QUARTER = DateIntervalUnit::QUARTER;
132
133
    /**
134
     * @deprecated Use DateIntervalUnit::QUARTER.
135
     */
136
    public const DATE_INTERVAL_UNIT_YEAR = DateIntervalUnit::YEAR;
137
138
    /**
139
     * @var int
140
     *
141
     * @deprecated Use TrimMode::UNSPECIFIED.
142
     */
143
    public const TRIM_UNSPECIFIED = TrimMode::UNSPECIFIED;
144
145
    /**
146
     * @var int
147
     *
148
     * @deprecated Use TrimMode::LEADING.
149
     */
150
    public const TRIM_LEADING = TrimMode::LEADING;
151
152
    /**
153
     * @var int
154
     *
155
     * @deprecated Use TrimMode::TRAILING.
156
     */
157
    public const TRIM_TRAILING = TrimMode::TRAILING;
158
159
    /**
160
     * @var int
161
     *
162
     * @deprecated Use TrimMode::BOTH.
163
     */
164
    public const TRIM_BOTH = TrimMode::BOTH;
165
166
    /**
167
     * @var array|null
168
     */
169
    protected $doctrineTypeMapping = null;
170
171
    /**
172
     * Contains a list of all columns that should generate parseable column comments for type-detection
173
     * in reverse engineering scenarios.
174
     *
175
     * @var array|null
176
     */
177
    protected $doctrineTypeComments = null;
178
179
    /**
180
     * @var \Doctrine\Common\EventManager
181
     */
182
    protected $_eventManager;
183
184
    /**
185
     * Holds the KeywordList instance for the current platform.
186
     *
187
     * @var \Doctrine\DBAL\Platforms\Keywords\KeywordList
188
     */
189
    protected $_keywords;
190
191
    /**
192
     * Constructor.
193
     */
194 62877
    public function __construct()
195
    {
196 62877
    }
197
198
    /**
199
     * Sets the EventManager used by the Platform.
200
     *
201
     * @param \Doctrine\Common\EventManager $eventManager
202
     */
203 1623
    public function setEventManager(EventManager $eventManager)
204
    {
205 1623
        $this->_eventManager = $eventManager;
206 1623
    }
207
208
    /**
209
     * Gets the EventManager used by the Platform.
210
     *
211
     * @return \Doctrine\Common\EventManager
212
     */
213 984
    public function getEventManager()
214
    {
215 984
        return $this->_eventManager;
216
    }
217
218
    /**
219
     * Returns the SQL snippet that declares a boolean column.
220
     *
221
     * @param array $columnDef
222
     *
223
     * @return string
224
     */
225
    abstract public function getBooleanTypeDeclarationSQL(array $columnDef);
226
227
    /**
228
     * Returns the SQL snippet that declares a 4 byte integer column.
229
     *
230
     * @param array $columnDef
231
     *
232
     * @return string
233
     */
234
    abstract public function getIntegerTypeDeclarationSQL(array $columnDef);
235
236
    /**
237
     * Returns the SQL snippet that declares an 8 byte integer column.
238
     *
239
     * @param array $columnDef
240
     *
241
     * @return string
242
     */
243
    abstract public function getBigIntTypeDeclarationSQL(array $columnDef);
244
245
    /**
246
     * Returns the SQL snippet that declares a 2 byte integer column.
247
     *
248
     * @param array $columnDef
249
     *
250
     * @return string
251
     */
252
    abstract public function getSmallIntTypeDeclarationSQL(array $columnDef);
253
254
    /**
255
     * Returns the SQL snippet that declares common properties of an integer column.
256
     *
257
     * @param array $columnDef
258
     *
259
     * @return string
260
     */
261
    abstract protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef);
262
263
    /**
264
     * Lazy load Doctrine Type Mappings.
265
     *
266
     * @return void
267
     */
268
    abstract protected function initializeDoctrineTypeMappings();
269
270
    /**
271
     * Initializes Doctrine Type Mappings with the platform defaults
272
     * and with all additional type mappings.
273
     *
274
     * @return void
275
     */
276 1782
    private function initializeAllDoctrineTypeMappings()
277
    {
278 1782
        $this->initializeDoctrineTypeMappings();
279
280 1782
        foreach (Type::getTypesMap() as $typeName => $className) {
281 1782
            foreach (Type::getType($typeName)->getMappedDatabaseTypes($this) as $dbType) {
282 1782
                $this->doctrineTypeMapping[$dbType] = $typeName;
283
            }
284
        }
285 1782
    }
286
287
    /**
288
     * Returns the SQL snippet used to declare a VARCHAR column type.
289
     *
290
     * @param array $field
291
     *
292
     * @return string
293
     */
294 6251
    public function getVarcharTypeDeclarationSQL(array $field)
295
    {
296 6251
        if ( !isset($field['length'])) {
297 1490
            $field['length'] = $this->getVarcharDefaultLength();
298
        }
299
300 6251
        $fixed = $field['fixed'] ?? false;
301
302 6251
        $maxLength = $fixed
303
            ? $this->getCharMaxLength()
304
            : $this->getVarcharMaxLength();
305
306 6251
        if ($field['length'] > $maxLength) {
307
            return $this->getClobTypeDeclarationSQL($field);
308
        }
309
310
        return $this->getVarcharTypeDeclarationSQLSnippet($field['length'], $fixed);
311
    }
312
313
    /**
314
     * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
315
     *
316 393
     * @param array $field The column definition.
317
     *
318 393
     * @return string
319 342
     */
320
    public function getBinaryTypeDeclarationSQL(array $field)
321
    {
322 393
        if ( ! isset($field['length'])) {
323
            $field['length'] = $this->getBinaryDefaultLength();
324 393
        }
325 345
326
        $fixed = $field['fixed'] ?? false;
327
328 372
        if ($field['length'] > $this->getBinaryMaxLength()) {
329
            return $this->getBlobTypeDeclarationSQL($field);
330
        }
331
332
        return $this->getBinaryTypeDeclarationSQLSnippet($field['length'], $fixed);
333
    }
334
335
    /**
336
     * Returns the SQL snippet to declare a GUID/UUID field.
337
     *
338
     * By default this maps directly to a CHAR(36) and only maps to more
339
     * special datatypes when the underlying databases support this datatype.
340
     *
341 116
     * @param array $field
342
     *
343 116
     * @return string
344 116
     */
345
    public function getGuidTypeDeclarationSQL(array $field)
346 116
    {
347
        $field['length'] = 36;
348
        $field['fixed']  = true;
349
350
        return $this->getVarcharTypeDeclarationSQL($field);
351
    }
352
353
    /**
354
     * Returns the SQL snippet to declare a JSON field.
355
     *
356
     * By default this maps directly to a CLOB and only maps to more
357
     * special datatypes when the underlying databases support this datatype.
358
     *
359 374
     * @param array $field
360
     *
361 374
     * @return string
362
     */
363
    public function getJsonTypeDeclarationSQL(array $field)
364
    {
365
        return $this->getClobTypeDeclarationSQL($field);
366
    }
367
368
    /**
369
     * @param int  $length
370
     * @param bool $fixed
371
     *
372
     * @return string
373
     *
374
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
375
     */
376
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
0 ignored issues
show
Unused Code introduced by
The parameter $fixed is not used and could be removed. ( Ignorable by Annotation )

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

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

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

Loading history...
377
    {
378
        throw DBALException::notSupported('VARCHARs not supported by Platform.');
379
    }
380
381
    /**
382
     * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
383
     *
384
     * @param int  $length The length of the column.
385
     * @param bool $fixed  Whether the column length is fixed.
386
     *
387
     * @return string
388
     *
389
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
390
     */
391
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
0 ignored issues
show
Unused Code introduced by
The parameter $fixed is not used and could be removed. ( Ignorable by Annotation )

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

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

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

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

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
826 540
                $expression = 'LEADING ';
827 420
                break;
828
829
            case TrimMode::TRAILING:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
830 540
                $expression = 'TRAILING ';
831 510
                break;
832
833
            case TrimMode::BOTH:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
834 540
                $expression = 'BOTH ';
835
                break;
836
        }
837
838
        if ($char !== false) {
839
            $expression .= $char . ' ';
0 ignored issues
show
Bug introduced by
Are you sure $char of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

839
            $expression .= /** @scrutinizer ignore-type */ $char . ' ';
Loading history...
840
        }
841
842
        if ($mode || $char !== false) {
843
            $expression .= 'FROM ';
844 72
        }
845
846 72
        return 'TRIM(' . $expression . $str . ')';
847
    }
848
849
    /**
850
     * Returns the SQL snippet to trim trailing space characters from the expression.
851
     *
852
     * @param string $str Literal string or column name.
853
     *
854
     * @return string
855
     */
856 72
    public function getRtrimExpression($str)
857
    {
858 72
        return 'RTRIM(' . $str . ')';
859
    }
860
861
    /**
862
     * Returns the SQL snippet to trim leading space characters from the expression.
863
     *
864
     * @param string $str Literal string or column name.
865
     *
866
     * @return string
867
     */
868
    public function getLtrimExpression($str)
869
    {
870
        return 'LTRIM(' . $str . ')';
871
    }
872
873
    /**
874
     * Returns the SQL snippet to change all characters from the expression to uppercase,
875
     * according to the current character set mapping.
876
     *
877
     * @param string $str Literal string or column name.
878
     *
879
     * @return string
880
     */
881
    public function getUpperExpression($str)
882
    {
883
        return 'UPPER(' . $str . ')';
884
    }
885
886
    /**
887
     * Returns the SQL snippet to change all characters from the expression to lowercase,
888
     * according to the current character set mapping.
889
     *
890
     * @param string $str Literal string or column name.
891
     *
892
     * @return string
893
     */
894
    public function getLowerExpression($str)
895
    {
896
        return 'LOWER(' . $str . ')';
897
    }
898
899
    /**
900
     * Returns the SQL snippet to get the position of the first occurrence of substring $substr in string $str.
901
     *
902
     * @param string   $str      Literal string.
903
     * @param string   $substr   Literal string to find.
904
     * @param int|bool $startPos Position to start at, beginning of string by default.
905
     *
906
     * @return string
907
     *
908
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
909
     */
910
    public function getLocateExpression($str, $substr, $startPos = false)
0 ignored issues
show
Unused Code introduced by
The parameter $startPos is not used and could be removed. ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
1340 305
    {
1341
        throw DBALException::notSupported(__METHOD__);
1342 305
    }
1343
1344
    /**
1345
     * Returns the SQL bit AND comparison expression.
1346
     *
1347
     * @param string $value1
1348
     * @param string $value2
1349
     *
1350
     * @return string
1351
     */
1352
    public function getBitAndComparisonExpression($value1, $value2)
1353 305
    {
1354
        return '(' . $value1 . ' & ' . $value2 . ')';
1355 305
    }
1356
1357
    /**
1358
     * Returns the SQL bit OR comparison expression.
1359
     *
1360
     * @param string $value1
1361
     * @param string $value2
1362
     *
1363 30
     * @return string
1364
     */
1365 30
    public function getBitOrComparisonExpression($value1, $value2)
1366
    {
1367
        return '(' . $value1 . ' | ' . $value2 . ')';
1368
    }
1369
1370
    /**
1371
     * Returns the FOR UPDATE expression.
1372
     *
1373
     * @return string
1374
     */
1375
    public function getForUpdateSQL()
1376
    {
1377 30
        return 'FOR UPDATE';
1378
    }
1379 30
1380
    /**
1381
     * Honors that some SQL vendors such as MsSql use table hints for locking instead of the ANSI SQL FOR UPDATE specification.
1382
     *
1383
     * @param string   $fromClause The FROM clause to append the hint for the given lock mode to.
1384
     * @param int|null $lockMode   One of the Doctrine\DBAL\LockMode::* constants. If null is given, nothing will
1385
     *                             be appended to the FROM clause.
1386
     *
1387
     * @return string
1388
     */
1389
    public function appendLockHint($fromClause, $lockMode)
0 ignored issues
show
Unused Code introduced by
The parameter $lockMode is not used and could be removed. ( Ignorable by Annotation )

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

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

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

Loading history...
1390
    {
1391
        return $fromClause;
1392
    }
1393
1394
    /**
1395
     * Returns the SQL snippet to append to any SELECT statement which locks rows in shared read lock.
1396
     *
1397
     * This defaults to the ANSI SQL "FOR UPDATE", which is an exclusive lock (Write). Some database
1398
     * vendors allow to lighten this constraint up to be a real read lock.
1399
     *
1400
     * @return string
1401
     */
1402 34
    public function getReadLockSQL()
1403
    {
1404 34
        return $this->getForUpdateSQL();
1405
    }
1406
1407
    /**
1408
     * Returns the SQL snippet to append to any SELECT statement which obtains an exclusive lock on the rows.
1409
     *
1410
     * The semantics of this lock mode should equal the SELECT .. FOR UPDATE of the ANSI SQL standard.
1411
     *
1412
     * @return string
1413
     */
1414 102
    public function getWriteLockSQL()
1415
    {
1416 102
        return $this->getForUpdateSQL();
1417
    }
1418
1419
    /**
1420
     * Returns the SQL snippet to drop an existing database.
1421
     *
1422
     * @param string $database The name of the database that should be dropped.
1423
     *
1424
     * @return string
1425
     */
1426
    public function getDropDatabaseSQL($database)
1427
    {
1428 2713
        return 'DROP DATABASE ' . $database;
1429
    }
1430 2713
1431
    /**
1432 2713
     * Returns the SQL snippet to drop an existing table.
1433 265
     *
1434 2461
     * @param \Doctrine\DBAL\Schema\Table|string $table
1435
     *
1436
     * @return string
1437
     *
1438 2713
     * @throws \InvalidArgumentException
1439 324
     */
1440 324
    public function getDropTableSQL($table)
1441
    {
1442 324
        $tableArg = $table;
1443
1444
        if ($table instanceof Table) {
1445
            $table = $table->getQuotedName($this);
1446
        } elseif (!is_string($table)) {
0 ignored issues
show
introduced by
The condition is_string($table) is always true.
Loading history...
1447 2713
            throw new \InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.');
1448
        }
1449
1450
        if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaDropTable)) {
1451
            $eventArgs = new SchemaDropTableEventArgs($tableArg, $this);
1452
            $this->_eventManager->dispatchEvent(Events::onSchemaDropTable, $eventArgs);
1453
1454
            if ($eventArgs->isDefaultPrevented()) {
1455
                return $eventArgs->getSql();
1456
            }
1457 18
        }
1458
1459 18
        return 'DROP TABLE ' . $table;
1460
    }
1461
1462
    /**
1463
     * Returns the SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction.
1464
     *
1465
     * @param \Doctrine\DBAL\Schema\Table|string $table
1466
     *
1467
     * @return string
1468
     */
1469
    public function getDropTemporaryTableSQL($table)
1470
    {
1471
        return $this->getDropTableSQL($table);
1472 167
    }
1473
1474 167
    /**
1475 159
     * Returns the SQL to drop an index from a table.
1476 8
     *
1477
     * @param \Doctrine\DBAL\Schema\Index|string $index
1478
     * @param \Doctrine\DBAL\Schema\Table|string $table
1479
     *
1480 167
     * @return string
1481
     *
1482
     * @throws \InvalidArgumentException
1483
     */
1484
    public function getDropIndexSQL($index, $table = null)
1485
    {
1486
        if ($index instanceof Index) {
1487
            $index = $index->getQuotedName($this);
1488
        } elseif (!is_string($index)) {
0 ignored issues
show
introduced by
The condition is_string($index) is always true.
Loading history...
1489
            throw new \InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.');
1490
        }
1491 745
1492
        return 'DROP INDEX ' . $index;
1493 745
    }
1494 559
1495
    /**
1496
     * Returns the SQL to drop a constraint.
1497 745
     *
1498 745
     * @param \Doctrine\DBAL\Schema\Constraint|string $constraint
1499
     * @param \Doctrine\DBAL\Schema\Table|string      $table
1500
     *
1501 745
     * @return string
1502 745
     */
1503
    public function getDropConstraintSQL($constraint, $table)
1504 745
    {
1505
        if (! $constraint instanceof Constraint) {
1506
            $constraint = new Identifier($constraint);
1507
        }
1508
1509
        if (! $table instanceof Table) {
1510
            $table = new Identifier($table);
1511
        }
1512
1513
        $constraint = $constraint->getQuotedName($this);
1514
        $table = $table->getQuotedName($this);
1515 392
1516
        return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $constraint;
1517 392
    }
1518 144
1519
    /**
1520
     * Returns the SQL to drop a foreign key.
1521 392
     *
1522 392
     * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint|string $foreignKey
1523
     * @param \Doctrine\DBAL\Schema\Table|string                $table
1524
     *
1525 392
     * @return string
1526 392
     */
1527
    public function getDropForeignKeySQL($foreignKey, $table)
1528 392
    {
1529
        if (! $foreignKey instanceof ForeignKeyConstraint) {
1530
            $foreignKey = new Identifier($foreignKey);
1531
        }
1532
1533
        if (! $table instanceof Table) {
1534
            $table = new Identifier($table);
1535
        }
1536
1537
        $foreignKey = $foreignKey->getQuotedName($this);
1538
        $table = $table->getQuotedName($this);
1539
1540
        return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $foreignKey;
1541
    }
1542
1543 7778
    /**
1544
     * Returns the SQL statement(s) to create a table with the specified name, columns and constraints
1545 7778
     * on this platform.
1546
     *
1547
     * @param \Doctrine\DBAL\Schema\Table $table
1548
     * @param int                         $createFlags
1549 7778
     *
1550 324
     * @return array The sequence of SQL statements.
1551
     *
1552
     * @throws \Doctrine\DBAL\DBALException
1553 7454
     * @throws \InvalidArgumentException
1554 7454
     */
1555 7454
    public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDEXES)
1556 7454
    {
1557 7454
        if ( ! is_int($createFlags)) {
0 ignored issues
show
introduced by
The condition is_int($createFlags) is always true.
Loading history...
1558
            throw new \InvalidArgumentException("Second argument of AbstractPlatform::getCreateTableSQL() has to be integer.");
1559 7454
        }
1560 7040
1561
        if (count($table->getColumns()) === 0) {
1562 4734
            throw DBALException::noColumnsSpecifiedForTable($table->getName());
1563 3535
        }
1564 3535
1565
        $tableName = $table->getQuotedName($this);
1566 4734
        $options = $table->getOptions();
1567
        $options['uniqueConstraints'] = [];
1568
        $options['indexes'] = [];
1569
        $options['primary'] = [];
1570
1571 7454
        if (($createFlags&self::CREATE_INDEXES) > 0) {
1572 7454
            foreach ($table->getIndexes() as $index) {
1573
                /* @var $index Index */
1574 7454
                if ($index->isPrimary()) {
1575
                    $options['primary']       = $index->getQuotedColumns($this);
1576
                    $options['primary_index'] = $index;
1577 7454
                } else {
1578 324
                    $options['indexes'][$index->getQuotedName($this)] = $index;
1579 324
                }
1580
            }
1581 324
        }
1582
1583 324
        $columnSql = [];
1584
        $columns = [];
1585
1586
        foreach ($table->getColumns() as $column) {
1587
            /* @var \Doctrine\DBAL\Schema\Column $column */
1588 7454
1589 7454
            if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn)) {
1590 7454
                $eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this);
1591 7454
                $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs);
1592
1593 7454
                $columnSql = array_merge($columnSql, $eventArgs->getSql());
1594 2799
1595
                if ($eventArgs->isDefaultPrevented()) {
1596
                    continue;
1597 7454
                }
1598 3175
            }
1599
1600
            $columnData = $column->toArray();
1601 7454
            $columnData['name'] = $column->getQuotedName($this);
1602
            $columnData['version'] = $column->hasPlatformOption("version") ? $column->getPlatformOption('version') : false;
1603
            $columnData['comment'] = $this->getColumnComment($column);
1604 7454
1605 3437
            if ($columnData['type'] instanceof Types\StringType && $columnData['length'] === null) {
1606 3437
                $columnData['length'] = 255;
1607 696
            }
1608
1609
            if (in_array($column->getName(), $options['primary'])) {
1610
                $columnData['primary'] = true;
1611 7454
            }
1612 324
1613 324
            $columns[$columnData['name']] = $columnData;
1614
        }
1615 324
1616
        if (($createFlags&self::CREATE_FOREIGNKEYS) > 0) {
1617
            $options['foreignKeys'] = [];
1618
            foreach ($table->getForeignKeys() as $fkConstraint) {
1619
                $options['foreignKeys'][] = $fkConstraint;
1620 7454
            }
1621 7454
        }
1622 3606
1623 3606
        if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) {
1624
            $eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this);
1625 3606
            $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs);
1626 3606
1627
            if ($eventArgs->isDefaultPrevented()) {
1628
                return array_merge($eventArgs->getSql(), $columnSql);
1629
            }
1630
        }
1631 7454
1632
        $sql = $this->_getCreateTableSQL($tableName, $columns, $options);
1633
        if ($this->supportsCommentOnStatement()) {
1634
            foreach ($table->getColumns() as $column) {
1635
                $comment = $this->getColumnComment($column);
1636
1637
                if (null !== $comment && '' !== $comment) {
1638
                    $sql[] = $this->getCommentOnColumnSQL($tableName, $column->getQuotedName($this), $comment);
1639
                }
1640
            }
1641 644
        }
1642
1643 644
        return array_merge($sql, $columnSql);
1644 644
    }
1645 644
1646
    /**
1647 644
     * @param string $tableName
1648 644
     * @param string $columnName
1649
     * @param string $comment
1650
     *
1651
     * @return string
1652
     */
1653
    public function getCommentOnColumnSQL($tableName, $columnName, $comment)
1654
    {
1655
        $tableName = new Identifier($tableName);
1656
        $columnName = new Identifier($columnName);
1657
        $comment = $this->quoteStringLiteral($comment);
1658
1659
        return "COMMENT ON COLUMN " . $tableName->getQuotedName($this) . "." . $columnName->getQuotedName($this) .
1660 1006
            " IS " . $comment;
1661
    }
1662 1006
1663 252
    /**
1664
     * Returns the SQL to create inline comment on a column.
1665
     *
1666 754
     * @param string $comment
1667
     *
1668
     * @return string
1669
     *
1670
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1671
     */
1672
    public function getInlineColumnCommentSQL($comment)
1673
    {
1674
        if (! $this->supportsInlineColumnComments()) {
1675
            throw DBALException::notSupported(__METHOD__);
1676
        }
1677
1678 550
        return "COMMENT " . $this->quoteStringLiteral($comment);
1679
    }
1680 550
1681
    /**
1682 550
     * Returns the SQL used to create a table.
1683
     *
1684
     * @param string $tableName
1685
     * @param array  $columns
1686
     * @param array  $options
1687
     *
1688 550
     * @return array
1689 269
     */
1690
    protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
1691
    {
1692 550
        $columnListSql = $this->getColumnDeclarationListSQL($columns);
1693
1694
        if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
1695
            foreach ($options['uniqueConstraints'] as $name => $definition) {
1696
                $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
1697
            }
1698 550
        }
1699
1700 550
        if (isset($options['primary']) && ! empty($options['primary'])) {
1701 550
            $columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')';
1702 18
        }
1703
1704 550
        if (isset($options['indexes']) && ! empty($options['indexes'])) {
1705
            foreach ($options['indexes'] as $index => $definition) {
1706 550
                $columnListSql .= ', ' . $this->getIndexDeclarationSQL($index, $definition);
1707
            }
1708 550
        }
1709 181
1710 63
        $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql;
1711
1712
        $check = $this->getCheckDeclarationSQL($columns);
1713
        if ( ! empty($check)) {
1714 550
            $query .= ', ' . $check;
1715
        }
1716
        $query .= ')';
1717
1718
        $sql[] = $query;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$sql was never initialized. Although not strictly required by PHP, it is generally a good practice to add $sql = array(); before regardless.
Loading history...
1719
1720 30
        if (isset($options['foreignKeys'])) {
1721
            foreach ((array) $options['foreignKeys'] as $definition) {
1722 30
                $sql[] = $this->getCreateForeignKeySQL($definition, $tableName);
1723
            }
1724
        }
1725
1726
        return $sql;
1727
    }
1728
1729
    /**
1730
     * @return string
1731
     */
1732
    public function getCreateTemporaryTableSnippetSQL()
1733
    {
1734
        return "CREATE TEMPORARY TABLE";
1735
    }
1736
1737
    /**
1738
     * Returns the SQL to create a sequence on this platform.
1739
     *
1740
     * @param \Doctrine\DBAL\Schema\Sequence $sequence
1741
     *
1742
     * @return string
1743
     *
1744
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1745
     */
1746
    public function getCreateSequenceSQL(Sequence $sequence)
0 ignored issues
show
Unused Code introduced by
The parameter $sequence is not used and could be removed. ( Ignorable by Annotation )

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

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

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

Loading history...
1747
    {
1748
        throw DBALException::notSupported(__METHOD__);
1749
    }
1750
1751
    /**
1752
     * Returns the SQL to change a sequence on this platform.
1753
     *
1754
     * @param \Doctrine\DBAL\Schema\Sequence $sequence
1755
     *
1756
     * @return string
1757
     *
1758
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1759
     */
1760
    public function getAlterSequenceSQL(Sequence $sequence)
0 ignored issues
show
Unused Code introduced by
The parameter $sequence is not used and could be removed. ( Ignorable by Annotation )

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

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

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

Loading history...
1761
    {
1762
        throw DBALException::notSupported(__METHOD__);
1763 311
    }
1764
1765 311
    /**
1766
     * Returns the SQL to create a constraint on a table on this platform.
1767
     *
1768
     * @param \Doctrine\DBAL\Schema\Constraint   $constraint
1769 311
     * @param \Doctrine\DBAL\Schema\Table|string $table
1770
     *
1771 311
     * @return string
1772
     *
1773 311
     * @throws \InvalidArgumentException
1774 311
     */
1775 311
    public function getCreateConstraintSQL(Constraint $constraint, $table)
1776 311
    {
1777 234
        if ($table instanceof Table) {
1778 234
            $table = $table->getQuotedName($this);
1779
        }
1780
1781 311
        $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this);
1782
1783
        $columnList = '('. implode(', ', $constraint->getQuotedColumns($this)) . ')';
1784 234
1785 234
        $referencesClause = '';
1786
        if ($constraint instanceof Index) {
1787 234
            if ($constraint->isPrimary()) {
1788 234
                $query .= ' PRIMARY KEY';
1789
            } elseif ($constraint->isUnique()) {
1790 311
                $query .= ' UNIQUE';
1791
            } else {
1792 311
                throw new \InvalidArgumentException(
1793
                    'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().'
1794
                );
1795
            }
1796
        } elseif ($constraint instanceof ForeignKeyConstraint) {
1797
            $query .= ' FOREIGN KEY';
1798
1799
            $referencesClause = ' REFERENCES ' . $constraint->getQuotedForeignTableName($this) .
1800
                ' (' . implode(', ', $constraint->getQuotedForeignColumns($this)) . ')';
1801
        }
1802
        $query .= ' '.$columnList.$referencesClause;
1803
1804
        return $query;
1805 2876
    }
1806
1807 2876
    /**
1808 18
     * Returns the SQL to create an index on a table on this platform.
1809
     *
1810 2876
     * @param \Doctrine\DBAL\Schema\Index        $index
1811 2876
     * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table on which the index is to be created.
1812
     *
1813 2876
     * @return string
1814
     *
1815
     * @throws \InvalidArgumentException
1816
     */
1817 2876
    public function getCreateIndexSQL(Index $index, $table)
1818 378
    {
1819
        if ($table instanceof Table) {
1820
            $table = $table->getQuotedName($this);
1821 2534
        }
1822 2534
        $name = $index->getQuotedName($this);
1823
        $columns = $index->getQuotedColumns($this);
1824 2534
1825
        if (count($columns) == 0) {
1826
            throw new \InvalidArgumentException("Incomplete definition. 'columns' required.");
1827
        }
1828
1829
        if ($index->isPrimary()) {
1830
            return $this->getCreatePrimaryKeySQL($index, $table);
1831
        }
1832
1833
        $query = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table;
1834 3486
        $query .= ' (' . $this->getIndexFieldDeclarationListSQL($columns) . ')' . $this->getPartialIndexSQL($index);
1835
1836 3486
        return $query;
1837 96
    }
1838
1839
    /**
1840 3390
     * Adds condition for partial index.
1841
     *
1842
     * @param \Doctrine\DBAL\Schema\Index $index
1843
     *
1844
     * @return string
1845
     */
1846
    protected function getPartialIndexSQL(Index $index)
1847
    {
1848
        if ($this->supportsPartialIndexes() && $index->hasOption('where')) {
1849
            return  ' WHERE ' . $index->getOption('where');
1850 1244
        }
1851
1852 1244
        return '';
1853
    }
1854
1855
    /**
1856
     * Adds additional flags for index generation.
1857
     *
1858
     * @param \Doctrine\DBAL\Schema\Index $index
1859
     *
1860
     * @return string
1861
     */
1862
    protected function getCreateIndexSQLFlags(Index $index)
1863 306
    {
1864
        return $index->isUnique() ? 'UNIQUE ' : '';
1865 306
    }
1866
1867
    /**
1868
     * Returns the SQL to create an unnamed primary key constraint.
1869
     *
1870
     * @param \Doctrine\DBAL\Schema\Index        $index
1871
     * @param \Doctrine\DBAL\Schema\Table|string $table
1872
     *
1873
     * @return string
1874
     */
1875
    public function getCreatePrimaryKeySQL(Index $index, $table)
1876 180
    {
1877
        return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index->getQuotedColumns($this)) . ')';
0 ignored issues
show
Bug introduced by
Are you sure $table of type Doctrine\DBAL\Schema\Table|string can be used in concatenation? ( Ignorable by Annotation )

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

1877
        return 'ALTER TABLE ' . /** @scrutinizer ignore-type */ $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index->getQuotedColumns($this)) . ')';
Loading history...
1878 180
    }
1879
1880
    /**
1881
     * Returns the SQL to create a named schema.
1882
     *
1883
     * @param string $schemaName
1884
     *
1885
     * @return string
1886
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1887
     */
1888
    public function getCreateSchemaSQL($schemaName)
0 ignored issues
show
Unused Code introduced by
The parameter $schemaName is not used and could be removed. ( Ignorable by Annotation )

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

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

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

Loading history...
1889
    {
1890
        throw DBALException::notSupported(__METHOD__);
1891
    }
1892
1893
    /**
1894 7176
     * Quotes a string so that it can be safely used as a table or column name,
1895
     * even if it is a reserved word of the platform. This also detects identifier
1896 7176
     * chains separated by dot and quotes them independently.
1897 354
     *
1898
     * NOTE: Just because you CAN use quoted identifiers doesn't mean
1899 354
     * you SHOULD use them. In general, they end up causing way more
1900
     * problems than they solve.
1901
     *
1902 7176
     * @param string $str The identifier name to be quoted.
1903
     *
1904
     * @return string The quoted identifier string.
1905
     */
1906
    public function quoteIdentifier($str)
1907
    {
1908
        if (strpos($str, ".") !== false) {
1909
            $parts = array_map([$this, "quoteSingleIdentifier"], explode(".", $str));
1910
1911
            return implode(".", $parts);
1912 6666
        }
1913
1914 6666
        return $this->quoteSingleIdentifier($str);
1915
    }
1916 6666
1917
    /**
1918
     * Quotes a single identifier (no dot chain separation).
1919
     *
1920
     * @param string $str The identifier name to be quoted.
1921
     *
1922
     * @return string The quoted identifier string.
1923
     */
1924
    public function quoteSingleIdentifier($str)
1925
    {
1926
        $c = $this->getIdentifierQuoteCharacter();
1927 1610
1928
        return $c . str_replace($c, $c.$c, $str) . $c;
1929 1610
    }
1930 18
1931
    /**
1932
     * Returns the SQL to create a new foreign key.
1933 1610
     *
1934
     * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey The foreign key constraint.
1935 1610
     * @param \Doctrine\DBAL\Schema\Table|string         $table      The name of the table on which the foreign key is to be created.
1936
     *
1937
     * @return string
1938
     */
1939
    public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table)
1940
    {
1941
        if ($table instanceof Table) {
1942
            $table = $table->getQuotedName($this);
1943
        }
1944
1945
        $query = 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey);
1946
1947
        return $query;
1948
    }
1949
1950
    /**
1951
     * Gets the SQL statements for altering an existing table.
1952
     *
1953
     * This method returns an array of SQL statements, since some platforms need several statements.
1954
     *
1955
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
1956
     *
1957
     * @return array
1958
     *
1959
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1960
     */
1961 1786
    public function getAlterTableSQL(TableDiff $diff)
1962
    {
1963 1786
        throw DBALException::notSupported(__METHOD__);
1964 1440
    }
1965
1966
    /**
1967 346
     * @param \Doctrine\DBAL\Schema\Column    $column
1968 22
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
1969
     * @param array                           $columnSql
1970
     *
1971 324
     * @return bool
1972 324
     */
1973
    protected function onSchemaAlterTableAddColumn(Column $column, TableDiff $diff, &$columnSql)
1974 324
    {
1975
        if (null === $this->_eventManager) {
1976 324
            return false;
1977
        }
1978
1979
        if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableAddColumn)) {
1980
            return false;
1981
        }
1982
1983
        $eventArgs = new SchemaAlterTableAddColumnEventArgs($column, $diff, $this);
1984
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn, $eventArgs);
1985
1986 1374
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1987
1988 1374
        return $eventArgs->isDefaultPrevented();
1989 1026
    }
1990
1991
    /**
1992 348
     * @param \Doctrine\DBAL\Schema\Column    $column
1993 24
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
1994
     * @param array                           $columnSql
1995
     *
1996 324
     * @return bool
1997 324
     */
1998
    protected function onSchemaAlterTableRemoveColumn(Column $column, TableDiff $diff, &$columnSql)
1999 324
    {
2000
        if (null === $this->_eventManager) {
2001 324
            return false;
2002
        }
2003
2004
        if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRemoveColumn)) {
2005
            return false;
2006
        }
2007
2008
        $eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column, $diff, $this);
2009
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn, $eventArgs);
2010
2011 3473
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
2012
2013 3473
        return $eventArgs->isDefaultPrevented();
2014 2898
    }
2015
2016
    /**
2017 575
     * @param \Doctrine\DBAL\Schema\ColumnDiff $columnDiff
2018 251
     * @param \Doctrine\DBAL\Schema\TableDiff  $diff
2019
     * @param array                            $columnSql
2020
     *
2021 324
     * @return bool
2022 324
     */
2023
    protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiff, TableDiff $diff, &$columnSql)
2024 324
    {
2025
        if (null === $this->_eventManager) {
2026 324
            return false;
2027
        }
2028
2029
        if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) {
2030
            return false;
2031
        }
2032
2033
        $eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff, $diff, $this);
2034
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn, $eventArgs);
2035
2036
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
2037 1389
2038
        return $eventArgs->isDefaultPrevented();
2039 1389
    }
2040 1044
2041
    /**
2042
     * @param string                          $oldColumnName
2043 345
     * @param \Doctrine\DBAL\Schema\Column    $column
2044 21
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
2045
     * @param array                           $columnSql
2046
     *
2047 324
     * @return bool
2048 324
     */
2049
    protected function onSchemaAlterTableRenameColumn($oldColumnName, Column $column, TableDiff $diff, &$columnSql)
2050 324
    {
2051
        if (null === $this->_eventManager) {
2052 324
            return false;
2053
        }
2054
2055
        if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRenameColumn)) {
2056
            return false;
2057
        }
2058
2059
        $eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName, $column, $diff, $this);
2060
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn, $eventArgs);
2061 6600
2062
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
2063 6600
2064 5940
        return $eventArgs->isDefaultPrevented();
2065
    }
2066
2067 660
    /**
2068 336
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
2069
     * @param array                           $sql
2070
     *
2071 324
     * @return bool
2072 324
     */
2073
    protected function onSchemaAlterTable(TableDiff $diff, &$sql)
2074 324
    {
2075
        if (null === $this->_eventManager) {
2076 324
            return false;
2077
        }
2078
2079
        if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTable)) {
2080
            return false;
2081
        }
2082
2083
        $eventArgs = new SchemaAlterTableEventArgs($diff, $this);
2084 6347
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTable, $eventArgs);
2085
2086 6347
        $sql = array_merge($sql, $eventArgs->getSql());
2087
2088 6347
        return $eventArgs->isDefaultPrevented();
2089 6347
    }
2090 6347
2091 467
    /**
2092
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
2093 6347
     *
2094 360
     * @return array
2095
     */
2096
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
2097
    {
2098 6347
        $tableName = $diff->getName($this)->getQuotedName($this);
2099 231
2100
        $sql = [];
2101 6347
        if ($this->supportsForeignKeyConstraints()) {
2102 322
            foreach ($diff->removedForeignKeys as $foreignKey) {
2103
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
2104
            }
2105 6347
            foreach ($diff->changedForeignKeys as $foreignKey) {
2106
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
2107
            }
2108
        }
2109
2110
        foreach ($diff->removedIndexes as $index) {
2111
            $sql[] = $this->getDropIndexSQL($index, $tableName);
2112
        }
2113 6347
        foreach ($diff->changedIndexes as $index) {
2114
            $sql[] = $this->getDropIndexSQL($index, $tableName);
2115 6347
        }
2116 615
2117 6347
        return $sql;
2118
    }
2119 6347
2120
    /**
2121 6347
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
2122 6347
     *
2123 394
     * @return array
2124
     */
2125
    protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff)
2126 6347
    {
2127 360
        $tableName = (false !== $diff->newName)
2128
            ? $diff->getNewName()->getQuotedName($this)
2129
            : $diff->getName($this)->getQuotedName($this);
2130
2131 6347
        $sql = [];
2132 71
2133
        if ($this->supportsForeignKeyConstraints()) {
2134
            foreach ($diff->addedForeignKeys as $foreignKey) {
2135 6347
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
2136 322
            }
2137
2138
            foreach ($diff->changedForeignKeys as $foreignKey) {
2139 6347
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
2140 1564
            }
2141 1564
        }
2142 1564
2143 1564
        foreach ($diff->addedIndexes as $index) {
2144
            $sql[] = $this->getCreateIndexSQL($index, $tableName);
2145
        }
2146
2147 6347
        foreach ($diff->changedIndexes as $index) {
2148
            $sql[] = $this->getCreateIndexSQL($index, $tableName);
2149
        }
2150
2151
        foreach ($diff->renamedIndexes as $oldIndexName => $index) {
2152
            $oldIndexName = new Identifier($oldIndexName);
2153
            $sql          = array_merge(
2154
                $sql,
2155
                $this->getRenameIndexSQL($oldIndexName->getQuotedName($this), $index, $tableName)
2156
            );
2157
        }
2158
2159 192
        return $sql;
2160
    }
2161
2162 192
    /**
2163 192
     * Returns the SQL for renaming an index on a table.
2164
     *
2165
     * @param string                      $oldIndexName The name of the index to rename from.
2166
     * @param \Doctrine\DBAL\Schema\Index $index        The definition of the index to rename to.
2167
     * @param string                      $tableName    The table to rename the given index on.
2168
     *
2169
     * @return array The sequence of SQL statements for renaming the given index.
2170
     */
2171
    protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
2172
    {
2173
        return [
2174
            $this->getDropIndexSQL($oldIndexName, $tableName),
2175
            $this->getCreateIndexSQL($index, $tableName)
2176
        ];
2177
    }
2178
2179
    /**
2180
     * Common code for alter table statement generation that updates the changed Index and Foreign Key definitions.
2181
     *
2182
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
2183
     *
2184
     * @return array
2185
     */
2186
    protected function _getAlterTableIndexForeignKeySQL(TableDiff $diff)
2187
    {
2188
        return array_merge($this->getPreAlterTableIndexForeignKeySQL($diff), $this->getPostAlterTableIndexForeignKeySQL($diff));
2189
    }
2190
2191
    /**
2192
     * Gets declaration of a number of fields in bulk.
2193
     *
2194
     * @param array $fields A multidimensional associative array.
2195
     *                      The first dimension determines the field name, while the second
2196
     *                      dimension is keyed with the name of the properties
2197
     *                      of the field being declared as array indexes. Currently, the types
2198
     *                      of supported field properties are as follows:
2199
     *
2200
     *      length
2201
     *          Integer value that determines the maximum length of the text
2202
     *          field. If this argument is missing the field should be
2203
     *          declared to have the longest length allowed by the DBMS.
2204
     *
2205
     *      default
2206
     *          Text value to be used as default for this field.
2207
     *
2208 7454
     *      notnull
2209
     *          Boolean flag that indicates whether this field is constrained
2210 7454
     *          to not be set to null.
2211
     *      charset
2212 7454
     *          Text value with the default CHARACTER SET for this field.
2213 7454
     *      collation
2214
     *          Text value with the default COLLATION for this field.
2215
     *      unique
2216 7454
     *          unique constraint
2217
     *
2218
     * @return string
2219
     */
2220
    public function getColumnDeclarationListSQL(array $fields)
2221
    {
2222
        $queryFields = [];
2223
2224
        foreach ($fields as $fieldName => $field) {
2225
            $queryFields[] = $this->getColumnDeclarationSQL($fieldName, $field);
2226
        }
2227
2228
        return implode(', ', $queryFields);
2229
    }
2230
2231
    /**
2232
     * Obtains DBMS specific SQL code portion needed to declare a generic type
2233
     * field to be used in statements like CREATE TABLE.
2234
     *
2235
     * @param string $name  The name the field to be declared.
2236
     * @param array  $field An associative array with the name of the properties
2237
     *                      of the field being declared as array indexes. Currently, the types
2238
     *                      of supported field properties are as follows:
2239
     *
2240
     *      length
2241
     *          Integer value that determines the maximum length of the text
2242
     *          field. If this argument is missing the field should be
2243
     *          declared to have the longest length allowed by the DBMS.
2244
     *
2245
     *      default
2246
     *          Text value to be used as default for this field.
2247
     *
2248
     *      notnull
2249
     *          Boolean flag that indicates whether this field is constrained
2250
     *          to not be set to null.
2251
     *      charset
2252 7878
     *          Text value with the default CHARACTER SET for this field.
2253
     *      collation
2254 7878
     *          Text value with the default COLLATION for this field.
2255 261
     *      unique
2256
     *          unique constraint
2257 7626
     *      check
2258
     *          column check constraint
2259 7626
     *      columnDefinition
2260 7626
     *          a string that defines the complete column
2261
     *
2262 7626
     * @return string DBMS specific SQL code portion that should be used to declare the column.
2263 7626
     */
2264
    public function getColumnDeclarationSQL($name, array $field)
2265 7626
    {
2266
        if (isset($field['columnDefinition'])) {
2267 7626
            $columnDef = $this->getCustomTypeDeclarationSQL($field);
2268 7626
        } else {
2269
            $default = $this->getDefaultValueDeclarationSQL($field);
2270 7626
2271 7626
            $charset = (isset($field['charset']) && $field['charset']) ?
2272
                ' ' . $this->getColumnCharsetDeclarationSQL($field['charset']) : '';
2273 7626
2274 7626
            $collation = (isset($field['collation']) && $field['collation']) ?
2275
                ' ' . $this->getColumnCollationDeclarationSQL($field['collation']) : '';
2276 7626
2277 678
            $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : '';
2278
2279
            $unique = (isset($field['unique']) && $field['unique']) ?
2280
                ' ' . $this->getUniqueFieldDeclarationSQL() : '';
2281 7878
2282
            $check = (isset($field['check']) && $field['check']) ?
2283
                ' ' . $field['check'] : '';
2284
2285
            $typeDecl = $field['type']->getSQLDeclaration($field, $this);
2286
            $columnDef = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation;
2287
2288
            if ($this->supportsInlineColumnComments() && isset($field['comment']) && $field['comment'] !== '') {
2289
                $columnDef .= ' ' . $this->getInlineColumnCommentSQL($field['comment']);
2290
            }
2291 2418
        }
2292
2293 2418
        return $name . ' ' . $columnDef;
2294 2418
    }
2295 2418
2296 2418
    /**
2297
     * Returns the SQL snippet that declares a floating point column of arbitrary precision.
2298 2418
     *
2299
     * @param array $columnDef
2300
     *
2301
     * @return string
2302
     */
2303
    public function getDecimalTypeDeclarationSQL(array $columnDef)
2304
    {
2305
        $columnDef['precision'] = ( ! isset($columnDef['precision']) || empty($columnDef['precision']))
2306
            ? 10 : $columnDef['precision'];
2307
        $columnDef['scale'] = ( ! isset($columnDef['scale']) || empty($columnDef['scale']))
2308
            ? 0 : $columnDef['scale'];
2309 8674
2310
        return 'NUMERIC(' . $columnDef['precision'] . ', ' . $columnDef['scale'] . ')';
2311 8674
    }
2312 7463
2313
    /**
2314
     * Obtains DBMS specific SQL code portion needed to set a default value
2315 1631
     * declaration to be used in statements like CREATE TABLE.
2316
     *
2317 1631
     * @param array $field The field definition array.
2318
     *
2319
     * @return string DBMS specific SQL code portion needed to set a default value.
2320
     */
2321 1631
    public function getDefaultValueDeclarationSQL($field)
2322
    {
2323 1631
        if ( ! isset($field['default'])) {
2324 445
            return empty($field['notnull']) ? ' DEFAULT NULL' : '';
2325
        }
2326
2327 1232
        $default = $field['default'];
2328 288
2329
        if ( ! isset($field['type'])) {
2330
            return " DEFAULT '" . $default . "'";
2331 954
        }
2332 2
2333
        $type = $field['type'];
2334
2335 954
        if ($type instanceof Types\PhpIntegerMappingType) {
2336 272
            return ' DEFAULT ' . $default;
2337
        }
2338
2339 682
        if ($type instanceof Types\PhpDateTimeMappingType && $default === $this->getCurrentTimestampSQL()) {
2340 276
            return ' DEFAULT ' . $this->getCurrentTimestampSQL();
2341
        }
2342
2343 676
        if ($type instanceof Types\TimeType && $default === $this->getCurrentTimeSQL()) {
2344
            return ' DEFAULT ' . $this->getCurrentTimeSQL();
2345
        }
2346
2347
        if ($type instanceof Types\DateType && $default === $this->getCurrentDateSQL()) {
2348
            return ' DEFAULT ' . $this->getCurrentDateSQL();
2349
        }
2350
2351
        if ($type instanceof Types\BooleanType) {
2352
            return " DEFAULT '" . $this->convertBooleans($default) . "'";
2353
        }
2354 2600
2355
        return " DEFAULT '" . $default . "'";
2356 2600
    }
2357 2600
2358 2600
    /**
2359
     * Obtains DBMS specific SQL code portion needed to set a CHECK constraint
2360
     * declaration to be used in statements like CREATE TABLE.
2361 2600
     *
2362 90
     * @param array $definition The check definition.
2363
     *
2364
     * @return string DBMS specific SQL code portion needed to set a CHECK constraint.
2365 2600
     */
2366 2600
    public function getCheckDeclarationSQL(array $definition)
2367
    {
2368
        $constraints = [];
2369
        foreach ($definition as $field => $def) {
2370
            if (is_string($def)) {
2371 2600
                $constraints[] = 'CHECK (' . $def . ')';
2372
            } else {
2373
                if (isset($def['min'])) {
2374
                    $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')';
2375
                }
2376
2377
                if (isset($def['max'])) {
2378
                    $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')';
2379
                }
2380
            }
2381
        }
2382
2383
        return implode(', ', $constraints);
2384
    }
2385 504
2386
    /**
2387 504
     * Obtains DBMS specific SQL code portion needed to set a unique
2388 504
     * constraint declaration to be used in statements like CREATE TABLE.
2389
     *
2390 504
     * @param string                      $name  The name of the unique constraint.
2391
     * @param \Doctrine\DBAL\Schema\Index $index The index definition.
2392
     *
2393
     * @return string DBMS specific SQL code portion needed to set a constraint.
2394 504
     *
2395 504
     * @throws \InvalidArgumentException
2396 504
     */
2397
    public function getUniqueConstraintDeclarationSQL($name, Index $index)
2398
    {
2399
        $columns = $index->getQuotedColumns($this);
2400
        $name = new Identifier($name);
2401
2402
        if (count($columns) === 0) {
2403
            throw new \InvalidArgumentException("Incomplete definition. 'columns' required.");
2404
        }
2405
2406
        return 'CONSTRAINT ' . $name->getQuotedName($this) . ' UNIQUE ('
2407
            . $this->getIndexFieldDeclarationListSQL($columns)
2408
            . ')' . $this->getPartialIndexSQL($index);
2409
    }
2410 956
2411
    /**
2412 956
     * Obtains DBMS specific SQL code portion needed to set an index
2413 956
     * declaration to be used in statements like CREATE TABLE.
2414
     *
2415 956
     * @param string                      $name  The name of the index.
2416
     * @param \Doctrine\DBAL\Schema\Index $index The index definition.
2417
     *
2418
     * @return string DBMS specific SQL code portion needed to set an index.
2419 956
     *
2420 956
     * @throws \InvalidArgumentException
2421 956
     */
2422
    public function getIndexDeclarationSQL($name, Index $index)
2423
    {
2424
        $columns = $index->getQuotedColumns($this);
2425
        $name = new Identifier($name);
2426
2427
        if (count($columns) === 0) {
2428
            throw new \InvalidArgumentException("Incomplete definition. 'columns' required.");
2429
        }
2430
2431
        return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name->getQuotedName($this) . ' ('
2432
            . $this->getIndexFieldDeclarationListSQL($columns)
2433 333
            . ')' . $this->getPartialIndexSQL($index);
2434
    }
2435 333
2436
    /**
2437
     * Obtains SQL code portion needed to create a custom column,
2438
     * e.g. when a field has the "columnDefinition" keyword.
2439
     * Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate.
2440
     *
2441
     * @param array $columnDef
2442
     *
2443
     * @return string
2444
     */
2445
    public function getCustomTypeDeclarationSQL(array $columnDef)
2446 4746
    {
2447
        return $columnDef['columnDefinition'];
2448 4746
    }
2449
2450 4746
    /**
2451 4746
     * Obtains DBMS specific SQL code portion needed to set an index
2452
     * declaration to be used in statements like CREATE TABLE.
2453
     *
2454 4746
     * @param array $fields
2455
     *
2456
     * @return string
2457
     */
2458 4746
    public function getIndexFieldDeclarationListSQL(array $fields)
2459
    {
2460
        $ret = [];
2461
2462
        foreach ($fields as $field => $definition) {
2463
            if (is_array($definition)) {
2464
                $ret[] = $field;
2465
            } else {
2466
                $ret[] = $definition;
2467
            }
2468
        }
2469
2470
        return implode(', ', $ret);
2471
    }
2472
2473
    /**
2474
     * Returns the required SQL string that fits between CREATE ... TABLE
2475
     * to create the table as a temporary table.
2476
     *
2477
     * Should be overridden in driver classes to return the correct string for the
2478
     * specific database type.
2479
     *
2480
     * The default is to return the string "TEMPORARY" - this will result in a
2481
     * SQL error for any database that does not support temporary tables, or that
2482
     * requires a different SQL command from "CREATE TEMPORARY TABLE".
2483
     *
2484
     * @return string The string required to be placed between "CREATE" and "TABLE"
2485
     *                to generate a temporary table, if possible.
2486
     */
2487 28
    public function getTemporaryTableSQL()
2488
    {
2489 28
        return 'TEMPORARY';
2490
    }
2491
2492
    /**
2493
     * Some vendors require temporary table names to be qualified specially.
2494
     *
2495
     * @param string $tableName
2496
     *
2497
     * @return string
2498
     */
2499
    public function getTemporaryTableName($tableName)
2500
    {
2501 2223
        return $tableName;
2502
    }
2503 2223
2504 2007
    /**
2505
     * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2506 2007
     * of a field declaration to be used in statements like CREATE TABLE.
2507
     *
2508
     * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey
2509
     *
2510
     * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2511
     *                of a field declaration.
2512
     */
2513
    public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey)
2514
    {
2515
        $sql  = $this->getForeignKeyBaseDeclarationSQL($foreignKey);
2516
        $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey);
2517 1923
2518
        return $sql;
2519 1923
    }
2520 1923
2521 72
    /**
2522
     * Returns the FOREIGN KEY query section dealing with non-standard options
2523 1923
     * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
2524 178
     *
2525
     * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey The foreign key definition.
2526
     *
2527 1923
     * @return string
2528
     */
2529
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
2530
    {
2531
        $query = '';
2532
        if ($this->supportsForeignKeyOnUpdate() && $foreignKey->hasOption('onUpdate')) {
2533
            $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate'));
2534
        }
2535
        if ($foreignKey->hasOption('onDelete')) {
2536
            $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
2537
        }
2538
2539 2194
        return $query;
2540
    }
2541 2194
2542 121
    /**
2543 2194
     * Returns the given referential action in uppercase if valid, otherwise throws an exception.
2544 1482
     *
2545 1104
     * @param string $action The foreign key referential action.
2546 870
     *
2547 618
     * @return string
2548 1888
     *
2549
     * @throws \InvalidArgumentException if unknown referential action given
2550 306
     */
2551
    public function getForeignKeyReferentialActionSQL($action)
2552
    {
2553
        $upper = strtoupper($action);
2554
        switch ($upper) {
2555
            case 'CASCADE':
2556
            case 'SET NULL':
2557
            case 'NO ACTION':
2558
            case 'RESTRICT':
2559
            case 'SET DEFAULT':
2560
                return $upper;
2561
            default:
2562
                throw new \InvalidArgumentException('Invalid foreign key action: ' . $upper);
2563
        }
2564 1503
    }
2565
2566 1503
    /**
2567 1503
     * Obtains DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2568 1251
     * of a field declaration to be used in statements like CREATE TABLE.
2569
     *
2570 1503
     * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey
2571
     *
2572 1503
     * @return string
2573
     *
2574
     * @throws \InvalidArgumentException
2575 1503
     */
2576
    public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey)
2577
    {
2578 1503
        $sql = '';
2579
        if (strlen($foreignKey->getName())) {
2580
            $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' ';
2581
        }
2582 1503
        $sql .= 'FOREIGN KEY (';
2583 1503
2584 1503
        if (count($foreignKey->getLocalColumns()) === 0) {
2585 1503
            throw new \InvalidArgumentException("Incomplete definition. 'local' required.");
2586
        }
2587 1503
        if (count($foreignKey->getForeignColumns()) === 0) {
2588
            throw new \InvalidArgumentException("Incomplete definition. 'foreign' required.");
2589
        }
2590
        if (strlen($foreignKey->getForeignTableName()) === 0) {
2591
            throw new \InvalidArgumentException("Incomplete definition. 'foreignTable' required.");
2592
        }
2593
2594
        $sql .= implode(', ', $foreignKey->getQuotedLocalColumns($this))
2595
            . ') REFERENCES '
2596
            . $foreignKey->getQuotedForeignTableName($this) . ' ('
2597
            . implode(', ', $foreignKey->getQuotedForeignColumns($this)) . ')';
2598
2599
        return $sql;
2600
    }
2601
2602
    /**
2603
     * Obtains DBMS specific SQL code portion needed to set the UNIQUE constraint
2604
     * of a field declaration to be used in statements like CREATE TABLE.
2605
     *
2606
     * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint
2607
     *                of a field declaration.
2608
     */
2609
    public function getUniqueFieldDeclarationSQL()
2610
    {
2611
        return 'UNIQUE';
2612
    }
2613
2614
    /**
2615
     * Obtains DBMS specific SQL code portion needed to set the CHARACTER SET
2616
     * of a field declaration to be used in statements like CREATE TABLE.
2617
     *
2618
     * @param string $charset The name of the charset.
2619
     *
2620
     * @return string DBMS specific SQL code portion needed to set the CHARACTER SET
2621
     *                of a field declaration.
2622
     */
2623
    public function getColumnCharsetDeclarationSQL($charset)
0 ignored issues
show
Unused Code introduced by
The parameter $charset is not used and could be removed. ( Ignorable by Annotation )

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

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

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

Loading history...
2624
    {
2625 263
        return '';
2626
    }
2627 263
2628
    /**
2629
     * Obtains DBMS specific SQL code portion needed to set the COLLATION
2630
     * of a field declaration to be used in statements like CREATE TABLE.
2631
     *
2632
     * @param string $collation The name of the collation.
2633
     *
2634
     * @return string DBMS specific SQL code portion needed to set the COLLATION
2635
     *                of a field declaration.
2636 90
     */
2637
    public function getColumnCollationDeclarationSQL($collation)
2638 90
    {
2639
        return $this->supportsColumnCollation() ? 'COLLATE ' . $collation : '';
2640
    }
2641
2642
    /**
2643
     * Whether the platform prefers sequences for ID generation.
2644
     * Subclasses should override this method to return TRUE if they prefer sequences.
2645
     *
2646
     * @return bool
2647 108
     */
2648
    public function prefersSequences()
2649 108
    {
2650
        return false;
2651
    }
2652
2653
    /**
2654
     * Whether the platform prefers identity columns (eg. autoincrement) for ID generation.
2655
     * Subclasses should override this method to return TRUE if they prefer identity columns.
2656
     *
2657
     * @return bool
2658
     */
2659
    public function prefersIdentityColumns()
2660
    {
2661
        return false;
2662
    }
2663
2664
    /**
2665
     * Some platforms need the boolean values to be converted.
2666 398
     *
2667
     * The default conversion in this implementation converts to integers (false => 0, true => 1).
2668 398
     *
2669
     * Note: if the input is not a boolean the original input might be returned.
2670
     *
2671
     * There are two contexts when converting booleans: Literals and Prepared Statements.
2672
     * This method should handle the literal case
2673
     *
2674 398
     * @param mixed $item A boolean or an array of them.
2675 380
     *
2676
     * @return mixed A boolean database value or an array of them.
2677
     */
2678 398
    public function convertBooleans($item)
2679
    {
2680
        if (is_array($item)) {
2681
            foreach ($item as $k => $value) {
2682
                if (is_bool($value)) {
2683
                    $item[$k] = (int) $value;
2684
                }
2685
            }
2686
        } elseif (is_bool($item)) {
2687
            $item = (int) $item;
2688
        }
2689
2690 882
        return $item;
2691
    }
2692 882
2693
    /**
2694
     * Some platforms have boolean literals that needs to be correctly converted
2695
     *
2696
     * The default conversion tries to convert value into bool "(bool)$item"
2697
     *
2698
     * @param mixed $item
2699
     *
2700
     * @return bool|null
2701
     */
2702
    public function convertFromBoolean($item)
2703
    {
2704
        return null === $item ? null: (bool) $item ;
2705 132
    }
2706
2707 132
    /**
2708
     * This method should handle the prepared statements case. When there is no
2709
     * distinction, it's OK to use the same method.
2710
     *
2711
     * Note: if the input is not a boolean the original input might be returned.
2712
     *
2713
     * @param mixed $item A boolean or an array of them.
2714
     *
2715 212
     * @return mixed A boolean database value or an array of them.
2716
     */
2717 212
    public function convertBooleansToDatabaseValue($item)
2718
    {
2719
        return $this->convertBooleans($item);
2720
    }
2721
2722
    /**
2723
     * Returns the SQL specific for the platform to get the current date.
2724
     *
2725 6
     * @return string
2726
     */
2727 6
    public function getCurrentDateSQL()
2728
    {
2729
        return 'CURRENT_DATE';
2730
    }
2731
2732
    /**
2733
     * Returns the SQL specific for the platform to get the current time.
2734
     *
2735 312
     * @return string
2736
     */
2737 312
    public function getCurrentTimeSQL()
2738
    {
2739
        return 'CURRENT_TIME';
2740
    }
2741
2742
    /**
2743
     * Returns the SQL specific for the platform to get the current timestamp
2744
     *
2745
     * @return string
2746
     */
2747
    public function getCurrentTimestampSQL()
2748
    {
2749 198
        return 'CURRENT_TIMESTAMP';
2750
    }
2751 11
2752 187
    /**
2753 198
     * Returns the SQL for a given transaction isolation level Connection constant.
2754 187
     *
2755 198
     * @param int $level
2756 187
     *
2757 198
     * @return string
2758 187
     *
2759 198
     * @throws \InvalidArgumentException
2760
     */
2761
    protected function _getTransactionIsolationLevelSQL($level)
2762
    {
2763
        switch ($level) {
2764
            case TransactionIsolationLevel::READ_UNCOMMITTED:
2765
                return 'READ UNCOMMITTED';
2766
            case TransactionIsolationLevel::READ_COMMITTED:
2767
                return 'READ COMMITTED';
2768
            case TransactionIsolationLevel::REPEATABLE_READ:
2769
                return 'REPEATABLE READ';
2770 1
            case TransactionIsolationLevel::SERIALIZABLE:
2771
                return 'SERIALIZABLE';
2772 1
            default:
2773
                throw new \InvalidArgumentException('Invalid isolation level:' . $level);
2774
        }
2775
    }
2776
2777
    /**
2778
     * @return string
2779
     *
2780
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2781
     */
2782
    public function getListDatabasesSQL()
2783
    {
2784
        throw DBALException::notSupported(__METHOD__);
2785
    }
2786
2787
    /**
2788
     * Returns the SQL statement for retrieving the namespaces defined in the database.
2789
     *
2790
     * @return string
2791
     *
2792
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2793
     */
2794
    public function getListNamespacesSQL()
2795
    {
2796
        throw DBALException::notSupported(__METHOD__);
2797
    }
2798
2799
    /**
2800
     * @param string $database
2801
     *
2802
     * @return string
2803
     *
2804
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2805
     */
2806
    public function getListSequencesSQL($database)
2807
    {
2808
        throw DBALException::notSupported(__METHOD__);
2809
    }
2810
2811
    /**
2812
     * @param string $table
2813
     *
2814
     * @return string
2815
     *
2816
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2817
     */
2818
    public function getListTableConstraintsSQL($table)
2819
    {
2820
        throw DBALException::notSupported(__METHOD__);
2821
    }
2822
2823
    /**
2824
     * @param string      $table
2825
     * @param string|null $database
2826
     *
2827
     * @return string
2828
     *
2829
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2830
     */
2831
    public function getListTableColumnsSQL($table, $database = null)
2832
    {
2833
        throw DBALException::notSupported(__METHOD__);
2834
    }
2835
2836
    /**
2837
     * @return string
2838
     *
2839
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2840
     */
2841
    public function getListTablesSQL()
2842
    {
2843
        throw DBALException::notSupported(__METHOD__);
2844
    }
2845
2846
    /**
2847
     * @return string
2848
     *
2849
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2850
     */
2851
    public function getListUsersSQL()
2852
    {
2853
        throw DBALException::notSupported(__METHOD__);
2854
    }
2855
2856
    /**
2857
     * Returns the SQL to list all views of a database or user.
2858
     *
2859
     * @param string $database
2860
     *
2861
     * @return string
2862
     *
2863
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2864
     */
2865
    public function getListViewsSQL($database)
2866
    {
2867
        throw DBALException::notSupported(__METHOD__);
2868
    }
2869
2870
    /**
2871
     * Returns the list of indexes for the current database.
2872
     *
2873
     * The current database parameter is optional but will always be passed
2874
     * when using the SchemaManager API and is the database the given table is in.
2875
     *
2876
     * Attention: Some platforms only support currentDatabase when they
2877
     * are connected with that database. Cross-database information schema
2878
     * requests may be impossible.
2879
     *
2880
     * @param string $table
2881
     * @param string $currentDatabase
2882
     *
2883
     * @return string
2884
     *
2885
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2886
     */
2887
    public function getListTableIndexesSQL($table, $currentDatabase = null)
2888
    {
2889
        throw DBALException::notSupported(__METHOD__);
2890
    }
2891
2892
    /**
2893
     * @param string $table
2894
     *
2895
     * @return string
2896
     *
2897
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2898
     */
2899
    public function getListTableForeignKeysSQL($table)
2900
    {
2901
        throw DBALException::notSupported(__METHOD__);
2902
    }
2903
2904
    /**
2905
     * @param string $name
2906
     * @param string $sql
2907
     *
2908
     * @return string
2909
     *
2910
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2911
     */
2912
    public function getCreateViewSQL($name, $sql)
2913
    {
2914
        throw DBALException::notSupported(__METHOD__);
2915
    }
2916
2917
    /**
2918
     * @param string $name
2919
     *
2920
     * @return string
2921
     *
2922
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2923
     */
2924
    public function getDropViewSQL($name)
2925
    {
2926
        throw DBALException::notSupported(__METHOD__);
2927
    }
2928
2929
    /**
2930
     * Returns the SQL snippet to drop an existing sequence.
2931
     *
2932
     * @param Sequence|string $sequence
2933
     *
2934
     * @return string
2935
     *
2936
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2937
     */
2938
    public function getDropSequenceSQL($sequence)
0 ignored issues
show
Unused Code introduced by
The parameter $sequence is not used and could be removed. ( Ignorable by Annotation )

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

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

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

Loading history...
2939
    {
2940
        throw DBALException::notSupported(__METHOD__);
2941
    }
2942
2943
    /**
2944
     * @param string $sequenceName
2945
     *
2946
     * @return string
2947
     *
2948
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2949
     */
2950
    public function getSequenceNextValSQL($sequenceName)
0 ignored issues
show
Unused Code introduced by
The parameter $sequenceName is not used and could be removed. ( Ignorable by Annotation )

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

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

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

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

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

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

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

Loading history...
3503
    {
3504 133
        $tableIdentifier = new Identifier($tableName);
3505
3506 133
        return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this);
3507
    }
3508
3509
    /**
3510
     * This is for test reasons, many vendors have special requirements for dummy statements.
3511
     *
3512
     * @return string
3513
     */
3514
    public function getDummySelectSQL()
3515
    {
3516 16
        $expression = func_num_args() > 0 ? func_get_arg(0) : '1';
3517
3518 16
        return sprintf('SELECT %s', $expression);
3519
    }
3520
3521
    /**
3522
     * Returns the SQL to create a new savepoint.
3523
     *
3524
     * @param string $savepoint
3525
     *
3526
     * @return string
3527
     */
3528 15
    public function createSavePoint($savepoint)
3529
    {
3530 15
        return 'SAVEPOINT ' . $savepoint;
3531
    }
3532
3533
    /**
3534
     * Returns the SQL to release a savepoint.
3535
     *
3536
     * @param string $savepoint
3537
     *
3538
     * @return string
3539
     */
3540 16
    public function releaseSavePoint($savepoint)
3541
    {
3542 16
        return 'RELEASE SAVEPOINT ' . $savepoint;
3543
    }
3544
3545
    /**
3546
     * Returns the SQL to rollback a savepoint.
3547
     *
3548
     * @param string $savepoint
3549
     *
3550
     * @return string
3551
     */
3552 20219
    public function rollbackSavePoint($savepoint)
3553
    {
3554
        return 'ROLLBACK TO SAVEPOINT ' . $savepoint;
3555 20219
    }
3556 19013
3557
    /**
3558
     * Returns the keyword list instance of this platform.
3559 17501
     *
3560 17501
     * @return \Doctrine\DBAL\Platforms\Keywords\KeywordList
3561 17501
     *
3562
     * @throws \Doctrine\DBAL\DBALException If no keyword list is specified.
3563
     */
3564
    final public function getReservedKeywordsList()
3565
    {
3566 17501
        // Check for an existing instantiation of the keywords class.
3567
        if ($this->_keywords) {
3568 17501
            return $this->_keywords;
3569
        }
3570
3571
        $class = $this->getReservedKeywordsClass();
3572
        $keywords = new $class;
3573
        if ( ! $keywords instanceof \Doctrine\DBAL\Platforms\Keywords\KeywordList) {
3574
            throw DBALException::notSupported(__METHOD__);
3575
        }
3576
3577
        // Store the instance so it doesn't need to be generated on every request.
3578
        $this->_keywords = $keywords;
3579
3580
        return $keywords;
3581
    }
3582
3583
    /**
3584
     * Returns the class name of the reserved keywords list.
3585
     *
3586
     * @return string
3587
     *
3588
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
3589
     */
3590
    protected function getReservedKeywordsClass()
3591
    {
3592
        throw DBALException::notSupported(__METHOD__);
3593 6705
    }
3594
3595 6705
    /**
3596
     * Quotes a literal string.
3597 6705
     * This method is NOT meant to fix SQL injections!
3598
     * It is only meant to escape this platform's string literal
3599
     * quote character inside the given literal string.
3600
     *
3601
     * @param string $str The literal string to be quoted.
3602
     *
3603
     * @return string The quoted literal string.
3604
     */
3605 7029
    public function quoteStringLiteral($str)
3606
    {
3607 7029
        $c = $this->getStringLiteralQuoteCharacter();
3608
3609
        return $c . str_replace($c, $c . $c, $str) . $c;
3610
    }
3611
3612
    /**
3613
     * Gets the character used for string literal quoting.
3614
     *
3615
     * @return string
3616
     */
3617
    public function getStringLiteralQuoteCharacter()
3618 342
    {
3619
        return "'";
3620 342
    }
3621 342
3622 342
    /**
3623 342
     * Escapes metacharacters in a string intended to be used with a LIKE
3624
     * operator.
3625
     *
3626
     * @param string $inputString a literal, unquoted string
3627 342
     * @param string $escapeChar  should be reused by the caller in the LIKE
3628
     *                            expression.
3629 342
     */
3630
    final public function escapeStringForLike(string $inputString, string $escapeChar) : string
3631
    {
3632
        return preg_replace(
3633
            '~([' . preg_quote($this->getLikeWildcardCharacters() . $escapeChar, '~') . '])~u',
3634
            addcslashes($escapeChar, '\\') . '$1',
3635
            $inputString
3636
        );
3637
    }
3638
3639
    protected function getLikeWildcardCharacters() : string
3640
    {
3641
        return '%_';
3642
    }
3643
}
3644