Failed Conditions
Pull Request — develop (#3348)
by Sergei
67:43 queued 02:19
created

SqlitePlatform   F

Complexity

Total Complexity 210

Size/Duplication

Total Lines 1148
Duplicated Lines 0 %

Test Coverage

Coverage 91.06%

Importance

Changes 0
Metric Value
wmc 210
eloc 430
dl 0
loc 1148
ccs 397
cts 436
cp 0.9106
rs 2
c 0
b 0
f 0

67 Methods

Rating   Name   Duplication   Size   Complexity  
A getForUpdateSql() 0 3 1
A udfSqrt() 0 3 1
A getRegexpExpression() 0 3 1
A udfLocate() 0 15 3
A getNowExpression() 0 10 4
A getListViewsSQL() 0 3 1
A getName() 0 3 1
A getTinyIntTypeDeclarationSql() 0 8 2
A getCreateTableSQL() 0 3 1
A getBlobTypeDeclarationSQL() 0 3 1
B getTrimExpression() 0 32 6
A getBinaryDefaultLength() 0 3 1
A getCreateConstraintSQL() 0 3 1
A supportsForeignKeyConstraints() 0 3 1
A getNonAutoincrementPrimaryKeyDefinition() 0 15 4
A getBinaryTypeDeclarationSQLSnippet() 0 3 1
A getReservedKeywordsClass() 0 3 1
A getInlineColumnCommentSQL() 0 7 3
A getListTableColumnsSQL() 0 5 1
A getMediumIntTypeDeclarationSql() 0 8 2
A getTemporaryTableName() 0 5 1
A getDateTypeDeclarationSQL() 0 3 1
B getColumnNamesInAlteredTable() 0 35 7
A getDateTimeTypeDeclarationSQL() 0 3 1
A getListTableForeignKeysSQL() 0 5 1
A getListTablesSQL() 0 5 1
A getClobTypeDeclarationSQL() 0 3 1
A getListTableIndexesSQL() 0 5 1
D getSimpleAlterTableSQL() 0 72 35
A getPreAlterTableIndexForeignKeySQL() 0 16 4
A getCreateViewSQL() 0 3 1
A getIntegerTypeDeclarationSQL() 0 3 1
C _getCreateTableSQL() 0 38 14
A getBooleanTypeDeclarationSQL() 0 3 1
A getSmallIntTypeDeclarationSQL() 0 8 2
A getForeignKeyDeclarationSQL() 0 8 1
A supportsColumnCollation() 0 3 1
A getSubstringExpression() 0 7 2
A doModifyLimitQuery() 0 7 3
A getTruncateTableSQL() 0 6 1
A getBinaryMaxLength() 0 3 1
A getLocateExpression() 0 7 2
A getCreatePrimaryKeySQL() 0 3 1
F getAlterTableSQL() 0 119 19
A prefersIdentityColumns() 0 3 1
A udfMod() 0 3 1
B getDateArithmeticIntervalExpression() 0 26 7
A getVarcharTypeDeclarationSQLSnippet() 0 5 4
A getAdvancedForeignKeyOptionsSQL() 0 8 5
A getDropForeignKeySQL() 0 3 1
A getDateDiffExpression() 0 3 1
A _getTransactionIsolationLevelSQL() 0 11 5
A getTimeTypeDeclarationSQL() 0 3 1
A canEmulateSchemas() 0 3 1
A getPostAlterTableIndexForeignKeySQL() 0 22 5
A initializeDoctrineTypeMappings() 0 36 1
A getCreateForeignKeySQL() 0 3 1
A getSetTransactionIsolationSQL() 0 3 1
A _getCommonIntegerTypeDeclarationSQL() 0 8 3
A supportsIdentityColumns() 0 3 1
A getListTableConstraintsSQL() 0 7 1
A getBigIntTypeDeclarationSQL() 0 8 2
A getDropViewSQL() 0 3 1
A supportsInlineColumnComments() 0 3 1
A getPrimaryIndexInAlteredTable() 0 13 3
B getForeignKeysInAlteredTable() 0 55 11
C getIndexesInAlteredTable() 0 59 12

How to fix   Complexity   

Complex Class

Complex classes like SqlitePlatform often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SqlitePlatform, and based on these observations, apply Extract Interface, too.

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

993
                $newTable = new Identifier(/** @scrutinizer ignore-type */ $diff->newName);
Loading history...
994
                $sql[]    = 'ALTER TABLE ' . $table->getQuotedName($this) . ' RENAME TO ' . $newTable->getQuotedName($this);
995
            }
996 1582
        }
997
998
        return array_merge($sql, $tableSql, $columnSql);
999
    }
1000
1001
    /**
1002 1479
     * @return string[]
1003
     */
1004 1479
    private function getColumnNamesInAlteredTable(TableDiff $diff) : array
1005
    {
1006 1479
        $columns = [];
1007 1478
1008
        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

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