Failed Conditions
Pull Request — develop (#3348)
by Sergei
126:17 queued 61:12
created

SqlitePlatform::getForeignKeysInAlteredTable()   B

Complexity

Conditions 11
Paths 120

Size

Total Lines 55
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 27
CRAP Score 11.0397

Importance

Changes 0
Metric Value
eloc 31
dl 0
loc 55
ccs 27
cts 29
cp 0.931
rs 7.15
c 0
b 0
f 0
cc 11
nc 120
nop 1
crap 11.0397

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
namespace Doctrine\DBAL\Platforms;
4
5
use Doctrine\DBAL\DBALException;
6
use Doctrine\DBAL\Schema\Column;
7
use Doctrine\DBAL\Schema\Constraint;
8
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
9
use Doctrine\DBAL\Schema\Identifier;
10
use Doctrine\DBAL\Schema\Index;
11
use Doctrine\DBAL\Schema\Table;
12
use Doctrine\DBAL\Schema\TableDiff;
13
use Doctrine\DBAL\TransactionIsolationLevel;
14
use Doctrine\DBAL\Types;
15
use InvalidArgumentException;
16
use function array_merge;
17
use function array_unique;
18
use function array_values;
19
use function implode;
20
use function is_numeric;
21
use function sprintf;
22
use function sqrt;
23
use function str_replace;
24
use function strpos;
25
use function strtolower;
26
27
/**
28
 * The SqlitePlatform class describes the specifics and dialects of the SQLite
29
 * database platform.
30
 *
31
 * @todo   Rename: SQLitePlatform
32
 */
33
class SqlitePlatform extends AbstractPlatform
34
{
35
    /**
36
     * {@inheritDoc}
37
     */
38 1801
    public function getRegexpExpression() : string
39
    {
40 1801
        return 'REGEXP';
41
    }
42
43
    /**
44
     * {@inheritDoc}
45
     */
46
    public function getNowExpression($type = 'timestamp') : string
47
    {
48
        switch ($type) {
49
            case 'time':
50
                return 'time(\'now\')';
51
            case 'date':
52
                return 'date(\'now\')';
53
            case 'timestamp':
54
            default:
55
                return 'datetime(\'now\')';
56
        }
57
    }
58
59
    /**
60
     * {@inheritDoc}
61
     */
62 253
    public function getTrimExpression(string $str, int $mode = TrimMode::UNSPECIFIED, ?string $char = null) : string
63
    {
64 253
        switch ($mode) {
65
            case TrimMode::UNSPECIFIED:
66 253
            case TrimMode::BOTH:
67
                $trimFn = 'TRIM';
68 252
                break;
69 252
70
            case TrimMode::LEADING:
71
                $trimFn = 'LTRIM';
72 251
                break;
73 251
74
            case TrimMode::TRAILING:
75
                $trimFn = 'RTRIM';
76 253
                break;
77
78
            default:
79 253
                throw new InvalidArgumentException(
80
                    sprintf(
81
                        'The value of $mode is expected to be one of the TrimMode constants, %d given',
82
                        $mode
83
                    )
84
                );
85
        }
86
87 1801
        $arguments = [$str];
88
89 1801
        if ($char !== null) {
90 1801
            $arguments[] = $char;
91
        }
92
93 1801
        return sprintf('%s(%s)', $trimFn, implode(', ', $arguments));
94
    }
95
96
    /**
97
     * {@inheritDoc}
98
     */
99 226
    public function getSubstringExpression(string $string, string $start, ?string $length = null) : string
100
    {
101 226
        if ($length === null) {
102 226
            return sprintf('SUBSTR(%s, %s)', $string, $start);
103
        }
104
105 226
        return sprintf('SUBSTR(%s, %s, %s)', $string, $start, $length);
106
    }
107
108
    /**
109
     * {@inheritDoc}
110
     */
111 1403
    public function getLocateExpression(string $string, string $substring, ?string $start = null) : string
112
    {
113 1403
        if ($start === null) {
114
            return sprintf('LOCATE(%s, %s)', $string, $substring);
115
        }
116
117 228
        return sprintf('LOCATE(%s, %s, %s)', $string, $substring, $start);
118
    }
119
120 1403
    /**
121
     * {@inheritdoc}
122 228
     */
123 228
    protected function getDateArithmeticIntervalExpression(string $date, string $operator, string $interval, string $unit) : string
124 228
    {
125
        switch ($unit) {
126
            case DateIntervalUnit::SECOND:
127 228
            case DateIntervalUnit::MINUTE:
128 228
            case DateIntervalUnit::HOUR:
129 228
                return 'DATETIME(' . $date . ",'" . $operator . $interval . ' ' . $unit . "')";
130
131
            default:
132 1403
                switch ($unit) {
133 1378
                    case DateIntervalUnit::WEEK:
134
                        $interval = $this->convertNonNativeInterval($interval, $unit, 7);
135
                        $unit     = DateIntervalUnit::DAY;
136 1403
                        break;
137
138
                    case DateIntervalUnit::QUARTER:
139
                        $interval = $this->convertNonNativeInterval($interval, $unit, 3);
140
                        $unit     = DateIntervalUnit::MONTH;
141
                        break;
142
                }
143 201
144
                if (! is_numeric($interval)) {
145 201
                    $interval = "' || " . $interval . " || '";
146
                }
147
148
                return 'DATE(' . $date . ",'" . $operator . $interval . ' ' . $unit . "')";
149
        }
150
    }
151 1777
152
    /**
153 1
     * {@inheritDoc}
154 1776
     */
155 1777
    public function getDateDiffExpression(string $date1, string $date2) : string
156 1776
    {
157 1776
        return sprintf("JULIANDAY(%s, 'start of day') - JULIANDAY(%s, 'start of day')", $date1, $date2);
158 1776
    }
159 1777
160
    /**
161
     * {@inheritDoc}
162
     */
163
    protected function _getTransactionIsolationLevelSQL(int $level) : string
164
    {
165
        switch ($level) {
166
            case TransactionIsolationLevel::READ_UNCOMMITTED:
167
                return 0;
168 1777
            case TransactionIsolationLevel::READ_COMMITTED:
169
            case TransactionIsolationLevel::REPEATABLE_READ:
170 1777
            case TransactionIsolationLevel::SERIALIZABLE:
171
                return 1;
172
            default:
173
                return parent::_getTransactionIsolationLevelSQL($level);
174
        }
175
    }
176 1762
177
    /**
178 1762
     * {@inheritDoc}
179
     */
180
    public function getSetTransactionIsolationSQL(int $level) : string
181
    {
182
        return 'PRAGMA read_uncommitted = ' . $this->_getTransactionIsolationLevelSQL($level);
183
    }
184 965
185
    /**
186 965
     * {@inheritDoc}
187
     */
188
    public function prefersIdentityColumns() : bool
189
    {
190
        return true;
191
    }
192 1941
193
    /**
194 1941
     * {@inheritDoc}
195
     */
196
    public function getBooleanTypeDeclarationSQL(array $columnDef) : string
197
    {
198
        return 'BOOLEAN';
199
    }
200 1733
201
    /**
202
     * {@inheritDoc}
203 1733
     */
204 1733
    public function getIntegerTypeDeclarationSQL(array $columnDef) : string
205
    {
206
        return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
207 1673
    }
208
209
    /**
210
     * {@inheritDoc}
211
     */
212
    public function getBigIntTypeDeclarationSQL(array $columnDef) : string
213 1706
    {
214
        //  SQLite autoincrement is implicit for INTEGER PKs, but not for BIGINT fields.
215
        if (! empty($columnDef['autoincrement'])) {
216 1706
            return $this->getIntegerTypeDeclarationSQL($columnDef);
217 1706
        }
218
219
        return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
220 1705
    }
221
222
    /**
223
     * {@inheritDoc}
224
     */
225
    public function getTinyIntTypeDeclarationSql(array $field) : string
226 1783
    {
227
        //  SQLite autoincrement is implicit for INTEGER PKs, but not for TINYINT fields.
228
        if (! empty($field['autoincrement'])) {
229 1783
            return $this->getIntegerTypeDeclarationSQL($field);
230 1783
        }
231
232
        return 'TINYINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
233 1760
    }
234
235
    /**
236
     * {@inheritDoc}
237
     */
238
    public function getSmallIntTypeDeclarationSQL(array $columnDef) : string
239 1657
    {
240
        //  SQLite autoincrement is implicit for INTEGER PKs, but not for SMALLINT fields.
241
        if (! empty($columnDef['autoincrement'])) {
242 1657
            return $this->getIntegerTypeDeclarationSQL($columnDef);
243 1657
        }
244
245
        return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
246 1657
    }
247
248
    /**
249
     * {@inheritDoc}
250
     */
251
    public function getMediumIntTypeDeclarationSql(array $field) : string
252 254
    {
253
        //  SQLite autoincrement is implicit for INTEGER PKs, but not for MEDIUMINT fields.
254 254
        if (! empty($field['autoincrement'])) {
255
            return $this->getIntegerTypeDeclarationSQL($field);
256
        }
257
258
        return 'MEDIUMINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
259
    }
260 227
261
    /**
262 227
     * {@inheritDoc}
263
     */
264
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) : string
265
    {
266
        return 'DATETIME';
267
    }
268 164
269
    /**
270 164
     * {@inheritDoc}
271
     */
272
    public function getDateTypeDeclarationSQL(array $fieldDeclaration) : string
273
    {
274
        return 'DATE';
275
    }
276 1941
277
    /**
278
     * {@inheritDoc}
279 1941
     */
280 1881
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration) : string
281
    {
282
        return 'TIME';
283 1915
    }
284
285
    /**
286
     * {@inheritDoc}
287
     */
288
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) : string
289 1495
    {
290
        // sqlite autoincrement is only possible for the primary key
291 1495
        if (! empty($columnDef['autoincrement'])) {
292 1495
            return ' PRIMARY KEY AUTOINCREMENT';
293 1495
        }
294 1495
295 1495
        return ! empty($columnDef['unsigned']) ? ' UNSIGNED' : '';
296 1495
    }
297
298
    /**
299
     * {@inheritDoc}
300
     */
301
    public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey) : string
302
    {
303 1714
        return parent::getForeignKeyDeclarationSQL(new ForeignKeyConstraint(
304
            $foreignKey->getQuotedLocalColumns($this),
305 1714
            str_replace('.', '__', $foreignKey->getQuotedForeignTableName($this)),
306 1714
            $foreignKey->getQuotedForeignColumns($this),
307
            $foreignKey->getName(),
308 1714
            $foreignKey->getOptions()
309
        ));
310
    }
311
312
    /**
313
     * {@inheritDoc}
314 1714
     */
315
    protected function _getCreateTableSQL(string $tableName, array $columns, array $options = []) : array
316 1714
    {
317 1713
        $tableName   = str_replace('.', '__', $tableName);
318 1495
        $queryFields = $this->getColumnDeclarationListSQL($columns);
319
320
        if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
321
            foreach ($options['uniqueConstraints'] as $name => $definition) {
322 1714
                $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
323
            }
324 1714
        }
325 1479
326
        $queryFields .= $this->getNonAutoincrementPrimaryKeyDefinition($columns, $options);
327
328 1702
        if (isset($options['foreignKeys'])) {
329 1558
            foreach ($options['foreignKeys'] as $foreignKey) {
330 1558
                $queryFields .= ', ' . $this->getForeignKeyDeclarationSQL($foreignKey);
331
            }
332
        }
333
334 1702
        $query = ['CREATE TABLE ' . $tableName . ' (' . $queryFields . ')'];
335
336
        if (isset($options['alter']) && $options['alter'] === true) {
337
            return $query;
338
        }
339
340 1702
        if (isset($options['indexes']) && ! empty($options['indexes'])) {
341
            foreach ($options['indexes'] as $indexDef) {
342
                $query[] = $this->getCreateIndexSQL($indexDef, $tableName);
343
            }
344
        }
345
346
        if (isset($options['unique']) && ! empty($options['unique'])) {
347
            foreach ($options['unique'] as $indexDef) {
348
                $query[] = $this->getCreateIndexSQL($indexDef, $tableName);
349 1714
            }
350
        }
351 1714
352 1317
        return $query;
353
    }
354
355 1700
    /**
356
     * Generate a PRIMARY KEY definition if no autoincrement value is used
357 1700
     *
358 1700
     * @param mixed[][] $columns
359 1654
     * @param mixed[]   $options
360
     */
361
    private function getNonAutoincrementPrimaryKeyDefinition(array $columns, array $options) : string
362
    {
363 1605
        if (empty($options['primary'])) {
364
            return '';
365
        }
366
367
        $keyColumns = array_unique(array_values($options['primary']));
368
369 1811
        foreach ($keyColumns as $keyColumn) {
370
            if (! empty($columns[$keyColumn]['autoincrement'])) {
371 1811
                return '';
372 1743
            }
373 1811
        }
374
375
        return ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
376
    }
377
378
    /**
379 1464
     * {@inheritDoc}
380
     */
381 1464
    protected function getVarcharTypeDeclarationSQLSnippet(?int $length, bool $fixed) : string
382
    {
383
        return $fixed
384
            ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)')
385
            : ($length ? 'VARCHAR(' . $length . ')' : 'TEXT');
386
    }
387 505
388
    /**
389 505
     * {@inheritdoc}
390
     */
391
    protected function getBinaryTypeDeclarationSQLSnippet(?int $length, bool $fixed) : string
392
    {
393
        return 'BLOB';
394
    }
395 1465
396
    /**
397 1465
     * {@inheritdoc}
398
     */
399
    public function getBinaryMaxLength() : int
400
    {
401
        return 0;
402
    }
403 746
404
    /**
405 746
     * {@inheritdoc}
406
     */
407
    public function getBinaryDefaultLength() : int
408
    {
409
        return 0;
410
    }
411 1297
412
    /**
413 1297
     * {@inheritDoc}
414
     */
415 1297
    public function getClobTypeDeclarationSQL(array $field) : string
416 1
    {
417 1297
        return 'CLOB';
418
    }
419
420
    /**
421
     * {@inheritDoc}
422
     */
423
    public function getListTableConstraintsSQL(string $table) : string
424 1405
    {
425
        $table = str_replace('.', '__', $table);
426 1405
427
        return sprintf(
428 1405
            "SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name = %s AND sql NOT NULL ORDER BY name",
429
            $this->quoteStringLiteral($table)
430
        );
431
    }
432
433
    /**
434 1382
     * {@inheritDoc}
435
     */
436 1382
    public function getListTableColumnsSQL(string $table, ?string $database = null) : string
437
    {
438 1382
        $table = str_replace('.', '__', $table);
439
440
        return sprintf('PRAGMA table_info(%s)', $this->quoteStringLiteral($table));
441
    }
442
443
    /**
444 304
     * {@inheritDoc}
445
     */
446
    public function getListTableIndexesSQL(string $table, ?string $currentDatabase = null) : string
447
    {
448 304
        $table = str_replace('.', '__', $table);
449
450
        return sprintf('PRAGMA index_list(%s)', $this->quoteStringLiteral($table));
451
    }
452
453
    /**
454 155
     * {@inheritDoc}
455
     */
456 155
    public function getListTablesSQL() : string
457
    {
458
        return "SELECT name FROM sqlite_master WHERE type = 'table' AND name != 'sqlite_sequence' AND name != 'geometry_columns' AND name != 'spatial_ref_sys' "
459
             . 'UNION ALL SELECT name FROM sqlite_temp_master '
460
             . "WHERE type = 'table' ORDER BY name";
461
    }
462 155
463
    /**
464 155
     * {@inheritDoc}
465
     */
466
    public function getListViewsSQL(?string $database) : string
467
    {
468
        return "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL";
469
    }
470 155
471
    /**
472 155
     * {@inheritDoc}
473
     */
474
    public function getCreateViewSQL(string $name, string $sql) : string
475
    {
476
        return 'CREATE VIEW ' . $name . ' AS ' . $sql;
477
    }
478 1495
479
    /**
480 1495
     * {@inheritDoc}
481
     */
482 1495
    public function getDropViewSQL(string $name) : string
483 1495
    {
484
        return 'DROP VIEW ' . $name;
485 1495
    }
486
487
    /**
488
     * {@inheritDoc}
489
     */
490
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) : string
491 154
    {
492
        $query = parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
493 154
494
        $query .= ($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false ? ' ' : ' NOT ') . 'DEFERRABLE';
495
        $query .= ' INITIALLY ' . ($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false ? 'DEFERRED' : 'IMMEDIATE');
496
497
        return $query;
498
    }
499 1306
500
    /**
501 1306
     * {@inheritDoc}
502
     */
503
    public function supportsIdentityColumns() : bool
504
    {
505
        return true;
506
    }
507 1718
508
    /**
509 1718
     * {@inheritDoc}
510
     */
511
    public function supportsColumnCollation() : bool
512
    {
513
        return true;
514
    }
515 2014
516
    /**
517 2014
     * {@inheritDoc}
518
     */
519
    public function supportsInlineColumnComments() : bool
520
    {
521
        return true;
522
    }
523 792
524
    /**
525 792
     * {@inheritDoc}
526 792
     */
527
    public function getName() : string
528 792
    {
529
        return 'sqlite';
530
    }
531
532
    /**
533
     * {@inheritDoc}
534
     */
535
    public function getTruncateTableSQL(string $tableName, bool $cascade = false) : string
536
    {
537
        $tableIdentifier = new Identifier($tableName);
538
        $tableName       = str_replace('.', '__', $tableIdentifier->getQuotedName($this));
539
540
        return 'DELETE FROM ' . $tableName;
541
    }
542
543
    /**
544
     * User-defined function for Sqlite that is used with PDO::sqliteCreateFunction().
545
     *
546
     * @param int|float $value
547
     */
548
    public static function udfSqrt($value) : float
549
    {
550
        return sqrt($value);
551
    }
552
553
    /**
554
     * User-defined function for Sqlite that implements MOD(a, b).
555
     */
556
    public static function udfMod(int $a, int $b) : int
557
    {
558
        return $a % $b;
559
    }
560
561
    public static function udfLocate(string $str, string $substr, int $offset = 0) : int
562
    {
563 226
        // SQL's LOCATE function works on 1-based positions, while PHP's strpos works on 0-based positions.
564
        // So we have to make them compatible if an offset is given.
565
        if ($offset > 0) {
566
            $offset -= 1;
567 226
        }
568 226
569
        $pos = strpos($str, $substr, $offset);
570
571 226
        if ($pos !== false) {
572
            return $pos + 1;
573 226
        }
574 226
575
        return 0;
576
    }
577 226
578
    /**
579
     * {@inheritDoc}
580
     */
581
    public function getForUpdateSql() : string
582
    {
583
        return '';
584
    }
585
586
    /**
587
     * {@inheritDoc}
588
     */
589
    public function getInlineColumnCommentSQL(?string $comment) : string
590
    {
591 572
        if ($comment === null || $comment === '') {
592
            return '';
593 572
        }
594
595
        return '--' . str_replace("\n", "\n--", $comment) . "\n";
596
    }
597
598
    /**
599 1201
     * {@inheritDoc}
600
     */
601 1201
    protected function initializeDoctrineTypeMappings() : void
602
    {
603
        $this->doctrineTypeMapping = [
604
            'bigint'           => 'bigint',
605
            'bigserial'        => 'bigint',
606
            'blob'             => 'blob',
607
            'boolean'          => 'boolean',
608
            'char'             => 'string',
609
            'clob'             => 'text',
610
            'date'             => 'date',
611
            'datetime'         => 'datetime',
612
            'decimal'          => 'decimal',
613
            'double'           => 'float',
614
            'double precision' => 'float',
615
            'float'            => 'float',
616
            'image'            => 'string',
617
            'int'              => 'integer',
618
            'integer'          => 'integer',
619
            'longtext'         => 'text',
620
            'longvarchar'      => 'string',
621
            'mediumint'        => 'integer',
622
            'mediumtext'       => 'text',
623
            'ntext'            => 'string',
624
            'numeric'          => 'decimal',
625
            'nvarchar'         => 'string',
626
            'real'             => 'float',
627
            'serial'           => 'integer',
628
            'smallint'         => 'smallint',
629
            'string'           => 'string',
630
            'text'             => 'text',
631
            'time'             => 'time',
632
            'timestamp'        => 'datetime',
633
            'tinyint'          => 'boolean',
634
            'tinytext'         => 'text',
635
            'varchar'          => 'string',
636 1201
            'varchar2'         => 'string',
637
        ];
638
    }
639
640
    /**
641 1728
     * {@inheritDoc}
642
     */
643 1728
    protected function getReservedKeywordsClass() : string
644
    {
645
        return Keywords\SQLiteKeywords::class;
646
    }
647
648
    /**
649 1479
     * {@inheritDoc}
650
     */
651 1479
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) : array
652
    {
653
        if (! $diff->fromTable instanceof Table) {
654
            throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema');
655 1479
        }
656 1479
657 1473
        $sql = [];
658 1471
        foreach ($diff->fromTable->getIndexes() as $index) {
659
            if ($index->isPrimary()) {
660
                continue;
661 1470
            }
662
663
            $sql[] = $this->getDropIndexSQL($index, $diff->name);
664 1479
        }
665
666
        return $sql;
667
    }
668
669
    /**
670 1479
     * {@inheritDoc}
671
     */
672 1479
    protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) : array
673
    {
674
        if (! $diff->fromTable instanceof Table) {
675
            throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema');
676 1479
        }
677 1479
678
        $sql       = [];
679 1479
        $tableName = $diff->getNewName();
680 901
681
        if ($tableName === false) {
682
            $tableName = $diff->getName($this);
683 1479
        }
684 1473
685 1471
        foreach ($this->getIndexesInAlteredTable($diff) as $index) {
686
            if ($index->isPrimary()) {
687
                continue;
688 1472
            }
689
690
            $sql[] = $this->getCreateIndexSQL($index, $tableName->getQuotedName($this));
691 1479
        }
692
693
        return $sql;
694
    }
695
696
    /**
697 1692
     * {@inheritDoc}
698
     */
699 1692
    protected function doModifyLimitQuery(string $query, ?int $limit, int $offset) : string
700 1666
    {
701
        if ($limit === null && $offset > 0) {
702
            $limit = -1;
703 1692
        }
704
705
        return parent::doModifyLimitQuery($query, $limit, $offset);
706
    }
707
708
    /**
709 262
     * {@inheritDoc}
710
     */
711 262
    public function getBlobTypeDeclarationSQL(array $field) : string
712
    {
713
        return 'BLOB';
714
    }
715
716
    /**
717 111
     * {@inheritDoc}
718
     */
719 111
    public function getTemporaryTableName(string $tableName) : string
720
    {
721 111
        $tableName = str_replace('.', '__', $tableName);
722
723
        return $tableName;
724
    }
725
726
    /**
727
     * {@inheritDoc}
728
     *
729
     * Sqlite Platform emulates schema by underscoring each dot and generating tables
730
     * into the default database.
731
     *
732
     * This hack is implemented to be able to use SQLite as testdriver when
733
     * using schema supporting databases.
734
     */
735
    public function canEmulateSchemas() : bool
736
    {
737
        return true;
738
    }
739
740
    /**
741 1566
     * {@inheritDoc}
742
     */
743 1566
    public function supportsForeignKeyConstraints() : bool
744
    {
745
        return false;
746
    }
747
748
    /**
749
     * {@inheritDoc}
750
     */
751
    public function getCreatePrimaryKeySQL(Index $index, $table) : string
752
    {
753
        throw new DBALException('Sqlite platform does not support alter primary key.');
754
    }
755
756
    /**
757 1586
     * {@inheritdoc}
758
     */
759 1586
    public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) : string
760
    {
761
        throw new DBALException('Sqlite platform does not support alter foreign key.');
762
    }
763
764
    /**
765
     * {@inheritdoc}
766
     */
767
    public function getDropForeignKeySQL($foreignKey, $table) : string
768
    {
769
        throw new DBALException('Sqlite platform does not support alter foreign key.');
770
    }
771
772
    /**
773 1561
     * {@inheritDoc}
774
     */
775 1561
    public function getCreateConstraintSQL(Constraint $constraint, $table) : string
776
    {
777
        throw new DBALException('Sqlite platform does not support alter constraint.');
778
    }
779
780
    /**
781 1715
     * {@inheritDoc}
782
     */
783 1715
    public function getCreateTableSQL(Table $table, int $createFlags = self::CREATE_INDEXES | self::CREATE_FOREIGNKEYS) : array
784
    {
785 1715
        return parent::getCreateTableSQL($table, $createFlags);
786
    }
787
788
    /**
789
     * {@inheritDoc}
790
     */
791 1374
    public function getListTableForeignKeysSQL(string $table, ?string $database = null) : string
792
    {
793 1374
        $table = str_replace('.', '__', $table);
794
795 1374
        return sprintf('PRAGMA foreign_key_list(%s)', $this->quoteStringLiteral($table));
796
    }
797
798
    /**
799
     * {@inheritDoc}
800
     */
801 1596
    public function getAlterTableSQL(TableDiff $diff) : array
802
    {
803 1596
        $sql = $this->getSimpleAlterTableSQL($diff);
804 1596
        if ($sql !== false) {
0 ignored issues
show
introduced by
The condition $sql !== false is always false.
Loading history...
805 1582
            return $sql;
806
        }
807
808 1550
        $fromTable = $diff->fromTable;
809 1550
        if (! $fromTable instanceof Table) {
810 1442
            throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema');
811
        }
812
813 1479
        $table = clone $fromTable;
814
815 1479
        $columns        = [];
816 1479
        $oldColumnNames = [];
817 1479
        $newColumnNames = [];
818 1479
        $columnSql      = [];
819
820 1479
        foreach ($table->getColumns() as $columnName => $column) {
821 1478
            $columnName                  = strtolower($columnName);
822 1478
            $columns[$columnName]        = $column;
823 1478
            $oldColumnNames[$columnName] = $newColumnNames[$columnName] = $column->getQuotedName($this);
824
        }
825
826 1479
        foreach ($diff->removedColumns as $columnName => $column) {
827 1471
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
828
                continue;
829
            }
830
831 1471
            $columnName = strtolower($columnName);
832 1471
            if (! isset($columns[$columnName])) {
833
                continue;
834
            }
835
836
            unset(
837 1471
                $columns[$columnName],
838 1471
                $oldColumnNames[$columnName],
839 1471
                $newColumnNames[$columnName]
840
            );
841
        }
842
843 1479
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
844 1373
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
845
                continue;
846
            }
847
848 1373
            $oldColumnName = strtolower($oldColumnName);
849 1373
            if (isset($columns[$oldColumnName])) {
850 1373
                unset($columns[$oldColumnName]);
851
            }
852
853 1373
            $columns[strtolower($column->getName())] = $column;
854
855 1373
            if (! isset($newColumnNames[$oldColumnName])) {
856
                continue;
857
            }
858
859 1373
            $newColumnNames[$oldColumnName] = $column->getQuotedName($this);
860
        }
861
862 1479
        foreach ($diff->changedColumns as $oldColumnName => $columnDiff) {
863 940
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
864
                continue;
865
            }
866
867 940
            if (isset($columns[$oldColumnName])) {
868 939
                unset($columns[$oldColumnName]);
869
            }
870
871 940
            $columns[strtolower($columnDiff->column->getName())] = $columnDiff->column;
872
873 940
            if (! isset($newColumnNames[$oldColumnName])) {
874 553
                continue;
875
            }
876
877 939
            $newColumnNames[$oldColumnName] = $columnDiff->column->getQuotedName($this);
878
        }
879
880 1479
        foreach ($diff->addedColumns as $columnName => $column) {
881 941
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
882
                continue;
883
            }
884
885 941
            $columns[strtolower($columnName)] = $column;
886
        }
887
888 1479
        $sql      = [];
889 1479
        $tableSql = [];
890
891 1479
        if (! $this->onSchemaAlterTable($diff, $tableSql)) {
892 1479
            $dataTable = new Table('__temp__' . $table->getName());
893
894 1479
            $newTable = new Table($table->getQuotedName($this), $columns, $this->getPrimaryIndexInAlteredTable($diff), [], $this->getForeignKeysInAlteredTable($diff), $table->getOptions());
895 1479
            $newTable->addOption('alter', true);
896
897 1479
            $sql = $this->getPreAlterTableIndexForeignKeySQL($diff);
898
            //$sql = array_merge($sql, $this->getCreateTableSQL($dataTable, 0));
899 1479
            $sql[] = sprintf('CREATE TEMPORARY TABLE %s AS SELECT %s FROM %s', $dataTable->getQuotedName($this), implode(', ', $oldColumnNames), $table->getQuotedName($this));
900 1479
            $sql[] = $this->getDropTableSQL($fromTable);
901
902 1479
            $sql   = array_merge($sql, $this->getCreateTableSQL($newTable));
903 1479
            $sql[] = sprintf('INSERT INTO %s (%s) SELECT %s FROM %s', $newTable->getQuotedName($this), implode(', ', $newColumnNames), implode(', ', $oldColumnNames), $dataTable->getQuotedName($this));
904 1479
            $sql[] = $this->getDropTableSQL($dataTable);
905
906 1479
            $newName = $diff->getNewName();
907
908 1479
            if ($newName !== false) {
909 1371
                $sql[] = sprintf(
910 3
                    'ALTER TABLE %s RENAME TO %s',
911 1371
                    $newTable->getQuotedName($this),
912 1371
                    $newName->getQuotedName($this)
913
                );
914
            }
915
916 1479
            $sql = array_merge($sql, $this->getPostAlterTableIndexForeignKeySQL($diff));
917
        }
918
919 1479
        return array_merge($sql, $tableSql, $columnSql);
920
    }
921
922
    /**
923
     * @return string[]|false
924
     */
925 1596
    private function getSimpleAlterTableSQL(TableDiff $diff)
926
    {
927
        // Suppress changes on integer type autoincrement columns.
928 1596
        foreach ($diff->changedColumns as $oldColumnName => $columnDiff) {
929 960
            if (! $columnDiff->fromColumn instanceof Column ||
930 956
                ! $columnDiff->column instanceof Column ||
931 956
                ! $columnDiff->column->getAutoincrement() ||
932 960
                ! $columnDiff->column->getType() instanceof Types\IntegerType
933
            ) {
934 940
                continue;
935
            }
936
937 172
            if (! $columnDiff->hasChanged('type') && $columnDiff->hasChanged('unsigned')) {
938 169
                unset($diff->changedColumns[$oldColumnName]);
939
940 169
                continue;
941
            }
942
943 172
            $fromColumnType = $columnDiff->fromColumn->getType();
944
945 172
            if (! ($fromColumnType instanceof Types\SmallIntType) && ! ($fromColumnType instanceof Types\BigIntType)) {
946
                continue;
947
            }
948
949 172
            unset($diff->changedColumns[$oldColumnName]);
950
        }
951
952 1596
        if (! empty($diff->renamedColumns) || ! empty($diff->addedForeignKeys) || ! empty($diff->addedIndexes)
953 1591
                || ! empty($diff->changedColumns) || ! empty($diff->changedForeignKeys) || ! empty($diff->changedIndexes)
954 1587
                || ! empty($diff->removedColumns) || ! empty($diff->removedForeignKeys) || ! empty($diff->removedIndexes)
955 1596
                || ! empty($diff->renamedIndexes)
956
        ) {
957 1479
            return false;
958
        }
959
960 1584
        $table = new Table($diff->name);
961
962 1584
        $sql       = [];
963 1584
        $tableSql  = [];
964 1584
        $columnSql = [];
965
966 1584
        foreach ($diff->addedColumns as $column) {
967 1467
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
968
                continue;
969
            }
970
971 1467
            $field = array_merge(['unique' => null, 'autoincrement' => null, 'default' => null], $column->toArray());
972 1467
            $type  = $field['type'];
973
            switch (true) {
974 1467
                case isset($field['columnDefinition']) || $field['autoincrement'] || $field['unique']:
975 1466
                case $type instanceof Types\DateTimeType && $field['default'] === $this->getCurrentTimestampSQL():
976 1466
                case $type instanceof Types\DateType && $field['default'] === $this->getCurrentDateSQL():
977 1465
                case $type instanceof Types\TimeType && $field['default'] === $this->getCurrentTimeSQL():
978 1442
                    return false;
979
            }
980
981 1465
            $field['name'] = $column->getQuotedName($this);
982 1465
            if ($type instanceof Types\StringType && $field['length'] === null) {
983 1465
                $field['length'] = 255;
984
            }
985
986 1465
            $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ADD COLUMN ' . $this->getColumnDeclarationSQL($field['name'], $field);
987
        }
988
989 1582
        if (! $this->onSchemaAlterTable($diff, $tableSql)) {
990 1582
            if ($diff->newName !== false) {
991 178
                $newTable = new Identifier($diff->newName);
0 ignored issues
show
Bug introduced by
It seems like $diff->newName can also be of type true; however, parameter $identifier of Doctrine\DBAL\Schema\Identifier::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

991
                $newTable = new Identifier(/** @scrutinizer ignore-type */ $diff->newName);
Loading history...
992 178
                $sql[]    = 'ALTER TABLE ' . $table->getQuotedName($this) . ' RENAME TO ' . $newTable->getQuotedName($this);
993
            }
994
        }
995
996 1582
        return array_merge($sql, $tableSql, $columnSql);
997
    }
998
999
    /**
1000
     * @return string[]
1001
     */
1002 1479
    private function getColumnNamesInAlteredTable(TableDiff $diff) : array
1003
    {
1004 1479
        $columns = [];
1005
1006 1479
        foreach ($diff->fromTable->getColumns() as $columnName => $column) {
0 ignored issues
show
Bug introduced by
The method getColumns() does not exist on null. ( Ignorable by Annotation )

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

1006
        foreach ($diff->fromTable->/** @scrutinizer ignore-call */ getColumns() as $columnName => $column) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1007 1478
            $columns[strtolower($columnName)] = $column->getName();
1008
        }
1009
1010 1479
        foreach ($diff->removedColumns as $columnName => $column) {
1011 1471
            $columnName = strtolower($columnName);
1012 1471
            if (! isset($columns[$columnName])) {
1013
                continue;
1014
            }
1015
1016 1471
            unset($columns[$columnName]);
1017
        }
1018
1019 1479
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
1020 1373
            $columnName                          = $column->getName();
1021 1373
            $columns[strtolower($oldColumnName)] = $columnName;
1022 1373
            $columns[strtolower($columnName)]    = $columnName;
1023
        }
1024
1025 1479
        foreach ($diff->changedColumns as $oldColumnName => $columnDiff) {
1026 940
            $columnName                          = $columnDiff->column->getName();
1027 940
            $columns[strtolower($oldColumnName)] = $columnName;
1028 940
            $columns[strtolower($columnName)]    = $columnName;
1029
        }
1030
1031 1479
        foreach ($diff->addedColumns as $column) {
1032 941
            $columnName                       = $column->getName();
1033 941
            $columns[strtolower($columnName)] = $columnName;
1034
        }
1035
1036 1479
        return $columns;
1037
    }
1038
1039
    /**
1040
     * @return Index[]
1041
     */
1042 1479
    private function getIndexesInAlteredTable(TableDiff $diff) : array
1043
    {
1044 1479
        $indexes     = $diff->fromTable->getIndexes();
1045 1479
        $columnNames = $this->getColumnNamesInAlteredTable($diff);
1046
1047 1479
        foreach ($indexes as $key => $index) {
1048 1473
            foreach ($diff->renamedIndexes as $oldIndexName => $renamedIndex) {
1049 619
                if (strtolower($key) !== strtolower($oldIndexName)) {
1050 619
                    continue;
1051
                }
1052
1053 341
                unset($indexes[$key]);
1054
            }
1055
1056 1473
            $changed      = false;
1057 1473
            $indexColumns = [];
1058 1473
            foreach ($index->getColumns() as $columnName) {
1059 1473
                $normalizedColumnName = strtolower($columnName);
1060 1473
                if (! isset($columnNames[$normalizedColumnName])) {
1061 1369
                    unset($indexes[$key]);
1062 1369
                    continue 2;
1063
                }
1064 1473
1065 1473
                $indexColumns[] = $columnNames[$normalizedColumnName];
1066 1374
                if ($columnName === $columnNames[$normalizedColumnName]) {
1067
                    continue;
1068
                }
1069
1070
                $changed = true;
1071 1473
            }
1072 1473
1073
            if (! $changed) {
1074
                continue;
1075 1369
            }
1076
1077
            $indexes[$key] = new Index($index->getName(), $indexColumns, $index->isUnique(), $index->isPrimary(), $index->getFlags());
1078 1479
        }
1079 1468
1080 1468
        foreach ($diff->removedIndexes as $index) {
1081
            $indexName = $index->getName();
1082
1083
            if ($indexName === null) {
1084 1468
                continue;
1085
            }
1086
1087 1479
            unset($indexes[strtolower($indexName)]);
1088 619
        }
1089 619
1090 619
        foreach (array_merge($diff->changedIndexes, $diff->addedIndexes, $diff->renamedIndexes) as $index) {
1091
            $indexName = $index->getName();
1092 3
1093
            if ($indexName !== null) {
1094
                $indexes[strtolower($indexName)] = $index;
1095
            } else {
1096 1479
                $indexes[] = $index;
1097
            }
1098
        }
1099
1100
        return $indexes;
1101
    }
1102 1479
1103
    /**
1104 1479
     * @return ForeignKeyConstraint[]
1105 1479
     */
1106
    private function getForeignKeysInAlteredTable(TableDiff $diff) : array
1107 1479
    {
1108 1371
        $foreignKeys = $diff->fromTable->getForeignKeys();
1109 1371
        $columnNames = $this->getColumnNamesInAlteredTable($diff);
1110 1371
1111 1371
        foreach ($foreignKeys as $key => $constraint) {
1112 1371
            $changed      = false;
1113 1369
            $localColumns = [];
1114 1369
            foreach ($constraint->getLocalColumns() as $columnName) {
1115
                $normalizedColumnName = strtolower($columnName);
1116 1371
                if (! isset($columnNames[$normalizedColumnName])) {
1117 1371
                    unset($foreignKeys[$key]);
1118 1371
                    continue 2;
1119
                }
1120
1121
                $localColumns[] = $columnNames[$normalizedColumnName];
1122
                if ($columnName === $columnNames[$normalizedColumnName]) {
1123 1371
                    continue;
1124 1371
                }
1125
1126
                $changed = true;
1127 1369
            }
1128
1129
            if (! $changed) {
1130 1479
                continue;
1131 241
            }
1132
1133
            $foreignKeys[$key] = new ForeignKeyConstraint($localColumns, $constraint->getForeignTableName(), $constraint->getForeignColumns(), $constraint->getName(), $constraint->getOptions());
1134
        }
1135 241
1136 241
        foreach ($diff->removedForeignKeys as $constraint) {
1137
            if (! $constraint instanceof ForeignKeyConstraint) {
1138
                $constraint = new Identifier($constraint);
1139
            }
1140 241
1141
            $constraintName = $constraint->getName();
1142
1143 1479
            if ($constraintName === null) {
1144 387
                continue;
1145 387
            }
1146 241
1147
            unset($foreignKeys[strtolower($constraintName)]);
1148 157
        }
1149
1150
        foreach (array_merge($diff->changedForeignKeys, $diff->addedForeignKeys) as $constraint) {
1151
            $constraintName = $constraint->getName();
1152 1479
1153
            if ($constraintName !== null) {
1154
                $foreignKeys[strtolower($constraintName)] = $constraint;
1155
            } else {
1156
                $foreignKeys[] = $constraint;
1157
            }
1158 1479
        }
1159
1160 1479
        return $foreignKeys;
1161
    }
1162 1479
1163 1473
    /**
1164 1472
     * @return Index[]
1165
     */
1166
    private function getPrimaryIndexInAlteredTable(TableDiff $diff) : array
1167 1471
    {
1168
        $primaryIndex = [];
1169
1170 1479
        foreach ($this->getIndexesInAlteredTable($diff) as $index) {
1171
            if (! $index->isPrimary()) {
1172
                continue;
1173
            }
1174
1175
            $primaryIndex = [$index->getName() => $index];
1176
        }
1177
1178
        return $primaryIndex;
1179
    }
1180
}
1181