Failed Conditions
Push — exceptions ( 7b5f2f...d1ce0d )
by Michael
24:32
created

SqlitePlatform   F

Complexity

Total Complexity 211

Size/Duplication

Total Lines 1122
Duplicated Lines 0 %

Test Coverage

Coverage 92.31%

Importance

Changes 0
Metric Value
wmc 211
dl 0
loc 1122
ccs 384
cts 416
cp 0.9231
rs 0.6314
c 0
b 0
f 0

68 Methods

Rating   Name   Duplication   Size   Complexity  
A getTinyIntTypeDeclarationSql() 0 8 2
A getTrimExpression() 0 18 4
A getRegexpExpression() 0 3 1
A getMediumIntTypeDeclarationSql() 0 8 2
A getDateTypeDeclarationSQL() 0 3 1
A getDateTimeTypeDeclarationSQL() 0 3 1
A getNowExpression() 0 10 4
A getIntegerTypeDeclarationSQL() 0 3 1
A getBooleanTypeDeclarationSQL() 0 3 1
A getSmallIntTypeDeclarationSQL() 0 8 2
A getGuidExpression() 0 6 1
A getForeignKeyDeclarationSQL() 0 8 1
A getSubstringExpression() 0 7 2
A getLocateExpression() 0 7 2
A prefersIdentityColumns() 0 3 1
C getDateArithmeticIntervalExpression() 0 26 7
A getDateDiffExpression() 0 3 1
B _getTransactionIsolationLevelSQL() 0 11 5
A getTimeTypeDeclarationSQL() 0 3 1
A getSetTransactionIsolationSQL() 0 3 1
A _getCommonIntegerTypeDeclarationSQL() 0 8 3
A getBigIntTypeDeclarationSQL() 0 8 2
A getListViewsSQL() 0 3 1
A getName() 0 3 1
A udfSqrt() 0 3 1
A getCreateTableSQL() 0 5 2
A udfLocate() 0 15 3
A getBlobTypeDeclarationSQL() 0 3 1
A getBinaryDefaultLength() 0 3 1
A getCreateConstraintSQL() 0 3 1
A getForUpdateSql() 0 3 1
A supportsForeignKeyConstraints() 0 3 1
B getNonAutoincrementPrimaryKeyDefinition() 0 15 5
A getBinaryTypeDeclarationSQLSnippet() 0 3 1
A getReservedKeywordsClass() 0 3 1
A getInlineColumnCommentSQL() 0 3 1
A getListTableColumnsSQL() 0 6 1
A getTemporaryTableName() 0 5 1
C getColumnNamesInAlteredTable() 0 32 7
A getListTableForeignKeysSQL() 0 6 1
A getListTablesSQL() 0 5 1
A getClobTypeDeclarationSQL() 0 3 1
A getListTableIndexesSQL() 0 6 1
C getSimpleAlterTableSQL() 0 70 35
A getPreAlterTableIndexForeignKeySQL() 0 14 4
A getCreateViewSQL() 0 3 1
C _getCreateTableSQL() 0 38 14
A getPrimaryIndexInAlteredTable() 0 11 3
C getForeignKeysInAlteredTable() 0 43 11
A supportsColumnCollation() 0 3 1
A doModifyLimitQuery() 0 7 3
D getIndexesInAlteredTable() 0 49 13
A getTruncateTableSQL() 0 6 1
A getBinaryMaxLength() 0 3 1
A getCreatePrimaryKeySQL() 0 3 1
F getAlterTableSQL() 0 108 20
A udfMod() 0 3 1
A getVarcharTypeDeclarationSQLSnippet() 0 5 4
B getAdvancedForeignKeyOptionsSQL() 0 8 5
A getDropForeignKeySQL() 0 3 1
A canEmulateSchemas() 0 3 1
B getPostAlterTableIndexForeignKeySQL() 0 17 5
B initializeDoctrineTypeMappings() 0 36 1
A getCreateForeignKeySQL() 0 3 1
A supportsIdentityColumns() 0 3 1
A getListTableConstraintsSQL() 0 6 1
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
27
/**
28
 * The SqlitePlatform class describes the specifics and dialects of the SQLite
29
 * database platform.
30
 *
31
 * @since  2.0
32
 * @author Roman Borschel <[email protected]>
33
 * @author Benjamin Eberlei <[email protected]>
34
 * @author Martin Hasoň <[email protected]>
35
 * @todo   Rename: SQLitePlatform
36
 */
37
class SqlitePlatform extends AbstractPlatform
38
{
39
    /**
40
     * {@inheritDoc}
41
     */
42 16
    public function getRegexpExpression()
43
    {
44 16
        return 'REGEXP';
45
    }
46
47
    /**
48
     * {@inheritDoc}
49
     */
50 2
    public function getGuidExpression()
51
    {
52
        return "HEX(RANDOMBLOB(4)) || '-' || HEX(RANDOMBLOB(2)) || '-4' || "
53
            . "SUBSTR(HEX(RANDOMBLOB(2)), 2) || '-' || "
54
            . "SUBSTR('89AB', 1 + (ABS(RANDOM()) % 4), 1) || "
55 2
            . "SUBSTR(HEX(RANDOMBLOB(2)), 2) || '-' || HEX(RANDOMBLOB(6))";
56
    }
57
58
    /**
59
     * {@inheritDoc}
60
     */
61
    public function getNowExpression($type = 'timestamp')
62
    {
63
        switch ($type) {
64
            case 'time':
65
                return 'time(\'now\')';
66
            case 'date':
67
                return 'date(\'now\')';
68
            case 'timestamp':
69
            default:
70
                return 'datetime(\'now\')';
71
        }
72
    }
73
74
    /**
75
     * {@inheritDoc}
76
     */
77 36
    public function getTrimExpression($str, $pos = TrimMode::UNSPECIFIED, $char = false)
78
    {
79 36
        $trimChar = ($char != false) ? (', ' . $char) : '';
80
81 36
        switch ($pos) {
82
            case TrimMode::LEADING:
83 9
                $trimFn = 'LTRIM';
84 9
                break;
85
86
            case TrimMode::TRAILING:
87 9
                $trimFn = 'RTRIM';
88 9
                break;
89
90
            default:
91 18
                $trimFn = 'TRIM';
92
        }
93
94 36
        return $trimFn . '(' . $str . $trimChar . ')';
95
    }
96
97
    /**
98
     * {@inheritDoc}
99
     *
100
     * SQLite only supports the 2 parameter variant of this function
101
     */
102 16
    public function getSubstringExpression($value, $position, $length = null)
103
    {
104 16
        if ($length !== null) {
105 16
            return 'SUBSTR(' . $value . ', ' . $position . ', ' . $length . ')';
106
        }
107
108 16
        return 'SUBSTR(' . $value . ', ' . $position . ', LENGTH(' . $value . '))';
109
    }
110
111
    /**
112
     * {@inheritDoc}
113
     */
114 1
    public function getLocateExpression($str, $substr, $startPos = false)
115
    {
116 1
        if ($startPos == false) {
117 1
            return 'LOCATE('.$str.', '.$substr.')';
118
        }
119
120 1
        return 'LOCATE('.$str.', '.$substr.', '.$startPos.')';
121
    }
122
123
    /**
124
     * {@inheritdoc}
125
     */
126 33
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
127
    {
128 33
        switch ($unit) {
129
            case DateIntervalUnit::SECOND:
130
            case DateIntervalUnit::MINUTE:
131
            case DateIntervalUnit::HOUR:
132 1
                return "DATETIME(" . $date . ",'" . $operator . $interval . " " . $unit . "')";
133
134
            default:
135 33
                switch ($unit) {
136
                    case DateIntervalUnit::WEEK:
137 1
                        $interval *= 7;
138 1
                        $unit      = DateIntervalUnit::DAY;
139 1
                        break;
140
141
                    case DateIntervalUnit::QUARTER:
142 1
                        $interval *= 3;
143 1
                        $unit      = DateIntervalUnit::MONTH;
144 1
                        break;
145
                }
146
147 33
                if (! is_numeric($interval)) {
148 16
                    $interval = "' || " . $interval . " || '";
149
                }
150
151 33
                return "DATE(" . $date . ",'" . $operator . $interval . " " . $unit . "')";
152
        }
153
    }
154
155
    /**
156
     * {@inheritDoc}
157
     */
158 3
    public function getDateDiffExpression($date1, $date2)
159
    {
160 3
        return sprintf("JULIANDAY(%s, 'start of day') - JULIANDAY(%s, 'start of day')", $date1, $date2);
161
    }
162
163
    /**
164
     * {@inheritDoc}
165
     */
166 16
    protected function _getTransactionIsolationLevelSQL($level)
167
    {
168 16
        switch ($level) {
169
            case TransactionIsolationLevel::READ_UNCOMMITTED:
170 16
                return 0;
171
            case TransactionIsolationLevel::READ_COMMITTED:
172
            case TransactionIsolationLevel::REPEATABLE_READ:
173
            case TransactionIsolationLevel::SERIALIZABLE:
174 16
                return 1;
175
            default:
176
                return parent::_getTransactionIsolationLevelSQL($level);
177
        }
178
    }
179
180
    /**
181
     * {@inheritDoc}
182
     */
183 16
    public function getSetTransactionIsolationSQL($level)
184
    {
185 16
        return 'PRAGMA read_uncommitted = ' . $this->_getTransactionIsolationLevelSQL($level);
186
    }
187
188
    /**
189
     * {@inheritDoc}
190
     */
191 17
    public function prefersIdentityColumns()
192
    {
193 17
        return true;
194
    }
195
196
    /**
197
     * {@inheritDoc}
198
     */
199 31
    public function getBooleanTypeDeclarationSQL(array $field)
200
    {
201 31
        return 'BOOLEAN';
202
    }
203
204
    /**
205
     * {@inheritDoc}
206
     */
207 493
    public function getIntegerTypeDeclarationSQL(array $field)
208
    {
209 493
        return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($field);
210
    }
211
212
    /**
213
     * {@inheritDoc}
214
     */
215 33
    public function getBigIntTypeDeclarationSQL(array $field)
216
    {
217
        //  SQLite autoincrement is implicit for INTEGER PKs, but not for BIGINT fields.
218 33
        if ( ! empty($field['autoincrement'])) {
219 18
            return $this->getIntegerTypeDeclarationSQL($field);
220
        }
221
222 31
        return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
223
    }
224
225
    /**
226
     * {@inheritDoc}
227
     */
228 32
    public function getTinyIntTypeDeclarationSql(array $field)
229
    {
230
        //  SQLite autoincrement is implicit for INTEGER PKs, but not for TINYINT fields.
231 32
        if ( ! empty($field['autoincrement'])) {
232 32
            return $this->getIntegerTypeDeclarationSQL($field);
233
        }
234
235 16
        return 'TINYINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
236
    }
237
238
    /**
239
     * {@inheritDoc}
240
     */
241 19
    public function getSmallIntTypeDeclarationSQL(array $field)
242
    {
243
        //  SQLite autoincrement is implicit for INTEGER PKs, but not for SMALLINT fields.
244 19
        if ( ! empty($field['autoincrement'])) {
245 18
            return $this->getIntegerTypeDeclarationSQL($field);
246
        }
247
248 17
        return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
249
    }
250
251
    /**
252
     * {@inheritDoc}
253
     */
254 16
    public function getMediumIntTypeDeclarationSql(array $field)
255
    {
256
        //  SQLite autoincrement is implicit for INTEGER PKs, but not for MEDIUMINT fields.
257 16
        if ( ! empty($field['autoincrement'])) {
258 16
            return $this->getIntegerTypeDeclarationSQL($field);
259
        }
260
261 16
        return 'MEDIUMINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
262
    }
263
264
    /**
265
     * {@inheritDoc}
266
     */
267 22
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
268
    {
269 22
        return 'DATETIME';
270
    }
271
272
    /**
273
     * {@inheritDoc}
274
     */
275 18
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
276
    {
277 18
        return 'DATE';
278
    }
279
280
    /**
281
     * {@inheritDoc}
282
     */
283 18
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
284
    {
285 18
        return 'TIME';
286
    }
287
288
    /**
289
     * {@inheritDoc}
290
     */
291 493
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
292
    {
293
        // sqlite autoincrement is only possible for the primary key
294 493
        if ( ! empty($columnDef['autoincrement'])) {
295 175
            return ' PRIMARY KEY AUTOINCREMENT';
296
        }
297
298 434
        return ! empty($columnDef['unsigned']) ? ' UNSIGNED' : '';
299
    }
300
301
    /**
302
     * {@inheritDoc}
303
     */
304 81
    public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey)
305
    {
306 81
        return parent::getForeignKeyDeclarationSQL(new ForeignKeyConstraint(
307 81
            $foreignKey->getQuotedLocalColumns($this),
308 81
            str_replace('.', '__', $foreignKey->getQuotedForeignTableName($this)),
309 81
            $foreignKey->getQuotedForeignColumns($this),
310 81
            $foreignKey->getName(),
311 81
            $foreignKey->getOptions()
312
        ));
313
    }
314
315
    /**
316
     * {@inheritDoc}
317
     */
318 548
    protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
319
    {
320 548
        $tableName = str_replace('.', '__', $tableName);
321 548
        $queryFields = $this->getColumnDeclarationListSQL($columns);
322
323 548
        if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
324
            foreach ($options['uniqueConstraints'] as $name => $definition) {
325
                $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
326
            }
327
        }
328
329 548
        $queryFields .= $this->getNonAutoincrementPrimaryKeyDefinition($columns, $options);
330
331 548
        if (isset($options['foreignKeys'])) {
332 532
            foreach ($options['foreignKeys'] as $foreignKey) {
333 81
                $queryFields.= ', '.$this->getForeignKeyDeclarationSQL($foreignKey);
334
            }
335
        }
336
337 548
        $query[] = 'CREATE TABLE ' . $tableName . ' (' . $queryFields . ')';
0 ignored issues
show
Comprehensibility Best Practice introduced by
$query was never initialized. Although not strictly required by PHP, it is generally a good practice to add $query = array(); before regardless.
Loading history...
338
339 548
        if (isset($options['alter']) && true === $options['alter']) {
340 205
            return $query;
341
        }
342
343 356
        if (isset($options['indexes']) && ! empty($options['indexes'])) {
344 68
            foreach ($options['indexes'] as $indexDef) {
345 68
                $query[] = $this->getCreateIndexSQL($indexDef, $tableName);
346
            }
347
        }
348
349 356
        if (isset($options['unique']) && ! empty($options['unique'])) {
350
            foreach ($options['unique'] as $indexDef) {
351
                $query[] = $this->getCreateIndexSQL($indexDef, $tableName);
352
            }
353
        }
354
355 356
        return $query;
356
    }
357
358
    /**
359
     * Generate a PRIMARY KEY definition if no autoincrement value is used
360
     *
361
     * @param string[] $columns
362
     * @param mixed[]  $options
363
     */
364 548
    private function getNonAutoincrementPrimaryKeyDefinition(array $columns, array $options) : string
365
    {
366 548
        if (empty($options['primary'])) {
367 268
            return '';
368
        }
369
370 280
        $keyColumns = array_unique(array_values($options['primary']));
371
372 280
        foreach ($keyColumns as $keyColumn) {
373 280
            if (isset($columns[$keyColumn]['autoincrement']) && ! empty($columns[$keyColumn]['autoincrement'])) {
374 280
                return '';
375
            }
376
        }
377
378 203
        return ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
379
    }
380
381
    /**
382
     * {@inheritDoc}
383
     */
384 322
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
385
    {
386 322
        return $fixed
387 55
            ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)')
388 322
            : ($length ? 'VARCHAR(' . $length . ')' : 'TEXT')
389
        ;
390
    }
391
392
    /**
393
     * {@inheritdoc}
394
     */
395 17
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
396
    {
397 17
        return 'BLOB';
398
    }
399
400
    /**
401
     * {@inheritdoc}
402
     */
403 34
    public function getBinaryMaxLength()
404
    {
405 34
        return 0;
406
    }
407
408
    /**
409
     * {@inheritdoc}
410
     */
411 33
    public function getBinaryDefaultLength()
412
    {
413 33
        return 0;
414
    }
415
416
    /**
417
     * {@inheritDoc}
418
     */
419 63
    public function getClobTypeDeclarationSQL(array $field)
420
    {
421 63
        return 'CLOB';
422
    }
423
424
    /**
425
     * {@inheritDoc}
426
     */
427 16
    public function getListTableConstraintsSQL($table)
428
    {
429 16
        $table = str_replace('.', '__', $table);
430 16
        $table = $this->quoteStringLiteral($table);
431
432 16
        return "SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name = $table AND sql NOT NULL ORDER BY name";
433
    }
434
435
    /**
436
     * {@inheritDoc}
437
     */
438 86
    public function getListTableColumnsSQL($table, $currentDatabase = null)
439
    {
440 86
        $table = str_replace('.', '__', $table);
441 86
        $table = $this->quoteStringLiteral($table);
442
443 86
        return "PRAGMA table_info($table)";
444
    }
445
446
    /**
447
     * {@inheritDoc}
448
     */
449 79
    public function getListTableIndexesSQL($table, $currentDatabase = null)
450
    {
451 79
        $table = str_replace('.', '__', $table);
452 79
        $table = $this->quoteStringLiteral($table);
453
454 79
        return "PRAGMA index_list($table)";
455
    }
456
457
    /**
458
     * {@inheritDoc}
459
     */
460 111
    public function getListTablesSQL()
461
    {
462
        return "SELECT name FROM sqlite_master WHERE type = 'table' AND name != 'sqlite_sequence' AND name != 'geometry_columns' AND name != 'spatial_ref_sys' "
463
             . "UNION ALL SELECT name FROM sqlite_temp_master "
464 111
             . "WHERE type = 'table' ORDER BY name";
465
    }
466
467
    /**
468
     * {@inheritDoc}
469
     */
470 1
    public function getListViewsSQL($database)
471
    {
472 1
        return "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL";
473
    }
474
475
    /**
476
     * {@inheritDoc}
477
     */
478 1
    public function getCreateViewSQL($name, $sql)
479
    {
480 1
        return 'CREATE VIEW ' . $name . ' AS ' . $sql;
481
    }
482
483
    /**
484
     * {@inheritDoc}
485
     */
486 1
    public function getDropViewSQL($name)
487
    {
488 1
        return 'DROP VIEW '. $name;
489
    }
490
491
    /**
492
     * {@inheritDoc}
493
     */
494 81
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
495
    {
496 81
        $query = parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
497
498 81
        $query .= (($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false) ? ' ' : ' NOT ') . 'DEFERRABLE';
499 81
        $query .= ' INITIALLY ' . (($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) ? 'DEFERRED' : 'IMMEDIATE');
500
501 81
        return $query;
502
    }
503
504
    /**
505
     * {@inheritDoc}
506
     */
507 3
    public function supportsIdentityColumns()
508
    {
509 3
        return true;
510
    }
511
512
    /**
513
     * {@inheritDoc}
514
     */
515 49
    public function supportsColumnCollation()
516
    {
517 49
        return true;
518
    }
519
520
    /**
521
     * {@inheritDoc}
522
     */
523 612
    public function supportsInlineColumnComments()
524
    {
525 612
        return true;
526
    }
527
528
    /**
529
     * {@inheritDoc}
530
     */
531 117
    public function getName()
532
    {
533 117
        return 'sqlite';
534
    }
535
536
    /**
537
     * {@inheritDoc}
538
     */
539 24
    public function getTruncateTableSQL($tableName, $cascade = false)
540
    {
541 24
        $tableIdentifier = new Identifier($tableName);
542 24
        $tableName = str_replace('.', '__', $tableIdentifier->getQuotedName($this));
543
544 24
        return 'DELETE FROM ' . $tableName;
545
    }
546
547
    /**
548
     * User-defined function for Sqlite that is used with PDO::sqliteCreateFunction().
549
     *
550
     * @param int|float $value
551
     *
552
     * @return float
553
     */
554
    public static function udfSqrt($value)
555
    {
556
        return sqrt($value);
557
    }
558
559
    /**
560
     * User-defined function for Sqlite that implements MOD(a, b).
561
     *
562
     * @param int $a
563
     * @param int $b
564
     *
565
     * @return int
566
     */
567
    public static function udfMod($a, $b)
568
    {
569
        return ($a % $b);
570
    }
571
572
    /**
573
     * @param string $str
574
     * @param string $substr
575
     * @param int    $offset
576
     *
577
     * @return int
578
     */
579 1
    public static function udfLocate($str, $substr, $offset = 0)
580
    {
581
        // SQL's LOCATE function works on 1-based positions, while PHP's strpos works on 0-based positions.
582
        // So we have to make them compatible if an offset is given.
583 1
        if ($offset > 0) {
584 1
            $offset -= 1;
585
        }
586
587 1
        $pos = strpos($str, $substr, $offset);
588
589 1
        if ($pos !== false) {
590 1
            return $pos + 1;
591
        }
592
593 1
        return 0;
594
    }
595
596
    /**
597
     * {@inheritDoc}
598
     */
599
    public function getForUpdateSql()
600
    {
601
        return '';
602
    }
603
604
    /**
605
     * {@inheritDoc}
606
     */
607 128
    public function getInlineColumnCommentSQL($comment)
608
    {
609 128
        return '--' . str_replace("\n", "\n--", $comment) . "\n";
610
    }
611
612
    /**
613
     * {@inheritDoc}
614
     */
615 97
    protected function initializeDoctrineTypeMappings()
616
    {
617 97
        $this->doctrineTypeMapping = [
618
            'bigint'           => 'bigint',
619
            'bigserial'        => 'bigint',
620
            'blob'             => 'blob',
621
            'boolean'          => 'boolean',
622
            'char'             => 'string',
623
            'clob'             => 'text',
624
            'date'             => 'date',
625
            'datetime'         => 'datetime',
626
            'decimal'          => 'decimal',
627
            'double'           => 'float',
628
            'double precision' => 'float',
629
            'float'            => 'float',
630
            'image'            => 'string',
631
            'int'              => 'integer',
632
            'integer'          => 'integer',
633
            'longtext'         => 'text',
634
            'longvarchar'      => 'string',
635
            'mediumint'        => 'integer',
636
            'mediumtext'       => 'text',
637
            'ntext'            => 'string',
638
            'numeric'          => 'decimal',
639
            'nvarchar'         => 'string',
640
            'real'             => 'float',
641
            'serial'           => 'integer',
642
            'smallint'         => 'smallint',
643
            'string'           => 'string',
644
            'text'             => 'text',
645
            'time'             => 'time',
646
            'timestamp'        => 'datetime',
647
            'tinyint'          => 'boolean',
648
            'tinytext'         => 'text',
649
            'varchar'          => 'string',
650
            'varchar2'         => 'string',
651
        ];
652 97
    }
653
654
    /**
655
     * {@inheritDoc}
656
     */
657 650
    protected function getReservedKeywordsClass()
658
    {
659 650
        return Keywords\SQLiteKeywords::class;
660
    }
661
662
    /**
663
     * {@inheritDoc}
664
     */
665 205
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
666
    {
667 205
        if ( ! $diff->fromTable instanceof Table) {
0 ignored issues
show
introduced by
$diff->fromTable is always a sub-type of Doctrine\DBAL\Schema\Table. If $diff->fromTable can have other possible types, add them to lib/Doctrine/DBAL/Schema/TableDiff.php:104.
Loading history...
668
            throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema');
669
        }
670
671 205
        $sql = [];
672 205
        foreach ($diff->fromTable->getIndexes() as $index) {
673 99
            if ( ! $index->isPrimary()) {
674 99
                $sql[] = $this->getDropIndexSQL($index, $diff->name);
675
            }
676
        }
677
678 205
        return $sql;
679
    }
680
681
    /**
682
     * {@inheritDoc}
683
     */
684 205
    protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff)
685
    {
686 205
        if ( ! $diff->fromTable instanceof Table) {
0 ignored issues
show
introduced by
$diff->fromTable is always a sub-type of Doctrine\DBAL\Schema\Table. If $diff->fromTable can have other possible types, add them to lib/Doctrine/DBAL/Schema/TableDiff.php:104.
Loading history...
687
            throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema');
688
        }
689
690 205
        $sql = [];
691 205
        $tableName = $diff->newName ? $diff->getNewName(): $diff->getName($this);
692 205
        foreach ($this->getIndexesInAlteredTable($diff) as $index) {
693 99
            if ($index->isPrimary()) {
694 67
                continue;
695
            }
696
697 81
            $sql[] = $this->getCreateIndexSQL($index, $tableName->getQuotedName($this));
698
        }
699
700 205
        return $sql;
701
    }
702
703
    /**
704
     * {@inheritDoc}
705
     */
706 56
    protected function doModifyLimitQuery($query, $limit, $offset)
707
    {
708 56
        if (null === $limit && null !== $offset) {
709 18
            return $query . ' LIMIT -1 OFFSET ' . $offset;
710
        }
711
712 39
        return parent::doModifyLimitQuery($query, $limit, $offset);
713
    }
714
715
    /**
716
     * {@inheritDoc}
717
     */
718 22
    public function getBlobTypeDeclarationSQL(array $field)
719
    {
720 22
        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 171
    public function supportsForeignKeyConstraints()
751
    {
752 171
        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 32
    public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table)
767
    {
768 32
        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 16
    public function getCreateConstraintSQL(Constraint $constraint, $table)
783
    {
784 16
        throw new DBALException('Sqlite platform does not support alter constraint.');
785
    }
786
787
    /**
788
     * {@inheritDoc}
789
     */
790 564
    public function getCreateTableSQL(Table $table, $createFlags = null)
791
    {
792 564
        $createFlags = null === $createFlags ? self::CREATE_INDEXES | self::CREATE_FOREIGNKEYS : $createFlags;
793
794 564
        return parent::getCreateTableSQL($table, $createFlags);
795
    }
796
797
    /**
798
     * {@inheritDoc}
799
     */
800 17
    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 17
        $table = str_replace('.', '__', $table);
803 17
        $table = $this->quoteStringLiteral($table);
804
805 17
        return "PRAGMA foreign_key_list($table)";
806
    }
807
808
    /**
809
     * {@inheritDoc}
810
     */
811 259
    public function getAlterTableSQL(TableDiff $diff)
812
    {
813 259
        $sql = $this->getSimpleAlterTableSQL($diff);
814 259
        if (false !== $sql) {
815 22
            return $sql;
816
        }
817
818 237
        $fromTable = $diff->fromTable;
819 237
        if ( ! $fromTable instanceof Table) {
0 ignored issues
show
introduced by
$fromTable is always a sub-type of Doctrine\DBAL\Schema\Table. If $fromTable can have other possible types, add them to lib/Doctrine/DBAL/Schema/TableDiff.php:104.
Loading history...
820 32
            throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema');
821
        }
822
823 205
        $table = clone $fromTable;
824
825 205
        $columns = [];
826 205
        $oldColumnNames = [];
827 205
        $newColumnNames = [];
828 205
        $columnSql = [];
829
830 205
        foreach ($table->getColumns() as $columnName => $column) {
831 189
            $columnName = strtolower($columnName);
832 189
            $columns[$columnName] = $column;
833 189
            $oldColumnNames[$columnName] = $newColumnNames[$columnName] = $column->getQuotedName($this);
834
        }
835
836 205
        foreach ($diff->removedColumns as $columnName => $column) {
837 65
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
838
                continue;
839
            }
840
841 65
            $columnName = strtolower($columnName);
842 65
            if (isset($columns[$columnName])) {
843
                unset(
844 65
                    $columns[$columnName],
845 65
                    $oldColumnNames[$columnName],
846 65
                    $newColumnNames[$columnName]
847
                );
848
            }
849
        }
850
851 205
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
852 80
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
853
                continue;
854
            }
855
856 80
            $oldColumnName = strtolower($oldColumnName);
857 80
            if (isset($columns[$oldColumnName])) {
858 80
                unset($columns[$oldColumnName]);
859
            }
860
861 80
            $columns[strtolower($column->getName())] = $column;
862
863 80
            if (isset($newColumnNames[$oldColumnName])) {
864 80
                $newColumnNames[$oldColumnName] = $column->getQuotedName($this);
865
            }
866
        }
867
868 205
        foreach ($diff->changedColumns as $oldColumnName => $columnDiff) {
869 108
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
870
                continue;
871
            }
872
873 108
            if (isset($columns[$oldColumnName])) {
874 92
                unset($columns[$oldColumnName]);
875
            }
876
877 108
            $columns[strtolower($columnDiff->column->getName())] = $columnDiff->column;
878
879 108
            if (isset($newColumnNames[$oldColumnName])) {
880 108
                $newColumnNames[$oldColumnName] = $columnDiff->column->getQuotedName($this);
881
            }
882
        }
883
884 205
        foreach ($diff->addedColumns as $columnName => $column) {
885 49
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
886
                continue;
887
            }
888
889 49
            $columns[strtolower($columnName)] = $column;
890
        }
891
892 205
        $sql = [];
893 205
        $tableSql = [];
894
895 205
        if ( ! $this->onSchemaAlterTable($diff, $tableSql)) {
896 205
            $dataTable = new Table('__temp__'.$table->getName());
897
898 205
            $newTable = new Table($table->getQuotedName($this), $columns, $this->getPrimaryIndexInAlteredTable($diff), array(), $this->getForeignKeysInAlteredTable($diff), $table->getOptions());
899 205
            $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

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

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