Completed
Push — master ( c7757e...39cb21 )
by Luís
16s
created

lib/Doctrine/DBAL/Platforms/SqlitePlatform.php (1 issue)

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\Types;
31
32
/**
33
 * The SqlitePlatform class describes the specifics and dialects of the SQLite
34
 * database platform.
35
 *
36
 * @since  2.0
37
 * @author Roman Borschel <[email protected]>
38
 * @author Benjamin Eberlei <[email protected]>
39
 * @author Martin Hasoň <[email protected]>
40
 * @todo   Rename: SQLitePlatform
41
 */
42
class SqlitePlatform extends AbstractPlatform
43
{
44
    /**
45
     * {@inheritDoc}
46
     */
47 1
    public function getRegexpExpression()
48
    {
49 1
        return 'REGEXP';
50
    }
51
52
    /**
53
     * {@inheritDoc}
54
     */
55 2
    public function getGuidExpression()
56
    {
57
        return "HEX(RANDOMBLOB(4)) || '-' || HEX(RANDOMBLOB(2)) || '-4' || "
58
            . "SUBSTR(HEX(RANDOMBLOB(2)), 2) || '-' || "
59
            . "SUBSTR('89AB', 1 + (ABS(RANDOM()) % 4), 1) || "
60 2
            . "SUBSTR(HEX(RANDOMBLOB(2)), 2) || '-' || HEX(RANDOMBLOB(6))";
61
    }
62
63
    /**
64
     * {@inheritDoc}
65
     */
66
    public function getNowExpression($type = 'timestamp')
67
    {
68
        switch ($type) {
69
            case 'time':
70
                return 'time(\'now\')';
71
            case 'date':
72
                return 'date(\'now\')';
73
            case 'timestamp':
74
            default:
75
                return 'datetime(\'now\')';
76
        }
77
    }
78
79
    /**
80
     * {@inheritDoc}
81
     */
82 36
    public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false)
83
    {
84 36
        $trimChar = ($char != false) ? (', ' . $char) : '';
85
86 View Code Duplication
        switch ($pos) {
87 36
            case self::TRIM_LEADING:
88 9
                $trimFn = 'LTRIM';
89 9
                break;
90
91 27
            case self::TRIM_TRAILING:
92 9
                $trimFn = 'RTRIM';
93 9
                break;
94
95
            default:
96 18
                $trimFn = 'TRIM';
97
        }
98
99 36
        return $trimFn . '(' . $str . $trimChar . ')';
100
    }
101
102
    /**
103
     * {@inheritDoc}
104
     *
105
     * SQLite only supports the 2 parameter variant of this function
106
     */
107 1 View Code Duplication
    public function getSubstringExpression($value, $position, $length = null)
108
    {
109 1
        if ($length !== null) {
110 1
            return 'SUBSTR(' . $value . ', ' . $position . ', ' . $length . ')';
111
        }
112
113 1
        return 'SUBSTR(' . $value . ', ' . $position . ', LENGTH(' . $value . '))';
114
    }
115
116
    /**
117
     * {@inheritDoc}
118
     */
119 1 View Code Duplication
    public function getLocateExpression($str, $substr, $startPos = false)
120
    {
121 1
        if ($startPos == false) {
122 1
            return 'LOCATE('.$str.', '.$substr.')';
123
        }
124
125 1
        return 'LOCATE('.$str.', '.$substr.', '.$startPos.')';
126
    }
127
128
    /**
129
     * {@inheritdoc}
130
     */
131 1
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
132
    {
133
        switch ($unit) {
134 1
            case self::DATE_INTERVAL_UNIT_SECOND:
135 1
            case self::DATE_INTERVAL_UNIT_MINUTE:
136 1
            case self::DATE_INTERVAL_UNIT_HOUR:
137 1
                return "DATETIME(" . $date . ",'" . $operator . $interval . " " . $unit . "')";
138
139
            default:
140 View Code Duplication
                switch ($unit) {
141 1
                    case self::DATE_INTERVAL_UNIT_WEEK:
142 1
                        $interval *= 7;
143 1
                        $unit = self::DATE_INTERVAL_UNIT_DAY;
144 1
                        break;
145
146 1
                    case self::DATE_INTERVAL_UNIT_QUARTER:
147 1
                        $interval *= 3;
148 1
                        $unit = self::DATE_INTERVAL_UNIT_MONTH;
149 1
                        break;
150
                }
151
152 1
                return "DATE(" . $date . ",'" . $operator . $interval . " " . $unit . "')";
153
        }
154
    }
155
156
    /**
157
     * {@inheritDoc}
158
     */
159 1
    public function getDateDiffExpression($date1, $date2)
160
    {
161 1
        return 'ROUND(JULIANDAY('.$date1 . ')-JULIANDAY('.$date2.'))';
162
    }
163
164
    /**
165
     * {@inheritDoc}
166
     */
167 1 View Code Duplication
    protected function _getTransactionIsolationLevelSQL($level)
168
    {
169
        switch ($level) {
170 1
            case \Doctrine\DBAL\Connection::TRANSACTION_READ_UNCOMMITTED:
171 1
                return 0;
172 1
            case \Doctrine\DBAL\Connection::TRANSACTION_READ_COMMITTED:
173 1
            case \Doctrine\DBAL\Connection::TRANSACTION_REPEATABLE_READ:
174 1
            case \Doctrine\DBAL\Connection::TRANSACTION_SERIALIZABLE:
175 1
                return 1;
176
            default:
177
                return parent::_getTransactionIsolationLevelSQL($level);
178
        }
179
    }
180
181
    /**
182
     * {@inheritDoc}
183
     */
184 1
    public function getSetTransactionIsolationSQL($level)
185
    {
186 1
        return 'PRAGMA read_uncommitted = ' . $this->_getTransactionIsolationLevelSQL($level);
187
    }
188
189
    /**
190
     * {@inheritDoc}
191
     */
192 2
    public function prefersIdentityColumns()
193
    {
194 2
        return true;
195
    }
196
197
    /**
198
     * {@inheritDoc}
199
     */
200 16
    public function getBooleanTypeDeclarationSQL(array $field)
201
    {
202 16
        return 'BOOLEAN';
203
    }
204
205
    /**
206
     * {@inheritDoc}
207
     */
208 144
    public function getIntegerTypeDeclarationSQL(array $field)
209
    {
210 144
        return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($field);
211
    }
212
213
    /**
214
     * {@inheritDoc}
215
     */
216 18 View Code Duplication
    public function getBigIntTypeDeclarationSQL(array $field)
217
    {
218
        //  SQLite autoincrement is implicit for INTEGER PKs, but not for BIGINT fields.
219 18
        if ( ! empty($field['autoincrement'])) {
220 3
            return $this->getIntegerTypeDeclarationSQL($field);
221
        }
222
223 16
        return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
224
    }
225
226
    /**
227
     * {@inheritDoc}
228
     */
229 2 View Code Duplication
    public function getTinyIntTypeDeclarationSql(array $field)
230
    {
231
        //  SQLite autoincrement is implicit for INTEGER PKs, but not for TINYINT fields.
232 2
        if ( ! empty($field['autoincrement'])) {
233 2
            return $this->getIntegerTypeDeclarationSQL($field);
234
        }
235
236 1
        return 'TINYINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
237
    }
238
239
    /**
240
     * {@inheritDoc}
241
     */
242 4 View Code Duplication
    public function getSmallIntTypeDeclarationSQL(array $field)
243
    {
244
        //  SQLite autoincrement is implicit for INTEGER PKs, but not for SMALLINT fields.
245 4
        if ( ! empty($field['autoincrement'])) {
246 3
            return $this->getIntegerTypeDeclarationSQL($field);
247
        }
248
249 2
        return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
250
    }
251
252
    /**
253
     * {@inheritDoc}
254
     */
255 1 View Code Duplication
    public function getMediumIntTypeDeclarationSql(array $field)
256
    {
257
        //  SQLite autoincrement is implicit for INTEGER PKs, but not for MEDIUMINT fields.
258 1
        if ( ! empty($field['autoincrement'])) {
259 1
            return $this->getIntegerTypeDeclarationSQL($field);
260
        }
261
262 1
        return 'MEDIUMINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
263
    }
264
265
    /**
266
     * {@inheritDoc}
267
     */
268 19
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
269
    {
270 19
        return 'DATETIME';
271
    }
272
273
    /**
274
     * {@inheritDoc}
275
     */
276 18
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
277
    {
278 18
        return 'DATE';
279
    }
280
281
    /**
282
     * {@inheritDoc}
283
     */
284 18
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
285
    {
286 18
        return 'TIME';
287
    }
288
289
    /**
290
     * {@inheritDoc}
291
     */
292 144
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
293
    {
294
        // sqlite autoincrement is implicit for integer PKs, but not when the field is unsigned
295 144
        if ( ! empty($columnDef['autoincrement'])) {
296 39
            return '';
297
        }
298
299 131
        return ! empty($columnDef['unsigned']) ? ' UNSIGNED' : '';
300
    }
301
302
    /**
303
     * {@inheritDoc}
304
     */
305 6
    public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey)
306
    {
307 6
        return parent::getForeignKeyDeclarationSQL(new ForeignKeyConstraint(
308 6
            $foreignKey->getQuotedLocalColumns($this),
309 6
            str_replace('.', '__', $foreignKey->getQuotedForeignTableName($this)),
310 6
            $foreignKey->getQuotedForeignColumns($this),
311 6
            $foreignKey->getName(),
312 6
            $foreignKey->getOptions()
313
        ));
314
    }
315
316
    /**
317
     * {@inheritDoc}
318
     */
319 149
    protected function _getCreateTableSQL($name, array $columns, array $options = [])
320
    {
321 149
        $name = str_replace('.', '__', $name);
322 149
        $queryFields = $this->getColumnDeclarationListSQL($columns);
323
324 149
        if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
325
            foreach ($options['uniqueConstraints'] as $name => $definition) {
326
                $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
327
            }
328
        }
329
330 149 View Code Duplication
        if (isset($options['primary']) && ! empty($options['primary'])) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
331 97
            $keyColumns = array_unique(array_values($options['primary']));
332 97
            $queryFields.= ', PRIMARY KEY('.implode(', ', $keyColumns).')';
333
        }
334
335 149
        if (isset($options['foreignKeys'])) {
336 148
            foreach ($options['foreignKeys'] as $foreignKey) {
337 6
                $queryFields.= ', '.$this->getForeignKeyDeclarationSQL($foreignKey);
338
            }
339
        }
340
341 149
        $query[] = 'CREATE TABLE ' . $name . ' (' . $queryFields . ')';
342
343 149
        if (isset($options['alter']) && true === $options['alter']) {
344 25
            return $query;
345
        }
346
347 137 View Code Duplication
        if (isset($options['indexes']) && ! empty($options['indexes'])) {
348 8
            foreach ($options['indexes'] as $indexDef) {
349 8
                $query[] = $this->getCreateIndexSQL($indexDef, $name);
350
            }
351
        }
352
353 137 View Code Duplication
        if (isset($options['unique']) && ! empty($options['unique'])) {
354
            foreach ($options['unique'] as $indexDef) {
355
                $query[] = $this->getCreateIndexSQL($indexDef, $name);
356
            }
357
        }
358
359 137
        return $query;
360
    }
361
362
    /**
363
     * {@inheritDoc}
364
     */
365 80
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
366
    {
367 80
        return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)')
368 80
                : ($length ? 'VARCHAR(' . $length . ')' : 'TEXT');
369
    }
370
371
    /**
372
     * {@inheritdoc}
373
     */
374 2
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
375
    {
376 2
        return 'BLOB';
377
    }
378
379
    /**
380
     * {@inheritdoc}
381
     */
382 6
    public function getBinaryMaxLength()
383
    {
384 6
        return 0;
385
    }
386
387
    /**
388
     * {@inheritdoc}
389
     */
390 3
    public function getBinaryDefaultLength()
391
    {
392 3
        return 0;
393
    }
394
395
    /**
396
     * {@inheritDoc}
397
     */
398 45
    public function getClobTypeDeclarationSQL(array $field)
399
    {
400 45
        return 'CLOB';
401
    }
402
403
    /**
404
     * {@inheritDoc}
405
     */
406 1
    public function getListTableConstraintsSQL($table)
407
    {
408 1
        $table = str_replace('.', '__', $table);
409 1
        $table = $this->quoteStringLiteral($table);
410
411 1
        return "SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name = $table AND sql NOT NULL ORDER BY name";
412
    }
413
414
    /**
415
     * {@inheritDoc}
416
     */
417 41 View Code Duplication
    public function getListTableColumnsSQL($table, $currentDatabase = null)
418
    {
419 41
        $table = str_replace('.', '__', $table);
420 41
        $table = $this->quoteStringLiteral($table);
421
422 41
        return "PRAGMA table_info($table)";
423
    }
424
425
    /**
426
     * {@inheritDoc}
427
     */
428 34 View Code Duplication
    public function getListTableIndexesSQL($table, $currentDatabase = null)
429
    {
430 34
        $table = str_replace('.', '__', $table);
431 34
        $table = $this->quoteStringLiteral($table);
432
433 34
        return "PRAGMA index_list($table)";
434
    }
435
436
    /**
437
     * {@inheritDoc}
438
     */
439 58
    public function getListTablesSQL()
440
    {
441
        return "SELECT name FROM sqlite_master WHERE type = 'table' AND name != 'sqlite_sequence' AND name != 'geometry_columns' AND name != 'spatial_ref_sys' "
442
             . "UNION ALL SELECT name FROM sqlite_temp_master "
443 58
             . "WHERE type = 'table' ORDER BY name";
444
    }
445
446
    /**
447
     * {@inheritDoc}
448
     */
449 1
    public function getListViewsSQL($database)
450
    {
451 1
        return "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL";
452
    }
453
454
    /**
455
     * {@inheritDoc}
456
     */
457 1
    public function getCreateViewSQL($name, $sql)
458
    {
459 1
        return 'CREATE VIEW ' . $name . ' AS ' . $sql;
460
    }
461
462
    /**
463
     * {@inheritDoc}
464
     */
465 1
    public function getDropViewSQL($name)
466
    {
467 1
        return 'DROP VIEW '. $name;
468
    }
469
470
    /**
471
     * {@inheritDoc}
472
     */
473 6
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
474
    {
475 6
        $query = parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
476
477 6
        $query .= (($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false) ? ' ' : ' NOT ') . 'DEFERRABLE';
478 6
        $query .= ' INITIALLY ' . (($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) ? 'DEFERRED' : 'IMMEDIATE');
479
480 6
        return $query;
481
    }
482
483
    /**
484
     * {@inheritDoc}
485
     */
486 3
    public function supportsIdentityColumns()
487
    {
488 3
        return true;
489
    }
490
491
    /**
492
     * {@inheritDoc}
493
     */
494 1
    public function supportsColumnCollation()
495
    {
496 1
        return true;
497
    }
498
499
    /**
500
     * {@inheritDoc}
501
     */
502 153
    public function supportsInlineColumnComments()
503
    {
504 153
        return true;
505
    }
506
507
    /**
508
     * {@inheritDoc}
509
     */
510 64
    public function getName()
511
    {
512 64
        return 'sqlite';
513
    }
514
515
    /**
516
     * {@inheritDoc}
517
     */
518 12
    public function getTruncateTableSQL($tableName, $cascade = false)
519
    {
520 12
        $tableIdentifier = new Identifier($tableName);
521 12
        $tableName = str_replace('.', '__', $tableIdentifier->getQuotedName($this));
522
523 12
        return 'DELETE FROM ' . $tableName;
524
    }
525
526
    /**
527
     * User-defined function for Sqlite that is used with PDO::sqliteCreateFunction().
528
     *
529
     * @param integer|float $value
530
     *
531
     * @return float
532
     */
533
    public static function udfSqrt($value)
534
    {
535
        return sqrt($value);
536
    }
537
538
    /**
539
     * User-defined function for Sqlite that implements MOD(a, b).
540
     *
541
     * @param integer $a
542
     * @param integer $b
543
     *
544
     * @return integer
545
     */
546
    public static function udfMod($a, $b)
547
    {
548
        return ($a % $b);
549
    }
550
551
    /**
552
     * @param string  $str
553
     * @param string  $substr
554
     * @param integer $offset
555
     *
556
     * @return integer
557
     */
558 1
    public static function udfLocate($str, $substr, $offset = 0)
559
    {
560
        // SQL's LOCATE function works on 1-based positions, while PHP's strpos works on 0-based positions.
561
        // So we have to make them compatible if an offset is given.
562 1
        if ($offset > 0) {
563 1
            $offset -= 1;
564
        }
565
566 1
        $pos = strpos($str, $substr, $offset);
567
568 1
        if ($pos !== false) {
569 1
            return $pos + 1;
570
        }
571
572 1
        return 0;
573
    }
574
575
    /**
576
     * {@inheritDoc}
577
     */
578
    public function getForUpdateSql()
579
    {
580
        return '';
581
    }
582
583
    /**
584
     * {@inheritDoc}
585
     */
586 38
    public function getInlineColumnCommentSQL($comment)
587
    {
588 38
        return '--' . str_replace("\n", "\n--", $comment) . "\n";
589
    }
590
591
    /**
592
     * {@inheritDoc}
593
     */
594 7
    protected function initializeDoctrineTypeMappings()
595
    {
596 7
        $this->doctrineTypeMapping = [
597
            'boolean'          => 'boolean',
598
            'tinyint'          => 'boolean',
599
            'smallint'         => 'smallint',
600
            'mediumint'        => 'integer',
601
            'int'              => 'integer',
602
            'integer'          => 'integer',
603
            'serial'           => 'integer',
604
            'bigint'           => 'bigint',
605
            'bigserial'        => 'bigint',
606
            'clob'             => 'text',
607
            'tinytext'         => 'text',
608
            'mediumtext'       => 'text',
609
            'longtext'         => 'text',
610
            'text'             => 'text',
611
            'varchar'          => 'string',
612
            'longvarchar'      => 'string',
613
            'varchar2'         => 'string',
614
            'nvarchar'         => 'string',
615
            'image'            => 'string',
616
            'ntext'            => 'string',
617
            'char'             => 'string',
618
            'date'             => 'date',
619
            'datetime'         => 'datetime',
620
            'timestamp'        => 'datetime',
621
            'time'             => 'time',
622
            'float'            => 'float',
623
            'double'           => 'float',
624
            'double precision' => 'float',
625
            'real'             => 'float',
626
            'decimal'          => 'decimal',
627
            'numeric'          => 'decimal',
628
            'blob'             => 'blob',
629
        ];
630 7
    }
631
632
    /**
633
     * {@inheritDoc}
634
     */
635 49
    protected function getReservedKeywordsClass()
636
    {
637 49
        return Keywords\SQLiteKeywords::class;
638
    }
639
640
    /**
641
     * {@inheritDoc}
642
     */
643 25
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
644
    {
645 25
        if ( ! $diff->fromTable instanceof Table) {
646
            throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema');
647
        }
648
649 25
        $sql = [];
650 25
        foreach ($diff->fromTable->getIndexes() as $index) {
651 9
            if ( ! $index->isPrimary()) {
652 9
                $sql[] = $this->getDropIndexSQL($index, $diff->name);
653
            }
654
        }
655
656 25
        return $sql;
657
    }
658
659
    /**
660
     * {@inheritDoc}
661
     */
662 25
    protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff)
663
    {
664 25
        if ( ! $diff->fromTable instanceof Table) {
665
            throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema');
666
        }
667
668 25
        $sql = [];
669 25
        $tableName = $diff->newName ? $diff->getNewName(): $diff->getName($this);
670 25
        foreach ($this->getIndexesInAlteredTable($diff) as $index) {
671 9
            if ($index->isPrimary()) {
672 7
                continue;
673
            }
674
675 6
            $sql[] = $this->getCreateIndexSQL($index, $tableName->getQuotedName($this));
676
        }
677
678 25
        return $sql;
679
    }
680
681
    /**
682
     * {@inheritDoc}
683
     */
684 11
    protected function doModifyLimitQuery($query, $limit, $offset)
685
    {
686 11
        if (null === $limit && null !== $offset) {
687 3
            return $query . ' LIMIT -1 OFFSET ' . $offset;
688
        }
689
690 9
        return parent::doModifyLimitQuery($query, $limit, $offset);
691
    }
692
693
    /**
694
     * {@inheritDoc}
695
     */
696 6
    public function getBlobTypeDeclarationSQL(array $field)
697
    {
698 6
        return 'BLOB';
699
    }
700
701
    /**
702
     * {@inheritDoc}
703
     */
704 2
    public function getTemporaryTableName($tableName)
705
    {
706 2
        $tableName = str_replace('.', '__', $tableName);
707
708 2
        return $tableName;
709
    }
710
711
    /**
712
     * {@inheritDoc}
713
     *
714
     * Sqlite Platform emulates schema by underscoring each dot and generating tables
715
     * into the default database.
716
     *
717
     * This hack is implemented to be able to use SQLite as testdriver when
718
     * using schema supporting databases.
719
     */
720
    public function canEmulateSchemas()
721
    {
722
        return true;
723
    }
724
725
    /**
726
     * {@inheritDoc}
727
     */
728 36
    public function supportsForeignKeyConstraints()
729
    {
730 36
        return false;
731
    }
732
733
    /**
734
     * {@inheritDoc}
735
     */
736
    public function getCreatePrimaryKeySQL(Index $index, $table)
737
    {
738
        throw new DBALException('Sqlite platform does not support alter primary key.');
739
    }
740
741
    /**
742
     * {@inheritdoc}
743
     */
744 2
    public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table)
745
    {
746 2
        throw new DBALException('Sqlite platform does not support alter foreign key.');
747
    }
748
749
    /**
750
     * {@inheritdoc}
751
     */
752
    public function getDropForeignKeySQL($foreignKey, $table)
753
    {
754
        throw new DBALException('Sqlite platform does not support alter foreign key.');
755
    }
756
757
    /**
758
     * {@inheritDoc}
759
     */
760 1
    public function getCreateConstraintSQL(Constraint $constraint, $table)
761
    {
762 1
        throw new DBALException('Sqlite platform does not support alter constraint.');
763
    }
764
765
    /**
766
     * {@inheritDoc}
767
     */
768 150
    public function getCreateTableSQL(Table $table, $createFlags = null)
769
    {
770 150
        $createFlags = null === $createFlags ? self::CREATE_INDEXES | self::CREATE_FOREIGNKEYS : $createFlags;
771
772 150
        return parent::getCreateTableSQL($table, $createFlags);
773
    }
774
775
    /**
776
     * {@inheritDoc}
777
     */
778 2 View Code Duplication
    public function getListTableForeignKeysSQL($table, $database = null)
779
    {
780 2
        $table = str_replace('.', '__', $table);
781 2
        $table = $this->quoteStringLiteral($table);
782
783 2
        return "PRAGMA foreign_key_list($table)";
784
    }
785
786
    /**
787
     * {@inheritDoc}
788
     */
789 34
    public function getAlterTableSQL(TableDiff $diff)
790
    {
791 34
        $sql = $this->getSimpleAlterTableSQL($diff);
792 34
        if (false !== $sql) {
793 7
            return $sql;
794
        }
795
796 27
        $fromTable = $diff->fromTable;
797 27
        if ( ! $fromTable instanceof Table) {
798 2
            throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema');
799
        }
800
801 25
        $table = clone $fromTable;
802
803 25
        $columns = [];
804 25
        $oldColumnNames = [];
805 25
        $newColumnNames = [];
806 25
        $columnSql = [];
807
808 25
        foreach ($table->getColumns() as $columnName => $column) {
809 24
            $columnName = strtolower($columnName);
810 24
            $columns[$columnName] = $column;
811 24
            $oldColumnNames[$columnName] = $newColumnNames[$columnName] = $column->getQuotedName($this);
812
        }
813
814 25
        foreach ($diff->removedColumns as $columnName => $column) {
815 5
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
816
                continue;
817
            }
818
819 5
            $columnName = strtolower($columnName);
820 5
            if (isset($columns[$columnName])) {
821 5
                unset($columns[$columnName]);
822 5
                unset($oldColumnNames[$columnName]);
823 5
                unset($newColumnNames[$columnName]);
824
            }
825
        }
826
827 25
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
828 5
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
829
                continue;
830
            }
831
832 5
            $oldColumnName = strtolower($oldColumnName);
833 5
            if (isset($columns[$oldColumnName])) {
834 5
                unset($columns[$oldColumnName]);
835
            }
836
837 5
            $columns[strtolower($column->getName())] = $column;
838
839 5
            if (isset($newColumnNames[$oldColumnName])) {
840 5
                $newColumnNames[$oldColumnName] = $column->getQuotedName($this);
841
            }
842
        }
843
844 25
        foreach ($diff->changedColumns as $oldColumnName => $columnDiff) {
845 18
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
846
                continue;
847
            }
848
849 18
            if (isset($columns[$oldColumnName])) {
850 17
                unset($columns[$oldColumnName]);
851
            }
852
853 18
            $columns[strtolower($columnDiff->column->getName())] = $columnDiff->column;
854
855 18
            if (isset($newColumnNames[$oldColumnName])) {
856 18
                $newColumnNames[$oldColumnName] = $columnDiff->column->getQuotedName($this);
857
            }
858
        }
859
860 25
        foreach ($diff->addedColumns as $columnName => $column) {
861 4
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
862
                continue;
863
            }
864
865 4
            $columns[strtolower($columnName)] = $column;
866
        }
867
868 25
        $sql = [];
869 25
        $tableSql = [];
870 25
        if ( ! $this->onSchemaAlterTable($diff, $tableSql)) {
871 25
            $dataTable = new Table('__temp__'.$table->getName());
872
873 25
            $newTable = new Table($table->getQuotedName($this), $columns, $this->getPrimaryIndexInAlteredTable($diff), $this->getForeignKeysInAlteredTable($diff), 0, $table->getOptions());
874 25
            $newTable->addOption('alter', true);
875
876 25
            $sql = $this->getPreAlterTableIndexForeignKeySQL($diff);
877
            //$sql = array_merge($sql, $this->getCreateTableSQL($dataTable, 0));
878 25
            $sql[] = sprintf('CREATE TEMPORARY TABLE %s AS SELECT %s FROM %s', $dataTable->getQuotedName($this), implode(', ', $oldColumnNames), $table->getQuotedName($this));
879 25
            $sql[] = $this->getDropTableSQL($fromTable);
880
881 25
            $sql = array_merge($sql, $this->getCreateTableSQL($newTable));
882 25
            $sql[] = sprintf('INSERT INTO %s (%s) SELECT %s FROM %s', $newTable->getQuotedName($this), implode(', ', $newColumnNames), implode(', ', $oldColumnNames), $dataTable->getQuotedName($this));
883 25
            $sql[] = $this->getDropTableSQL($dataTable);
884
885 25
            if ($diff->newName && $diff->newName != $diff->name) {
886 3
                $renamedTable = $diff->getNewName();
887 3
                $sql[] = 'ALTER TABLE '.$newTable->getQuotedName($this).' RENAME TO '.$renamedTable->getQuotedName($this);
888
            }
889
890 25
            $sql = array_merge($sql, $this->getPostAlterTableIndexForeignKeySQL($diff));
891
        }
892
893 25
        return array_merge($sql, $tableSql, $columnSql);
894
    }
895
896
    /**
897
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
898
     *
899
     * @return array|bool
900
     */
901 34
    private function getSimpleAlterTableSQL(TableDiff $diff)
902
    {
903
        // Suppress changes on integer type autoincrement columns.
904 34
        foreach ($diff->changedColumns as $oldColumnName => $columnDiff) {
905 23
            if ( ! $columnDiff->fromColumn instanceof Column ||
906 19
                ! $columnDiff->column instanceof Column ||
907 19
                ! $columnDiff->column->getAutoincrement() ||
908 23
                ! $columnDiff->column->getType() instanceof Types\IntegerType
909
            ) {
910 18
                continue;
911
            }
912
913 5
            if ( ! $columnDiff->hasChanged('type') && $columnDiff->hasChanged('unsigned')) {
914 1
                unset($diff->changedColumns[$oldColumnName]);
915
916 1
                continue;
917
            }
918
919 4
            $fromColumnType = $columnDiff->fromColumn->getType();
920
921 4
            if ($fromColumnType instanceof Types\SmallIntType || $fromColumnType instanceof Types\BigIntType) {
922 4
                unset($diff->changedColumns[$oldColumnName]);
923
            }
924
        }
925
926 34
        if ( ! empty($diff->renamedColumns) || ! empty($diff->addedForeignKeys) || ! empty($diff->addedIndexes)
927 29
                || ! empty($diff->changedColumns) || ! empty($diff->changedForeignKeys) || ! empty($diff->changedIndexes)
928 13
                || ! empty($diff->removedColumns) || ! empty($diff->removedForeignKeys) || ! empty($diff->removedIndexes)
929 34
                || ! empty($diff->renamedIndexes)
930
        ) {
931 25
            return false;
932
        }
933
934 9
        $table = new Table($diff->name);
935
936 9
        $sql = [];
937 9
        $tableSql = [];
938 9
        $columnSql = [];
939
940 9
        foreach ($diff->addedColumns as $column) {
941 3
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
942
                continue;
943
            }
944
945 3
            $field = array_merge(['unique' => null, 'autoincrement' => null, 'default' => null], $column->toArray());
946 3
            $type = $field['type'];
947
            switch (true) {
948 3
                case isset($field['columnDefinition']) || $field['autoincrement'] || $field['unique']:
949 2
                case $type instanceof Types\DateTimeType && $field['default'] == $this->getCurrentTimestampSQL():
950 2
                case $type instanceof Types\DateType && $field['default'] == $this->getCurrentDateSQL():
951 1
                case $type instanceof Types\TimeType && $field['default'] == $this->getCurrentTimeSQL():
952 2
                    return false;
953
            }
954
955 1
            $field['name'] = $column->getQuotedName($this);
956 1
            if ($type instanceof Types\StringType && $field['length'] === null) {
957 1
                $field['length'] = 255;
958
            }
959
960 1
            $sql[] = 'ALTER TABLE '.$table->getQuotedName($this).' ADD COLUMN '.$this->getColumnDeclarationSQL($field['name'], $field);
961
        }
962
963 7
        if ( ! $this->onSchemaAlterTable($diff, $tableSql)) {
964 7
            if ($diff->newName !== false) {
965 1
                $newTable = new Identifier($diff->newName);
966 1
                $sql[] = 'ALTER TABLE '.$table->getQuotedName($this).' RENAME TO '.$newTable->getQuotedName($this);
967
            }
968
        }
969
970 7
        return array_merge($sql, $tableSql, $columnSql);
971
    }
972
973
    /**
974
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
975
     *
976
     * @return array
977
     */
978 25
    private function getColumnNamesInAlteredTable(TableDiff $diff)
979
    {
980 25
        $columns = [];
981
982 25
        foreach ($diff->fromTable->getColumns() as $columnName => $column) {
983 24
            $columns[strtolower($columnName)] = $column->getName();
984
        }
985
986 25
        foreach ($diff->removedColumns as $columnName => $column) {
987 5
            $columnName = strtolower($columnName);
988 5
            if (isset($columns[$columnName])) {
989 5
                unset($columns[$columnName]);
990
            }
991
        }
992
993 25 View Code Duplication
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
994 5
            $columnName = $column->getName();
995 5
            $columns[strtolower($oldColumnName)] = $columnName;
996 5
            $columns[strtolower($columnName)] = $columnName;
997
        }
998
999 25 View Code Duplication
        foreach ($diff->changedColumns as $oldColumnName => $columnDiff) {
1000 18
            $columnName = $columnDiff->column->getName();
1001 18
            $columns[strtolower($oldColumnName)] = $columnName;
1002 18
            $columns[strtolower($columnName)] = $columnName;
1003
        }
1004
1005 25
        foreach ($diff->addedColumns as $columnName => $column) {
1006 4
            $columns[strtolower($columnName)] = $columnName;
1007
        }
1008
1009 25
        return $columns;
1010
    }
1011
1012
    /**
1013
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
1014
     *
1015
     * @return \Doctrine\DBAL\Schema\Index[]
1016
     */
1017 25
    private function getIndexesInAlteredTable(TableDiff $diff)
1018
    {
1019 25
        $indexes = $diff->fromTable->getIndexes();
1020 25
        $columnNames = $this->getColumnNamesInAlteredTable($diff);
1021
1022 25
        foreach ($indexes as $key => $index) {
1023 9
            foreach ($diff->renamedIndexes as $oldIndexName => $renamedIndex) {
1024 4
                if (strtolower($key) === strtolower($oldIndexName)) {
1025 4
                    unset($indexes[$key]);
1026
                }
1027
            }
1028
1029 9
            $changed = false;
1030 9
            $indexColumns = [];
1031 9 View Code Duplication
            foreach ($index->getColumns() as $columnName) {
1032 9
                $normalizedColumnName = strtolower($columnName);
1033 9
                if ( ! isset($columnNames[$normalizedColumnName])) {
1034 1
                    unset($indexes[$key]);
1035 1
                    continue 2;
1036
                } else {
1037 9
                    $indexColumns[] = $columnNames[$normalizedColumnName];
1038 9
                    if ($columnName !== $columnNames[$normalizedColumnName]) {
1039 9
                        $changed = true;
1040
                    }
1041
                }
1042
            }
1043
1044 9
            if ($changed) {
1045 9
                $indexes[$key] = new Index($index->getName(), $indexColumns, $index->isUnique(), $index->isPrimary(), $index->getFlags());
1046
            }
1047
        }
1048
1049 25
        foreach ($diff->removedIndexes as $index) {
1050 2
            $indexName = strtolower($index->getName());
1051 2
            if (strlen($indexName) && isset($indexes[$indexName])) {
1052 2
                unset($indexes[$indexName]);
1053
            }
1054
        }
1055
1056 25
        foreach (array_merge($diff->changedIndexes, $diff->addedIndexes, $diff->renamedIndexes) as $index) {
1057 4
            $indexName = strtolower($index->getName());
1058 4
            if (strlen($indexName)) {
1059 4
                $indexes[$indexName] = $index;
1060
            } else {
1061 4
                $indexes[] = $index;
1062
            }
1063
        }
1064
1065 25
        return $indexes;
1066
    }
1067
1068
    /**
1069
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
1070
     *
1071
     * @return array
1072
     */
1073 25
    private function getForeignKeysInAlteredTable(TableDiff $diff)
1074
    {
1075 25
        $foreignKeys = $diff->fromTable->getForeignKeys();
1076 25
        $columnNames = $this->getColumnNamesInAlteredTable($diff);
1077
1078 25
        foreach ($foreignKeys as $key => $constraint) {
1079 3
            $changed = false;
1080 3
            $localColumns = [];
1081 3 View Code Duplication
            foreach ($constraint->getLocalColumns() as $columnName) {
1082 3
                $normalizedColumnName = strtolower($columnName);
1083 3
                if ( ! isset($columnNames[$normalizedColumnName])) {
1084 1
                    unset($foreignKeys[$key]);
1085 1
                    continue 2;
1086
                } else {
1087 3
                    $localColumns[] = $columnNames[$normalizedColumnName];
1088 3
                    if ($columnName !== $columnNames[$normalizedColumnName]) {
1089 3
                        $changed = true;
1090
                    }
1091
                }
1092
            }
1093
1094 3
            if ($changed) {
1095 3
                $foreignKeys[$key] = new ForeignKeyConstraint($localColumns, $constraint->getForeignTableName(), $constraint->getForeignColumns(), $constraint->getName(), $constraint->getOptions());
1096
            }
1097
        }
1098
1099 25
        foreach ($diff->removedForeignKeys as $constraint) {
1100 1
            $constraintName = strtolower($constraint->getName());
1101 1
            if (strlen($constraintName) && isset($foreignKeys[$constraintName])) {
1102 1
                unset($foreignKeys[$constraintName]);
1103
            }
1104
        }
1105
1106 25
        foreach (array_merge($diff->changedForeignKeys, $diff->addedForeignKeys) as $constraint) {
1107 2
            $constraintName = strtolower($constraint->getName());
1108 2
            if (strlen($constraintName)) {
1109 1
                $foreignKeys[$constraintName] = $constraint;
1110
            } else {
1111 2
                $foreignKeys[] = $constraint;
1112
            }
1113
        }
1114
1115 25
        return $foreignKeys;
1116
    }
1117
1118
    /**
1119
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
1120
     *
1121
     * @return array
1122
     */
1123 25
    private function getPrimaryIndexInAlteredTable(TableDiff $diff)
1124
    {
1125 25
        $primaryIndex = [];
1126
1127 25
        foreach ($this->getIndexesInAlteredTable($diff) as $index) {
1128 9
            if ($index->isPrimary()) {
1129 9
                $primaryIndex = [$index->getName() => $index];
1130
            }
1131
        }
1132
1133 25
        return $primaryIndex;
1134
    }
1135
}
1136