ok
Direktori : /home2/selectio/public_html/3-idiots/vendor/doctrine/dbal/src/Platforms/ |
Current File : /home2/selectio/public_html/3-idiots/vendor/doctrine/dbal/src/Platforms/SqlitePlatform.php |
<?php namespace Doctrine\DBAL\Platforms; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Driver\API\SQLite\UserDefinedFunctions; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Schema\Column; use Doctrine\DBAL\Schema\Constraint; use Doctrine\DBAL\Schema\ForeignKeyConstraint; use Doctrine\DBAL\Schema\Identifier; use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\SchemaException; use Doctrine\DBAL\Schema\SqliteSchemaManager; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\TableDiff; use Doctrine\DBAL\TransactionIsolationLevel; use Doctrine\DBAL\Types; use Doctrine\DBAL\Types\IntegerType; use Doctrine\Deprecations\Deprecation; 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 implode; use function is_numeric; use function sprintf; use function sqrt; use function str_replace; use function strlen; use function strtolower; use function trim; /** * The SqlitePlatform class describes the specifics and dialects of the SQLite * database platform. * * @todo Rename: SQLitePlatform */ class SqlitePlatform extends AbstractPlatform { private bool $schemaEmulationEnabled = true; /** * {@inheritDoc} */ public function getRegexpExpression() { return 'REGEXP'; } /** * @deprecated Generate dates within the application. * * @param string $type * * @return string */ public function getNowExpression($type = 'timestamp') { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4753', 'SqlitePlatform::getNowExpression() is deprecated. Generate dates within the application.', ); switch ($type) { case 'time': return 'time(\'now\')'; case 'date': return 'date(\'now\')'; case 'timestamp': default: return 'datetime(\'now\')'; } } /** * {@inheritDoc} */ public function getModExpression($expression1, $expression2) { return $expression1 . ' % ' . $expression2; } /** * {@inheritDoc} */ public function getTrimExpression($str, $mode = TrimMode::UNSPECIFIED, $char = false) { $trimChar = $char !== false ? ', ' . $char : ''; switch ($mode) { case TrimMode::LEADING: $trimFn = 'LTRIM'; break; case TrimMode::TRAILING: $trimFn = 'RTRIM'; break; default: $trimFn = 'TRIM'; } return $trimFn . '(' . $str . $trimChar . ')'; } /** * {@inheritDoc} * * SQLite only supports the 2 parameter variant of this function */ public function getSubstringExpression($string, $start, $length = null) { if ($length !== null) { return 'SUBSTR(' . $string . ', ' . $start . ', ' . $length . ')'; } return 'SUBSTR(' . $string . ', ' . $start . ', LENGTH(' . $string . '))'; } /** * {@inheritDoc} */ public function getLocateExpression($str, $substr, $startPos = false) { if ($startPos === false || $startPos === 1 || $startPos === '1') { return 'INSTR(' . $str . ', ' . $substr . ')'; } return 'CASE WHEN INSTR(SUBSTR(' . $str . ', ' . $startPos . '), ' . $substr . ') > 0 THEN INSTR(SUBSTR(' . $str . ', ' . $startPos . '), ' . $substr . ') + ' . $startPos . ' - 1 ELSE 0 END'; } /** * {@inheritdoc} */ protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) { switch ($unit) { case DateIntervalUnit::SECOND: case DateIntervalUnit::MINUTE: case DateIntervalUnit::HOUR: return 'DATETIME(' . $date . ",'" . $operator . $interval . ' ' . $unit . "')"; } switch ($unit) { case DateIntervalUnit::WEEK: $interval *= 7; $unit = DateIntervalUnit::DAY; break; case DateIntervalUnit::QUARTER: $interval *= 3; $unit = DateIntervalUnit::MONTH; break; } if (! is_numeric($interval)) { $interval = "' || " . $interval . " || '"; } return 'DATE(' . $date . ",'" . $operator . $interval . ' ' . $unit . "')"; } /** * {@inheritDoc} */ public function getDateDiffExpression($date1, $date2) { 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'"; } /** * {@inheritDoc} */ protected function _getTransactionIsolationLevelSQL($level) { switch ($level) { case TransactionIsolationLevel::READ_UNCOMMITTED: return '0'; case TransactionIsolationLevel::READ_COMMITTED: case TransactionIsolationLevel::REPEATABLE_READ: case TransactionIsolationLevel::SERIALIZABLE: return '1'; default: return parent::_getTransactionIsolationLevelSQL($level); } } /** * {@inheritDoc} */ public function getSetTransactionIsolationSQL($level) { return 'PRAGMA read_uncommitted = ' . $this->_getTransactionIsolationLevelSQL($level); } /** * {@inheritDoc} * * @deprecated */ public function prefersIdentityColumns() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/1519', 'SqlitePlatform::prefersIdentityColumns() is deprecated.', ); return true; } /** * {@inheritDoc} */ public function getBooleanTypeDeclarationSQL(array $column) { return 'BOOLEAN'; } /** * {@inheritDoc} */ public function getIntegerTypeDeclarationSQL(array $column) { return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function getBigIntTypeDeclarationSQL(array $column) { // SQLite autoincrement is implicit for INTEGER PKs, but not for BIGINT columns if (! empty($column['autoincrement'])) { return $this->getIntegerTypeDeclarationSQL($column); } return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } /** * @deprecated Use {@see getSmallIntTypeDeclarationSQL()} instead. * * @param array<string, mixed> $column * * @return string */ public function getTinyIntTypeDeclarationSQL(array $column) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5511', '%s is deprecated. Use getSmallIntTypeDeclarationSQL() instead.', __METHOD__, ); // SQLite autoincrement is implicit for INTEGER PKs, but not for TINYINT columns if (! empty($column['autoincrement'])) { return $this->getIntegerTypeDeclarationSQL($column); } return 'TINYINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function getSmallIntTypeDeclarationSQL(array $column) { // SQLite autoincrement is implicit for INTEGER PKs, but not for SMALLINT columns if (! empty($column['autoincrement'])) { return $this->getIntegerTypeDeclarationSQL($column); } return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } /** * @deprecated Use {@see getIntegerTypeDeclarationSQL()} instead. * * @param array<string, mixed> $column * * @return string */ public function getMediumIntTypeDeclarationSQL(array $column) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5511', '%s is deprecated. Use getIntegerTypeDeclarationSQL() instead.', __METHOD__, ); // SQLite autoincrement is implicit for INTEGER PKs, but not for MEDIUMINT columns if (! empty($column['autoincrement'])) { return $this->getIntegerTypeDeclarationSQL($column); } return 'MEDIUMINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function getDateTimeTypeDeclarationSQL(array $column) { return 'DATETIME'; } /** * {@inheritDoc} */ public function getDateTypeDeclarationSQL(array $column) { return 'DATE'; } /** * {@inheritDoc} */ public function getTimeTypeDeclarationSQL(array $column) { return 'TIME'; } /** * {@inheritDoc} */ protected function _getCommonIntegerTypeDeclarationSQL(array $column) { // sqlite autoincrement is only possible for the primary key if (! empty($column['autoincrement'])) { return ' PRIMARY KEY AUTOINCREMENT'; } return ! empty($column['unsigned']) ? ' UNSIGNED' : ''; } /** * Disables schema emulation. * * Schema emulation is enabled by default to maintain backwards compatibility. * Disable it to opt-in to the behavior of DBAL 4. * * @deprecated Will be removed in DBAL 4.0. */ public function disableSchemaEmulation(): void { $this->schemaEmulationEnabled = false; } private function emulateSchemaNamespacing(string $tableName): string { return $this->schemaEmulationEnabled ? str_replace('.', '__', $tableName) : $tableName; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey) { return parent::getForeignKeyDeclarationSQL(new ForeignKeyConstraint( $foreignKey->getQuotedLocalColumns($this), $this->emulateSchemaNamespacing($foreignKey->getQuotedForeignTableName($this)), $foreignKey->getQuotedForeignColumns($this), $foreignKey->getName(), $foreignKey->getOptions(), )); } /** * {@inheritDoc} */ protected function _getCreateTableSQL($name, array $columns, array $options = []) { $name = $this->emulateSchemaNamespacing($name); $queryFields = $this->getColumnDeclarationListSQL($columns); if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { foreach ($options['uniqueConstraints'] as $constraintName => $definition) { $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($constraintName, $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) { if (! empty($columns[$keyColumn]['autoincrement'])) { return ''; } } return ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; } /** * {@inheritDoc} */ protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) { return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(255)') : ($length > 0 ? 'VARCHAR(' . $length . ')' : 'TEXT'); } /** * {@inheritdoc} */ protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) { return 'BLOB'; } /** * {@inheritdoc} * * @deprecated */ public function getBinaryMaxLength() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'SqlitePlatform::getBinaryMaxLength() is deprecated.', ); return 0; } /** * {@inheritdoc} * * @deprecated */ public function getBinaryDefaultLength() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'Relying on the default binary column length is deprecated, specify the length explicitly.', ); return 0; } /** * {@inheritDoc} */ public function getClobTypeDeclarationSQL(array $column) { return 'CLOB'; } /** * @deprecated * * {@inheritDoc} */ public function getListTableConstraintsSQL($table) { $table = $this->emulateSchemaNamespacing($table); return sprintf( "SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name = %s AND sql NOT NULL ORDER BY name", $this->quoteStringLiteral($table), ); } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * {@inheritDoc} */ public function getListTableColumnsSQL($table, $database = null) { $table = $this->emulateSchemaNamespacing($table); return sprintf('PRAGMA table_info(%s)', $this->quoteStringLiteral($table)); } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * {@inheritDoc} */ public function getListTableIndexesSQL($table, $database = null) { $table = $this->emulateSchemaNamespacing($table); return sprintf('PRAGMA index_list(%s)', $this->quoteStringLiteral($table)); } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * {@inheritDoc} */ public function getListTablesSQL() { return 'SELECT name FROM sqlite_master' . " WHERE type = 'table'" . " AND name != 'sqlite_sequence'" . " AND name != 'geometry_columns'" . " AND name != 'spatial_ref_sys'" . ' UNION ALL SELECT name FROM sqlite_temp_master' . " WHERE type = 'table' ORDER BY name"; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListViewsSQL($database) { return "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL"; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) { $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; } /** * {@inheritDoc} * * @deprecated */ public function supportsCreateDropDatabase() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5513', '%s is deprecated.', __METHOD__, ); return false; } /** * {@inheritDoc} */ public function supportsIdentityColumns() { return true; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsColumnCollation() { return true; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsInlineColumnComments() { return true; } /** * {@inheritDoc} */ public function getName() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4749', 'SqlitePlatform::getName() is deprecated. Identify platforms by their class.', ); return 'sqlite'; } /** * {@inheritDoc} */ public function getTruncateTableSQL($tableName, $cascade = false) { $tableIdentifier = new Identifier($tableName); $tableName = $this->emulateSchemaNamespacing($tableIdentifier->getQuotedName($this)); return 'DELETE FROM ' . $tableName; } /** * User-defined function for Sqlite that is used with PDO::sqliteCreateFunction(). * * @deprecated The driver will use {@see sqrt()} in the next major release. * * @param int|float $value * * @return float */ public static function udfSqrt($value) { return sqrt($value); } /** * User-defined function for Sqlite that implements MOD(a, b). * * @deprecated The driver will use {@see UserDefinedFunctions::mod()} in the next major release. * * @param int $a * @param int $b * * @return int */ public static function udfMod($a, $b) { return UserDefinedFunctions::mod($a, $b); } /** * @deprecated The driver will use {@see UserDefinedFunctions::locate()} in the next major release. * * @param string $str * @param string $substr * @param int $offset * * @return int */ public static function udfLocate($str, $substr, $offset = 0) { return UserDefinedFunctions::locate($str, $substr, $offset); } /** * {@inheritDoc} */ public function getForUpdateSQL() { return ''; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getInlineColumnCommentSQL($comment) { return '--' . str_replace("\n", "\n--", $comment) . "\n"; } private function getInlineTableCommentSQL(string $comment): string { return $this->getInlineColumnCommentSQL($comment); } /** * {@inheritDoc} */ protected function initializeDoctrineTypeMappings() { $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', 'text' => 'text', 'time' => 'time', 'timestamp' => 'datetime', 'tinyint' => 'boolean', 'tinytext' => 'text', 'varchar' => 'string', 'varchar2' => 'string', ]; } /** * {@inheritDoc} * * @deprecated Implement {@see createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', 'SqlitePlatform::getReservedKeywordsClass() is deprecated,' . ' use SqlitePlatform::createReservedKeywordsList() instead.', ); return Keywords\SQLiteKeywords::class; } /** * {@inheritDoc} */ protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) { return []; } /** * {@inheritDoc} */ protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) { $table = $diff->getOldTable(); if (! $table instanceof Table) { throw new Exception( 'Sqlite platform requires for alter table the table diff with reference to original table schema', ); } $sql = []; $tableName = $diff->getNewName(); if ($tableName === false) { $tableName = $diff->getName($this); } foreach ($this->getIndexesInAlteredTable($diff, $table) as $index) { if ($index->isPrimary()) { continue; } $sql[] = $this->getCreateIndexSQL($index, $tableName->getQuotedName($this)); } return $sql; } /** * {@inheritDoc} */ protected function doModifyLimitQuery($query, $limit, $offset) { if ($limit === null && $offset > 0) { return sprintf('%s LIMIT -1 OFFSET %d', $query, $offset); } return parent::doModifyLimitQuery($query, $limit, $offset); } /** * {@inheritDoc} */ public function getBlobTypeDeclarationSQL(array $column) { return 'BLOB'; } /** * {@inheritDoc} */ public function getTemporaryTableName($tableName) { $tableName = $this->emulateSchemaNamespacing($tableName); return $tableName; } /** * {@inheritDoc} * * @deprecated * * Sqlite Platform emulates schema by underscoring each dot and generating tables * into the default database. * * This hack is implemented to be able to use SQLite as testdriver when * using schema supporting databases. */ public function canEmulateSchemas() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4805', 'SqlitePlatform::canEmulateSchemas() is deprecated.', ); return $this->schemaEmulationEnabled; } /** * {@inheritDoc} */ public function getCreateTablesSQL(array $tables): array { $sql = []; foreach ($tables as $table) { $sql = array_merge($sql, $this->getCreateTableSQL($table)); } return $sql; } /** * {@inheritDoc} */ public function getDropTablesSQL(array $tables): array { $sql = []; foreach ($tables as $table) { $sql[] = $this->getDropTableSQL($table->getQuotedName($this)); } return $sql; } /** * {@inheritDoc} */ public function getCreatePrimaryKeySQL(Index $index, $table) { throw new Exception('Sqlite platform does not support alter primary key.'); } /** * {@inheritdoc} */ public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) { throw new Exception('Sqlite platform does not support alter foreign key.'); } /** * {@inheritdoc} */ public function getDropForeignKeySQL($foreignKey, $table) { throw new Exception('Sqlite platform does not support alter foreign key.'); } /** * {@inheritDoc} * * @deprecated */ public function getCreateConstraintSQL(Constraint $constraint, $table) { throw new Exception('Sqlite platform does not support alter constraint.'); } /** * {@inheritDoc} * * @param int|null $createFlags * @psalm-param int-mask-of<AbstractPlatform::CREATE_*>|null $createFlags */ public function getCreateTableSQL(Table $table, $createFlags = null) { $createFlags = $createFlags ?? self::CREATE_INDEXES | self::CREATE_FOREIGNKEYS; return parent::getCreateTableSQL($table, $createFlags); } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * @param string $table * @param string|null $database * * @return string */ public function getListTableForeignKeysSQL($table, $database = null) { $table = $this->emulateSchemaNamespacing($table); return sprintf('PRAGMA foreign_key_list(%s)', $this->quoteStringLiteral($table)); } /** * {@inheritDoc} */ public function getAlterTableSQL(TableDiff $diff) { $sql = $this->getSimpleAlterTableSQL($diff); if ($sql !== false) { return $sql; } $table = $diff->getOldTable(); if (! $table instanceof Table) { throw new Exception( 'Sqlite platform requires for alter table the table diff with reference to original table schema', ); } $columns = []; $oldColumnNames = []; $newColumnNames = []; $columnSql = []; foreach ($table->getColumns() as $columnName => $column) { $columnName = strtolower($columnName); $columns[$columnName] = $column; $oldColumnNames[$columnName] = $newColumnNames[$columnName] = $column->getQuotedName($this); } foreach ($diff->getDroppedColumns() as $column) { if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { continue; } $columnName = strtolower($column->getName()); if (! isset($columns[$columnName])) { continue; } unset( $columns[$columnName], $oldColumnNames[$columnName], $newColumnNames[$columnName], ); } foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { continue; } $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) { if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { continue; } $oldColumn = $columnDiff->getOldColumn() ?? $columnDiff->getOldColumnName(); $oldColumnName = strtolower($oldColumn->getName()); $columns = $this->replaceColumn( $table->getName(), $columns, $oldColumnName, $columnDiff->getNewColumn(), ); if (! isset($newColumnNames[$oldColumnName])) { continue; } $newColumnNames[$oldColumnName] = $columnDiff->getNewColumn()->getQuotedName($this); } foreach ($diff->getAddedColumns() as $column) { if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { continue; } $columns[strtolower($column->getName())] = $column; } $sql = []; $tableSql = []; if (! $this->onSchemaAlterTable($diff, $tableSql)) { $dataTable = new Table('__temp__' . $table->getName()); $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); $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)); $newName = $diff->getNewName(); if ($newName !== false) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5663', 'Generation of "rename table" SQL using %s is deprecated. Use getRenameTableSQL() instead.', __METHOD__, ); $sql[] = sprintf( 'ALTER TABLE %s RENAME TO %s', $newTable->getQuotedName($this), $newName->getQuotedName($this), ); } $sql = array_merge($sql, $this->getPostAlterTableIndexForeignKeySQL($diff)); } return array_merge($sql, $tableSql, $columnSql); } /** * Replace the column with the given name with the new column. * * @param string $tableName * @param array<string,Column> $columns * @param string $columnName * * @return array<string,Column> * * @throws Exception */ private function replaceColumn($tableName, array $columns, $columnName, Column $column): array { $keys = array_keys($columns); $index = array_search($columnName, $keys, true); if ($index === false) { throw SchemaException::columnDoesNotExist($columnName, $tableName); } $values = array_values($columns); $keys[$index] = strtolower($column->getName()); $values[$index] = $column; return array_combine($keys, $values); } /** * @return string[]|false * * @throws Exception */ private function getSimpleAlterTableSQL(TableDiff $diff) { // Suppress changes on integer type autoincrement columns. foreach ($diff->getModifiedColumns() as $columnDiff) { $oldColumn = $columnDiff->getOldColumn(); if ($oldColumn === null) { continue; } $newColumn = $columnDiff->getNewColumn(); if (! $newColumn->getAutoincrement() || ! $newColumn->getType() instanceof IntegerType) { continue; } $oldColumnName = $oldColumn->getName(); if (! $columnDiff->hasTypeChanged() && $columnDiff->hasUnsignedChanged()) { unset($diff->changedColumns[$oldColumnName]); continue; } $fromColumnType = $oldColumn->getType(); if (! ($fromColumnType instanceof Types\SmallIntType) && ! ($fromColumnType instanceof Types\BigIntType)) { continue; } unset($diff->changedColumns[$oldColumnName]); } 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() ?? $diff->getName($this); $sql = []; $tableSql = []; $columnSql = []; foreach ($diff->getAddedColumns() as $column) { if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { continue; } $definition = array_merge([ 'unique' => null, 'autoincrement' => null, 'default' => null, ], $column->toArray()); $type = $definition['type']; 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); if ($type instanceof Types\StringType) { $definition['length'] ??= 255; } $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ADD COLUMN ' . $this->getColumnDeclarationSQL($definition['name'], $definition); } if (! $this->onSchemaAlterTable($diff, $tableSql)) { if ($diff->newName !== false) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5663', 'Generation of SQL that renames a table using %s is deprecated.' . ' Use getRenameTableSQL() instead.', __METHOD__, ); $newTable = new Identifier($diff->newName); $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' RENAME TO ' . $newTable->getQuotedName($this); } } return array_merge($sql, $tableSql, $columnSql); } /** @return string[] */ private function getColumnNamesInAlteredTable(TableDiff $diff, Table $fromTable): array { $columns = []; foreach ($fromTable->getColumns() as $columnName => $column) { $columns[strtolower($columnName)] = $column->getName(); } 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) { $oldColumn = $columnDiff->getOldColumn() ?? $columnDiff->getOldColumnName(); $oldColumnName = $oldColumn->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 $fromTable): array { $indexes = $fromTable->getIndexes(); $columnNames = $this->getColumnNamesInAlteredTable($diff, $fromTable); 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 = strtolower($index->getName()); if (strlen($indexName) === 0 || ! isset($indexes[$indexName])) { continue; } unset($indexes[$indexName]); } foreach ( array_merge( $diff->getModifiedIndexes(), $diff->getAddedIndexes(), $diff->getRenamedIndexes(), ) as $index ) { $indexName = strtolower($index->getName()); if (strlen($indexName) > 0) { $indexes[$indexName] = $index; } else { $indexes[] = $index; } } return $indexes; } /** @return ForeignKeyConstraint[] */ private function getForeignKeysInAlteredTable(TableDiff $diff, Table $fromTable): array { $foreignKeys = $fromTable->getForeignKeys(); $columnNames = $this->getColumnNamesInAlteredTable($diff, $fromTable); 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) { if (! $constraint instanceof ForeignKeyConstraint) { $constraint = new Identifier($constraint); } $constraintName = strtolower($constraint->getName()); if (strlen($constraintName) === 0 || ! isset($foreignKeys[$constraintName])) { continue; } unset($foreignKeys[$constraintName]); } foreach (array_merge($diff->getModifiedForeignKeys(), $diff->getAddedForeignKeys()) as $constraint) { $constraintName = strtolower($constraint->getName()); if (strlen($constraintName) > 0) { $foreignKeys[$constraintName] = $constraint; } else { $foreignKeys[] = $constraint; } } return $foreignKeys; } /** @return Index[] */ private function getPrimaryIndexInAlteredTable(TableDiff $diff, Table $fromTable): array { $primaryIndex = []; foreach ($this->getIndexesInAlteredTable($diff, $fromTable) as $index) { if (! $index->isPrimary()) { continue; } $primaryIndex = [$index->getName() => $index]; } return $primaryIndex; } public function createSchemaManager(Connection $connection): SqliteSchemaManager { return new SqliteSchemaManager($connection, $this); } }