Completed
Push — master ( 11b67b...3a0a1d )
by Sergei
19:09 queued 19:04
created

SqlitePlatform::getListViewsSQL()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
ccs 0
cts 2
cp 0
cc 1
nc 1
nop 1
crap 2
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 function array_merge;
16
use function array_unique;
17
use function array_values;
18
use function implode;
19
use function is_numeric;
20
use function sprintf;
21
use function sqrt;
22
use function str_replace;
23
use function strlen;
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 2054
    public function getRegexpExpression()
39
    {
40 2054
        return 'REGEXP';
41
    }
42
43
    /**
44
     * {@inheritDoc}
45
     *
46
     * @deprecated Use application-generated UUIDs instead
47
     */
48
    public function getGuidExpression()
49
    {
50
        return "HEX(RANDOMBLOB(4)) || '-' || HEX(RANDOMBLOB(2)) || '-4' || "
51
            . "SUBSTR(HEX(RANDOMBLOB(2)), 2) || '-' || "
52
            . "SUBSTR('89AB', 1 + (ABS(RANDOM()) % 4), 1) || "
53
            . "SUBSTR(HEX(RANDOMBLOB(2)), 2) || '-' || HEX(RANDOMBLOB(6))";
54
    }
55
56
    /**
57
     * {@inheritDoc}
58
     */
59
    public function getNowExpression($type = 'timestamp')
60
    {
61
        switch ($type) {
62
            case 'time':
63
                return 'time(\'now\')';
64
            case 'date':
65
                return 'date(\'now\')';
66
            case 'timestamp':
67
            default:
68
                return 'datetime(\'now\')';
69
        }
70
    }
71
72
    /**
73
     * {@inheritDoc}
74
     */
75
    public function getTrimExpression($str, $pos = TrimMode::UNSPECIFIED, $char = false)
76
    {
77
        $trimChar = $char !== false ? (', ' . $char) : '';
78
79
        switch ($pos) {
80
            case TrimMode::LEADING:
81
                $trimFn = 'LTRIM';
82
                break;
83
84
            case TrimMode::TRAILING:
85
                $trimFn = 'RTRIM';
86
                break;
87
88
            default:
89
                $trimFn = 'TRIM';
90
        }
91
92
        return $trimFn . '(' . $str . $trimChar . ')';
93
    }
94
95
    /**
96
     * {@inheritDoc}
97
     *
98
     * SQLite only supports the 2 parameter variant of this function
99
     */
100 2054
    public function getSubstringExpression($value, $position, $length = null)
101
    {
102 2054
        if ($length !== null) {
103 2054
            return 'SUBSTR(' . $value . ', ' . $position . ', ' . $length . ')';
104
        }
105
106 2054
        return 'SUBSTR(' . $value . ', ' . $position . ', LENGTH(' . $value . '))';
107
    }
108
109
    /**
110
     * {@inheritDoc}
111
     */
112
    public function getLocateExpression($str, $substr, $startPos = false)
113
    {
114
        if ($startPos === false) {
115
            return 'LOCATE(' . $str . ', ' . $substr . ')';
116
        }
117
118
        return 'LOCATE(' . $str . ', ' . $substr . ', ' . $startPos . ')';
119
    }
120
121
    /**
122
     * {@inheritdoc}
123
     */
124 1381
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
125
    {
126 1381
        switch ($unit) {
127
            case DateIntervalUnit::SECOND:
128
            case DateIntervalUnit::MINUTE:
129
            case DateIntervalUnit::HOUR:
130
                return 'DATETIME(' . $date . ",'" . $operator . $interval . ' ' . $unit . "')";
131
132
            default:
133 1381
                switch ($unit) {
134
                    case DateIntervalUnit::WEEK:
135
                        $interval *= 7;
136
                        $unit      = DateIntervalUnit::DAY;
137
                        break;
138
139
                    case DateIntervalUnit::QUARTER:
140
                        $interval *= 3;
141
                        $unit      = DateIntervalUnit::MONTH;
142
                        break;
143
                }
144
145 1381
                if (! is_numeric($interval)) {
146 1352
                    $interval = "' || " . $interval . " || '";
147
                }
148
149 1381
                return 'DATE(' . $date . ",'" . $operator . $interval . ' ' . $unit . "')";
150
        }
151
    }
152
153
    /**
154
     * {@inheritDoc}
155
     */
156
    public function getDateDiffExpression($date1, $date2)
157
    {
158
        return sprintf("JULIANDAY(%s, 'start of day') - JULIANDAY(%s, 'start of day')", $date1, $date2);
159
    }
160
161
    /**
162
     * {@inheritDoc}
163
     */
164 2027
    protected function _getTransactionIsolationLevelSQL($level)
165
    {
166 2
        switch ($level) {
167 2025
            case TransactionIsolationLevel::READ_UNCOMMITTED:
168 2027
                return 0;
169 2025
            case TransactionIsolationLevel::READ_COMMITTED:
170 2025
            case TransactionIsolationLevel::REPEATABLE_READ:
171 2025
            case TransactionIsolationLevel::SERIALIZABLE:
172 2027
                return 1;
173
            default:
174
                return parent::_getTransactionIsolationLevelSQL($level);
175
        }
176
    }
177
178
    /**
179
     * {@inheritDoc}
180
     */
181 2027
    public function getSetTransactionIsolationSQL($level)
182
    {
183 2027
        return 'PRAGMA read_uncommitted = ' . $this->_getTransactionIsolationLevelSQL($level);
184
    }
185
186
    /**
187
     * {@inheritDoc}
188
     */
189 2000
    public function prefersIdentityColumns()
190
    {
191 2000
        return true;
192
    }
193
194
    /**
195
     * {@inheritDoc}
196
     */
197 920
    public function getBooleanTypeDeclarationSQL(array $field)
198
    {
199 920
        return 'BOOLEAN';
200
    }
201
202
    /**
203
     * {@inheritDoc}
204
     */
205 2017
    public function getIntegerTypeDeclarationSQL(array $field)
206
    {
207 2017
        return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($field);
208
    }
209
210
    /**
211
     * {@inheritDoc}
212
     */
213 1865
    public function getBigIntTypeDeclarationSQL(array $field)
214
    {
215
        //  SQLite autoincrement is implicit for INTEGER PKs, but not for BIGINT fields.
216 1865
        if (! empty($field['autoincrement'])) {
217 1865
            return $this->getIntegerTypeDeclarationSQL($field);
218
        }
219
220 1865
        return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
221
    }
222
223
    /**
224
     * {@inheritDoc}
225
     */
226 1948
    public function getTinyIntTypeDeclarationSql(array $field)
227
    {
228
        //  SQLite autoincrement is implicit for INTEGER PKs, but not for TINYINT fields.
229 1948
        if (! empty($field['autoincrement'])) {
230 1948
            return $this->getIntegerTypeDeclarationSQL($field);
231
        }
232
233 1946
        return 'TINYINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
234
    }
235
236
    /**
237
     * {@inheritDoc}
238
     */
239 1919
    public function getSmallIntTypeDeclarationSQL(array $field)
240
    {
241
        //  SQLite autoincrement is implicit for INTEGER PKs, but not for SMALLINT fields.
242 1919
        if (! empty($field['autoincrement'])) {
243 1919
            return $this->getIntegerTypeDeclarationSQL($field);
244
        }
245
246 1919
        return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
247
    }
248
249
    /**
250
     * {@inheritDoc}
251
     */
252 1892
    public function getMediumIntTypeDeclarationSql(array $field)
253
    {
254
        //  SQLite autoincrement is implicit for INTEGER PKs, but not for MEDIUMINT fields.
255 1892
        if (! empty($field['autoincrement'])) {
256 1892
            return $this->getIntegerTypeDeclarationSQL($field);
257
        }
258
259 1892
        return 'MEDIUMINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
260
    }
261
262
    /**
263
     * {@inheritDoc}
264
     */
265
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
266
    {
267
        return 'DATETIME';
268
    }
269
270
    /**
271
     * {@inheritDoc}
272
     */
273
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
274
    {
275
        return 'DATE';
276
    }
277
278
    /**
279
     * {@inheritDoc}
280
     */
281
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
282
    {
283
        return 'TIME';
284
    }
285
286
    /**
287
     * {@inheritDoc}
288
     */
289 2017
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
290
    {
291
        // sqlite autoincrement is only possible for the primary key
292 2017
        if (! empty($columnDef['autoincrement'])) {
293 1989
            return ' PRIMARY KEY AUTOINCREMENT';
294
        }
295
296 1984
        return ! empty($columnDef['unsigned']) ? ' UNSIGNED' : '';
297
    }
298
299
    /**
300
     * {@inheritDoc}
301
     */
302 1576
    public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey)
303
    {
304 1576
        return parent::getForeignKeyDeclarationSQL(new ForeignKeyConstraint(
305 1576
            $foreignKey->getQuotedLocalColumns($this),
306 1576
            str_replace('.', '__', $foreignKey->getQuotedForeignTableName($this)),
307 1576
            $foreignKey->getQuotedForeignColumns($this),
308 1576
            $foreignKey->getName(),
309 1576
            $foreignKey->getOptions()
310
        ));
311
    }
312
313
    /**
314
     * {@inheritDoc}
315
     */
316 1726
    protected function _getCreateTableSQL($name, array $columns, array $options = [])
317
    {
318 1726
        $name        = str_replace('.', '__', $name);
319 1726
        $queryFields = $this->getColumnDeclarationListSQL($columns);
320
321 1726
        if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
322
            foreach ($options['uniqueConstraints'] as $name => $definition) {
0 ignored issues
show
introduced by
$name is overwriting one of the parameters of this function.
Loading history...
323
                $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
324
            }
325
        }
326
327 1726
        $queryFields .= $this->getNonAutoincrementPrimaryKeyDefinition($columns, $options);
328
329 1726
        if (isset($options['foreignKeys'])) {
330 1724
            foreach ($options['foreignKeys'] as $foreignKey) {
331 1576
                $queryFields .= ', ' . $this->getForeignKeyDeclarationSQL($foreignKey);
332
            }
333
        }
334
335 1726
        $query = ['CREATE TABLE ' . $name . ' (' . $queryFields . ')'];
336
337 1726
        if (isset($options['alter']) && $options['alter'] === true) {
338 1563
            return $query;
339
        }
340
341 1702
        if (isset($options['indexes']) && ! empty($options['indexes'])) {
342 1574
            foreach ($options['indexes'] as $indexDef) {
343 1574
                $query[] = $this->getCreateIndexSQL($indexDef, $name);
344
            }
345
        }
346
347 1702
        if (isset($options['unique']) && ! empty($options['unique'])) {
348
            foreach ($options['unique'] as $indexDef) {
349
                $query[] = $this->getCreateIndexSQL($indexDef, $name);
350
            }
351
        }
352
353 1702
        return $query;
354
    }
355
356
    /**
357
     * Generate a PRIMARY KEY definition if no autoincrement value is used
358
     *
359
     * @param mixed[][] $columns
360
     * @param mixed[]   $options
361
     */
362 1726
    private function getNonAutoincrementPrimaryKeyDefinition(array $columns, array $options) : string
363
    {
364 1726
        if (empty($options['primary'])) {
365 1297
            return '';
366
        }
367
368 1698
        $keyColumns = array_unique(array_values($options['primary']));
369
370 1698
        foreach ($keyColumns as $keyColumn) {
371 1698
            if (! empty($columns[$keyColumn]['autoincrement'])) {
372 1698
                return '';
373
            }
374
        }
375
376 1584
        return ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
377
    }
378
379
    /**
380
     * {@inheritDoc}
381
     */
382 1868
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
383
    {
384 1868
        return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)')
385 1868
                : ($length ? 'VARCHAR(' . $length . ')' : 'TEXT');
386
    }
387
388
    /**
389
     * {@inheritdoc}
390
     */
391 1514
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
392
    {
393 1514
        return 'BLOB';
394
    }
395
396
    /**
397
     * {@inheritdoc}
398
     */
399 1516
    public function getBinaryMaxLength()
400
    {
401 1516
        return 0;
402
    }
403
404
    /**
405
     * {@inheritdoc}
406
     */
407 1516
    public function getBinaryDefaultLength()
408
    {
409 1516
        return 0;
410
    }
411
412
    /**
413
     * {@inheritDoc}
414
     */
415 569
    public function getClobTypeDeclarationSQL(array $field)
416
    {
417 569
        return 'CLOB';
418
    }
419
420
    /**
421
     * {@inheritDoc}
422
     */
423 1460
    public function getListTableConstraintsSQL($table)
424
    {
425 1460
        $table = str_replace('.', '__', $table);
426
427 1460
        return sprintf(
428 2
            "SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name = %s AND sql NOT NULL ORDER BY name",
429 1460
            $this->quoteStringLiteral($table)
430
        );
431
    }
432
433
    /**
434
     * {@inheritDoc}
435
     */
436 1437
    public function getListTableColumnsSQL($table, $currentDatabase = null)
437
    {
438 1437
        $table = str_replace('.', '__', $table);
439
440 1437
        return sprintf('PRAGMA table_info(%s)', $this->quoteStringLiteral($table));
441
    }
442
443
    /**
444
     * {@inheritDoc}
445
     */
446 1410
    public function getListTableIndexesSQL($table, $currentDatabase = null)
447
    {
448 1410
        $table = str_replace('.', '__', $table);
449
450 1410
        return sprintf('PRAGMA index_list(%s)', $this->quoteStringLiteral($table));
451
    }
452
453
    /**
454
     * {@inheritDoc}
455
     */
456 114
    public function getListTablesSQL()
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 114
             . "WHERE type = 'table' ORDER BY name";
461
    }
462
463
    /**
464
     * {@inheritDoc}
465
     */
466
    public function getListViewsSQL($database)
467
    {
468
        return "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL";
469
    }
470
471
    /**
472
     * {@inheritDoc}
473
     */
474
    public function getCreateViewSQL($name, $sql)
475
    {
476
        return 'CREATE VIEW ' . $name . ' AS ' . $sql;
477
    }
478
479
    /**
480
     * {@inheritDoc}
481
     */
482
    public function getDropViewSQL($name)
483
    {
484
        return 'DROP VIEW ' . $name;
485
    }
486
487
    /**
488
     * {@inheritDoc}
489
     */
490 1576
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
491
    {
492 1576
        $query = parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
493
494 1576
        $query .= ($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false ? ' ' : ' NOT ') . 'DEFERRABLE';
495 1576
        $query .= ' INITIALLY ' . ($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false ? 'DEFERRED' : 'IMMEDIATE');
496
497 1576
        return $query;
498
    }
499
500
    /**
501
     * {@inheritDoc}
502
     */
503
    public function supportsIdentityColumns()
504
    {
505
        return true;
506
    }
507
508
    /**
509
     * {@inheritDoc}
510
     */
511 1329
    public function supportsColumnCollation()
512
    {
513 1329
        return true;
514
    }
515
516
    /**
517
     * {@inheritDoc}
518
     */
519 1734
    public function supportsInlineColumnComments()
520
    {
521 1734
        return true;
522
    }
523
524
    /**
525
     * {@inheritDoc}
526
     */
527 2085
    public function getName()
528
    {
529 2085
        return 'sqlite';
530
    }
531
532
    /**
533
     * {@inheritDoc}
534
     */
535 677
    public function getTruncateTableSQL($tableName, $cascade = false)
536
    {
537 677
        $tableIdentifier = new Identifier($tableName);
538 677
        $tableName       = str_replace('.', '__', $tableIdentifier->getQuotedName($this));
539
540 677
        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
     * @return float
549
     */
550
    public static function udfSqrt($value)
551
    {
552
        return sqrt($value);
553
    }
554
555
    /**
556
     * User-defined function for Sqlite that implements MOD(a, b).
557
     *
558
     * @param int $a
559
     * @param int $b
560
     *
561
     * @return int
562
     */
563
    public static function udfMod($a, $b)
564
    {
565
        return $a % $b;
566
    }
567
568
    /**
569
     * @param string $str
570
     * @param string $substr
571
     * @param int    $offset
572
     *
573
     * @return int
574
     */
575
    public static function udfLocate($str, $substr, $offset = 0)
576
    {
577
        // SQL's LOCATE function works on 1-based positions, while PHP's strpos works on 0-based positions.
578
        // So we have to make them compatible if an offset is given.
579
        if ($offset > 0) {
580
            $offset -= 1;
581
        }
582
583
        $pos = strpos($str, $substr, $offset);
584
585
        if ($pos !== false) {
586
            return $pos + 1;
587
        }
588
589
        return 0;
590
    }
591
592
    /**
593
     * {@inheritDoc}
594
     */
595
    public function getForUpdateSql()
596
    {
597
        return '';
598
    }
599
600
    /**
601
     * {@inheritDoc}
602
     */
603 498
    public function getInlineColumnCommentSQL($comment)
604
    {
605 498
        return '--' . str_replace("\n", "\n--", $comment) . "\n";
606
    }
607
608
    /**
609
     * {@inheritDoc}
610
     */
611 1200
    protected function initializeDoctrineTypeMappings()
612
    {
613 1200
        $this->doctrineTypeMapping = [
614
            'boolean'          => 'boolean',
615
            'tinyint'          => 'boolean',
616
            'smallint'         => 'smallint',
617
            'mediumint'        => 'integer',
618
            'int'              => 'integer',
619
            'integer'          => 'integer',
620
            'serial'           => 'integer',
621
            'bigint'           => 'bigint',
622
            'bigserial'        => 'bigint',
623
            'clob'             => 'text',
624
            'tinytext'         => 'text',
625
            'mediumtext'       => 'text',
626
            'longtext'         => 'text',
627
            'text'             => 'text',
628
            'varchar'          => 'string',
629
            'longvarchar'      => 'string',
630
            'varchar2'         => 'string',
631
            'nvarchar'         => 'string',
632
            'image'            => 'string',
633
            'ntext'            => 'string',
634
            'char'             => 'string',
635
            'date'             => 'date',
636
            'datetime'         => 'datetime',
637
            'timestamp'        => 'datetime',
638
            'time'             => 'time',
639
            'float'            => 'float',
640
            'double'           => 'float',
641
            'double precision' => 'float',
642
            'real'             => 'float',
643
            'decimal'          => 'decimal',
644
            'numeric'          => 'decimal',
645
            'blob'             => 'blob',
646
        ];
647 1200
    }
648
649
    /**
650
     * {@inheritDoc}
651
     */
652 1754
    protected function getReservedKeywordsClass()
653
    {
654 1754
        return Keywords\SQLiteKeywords::class;
655
    }
656
657
    /**
658
     * {@inheritDoc}
659
     */
660 1563
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
661
    {
662 1563
        if (! $diff->fromTable instanceof Table) {
663
            throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema');
664
        }
665
666 1563
        $sql = [];
667 1563
        foreach ($diff->fromTable->getIndexes() as $index) {
668 1551
            if ($index->isPrimary()) {
669 1547
                continue;
670
            }
671
672 1545
            $sql[] = $this->getDropIndexSQL($index, $diff->name);
673
        }
674
675 1563
        return $sql;
676
    }
677
678
    /**
679
     * {@inheritDoc}
680
     */
681 1563
    protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff)
682
    {
683 1563
        if (! $diff->fromTable instanceof Table) {
684
            throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema');
685
        }
686
687 1563
        $sql       = [];
688 1563
        $tableName = $diff->getNewName();
689
690 1563
        if ($tableName === false) {
691 882
            $tableName = $diff->getName($this);
692
        }
693
694 1563
        foreach ($this->getIndexesInAlteredTable($diff) as $index) {
695 1551
            if ($index->isPrimary()) {
696 1547
                continue;
697
            }
698
699 1549
            $sql[] = $this->getCreateIndexSQL($index, $tableName->getQuotedName($this));
700
        }
701
702 1563
        return $sql;
703
    }
704
705
    /**
706
     * {@inheritDoc}
707
     */
708 1763
    protected function doModifyLimitQuery($query, $limit, $offset)
709
    {
710 1763
        if ($limit === null && $offset > 0) {
711 1703
            return $query . ' LIMIT -1 OFFSET ' . $offset;
712
        }
713
714 1761
        return parent::doModifyLimitQuery($query, $limit, $offset);
715
    }
716
717
    /**
718
     * {@inheritDoc}
719
     */
720 1514
    public function getBlobTypeDeclarationSQL(array $field)
721
    {
722 1514
        return 'BLOB';
723
    }
724
725
    /**
726
     * {@inheritDoc}
727
     */
728
    public function getTemporaryTableName($tableName)
729
    {
730
        $tableName = str_replace('.', '__', $tableName);
731
732
        return $tableName;
733
    }
734
735
    /**
736
     * {@inheritDoc}
737
     *
738
     * Sqlite Platform emulates schema by underscoring each dot and generating tables
739
     * into the default database.
740
     *
741
     * This hack is implemented to be able to use SQLite as testdriver when
742
     * using schema supporting databases.
743
     */
744
    public function canEmulateSchemas()
745
    {
746
        return true;
747
    }
748
749
    /**
750
     * {@inheritDoc}
751
     */
752 1584
    public function supportsForeignKeyConstraints()
753
    {
754 1584
        return false;
755
    }
756
757
    /**
758
     * {@inheritDoc}
759
     */
760
    public function getCreatePrimaryKeySQL(Index $index, $table)
761
    {
762
        throw new DBALException('Sqlite platform does not support alter primary key.');
763
    }
764
765
    /**
766
     * {@inheritdoc}
767
     */
768 1813
    public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table)
769
    {
770 1813
        throw new DBALException('Sqlite platform does not support alter foreign key.');
771
    }
772
773
    /**
774
     * {@inheritdoc}
775
     */
776
    public function getDropForeignKeySQL($foreignKey, $table)
777
    {
778
        throw new DBALException('Sqlite platform does not support alter foreign key.');
779
    }
780
781
    /**
782
     * {@inheritDoc}
783
     */
784 1784
    public function getCreateConstraintSQL(Constraint $constraint, $table)
785
    {
786 1784
        throw new DBALException('Sqlite platform does not support alter constraint.');
787
    }
788
789
    /**
790
     * {@inheritDoc}
791
     */
792 1728
    public function getCreateTableSQL(Table $table, $createFlags = null)
793
    {
794 1728
        $createFlags = $createFlags ?? self::CREATE_INDEXES | self::CREATE_FOREIGNKEYS;
795
796 1728
        return parent::getCreateTableSQL($table, $createFlags);
797
    }
798
799
    /**
800
     * {@inheritDoc}
801
     */
802 1406
    public function getListTableForeignKeysSQL($table, $database = null)
0 ignored issues
show
Unused Code introduced by
The parameter $database 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

802
    public function getListTableForeignKeysSQL($table, /** @scrutinizer ignore-unused */ $database = null)

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...
803
    {
804 1406
        $table = str_replace('.', '__', $table);
805
806 1406
        return sprintf('PRAGMA foreign_key_list(%s)', $this->quoteStringLiteral($table));
807
    }
808
809
    /**
810
     * {@inheritDoc}
811
     */
812 1677
    public function getAlterTableSQL(TableDiff $diff)
813
    {
814 1677
        $sql = $this->getSimpleAlterTableSQL($diff);
815 1677
        if ($sql !== false) {
0 ignored issues
show
introduced by
The condition $sql !== false is always false.
Loading history...
816 1649
            return $sql;
817
        }
818
819 1648
        $fromTable = $diff->fromTable;
820 1648
        if (! $fromTable instanceof Table) {
821 1624
            throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema');
822
        }
823
824 1563
        $table = clone $fromTable;
825
826 1563
        $columns        = [];
827 1563
        $oldColumnNames = [];
828 1563
        $newColumnNames = [];
829 1563
        $columnSql      = [];
830
831 1563
        foreach ($table->getColumns() as $columnName => $column) {
832 1561
            $columnName                  = strtolower($columnName);
833 1561
            $columns[$columnName]        = $column;
834 1561
            $oldColumnNames[$columnName] = $newColumnNames[$columnName] = $column->getQuotedName($this);
835
        }
836
837 1563
        foreach ($diff->removedColumns as $columnName => $column) {
838 1547
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
839
                continue;
840
            }
841
842 1547
            $columnName = strtolower($columnName);
843 1547
            if (! isset($columns[$columnName])) {
844
                continue;
845
            }
846
847
            unset(
848 1547
                $columns[$columnName],
849 1547
                $oldColumnNames[$columnName],
850 1547
                $newColumnNames[$columnName]
851
            );
852
        }
853
854 1563
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
855 1549
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
856
                continue;
857
            }
858
859 1549
            $oldColumnName = strtolower($oldColumnName);
860 1549
            if (isset($columns[$oldColumnName])) {
861 1549
                unset($columns[$oldColumnName]);
862
            }
863
864 1549
            $columns[strtolower($column->getName())] = $column;
865
866 1549
            if (! isset($newColumnNames[$oldColumnName])) {
867
                continue;
868
            }
869
870 1549
            $newColumnNames[$oldColumnName] = $column->getQuotedName($this);
871
        }
872
873 1563
        foreach ($diff->changedColumns as $oldColumnName => $columnDiff) {
874 930
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
875
                continue;
876
            }
877
878 930
            if (isset($columns[$oldColumnName])) {
879 928
                unset($columns[$oldColumnName]);
880
            }
881
882 930
            $columns[strtolower($columnDiff->column->getName())] = $columnDiff->column;
883
884 930
            if (! isset($newColumnNames[$oldColumnName])) {
885 623
                continue;
886
            }
887
888 928
            $newColumnNames[$oldColumnName] = $columnDiff->column->getQuotedName($this);
889
        }
890
891 1563
        foreach ($diff->addedColumns as $columnName => $column) {
892 924
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
893
                continue;
894
            }
895
896 924
            $columns[strtolower($columnName)] = $column;
897
        }
898
899 1563
        $sql      = [];
900 1563
        $tableSql = [];
901 1563
        if (! $this->onSchemaAlterTable($diff, $tableSql)) {
902 1563
            $dataTable = new Table('__temp__' . $table->getName());
903
904 1563
            $newTable = new Table($table->getQuotedName($this), $columns, $this->getPrimaryIndexInAlteredTable($diff), $this->getForeignKeysInAlteredTable($diff), 0, $table->getOptions());
905 1563
            $newTable->addOption('alter', true);
906
907 1563
            $sql = $this->getPreAlterTableIndexForeignKeySQL($diff);
908
            //$sql = array_merge($sql, $this->getCreateTableSQL($dataTable, 0));
909 1563
            $sql[] = sprintf('CREATE TEMPORARY TABLE %s AS SELECT %s FROM %s', $dataTable->getQuotedName($this), implode(', ', $oldColumnNames), $table->getQuotedName($this));
910 1563
            $sql[] = $this->getDropTableSQL($fromTable);
911
912 1563
            $sql   = array_merge($sql, $this->getCreateTableSQL($newTable));
913 1563
            $sql[] = sprintf('INSERT INTO %s (%s) SELECT %s FROM %s', $newTable->getQuotedName($this), implode(', ', $newColumnNames), implode(', ', $oldColumnNames), $dataTable->getQuotedName($this));
914 1563
            $sql[] = $this->getDropTableSQL($dataTable);
915
916 1563
            $newName = $diff->getNewName();
917
918 1563
            if ($newName !== false) {
919 1545
                $sql[] = sprintf(
920 6
                    'ALTER TABLE %s RENAME TO %s',
921 1545
                    $newTable->getQuotedName($this),
922 1545
                    $newName->getQuotedName($this)
923
                );
924
            }
925
926 1563
            $sql = array_merge($sql, $this->getPostAlterTableIndexForeignKeySQL($diff));
927
        }
928
929 1563
        return array_merge($sql, $tableSql, $columnSql);
930
    }
931
932
    /**
933
     * @return string[]|false
934
     */
935 1677
    private function getSimpleAlterTableSQL(TableDiff $diff)
936
    {
937
        // Suppress changes on integer type autoincrement columns.
938 1677
        foreach ($diff->changedColumns as $oldColumnName => $columnDiff) {
939 930
            if (! $columnDiff->fromColumn instanceof Column ||
940 922
                ! $columnDiff->column instanceof Column ||
941 922
                ! $columnDiff->column->getAutoincrement() ||
942 930
                ! $columnDiff->column->getType() instanceof Types\IntegerType
943
            ) {
944 930
                continue;
945
            }
946
947
            if (! $columnDiff->hasChanged('type') && $columnDiff->hasChanged('unsigned')) {
948
                unset($diff->changedColumns[$oldColumnName]);
949
950
                continue;
951
            }
952
953
            $fromColumnType = $columnDiff->fromColumn->getType();
954
955
            if (! ($fromColumnType instanceof Types\SmallIntType) && ! ($fromColumnType instanceof Types\BigIntType)) {
956
                continue;
957
            }
958
959
            unset($diff->changedColumns[$oldColumnName]);
960
        }
961
962 1677
        if (! empty($diff->renamedColumns) || ! empty($diff->addedForeignKeys) || ! empty($diff->addedIndexes)
963 1667
                || ! empty($diff->changedColumns) || ! empty($diff->changedForeignKeys) || ! empty($diff->changedIndexes)
964 1659
                || ! empty($diff->removedColumns) || ! empty($diff->removedForeignKeys) || ! empty($diff->removedIndexes)
965 1677
                || ! empty($diff->renamedIndexes)
966
        ) {
967 1563
            return false;
968
        }
969
970 1653
        $table = new Table($diff->name);
971
972 1653
        $sql       = [];
973 1653
        $tableSql  = [];
974 1653
        $columnSql = [];
975
976 1653
        foreach ($diff->addedColumns as $column) {
977 1653
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
978
                continue;
979
            }
980
981 1653
            $field = array_merge(['unique' => null, 'autoincrement' => null, 'default' => null], $column->toArray());
982 1653
            $type  = $field['type'];
983
            switch (true) {
984 1653
                case isset($field['columnDefinition']) || $field['autoincrement'] || $field['unique']:
985 1651
                case $type instanceof Types\DateTimeType && $field['default'] === $this->getCurrentTimestampSQL():
986 1651
                case $type instanceof Types\DateType && $field['default'] === $this->getCurrentDateSQL():
987 1649
                case $type instanceof Types\TimeType && $field['default'] === $this->getCurrentTimeSQL():
988 1624
                    return false;
989
            }
990
991 1649
            $field['name'] = $column->getQuotedName($this);
992 1649
            if ($type instanceof Types\StringType && $field['length'] === null) {
993 1649
                $field['length'] = 255;
994
            }
995
996 1649
            $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ADD COLUMN ' . $this->getColumnDeclarationSQL($field['name'], $field);
997
        }
998
999 1649
        if (! $this->onSchemaAlterTable($diff, $tableSql)) {
1000 1649
            if ($diff->newName !== false) {
1001
                $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

1001
                $newTable = new Identifier(/** @scrutinizer ignore-type */ $diff->newName);
Loading history...
1002
                $sql[]    = 'ALTER TABLE ' . $table->getQuotedName($this) . ' RENAME TO ' . $newTable->getQuotedName($this);
1003
            }
1004
        }
1005
1006 1649
        return array_merge($sql, $tableSql, $columnSql);
1007
    }
1008
1009
    /**
1010
     * @return string[]
1011
     */
1012 1563
    private function getColumnNamesInAlteredTable(TableDiff $diff)
1013
    {
1014 1563
        $columns = [];
1015
1016 1563
        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

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