<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Platforms;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Platforms\Exception\NotSupported;
use Doctrine\DBAL\Platforms\Keywords\KeywordList;
use Doctrine\DBAL\Platforms\Keywords\SQLiteKeywords;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\Exception\ColumnDoesNotExist;
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use Doctrine\DBAL\Schema\Identifier;
use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Schema\SQLiteSchemaManager;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Schema\TableDiff;
use Doctrine\DBAL\SQL\Builder\DefaultSelectSQLBuilder;
use Doctrine\DBAL\SQL\Builder\SelectSQLBuilder;
use Doctrine\DBAL\TransactionIsolationLevel;
use Doctrine\DBAL\Types;
use InvalidArgumentException;
use function array_combine;
use function array_keys;
use function array_merge;
use function array_search;
use function array_unique;
use function array_values;
use function count;
use function explode;
use function implode;
use function sprintf;
use function str_replace;
use function strpos;
use function strtolower;
use function substr;
use function trim;
/**
* The SQLitePlatform class describes the specifics and dialects of the SQLite
* database platform.
*/
class SQLitePlatform extends AbstractPlatform
{
public function getCreateDatabaseSQL(string $name): string
{
throw NotSupported::new(__METHOD__);
}
public function getDropDatabaseSQL(string $name): string
{
throw NotSupported::new(__METHOD__);
}
public function getRegexpExpression(): string
{
return 'REGEXP';
}
public function getModExpression(string $dividend, string $divisor): string
{
return $dividend . ' % ' . $divisor;
}
public function getTrimExpression(
string $str,
TrimMode $mode = TrimMode::UNSPECIFIED,
?string $char = null,
): string {
$trimFn = match ($mode) {
TrimMode::UNSPECIFIED,
TrimMode::BOTH => 'TRIM',
TrimMode::LEADING => 'LTRIM',
TrimMode::TRAILING => 'RTRIM',
};
$arguments = [$str];
if ($char !== null) {
$arguments[] = $char;
}
return sprintf('%s(%s)', $trimFn, implode(', ', $arguments));
}
public function getSubstringExpression(string $string, string $start, ?string $length = null): string
{
if ($length === null) {
return sprintf('SUBSTR(%s, %s)', $string, $start);
}
return sprintf('SUBSTR(%s, %s, %s)', $string, $start, $length);
}
public function getLocateExpression(string $string, string $substring, ?string $start = null): string
{
if ($start === null || $start === '1') {
return sprintf('INSTR(%s, %s)', $string, $substring);
}
return sprintf(
'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',
$string,
$substring,
$start,
);
}
protected function getDateArithmeticIntervalExpression(
string $date,
string $operator,
string $interval,
DateIntervalUnit $unit,
): string {
switch ($unit) {
case DateIntervalUnit::WEEK:
$interval = $this->multiplyInterval($interval, 7);
$unit = DateIntervalUnit::DAY;
break;
case DateIntervalUnit::QUARTER:
$interval = $this->multiplyInterval($interval, 3);
$unit = DateIntervalUnit::MONTH;
break;
}
return 'DATETIME(' . $date . ',' . $this->getConcatExpression(
$this->quoteStringLiteral($operator),
$interval,
$this->quoteStringLiteral(' ' . $unit->value),
) . ')';
}
public function getDateDiffExpression(string $date1, string $date2): string
{
return sprintf("JULIANDAY(%s, 'start of day') - JULIANDAY(%s, 'start of day')", $date1, $date2);
}
/**
* {@inheritDoc}
*
* The DBAL doesn't support databases on the SQLite platform. The expression here always returns a fixed string
* as an indicator of an implicitly selected database.
*
* @link https://www.sqlite.org/lang_select.html
* @see Connection::getDatabase()
*/
public function getCurrentDatabaseExpression(): string
{
return "'main'";
}
/** @link https://www2.sqlite.org/cvstrac/wiki?p=UnsupportedSql */
public function createSelectSQLBuilder(): SelectSQLBuilder
{
return new DefaultSelectSQLBuilder($this, null, null);
}
protected function _getTransactionIsolationLevelSQL(TransactionIsolationLevel $level): string
{
return match ($level) {
TransactionIsolationLevel::READ_UNCOMMITTED => '0',
TransactionIsolationLevel::READ_COMMITTED,
TransactionIsolationLevel::REPEATABLE_READ,
TransactionIsolationLevel::SERIALIZABLE => '1',
};
}
public function getSetTransactionIsolationSQL(TransactionIsolationLevel $level): string
{
return 'PRAGMA read_uncommitted = ' . $this->_getTransactionIsolationLevelSQL($level);
}
/**
* {@inheritDoc}
*/
public function getBooleanTypeDeclarationSQL(array $column): string
{
return 'BOOLEAN';
}
/**
* {@inheritDoc}
*/
public function getIntegerTypeDeclarationSQL(array $column): string
{
return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($column);
}
/**
* {@inheritDoc}
*/
public function getBigIntTypeDeclarationSQL(array $column): string
{
// SQLite autoincrement is implicit for INTEGER PKs, but not for BIGINT fields.
if (! empty($column['autoincrement'])) {
return $this->getIntegerTypeDeclarationSQL($column);
}
return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column);
}
/**
* {@inheritDoc}
*/
public function getSmallIntTypeDeclarationSQL(array $column): string
{
// SQLite autoincrement is implicit for INTEGER PKs, but not for SMALLINT fields.
if (! empty($column['autoincrement'])) {
return $this->getIntegerTypeDeclarationSQL($column);
}
return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column);
}
/**
* {@inheritDoc}
*/
public function getDateTimeTypeDeclarationSQL(array $column): string
{
return 'DATETIME';
}
/**
* {@inheritDoc}
*/
public function getDateTypeDeclarationSQL(array $column): string
{
return 'DATE';
}
/**
* {@inheritDoc}
*/
public function getTimeTypeDeclarationSQL(array $column): string
{
return 'TIME';
}
/**
* {@inheritDoc}
*/
protected function _getCommonIntegerTypeDeclarationSQL(array $column): string
{
// sqlite autoincrement is only possible for the primary key
if (! empty($column['autoincrement'])) {
return ' PRIMARY KEY AUTOINCREMENT';
}
return ! empty($column['unsigned']) ? ' UNSIGNED' : '';
}
/** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */
public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey): string
{
return parent::getForeignKeyDeclarationSQL(new ForeignKeyConstraint(
$foreignKey->getQuotedLocalColumns($this),
$foreignKey->getQuotedForeignTableName($this),
$foreignKey->getQuotedForeignColumns($this),
$foreignKey->getName(),
$foreignKey->getOptions(),
));
}
/**
* {@inheritDoc}
*/
protected function _getCreateTableSQL(string $name, array $columns, array $options = []): array
{
$queryFields = $this->getColumnDeclarationListSQL($columns);
if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
foreach ($options['uniqueConstraints'] as $definition) {
$queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($definition);
}
}
$queryFields .= $this->getNonAutoincrementPrimaryKeyDefinition($columns, $options);
if (isset($options['foreignKeys'])) {
foreach ($options['foreignKeys'] as $foreignKey) {
$queryFields .= ', ' . $this->getForeignKeyDeclarationSQL($foreignKey);
}
}
$tableComment = '';
if (isset($options['comment'])) {
$comment = trim($options['comment'], " '");
$tableComment = $this->getInlineTableCommentSQL($comment);
}
$query = ['CREATE TABLE ' . $name . ' ' . $tableComment . '(' . $queryFields . ')'];
if (isset($options['alter']) && $options['alter'] === true) {
return $query;
}
if (isset($options['indexes']) && ! empty($options['indexes'])) {
foreach ($options['indexes'] as $indexDef) {
$query[] = $this->getCreateIndexSQL($indexDef, $name);
}
}
if (isset($options['unique']) && ! empty($options['unique'])) {
foreach ($options['unique'] as $indexDef) {
$query[] = $this->getCreateIndexSQL($indexDef, $name);
}
}
return $query;
}
/**
* Generate a PRIMARY KEY definition if no autoincrement value is used
*
* @param mixed[][] $columns
* @param mixed[] $options
*/
private function getNonAutoincrementPrimaryKeyDefinition(array $columns, array $options): string
{
if (empty($options['primary'])) {
return '';
}
$keyColumns = array_unique(array_values($options['primary']));
foreach ($keyColumns as $keyColumn) {
foreach ($columns as $column) {
if ($column['name'] === $keyColumn && ! empty($column['autoincrement'])) {
return '';
}
}
}
return ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
}
protected function getBinaryTypeDeclarationSQLSnippet(?int $length): string
{
return 'BLOB';
}
protected function getVarcharTypeDeclarationSQLSnippet(?int $length): string
{
$sql = 'VARCHAR';
if ($length !== null) {
$sql .= sprintf('(%d)', $length);
}
return $sql;
}
protected function getVarbinaryTypeDeclarationSQLSnippet(?int $length): string
{
return 'BLOB';
}
/**
* {@inheritDoc}
*/
public function getClobTypeDeclarationSQL(array $column): string
{
return 'CLOB';
}
/** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */
public function getListViewsSQL(string $database): string
{
return "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL";
}
/** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */
public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey): string
{
$query = parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
if (! $foreignKey->hasOption('deferrable') || $foreignKey->getOption('deferrable') === false) {
$query .= ' NOT';
}
$query .= ' DEFERRABLE';
$query .= ' INITIALLY';
if ($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) {
$query .= ' DEFERRED';
} else {
$query .= ' IMMEDIATE';
}
return $query;
}
public function supportsIdentityColumns(): bool
{
return true;
}
/** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */
public function supportsColumnCollation(): bool
{
return true;
}
/** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */
public function supportsInlineColumnComments(): bool
{
return true;
}
public function getTruncateTableSQL(string $tableName, bool $cascade = false): string
{
$tableIdentifier = new Identifier($tableName);
return 'DELETE FROM ' . $tableIdentifier->getQuotedName($this);
}
/** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */
public function getInlineColumnCommentSQL(string $comment): string
{
if ($comment === '') {
return '';
}
return '--' . str_replace("\n", "\n--", $comment) . "\n";
}
private function getInlineTableCommentSQL(string $comment): string
{
return $this->getInlineColumnCommentSQL($comment);
}
protected function initializeDoctrineTypeMappings(): void
{
$this->doctrineTypeMapping = [
'bigint' => 'bigint',
'bigserial' => 'bigint',
'blob' => 'blob',
'boolean' => 'boolean',
'char' => 'string',
'clob' => 'text',
'date' => 'date',
'datetime' => 'datetime',
'decimal' => 'decimal',
'double' => 'float',
'double precision' => 'float',
'float' => 'float',
'image' => 'string',
'int' => 'integer',
'integer' => 'integer',
'longtext' => 'text',
'longvarchar' => 'string',
'mediumint' => 'integer',
'mediumtext' => 'text',
'ntext' => 'string',
'numeric' => 'decimal',
'nvarchar' => 'string',
'real' => 'float',
'serial' => 'integer',
'smallint' => 'smallint',
'string' => 'string',
'text' => 'text',
'time' => 'time',
'timestamp' => 'datetime',
'tinyint' => 'boolean',
'tinytext' => 'text',
'varchar' => 'string',
'varchar2' => 'string',
];
}
protected function createReservedKeywordsList(): KeywordList
{
return new SQLiteKeywords();
}
/**
* {@inheritDoc}
*/
protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff): array
{
return [];
}
/**
* {@inheritDoc}
*/
protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff): array
{
$table = $diff->getOldTable();
$sql = [];
foreach ($this->getIndexesInAlteredTable($diff, $table) as $index) {
if ($index->isPrimary()) {
continue;
}
$sql[] = $this->getCreateIndexSQL($index, $table->getQuotedName($this));
}
return $sql;
}
protected function doModifyLimitQuery(string $query, ?int $limit, int $offset): string
{
if ($limit === null && $offset > 0) {
$limit = -1;
}
return parent::doModifyLimitQuery($query, $limit, $offset);
}
/**
* {@inheritDoc}
*/
public function getBlobTypeDeclarationSQL(array $column): string
{
return 'BLOB';
}
public function getTemporaryTableName(string $tableName): string
{
return $tableName;
}
/**
* {@inheritDoc}
*/
public function getCreateTablesSQL(array $tables): array
{
$sql = [];
foreach ($tables as $table) {
$sql = array_merge($sql, $this->getCreateTableSQL($table));
}
return $sql;
}
/** {@inheritDoc} */
public function getCreateIndexSQL(Index $index, string $table): string
{
$name = $index->getQuotedName($this);
$columns = $index->getColumns();
if (count($columns) === 0) {
throw new InvalidArgumentException(sprintf(
'Incomplete or invalid index definition %s on table %s',
$name,
$table,
));
}
if ($index->isPrimary()) {
return $this->getCreatePrimaryKeySQL($index, $table);
}
if (strpos($table, '.') !== false) {
[$schema, $table] = explode('.', $table);
$name = $schema . '.' . $name;
}
$query = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table;
$query .= ' (' . implode(', ', $index->getQuotedColumns($this)) . ')' . $this->getPartialIndexSQL($index);
return $query;
}
/**
* {@inheritDoc}
*/
public function getDropTablesSQL(array $tables): array
{
$sql = [];
foreach ($tables as $table) {
$sql[] = $this->getDropTableSQL($table->getQuotedName($this));
}
return $sql;
}
public function getCreatePrimaryKeySQL(Index $index, string $table): string
{
throw NotSupported::new(__METHOD__);
}
public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, string $table): string
{
throw NotSupported::new(__METHOD__);
}
public function getDropForeignKeySQL(string $foreignKey, string $table): string
{
throw NotSupported::new(__METHOD__);
}
/**
* {@inheritDoc}
*/
public function getAlterTableSQL(TableDiff $diff): array
{
$sql = $this->getSimpleAlterTableSQL($diff);
if ($sql !== false) {
return $sql;
}
$table = $diff->getOldTable();
$columns = [];
$oldColumnNames = [];
$newColumnNames = [];
$columnSql = [];
foreach ($table->getColumns() as $column) {
$columnName = strtolower($column->getName());
$columns[$columnName] = $column;
$oldColumnNames[$columnName] = $newColumnNames[$columnName] = $column->getQuotedName($this);
}
foreach ($diff->getDroppedColumns() as $column) {
$columnName = strtolower($column->getName());
if (! isset($columns[$columnName])) {
continue;
}
unset(
$columns[$columnName],
$oldColumnNames[$columnName],
$newColumnNames[$columnName],
);
}
foreach ($diff->getRenamedColumns() as $oldColumnName => $column) {
$oldColumnName = strtolower($oldColumnName);
$columns = $this->replaceColumn(
$table->getName(),
$columns,
$oldColumnName,
$column,
);
if (! isset($newColumnNames[$oldColumnName])) {
continue;
}
$newColumnNames[$oldColumnName] = $column->getQuotedName($this);
}
foreach ($diff->getModifiedColumns() as $columnDiff) {
$oldColumnName = strtolower($columnDiff->getOldColumn()->getName());
$newColumn = $columnDiff->getNewColumn();
$columns = $this->replaceColumn(
$table->getName(),
$columns,
$oldColumnName,
$newColumn,
);
if (! isset($newColumnNames[$oldColumnName])) {
continue;
}
$newColumnNames[$oldColumnName] = $newColumn->getQuotedName($this);
}
foreach ($diff->getAddedColumns() as $column) {
$columns[strtolower($column->getName())] = $column;
}
$tableName = $table->getName();
$pos = strpos($tableName, '.');
if ($pos !== false) {
$tableName = substr($tableName, $pos + 1);
}
$dataTable = new Table('__temp__' . $tableName);
$newTable = new Table(
$table->getQuotedName($this),
$columns,
$this->getPrimaryIndexInAlteredTable($diff, $table),
[],
$this->getForeignKeysInAlteredTable($diff, $table),
$table->getOptions(),
);
$newTable->addOption('alter', true);
$sql = $this->getPreAlterTableIndexForeignKeySQL($diff);
$sql[] = sprintf(
'CREATE TEMPORARY TABLE %s AS SELECT %s FROM %s',
$dataTable->getQuotedName($this),
implode(', ', $oldColumnNames),
$table->getQuotedName($this),
);
$sql[] = $this->getDropTableSQL($table->getQuotedName($this));
$sql = array_merge($sql, $this->getCreateTableSQL($newTable));
$sql[] = sprintf(
'INSERT INTO %s (%s) SELECT %s FROM %s',
$newTable->getQuotedName($this),
implode(', ', $newColumnNames),
implode(', ', $oldColumnNames),
$dataTable->getQuotedName($this),
);
$sql[] = $this->getDropTableSQL($dataTable->getQuotedName($this));
return array_merge($sql, $this->getPostAlterTableIndexForeignKeySQL($diff), $columnSql);
}
/**
* Replace the column with the given name with the new column.
*
* @param array<string,Column> $columns
*
* @return array<string,Column>
*
* @throws Exception
*/
private function replaceColumn(string $tableName, array $columns, string $columnName, Column $column): array
{
$keys = array_keys($columns);
$index = array_search($columnName, $keys, true);
if ($index === false) {
throw ColumnDoesNotExist::new($columnName, $tableName);
}
$values = array_values($columns);
$keys[$index] = strtolower($column->getName());
$values[$index] = $column;
return array_combine($keys, $values);
}
/**
* @return list<string>|false
*
* @throws Exception
*/
private function getSimpleAlterTableSQL(TableDiff $diff): array|false
{
if (
count($diff->getModifiedColumns()) > 0
|| count($diff->getDroppedColumns()) > 0
|| count($diff->getRenamedColumns()) > 0
|| count($diff->getAddedIndexes()) > 0
|| count($diff->getModifiedIndexes()) > 0
|| count($diff->getDroppedIndexes()) > 0
|| count($diff->getRenamedIndexes()) > 0
|| count($diff->getAddedForeignKeys()) > 0
|| count($diff->getModifiedForeignKeys()) > 0
|| count($diff->getDroppedForeignKeys()) > 0
) {
return false;
}
$table = $diff->getOldTable();
$sql = [];
$columnSql = [];
foreach ($diff->getAddedColumns() as $column) {
$definition = array_merge([
'unique' => null,
'autoincrement' => null,
'default' => null,
], $column->toArray());
$type = $definition['type'];
/** @psalm-suppress RiskyTruthyFalsyComparison */
switch (true) {
case isset($definition['columnDefinition']) || $definition['autoincrement'] || $definition['unique']:
case $type instanceof Types\DateTimeType && $definition['default'] === $this->getCurrentTimestampSQL():
case $type instanceof Types\DateType && $definition['default'] === $this->getCurrentDateSQL():
case $type instanceof Types\TimeType && $definition['default'] === $this->getCurrentTimeSQL():
return false;
}
$definition['name'] = $column->getQuotedName($this);
$sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ADD COLUMN '
. $this->getColumnDeclarationSQL($definition['name'], $definition);
}
return array_merge($sql, $columnSql);
}
/** @return string[] */
private function getColumnNamesInAlteredTable(TableDiff $diff, Table $oldTable): array
{
$columns = [];
foreach ($oldTable->getColumns() as $column) {
$columnName = $column->getName();
$columns[strtolower($columnName)] = $columnName;
}
foreach ($diff->getDroppedColumns() as $column) {
$columnName = strtolower($column->getName());
if (! isset($columns[$columnName])) {
continue;
}
unset($columns[$columnName]);
}
foreach ($diff->getRenamedColumns() as $oldColumnName => $column) {
$columnName = $column->getName();
$columns[strtolower($oldColumnName)] = $columnName;
$columns[strtolower($columnName)] = $columnName;
}
foreach ($diff->getModifiedColumns() as $columnDiff) {
$oldColumnName = $columnDiff->getOldColumn()->getName();
$newColumnName = $columnDiff->getNewColumn()->getName();
$columns[strtolower($oldColumnName)] = $newColumnName;
$columns[strtolower($newColumnName)] = $newColumnName;
}
foreach ($diff->getAddedColumns() as $column) {
$columnName = $column->getName();
$columns[strtolower($columnName)] = $columnName;
}
return $columns;
}
/** @return Index[] */
private function getIndexesInAlteredTable(TableDiff $diff, Table $oldTable): array
{
$indexes = $oldTable->getIndexes();
$columnNames = $this->getColumnNamesInAlteredTable($diff, $oldTable);
foreach ($indexes as $key => $index) {
foreach ($diff->getRenamedIndexes() as $oldIndexName => $renamedIndex) {
if (strtolower($key) !== strtolower($oldIndexName)) {
continue;
}
unset($indexes[$key]);
}
$changed = false;
$indexColumns = [];
foreach ($index->getColumns() as $columnName) {
$normalizedColumnName = strtolower($columnName);
if (! isset($columnNames[$normalizedColumnName])) {
unset($indexes[$key]);
continue 2;
}
$indexColumns[] = $columnNames[$normalizedColumnName];
if ($columnName === $columnNames[$normalizedColumnName]) {
continue;
}
$changed = true;
}
if (! $changed) {
continue;
}
$indexes[$key] = new Index(
$index->getName(),
$indexColumns,
$index->isUnique(),
$index->isPrimary(),
$index->getFlags(),
);
}
foreach ($diff->getDroppedIndexes() as $index) {
$indexName = $index->getName();
if ($indexName === '') {
continue;
}
unset($indexes[strtolower($indexName)]);
}
foreach (
array_merge(
$diff->getModifiedIndexes(),
$diff->getAddedIndexes(),
$diff->getRenamedIndexes(),
) as $index
) {
$indexName = $index->getName();
if ($indexName !== '') {
$indexes[strtolower($indexName)] = $index;
} else {
$indexes[] = $index;
}
}
return $indexes;
}
/** @return ForeignKeyConstraint[] */
private function getForeignKeysInAlteredTable(TableDiff $diff, Table $oldTable): array
{
$foreignKeys = $oldTable->getForeignKeys();
$columnNames = $this->getColumnNamesInAlteredTable($diff, $oldTable);
foreach ($foreignKeys as $key => $constraint) {
$changed = false;
$localColumns = [];
foreach ($constraint->getLocalColumns() as $columnName) {
$normalizedColumnName = strtolower($columnName);
if (! isset($columnNames[$normalizedColumnName])) {
unset($foreignKeys[$key]);
continue 2;
}
$localColumns[] = $columnNames[$normalizedColumnName];
if ($columnName === $columnNames[$normalizedColumnName]) {
continue;
}
$changed = true;
}
if (! $changed) {
continue;
}
$foreignKeys[$key] = new ForeignKeyConstraint(
$localColumns,
$constraint->getForeignTableName(),
$constraint->getForeignColumns(),
$constraint->getName(),
$constraint->getOptions(),
);
}
foreach ($diff->getDroppedForeignKeys() as $constraint) {
$constraintName = $constraint->getName();
if ($constraintName === '') {
continue;
}
unset($foreignKeys[strtolower($constraintName)]);
}
foreach (array_merge($diff->getModifiedForeignKeys(), $diff->getAddedForeignKeys()) as $constraint) {
$constraintName = $constraint->getName();
if ($constraintName !== '') {
$foreignKeys[strtolower($constraintName)] = $constraint;
} else {
$foreignKeys[] = $constraint;
}
}
return $foreignKeys;
}
/** @return Index[] */
private function getPrimaryIndexInAlteredTable(TableDiff $diff, Table $oldTable): array
{
$primaryIndex = [];
foreach ($this->getIndexesInAlteredTable($diff, $oldTable) as $index) {
if (! $index->isPrimary()) {
continue;
}
$primaryIndex = [$index->getName() => $index];
}
return $primaryIndex;
}
public function createSchemaManager(Connection $connection): SQLiteSchemaManager
{
return new SQLiteSchemaManager($connection, $this);
}
}