Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Doctrine\DBAL\Platforms;
4
 
5
use Doctrine\DBAL\Connection;
6
use Doctrine\DBAL\Driver\API\SQLite\UserDefinedFunctions;
7
use Doctrine\DBAL\Exception;
8
use Doctrine\DBAL\Schema\Column;
9
use Doctrine\DBAL\Schema\Constraint;
10
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
11
use Doctrine\DBAL\Schema\Identifier;
12
use Doctrine\DBAL\Schema\Index;
13
use Doctrine\DBAL\Schema\SchemaException;
14
use Doctrine\DBAL\Schema\SqliteSchemaManager;
15
use Doctrine\DBAL\Schema\Table;
16
use Doctrine\DBAL\Schema\TableDiff;
17
use Doctrine\DBAL\TransactionIsolationLevel;
18
use Doctrine\DBAL\Types;
19
use Doctrine\DBAL\Types\IntegerType;
20
use Doctrine\Deprecations\Deprecation;
21
 
22
use function array_combine;
23
use function array_keys;
24
use function array_merge;
25
use function array_search;
26
use function array_unique;
27
use function array_values;
28
use function count;
29
use function implode;
30
use function is_numeric;
31
use function sprintf;
32
use function sqrt;
33
use function str_replace;
34
use function strlen;
35
use function strtolower;
36
use function trim;
37
 
38
/**
39
 * The SqlitePlatform class describes the specifics and dialects of the SQLite
40
 * database platform.
41
 *
42
 * @todo   Rename: SQLitePlatform
43
 */
44
class SqlitePlatform extends AbstractPlatform
45
{
46
    private bool $schemaEmulationEnabled = true;
47
 
48
    /**
49
     * {@inheritDoc}
50
     */
51
    public function getRegexpExpression()
52
    {
53
        return 'REGEXP';
54
    }
55
 
56
    /**
57
     * @deprecated Generate dates within the application.
58
     *
59
     * @param string $type
60
     *
61
     * @return string
62
     */
63
    public function getNowExpression($type = 'timestamp')
64
    {
65
        Deprecation::trigger(
66
            'doctrine/dbal',
67
            'https://github.com/doctrine/dbal/pull/4753',
68
            'SqlitePlatform::getNowExpression() is deprecated. Generate dates within the application.',
69
        );
70
 
71
        switch ($type) {
72
            case 'time':
73
                return 'time(\'now\')';
74
 
75
            case 'date':
76
                return 'date(\'now\')';
77
 
78
            case 'timestamp':
79
            default:
80
                return 'datetime(\'now\')';
81
        }
82
    }
83
 
84
    /**
85
     * {@inheritDoc}
86
     */
87
    public function getModExpression($expression1, $expression2)
88
    {
89
        return $expression1 . ' % ' . $expression2;
90
    }
91
 
92
    /**
93
     * {@inheritDoc}
94
     */
95
    public function getTrimExpression($str, $mode = TrimMode::UNSPECIFIED, $char = false)
96
    {
97
        $trimChar = $char !== false ? ', ' . $char : '';
98
 
99
        switch ($mode) {
100
            case TrimMode::LEADING:
101
                $trimFn = 'LTRIM';
102
                break;
103
 
104
            case TrimMode::TRAILING:
105
                $trimFn = 'RTRIM';
106
                break;
107
 
108
            default:
109
                $trimFn = 'TRIM';
110
        }
111
 
112
        return $trimFn . '(' . $str . $trimChar . ')';
113
    }
114
 
115
    /**
116
     * {@inheritDoc}
117
     *
118
     * SQLite only supports the 2 parameter variant of this function
119
     */
120
    public function getSubstringExpression($string, $start, $length = null)
121
    {
122
        if ($length !== null) {
123
            return 'SUBSTR(' . $string . ', ' . $start . ', ' . $length . ')';
124
        }
125
 
126
        return 'SUBSTR(' . $string . ', ' . $start . ', LENGTH(' . $string . '))';
127
    }
128
 
129
    /**
130
     * {@inheritDoc}
131
     */
132
    public function getLocateExpression($str, $substr, $startPos = false)
133
    {
134
        if ($startPos === false || $startPos === 1 || $startPos === '1') {
135
            return 'INSTR(' . $str . ', ' . $substr . ')';
136
        }
137
 
138
        return 'CASE WHEN INSTR(SUBSTR(' . $str . ', ' . $startPos . '), ' . $substr
139
            . ') > 0 THEN INSTR(SUBSTR(' . $str . ', ' . $startPos . '), ' . $substr . ') + ' . $startPos
140
            . ' - 1 ELSE 0 END';
141
    }
142
 
143
    /**
144
     * {@inheritdoc}
145
     */
146
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
147
    {
148
        switch ($unit) {
149
            case DateIntervalUnit::SECOND:
150
            case DateIntervalUnit::MINUTE:
151
            case DateIntervalUnit::HOUR:
152
                return 'DATETIME(' . $date . ",'" . $operator . $interval . ' ' . $unit . "')";
153
        }
154
 
155
        switch ($unit) {
156
            case DateIntervalUnit::WEEK:
157
                $interval *= 7;
158
                $unit      = DateIntervalUnit::DAY;
159
                break;
160
 
161
            case DateIntervalUnit::QUARTER:
162
                $interval *= 3;
163
                $unit      = DateIntervalUnit::MONTH;
164
                break;
165
        }
166
 
167
        if (! is_numeric($interval)) {
168
            $interval = "' || " . $interval . " || '";
169
        }
170
 
171
        return 'DATE(' . $date . ",'" . $operator . $interval . ' ' . $unit . "')";
172
    }
173
 
174
    /**
175
     * {@inheritDoc}
176
     */
177
    public function getDateDiffExpression($date1, $date2)
178
    {
179
        return sprintf("JULIANDAY(%s, 'start of day') - JULIANDAY(%s, 'start of day')", $date1, $date2);
180
    }
181
 
182
    /**
183
     * {@inheritDoc}
184
     *
185
     * The DBAL doesn't support databases on the SQLite platform. The expression here always returns a fixed string
186
     * as an indicator of an implicitly selected database.
187
     *
188
     * @link https://www.sqlite.org/lang_select.html
189
     * @see Connection::getDatabase()
190
     */
191
    public function getCurrentDatabaseExpression(): string
192
    {
193
        return "'main'";
194
    }
195
 
196
    /**
197
     * {@inheritDoc}
198
     */
199
    protected function _getTransactionIsolationLevelSQL($level)
200
    {
201
        switch ($level) {
202
            case TransactionIsolationLevel::READ_UNCOMMITTED:
203
                return '0';
204
 
205
            case TransactionIsolationLevel::READ_COMMITTED:
206
            case TransactionIsolationLevel::REPEATABLE_READ:
207
            case TransactionIsolationLevel::SERIALIZABLE:
208
                return '1';
209
 
210
            default:
211
                return parent::_getTransactionIsolationLevelSQL($level);
212
        }
213
    }
214
 
215
    /**
216
     * {@inheritDoc}
217
     */
218
    public function getSetTransactionIsolationSQL($level)
219
    {
220
        return 'PRAGMA read_uncommitted = ' . $this->_getTransactionIsolationLevelSQL($level);
221
    }
222
 
223
    /**
224
     * {@inheritDoc}
225
     *
226
     * @deprecated
227
     */
228
    public function prefersIdentityColumns()
229
    {
230
        Deprecation::trigger(
231
            'doctrine/dbal',
232
            'https://github.com/doctrine/dbal/pull/1519',
233
            'SqlitePlatform::prefersIdentityColumns() is deprecated.',
234
        );
235
 
236
        return true;
237
    }
238
 
239
    /**
240
     * {@inheritDoc}
241
     */
242
    public function getBooleanTypeDeclarationSQL(array $column)
243
    {
244
        return 'BOOLEAN';
245
    }
246
 
247
    /**
248
     * {@inheritDoc}
249
     */
250
    public function getIntegerTypeDeclarationSQL(array $column)
251
    {
252
        return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($column);
253
    }
254
 
255
    /**
256
     * {@inheritDoc}
257
     */
258
    public function getBigIntTypeDeclarationSQL(array $column)
259
    {
260
        // SQLite autoincrement is implicit for INTEGER PKs, but not for BIGINT columns
261
        if (! empty($column['autoincrement'])) {
262
            return $this->getIntegerTypeDeclarationSQL($column);
263
        }
264
 
265
        return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column);
266
    }
267
 
268
    /**
269
     * @deprecated Use {@see getSmallIntTypeDeclarationSQL()} instead.
270
     *
271
     * @param array<string, mixed> $column
272
     *
273
     * @return string
274
     */
275
    public function getTinyIntTypeDeclarationSQL(array $column)
276
    {
277
        Deprecation::trigger(
278
            'doctrine/dbal',
279
            'https://github.com/doctrine/dbal/pull/5511',
280
            '%s is deprecated. Use getSmallIntTypeDeclarationSQL() instead.',
281
            __METHOD__,
282
        );
283
 
284
        // SQLite autoincrement is implicit for INTEGER PKs, but not for TINYINT columns
285
        if (! empty($column['autoincrement'])) {
286
            return $this->getIntegerTypeDeclarationSQL($column);
287
        }
288
 
289
        return 'TINYINT' . $this->_getCommonIntegerTypeDeclarationSQL($column);
290
    }
291
 
292
    /**
293
     * {@inheritDoc}
294
     */
295
    public function getSmallIntTypeDeclarationSQL(array $column)
296
    {
297
        // SQLite autoincrement is implicit for INTEGER PKs, but not for SMALLINT columns
298
        if (! empty($column['autoincrement'])) {
299
            return $this->getIntegerTypeDeclarationSQL($column);
300
        }
301
 
302
        return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column);
303
    }
304
 
305
    /**
306
     * @deprecated Use {@see getIntegerTypeDeclarationSQL()} instead.
307
     *
308
     * @param array<string, mixed> $column
309
     *
310
     * @return string
311
     */
312
    public function getMediumIntTypeDeclarationSQL(array $column)
313
    {
314
        Deprecation::trigger(
315
            'doctrine/dbal',
316
            'https://github.com/doctrine/dbal/pull/5511',
317
            '%s is deprecated. Use getIntegerTypeDeclarationSQL() instead.',
318
            __METHOD__,
319
        );
320
 
321
        // SQLite autoincrement is implicit for INTEGER PKs, but not for MEDIUMINT columns
322
        if (! empty($column['autoincrement'])) {
323
            return $this->getIntegerTypeDeclarationSQL($column);
324
        }
325
 
326
        return 'MEDIUMINT' . $this->_getCommonIntegerTypeDeclarationSQL($column);
327
    }
328
 
329
    /**
330
     * {@inheritDoc}
331
     */
332
    public function getDateTimeTypeDeclarationSQL(array $column)
333
    {
334
        return 'DATETIME';
335
    }
336
 
337
    /**
338
     * {@inheritDoc}
339
     */
340
    public function getDateTypeDeclarationSQL(array $column)
341
    {
342
        return 'DATE';
343
    }
344
 
345
    /**
346
     * {@inheritDoc}
347
     */
348
    public function getTimeTypeDeclarationSQL(array $column)
349
    {
350
        return 'TIME';
351
    }
352
 
353
    /**
354
     * {@inheritDoc}
355
     */
356
    protected function _getCommonIntegerTypeDeclarationSQL(array $column)
357
    {
358
        // sqlite autoincrement is only possible for the primary key
359
        if (! empty($column['autoincrement'])) {
360
            return ' PRIMARY KEY AUTOINCREMENT';
361
        }
362
 
363
        return ! empty($column['unsigned']) ? ' UNSIGNED' : '';
364
    }
365
 
366
    /**
367
     * Disables schema emulation.
368
     *
369
     * Schema emulation is enabled by default to maintain backwards compatibility.
370
     * Disable it to opt-in to the behavior of DBAL 4.
371
     *
372
     * @deprecated Will be removed in DBAL 4.0.
373
     */
374
    public function disableSchemaEmulation(): void
375
    {
376
        $this->schemaEmulationEnabled = false;
377
    }
378
 
379
    private function emulateSchemaNamespacing(string $tableName): string
380
    {
381
        return $this->schemaEmulationEnabled
382
            ? str_replace('.', '__', $tableName)
383
            : $tableName;
384
    }
385
 
386
    /**
387
     * {@inheritDoc}
388
     *
389
     * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy.
390
     */
391
    public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey)
392
    {
393
        return parent::getForeignKeyDeclarationSQL(new ForeignKeyConstraint(
394
            $foreignKey->getQuotedLocalColumns($this),
395
            $this->emulateSchemaNamespacing($foreignKey->getQuotedForeignTableName($this)),
396
            $foreignKey->getQuotedForeignColumns($this),
397
            $foreignKey->getName(),
398
            $foreignKey->getOptions(),
399
        ));
400
    }
401
 
402
    /**
403
     * {@inheritDoc}
404
     */
405
    protected function _getCreateTableSQL($name, array $columns, array $options = [])
406
    {
407
        $name        = $this->emulateSchemaNamespacing($name);
408
        $queryFields = $this->getColumnDeclarationListSQL($columns);
409
 
410
        if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
411
            foreach ($options['uniqueConstraints'] as $constraintName => $definition) {
412
                $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($constraintName, $definition);
413
            }
414
        }
415
 
416
        $queryFields .= $this->getNonAutoincrementPrimaryKeyDefinition($columns, $options);
417
 
418
        if (isset($options['foreignKeys'])) {
419
            foreach ($options['foreignKeys'] as $foreignKey) {
420
                $queryFields .= ', ' . $this->getForeignKeyDeclarationSQL($foreignKey);
421
            }
422
        }
423
 
424
        $tableComment = '';
425
        if (isset($options['comment'])) {
426
            $comment = trim($options['comment'], " '");
427
 
428
            $tableComment = $this->getInlineTableCommentSQL($comment);
429
        }
430
 
431
        $query = ['CREATE TABLE ' . $name . ' ' . $tableComment . '(' . $queryFields . ')'];
432
 
433
        if (isset($options['alter']) && $options['alter'] === true) {
434
            return $query;
435
        }
436
 
437
        if (isset($options['indexes']) && ! empty($options['indexes'])) {
438
            foreach ($options['indexes'] as $indexDef) {
439
                $query[] = $this->getCreateIndexSQL($indexDef, $name);
440
            }
441
        }
442
 
443
        if (isset($options['unique']) && ! empty($options['unique'])) {
444
            foreach ($options['unique'] as $indexDef) {
445
                $query[] = $this->getCreateIndexSQL($indexDef, $name);
446
            }
447
        }
448
 
449
        return $query;
450
    }
451
 
452
    /**
453
     * Generate a PRIMARY KEY definition if no autoincrement value is used
454
     *
455
     * @param mixed[][] $columns
456
     * @param mixed[]   $options
457
     */
458
    private function getNonAutoincrementPrimaryKeyDefinition(array $columns, array $options): string
459
    {
460
        if (empty($options['primary'])) {
461
            return '';
462
        }
463
 
464
        $keyColumns = array_unique(array_values($options['primary']));
465
 
466
        foreach ($keyColumns as $keyColumn) {
467
            if (! empty($columns[$keyColumn]['autoincrement'])) {
468
                return '';
469
            }
470
        }
471
 
472
        return ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
473
    }
474
 
475
    /**
476
     * {@inheritDoc}
477
     */
478
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
479
    {
480
        return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(255)')
481
            : ($length > 0 ? 'VARCHAR(' . $length . ')' : 'TEXT');
482
    }
483
 
484
    /**
485
     * {@inheritdoc}
486
     */
487
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
488
    {
489
        return 'BLOB';
490
    }
491
 
492
    /**
493
     * {@inheritdoc}
494
     *
495
     * @deprecated
496
     */
497
    public function getBinaryMaxLength()
498
    {
499
        Deprecation::triggerIfCalledFromOutside(
500
            'doctrine/dbal',
501
            'https://github.com/doctrine/dbal/issues/3263',
502
            'SqlitePlatform::getBinaryMaxLength() is deprecated.',
503
        );
504
 
505
        return 0;
506
    }
507
 
508
    /**
509
     * {@inheritdoc}
510
     *
511
     * @deprecated
512
     */
513
    public function getBinaryDefaultLength()
514
    {
515
        Deprecation::trigger(
516
            'doctrine/dbal',
517
            'https://github.com/doctrine/dbal/issues/3263',
518
            'Relying on the default binary column length is deprecated, specify the length explicitly.',
519
        );
520
 
521
        return 0;
522
    }
523
 
524
    /**
525
     * {@inheritDoc}
526
     */
527
    public function getClobTypeDeclarationSQL(array $column)
528
    {
529
        return 'CLOB';
530
    }
531
 
532
    /**
533
     * @deprecated
534
     *
535
     * {@inheritDoc}
536
     */
537
    public function getListTableConstraintsSQL($table)
538
    {
539
        $table = $this->emulateSchemaNamespacing($table);
540
 
541
        return sprintf(
542
            "SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name = %s AND sql NOT NULL ORDER BY name",
543
            $this->quoteStringLiteral($table),
544
        );
545
    }
546
 
547
    /**
548
     * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon.
549
     *
550
     * {@inheritDoc}
551
     */
552
    public function getListTableColumnsSQL($table, $database = null)
553
    {
554
        $table = $this->emulateSchemaNamespacing($table);
555
 
556
        return sprintf('PRAGMA table_info(%s)', $this->quoteStringLiteral($table));
557
    }
558
 
559
    /**
560
     * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon.
561
     *
562
     * {@inheritDoc}
563
     */
564
    public function getListTableIndexesSQL($table, $database = null)
565
    {
566
        $table = $this->emulateSchemaNamespacing($table);
567
 
568
        return sprintf('PRAGMA index_list(%s)', $this->quoteStringLiteral($table));
569
    }
570
 
571
    /**
572
     * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon.
573
     *
574
     * {@inheritDoc}
575
     */
576
    public function getListTablesSQL()
577
    {
578
        return 'SELECT name FROM sqlite_master'
579
            . " WHERE type = 'table'"
580
            . " AND name != 'sqlite_sequence'"
581
            . " AND name != 'geometry_columns'"
582
            . " AND name != 'spatial_ref_sys'"
583
            . ' UNION ALL SELECT name FROM sqlite_temp_master'
584
            . " WHERE type = 'table' ORDER BY name";
585
    }
586
 
587
    /**
588
     * {@inheritDoc}
589
     *
590
     * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy.
591
     */
592
    public function getListViewsSQL($database)
593
    {
594
        return "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL";
595
    }
596
 
597
    /**
598
     * {@inheritDoc}
599
     *
600
     * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy.
601
     */
602
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
603
    {
604
        $query = parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
605
 
606
        if (! $foreignKey->hasOption('deferrable') || $foreignKey->getOption('deferrable') === false) {
607
            $query .= ' NOT';
608
        }
609
 
610
        $query .= ' DEFERRABLE';
611
        $query .= ' INITIALLY';
612
 
613
        if ($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) {
614
            $query .= ' DEFERRED';
615
        } else {
616
            $query .= ' IMMEDIATE';
617
        }
618
 
619
        return $query;
620
    }
621
 
622
    /**
623
     * {@inheritDoc}
624
     *
625
     * @deprecated
626
     */
627
    public function supportsCreateDropDatabase()
628
    {
629
        Deprecation::trigger(
630
            'doctrine/dbal',
631
            'https://github.com/doctrine/dbal/pull/5513',
632
            '%s is deprecated.',
633
            __METHOD__,
634
        );
635
 
636
        return false;
637
    }
638
 
639
    /**
640
     * {@inheritDoc}
641
     */
642
    public function supportsIdentityColumns()
643
    {
644
        return true;
645
    }
646
 
647
    /**
648
     * {@inheritDoc}
649
     *
650
     * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy.
651
     */
652
    public function supportsColumnCollation()
653
    {
654
        return true;
655
    }
656
 
657
    /**
658
     * {@inheritDoc}
659
     *
660
     * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy.
661
     */
662
    public function supportsInlineColumnComments()
663
    {
664
        return true;
665
    }
666
 
667
    /**
668
     * {@inheritDoc}
669
     */
670
    public function getName()
671
    {
672
        Deprecation::triggerIfCalledFromOutside(
673
            'doctrine/dbal',
674
            'https://github.com/doctrine/dbal/issues/4749',
675
            'SqlitePlatform::getName() is deprecated. Identify platforms by their class.',
676
        );
677
 
678
        return 'sqlite';
679
    }
680
 
681
    /**
682
     * {@inheritDoc}
683
     */
684
    public function getTruncateTableSQL($tableName, $cascade = false)
685
    {
686
        $tableIdentifier = new Identifier($tableName);
687
        $tableName       = $this->emulateSchemaNamespacing($tableIdentifier->getQuotedName($this));
688
 
689
        return 'DELETE FROM ' . $tableName;
690
    }
691
 
692
    /**
693
     * User-defined function for Sqlite that is used with PDO::sqliteCreateFunction().
694
     *
695
     * @deprecated The driver will use {@see sqrt()} in the next major release.
696
     *
697
     * @param int|float $value
698
     *
699
     * @return float
700
     */
701
    public static function udfSqrt($value)
702
    {
703
        return sqrt($value);
704
    }
705
 
706
    /**
707
     * User-defined function for Sqlite that implements MOD(a, b).
708
     *
709
     * @deprecated The driver will use {@see UserDefinedFunctions::mod()} in the next major release.
710
     *
711
     * @param int $a
712
     * @param int $b
713
     *
714
     * @return int
715
     */
716
    public static function udfMod($a, $b)
717
    {
718
        return UserDefinedFunctions::mod($a, $b);
719
    }
720
 
721
    /**
722
     * @deprecated The driver will use {@see UserDefinedFunctions::locate()} in the next major release.
723
     *
724
     * @param string $str
725
     * @param string $substr
726
     * @param int    $offset
727
     *
728
     * @return int
729
     */
730
    public static function udfLocate($str, $substr, $offset = 0)
731
    {
732
        return UserDefinedFunctions::locate($str, $substr, $offset);
733
    }
734
 
735
    /**
736
     * {@inheritDoc}
737
     */
738
    public function getForUpdateSQL()
739
    {
740
        return '';
741
    }
742
 
743
    /**
744
     * {@inheritDoc}
745
     *
746
     * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy.
747
     */
748
    public function getInlineColumnCommentSQL($comment)
749
    {
750
        return '--' . str_replace("\n", "\n--", $comment) . "\n";
751
    }
752
 
753
    private function getInlineTableCommentSQL(string $comment): string
754
    {
755
        return $this->getInlineColumnCommentSQL($comment);
756
    }
757
 
758
    /**
759
     * {@inheritDoc}
760
     */
761
    protected function initializeDoctrineTypeMappings()
762
    {
763
        $this->doctrineTypeMapping = [
764
            'bigint'           => 'bigint',
765
            'bigserial'        => 'bigint',
766
            'blob'             => 'blob',
767
            'boolean'          => 'boolean',
768
            'char'             => 'string',
769
            'clob'             => 'text',
770
            'date'             => 'date',
771
            'datetime'         => 'datetime',
772
            'decimal'          => 'decimal',
773
            'double'           => 'float',
774
            'double precision' => 'float',
775
            'float'            => 'float',
776
            'image'            => 'string',
777
            'int'              => 'integer',
778
            'integer'          => 'integer',
779
            'longtext'         => 'text',
780
            'longvarchar'      => 'string',
781
            'mediumint'        => 'integer',
782
            'mediumtext'       => 'text',
783
            'ntext'            => 'string',
784
            'numeric'          => 'decimal',
785
            'nvarchar'         => 'string',
786
            'real'             => 'float',
787
            'serial'           => 'integer',
788
            'smallint'         => 'smallint',
789
            'text'             => 'text',
790
            'time'             => 'time',
791
            'timestamp'        => 'datetime',
792
            'tinyint'          => 'boolean',
793
            'tinytext'         => 'text',
794
            'varchar'          => 'string',
795
            'varchar2'         => 'string',
796
        ];
797
    }
798
 
799
    /**
800
     * {@inheritDoc}
801
     *
802
     * @deprecated Implement {@see createReservedKeywordsList()} instead.
803
     */
804
    protected function getReservedKeywordsClass()
805
    {
806
        Deprecation::triggerIfCalledFromOutside(
807
            'doctrine/dbal',
808
            'https://github.com/doctrine/dbal/issues/4510',
809
            'SqlitePlatform::getReservedKeywordsClass() is deprecated,'
810
                . ' use SqlitePlatform::createReservedKeywordsList() instead.',
811
        );
812
 
813
        return Keywords\SQLiteKeywords::class;
814
    }
815
 
816
    /**
817
     * {@inheritDoc}
818
     */
819
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
820
    {
821
        return [];
822
    }
823
 
824
    /**
825
     * {@inheritDoc}
826
     */
827
    protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff)
828
    {
829
        $table = $diff->getOldTable();
830
 
831
        if (! $table instanceof Table) {
832
            throw new Exception(
833
                'Sqlite platform requires for alter table the table diff with reference to original table schema',
834
            );
835
        }
836
 
837
        $sql       = [];
838
        $tableName = $diff->getNewName();
839
 
840
        if ($tableName === false) {
841
            $tableName = $diff->getName($this);
842
        }
843
 
844
        foreach ($this->getIndexesInAlteredTable($diff, $table) as $index) {
845
            if ($index->isPrimary()) {
846
                continue;
847
            }
848
 
849
            $sql[] = $this->getCreateIndexSQL($index, $tableName->getQuotedName($this));
850
        }
851
 
852
        return $sql;
853
    }
854
 
855
    /**
856
     * {@inheritDoc}
857
     */
858
    protected function doModifyLimitQuery($query, $limit, $offset)
859
    {
860
        if ($limit === null && $offset > 0) {
861
            return sprintf('%s LIMIT -1 OFFSET %d', $query, $offset);
862
        }
863
 
864
        return parent::doModifyLimitQuery($query, $limit, $offset);
865
    }
866
 
867
    /**
868
     * {@inheritDoc}
869
     */
870
    public function getBlobTypeDeclarationSQL(array $column)
871
    {
872
        return 'BLOB';
873
    }
874
 
875
    /**
876
     * {@inheritDoc}
877
     */
878
    public function getTemporaryTableName($tableName)
879
    {
880
        $tableName = $this->emulateSchemaNamespacing($tableName);
881
 
882
        return $tableName;
883
    }
884
 
885
    /**
886
     * {@inheritDoc}
887
     *
888
     * @deprecated
889
     *
890
     * Sqlite Platform emulates schema by underscoring each dot and generating tables
891
     * into the default database.
892
     *
893
     * This hack is implemented to be able to use SQLite as testdriver when
894
     * using schema supporting databases.
895
     */
896
    public function canEmulateSchemas()
897
    {
898
        Deprecation::trigger(
899
            'doctrine/dbal',
900
            'https://github.com/doctrine/dbal/pull/4805',
901
            'SqlitePlatform::canEmulateSchemas() is deprecated.',
902
        );
903
 
904
        return $this->schemaEmulationEnabled;
905
    }
906
 
907
    /**
908
     * {@inheritDoc}
909
     */
910
    public function getCreateTablesSQL(array $tables): array
911
    {
912
        $sql = [];
913
 
914
        foreach ($tables as $table) {
915
            $sql = array_merge($sql, $this->getCreateTableSQL($table));
916
        }
917
 
918
        return $sql;
919
    }
920
 
921
    /**
922
     * {@inheritDoc}
923
     */
924
    public function getDropTablesSQL(array $tables): array
925
    {
926
        $sql = [];
927
 
928
        foreach ($tables as $table) {
929
            $sql[] = $this->getDropTableSQL($table->getQuotedName($this));
930
        }
931
 
932
        return $sql;
933
    }
934
 
935
    /**
936
     * {@inheritDoc}
937
     */
938
    public function getCreatePrimaryKeySQL(Index $index, $table)
939
    {
940
        throw new Exception('Sqlite platform does not support alter primary key.');
941
    }
942
 
943
    /**
944
     * {@inheritdoc}
945
     */
946
    public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table)
947
    {
948
        throw new Exception('Sqlite platform does not support alter foreign key.');
949
    }
950
 
951
    /**
952
     * {@inheritdoc}
953
     */
954
    public function getDropForeignKeySQL($foreignKey, $table)
955
    {
956
        throw new Exception('Sqlite platform does not support alter foreign key.');
957
    }
958
 
959
    /**
960
     * {@inheritDoc}
961
     *
962
     * @deprecated
963
     */
964
    public function getCreateConstraintSQL(Constraint $constraint, $table)
965
    {
966
        throw new Exception('Sqlite platform does not support alter constraint.');
967
    }
968
 
969
    /**
970
     * {@inheritDoc}
971
     *
972
     * @param int|null $createFlags
973
     * @psalm-param int-mask-of<AbstractPlatform::CREATE_*>|null $createFlags
974
     */
975
    public function getCreateTableSQL(Table $table, $createFlags = null)
976
    {
977
        $createFlags = $createFlags ?? self::CREATE_INDEXES | self::CREATE_FOREIGNKEYS;
978
 
979
        return parent::getCreateTableSQL($table, $createFlags);
980
    }
981
 
982
    /**
983
     * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon.
984
     *
985
     * @param string      $table
986
     * @param string|null $database
987
     *
988
     * @return string
989
     */
990
    public function getListTableForeignKeysSQL($table, $database = null)
991
    {
992
        $table = $this->emulateSchemaNamespacing($table);
993
 
994
        return sprintf('PRAGMA foreign_key_list(%s)', $this->quoteStringLiteral($table));
995
    }
996
 
997
    /**
998
     * {@inheritDoc}
999
     */
1000
    public function getAlterTableSQL(TableDiff $diff)
1001
    {
1002
        $sql = $this->getSimpleAlterTableSQL($diff);
1003
        if ($sql !== false) {
1004
            return $sql;
1005
        }
1006
 
1007
        $table = $diff->getOldTable();
1008
 
1009
        if (! $table instanceof Table) {
1010
            throw new Exception(
1011
                'Sqlite platform requires for alter table the table diff with reference to original table schema',
1012
            );
1013
        }
1014
 
1015
        $columns        = [];
1016
        $oldColumnNames = [];
1017
        $newColumnNames = [];
1018
        $columnSql      = [];
1019
 
1020
        foreach ($table->getColumns() as $columnName => $column) {
1021
            $columnName                  = strtolower($columnName);
1022
            $columns[$columnName]        = $column;
1023
            $oldColumnNames[$columnName] = $newColumnNames[$columnName] = $column->getQuotedName($this);
1024
        }
1025
 
1026
        foreach ($diff->getDroppedColumns() as $column) {
1027
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
1028
                continue;
1029
            }
1030
 
1031
            $columnName = strtolower($column->getName());
1032
            if (! isset($columns[$columnName])) {
1033
                continue;
1034
            }
1035
 
1036
            unset(
1037
                $columns[$columnName],
1038
                $oldColumnNames[$columnName],
1039
                $newColumnNames[$columnName],
1040
            );
1041
        }
1042
 
1043
        foreach ($diff->getRenamedColumns() as $oldColumnName => $column) {
1044
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
1045
                continue;
1046
            }
1047
 
1048
            $oldColumnName = strtolower($oldColumnName);
1049
 
1050
            $columns = $this->replaceColumn(
1051
                $table->getName(),
1052
                $columns,
1053
                $oldColumnName,
1054
                $column,
1055
            );
1056
 
1057
            if (! isset($newColumnNames[$oldColumnName])) {
1058
                continue;
1059
            }
1060
 
1061
            $newColumnNames[$oldColumnName] = $column->getQuotedName($this);
1062
        }
1063
 
1064
        foreach ($diff->getModifiedColumns() as $columnDiff) {
1065
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
1066
                continue;
1067
            }
1068
 
1069
            $oldColumn = $columnDiff->getOldColumn() ?? $columnDiff->getOldColumnName();
1070
 
1071
            $oldColumnName = strtolower($oldColumn->getName());
1072
 
1073
            $columns = $this->replaceColumn(
1074
                $table->getName(),
1075
                $columns,
1076
                $oldColumnName,
1077
                $columnDiff->getNewColumn(),
1078
            );
1079
 
1080
            if (! isset($newColumnNames[$oldColumnName])) {
1081
                continue;
1082
            }
1083
 
1084
            $newColumnNames[$oldColumnName] = $columnDiff->getNewColumn()->getQuotedName($this);
1085
        }
1086
 
1087
        foreach ($diff->getAddedColumns() as $column) {
1088
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
1089
                continue;
1090
            }
1091
 
1092
            $columns[strtolower($column->getName())] = $column;
1093
        }
1094
 
1095
        $sql      = [];
1096
        $tableSql = [];
1097
        if (! $this->onSchemaAlterTable($diff, $tableSql)) {
1098
            $dataTable = new Table('__temp__' . $table->getName());
1099
 
1100
            $newTable = new Table(
1101
                $table->getQuotedName($this),
1102
                $columns,
1103
                $this->getPrimaryIndexInAlteredTable($diff, $table),
1104
                [],
1105
                $this->getForeignKeysInAlteredTable($diff, $table),
1106
                $table->getOptions(),
1107
            );
1108
            $newTable->addOption('alter', true);
1109
 
1110
            $sql = $this->getPreAlterTableIndexForeignKeySQL($diff);
1111
 
1112
            $sql[] = sprintf(
1113
                'CREATE TEMPORARY TABLE %s AS SELECT %s FROM %s',
1114
                $dataTable->getQuotedName($this),
1115
                implode(', ', $oldColumnNames),
1116
                $table->getQuotedName($this),
1117
            );
1118
            $sql[] = $this->getDropTableSQL($table);
1119
 
1120
            $sql   = array_merge($sql, $this->getCreateTableSQL($newTable));
1121
            $sql[] = sprintf(
1122
                'INSERT INTO %s (%s) SELECT %s FROM %s',
1123
                $newTable->getQuotedName($this),
1124
                implode(', ', $newColumnNames),
1125
                implode(', ', $oldColumnNames),
1126
                $dataTable->getQuotedName($this),
1127
            );
1128
            $sql[] = $this->getDropTableSQL($dataTable->getQuotedName($this));
1129
 
1130
            $newName = $diff->getNewName();
1131
 
1132
            if ($newName !== false) {
1133
                Deprecation::trigger(
1134
                    'doctrine/dbal',
1135
                    'https://github.com/doctrine/dbal/pull/5663',
1136
                    'Generation of "rename table" SQL using %s is deprecated. Use getRenameTableSQL() instead.',
1137
                    __METHOD__,
1138
                );
1139
 
1140
                $sql[] = sprintf(
1141
                    'ALTER TABLE %s RENAME TO %s',
1142
                    $newTable->getQuotedName($this),
1143
                    $newName->getQuotedName($this),
1144
                );
1145
            }
1146
 
1147
            $sql = array_merge($sql, $this->getPostAlterTableIndexForeignKeySQL($diff));
1148
        }
1149
 
1150
        return array_merge($sql, $tableSql, $columnSql);
1151
    }
1152
 
1153
    /**
1154
     * Replace the column with the given name with the new column.
1155
     *
1156
     * @param string               $tableName
1157
     * @param array<string,Column> $columns
1158
     * @param string               $columnName
1159
     *
1160
     * @return array<string,Column>
1161
     *
1162
     * @throws Exception
1163
     */
1164
    private function replaceColumn($tableName, array $columns, $columnName, Column $column): array
1165
    {
1166
        $keys  = array_keys($columns);
1167
        $index = array_search($columnName, $keys, true);
1168
 
1169
        if ($index === false) {
1170
            throw SchemaException::columnDoesNotExist($columnName, $tableName);
1171
        }
1172
 
1173
        $values = array_values($columns);
1174
 
1175
        $keys[$index]   = strtolower($column->getName());
1176
        $values[$index] = $column;
1177
 
1178
        return array_combine($keys, $values);
1179
    }
1180
 
1181
    /**
1182
     * @return string[]|false
1183
     *
1184
     * @throws Exception
1185
     */
1186
    private function getSimpleAlterTableSQL(TableDiff $diff)
1187
    {
1188
        // Suppress changes on integer type autoincrement columns.
1189
        foreach ($diff->getModifiedColumns() as $columnDiff) {
1190
            $oldColumn = $columnDiff->getOldColumn();
1191
 
1192
            if ($oldColumn === null) {
1193
                continue;
1194
            }
1195
 
1196
            $newColumn = $columnDiff->getNewColumn();
1197
 
1198
            if (! $newColumn->getAutoincrement() || ! $newColumn->getType() instanceof IntegerType) {
1199
                continue;
1200
            }
1201
 
1202
            $oldColumnName = $oldColumn->getName();
1203
 
1204
            if (! $columnDiff->hasTypeChanged() && $columnDiff->hasUnsignedChanged()) {
1205
                unset($diff->changedColumns[$oldColumnName]);
1206
 
1207
                continue;
1208
            }
1209
 
1210
            $fromColumnType = $oldColumn->getType();
1211
 
1212
            if (! ($fromColumnType instanceof Types\SmallIntType) && ! ($fromColumnType instanceof Types\BigIntType)) {
1213
                continue;
1214
            }
1215
 
1216
            unset($diff->changedColumns[$oldColumnName]);
1217
        }
1218
 
1219
        if (
1220
            count($diff->getModifiedColumns()) > 0
1221
            || count($diff->getDroppedColumns()) > 0
1222
            || count($diff->getRenamedColumns()) > 0
1223
            || count($diff->getAddedIndexes()) > 0
1224
            || count($diff->getModifiedIndexes()) > 0
1225
            || count($diff->getDroppedIndexes()) > 0
1226
            || count($diff->getRenamedIndexes()) > 0
1227
            || count($diff->getAddedForeignKeys()) > 0
1228
            || count($diff->getModifiedForeignKeys()) > 0
1229
            || count($diff->getDroppedForeignKeys()) > 0
1230
        ) {
1231
            return false;
1232
        }
1233
 
1234
        $table = $diff->getOldTable() ?? $diff->getName($this);
1235
 
1236
        $sql       = [];
1237
        $tableSql  = [];
1238
        $columnSql = [];
1239
 
1240
        foreach ($diff->getAddedColumns() as $column) {
1241
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
1242
                continue;
1243
            }
1244
 
1245
            $definition = array_merge([
1246
                'unique' => null,
1247
                'autoincrement' => null,
1248
                'default' => null,
1249
            ], $column->toArray());
1250
 
1251
            $type = $definition['type'];
1252
 
1253
            switch (true) {
1254
                case isset($definition['columnDefinition']) || $definition['autoincrement'] || $definition['unique']:
1255
                case $type instanceof Types\DateTimeType && $definition['default'] === $this->getCurrentTimestampSQL():
1256
                case $type instanceof Types\DateType && $definition['default'] === $this->getCurrentDateSQL():
1257
                case $type instanceof Types\TimeType && $definition['default'] === $this->getCurrentTimeSQL():
1258
                    return false;
1259
            }
1260
 
1261
            $definition['name'] = $column->getQuotedName($this);
1262
            if ($type instanceof Types\StringType) {
1263
                $definition['length'] ??= 255;
1264
            }
1265
 
1266
            $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ADD COLUMN '
1267
                . $this->getColumnDeclarationSQL($definition['name'], $definition);
1268
        }
1269
 
1270
        if (! $this->onSchemaAlterTable($diff, $tableSql)) {
1271
            if ($diff->newName !== false) {
1272
                Deprecation::trigger(
1273
                    'doctrine/dbal',
1274
                    'https://github.com/doctrine/dbal/pull/5663',
1275
                    'Generation of SQL that renames a table using %s is deprecated.'
1276
                        . ' Use getRenameTableSQL() instead.',
1277
                    __METHOD__,
1278
                );
1279
 
1280
                $newTable = new Identifier($diff->newName);
1281
 
1282
                $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' RENAME TO '
1283
                    . $newTable->getQuotedName($this);
1284
            }
1285
        }
1286
 
1287
        return array_merge($sql, $tableSql, $columnSql);
1288
    }
1289
 
1290
    /** @return string[] */
1291
    private function getColumnNamesInAlteredTable(TableDiff $diff, Table $fromTable): array
1292
    {
1293
        $columns = [];
1294
 
1295
        foreach ($fromTable->getColumns() as $columnName => $column) {
1296
            $columns[strtolower($columnName)] = $column->getName();
1297
        }
1298
 
1299
        foreach ($diff->getDroppedColumns() as $column) {
1300
            $columnName = strtolower($column->getName());
1301
            if (! isset($columns[$columnName])) {
1302
                continue;
1303
            }
1304
 
1305
            unset($columns[$columnName]);
1306
        }
1307
 
1308
        foreach ($diff->getRenamedColumns() as $oldColumnName => $column) {
1309
            $columnName                          = $column->getName();
1310
            $columns[strtolower($oldColumnName)] = $columnName;
1311
            $columns[strtolower($columnName)]    = $columnName;
1312
        }
1313
 
1314
        foreach ($diff->getModifiedColumns() as $columnDiff) {
1315
            $oldColumn = $columnDiff->getOldColumn() ?? $columnDiff->getOldColumnName();
1316
 
1317
            $oldColumnName                       = $oldColumn->getName();
1318
            $newColumnName                       = $columnDiff->getNewColumn()->getName();
1319
            $columns[strtolower($oldColumnName)] = $newColumnName;
1320
            $columns[strtolower($newColumnName)] = $newColumnName;
1321
        }
1322
 
1323
        foreach ($diff->getAddedColumns() as $column) {
1324
            $columnName                       = $column->getName();
1325
            $columns[strtolower($columnName)] = $columnName;
1326
        }
1327
 
1328
        return $columns;
1329
    }
1330
 
1331
    /** @return Index[] */
1332
    private function getIndexesInAlteredTable(TableDiff $diff, Table $fromTable): array
1333
    {
1334
        $indexes     = $fromTable->getIndexes();
1335
        $columnNames = $this->getColumnNamesInAlteredTable($diff, $fromTable);
1336
 
1337
        foreach ($indexes as $key => $index) {
1338
            foreach ($diff->getRenamedIndexes() as $oldIndexName => $renamedIndex) {
1339
                if (strtolower($key) !== strtolower($oldIndexName)) {
1340
                    continue;
1341
                }
1342
 
1343
                unset($indexes[$key]);
1344
            }
1345
 
1346
            $changed      = false;
1347
            $indexColumns = [];
1348
            foreach ($index->getColumns() as $columnName) {
1349
                $normalizedColumnName = strtolower($columnName);
1350
                if (! isset($columnNames[$normalizedColumnName])) {
1351
                    unset($indexes[$key]);
1352
                    continue 2;
1353
                }
1354
 
1355
                $indexColumns[] = $columnNames[$normalizedColumnName];
1356
                if ($columnName === $columnNames[$normalizedColumnName]) {
1357
                    continue;
1358
                }
1359
 
1360
                $changed = true;
1361
            }
1362
 
1363
            if (! $changed) {
1364
                continue;
1365
            }
1366
 
1367
            $indexes[$key] = new Index(
1368
                $index->getName(),
1369
                $indexColumns,
1370
                $index->isUnique(),
1371
                $index->isPrimary(),
1372
                $index->getFlags(),
1373
            );
1374
        }
1375
 
1376
        foreach ($diff->getDroppedIndexes() as $index) {
1377
            $indexName = strtolower($index->getName());
1378
            if (strlen($indexName) === 0 || ! isset($indexes[$indexName])) {
1379
                continue;
1380
            }
1381
 
1382
            unset($indexes[$indexName]);
1383
        }
1384
 
1385
        foreach (
1386
            array_merge(
1387
                $diff->getModifiedIndexes(),
1388
                $diff->getAddedIndexes(),
1389
                $diff->getRenamedIndexes(),
1390
            ) as $index
1391
        ) {
1392
            $indexName = strtolower($index->getName());
1393
            if (strlen($indexName) > 0) {
1394
                $indexes[$indexName] = $index;
1395
            } else {
1396
                $indexes[] = $index;
1397
            }
1398
        }
1399
 
1400
        return $indexes;
1401
    }
1402
 
1403
    /** @return ForeignKeyConstraint[] */
1404
    private function getForeignKeysInAlteredTable(TableDiff $diff, Table $fromTable): array
1405
    {
1406
        $foreignKeys = $fromTable->getForeignKeys();
1407
        $columnNames = $this->getColumnNamesInAlteredTable($diff, $fromTable);
1408
 
1409
        foreach ($foreignKeys as $key => $constraint) {
1410
            $changed      = false;
1411
            $localColumns = [];
1412
            foreach ($constraint->getLocalColumns() as $columnName) {
1413
                $normalizedColumnName = strtolower($columnName);
1414
                if (! isset($columnNames[$normalizedColumnName])) {
1415
                    unset($foreignKeys[$key]);
1416
                    continue 2;
1417
                }
1418
 
1419
                $localColumns[] = $columnNames[$normalizedColumnName];
1420
                if ($columnName === $columnNames[$normalizedColumnName]) {
1421
                    continue;
1422
                }
1423
 
1424
                $changed = true;
1425
            }
1426
 
1427
            if (! $changed) {
1428
                continue;
1429
            }
1430
 
1431
            $foreignKeys[$key] = new ForeignKeyConstraint(
1432
                $localColumns,
1433
                $constraint->getForeignTableName(),
1434
                $constraint->getForeignColumns(),
1435
                $constraint->getName(),
1436
                $constraint->getOptions(),
1437
            );
1438
        }
1439
 
1440
        foreach ($diff->getDroppedForeignKeys() as $constraint) {
1441
            if (! $constraint instanceof ForeignKeyConstraint) {
1442
                $constraint = new Identifier($constraint);
1443
            }
1444
 
1445
            $constraintName = strtolower($constraint->getName());
1446
            if (strlen($constraintName) === 0 || ! isset($foreignKeys[$constraintName])) {
1447
                continue;
1448
            }
1449
 
1450
            unset($foreignKeys[$constraintName]);
1451
        }
1452
 
1453
        foreach (array_merge($diff->getModifiedForeignKeys(), $diff->getAddedForeignKeys()) as $constraint) {
1454
            $constraintName = strtolower($constraint->getName());
1455
            if (strlen($constraintName) > 0) {
1456
                $foreignKeys[$constraintName] = $constraint;
1457
            } else {
1458
                $foreignKeys[] = $constraint;
1459
            }
1460
        }
1461
 
1462
        return $foreignKeys;
1463
    }
1464
 
1465
    /** @return Index[] */
1466
    private function getPrimaryIndexInAlteredTable(TableDiff $diff, Table $fromTable): array
1467
    {
1468
        $primaryIndex = [];
1469
 
1470
        foreach ($this->getIndexesInAlteredTable($diff, $fromTable) as $index) {
1471
            if (! $index->isPrimary()) {
1472
                continue;
1473
            }
1474
 
1475
            $primaryIndex = [$index->getName() => $index];
1476
        }
1477
 
1478
        return $primaryIndex;
1479
    }
1480
 
1481
    public function createSchemaManager(Connection $connection): SqliteSchemaManager
1482
    {
1483
        return new SqliteSchemaManager($connection, $this);
1484
    }
1485
}