null value means that there is no
* schema currently selected within the connection.
*
* The property should be accessed only when {@link $currentSchemaDetermined} is set to true. If the
* currently used database platform doesn't support schemas, the property will remain uninitialized.
*
* The property is initialized only once. If the underlying connection switches to a different schema, a new schema
* manager instance will have to be created to reflect this change.
*
* @var ?non-empty-string
*/
private ?string $currentSchemaName;
/**
* Indicates whether the current schema has been determined.
*/
private bool $currentSchemaDetermined = false;
/** @param T $platform */
public function __construct(protected Connection $connection, protected AbstractPlatform $platform)
{
}
/**
* Lists the available databases for this connection.
*
* @deprecated Use {@see introspectDatabaseNames()} instead.
*
* @return array
*
* @throws Exception
*/
public function listDatabases(): array
{
return array_map(function (array $row): string {
return $this->_getPortableDatabaseDefinition($row);
}, $this->connection->fetchAllAssociative(
$this->platform->getListDatabasesSQL(),
));
}
/**
* Returns a list of the names of all schemata in the current database.
*
* @deprecated Use {@see introspectSchemaNames()} instead.
*
* @return list
*
* @throws Exception
*/
public function listSchemaNames(): array
{
throw NotSupported::new(__METHOD__);
}
/**
* Lists the available sequences for this connection.
*
* @deprecated Use {@see introspectSequences()} instead.
*
* @return list
*
* @throws Exception
*/
public function listSequences(): array
{
return $this->filterAssetNames(
array_map(function (array $row): Sequence {
return $this->_getPortableSequenceDefinition($row);
}, $this->connection->fetchAllAssociative(
$this->platform->getListSequencesSQL(
$this->getDatabase(__METHOD__),
),
)),
);
}
/**
* Lists the columns for a given table.
*
* In contrast to other libraries and to the old version of Doctrine,
* this column definition does try to contain the 'primary' column for
* the reason that it is not portable across different RDBMS. Use
* {@see listTableIndexes($tableName)} to retrieve the primary key
* of a table. Where a RDBMS specifies more details, these are held
* in the platformDetails array.
*
* @deprecated Use {@see introspectTableColumns()}, {@see introspectTableColumnsByUnquotedName()}
* or {@see introspectTableColumnsByQuotedName()} instead.
*
* @return array
*
* @throws Exception
*/
public function listTableColumns(string $table): array
{
$this->validateTableName($table, __METHOD__);
$database = $this->getDatabase(__METHOD__);
return $this->_getPortableTableColumnList(
$table,
$database,
$this->fetchTableColumns($database, $this->normalizeName($table)),
);
}
/**
* Lists the indexes for a given table returning an array of Index instances.
*
* Keys of the portable indexes list are all lower-cased.
*
* @deprecated Use {@see introspectTableIndexes()}, {@see introspectTableIndexesByUnquotedName()}
* or {@see introspectTableIndexesByQuotedName()}
*
* @return array
*
* @throws Exception
*/
public function listTableIndexes(string $table): array
{
$this->validateTableName($table, __METHOD__);
$database = $this->getDatabase(__METHOD__);
$table = $this->normalizeName($table);
return $this->_getPortableTableIndexesList(
$this->fetchIndexColumns($database, $table),
$table,
);
}
/**
* Returns true if all the given tables exist.
*
* @param array $names
*
* @throws Exception
*/
public function tablesExist(array $names): bool
{
$names = array_map('strtolower', $names);
return count($names) === count(array_intersect($names, array_map('strtolower', $this->listTableNames())));
}
/** @throws Exception */
public function tableExists(string $tableName): bool
{
return $this->tablesExist([$tableName]);
}
/**
* Returns a list of all tables in the current database.
*
* @deprecated Use {@see introspectTableNames()} instead.
*
* @return list
*
* @throws Exception
*/
public function listTableNames(): array
{
return $this->filterAssetNames(
array_map(function (array $row): string {
return $this->_getPortableTableDefinition($row);
}, $this->selectTableNames(
$this->getDatabase(__METHOD__),
)->fetchAllAssociative()),
);
}
/**
* Filters asset names if they are configured to return only a subset of all
* the found elements.
*
* @param list $assetNames
*
* @return list
*
* @template N
*/
private function filterAssetNames(array $assetNames): array
{
$filter = $this->connection->getConfiguration()->getSchemaAssetsFilter();
return array_values(array_filter($assetNames, $filter));
}
/**
* Lists the tables for this connection.
*
* @deprecated Use {@see introspectTables()} instead.
*
* @return list
*
* @throws Exception
*/
public function listTables(): array
{
$database = $this->getDatabase(__METHOD__);
$tableColumnsByTable = $this->fetchTableColumnsByTable($database);
$indexColumnsByTable = $this->fetchIndexColumnsByTable($database);
$foreignKeyColumnsByTable = $this->fetchForeignKeyColumnsByTable($database);
$tableOptionsByTable = $this->fetchTableOptionsByTable($database);
$filter = $this->connection->getConfiguration()->getSchemaAssetsFilter();
$tables = [];
$configuration = $this->createSchemaConfig()
->toTableConfiguration();
foreach ($tableColumnsByTable as $tableName => $tableColumns) {
if (! $filter($tableName)) {
continue;
}
$tables[] = new Table(
$tableName,
$this->_getPortableTableColumnList($tableName, $database, $tableColumns),
$this->_getPortableTableIndexesList($indexColumnsByTable[$tableName] ?? [], $tableName),
[],
$this->_getPortableTableForeignKeysList($foreignKeyColumnsByTable[$tableName] ?? []),
$tableOptionsByTable[$tableName] ?? [],
$configuration,
);
}
return $tables;
}
/**
* Returns the current schema name used by the schema manager connection.
*
* The null value means that there is no schema currently selected within the connection or the
* corresponding database platform doesn't support schemas.
*
* @return ?non-empty-string
*
* @throws Exception
*/
final protected function getCurrentSchemaName(): ?string
{
if (! $this->platform->supportsSchemas()) {
return null;
}
if (! $this->currentSchemaDetermined) {
$this->currentSchemaName = $this->determineCurrentSchemaName();
$this->currentSchemaDetermined = true;
}
return $this->currentSchemaName;
}
/**
* Determines the name of the current schema.
*
* If the corresponding database platform supports schemas, the schema manager must implement this method.
*
* @return ?non-empty-string
*
* @throws Exception
*/
protected function determineCurrentSchemaName(): ?string
{
throw NotSupported::new(__METHOD__);
}
/**
* An extension point for those platforms where case sensitivity of the object name depends on whether it's quoted.
*
* Such platforms should convert a possibly quoted name into a value of the corresponding case.
*
* @deprecated Use {@see Identifier::toNormalizedValue()} instead.
*/
protected function normalizeName(string $name): string
{
$identifier = new Identifier($name);
return $identifier->getName();
}
private function validateTableName(string $input, string $methodName): void
{
$parser = Parsers::getOptionallyQualifiedNameParser();
try {
$tableName = $parser->parse($input);
} catch (Throwable $e) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/6768',
'Unable to parse table name passed to %s(): %s.',
$methodName,
$e->getMessage(),
);
return;
}
if ($tableName->getQualifier() === null || $this->platform->supportsSchemas()) {
return;
}
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/6768',
'Relying on %s() not parsing an unquoted table name containing a dot while working with %s is'
. ' deprecated. Pass a quoted name instead.',
$methodName,
$this->platform::class,
);
}
/**
* Selects names of tables in the specified database.
*
* @throws Exception
*/
abstract protected function selectTableNames(string $databaseName): Result;
/**
* Selects definitions of table columns in the specified database. If the table name is specified, narrows down
* the selection to this table.
*
* @throws Exception
*/
abstract protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result;
/**
* Selects definitions of index columns in the specified database. If the table name is specified, narrows down
* the selection to this table.
*
* @throws Exception
*/
abstract protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result;
/**
* Selects definitions of foreign key columns in the specified database. If the table name is specified,
* narrows down the selection to this table.
*
* @throws Exception
*/
abstract protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result;
/**
* Fetches definitions of table columns in the specified database. If the table name is specified, narrows down
* the selection to this table.
*
* @return list>
*
* @throws Exception
*/
protected function fetchTableColumns(string $databaseName, ?string $tableName = null): array
{
return $this->selectTableColumns($databaseName, $tableName)->fetchAllAssociative();
}
/**
* Fetches definitions of index columns in the specified database. If the table name is specified, narrows down
* the selection to this table.
*
* @return list>
*
* @throws Exception
*/
protected function fetchIndexColumns(string $databaseName, ?string $tableName = null): array
{
return $this->selectIndexColumns($databaseName, $tableName)->fetchAllAssociative();
}
/**
* Fetches definitions of foreign key columns in the specified database. If the table name is specified,
* narrows down the selection to this table.
*
* @return list>
*
* @throws Exception
*/
protected function fetchForeignKeyColumns(string $databaseName, ?string $tableName = null): array
{
return $this->selectForeignKeyColumns($databaseName, $tableName)->fetchAllAssociative();
}
/**
* Fetches definitions of table columns in the specified database and returns them grouped by table name.
*
* @return array>>
*
* @throws Exception
*/
protected function fetchTableColumnsByTable(string $databaseName): array
{
return $this->groupByTable($this->fetchTableColumns($databaseName));
}
/**
* Fetches definitions of index columns in the specified database and returns them grouped by table name.
*
* @return array>>
*
* @throws Exception
*/
protected function fetchIndexColumnsByTable(string $databaseName): array
{
return $this->groupByTable($this->fetchIndexColumns($databaseName));
}
/**
* Fetches definitions of foreign key columns in the specified database and returns them grouped by table name.
*
* @return array>>
*
* @throws Exception
*/
protected function fetchForeignKeyColumnsByTable(string $databaseName): array
{
return $this->groupByTable($this->fetchForeignKeyColumns($databaseName));
}
/**
* Fetches table options for the tables in the specified database and returns them grouped by table name.
* If the table name is specified, narrows down the selection to this table.
*
* @param ?non-empty-string $tableName
*
* @return array>
*
* @throws Exception
*/
abstract protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array;
/**
* Introspects the table with the given name.
*
* @deprecated Use {@see introspectTableByUnquotedName()} or {@see introspectTableByQuotedName()} instead.
*
* @throws Exception
*/
public function introspectTable(string $name): Table
{
$columns = $this->listTableColumns($name);
if ($columns === []) {
throw TableDoesNotExist::new($name);
}
return new Table(
$name,
$columns,
$this->listTableIndexes($name),
[],
$this->listTableForeignKeys($name),
$this->getTableOptions($name),
);
}
/**
* Lists the views this connection has.
*
* @deprecated Use {@see introspectViews()} instead.
*
* @return list
*
* @throws Exception
*/
public function listViews(): array
{
return array_map(function (array $row): View {
return $this->_getPortableViewDefinition($row);
}, $this->connection->fetchAllAssociative(
$this->platform->getListViewsSQL(
$this->getDatabase(__METHOD__),
),
));
}
/**
* Lists the foreign keys for the given table.
*
* @deprecated Use {@see introspectTableForeignKeyConstraints()},
* {@see introspectTableForeignKeyConstraintsByUnquotedName()}
* or {@see introspectTableForeignKeyConstraintsByQuotedName()} instead.
*
* @return array
*
* @throws Exception
*/
public function listTableForeignKeys(string $table): array
{
$this->validateTableName($table, __METHOD__);
$database = $this->getDatabase(__METHOD__);
return $this->_getPortableTableForeignKeysList(
$this->fetchForeignKeyColumns(
$database,
$this->normalizeName($table),
),
);
}
/**
* @return array
*
* @throws Exception
*/
private function getTableOptions(string $name): array
{
$this->validateTableName($name, __METHOD__);
$normalizedName = $this->normalizeName($name);
return $this->fetchTableOptionsByTable(
$this->getDatabase(__METHOD__),
$normalizedName, // @phpstan-ignore argument.type
)[$normalizedName] ?? [];
}
/**
* Introspects available databases and returns their names.
*
* @return list
*
* @throws Exception
*/
public function introspectDatabaseNames(): array
{
return $this->createSchemaProvider()->getAllDatabaseNames();
}
/**
* Introspects schemas in the current database and returns their names.
*
* @return list
*
* @throws Exception
*/
public function introspectSchemaNames(): array
{
return $this->createSchemaProvider()->getAllSchemaNames();
}
/**
* Introspects tables in the current database and returns their names.
*
* @return list
*
* @throws Exception
*/
public function introspectTableNames(): array
{
$filter = $this->connection->getConfiguration()->getSchemaAssetsFilter();
$tableNames = [];
foreach ($this->createSchemaProvider()->getAllTableNames() as $tableName) {
if (! $this->testTableName($tableName, $filter)) {
continue;
}
$tableNames[] = $tableName;
}
return $tableNames;
}
/**
* Introspects tables in the current database and returns their definitions.
*
* @return list
*
* @throws Exception
*/
public function introspectTables(): array
{
$filter = $this->connection->getConfiguration()->getSchemaAssetsFilter();
$tables = [];
foreach ($this->createSchemaProvider()->getAllTables() as $table) {
if (! $this->testTableName($table->getObjectName(), $filter)) {
continue;
}
$tables[] = $table;
}
return $tables;
}
/**
* Tests whether the table name matches the filter.
*/
private function testTableName(OptionallyQualifiedName $tableName, callable $filter): bool
{
$formattedName = $tableName->getUnqualifiedName()->getValue();
$qualifier = $tableName->getQualifier();
if ($qualifier !== null) {
$formattedName = $qualifier->getValue() . '.' . $formattedName;
}
return $filter($formattedName);
}
/**
* Introspects the table with the given name and returns its definition. If the name is unqualified, and the
* underlying database platform supports schemas, the current schema is used.
*
* This method is currently private due to the conflict with {@see introspectTable()}. Use
* {@see introspectTableByUnquotedName()} or {@see introspectTableByQuotedName()} instead.
*
* @throws Exception
*/
private function introspectTable0(OptionallyQualifiedName $tableName): Table
{
$columns = $this->introspectTableColumns($tableName);
if ($columns === []) {
throw TableDoesNotExist::new($tableName->toString());
}
$options = $this->introspectTableOptions($tableName);
assert($options !== null);
return Table::editor()
->setName($tableName)
->setColumns(...$columns)
->setPrimaryKeyConstraint($this->introspectTablePrimaryKeyConstraint($tableName))
->setIndexes(...$this->introspectTableIndexes($tableName))
->setForeignKeyConstraints(...$this->introspectTableForeignKeyConstraints($tableName))
->setOptions($options)
->create();
}
/**
* Introspects the table with the given unquoted name and schema name and returns its definition. If the schema name
* is omitted, and the underlying database platform supports schemas, the current schema is used.
*
* @param non-empty-string $tableName
* @param ?non-empty-string $schemaName
*
* @throws Exception
*/
public function introspectTableByUnquotedName(string $tableName, ?string $schemaName = null): Table
{
return $this->introspectTable0(
OptionallyQualifiedName::unquoted($tableName, $schemaName),
);
}
/**
* Introspects the table with the given quoted name and schema name and returns its definition. If the schema name
* is omitted, and the underlying database platform supports schemas, the current schema is used.
*
* @param non-empty-string $tableName
* @param ?non-empty-string $schemaName
*
* @throws Exception
*/
public function introspectTableByQuotedName(string $tableName, ?string $schemaName = null): Table
{
return $this->introspectTable0(
OptionallyQualifiedName::quoted($tableName, $schemaName),
);
}
/**
* Introspects the columns of a given table and returns their definitions. If the name is unqualified, and the
* underlying database platform supports schemas, the current schema is used.
*
* Returns an empty value if the table does not exist.
*
* @return list
*
* @throws Exception
*/
public function introspectTableColumns(OptionallyQualifiedName $tableName): array
{
return $this->introspectTableObjects(
$tableName,
static function (SchemaProvider $schemaProvider, ?string $schemaName, string $tableName): array {
return $schemaProvider->getColumnsForTable($schemaName, $tableName);
},
);
}
/**
* Introspects the columns of the table with the given unquoted name and schema name and returns their definitions.
* If the schema name is omitted and the underlying database platform supports schemas, the current schema is used.
*
* Returns an empty value if the table does not exist.
*
* @param non-empty-string $tableName
* @param ?non-empty-string $schemaName
*
* @return list
*
* @throws Exception
*/
public function introspectTableColumnsByUnquotedName(string $tableName, ?string $schemaName = null): array
{
return $this->introspectTableColumns(
OptionallyQualifiedName::unquoted($tableName, $schemaName),
);
}
/**
* Introspects the columns of the table with the given quoted name and schema name and returns their definitions. If
* the schema name is omitted and the underlying database platform supports schemas, the current schema is used.
*
* Returns an empty value if the table does not exist.
*
* @param non-empty-string $tableName
* @param ?non-empty-string $schemaName
*
* @return list
*
* @throws Exception
*/
public function introspectTableColumnsByQuotedName(string $tableName, ?string $schemaName = null): array
{
return $this->introspectTableColumns(
OptionallyQualifiedName::quoted($tableName, $schemaName),
);
}
/**
* Introspects the indexes of a given table and returns their definitions. If the name is unqualified, and the
* underlying database platform supports schemas, the current schema is used.
*
* Returns an empty value if the table does not exist.
*
* @return list
*
* @throws Exception
*/
public function introspectTableIndexes(OptionallyQualifiedName $tableName): array
{
return $this->introspectTableObjects(
$tableName,
static function (SchemaProvider $schemaProvider, ?string $schemaName, string $tableName): array {
return $schemaProvider->getIndexesForTable($schemaName, $tableName);
},
);
}
/**
* Introspects the indexes of the table with the given unquoted name and schema name and returns their definitions.
* If the schema name is omitted and the underlying database platform supports schemas, the current schema is used.
*
* Returns an empty value if the table does not exist.
*
* @param non-empty-string $tableName
* @param ?non-empty-string $schemaName
*
* @return list
*
* @throws Exception
*/
public function introspectTableIndexesByUnquotedName(string $tableName, ?string $schemaName = null): array
{
return $this->introspectTableIndexes(
OptionallyQualifiedName::unquoted($tableName, $schemaName),
);
}
/**
* Introspects the indexes of the table with the given quoted name and schema name and returns their definitions. If
* the schema name is omitted and the underlying database platform supports schemas, the current schema is used.
*
* Returns an empty value if the table does not exist.
*
* @param non-empty-string $tableName
* @param ?non-empty-string $schemaName
*
* @return list
*
* @throws Exception
*/
public function introspectTableIndexesByQuotedName(string $tableName, ?string $schemaName = null): array
{
return $this->introspectTableIndexes(
OptionallyQualifiedName::quoted($tableName, $schemaName),
);
}
/**
* Introspects the primary key constraint of a given table and returns its definition. If the name is unqualified,
* and the underlying database platform supports schemas, the current schema is used.
*
* Returns null if the table does not exist or does not have a primary key constraint.
*
* @throws Exception
*/
public function introspectTablePrimaryKeyConstraint(OptionallyQualifiedName $tableName): ?PrimaryKeyConstraint
{
return $this->introspectTableObjects(
$tableName,
static function (
SchemaProvider $schemaProvider,
?string $schemaName,
string $tableName,
): ?PrimaryKeyConstraint {
return $schemaProvider->getPrimaryKeyConstraintForTable($schemaName, $tableName);
},
);
}
/**
* Introspects the foreign key constraints of a given table and returns their definitions. If the name is
* unqualified, and the underlying database platform supports schemas, the current schema is used.
*
* Returns an empty value if the table does not exist.
*
* @return list
*
* @throws Exception
*/
public function introspectTableForeignKeyConstraints(OptionallyQualifiedName $tableName): array
{
return $this->introspectTableObjects(
$tableName,
static function (SchemaProvider $schemaProvider, ?string $schemaName, string $tableName): array {
return $schemaProvider->getForeignKeyConstraintsForTable($schemaName, $tableName);
},
);
}
/**
* Introspects the foreign key constraints of the table with the given unquoted name and schema name and returns
* their definitions. If the name is unqualified, and the underlying database platform supports schemas, the current
* schema is used.
*
* Returns an empty value if the table does not exist.
*
* @param non-empty-string $tableName
* @param ?non-empty-string $schemaName
*
* @return list
*
* @throws Exception
*/
public function introspectTableForeignKeyConstraintsByUnquotedName(
string $tableName,
?string $schemaName = null,
): array {
return $this->introspectTableForeignKeyConstraints(
OptionallyQualifiedName::unquoted($tableName, $schemaName),
);
}
/**
* Introspects the foreign key constraints of the table with the given quoted name and schema name and returns their
* definitions. If the name is unqualified, and the underlying database platform supports schemas, the current
* schema is used.
*
* Returns an empty value if the table does not exist.
*
* @param non-empty-string $tableName
* @param ?non-empty-string $schemaName
*
* @return list
*
* @throws Exception
*/
public function introspectTableForeignKeyConstraintsByQuotedName(
string $tableName,
?string $schemaName = null,
): array {
return $this->introspectTableForeignKeyConstraints(
OptionallyQualifiedName::quoted($tableName, $schemaName),
);
}
/**
* @param callable(SchemaProvider, ?non-empty-string, non-empty-string): R $function
*
* @return R
*
* @throws Exception
*
* @template R
*/
private function introspectTableObjects(OptionallyQualifiedName $tableName, callable $function)
{
$folding = $this->platform->getUnquotedIdentifierFolding();
$qualifier = $tableName->getQualifier();
if ($qualifier !== null) {
$schemaName = $qualifier->toNormalizedValue($folding);
} else {
$schemaName = $this->getCurrentSchemaName();
}
return $function(
$this->createSchemaProvider(),
$schemaName,
$tableName->getUnqualifiedName()->toNormalizedValue($folding),
);
}
/**
* @return ?array
*
* @throws Exception
*/
private function introspectTableOptions(OptionallyQualifiedName $tableName): ?array
{
$folding = $this->platform->getUnquotedIdentifierFolding();
$qualifier = $tableName->getQualifier();
if ($qualifier !== null) {
$schemaName = $qualifier->toNormalizedValue($folding);
} else {
$schemaName = $this->getCurrentSchemaName();
}
return $this->createSchemaProvider()->getOptionsForTable(
$schemaName,
$tableName->getUnqualifiedName()->toNormalizedValue($folding),
);
}
/**
* Introspects the views in the current database and returns their definitions.
*
* @return list
*
* @throws Exception
*/
public function introspectViews(): array
{
return $this->createSchemaProvider()->getAllViews();
}
/**
* Introspects the sequences in the current database and returns their definitions.
*
* @return list
*
* @throws Exception
*/
public function introspectSequences(): array
{
return $this->filterAssetNames(
$this->createSchemaProvider()->getAllSequences(),
);
}
/** @throws Exception */
private function createSchemaProvider(): IntrospectingSchemaProvider
{
return new IntrospectingSchemaProvider(
$this->platform->createMetadataProvider($this->connection),
$this->getCurrentSchemaName(),
$this->createSchemaConfig()->toTableConfiguration(),
);
}
/* drop*() Methods */
/**
* Drops a database.
*
* NOTE: You can not drop the database this SchemaManager is currently connected to.
*
* @throws Exception
*/
public function dropDatabase(string $database): void
{
$this->connection->executeStatement(
$this->platform->getDropDatabaseSQL($database),
);
}
/**
* Drops a schema.
*
* @throws Exception
*/
public function dropSchema(string $schemaName): void
{
$this->connection->executeStatement(
$this->platform->getDropSchemaSQL($schemaName),
);
}
/**
* Drops the given table.
*
* @throws Exception
*/
public function dropTable(string $name): void
{
$this->connection->executeStatement(
$this->platform->getDropTableSQL($name),
);
}
/**
* Drops the index from the given table.
*
* @throws Exception
*/
public function dropIndex(string $index, string $table): void
{
$this->connection->executeStatement(
$this->platform->getDropIndexSQL($index, $table),
);
}
/**
* Drops a foreign key from a table.
*
* @throws Exception
*/
public function dropForeignKey(string $name, string $table): void
{
$this->connection->executeStatement(
$this->platform->getDropForeignKeySQL($name, $table),
);
}
/**
* Drops a sequence with a given name.
*
* @throws Exception
*/
public function dropSequence(string $name): void
{
$this->connection->executeStatement(
$this->platform->getDropSequenceSQL($name),
);
}
/**
* Drops the unique constraint from the given table.
*
* @throws Exception
*/
public function dropUniqueConstraint(string $name, string $tableName): void
{
$this->connection->executeStatement(
$this->platform->getDropUniqueConstraintSQL($name, $tableName),
);
}
/**
* Drops a view.
*
* @throws Exception
*/
public function dropView(string $name): void
{
$this->connection->executeStatement(
$this->platform->getDropViewSQL($name),
);
}
/* create*() Methods */
/** @throws Exception */
public function createSchemaObjects(Schema $schema): void
{
$this->executeStatements($schema->toSql($this->platform));
}
/**
* Creates a new database.
*
* @throws Exception
*/
public function createDatabase(string $database): void
{
$this->connection->executeStatement(
$this->platform->getCreateDatabaseSQL($database),
);
}
/**
* Creates a new table.
*
* @throws Exception
*/
public function createTable(Table $table): void
{
$this->executeStatements($this->platform->getCreateTableSQL($table));
}
/**
* Creates a new sequence.
*
* @throws Exception
*/
public function createSequence(Sequence $sequence): void
{
$this->connection->executeStatement(
$this->platform->getCreateSequenceSQL($sequence),
);
}
/**
* Creates a new index on a table.
*
* @param string $table The name of the table on which the index is to be created.
*
* @throws Exception
*/
public function createIndex(Index $index, string $table): void
{
$this->connection->executeStatement(
$this->platform->getCreateIndexSQL($index, $table),
);
}
/**
* Creates a new foreign key.
*
* @param ForeignKeyConstraint $foreignKey The ForeignKey instance.
* @param string $table The name of the table on which the foreign key is to be created.
*
* @throws Exception
*/
public function createForeignKey(ForeignKeyConstraint $foreignKey, string $table): void
{
$this->connection->executeStatement(
$this->platform->getCreateForeignKeySQL($foreignKey, $table),
);
}
/**
* Creates a unique constraint on a table.
*
* @throws Exception
*/
public function createUniqueConstraint(UniqueConstraint $uniqueConstraint, string $tableName): void
{
$this->connection->executeStatement(
$this->platform->getCreateUniqueConstraintSQL($uniqueConstraint, $tableName),
);
}
/**
* Creates a new view.
*
* @throws Exception
*/
public function createView(View $view): void
{
$this->connection->executeStatement(
$this->platform->getCreateViewSQL(
$view->getQuotedName($this->platform),
$view->getSql(),
),
);
}
/** @throws Exception */
public function dropSchemaObjects(Schema $schema): void
{
$this->executeStatements($schema->toDropSql($this->platform));
}
/**
* Alters an existing schema.
*
* @throws Exception
*/
public function alterSchema(SchemaDiff $schemaDiff): void
{
$this->executeStatements($this->platform->getAlterSchemaSQL($schemaDiff));
}
/**
* Migrates an existing schema to a new schema.
*
* @throws Exception
*/
public function migrateSchema(Schema $newSchema): void
{
$schemaDiff = $this->createComparator()
->compareSchemas($this->introspectSchema(), $newSchema);
$this->alterSchema($schemaDiff);
}
/* alterTable() Methods */
/**
* Alters an existing tables schema.
*
* @throws Exception
*/
public function alterTable(TableDiff $tableDiff): void
{
$this->executeStatements($this->platform->getAlterTableSQL($tableDiff));
}
/**
* Renames a given table to another name.
*
* @throws Exception
*/
public function renameTable(string $name, string $newName): void
{
$this->connection->executeStatement(
$this->platform->getRenameTableSQL($name, $newName),
);
}
/**
* Methods for filtering return values of list*() methods to convert
* the native DBMS data definition to a portable Doctrine definition
*/
/** @param array $database */
protected function _getPortableDatabaseDefinition(array $database): string
{
throw NotSupported::new(__METHOD__);
}
/** @param array $sequence */
protected function _getPortableSequenceDefinition(array $sequence): Sequence
{
throw NotSupported::new(__METHOD__);
}
/**
* Independent of the database the keys of the column list result are lowercased.
*
* The name of the created column instance however is kept in its case.
*
* @param array> $rows
*
* @return array
*
* @throws TypesException
*/
protected function _getPortableTableColumnList(string $table, string $database, array $rows): array
{
$list = [];
foreach ($rows as $row) {
$column = $this->_getPortableTableColumnDefinition($row);
$name = strtolower($column->getQuotedName($this->platform));
$list[$name] = $column;
}
return $list;
}
/**
* Gets Table Column Definition.
*
* @param array $tableColumn
*
* @throws TypesException
*/
abstract protected function _getPortableTableColumnDefinition(array $tableColumn): Column;
/**
* Aggregates and groups the index results according to the required data result.
*
* @param array> $rows
*
* @return array
*/
protected function _getPortableTableIndexesList(array $rows, string $tableName): array
{
$result = [];
foreach ($rows as $row) {
$indexName = $keyName = $row['key_name'];
if ($row['primary']) {
$keyName = 'primary';
}
$keyName = strtolower($keyName);
if (! isset($result[$keyName])) {
$options = [
'lengths' => [],
];
if (isset($row['where'])) {
$options['where'] = $row['where'];
}
$result[$keyName] = [
'name' => $indexName,
'columns' => [],
'unique' => ! $row['non_unique'],
'primary' => $row['primary'],
'flags' => $row['flags'] ?? [],
'options' => $options,
];
}
$result[$keyName]['columns'][] = $row['column_name'];
$result[$keyName]['options']['lengths'][] = $row['length'] ?? null;
}
$indexes = [];
foreach ($result as $indexKey => $data) {
$indexes[$indexKey] = new Index(
$data['name'],
$data['columns'],
$data['unique'],
$data['primary'],
$data['flags'],
$data['options'],
);
}
return $indexes;
}
/**
* @deprecated Use the schema name and the unqualified table name separately instead.
*
* @param array $table
*
* @return non-empty-string
*/
abstract protected function _getPortableTableDefinition(array $table): string;
/** @param array $view */
abstract protected function _getPortableViewDefinition(array $view): View;
/**
* @param array> $rows
*
* @return array
*/
protected function _getPortableTableForeignKeysList(array $rows): array
{
$list = [];
foreach ($rows as $value) {
$list[] = $this->_getPortableTableForeignKeyDefinition($value);
}
return $list;
}
/** @param array $tableForeignKey */
abstract protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey): ForeignKeyConstraint;
/**
* @param array $sql
*
* @throws Exception
*/
private function executeStatements(array $sql): void
{
foreach ($sql as $query) {
$this->connection->executeStatement($query);
}
}
/**
* Returns a {@see Schema} instance representing the current database schema.
*
* @throws Exception
*/
public function introspectSchema(): Schema
{
$schemaNames = [];
if ($this->platform->supportsSchemas()) {
$schemaNames = $this->listSchemaNames();
}
$sequences = [];
if ($this->platform->supportsSequences()) {
$sequences = $this->listSequences();
}
$tables = $this->listTables();
return new Schema($tables, $sequences, $this->createSchemaConfig(), $schemaNames);
}
/**
* Creates the configuration for this schema.
*
* @throws Exception
*/
public function createSchemaConfig(): SchemaConfig
{
$schemaConfig = new SchemaConfig();
$schemaConfig->setMaxIdentifierLength($this->platform->getMaxIdentifierLength());
$schemaConfig->setName($this->getCurrentSchemaName());
$params = $this->connection->getParams();
if (! isset($params['defaultTableOptions'])) {
$params['defaultTableOptions'] = [];
}
if (! isset($params['defaultTableOptions']['charset']) && isset($params['charset'])) {
$params['defaultTableOptions']['charset'] = $params['charset'];
}
$schemaConfig->setDefaultTableOptions($params['defaultTableOptions']);
return $schemaConfig;
}
/**
* @return non-empty-string
*
* @throws Exception
*/
private function getDatabase(string $methodName): string
{
$database = $this->connection->getDatabase();
if ($database === null) {
throw DatabaseRequired::new($methodName);
}
return $database;
}
public function createComparator(/* ComparatorConfig $config = new ComparatorConfig() */): Comparator
{
return new Comparator($this->platform, func_num_args() > 0 ? func_get_arg(0) : new ComparatorConfig());
}
/**
* Groups the rows representing database object elements by table they belong to.
*
* @param list> $rows
*
* @return array>>
*/
private function groupByTable(array $rows): array
{
$data = [];
foreach ($rows as $row) {
$tableName = $this->_getPortableTableDefinition($row);
$data[$tableName][] = $row;
}
/** @phpstan-ignore return.type */
return $data;
}
}