summaryrefslogtreecommitdiff
path: root/vendor/doctrine/dbal/src/Platforms/SQLitePlatform.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/doctrine/dbal/src/Platforms/SQLitePlatform.php')
-rw-r--r--vendor/doctrine/dbal/src/Platforms/SQLitePlatform.php994
1 files changed, 994 insertions, 0 deletions
diff --git a/vendor/doctrine/dbal/src/Platforms/SQLitePlatform.php b/vendor/doctrine/dbal/src/Platforms/SQLitePlatform.php
new file mode 100644
index 0000000..acec163
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Platforms/SQLitePlatform.php
@@ -0,0 +1,994 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Platforms;
6
7use Doctrine\DBAL\Connection;
8use Doctrine\DBAL\Exception;
9use Doctrine\DBAL\Platforms\Exception\NotSupported;
10use Doctrine\DBAL\Platforms\Keywords\KeywordList;
11use Doctrine\DBAL\Platforms\Keywords\SQLiteKeywords;
12use Doctrine\DBAL\Schema\Column;
13use Doctrine\DBAL\Schema\Exception\ColumnDoesNotExist;
14use Doctrine\DBAL\Schema\ForeignKeyConstraint;
15use Doctrine\DBAL\Schema\Identifier;
16use Doctrine\DBAL\Schema\Index;
17use Doctrine\DBAL\Schema\SQLiteSchemaManager;
18use Doctrine\DBAL\Schema\Table;
19use Doctrine\DBAL\Schema\TableDiff;
20use Doctrine\DBAL\SQL\Builder\DefaultSelectSQLBuilder;
21use Doctrine\DBAL\SQL\Builder\SelectSQLBuilder;
22use Doctrine\DBAL\TransactionIsolationLevel;
23use Doctrine\DBAL\Types;
24use InvalidArgumentException;
25
26use function array_combine;
27use function array_keys;
28use function array_merge;
29use function array_search;
30use function array_unique;
31use function array_values;
32use function count;
33use function explode;
34use function implode;
35use function sprintf;
36use function str_replace;
37use function strpos;
38use function strtolower;
39use function substr;
40use function trim;
41
42/**
43 * The SQLitePlatform class describes the specifics and dialects of the SQLite
44 * database platform.
45 */
46class SQLitePlatform extends AbstractPlatform
47{
48 public function getCreateDatabaseSQL(string $name): string
49 {
50 throw NotSupported::new(__METHOD__);
51 }
52
53 public function getDropDatabaseSQL(string $name): string
54 {
55 throw NotSupported::new(__METHOD__);
56 }
57
58 public function getRegexpExpression(): string
59 {
60 return 'REGEXP';
61 }
62
63 public function getModExpression(string $dividend, string $divisor): string
64 {
65 return $dividend . ' % ' . $divisor;
66 }
67
68 public function getTrimExpression(
69 string $str,
70 TrimMode $mode = TrimMode::UNSPECIFIED,
71 ?string $char = null,
72 ): string {
73 $trimFn = match ($mode) {
74 TrimMode::UNSPECIFIED,
75 TrimMode::BOTH => 'TRIM',
76 TrimMode::LEADING => 'LTRIM',
77 TrimMode::TRAILING => 'RTRIM',
78 };
79
80 $arguments = [$str];
81
82 if ($char !== null) {
83 $arguments[] = $char;
84 }
85
86 return sprintf('%s(%s)', $trimFn, implode(', ', $arguments));
87 }
88
89 public function getSubstringExpression(string $string, string $start, ?string $length = null): string
90 {
91 if ($length === null) {
92 return sprintf('SUBSTR(%s, %s)', $string, $start);
93 }
94
95 return sprintf('SUBSTR(%s, %s, %s)', $string, $start, $length);
96 }
97
98 public function getLocateExpression(string $string, string $substring, ?string $start = null): string
99 {
100 if ($start === null || $start === '1') {
101 return sprintf('INSTR(%s, %s)', $string, $substring);
102 }
103
104 return sprintf(
105 'CASE WHEN INSTR(SUBSTR(%1$s, %3$s), %2$s) > 0 THEN INSTR(SUBSTR(%1$s, %3$s), %2$s) + %3$s - 1 ELSE 0 END',
106 $string,
107 $substring,
108 $start,
109 );
110 }
111
112 protected function getDateArithmeticIntervalExpression(
113 string $date,
114 string $operator,
115 string $interval,
116 DateIntervalUnit $unit,
117 ): string {
118 switch ($unit) {
119 case DateIntervalUnit::WEEK:
120 $interval = $this->multiplyInterval($interval, 7);
121 $unit = DateIntervalUnit::DAY;
122 break;
123
124 case DateIntervalUnit::QUARTER:
125 $interval = $this->multiplyInterval($interval, 3);
126 $unit = DateIntervalUnit::MONTH;
127 break;
128 }
129
130 return 'DATETIME(' . $date . ',' . $this->getConcatExpression(
131 $this->quoteStringLiteral($operator),
132 $interval,
133 $this->quoteStringLiteral(' ' . $unit->value),
134 ) . ')';
135 }
136
137 public function getDateDiffExpression(string $date1, string $date2): string
138 {
139 return sprintf("JULIANDAY(%s, 'start of day') - JULIANDAY(%s, 'start of day')", $date1, $date2);
140 }
141
142 /**
143 * {@inheritDoc}
144 *
145 * The DBAL doesn't support databases on the SQLite platform. The expression here always returns a fixed string
146 * as an indicator of an implicitly selected database.
147 *
148 * @link https://www.sqlite.org/lang_select.html
149 * @see Connection::getDatabase()
150 */
151 public function getCurrentDatabaseExpression(): string
152 {
153 return "'main'";
154 }
155
156 /** @link https://www2.sqlite.org/cvstrac/wiki?p=UnsupportedSql */
157 public function createSelectSQLBuilder(): SelectSQLBuilder
158 {
159 return new DefaultSelectSQLBuilder($this, null, null);
160 }
161
162 protected function _getTransactionIsolationLevelSQL(TransactionIsolationLevel $level): string
163 {
164 return match ($level) {
165 TransactionIsolationLevel::READ_UNCOMMITTED => '0',
166 TransactionIsolationLevel::READ_COMMITTED,
167 TransactionIsolationLevel::REPEATABLE_READ,
168 TransactionIsolationLevel::SERIALIZABLE => '1',
169 };
170 }
171
172 public function getSetTransactionIsolationSQL(TransactionIsolationLevel $level): string
173 {
174 return 'PRAGMA read_uncommitted = ' . $this->_getTransactionIsolationLevelSQL($level);
175 }
176
177 /**
178 * {@inheritDoc}
179 */
180 public function getBooleanTypeDeclarationSQL(array $column): string
181 {
182 return 'BOOLEAN';
183 }
184
185 /**
186 * {@inheritDoc}
187 */
188 public function getIntegerTypeDeclarationSQL(array $column): string
189 {
190 return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($column);
191 }
192
193 /**
194 * {@inheritDoc}
195 */
196 public function getBigIntTypeDeclarationSQL(array $column): string
197 {
198 // SQLite autoincrement is implicit for INTEGER PKs, but not for BIGINT fields.
199 if (! empty($column['autoincrement'])) {
200 return $this->getIntegerTypeDeclarationSQL($column);
201 }
202
203 return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column);
204 }
205
206 /**
207 * {@inheritDoc}
208 */
209 public function getSmallIntTypeDeclarationSQL(array $column): string
210 {
211 // SQLite autoincrement is implicit for INTEGER PKs, but not for SMALLINT fields.
212 if (! empty($column['autoincrement'])) {
213 return $this->getIntegerTypeDeclarationSQL($column);
214 }
215
216 return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column);
217 }
218
219 /**
220 * {@inheritDoc}
221 */
222 public function getDateTimeTypeDeclarationSQL(array $column): string
223 {
224 return 'DATETIME';
225 }
226
227 /**
228 * {@inheritDoc}
229 */
230 public function getDateTypeDeclarationSQL(array $column): string
231 {
232 return 'DATE';
233 }
234
235 /**
236 * {@inheritDoc}
237 */
238 public function getTimeTypeDeclarationSQL(array $column): string
239 {
240 return 'TIME';
241 }
242
243 /**
244 * {@inheritDoc}
245 */
246 protected function _getCommonIntegerTypeDeclarationSQL(array $column): string
247 {
248 // sqlite autoincrement is only possible for the primary key
249 if (! empty($column['autoincrement'])) {
250 return ' PRIMARY KEY AUTOINCREMENT';
251 }
252
253 return ! empty($column['unsigned']) ? ' UNSIGNED' : '';
254 }
255
256 /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */
257 public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey): string
258 {
259 return parent::getForeignKeyDeclarationSQL(new ForeignKeyConstraint(
260 $foreignKey->getQuotedLocalColumns($this),
261 $foreignKey->getQuotedForeignTableName($this),
262 $foreignKey->getQuotedForeignColumns($this),
263 $foreignKey->getName(),
264 $foreignKey->getOptions(),
265 ));
266 }
267
268 /**
269 * {@inheritDoc}
270 */
271 protected function _getCreateTableSQL(string $name, array $columns, array $options = []): array
272 {
273 $queryFields = $this->getColumnDeclarationListSQL($columns);
274
275 if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
276 foreach ($options['uniqueConstraints'] as $definition) {
277 $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($definition);
278 }
279 }
280
281 $queryFields .= $this->getNonAutoincrementPrimaryKeyDefinition($columns, $options);
282
283 if (isset($options['foreignKeys'])) {
284 foreach ($options['foreignKeys'] as $foreignKey) {
285 $queryFields .= ', ' . $this->getForeignKeyDeclarationSQL($foreignKey);
286 }
287 }
288
289 $tableComment = '';
290 if (isset($options['comment'])) {
291 $comment = trim($options['comment'], " '");
292
293 $tableComment = $this->getInlineTableCommentSQL($comment);
294 }
295
296 $query = ['CREATE TABLE ' . $name . ' ' . $tableComment . '(' . $queryFields . ')'];
297
298 if (isset($options['alter']) && $options['alter'] === true) {
299 return $query;
300 }
301
302 if (isset($options['indexes']) && ! empty($options['indexes'])) {
303 foreach ($options['indexes'] as $indexDef) {
304 $query[] = $this->getCreateIndexSQL($indexDef, $name);
305 }
306 }
307
308 if (isset($options['unique']) && ! empty($options['unique'])) {
309 foreach ($options['unique'] as $indexDef) {
310 $query[] = $this->getCreateIndexSQL($indexDef, $name);
311 }
312 }
313
314 return $query;
315 }
316
317 /**
318 * Generate a PRIMARY KEY definition if no autoincrement value is used
319 *
320 * @param mixed[][] $columns
321 * @param mixed[] $options
322 */
323 private function getNonAutoincrementPrimaryKeyDefinition(array $columns, array $options): string
324 {
325 if (empty($options['primary'])) {
326 return '';
327 }
328
329 $keyColumns = array_unique(array_values($options['primary']));
330
331 foreach ($keyColumns as $keyColumn) {
332 foreach ($columns as $column) {
333 if ($column['name'] === $keyColumn && ! empty($column['autoincrement'])) {
334 return '';
335 }
336 }
337 }
338
339 return ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
340 }
341
342 protected function getBinaryTypeDeclarationSQLSnippet(?int $length): string
343 {
344 return 'BLOB';
345 }
346
347 protected function getVarcharTypeDeclarationSQLSnippet(?int $length): string
348 {
349 $sql = 'VARCHAR';
350
351 if ($length !== null) {
352 $sql .= sprintf('(%d)', $length);
353 }
354
355 return $sql;
356 }
357
358 protected function getVarbinaryTypeDeclarationSQLSnippet(?int $length): string
359 {
360 return 'BLOB';
361 }
362
363 /**
364 * {@inheritDoc}
365 */
366 public function getClobTypeDeclarationSQL(array $column): string
367 {
368 return 'CLOB';
369 }
370
371 /** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */
372 public function getListViewsSQL(string $database): string
373 {
374 return "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL";
375 }
376
377 /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */
378 public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey): string
379 {
380 $query = parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
381
382 if (! $foreignKey->hasOption('deferrable') || $foreignKey->getOption('deferrable') === false) {
383 $query .= ' NOT';
384 }
385
386 $query .= ' DEFERRABLE';
387 $query .= ' INITIALLY';
388
389 if ($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) {
390 $query .= ' DEFERRED';
391 } else {
392 $query .= ' IMMEDIATE';
393 }
394
395 return $query;
396 }
397
398 public function supportsIdentityColumns(): bool
399 {
400 return true;
401 }
402
403 /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */
404 public function supportsColumnCollation(): bool
405 {
406 return true;
407 }
408
409 /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */
410 public function supportsInlineColumnComments(): bool
411 {
412 return true;
413 }
414
415 public function getTruncateTableSQL(string $tableName, bool $cascade = false): string
416 {
417 $tableIdentifier = new Identifier($tableName);
418
419 return 'DELETE FROM ' . $tableIdentifier->getQuotedName($this);
420 }
421
422 /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */
423 public function getInlineColumnCommentSQL(string $comment): string
424 {
425 if ($comment === '') {
426 return '';
427 }
428
429 return '--' . str_replace("\n", "\n--", $comment) . "\n";
430 }
431
432 private function getInlineTableCommentSQL(string $comment): string
433 {
434 return $this->getInlineColumnCommentSQL($comment);
435 }
436
437 protected function initializeDoctrineTypeMappings(): void
438 {
439 $this->doctrineTypeMapping = [
440 'bigint' => 'bigint',
441 'bigserial' => 'bigint',
442 'blob' => 'blob',
443 'boolean' => 'boolean',
444 'char' => 'string',
445 'clob' => 'text',
446 'date' => 'date',
447 'datetime' => 'datetime',
448 'decimal' => 'decimal',
449 'double' => 'float',
450 'double precision' => 'float',
451 'float' => 'float',
452 'image' => 'string',
453 'int' => 'integer',
454 'integer' => 'integer',
455 'longtext' => 'text',
456 'longvarchar' => 'string',
457 'mediumint' => 'integer',
458 'mediumtext' => 'text',
459 'ntext' => 'string',
460 'numeric' => 'decimal',
461 'nvarchar' => 'string',
462 'real' => 'float',
463 'serial' => 'integer',
464 'smallint' => 'smallint',
465 'string' => 'string',
466 'text' => 'text',
467 'time' => 'time',
468 'timestamp' => 'datetime',
469 'tinyint' => 'boolean',
470 'tinytext' => 'text',
471 'varchar' => 'string',
472 'varchar2' => 'string',
473 ];
474 }
475
476 protected function createReservedKeywordsList(): KeywordList
477 {
478 return new SQLiteKeywords();
479 }
480
481 /**
482 * {@inheritDoc}
483 */
484 protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff): array
485 {
486 return [];
487 }
488
489 /**
490 * {@inheritDoc}
491 */
492 protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff): array
493 {
494 $table = $diff->getOldTable();
495
496 $sql = [];
497
498 foreach ($this->getIndexesInAlteredTable($diff, $table) as $index) {
499 if ($index->isPrimary()) {
500 continue;
501 }
502
503 $sql[] = $this->getCreateIndexSQL($index, $table->getQuotedName($this));
504 }
505
506 return $sql;
507 }
508
509 protected function doModifyLimitQuery(string $query, ?int $limit, int $offset): string
510 {
511 if ($limit === null && $offset > 0) {
512 $limit = -1;
513 }
514
515 return parent::doModifyLimitQuery($query, $limit, $offset);
516 }
517
518 /**
519 * {@inheritDoc}
520 */
521 public function getBlobTypeDeclarationSQL(array $column): string
522 {
523 return 'BLOB';
524 }
525
526 public function getTemporaryTableName(string $tableName): string
527 {
528 return $tableName;
529 }
530
531 /**
532 * {@inheritDoc}
533 */
534 public function getCreateTablesSQL(array $tables): array
535 {
536 $sql = [];
537
538 foreach ($tables as $table) {
539 $sql = array_merge($sql, $this->getCreateTableSQL($table));
540 }
541
542 return $sql;
543 }
544
545 /** {@inheritDoc} */
546 public function getCreateIndexSQL(Index $index, string $table): string
547 {
548 $name = $index->getQuotedName($this);
549 $columns = $index->getColumns();
550
551 if (count($columns) === 0) {
552 throw new InvalidArgumentException(sprintf(
553 'Incomplete or invalid index definition %s on table %s',
554 $name,
555 $table,
556 ));
557 }
558
559 if ($index->isPrimary()) {
560 return $this->getCreatePrimaryKeySQL($index, $table);
561 }
562
563 if (strpos($table, '.') !== false) {
564 [$schema, $table] = explode('.', $table);
565 $name = $schema . '.' . $name;
566 }
567
568 $query = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table;
569 $query .= ' (' . implode(', ', $index->getQuotedColumns($this)) . ')' . $this->getPartialIndexSQL($index);
570
571 return $query;
572 }
573
574 /**
575 * {@inheritDoc}
576 */
577 public function getDropTablesSQL(array $tables): array
578 {
579 $sql = [];
580
581 foreach ($tables as $table) {
582 $sql[] = $this->getDropTableSQL($table->getQuotedName($this));
583 }
584
585 return $sql;
586 }
587
588 public function getCreatePrimaryKeySQL(Index $index, string $table): string
589 {
590 throw NotSupported::new(__METHOD__);
591 }
592
593 public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, string $table): string
594 {
595 throw NotSupported::new(__METHOD__);
596 }
597
598 public function getDropForeignKeySQL(string $foreignKey, string $table): string
599 {
600 throw NotSupported::new(__METHOD__);
601 }
602
603 /**
604 * {@inheritDoc}
605 */
606 public function getAlterTableSQL(TableDiff $diff): array
607 {
608 $sql = $this->getSimpleAlterTableSQL($diff);
609 if ($sql !== false) {
610 return $sql;
611 }
612
613 $table = $diff->getOldTable();
614
615 $columns = [];
616 $oldColumnNames = [];
617 $newColumnNames = [];
618 $columnSql = [];
619
620 foreach ($table->getColumns() as $column) {
621 $columnName = strtolower($column->getName());
622 $columns[$columnName] = $column;
623 $oldColumnNames[$columnName] = $newColumnNames[$columnName] = $column->getQuotedName($this);
624 }
625
626 foreach ($diff->getDroppedColumns() as $column) {
627 $columnName = strtolower($column->getName());
628 if (! isset($columns[$columnName])) {
629 continue;
630 }
631
632 unset(
633 $columns[$columnName],
634 $oldColumnNames[$columnName],
635 $newColumnNames[$columnName],
636 );
637 }
638
639 foreach ($diff->getRenamedColumns() as $oldColumnName => $column) {
640 $oldColumnName = strtolower($oldColumnName);
641
642 $columns = $this->replaceColumn(
643 $table->getName(),
644 $columns,
645 $oldColumnName,
646 $column,
647 );
648
649 if (! isset($newColumnNames[$oldColumnName])) {
650 continue;
651 }
652
653 $newColumnNames[$oldColumnName] = $column->getQuotedName($this);
654 }
655
656 foreach ($diff->getModifiedColumns() as $columnDiff) {
657 $oldColumnName = strtolower($columnDiff->getOldColumn()->getName());
658 $newColumn = $columnDiff->getNewColumn();
659
660 $columns = $this->replaceColumn(
661 $table->getName(),
662 $columns,
663 $oldColumnName,
664 $newColumn,
665 );
666
667 if (! isset($newColumnNames[$oldColumnName])) {
668 continue;
669 }
670
671 $newColumnNames[$oldColumnName] = $newColumn->getQuotedName($this);
672 }
673
674 foreach ($diff->getAddedColumns() as $column) {
675 $columns[strtolower($column->getName())] = $column;
676 }
677
678 $tableName = $table->getName();
679 $pos = strpos($tableName, '.');
680 if ($pos !== false) {
681 $tableName = substr($tableName, $pos + 1);
682 }
683
684 $dataTable = new Table('__temp__' . $tableName);
685
686 $newTable = new Table(
687 $table->getQuotedName($this),
688 $columns,
689 $this->getPrimaryIndexInAlteredTable($diff, $table),
690 [],
691 $this->getForeignKeysInAlteredTable($diff, $table),
692 $table->getOptions(),
693 );
694
695 $newTable->addOption('alter', true);
696
697 $sql = $this->getPreAlterTableIndexForeignKeySQL($diff);
698
699 $sql[] = sprintf(
700 'CREATE TEMPORARY TABLE %s AS SELECT %s FROM %s',
701 $dataTable->getQuotedName($this),
702 implode(', ', $oldColumnNames),
703 $table->getQuotedName($this),
704 );
705 $sql[] = $this->getDropTableSQL($table->getQuotedName($this));
706
707 $sql = array_merge($sql, $this->getCreateTableSQL($newTable));
708 $sql[] = sprintf(
709 'INSERT INTO %s (%s) SELECT %s FROM %s',
710 $newTable->getQuotedName($this),
711 implode(', ', $newColumnNames),
712 implode(', ', $oldColumnNames),
713 $dataTable->getQuotedName($this),
714 );
715 $sql[] = $this->getDropTableSQL($dataTable->getQuotedName($this));
716
717 return array_merge($sql, $this->getPostAlterTableIndexForeignKeySQL($diff), $columnSql);
718 }
719
720 /**
721 * Replace the column with the given name with the new column.
722 *
723 * @param array<string,Column> $columns
724 *
725 * @return array<string,Column>
726 *
727 * @throws Exception
728 */
729 private function replaceColumn(string $tableName, array $columns, string $columnName, Column $column): array
730 {
731 $keys = array_keys($columns);
732 $index = array_search($columnName, $keys, true);
733
734 if ($index === false) {
735 throw ColumnDoesNotExist::new($columnName, $tableName);
736 }
737
738 $values = array_values($columns);
739
740 $keys[$index] = strtolower($column->getName());
741 $values[$index] = $column;
742
743 return array_combine($keys, $values);
744 }
745
746 /**
747 * @return list<string>|false
748 *
749 * @throws Exception
750 */
751 private function getSimpleAlterTableSQL(TableDiff $diff): array|false
752 {
753 if (
754 count($diff->getModifiedColumns()) > 0
755 || count($diff->getDroppedColumns()) > 0
756 || count($diff->getRenamedColumns()) > 0
757 || count($diff->getAddedIndexes()) > 0
758 || count($diff->getModifiedIndexes()) > 0
759 || count($diff->getDroppedIndexes()) > 0
760 || count($diff->getRenamedIndexes()) > 0
761 || count($diff->getAddedForeignKeys()) > 0
762 || count($diff->getModifiedForeignKeys()) > 0
763 || count($diff->getDroppedForeignKeys()) > 0
764 ) {
765 return false;
766 }
767
768 $table = $diff->getOldTable();
769
770 $sql = [];
771 $columnSql = [];
772
773 foreach ($diff->getAddedColumns() as $column) {
774 $definition = array_merge([
775 'unique' => null,
776 'autoincrement' => null,
777 'default' => null,
778 ], $column->toArray());
779
780 $type = $definition['type'];
781
782 /** @psalm-suppress RiskyTruthyFalsyComparison */
783 switch (true) {
784 case isset($definition['columnDefinition']) || $definition['autoincrement'] || $definition['unique']:
785 case $type instanceof Types\DateTimeType && $definition['default'] === $this->getCurrentTimestampSQL():
786 case $type instanceof Types\DateType && $definition['default'] === $this->getCurrentDateSQL():
787 case $type instanceof Types\TimeType && $definition['default'] === $this->getCurrentTimeSQL():
788 return false;
789 }
790
791 $definition['name'] = $column->getQuotedName($this);
792
793 $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ADD COLUMN '
794 . $this->getColumnDeclarationSQL($definition['name'], $definition);
795 }
796
797 return array_merge($sql, $columnSql);
798 }
799
800 /** @return string[] */
801 private function getColumnNamesInAlteredTable(TableDiff $diff, Table $oldTable): array
802 {
803 $columns = [];
804
805 foreach ($oldTable->getColumns() as $column) {
806 $columnName = $column->getName();
807 $columns[strtolower($columnName)] = $columnName;
808 }
809
810 foreach ($diff->getDroppedColumns() as $column) {
811 $columnName = strtolower($column->getName());
812 if (! isset($columns[$columnName])) {
813 continue;
814 }
815
816 unset($columns[$columnName]);
817 }
818
819 foreach ($diff->getRenamedColumns() as $oldColumnName => $column) {
820 $columnName = $column->getName();
821 $columns[strtolower($oldColumnName)] = $columnName;
822 $columns[strtolower($columnName)] = $columnName;
823 }
824
825 foreach ($diff->getModifiedColumns() as $columnDiff) {
826 $oldColumnName = $columnDiff->getOldColumn()->getName();
827 $newColumnName = $columnDiff->getNewColumn()->getName();
828 $columns[strtolower($oldColumnName)] = $newColumnName;
829 $columns[strtolower($newColumnName)] = $newColumnName;
830 }
831
832 foreach ($diff->getAddedColumns() as $column) {
833 $columnName = $column->getName();
834 $columns[strtolower($columnName)] = $columnName;
835 }
836
837 return $columns;
838 }
839
840 /** @return Index[] */
841 private function getIndexesInAlteredTable(TableDiff $diff, Table $oldTable): array
842 {
843 $indexes = $oldTable->getIndexes();
844 $columnNames = $this->getColumnNamesInAlteredTable($diff, $oldTable);
845
846 foreach ($indexes as $key => $index) {
847 foreach ($diff->getRenamedIndexes() as $oldIndexName => $renamedIndex) {
848 if (strtolower($key) !== strtolower($oldIndexName)) {
849 continue;
850 }
851
852 unset($indexes[$key]);
853 }
854
855 $changed = false;
856 $indexColumns = [];
857 foreach ($index->getColumns() as $columnName) {
858 $normalizedColumnName = strtolower($columnName);
859 if (! isset($columnNames[$normalizedColumnName])) {
860 unset($indexes[$key]);
861 continue 2;
862 }
863
864 $indexColumns[] = $columnNames[$normalizedColumnName];
865 if ($columnName === $columnNames[$normalizedColumnName]) {
866 continue;
867 }
868
869 $changed = true;
870 }
871
872 if (! $changed) {
873 continue;
874 }
875
876 $indexes[$key] = new Index(
877 $index->getName(),
878 $indexColumns,
879 $index->isUnique(),
880 $index->isPrimary(),
881 $index->getFlags(),
882 );
883 }
884
885 foreach ($diff->getDroppedIndexes() as $index) {
886 $indexName = $index->getName();
887
888 if ($indexName === '') {
889 continue;
890 }
891
892 unset($indexes[strtolower($indexName)]);
893 }
894
895 foreach (
896 array_merge(
897 $diff->getModifiedIndexes(),
898 $diff->getAddedIndexes(),
899 $diff->getRenamedIndexes(),
900 ) as $index
901 ) {
902 $indexName = $index->getName();
903
904 if ($indexName !== '') {
905 $indexes[strtolower($indexName)] = $index;
906 } else {
907 $indexes[] = $index;
908 }
909 }
910
911 return $indexes;
912 }
913
914 /** @return ForeignKeyConstraint[] */
915 private function getForeignKeysInAlteredTable(TableDiff $diff, Table $oldTable): array
916 {
917 $foreignKeys = $oldTable->getForeignKeys();
918 $columnNames = $this->getColumnNamesInAlteredTable($diff, $oldTable);
919
920 foreach ($foreignKeys as $key => $constraint) {
921 $changed = false;
922 $localColumns = [];
923 foreach ($constraint->getLocalColumns() as $columnName) {
924 $normalizedColumnName = strtolower($columnName);
925 if (! isset($columnNames[$normalizedColumnName])) {
926 unset($foreignKeys[$key]);
927 continue 2;
928 }
929
930 $localColumns[] = $columnNames[$normalizedColumnName];
931 if ($columnName === $columnNames[$normalizedColumnName]) {
932 continue;
933 }
934
935 $changed = true;
936 }
937
938 if (! $changed) {
939 continue;
940 }
941
942 $foreignKeys[$key] = new ForeignKeyConstraint(
943 $localColumns,
944 $constraint->getForeignTableName(),
945 $constraint->getForeignColumns(),
946 $constraint->getName(),
947 $constraint->getOptions(),
948 );
949 }
950
951 foreach ($diff->getDroppedForeignKeys() as $constraint) {
952 $constraintName = $constraint->getName();
953
954 if ($constraintName === '') {
955 continue;
956 }
957
958 unset($foreignKeys[strtolower($constraintName)]);
959 }
960
961 foreach (array_merge($diff->getModifiedForeignKeys(), $diff->getAddedForeignKeys()) as $constraint) {
962 $constraintName = $constraint->getName();
963
964 if ($constraintName !== '') {
965 $foreignKeys[strtolower($constraintName)] = $constraint;
966 } else {
967 $foreignKeys[] = $constraint;
968 }
969 }
970
971 return $foreignKeys;
972 }
973
974 /** @return Index[] */
975 private function getPrimaryIndexInAlteredTable(TableDiff $diff, Table $oldTable): array
976 {
977 $primaryIndex = [];
978
979 foreach ($this->getIndexesInAlteredTable($diff, $oldTable) as $index) {
980 if (! $index->isPrimary()) {
981 continue;
982 }
983
984 $primaryIndex = [$index->getName() => $index];
985 }
986
987 return $primaryIndex;
988 }
989
990 public function createSchemaManager(Connection $connection): SQLiteSchemaManager
991 {
992 return new SQLiteSchemaManager($connection, $this);
993 }
994}