Failed Conditions
Pull Request — master (#2836)
by
unknown
14:05
created

SqlitePlatform   F

Complexity

Total Complexity 212

Size/Duplication

Total Lines 1172
Duplicated Lines 0 %

Test Coverage

Coverage 77.23%

Importance

Changes 5
Bugs 0 Features 0
Metric Value
wmc 212
eloc 429
c 5
b 0
f 0
dl 0
loc 1172
ccs 346
cts 448
cp 0.7723
rs 2

70 Methods

Rating   Name   Duplication   Size   Complexity  
A getIntegerTypeDeclarationSQL() 0 3 1
A getBooleanTypeDeclarationSQL() 0 3 1
A prefersIdentityColumns() 0 3 1
A getSetTransactionIsolationSQL() 0 3 1
A getRegexpExpression() 0 3 1
A getListViewsSQL() 0 3 1
A getName() 0 3 1
A getTinyIntTypeDeclarationSql() 0 8 2
A udfSqrt() 0 3 1
A getCreateTableSQL() 0 5 1
A udfLocate() 0 15 3
A getBlobTypeDeclarationSQL() 0 3 1
A getTrimExpression() 0 18 4
A getBinaryDefaultLength() 0 3 1
A getCreateConstraintSQL() 0 3 1
A getForUpdateSql() 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 3 1
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 getNowExpression() 0 10 4
A getCreateViewSQL() 0 3 1
C _getCreateTableSQL() 0 45 15
A getSmallIntTypeDeclarationSQL() 0 8 2
A getGuidExpression() 0 6 1
A getPrimaryIndexInAlteredTable() 0 13 3
A getForeignKeyDeclarationSQL() 0 8 1
C getForeignKeysInAlteredTable() 0 53 12
A supportsColumnCollation() 0 3 1
A getInlineTableCommentSQL() 0 3 1
A getSubstringExpression() 0 7 2
A doModifyLimitQuery() 0 7 3
C getIndexesInAlteredTable() 0 57 13
A getTruncateTableSQL() 0 6 1
A getBinaryMaxLength() 0 3 1
A getLocateExpression() 0 7 2
A getCreatePrimaryKeySQL() 0 3 1
F getAlterTableSQL() 0 118 19
A udfMod() 0 3 1
B getDateArithmeticIntervalExpression() 0 26 7
A getVarcharTypeDeclarationSQLSnippet() 0 4 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 enableForeignKeyConstraintsSupport() 0 3 1
A initializeDoctrineTypeMappings() 0 35 1
A getCreateForeignKeySQL() 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

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

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

1022
                $newTable = new Identifier(/** @scrutinizer ignore-type */ $diff->newName);
Loading history...
1023
                $sql[]    = 'ALTER TABLE ' . $table->getQuotedName($this) . ' RENAME TO ' . $newTable->getQuotedName($this);
1024
            }
1025
        }
1026
1027 1282
        return array_merge($sql, $tableSql, $columnSql);
1028
    }
1029
1030
    /**
1031
     * @return string[]
1032
     */
1033 1260
    private function getColumnNamesInAlteredTable(TableDiff $diff)
1034
    {
1035 1260
        $columns = [];
1036
1037 1260
        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

1037
        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...
1038 1254
            $columns[strtolower($columnName)] = $column->getName();
1039
        }
1040
1041 1260
        foreach ($diff->removedColumns as $columnName => $column) {
1042 1212
            $columnName = strtolower($columnName);
1043 1212
            if (! isset($columns[$columnName])) {
1044
                continue;
1045
            }
1046
1047 1212
            unset($columns[$columnName]);
1048
        }
1049
1050 1260
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
1051 1218
            $columnName                          = $column->getName();
1052 1218
            $columns[strtolower($oldColumnName)] = $columnName;
1053 1218
            $columns[strtolower($columnName)]    = $columnName;
1054
        }
1055
1056 1260
        foreach ($diff->changedColumns as $oldColumnName => $columnDiff) {
1057 762
            $columnName                          = $columnDiff->column->getName();
1058 762
            $columns[strtolower($oldColumnName)] = $columnName;
1059 762
            $columns[strtolower($columnName)]    = $columnName;
1060
        }
1061
1062 1260
        foreach ($diff->addedColumns as $column) {
1063 744
            $columnName                       = $column->getName();
1064 744
            $columns[strtolower($columnName)] = $columnName;
1065
        }
1066
1067 1260
        return $columns;
1068
    }
1069
1070
    /**
1071
     * @return Index[]
1072
     */
1073 1260
    private function getIndexesInAlteredTable(TableDiff $diff)
1074
    {
1075 1260
        $indexes     = $diff->fromTable->getIndexes();
1076 1260
        $columnNames = $this->getColumnNamesInAlteredTable($diff);
1077
1078 1260
        foreach ($indexes as $key => $index) {
1079 1224
            foreach ($diff->renamedIndexes as $oldIndexName => $renamedIndex) {
1080 436
                if (strtolower($key) !== strtolower($oldIndexName)) {
1081 436
                    continue;
1082
                }
1083
1084 182
                unset($indexes[$key]);
1085
            }
1086
1087 1224
            $changed      = false;
1088 1224
            $indexColumns = [];
1089 1224
            foreach ($index->getColumns() as $columnName) {
1090 1224
                $normalizedColumnName = strtolower($columnName);
1091 1224
                if (! isset($columnNames[$normalizedColumnName])) {
1092 1194
                    unset($indexes[$key]);
1093 1194
                    continue 2;
1094
                }
1095
1096 1224
                $indexColumns[] = $columnNames[$normalizedColumnName];
1097 1224
                if ($columnName === $columnNames[$normalizedColumnName]) {
1098 1224
                    continue;
1099
                }
1100
1101 1194
                $changed = true;
1102
            }
1103
1104 1224
            if (! $changed) {
1105 1224
                continue;
1106
            }
1107
1108 1194
            $indexes[$key] = new Index($index->getName(), $indexColumns, $index->isUnique(), $index->isPrimary(), $index->getFlags());
1109
        }
1110
1111 1260
        foreach ($diff->removedIndexes as $index) {
1112 1194
            $indexName = strtolower($index->getName());
1113 1194
            if (! strlen($indexName) || ! isset($indexes[$indexName])) {
1114
                continue;
1115
            }
1116
1117 1194
            unset($indexes[$indexName]);
1118
        }
1119
1120 1260
        foreach (array_merge($diff->changedIndexes, $diff->addedIndexes, $diff->renamedIndexes) as $index) {
1121 436
            $indexName = strtolower($index->getName());
1122 436
            if (strlen($indexName)) {
1123 436
                $indexes[$indexName] = $index;
1124
            } else {
1125 18
                $indexes[] = $index;
1126
            }
1127
        }
1128
1129 1260
        return $indexes;
1130
    }
1131
1132
    /**
1133
     * @return ForeignKeyConstraint[]
1134
     */
1135 1260
    private function getForeignKeysInAlteredTable(TableDiff $diff)
1136
    {
1137 1260
        $foreignKeys = $diff->fromTable->getForeignKeys();
1138 1260
        $columnNames = $this->getColumnNamesInAlteredTable($diff);
1139
1140 1260
        foreach ($foreignKeys as $key => $constraint) {
1141 1206
            $changed      = false;
1142 1206
            $localColumns = [];
1143 1206
            foreach ($constraint->getLocalColumns() as $columnName) {
1144 1206
                $normalizedColumnName = strtolower($columnName);
1145 1206
                if (! isset($columnNames[$normalizedColumnName])) {
1146 1194
                    unset($foreignKeys[$key]);
1147 1194
                    continue 2;
1148
                }
1149
1150 1206
                $localColumns[] = $columnNames[$normalizedColumnName];
1151 1206
                if ($columnName === $columnNames[$normalizedColumnName]) {
1152 1206
                    continue;
1153
                }
1154
1155 1194
                $changed = true;
1156
            }
1157
1158 1206
            if (! $changed) {
1159 1206
                continue;
1160
            }
1161
1162 1194
            $foreignKeys[$key] = new ForeignKeyConstraint($localColumns, $constraint->getForeignTableName(), $constraint->getForeignColumns(), $constraint->getName(), $constraint->getOptions());
1163
        }
1164
1165 1260
        foreach ($diff->removedForeignKeys as $constraint) {
1166 226
            if (! $constraint instanceof ForeignKeyConstraint) {
1167
                $constraint = new Identifier($constraint);
1168
            }
1169
1170 226
            $constraintName = strtolower($constraint->getName());
1171 226
            if (! strlen($constraintName) || ! isset($foreignKeys[$constraintName])) {
1172
                continue;
1173
            }
1174
1175 226
            unset($foreignKeys[$constraintName]);
1176
        }
1177
1178 1260
        foreach (array_merge($diff->changedForeignKeys, $diff->addedForeignKeys) as $constraint) {
1179 226
            $constraintName = strtolower($constraint->getName());
1180 226
            if (strlen($constraintName)) {
1181 226
                $foreignKeys[$constraintName] = $constraint;
1182
            } else {
1183 6
                $foreignKeys[] = $constraint;
1184
            }
1185
        }
1186
1187 1260
        return $foreignKeys;
1188
    }
1189
1190
    /**
1191
     * @return Index[]
1192
     */
1193 1260
    private function getPrimaryIndexInAlteredTable(TableDiff $diff)
1194
    {
1195 1260
        $primaryIndex = [];
1196
1197 1260
        foreach ($this->getIndexesInAlteredTable($diff) as $index) {
1198 1224
            if (! $index->isPrimary()) {
1199 1218
                continue;
1200
            }
1201
1202 1212
            $primaryIndex = [$index->getName() => $index];
1203
        }
1204
1205 1260
        return $primaryIndex;
1206
    }
1207
}
1208