Failed Conditions
Pull Request — master (#2765)
by Sergei
34:48
created

SqlitePlatform   F

Complexity

Total Complexity 208

Size/Duplication

Total Lines 1098
Duplicated Lines 0 %

Test Coverage

Coverage 92.53%

Importance

Changes 0
Metric Value
wmc 208
dl 0
loc 1098
ccs 384
cts 415
cp 0.9253
rs 0.6442
c 0
b 0
f 0

67 Methods

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

783
    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...
784
    {
785 2
        $table = str_replace('.', '__', $table);
786 2
        $table = $this->quoteStringLiteral($table);
787
788 2
        return "PRAGMA foreign_key_list($table)";
789
    }
790
791
    /**
792
     * {@inheritDoc}
793
     */
794 34
    public function getAlterTableSQL(TableDiff $diff)
795
    {
796 34
        $sql = $this->getSimpleAlterTableSQL($diff);
797 34
        if (false !== $sql) {
798 7
            return $sql;
799
        }
800
801 27
        $fromTable = $diff->fromTable;
802 27
        if ( ! $fromTable instanceof Table) {
0 ignored issues
show
introduced by
$fromTable is always a sub-type of Doctrine\DBAL\Schema\Table.
Loading history...
803 2
            throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema');
804
        }
805
806 25
        $table = clone $fromTable;
807
808 25
        $columns = [];
809 25
        $oldColumnNames = [];
810 25
        $newColumnNames = [];
811 25
        $columnSql = [];
812
813 25
        foreach ($table->getColumns() as $columnName => $column) {
814 24
            $columnName = strtolower($columnName);
815 24
            $columns[$columnName] = $column;
816 24
            $oldColumnNames[$columnName] = $newColumnNames[$columnName] = $column->getQuotedName($this);
817
        }
818
819 25
        foreach ($diff->removedColumns as $columnName => $column) {
820 5
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
821
                continue;
822
            }
823
824 5
            $columnName = strtolower($columnName);
825 5
            if (isset($columns[$columnName])) {
826
                unset(
827 5
                    $columns[$columnName],
828 5
                    $oldColumnNames[$columnName],
829 5
                    $newColumnNames[$columnName]
830
                );
831
            }
832
        }
833
834 25
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
835 5
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
836
                continue;
837
            }
838
839 5
            $oldColumnName = strtolower($oldColumnName);
840 5
            if (isset($columns[$oldColumnName])) {
841 5
                unset($columns[$oldColumnName]);
842
            }
843
844 5
            $columns[strtolower($column->getName())] = $column;
845
846 5
            if (isset($newColumnNames[$oldColumnName])) {
847 5
                $newColumnNames[$oldColumnName] = $column->getQuotedName($this);
848
            }
849
        }
850
851 25
        foreach ($diff->changedColumns as $oldColumnName => $columnDiff) {
852 18
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
853
                continue;
854
            }
855
856 18
            if (isset($columns[$oldColumnName])) {
857 17
                unset($columns[$oldColumnName]);
858
            }
859
860 18
            $columns[strtolower($columnDiff->column->getName())] = $columnDiff->column;
861
862 18
            if (isset($newColumnNames[$oldColumnName])) {
863 18
                $newColumnNames[$oldColumnName] = $columnDiff->column->getQuotedName($this);
864
            }
865
        }
866
867 25
        foreach ($diff->addedColumns as $columnName => $column) {
868 4
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
869
                continue;
870
            }
871
872 4
            $columns[strtolower($columnName)] = $column;
873
        }
874
875 25
        $sql = [];
876 25
        $tableSql = [];
877 25
        if ( ! $this->onSchemaAlterTable($diff, $tableSql)) {
878 25
            $dataTable = new Table('__temp__'.$table->getName());
879
880 25
            $newTable = new Table($table->getQuotedName($this), $columns, $this->getPrimaryIndexInAlteredTable($diff), $this->getForeignKeysInAlteredTable($diff), 0, $table->getOptions());
881 25
            $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

881
            $newTable->addOption('alter', /** @scrutinizer ignore-type */ true);
Loading history...
882
883 25
            $sql = $this->getPreAlterTableIndexForeignKeySQL($diff);
884
            //$sql = array_merge($sql, $this->getCreateTableSQL($dataTable, 0));
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
885 25
            $sql[] = sprintf('CREATE TEMPORARY TABLE %s AS SELECT %s FROM %s', $dataTable->getQuotedName($this), implode(', ', $oldColumnNames), $table->getQuotedName($this));
886 25
            $sql[] = $this->getDropTableSQL($fromTable);
887
888 25
            $sql = array_merge($sql, $this->getCreateTableSQL($newTable));
889 25
            $sql[] = sprintf('INSERT INTO %s (%s) SELECT %s FROM %s', $newTable->getQuotedName($this), implode(', ', $newColumnNames), implode(', ', $oldColumnNames), $dataTable->getQuotedName($this));
890 25
            $sql[] = $this->getDropTableSQL($dataTable);
891
892 25
            if ($diff->newName && $diff->newName != $diff->name) {
893 3
                $renamedTable = $diff->getNewName();
894 3
                $sql[] = 'ALTER TABLE '.$newTable->getQuotedName($this).' RENAME TO '.$renamedTable->getQuotedName($this);
895
            }
896
897 25
            $sql = array_merge($sql, $this->getPostAlterTableIndexForeignKeySQL($diff));
898
        }
899
900 25
        return array_merge($sql, $tableSql, $columnSql);
901
    }
902
903
    /**
904
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
905
     *
906
     * @return array|bool
907
     */
908 34
    private function getSimpleAlterTableSQL(TableDiff $diff)
909
    {
910
        // Suppress changes on integer type autoincrement columns.
911 34
        foreach ($diff->changedColumns as $oldColumnName => $columnDiff) {
912 23
            if ( ! $columnDiff->fromColumn instanceof Column ||
913 19
                ! $columnDiff->column instanceof Column ||
914 19
                ! $columnDiff->column->getAutoincrement() ||
915 23
                ! $columnDiff->column->getType() instanceof Types\IntegerType
916
            ) {
917 18
                continue;
918
            }
919
920 5
            if ( ! $columnDiff->hasChanged('type') && $columnDiff->hasChanged('unsigned')) {
921 1
                unset($diff->changedColumns[$oldColumnName]);
922
923 1
                continue;
924
            }
925
926 4
            $fromColumnType = $columnDiff->fromColumn->getType();
927
928 4
            if ($fromColumnType instanceof Types\SmallIntType || $fromColumnType instanceof Types\BigIntType) {
929 4
                unset($diff->changedColumns[$oldColumnName]);
930
            }
931
        }
932
933 34
        if ( ! empty($diff->renamedColumns) || ! empty($diff->addedForeignKeys) || ! empty($diff->addedIndexes)
934 29
                || ! empty($diff->changedColumns) || ! empty($diff->changedForeignKeys) || ! empty($diff->changedIndexes)
935 13
                || ! empty($diff->removedColumns) || ! empty($diff->removedForeignKeys) || ! empty($diff->removedIndexes)
936 34
                || ! empty($diff->renamedIndexes)
937
        ) {
938 25
            return false;
939
        }
940
941 9
        $table = new Table($diff->name);
942
943 9
        $sql = [];
944 9
        $tableSql = [];
945 9
        $columnSql = [];
946
947 9
        foreach ($diff->addedColumns as $column) {
948 3
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
949
                continue;
950
            }
951
952 3
            $field = array_merge(['unique' => null, 'autoincrement' => null, 'default' => null], $column->toArray());
953 3
            $type = $field['type'];
954
            switch (true) {
955 3
                case isset($field['columnDefinition']) || $field['autoincrement'] || $field['unique']:
956 2
                case $type instanceof Types\DateTimeType && $field['default'] == $this->getCurrentTimestampSQL():
957 2
                case $type instanceof Types\DateType && $field['default'] == $this->getCurrentDateSQL():
958 1
                case $type instanceof Types\TimeType && $field['default'] == $this->getCurrentTimeSQL():
959 2
                    return false;
960
            }
961
962 1
            $field['name'] = $column->getQuotedName($this);
963 1
            if ($type instanceof Types\StringType && $field['length'] === null) {
964 1
                $field['length'] = 255;
965
            }
966
967 1
            $sql[] = 'ALTER TABLE '.$table->getQuotedName($this).' ADD COLUMN '.$this->getColumnDeclarationSQL($field['name'], $field);
968
        }
969
970 7
        if ( ! $this->onSchemaAlterTable($diff, $tableSql)) {
971 7
            if ($diff->newName !== false) {
972 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

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