Failed Conditions
Push — master ( 792c5b...a4015c )
by Sergei
63:12 queued 10s
created

getPostAlterTableIndexForeignKeySQL()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 7
cts 7
cp 1
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 9
nc 7
nop 1
crap 5
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 1
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
133
    {
134
        switch ($unit) {
135 1
            case DateIntervalUnit::SECOND:
136 1
            case DateIntervalUnit::MINUTE:
137 1
            case DateIntervalUnit::HOUR:
138 1
                return "DATETIME(" . $date . ",'" . $operator . $interval . " " . $unit . "')";
139
140
            default:
141
                switch ($unit) {
142 1
                    case DateIntervalUnit::WEEK:
143 1
                        $interval *= 7;
144 1
                        $unit      = DateIntervalUnit::DAY;
145 1
                        break;
146
147 1
                    case DateIntervalUnit::QUARTER:
148 1
                        $interval *= 3;
149 1
                        $unit      = DateIntervalUnit::MONTH;
150 1
                        break;
151
                }
152
153 1
                if (! is_numeric($interval)) {
154
                    $interval = "' || " . $interval . " || '";
155
                }
156
157
                return "DATE(" . $date . ",'" . $operator . $interval . " " . $unit . "')";
158
        }
159
    }
160 1
161
    /**
162 1
     * {@inheritDoc}
163
     */
164
    public function getDateDiffExpression($date1, $date2)
165
    {
166
        return 'ROUND(JULIANDAY('.$date1 . ')-JULIANDAY('.$date2.'))';
167
    }
168 1
169
    /**
170
     * {@inheritDoc}
171 1
     */
172 1
    protected function _getTransactionIsolationLevelSQL($level)
173 1
    {
174 1
        switch ($level) {
175 1
            case TransactionIsolationLevel::READ_UNCOMMITTED:
176 1
                return 0;
177
            case TransactionIsolationLevel::READ_COMMITTED:
178
            case TransactionIsolationLevel::REPEATABLE_READ:
179
            case TransactionIsolationLevel::SERIALIZABLE:
180
                return 1;
181
            default:
182
                return parent::_getTransactionIsolationLevelSQL($level);
183
        }
184
    }
185 1
186
    /**
187 1
     * {@inheritDoc}
188
     */
189
    public function getSetTransactionIsolationSQL($level)
190
    {
191
        return 'PRAGMA read_uncommitted = ' . $this->_getTransactionIsolationLevelSQL($level);
192
    }
193 2
194
    /**
195 2
     * {@inheritDoc}
196
     */
197
    public function prefersIdentityColumns()
198
    {
199
        return true;
200
    }
201 16
202
    /**
203 16
     * {@inheritDoc}
204
     */
205
    public function getBooleanTypeDeclarationSQL(array $field)
206
    {
207
        return 'BOOLEAN';
208
    }
209 145
210
    /**
211 145
     * {@inheritDoc}
212
     */
213
    public function getIntegerTypeDeclarationSQL(array $field)
214
    {
215
        return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($field);
216
    }
217 18
218
    /**
219
     * {@inheritDoc}
220 18
     */
221 3
    public function getBigIntTypeDeclarationSQL(array $field)
222
    {
223
        //  SQLite autoincrement is implicit for INTEGER PKs, but not for BIGINT fields.
224 16
        if ( ! empty($field['autoincrement'])) {
225
            return $this->getIntegerTypeDeclarationSQL($field);
226
        }
227
228
        return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
229
    }
230 2
231
    /**
232
     * {@inheritDoc}
233 2
     */
234 2
    public function getTinyIntTypeDeclarationSql(array $field)
235
    {
236
        //  SQLite autoincrement is implicit for INTEGER PKs, but not for TINYINT fields.
237 1
        if ( ! empty($field['autoincrement'])) {
238
            return $this->getIntegerTypeDeclarationSQL($field);
239
        }
240
241
        return 'TINYINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
242
    }
243 4
244
    /**
245
     * {@inheritDoc}
246 4
     */
247 3
    public function getSmallIntTypeDeclarationSQL(array $field)
248
    {
249
        //  SQLite autoincrement is implicit for INTEGER PKs, but not for SMALLINT fields.
250 2
        if ( ! empty($field['autoincrement'])) {
251
            return $this->getIntegerTypeDeclarationSQL($field);
252
        }
253
254
        return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
255
    }
256 1
257
    /**
258
     * {@inheritDoc}
259 1
     */
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
            return $this->getIntegerTypeDeclarationSQL($field);
265
        }
266
267
        return 'MEDIUMINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
268
    }
269 19
270
    /**
271 19
     * {@inheritDoc}
272
     */
273
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
274
    {
275
        return 'DATETIME';
276
    }
277 18
278
    /**
279 18
     * {@inheritDoc}
280
     */
281
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
282
    {
283
        return 'DATE';
284
    }
285 18
286
    /**
287 18
     * {@inheritDoc}
288
     */
289
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
290
    {
291
        return 'TIME';
292
    }
293 145
294
    /**
295
     * {@inheritDoc}
296 145
     */
297 39
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
298
    {
299
        // sqlite autoincrement is implicit for integer PKs, but not when the field is unsigned
300 132
        if ( ! empty($columnDef['autoincrement'])) {
301
            return '';
302
        }
303
304
        return ! empty($columnDef['unsigned']) ? ' UNSIGNED' : '';
305
    }
306 6
307
    /**
308 6
     * {@inheritDoc}
309 6
     */
310 6
    public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey)
311 6
    {
312 6
        return parent::getForeignKeyDeclarationSQL(new ForeignKeyConstraint(
313 6
            $foreignKey->getQuotedLocalColumns($this),
314
            str_replace('.', '__', $foreignKey->getQuotedForeignTableName($this)),
315
            $foreignKey->getQuotedForeignColumns($this),
316
            $foreignKey->getName(),
317
            $foreignKey->getOptions()
318
        ));
319
    }
320 150
321
    /**
322 150
     * {@inheritDoc}
323 150
     */
324
    protected function _getCreateTableSQL($name, array $columns, array $options = [])
325 150
    {
326
        $name = str_replace('.', '__', $name);
327
        $queryFields = $this->getColumnDeclarationListSQL($columns);
328
329
        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 150
                $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
332 97
            }
333 97
        }
334
335
        if (isset($options['primary']) && ! empty($options['primary'])) {
336 150
            $keyColumns = array_unique(array_values($options['primary']));
337 149
            $queryFields.= ', PRIMARY KEY('.implode(', ', $keyColumns).')';
338 6
        }
339
340
        if (isset($options['foreignKeys'])) {
341
            foreach ($options['foreignKeys'] as $foreignKey) {
342 150
                $queryFields.= ', '.$this->getForeignKeyDeclarationSQL($foreignKey);
343
            }
344 150
        }
345 25
346
        $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 138
        if (isset($options['alter']) && true === $options['alter']) {
349 8
            return $query;
350 8
        }
351
352
        if (isset($options['indexes']) && ! empty($options['indexes'])) {
353
            foreach ($options['indexes'] as $indexDef) {
354 138
                $query[] = $this->getCreateIndexSQL($indexDef, $name);
355
            }
356
        }
357
358
        if (isset($options['unique']) && ! empty($options['unique'])) {
359
            foreach ($options['unique'] as $indexDef) {
360 138
                $query[] = $this->getCreateIndexSQL($indexDef, $name);
361
            }
362
        }
363
364
        return $query;
365
    }
366 80
367
    /**
368 80
     * {@inheritDoc}
369 80
     */
370
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
371
    {
372
        return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)')
373
                : ($length ? 'VARCHAR(' . $length . ')' : 'TEXT');
374
    }
375 2
376
    /**
377 2
     * {@inheritdoc}
378
     */
379
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
380
    {
381
        return 'BLOB';
382
    }
383 6
384
    /**
385 6
     * {@inheritdoc}
386
     */
387
    public function getBinaryMaxLength()
388
    {
389
        return 0;
390
    }
391 3
392
    /**
393 3
     * {@inheritdoc}
394
     */
395
    public function getBinaryDefaultLength()
396
    {
397
        return 0;
398
    }
399 46
400
    /**
401 46
     * {@inheritDoc}
402
     */
403
    public function getClobTypeDeclarationSQL(array $field)
404
    {
405
        return 'CLOB';
406
    }
407 1
408
    /**
409 1
     * {@inheritDoc}
410 1
     */
411
    public function getListTableConstraintsSQL($table)
412 1
    {
413
        $table = str_replace('.', '__', $table);
414
        $table = $this->quoteStringLiteral($table);
415
416
        return "SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name = $table AND sql NOT NULL ORDER BY name";
417
    }
418 41
419
    /**
420 41
     * {@inheritDoc}
421 41
     */
422
    public function getListTableColumnsSQL($table, $currentDatabase = null)
423 41
    {
424
        $table = str_replace('.', '__', $table);
425
        $table = $this->quoteStringLiteral($table);
426
427
        return "PRAGMA table_info($table)";
428
    }
429 34
430
    /**
431 34
     * {@inheritDoc}
432 34
     */
433
    public function getListTableIndexesSQL($table, $currentDatabase = null)
434 34
    {
435
        $table = str_replace('.', '__', $table);
436
        $table = $this->quoteStringLiteral($table);
437
438
        return "PRAGMA index_list($table)";
439
    }
440 64
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
             . "WHERE type = 'table' ORDER BY name";
449
    }
450 1
451
    /**
452 1
     * {@inheritDoc}
453
     */
454
    public function getListViewsSQL($database)
455
    {
456
        return "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL";
457
    }
458 1
459
    /**
460 1
     * {@inheritDoc}
461
     */
462
    public function getCreateViewSQL($name, $sql)
463
    {
464
        return 'CREATE VIEW ' . $name . ' AS ' . $sql;
465
    }
466 1
467
    /**
468 1
     * {@inheritDoc}
469
     */
470
    public function getDropViewSQL($name)
471
    {
472
        return 'DROP VIEW '. $name;
473
    }
474 6
475
    /**
476 6
     * {@inheritDoc}
477
     */
478 6
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
479 6
    {
480
        $query = parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
481 6
482
        $query .= (($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false) ? ' ' : ' NOT ') . 'DEFERRABLE';
483
        $query .= ' INITIALLY ' . (($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) ? 'DEFERRED' : 'IMMEDIATE');
484
485
        return $query;
486
    }
487 3
488
    /**
489 3
     * {@inheritDoc}
490
     */
491
    public function supportsIdentityColumns()
492
    {
493
        return true;
494
    }
495 1
496
    /**
497 1
     * {@inheritDoc}
498
     */
499
    public function supportsColumnCollation()
500
    {
501
        return true;
502
    }
503 154
504
    /**
505 154
     * {@inheritDoc}
506
     */
507
    public function supportsInlineColumnComments()
508
    {
509
        return true;
510
    }
511 70
512
    /**
513 70
     * {@inheritDoc}
514
     */
515
    public function getName()
516
    {
517
        return 'sqlite';
518
    }
519 12
520
    /**
521 12
     * {@inheritDoc}
522 12
     */
523
    public function getTruncateTableSQL($tableName, $cascade = false)
524 12
    {
525
        $tableIdentifier = new Identifier($tableName);
526
        $tableName = str_replace('.', '__', $tableIdentifier->getQuotedName($this));
527
528
        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 1
     * @param int    $offset
560
     *
561
     * @return int
562
     */
563 1
    public static function udfLocate($str, $substr, $offset = 0)
564 1
    {
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
            $offset -= 1;
569 1
        }
570 1
571
        $pos = strpos($str, $substr, $offset);
572
573 1
        if ($pos !== false) {
574
            return $pos + 1;
575
        }
576
577
        return 0;
578
    }
579
580
    /**
581
     * {@inheritDoc}
582
     */
583
    public function getForUpdateSql()
584
    {
585
        return '';
586
    }
587 38
588
    /**
589 38
     * {@inheritDoc}
590
     */
591
    public function getInlineColumnCommentSQL($comment)
592
    {
593
        return '--' . str_replace("\n", "\n--", $comment) . "\n";
594
    }
595 7
596
    /**
597 7
     * {@inheritDoc}
598
     */
599
    protected function initializeDoctrineTypeMappings()
600
    {
601
        $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 7
            'decimal'          => 'decimal',
632
            'numeric'          => 'decimal',
633
            'blob'             => 'blob',
634
        ];
635
    }
636 49
637
    /**
638 49
     * {@inheritDoc}
639
     */
640
    protected function getReservedKeywordsClass()
641
    {
642
        return Keywords\SQLiteKeywords::class;
643
    }
644 25
645
    /**
646 25
     * {@inheritDoc}
647
     */
648
    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 25
            throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema');
652 9
        }
653 9
654
        $sql = [];
655
        foreach ($diff->fromTable->getIndexes() as $index) {
656
            if ( ! $index->isPrimary()) {
657 25
                $sql[] = $this->getDropIndexSQL($index, $diff->name);
658
            }
659
        }
660
661
        return $sql;
662
    }
663 25
664
    /**
665 25
     * {@inheritDoc}
666
     */
667
    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 25
            throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema');
671 25
        }
672 9
673 7
        $sql = [];
674
        $tableName = $diff->newName ? $diff->getNewName(): $diff->getName($this);
675
        foreach ($this->getIndexesInAlteredTable($diff) as $index) {
676 6
            if ($index->isPrimary()) {
677
                continue;
678
            }
679 25
680
            $sql[] = $this->getCreateIndexSQL($index, $tableName->getQuotedName($this));
681
        }
682
683
        return $sql;
684
    }
685 11
686
    /**
687 11
     * {@inheritDoc}
688 3
     */
689
    protected function doModifyLimitQuery($query, $limit, $offset)
690
    {
691 9
        if (null === $limit && null !== $offset) {
692
            return $query . ' LIMIT -1 OFFSET ' . $offset;
693
        }
694
695
        return parent::doModifyLimitQuery($query, $limit, $offset);
696
    }
697 6
698
    /**
699 6
     * {@inheritDoc}
700
     */
701
    public function getBlobTypeDeclarationSQL(array $field)
702
    {
703
        return 'BLOB';
704
    }
705 2
706
    /**
707 2
     * {@inheritDoc}
708
     */
709 2
    public function getTemporaryTableName($tableName)
710
    {
711
        $tableName = str_replace('.', '__', $tableName);
712
713
        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 36
730
    /**
731 36
     * {@inheritDoc}
732
     */
733
    public function supportsForeignKeyConstraints()
734
    {
735
        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 2
746
    /**
747 2
     * {@inheritdoc}
748
     */
749
    public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table)
750
    {
751
        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 1
762
    /**
763 1
     * {@inheritDoc}
764
     */
765
    public function getCreateConstraintSQL(Constraint $constraint, $table)
766
    {
767
        throw new DBALException('Sqlite platform does not support alter constraint.');
768
    }
769 151
770
    /**
771 151
     * {@inheritDoc}
772
     */
773 151
    public function getCreateTableSQL(Table $table, $createFlags = null)
774
    {
775
        $createFlags = null === $createFlags ? self::CREATE_INDEXES | self::CREATE_FOREIGNKEYS : $createFlags;
776
777
        return parent::getCreateTableSQL($table, $createFlags);
778
    }
779 2
780
    /**
781 2
     * {@inheritDoc}
782 2
     */
783
    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 2
    {
785
        $table = str_replace('.', '__', $table);
786
        $table = $this->quoteStringLiteral($table);
787
788
        return "PRAGMA foreign_key_list($table)";
789
    }
790 34
791
    /**
792 34
     * {@inheritDoc}
793 34
     */
794 7
    public function getAlterTableSQL(TableDiff $diff)
795
    {
796
        $sql = $this->getSimpleAlterTableSQL($diff);
797 27
        if (false !== $sql) {
798 27
            return $sql;
799 2
        }
800
801
        $fromTable = $diff->fromTable;
802 25
        if ( ! $fromTable instanceof Table) {
0 ignored issues
show
introduced by
$fromTable is always a sub-type of Doctrine\DBAL\Schema\Table.
Loading history...
803
            throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema');
804 25
        }
805 25
806 25
        $table = clone $fromTable;
807 25
808
        $columns = [];
809 25
        $oldColumnNames = [];
810 24
        $newColumnNames = [];
811 24
        $columnSql = [];
812 24
813
        foreach ($table->getColumns() as $columnName => $column) {
814
            $columnName = strtolower($columnName);
815 25
            $columns[$columnName] = $column;
816 5
            $oldColumnNames[$columnName] = $newColumnNames[$columnName] = $column->getQuotedName($this);
817
        }
818
819
        foreach ($diff->removedColumns as $columnName => $column) {
820 5
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
821 5
                continue;
822
            }
823 5
824 5
            $columnName = strtolower($columnName);
825 5
            if (isset($columns[$columnName])) {
826
                unset(
827
                    $columns[$columnName],
828
                    $oldColumnNames[$columnName],
829
                    $newColumnNames[$columnName]
830 25
                );
831 5
            }
832
        }
833
834
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
835 5
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
836 5
                continue;
837 5
            }
838
839
            $oldColumnName = strtolower($oldColumnName);
840 5
            if (isset($columns[$oldColumnName])) {
841
                unset($columns[$oldColumnName]);
842 5
            }
843 5
844
            $columns[strtolower($column->getName())] = $column;
845
846
            if (isset($newColumnNames[$oldColumnName])) {
847 25
                $newColumnNames[$oldColumnName] = $column->getQuotedName($this);
848 18
            }
849
        }
850
851
        foreach ($diff->changedColumns as $oldColumnName => $columnDiff) {
852 18
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
853 17
                continue;
854
            }
855
856 18
            if (isset($columns[$oldColumnName])) {
857
                unset($columns[$oldColumnName]);
858 18
            }
859 18
860
            $columns[strtolower($columnDiff->column->getName())] = $columnDiff->column;
861
862
            if (isset($newColumnNames[$oldColumnName])) {
863 25
                $newColumnNames[$oldColumnName] = $columnDiff->column->getQuotedName($this);
864 4
            }
865
        }
866
867
        foreach ($diff->addedColumns as $columnName => $column) {
868 4
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
869
                continue;
870
            }
871 25
872 25
            $columns[strtolower($columnName)] = $column;
873 25
        }
874 25
875
        $sql = [];
876 25
        $tableSql = [];
877 25
        if ( ! $this->onSchemaAlterTable($diff, $tableSql)) {
878
            $dataTable = new Table('__temp__'.$table->getName());
879 25
880
            $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 25
883
            $sql = $this->getPreAlterTableIndexForeignKeySQL($diff);
884 25
            //$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 3
            $sql[] = sprintf('INSERT INTO %s (%s) SELECT %s FROM %s', $newTable->getQuotedName($this), implode(', ', $newColumnNames), implode(', ', $oldColumnNames), $dataTable->getQuotedName($this));
890 3
            $sql[] = $this->getDropTableSQL($dataTable);
891
892
            if ($diff->newName && $diff->newName != $diff->name) {
893 25
                $renamedTable = $diff->getNewName();
894
                $sql[] = 'ALTER TABLE '.$newTable->getQuotedName($this).' RENAME TO '.$renamedTable->getQuotedName($this);
895
            }
896 25
897
            $sql = array_merge($sql, $this->getPostAlterTableIndexForeignKeySQL($diff));
898
        }
899
900
        return array_merge($sql, $tableSql, $columnSql);
901
    }
902
903
    /**
904 34
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
905
     *
906
     * @return array|bool
907 34
     */
908 23
    private function getSimpleAlterTableSQL(TableDiff $diff)
909 19
    {
910 19
        // Suppress changes on integer type autoincrement columns.
911 23
        foreach ($diff->changedColumns as $oldColumnName => $columnDiff) {
912
            if ( ! $columnDiff->fromColumn instanceof Column ||
913 18
                ! $columnDiff->column instanceof Column ||
914
                ! $columnDiff->column->getAutoincrement() ||
915
                ! $columnDiff->column->getType() instanceof Types\IntegerType
916 5
            ) {
917 1
                continue;
918
            }
919 1
920
            if ( ! $columnDiff->hasChanged('type') && $columnDiff->hasChanged('unsigned')) {
921
                unset($diff->changedColumns[$oldColumnName]);
922 4
923
                continue;
924 4
            }
925 4
926
            $fromColumnType = $columnDiff->fromColumn->getType();
927
928
            if ($fromColumnType instanceof Types\SmallIntType || $fromColumnType instanceof Types\BigIntType) {
929 34
                unset($diff->changedColumns[$oldColumnName]);
930 29
            }
931 13
        }
932 34
933
        if ( ! empty($diff->renamedColumns) || ! empty($diff->addedForeignKeys) || ! empty($diff->addedIndexes)
934 25
                || ! empty($diff->changedColumns) || ! empty($diff->changedForeignKeys) || ! empty($diff->changedIndexes)
935
                || ! empty($diff->removedColumns) || ! empty($diff->removedForeignKeys) || ! empty($diff->removedIndexes)
936
                || ! empty($diff->renamedIndexes)
937 9
        ) {
938
            return false;
939 9
        }
940 9
941 9
        $table = new Table($diff->name);
942
943 9
        $sql = [];
944 3
        $tableSql = [];
945
        $columnSql = [];
946
947
        foreach ($diff->addedColumns as $column) {
948 3
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
949 3
                continue;
950
            }
951 3
952 2
            $field = array_merge(['unique' => null, 'autoincrement' => null, 'default' => null], $column->toArray());
953 2
            $type = $field['type'];
954 1
            switch (true) {
955 2
                case isset($field['columnDefinition']) || $field['autoincrement'] || $field['unique']:
956
                case $type instanceof Types\DateTimeType && $field['default'] == $this->getCurrentTimestampSQL():
957
                case $type instanceof Types\DateType && $field['default'] == $this->getCurrentDateSQL():
958 1
                case $type instanceof Types\TimeType && $field['default'] == $this->getCurrentTimeSQL():
959 1
                    return false;
960 1
            }
961
962
            $field['name'] = $column->getQuotedName($this);
963 1
            if ($type instanceof Types\StringType && $field['length'] === null) {
964
                $field['length'] = 255;
965
            }
966 7
967 7
            $sql[] = 'ALTER TABLE '.$table->getQuotedName($this).' ADD COLUMN '.$this->getColumnDeclarationSQL($field['name'], $field);
968 1
        }
969 1
970
        if ( ! $this->onSchemaAlterTable($diff, $tableSql)) {
971
            if ($diff->newName !== false) {
972
                $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 7
                $sql[] = 'ALTER TABLE '.$table->getQuotedName($this).' RENAME TO '.$newTable->getQuotedName($this);
974
            }
975
        }
976
977
        return array_merge($sql, $tableSql, $columnSql);
978
    }
979
980
    /**
981 25
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
982
     *
983 25
     * @return array
984
     */
985 25
    private function getColumnNamesInAlteredTable(TableDiff $diff)
986 24
    {
987
        $columns = [];
988
989 25
        foreach ($diff->fromTable->getColumns() as $columnName => $column) {
990 5
            $columns[strtolower($columnName)] = $column->getName();
991 5
        }
992 5
993
        foreach ($diff->removedColumns as $columnName => $column) {
994
            $columnName = strtolower($columnName);
995
            if (isset($columns[$columnName])) {
996 25
                unset($columns[$columnName]);
997 5
            }
998 5
        }
999 5
1000
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
1001
            $columnName = $column->getName();
1002 25
            $columns[strtolower($oldColumnName)] = $columnName;
1003 18
            $columns[strtolower($columnName)] = $columnName;
1004 18
        }
1005 18
1006
        foreach ($diff->changedColumns as $oldColumnName => $columnDiff) {
1007
            $columnName = $columnDiff->column->getName();
1008 25
            $columns[strtolower($oldColumnName)] = $columnName;
1009 4
            $columns[strtolower($columnName)] = $columnName;
1010
        }
1011
1012 25
        foreach ($diff->addedColumns as $columnName => $column) {
1013
            $columns[strtolower($columnName)] = $columnName;
1014
        }
1015
1016
        return $columns;
1017
    }
1018
1019
    /**
1020 25
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
1021
     *
1022 25
     * @return \Doctrine\DBAL\Schema\Index[]
1023 25
     */
1024
    private function getIndexesInAlteredTable(TableDiff $diff)
1025 25
    {
1026 9
        $indexes = $diff->fromTable->getIndexes();
1027 4
        $columnNames = $this->getColumnNamesInAlteredTable($diff);
1028 4
1029
        foreach ($indexes as $key => $index) {
1030
            foreach ($diff->renamedIndexes as $oldIndexName => $renamedIndex) {
1031
                if (strtolower($key) === strtolower($oldIndexName)) {
1032 9
                    unset($indexes[$key]);
1033 9
                }
1034 9
            }
1035 9
1036 9
            $changed = false;
1037 1
            $indexColumns = [];
1038 1
            foreach ($index->getColumns() as $columnName) {
1039
                $normalizedColumnName = strtolower($columnName);
1040 9
                if ( ! isset($columnNames[$normalizedColumnName])) {
1041 9
                    unset($indexes[$key]);
1042 9
                    continue 2;
1043
                } else {
1044
                    $indexColumns[] = $columnNames[$normalizedColumnName];
1045
                    if ($columnName !== $columnNames[$normalizedColumnName]) {
1046
                        $changed = true;
1047 9
                    }
1048 9
                }
1049
            }
1050
1051
            if ($changed) {
1052 25
                $indexes[$key] = new Index($index->getName(), $indexColumns, $index->isUnique(), $index->isPrimary(), $index->getFlags());
1053 2
            }
1054 2
        }
1055 2
1056
        foreach ($diff->removedIndexes as $index) {
1057
            $indexName = strtolower($index->getName());
1058
            if (strlen($indexName) && isset($indexes[$indexName])) {
1059 25
                unset($indexes[$indexName]);
1060 4
            }
1061 4
        }
1062 4
1063
        foreach (array_merge($diff->changedIndexes, $diff->addedIndexes, $diff->renamedIndexes) as $index) {
1064 4
            $indexName = strtolower($index->getName());
1065
            if (strlen($indexName)) {
1066
                $indexes[$indexName] = $index;
1067
            } else {
1068 25
                $indexes[] = $index;
1069
            }
1070
        }
1071
1072
        return $indexes;
1073
    }
1074
1075
    /**
1076 25
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
1077
     *
1078 25
     * @return array
1079 25
     */
1080
    private function getForeignKeysInAlteredTable(TableDiff $diff)
1081 25
    {
1082 3
        $foreignKeys = $diff->fromTable->getForeignKeys();
1083 3
        $columnNames = $this->getColumnNamesInAlteredTable($diff);
1084 3
1085 3
        foreach ($foreignKeys as $key => $constraint) {
1086 3
            $changed = false;
1087 1
            $localColumns = [];
1088 1
            foreach ($constraint->getLocalColumns() as $columnName) {
1089
                $normalizedColumnName = strtolower($columnName);
1090 3
                if ( ! isset($columnNames[$normalizedColumnName])) {
1091 3
                    unset($foreignKeys[$key]);
1092 3
                    continue 2;
1093
                } else {
1094
                    $localColumns[] = $columnNames[$normalizedColumnName];
1095
                    if ($columnName !== $columnNames[$normalizedColumnName]) {
1096
                        $changed = true;
1097 3
                    }
1098 3
                }
1099
            }
1100
1101
            if ($changed) {
1102 25
                $foreignKeys[$key] = new ForeignKeyConstraint($localColumns, $constraint->getForeignTableName(), $constraint->getForeignColumns(), $constraint->getName(), $constraint->getOptions());
1103 1
            }
1104 1
        }
1105 1
1106
        foreach ($diff->removedForeignKeys as $constraint) {
1107
            $constraintName = strtolower($constraint->getName());
1108
            if (strlen($constraintName) && isset($foreignKeys[$constraintName])) {
1109 25
                unset($foreignKeys[$constraintName]);
1110 2
            }
1111 2
        }
1112 1
1113
        foreach (array_merge($diff->changedForeignKeys, $diff->addedForeignKeys) as $constraint) {
1114 2
            $constraintName = strtolower($constraint->getName());
1115
            if (strlen($constraintName)) {
1116
                $foreignKeys[$constraintName] = $constraint;
1117
            } else {
1118 25
                $foreignKeys[] = $constraint;
1119
            }
1120
        }
1121
1122
        return $foreignKeys;
1123
    }
1124
1125
    /**
1126 25
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
1127
     *
1128 25
     * @return array
1129
     */
1130 25
    private function getPrimaryIndexInAlteredTable(TableDiff $diff)
1131 9
    {
1132 9
        $primaryIndex = [];
1133
1134
        foreach ($this->getIndexesInAlteredTable($diff) as $index) {
1135
            if ($index->isPrimary()) {
1136 25
                $primaryIndex = [$index->getName() => $index];
1137
            }
1138
        }
1139
1140
        return $primaryIndex;
1141
    }
1142
}
1143