Completed
Pull Request — master (#3206)
by Ilya
39:07 queued 24:33
created

SqlitePlatform   F

Complexity

Total Complexity 210

Size/Duplication

Total Lines 1136
Duplicated Lines 0 %

Test Coverage

Coverage 91.1%

Importance

Changes 0
Metric Value
wmc 210
eloc 416
dl 0
loc 1136
ccs 389
cts 427
cp 0.911
rs 2
c 0
b 0
f 0

68 Methods

Rating   Name   Duplication   Size   Complexity  
A udfSqrt() 0 3 1
A getForUpdateSql() 0 3 1
A getReservedKeywordsClass() 0 3 1
A getInlineColumnCommentSQL() 0 3 1
A udfMod() 0 3 1
A getListViewsSQL() 0 3 1
A getName() 0 3 1
A getTinyIntTypeDeclarationSql() 0 8 2
A getBinaryDefaultLength() 0 3 1
A getNonAutoincrementPrimaryKeyDefinition() 0 15 5
A getBinaryTypeDeclarationSQLSnippet() 0 3 1
A getListTableColumnsSQL() 0 5 1
A getRegexpExpression() 0 3 1
A getMediumIntTypeDeclarationSql() 0 8 2
A getDateTypeDeclarationSQL() 0 3 1
A getDateTimeTypeDeclarationSQL() 0 3 1
A getListTablesSQL() 0 5 1
A getClobTypeDeclarationSQL() 0 3 1
A getListTableIndexesSQL() 0 5 1
A getNowExpression() 0 10 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 getGuidExpression() 0 6 1
A getForeignKeyDeclarationSQL() 0 8 1
A supportsColumnCollation() 0 3 1
A getSubstringExpression() 0 7 2
A getBinaryMaxLength() 0 3 1
A getLocateExpression() 0 7 2
A prefersIdentityColumns() 0 3 1
A getVarcharTypeDeclarationSQLSnippet() 0 4 4
A getAdvancedForeignKeyOptionsSQL() 0 8 5
A getDateDiffExpression() 0 3 1
A getTimeTypeDeclarationSQL() 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 getCreateTableSQL() 0 5 1
A udfLocate() 0 15 3
A getBlobTypeDeclarationSQL() 0 3 1
A getCreateConstraintSQL() 0 3 1
A supportsForeignKeyConstraints() 0 3 1
A getTemporaryTableName() 0 5 1
B getColumnNamesInAlteredTable() 0 34 7
A getListTableForeignKeysSQL() 0 5 1
D getSimpleAlterTableSQL() 0 72 35
A getPreAlterTableIndexForeignKeySQL() 0 16 4
A getPrimaryIndexInAlteredTable() 0 13 3
B getForeignKeysInAlteredTable() 0 47 11
A doModifyLimitQuery() 0 7 3
C getIndexesInAlteredTable() 0 55 13
A getTruncateTableSQL() 0 9 1
A getCreatePrimaryKeySQL() 0 3 1
F getAlterTableSQL() 0 113 20
A getDropForeignKeySQL() 0 3 1
A canEmulateSchemas() 0 3 1
A getPostAlterTableIndexForeignKeySQL() 0 17 5
A initializeDoctrineTypeMappings() 0 35 1
A getCreateForeignKeySQL() 0 3 1
A getTrimExpression() 0 18 4
B getDateArithmeticIntervalExpression() 0 26 7
A _getTransactionIsolationLevelSQL() 0 11 5

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

800
    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...
801
    {
802 28
        $table = str_replace('.', '__', $table);
803
804 28
        return sprintf('PRAGMA foreign_key_list(%s)', $this->quoteStringLiteral($table));
805
    }
806
807
    /**
808
     * {@inheritDoc}
809
     */
810 424
    public function getAlterTableSQL(TableDiff $diff)
811
    {
812 424
        $sql = $this->getSimpleAlterTableSQL($diff);
813 424
        if ($sql !== false) {
0 ignored issues
show
introduced by
The condition $sql !== false is always false.
Loading history...
814 33
            return $sql;
815
        }
816
817 391
        $fromTable = $diff->fromTable;
818 391
        if (! $fromTable instanceof Table) {
0 ignored issues
show
introduced by
$fromTable is always a sub-type of Doctrine\DBAL\Schema\Table.
Loading history...
819 54
            throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema');
820
        }
821
822 337
        $table = clone $fromTable;
823
824 337
        $columns        = [];
825 337
        $oldColumnNames = [];
826 337
        $newColumnNames = [];
827 337
        $columnSql      = [];
828
829 337
        foreach ($table->getColumns() as $columnName => $column) {
830 310
            $columnName                  = strtolower($columnName);
831 310
            $columns[$columnName]        = $column;
832 310
            $oldColumnNames[$columnName] = $newColumnNames[$columnName] = $column->getQuotedName($this);
833
        }
834
835 337
        foreach ($diff->removedColumns as $columnName => $column) {
836 109
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
837
                continue;
838
            }
839
840 109
            $columnName = strtolower($columnName);
841 109
            if (! isset($columns[$columnName])) {
842
                continue;
843
            }
844
845
            unset(
846 109
                $columns[$columnName],
847 109
                $oldColumnNames[$columnName],
848 109
                $newColumnNames[$columnName]
849
            );
850
        }
851
852 337
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
853 135
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
854
                continue;
855
            }
856
857 135
            $oldColumnName = strtolower($oldColumnName);
858 135
            if (isset($columns[$oldColumnName])) {
859 135
                unset($columns[$oldColumnName]);
860
            }
861
862 135
            $columns[strtolower($column->getName())] = $column;
863
864 135
            if (! isset($newColumnNames[$oldColumnName])) {
865
                continue;
866
            }
867
868 135
            $newColumnNames[$oldColumnName] = $column->getQuotedName($this);
869
        }
870
871 337
        foreach ($diff->changedColumns as $oldColumnName => $columnDiff) {
872 174
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
873
                continue;
874
            }
875
876 174
            if (isset($columns[$oldColumnName])) {
877 147
                unset($columns[$oldColumnName]);
878
            }
879
880 174
            $columns[strtolower($columnDiff->column->getName())] = $columnDiff->column;
881
882 174
            if (! isset($newColumnNames[$oldColumnName])) {
883 27
                continue;
884
            }
885
886 147
            $newColumnNames[$oldColumnName] = $columnDiff->column->getQuotedName($this);
887
        }
888
889 337
        foreach ($diff->addedColumns as $columnName => $column) {
890 82
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
891
                continue;
892
            }
893
894 82
            $columns[strtolower($columnName)] = $column;
895
        }
896
897 337
        $sql      = [];
898 337
        $tableSql = [];
899 337
        if (! $this->onSchemaAlterTable($diff, $tableSql)) {
900 337
            $dataTable = new Table('__temp__' . $table->getName());
901
902 337
            $newTable = new Table($table->getQuotedName($this), $columns, $this->getPrimaryIndexInAlteredTable($diff), $this->getForeignKeysInAlteredTable($diff), 0, $table->getOptions());
903 337
            $newTable->addOption('alter', true);
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type string expected by parameter $value of Doctrine\DBAL\Schema\Table::addOption(). ( Ignorable by Annotation )

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

903
            $newTable->addOption('alter', /** @scrutinizer ignore-type */ true);
Loading history...
904
905 337
            $sql = $this->getPreAlterTableIndexForeignKeySQL($diff);
906
            //$sql = array_merge($sql, $this->getCreateTableSQL($dataTable, 0));
907 337
            $sql[] = sprintf('CREATE TEMPORARY TABLE %s AS SELECT %s FROM %s', $dataTable->getQuotedName($this), implode(', ', $oldColumnNames), $table->getQuotedName($this));
908 337
            $sql[] = $this->getDropTableSQL($fromTable);
909
910 337
            $sql   = array_merge($sql, $this->getCreateTableSQL($newTable));
911 337
            $sql[] = sprintf('INSERT INTO %s (%s) SELECT %s FROM %s', $newTable->getQuotedName($this), implode(', ', $newColumnNames), implode(', ', $oldColumnNames), $dataTable->getQuotedName($this));
912 337
            $sql[] = $this->getDropTableSQL($dataTable);
913
914 337
            if ($diff->newName && $diff->newName !== $diff->name) {
915 81
                $renamedTable = $diff->getNewName();
916 81
                $sql[]        = 'ALTER TABLE ' . $newTable->getQuotedName($this) . ' RENAME TO ' . $renamedTable->getQuotedName($this);
917
            }
918
919 337
            $sql = array_merge($sql, $this->getPostAlterTableIndexForeignKeySQL($diff));
920
        }
921
922 337
        return array_merge($sql, $tableSql, $columnSql);
923
    }
924
925
    /**
926
     * @return string[]|false
927
     */
928 424
    private function getSimpleAlterTableSQL(TableDiff $diff)
929
    {
930
        // Suppress changes on integer type autoincrement columns.
931 424
        foreach ($diff->changedColumns as $oldColumnName => $columnDiff) {
932 179
            if (! $columnDiff->fromColumn instanceof Column ||
933 71
                ! $columnDiff->column instanceof Column ||
934 71
                ! $columnDiff->column->getAutoincrement() ||
935 179
                ! $columnDiff->column->getType() instanceof Types\IntegerType
936
            ) {
937 174
                continue;
938
            }
939
940 5
            if (! $columnDiff->hasChanged('type') && $columnDiff->hasChanged('unsigned')) {
941 1
                unset($diff->changedColumns[$oldColumnName]);
942
943 1
                continue;
944
            }
945
946 4
            $fromColumnType = $columnDiff->fromColumn->getType();
947
948 4
            if (! ($fromColumnType instanceof Types\SmallIntType) && ! ($fromColumnType instanceof Types\BigIntType)) {
949
                continue;
950
            }
951
952 4
            unset($diff->changedColumns[$oldColumnName]);
953
        }
954
955 424
        if (! empty($diff->renamedColumns) || ! empty($diff->addedForeignKeys) || ! empty($diff->addedIndexes)
956 289
                || ! empty($diff->changedColumns) || ! empty($diff->changedForeignKeys) || ! empty($diff->changedIndexes)
957 169
                || ! empty($diff->removedColumns) || ! empty($diff->removedForeignKeys) || ! empty($diff->removedIndexes)
958 424
                || ! empty($diff->renamedIndexes)
959
        ) {
960 337
            return false;
961
        }
962
963 87
        $table = new Table($diff->name);
964
965 87
        $sql       = [];
966 87
        $tableSql  = [];
967 87
        $columnSql = [];
968
969 87
        foreach ($diff->addedColumns as $column) {
970 81
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
971
                continue;
972
            }
973
974 81
            $field = array_merge(['unique' => null, 'autoincrement' => null, 'default' => null], $column->toArray());
975 81
            $type  = $field['type'];
976
            switch (true) {
977 81
                case isset($field['columnDefinition']) || $field['autoincrement'] || $field['unique']:
978 54
                case $type instanceof Types\DateTimeType && $field['default'] === $this->getCurrentTimestampSQL():
979 54
                case $type instanceof Types\DateType && $field['default'] === $this->getCurrentDateSQL():
980 27
                case $type instanceof Types\TimeType && $field['default'] === $this->getCurrentTimeSQL():
981 54
                    return false;
982
            }
983
984 27
            $field['name'] = $column->getQuotedName($this);
985 27
            if ($type instanceof Types\StringType && $field['length'] === null) {
986 27
                $field['length'] = 255;
987
            }
988
989 27
            $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ADD COLUMN ' . $this->getColumnDeclarationSQL($field['name'], $field);
990
        }
991
992 33
        if (! $this->onSchemaAlterTable($diff, $tableSql)) {
993 33
            if ($diff->newName !== false) {
994 1
                $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

994
                $newTable = new Identifier(/** @scrutinizer ignore-type */ $diff->newName);
Loading history...
995 1
                $sql[]    = 'ALTER TABLE ' . $table->getQuotedName($this) . ' RENAME TO ' . $newTable->getQuotedName($this);
996
            }
997
        }
998
999 33
        return array_merge($sql, $tableSql, $columnSql);
1000
    }
1001
1002
    /**
1003
     * @return string[]
1004
     */
1005 337
    private function getColumnNamesInAlteredTable(TableDiff $diff)
1006
    {
1007 337
        $columns = [];
1008
1009 337
        foreach ($diff->fromTable->getColumns() as $columnName => $column) {
1010 310
            $columns[strtolower($columnName)] = $column->getName();
1011
        }
1012
1013 337
        foreach ($diff->removedColumns as $columnName => $column) {
1014 109
            $columnName = strtolower($columnName);
1015 109
            if (! isset($columns[$columnName])) {
1016
                continue;
1017
            }
1018
1019 109
            unset($columns[$columnName]);
1020
        }
1021
1022 337
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
1023 135
            $columnName                          = $column->getName();
1024 135
            $columns[strtolower($oldColumnName)] = $columnName;
1025 135
            $columns[strtolower($columnName)]    = $columnName;
1026
        }
1027
1028 337
        foreach ($diff->changedColumns as $oldColumnName => $columnDiff) {
1029 174
            $columnName                          = $columnDiff->column->getName();
1030 174
            $columns[strtolower($oldColumnName)] = $columnName;
1031 174
            $columns[strtolower($columnName)]    = $columnName;
1032
        }
1033
1034 337
        foreach ($diff->addedColumns as $columnName => $column) {
1035 82
            $columns[strtolower($columnName)] = $columnName;
1036
        }
1037
1038 337
        return $columns;
1039
    }
1040
1041
    /**
1042
     * @return Index[]
1043
     */
1044 337
    private function getIndexesInAlteredTable(TableDiff $diff)
1045
    {
1046 337
        $indexes     = $diff->fromTable->getIndexes();
1047 337
        $columnNames = $this->getColumnNamesInAlteredTable($diff);
1048
1049 337
        foreach ($indexes as $key => $index) {
1050 165
            foreach ($diff->renamedIndexes as $oldIndexName => $renamedIndex) {
1051 82
                if (strtolower($key) !== strtolower($oldIndexName)) {
1052 82
                    continue;
1053
                }
1054
1055 28
                unset($indexes[$key]);
1056
            }
1057
1058 165
            $changed      = false;
1059 165
            $indexColumns = [];
1060 165
            foreach ($index->getColumns() as $columnName) {
1061 165
                $normalizedColumnName = strtolower($columnName);
1062 165
                if (! isset($columnNames[$normalizedColumnName])) {
1063 27
                    unset($indexes[$key]);
1064 27
                    continue 2;
1065
                } else {
1066 165
                    $indexColumns[] = $columnNames[$normalizedColumnName];
1067 165
                    if ($columnName !== $columnNames[$normalizedColumnName]) {
1068 165
                        $changed = true;
1069
                    }
1070
                }
1071
            }
1072
1073 165
            if (! $changed) {
1074 165
                continue;
1075
            }
1076
1077 27
            $indexes[$key] = new Index($index->getName(), $indexColumns, $index->isUnique(), $index->isPrimary(), $index->getFlags());
1078
        }
1079
1080 337
        foreach ($diff->removedIndexes as $index) {
1081 28
            $indexName = strtolower($index->getName());
1082 28
            if (! strlen($indexName) || ! isset($indexes[$indexName])) {
1083
                continue;
1084
            }
1085
1086 28
            unset($indexes[$indexName]);
1087
        }
1088
1089 337
        foreach (array_merge($diff->changedIndexes, $diff->addedIndexes, $diff->renamedIndexes) as $index) {
1090 82
            $indexName = strtolower($index->getName());
1091 82
            if (strlen($indexName)) {
1092 82
                $indexes[$indexName] = $index;
1093
            } else {
1094 82
                $indexes[] = $index;
1095
            }
1096
        }
1097
1098 337
        return $indexes;
1099
    }
1100
1101
    /**
1102
     * @return ForeignKeyConstraint[]
1103
     */
1104 337
    private function getForeignKeysInAlteredTable(TableDiff $diff)
1105
    {
1106 337
        $foreignKeys = $diff->fromTable->getForeignKeys();
1107 337
        $columnNames = $this->getColumnNamesInAlteredTable($diff);
1108
1109 337
        foreach ($foreignKeys as $key => $constraint) {
1110 81
            $changed      = false;
1111 81
            $localColumns = [];
1112 81
            foreach ($constraint->getLocalColumns() as $columnName) {
1113 81
                $normalizedColumnName = strtolower($columnName);
1114 81
                if (! isset($columnNames[$normalizedColumnName])) {
1115 27
                    unset($foreignKeys[$key]);
1116 27
                    continue 2;
1117
                } else {
1118 81
                    $localColumns[] = $columnNames[$normalizedColumnName];
1119 81
                    if ($columnName !== $columnNames[$normalizedColumnName]) {
1120 81
                        $changed = true;
1121
                    }
1122
                }
1123
            }
1124
1125 81
            if (! $changed) {
1126 81
                continue;
1127
            }
1128
1129 27
            $foreignKeys[$key] = new ForeignKeyConstraint($localColumns, $constraint->getForeignTableName(), $constraint->getForeignColumns(), $constraint->getName(), $constraint->getOptions());
1130
        }
1131
1132 337
        foreach ($diff->removedForeignKeys as $constraint) {
1133 27
            $constraintName = strtolower($constraint->getName());
1134 27
            if (! strlen($constraintName) || ! isset($foreignKeys[$constraintName])) {
1135
                continue;
1136
            }
1137
1138 27
            unset($foreignKeys[$constraintName]);
1139
        }
1140
1141 337
        foreach (array_merge($diff->changedForeignKeys, $diff->addedForeignKeys) as $constraint) {
1142 28
            $constraintName = strtolower($constraint->getName());
1143 28
            if (strlen($constraintName)) {
1144 27
                $foreignKeys[$constraintName] = $constraint;
1145
            } else {
1146 28
                $foreignKeys[] = $constraint;
1147
            }
1148
        }
1149
1150 337
        return $foreignKeys;
1151
    }
1152
1153
    /**
1154
     * @return Index[]
1155
     */
1156 337
    private function getPrimaryIndexInAlteredTable(TableDiff $diff)
1157
    {
1158 337
        $primaryIndex = [];
1159
1160 337
        foreach ($this->getIndexesInAlteredTable($diff) as $index) {
1161 165
            if (! $index->isPrimary()) {
1162 136
                continue;
1163
            }
1164
1165 111
            $primaryIndex = [$index->getName() => $index];
1166
        }
1167
1168 337
        return $primaryIndex;
1169
    }
1170
}
1171