From 784f890ee34792de5e315fe6e462e92d79f51af9 Mon Sep 17 00:00:00 2001 From: Pierre Rineau Date: Tue, 12 Mar 2024 16:56:55 +0100 Subject: [PATCH] schema manager - better schema handling --- src/Platform/Schema/MySQLSchemaManager.php | 22 ++--- .../Schema/PostgreSQLSchemaManager.php | 22 ++--- src/Platform/Schema/SQLiteSchemaManager.php | 14 +-- src/Schema/Read/ObjectId.php | 2 +- src/Schema/SchemaManager.php | 98 ++++++++++++++++--- 5 files changed, 112 insertions(+), 46 deletions(-) diff --git a/src/Platform/Schema/MySQLSchemaManager.php b/src/Platform/Schema/MySQLSchemaManager.php index 4444bdc..7ea71a1 100644 --- a/src/Platform/Schema/MySQLSchemaManager.php +++ b/src/Platform/Schema/MySQLSchemaManager.php @@ -67,7 +67,7 @@ public function listSchemas(string $database): array } #[\Override] - public function listTables(string $database, string $schema = 'public'): array + protected function doListTables(string $database, string $schema): array { if ('public' !== $schema) { return []; @@ -92,7 +92,7 @@ public function listTables(string $database, string $schema = 'public'): array } #[\Override] - public function tableExists(string $database, string $name, string $schema = 'public'): bool + protected function doTableExists(string $database, string $schema, string $name): bool { if ('public' !== $schema) { return false; @@ -118,7 +118,7 @@ public function tableExists(string $database, string $name, string $schema = 'pu } #[\Override] - protected function getTableComment(string $database, string $name, string $schema = 'public'): ?string + protected function getTableComment(string $database, string $schema, string $name): ?string { return $this ->queryExecutor @@ -139,7 +139,7 @@ protected function getTableComment(string $database, string $name, string $schem } #[\Override] - protected function getTableColumns(string $database, string $name, string $schema = 'public'): array + protected function getTableColumns(string $database, string $schema, string $name): array { /* $defaultCollation = $this @@ -204,7 +204,7 @@ protected function getTableColumns(string $database, string $name, string $schem } #[\Override] - protected function getTablePrimaryKey(string $database, string $name, string $schema = 'public'): ?Key + protected function getTablePrimaryKey(string $database, string $schema, string $name): ?Key { $primaryKeyColumns = $this ->queryExecutor @@ -240,19 +240,19 @@ protected function getTablePrimaryKey(string $database, string $name, string $sc } #[\Override] - protected function getTableForeignKeys(string $database, string $name, string $schema = 'public'): array + protected function getTableForeignKeys(string $database, string $schema, string $name): array { return \array_values(\array_filter( - $this->getAllTableKeysInfo($database, $name, $schema), + $this->getAllTableKeysInfo($database, $schema, $name), fn (ForeignKey $key) => ($key->getTable() === $name && $key->getSchema() === $schema), )); } #[\Override] - protected function getTableReverseForeignKeys(string $database, string $name, string $schema = 'public'): array + protected function getTableReverseForeignKeys(string $database, string $schema, string $name): array { return \array_values(\array_filter( - $this->getAllTableKeysInfo($database, $name, $schema), + $this->getAllTableKeysInfo($database, $schema, $name), fn (ForeignKey $key) => ($key->getForeignTable() === $name && $key->getForeignSchema() === $schema), )); } @@ -268,7 +268,7 @@ protected function getTableReverseForeignKeys(string $database, string $name, st * * @return ForeignKey[] */ - private function getAllTableKeysInfo(string $database, string $name, string $schema = 'public'): array + private function getAllTableKeysInfo(string $database, string $schema, string $name): array { $ret = []; @@ -333,7 +333,7 @@ protected function doWriteColumnCollation(string $collation): Expression if ('binary' === $collation) { $characterSet = 'binary'; } else if (\str_contains($collation, '_')) { - list ($characterSet,) = \explode('_', $collation, 2); + list($characterSet,) = \explode('_', $collation, 2); } else { $characterSet = $collation; $collation .= '_general_ci'; diff --git a/src/Platform/Schema/PostgreSQLSchemaManager.php b/src/Platform/Schema/PostgreSQLSchemaManager.php index d5282d2..aef729b 100644 --- a/src/Platform/Schema/PostgreSQLSchemaManager.php +++ b/src/Platform/Schema/PostgreSQLSchemaManager.php @@ -65,7 +65,7 @@ public function listSchemas(string $database): array } #[\Override] - public function listTables(string $database, string $schema = 'public'): array + protected function doListTables(string $database, string $schema): array { return $this ->queryExecutor @@ -89,7 +89,7 @@ public function listTables(string $database, string $schema = 'public'): array } #[\Override] - public function tableExists(string $database, string $name, string $schema = 'public'): bool + protected function doTableExists(string $database, string $schema, string $name): bool { return (bool) $this ->queryExecutor @@ -111,7 +111,7 @@ public function tableExists(string $database, string $name, string $schema = 'pu } #[\Override] - protected function getTableComment(string $database, string $name, string $schema = 'public'): ?string + protected function getTableComment(string $database, string $schema, string $name): ?string { return $this ->queryExecutor @@ -134,7 +134,7 @@ protected function getTableComment(string $database, string $name, string $schem } #[\Override] - protected function getTableColumns(string $database, string $name, string $schema = 'public'): array + protected function getTableColumns(string $database, string $schema, string $name): array { $defaultCollation = $this ->queryExecutor @@ -191,9 +191,9 @@ protected function getTableColumns(string $database, string $name, string $schem } #[\Override] - protected function getTablePrimaryKey(string $database, string $name, string $schema = 'public'): ?Key + protected function getTablePrimaryKey(string $database, string $schema, string $name): ?Key { - $result = $this->getAllTableKeysInfo($database, $name, $schema); + $result = $this->getAllTableKeysInfo($database, $schema, $name); while ($row = $result->fetchRow()) { if ($row->get('type') === 'p') { @@ -213,10 +213,10 @@ protected function getTablePrimaryKey(string $database, string $name, string $sc } #[\Override] - protected function getTableForeignKeys(string $database, string $name, string $schema = 'public'): array + protected function getTableForeignKeys(string $database, string $schema, string $name): array { $ret = []; - $result = $this->getAllTableKeysInfo($database, $name, $schema); + $result = $this->getAllTableKeysInfo($database, $schema, $name); while ($row = $result->fetchRow()) { if ($row->get('type') === 'f' && $row->get('table_source', 'string') === $name) { @@ -239,10 +239,10 @@ protected function getTableForeignKeys(string $database, string $name, string $s } #[\Override] - protected function getTableReverseForeignKeys(string $database, string $name, string $schema = 'public'): array + protected function getTableReverseForeignKeys(string $database, string $schema, string $name): array { $ret = []; - $result = $this->getAllTableKeysInfo($database, $name, $schema); + $result = $this->getAllTableKeysInfo($database, $schema, $name); while ($row = $result->fetchRow()) { if ($row->get('type') === 'f' && $row->get('table_source', 'string') !== $name) { @@ -273,7 +273,7 @@ protected function getTableReverseForeignKeys(string $database, string $name, st * Since this is querying the catalog, it will be fast no matter how * much result this yields. */ - private function getAllTableKeysInfo(string $database, string $name, string $schema = 'public'): Result + private function getAllTableKeysInfo(string $database, string $schema, string $name): Result { return $this ->queryExecutor diff --git a/src/Platform/Schema/SQLiteSchemaManager.php b/src/Platform/Schema/SQLiteSchemaManager.php index eb6553e..c829933 100644 --- a/src/Platform/Schema/SQLiteSchemaManager.php +++ b/src/Platform/Schema/SQLiteSchemaManager.php @@ -60,7 +60,7 @@ public function listSchemas(string $database): array } #[\Override] - public function listTables(string $database, string $schema = 'public'): array + protected function doListTables(string $database, string $schema): array { if ('public' !== $schema) { return []; @@ -92,13 +92,13 @@ public function listTables(string $database, string $schema = 'public'): array } #[\Override] - public function tableExists(string $database, string $name, string $schema = 'public'): bool + protected function doTableExists(string $database, string $schema, string $name): bool { return \in_array($name, $this->listTables($database, $schema)); } #[\Override] - protected function getTableComment(string $database, string $name, string $schema = 'public'): ?string + protected function getTableComment(string $database, string $schema, string $name): ?string { if ('public' !== $schema) { return null; @@ -109,7 +109,7 @@ protected function getTableComment(string $database, string $name, string $schem } #[\Override] - protected function getTableColumns(string $database, string $name, string $schema = 'public'): array + protected function getTableColumns(string $database, string $schema, string $name): array { if ('public' !== $schema) { return []; @@ -175,7 +175,7 @@ protected function getTableColumns(string $database, string $name, string $schem } #[\Override] - protected function getTablePrimaryKey(string $database, string $name, string $schema = 'public'): ?Key + protected function getTablePrimaryKey(string $database, string $schema, string $name): ?Key { if ('public' !== $schema) { return null; @@ -231,7 +231,7 @@ protected function getTablePrimaryKey(string $database, string $name, string $sc } #[\Override] - protected function getTableForeignKeys(string $database, string $name, string $schema = 'public'): array + protected function getTableForeignKeys(string $database, string $schema, string $name): array { if ('public' !== $schema) { return []; @@ -274,7 +274,7 @@ protected function getTableForeignKeys(string $database, string $name, string $s } #[\Override] - protected function getTableReverseForeignKeys(string $database, string $name, string $schema = 'public'): array + protected function getTableReverseForeignKeys(string $database, string $schema, string $name): array { if ('public' !== $schema) { return []; diff --git a/src/Schema/Read/ObjectId.php b/src/Schema/Read/ObjectId.php index 9c161b0..f74ca29 100644 --- a/src/Schema/Read/ObjectId.php +++ b/src/Schema/Read/ObjectId.php @@ -19,9 +19,9 @@ public function __construct( private readonly string $database, private readonly string $type, private readonly string $name, + private readonly string $schema, /** For a column, for example, the table name. */ private readonly ?string $namespace = null, - private readonly ?string $schema = 'public', ) {} /** diff --git a/src/Schema/SchemaManager.php b/src/Schema/SchemaManager.php index 60e1d60..e67807d 100644 --- a/src/Schema/SchemaManager.php +++ b/src/Schema/SchemaManager.php @@ -77,10 +77,27 @@ abstract class SchemaManager implements LoggerAwareInterface public function __construct( protected readonly QueryBuilder $queryBuilder, protected readonly QueryExecutor $queryExecutor, + protected readonly ?string $defaultSchema = null, ) { $this->setLogger(new NullLogger()); } + /** + * Get default schema configured by user. + */ + public function getDefaultSchema(): string + { + return $this->defaultSchema ?? $this->getVendorDefaultSchema(); + } + + /** + * Get vendor-specific default schema. + */ + protected function getVendorDefaultSchema(): string + { + return 'public'; + } + /** * Get instance query builder. * @@ -147,66 +164,94 @@ public abstract function listSchemas(string $database): array; /** * List all tables in given database and schema. + * + * If no schema given, use the default schema name. */ - public abstract function listTables(string $database, string $schema = 'public'): array; + public function listTables(string $database, ?string $schema = null): array + { + $schema ??= $this->getDefaultSchema(); + + return $this->doListTables($database, $schema); + } /** * Does table exist. + * + * If no schema given, use the default schema name. */ - public abstract function tableExists(string $database, string $name, string $schema = 'public'): bool; + public function tableExists(string $database, string $name, ?string $schema = null): bool + { + list($schema, $name) = $this->detectSchema($name, $schema); + + return $this->doTableExists($database, $schema, $name); + } /** * Get table information. + * + * If no schema given, use the default schema name. */ - public function getTable(string $database, string $name, string $schema = 'public'): Table + public function getTable(string $database, string $name, ?string $schema = null): Table { + list($schema, $name) = $this->detectSchema($name, $schema); + if (!$this->tableExists($database, $name, $schema)) { throw new QueryBuilderError(\sprintf("Table '%s.%s.%s' does not exist", $database, $schema, $name)); } return new Table( - columns: $this->getTableColumns($database, $name, $schema), - comment: $this->getTableComment($database, $name, $schema), + columns: $this->getTableColumns($database, $schema, $name), + comment: $this->getTableComment($database, $schema, $name), database: $database, - foreignKeys: $this->getTableForeignKeys($database, $name, $schema), + foreignKeys: $this->getTableForeignKeys($database, $schema, $name), name: $name, options: [], - primaryKey: $this->getTablePrimaryKey($database, $name, $schema), - reverseForeignKeys: $this->getTableReverseForeignKeys($database, $name, $schema), + primaryKey: $this->getTablePrimaryKey($database, $schema, $name), + reverseForeignKeys: $this->getTableReverseForeignKeys($database, $schema, $name), schema: $schema, ); } + /** + * List all tables in given database and schema. + */ + protected abstract function doListTables(string $database, string $schema): array; + + /** + * Does table exist. + */ + protected abstract function doTableExists(string $database, string $schema, string $name): bool; + /** * Get table comment. */ - protected abstract function getTableComment(string $database, string $name, string $schema = 'public'): ?string; + protected abstract function getTableComment(string $database, string $schema, string $name): ?string; /** * Get table columns. * * @return Column[] */ - protected abstract function getTableColumns(string $database, string $name, string $schema = 'public'): array; + protected abstract function getTableColumns(string $database, string $schema, string $name): array; /** * Get table primary key. */ - protected abstract function getTablePrimaryKey(string $database, string $name, string $schema = 'public'): ?Key; + protected abstract function getTablePrimaryKey(string $database, string $schema, string $name): ?Key; /** * Get table foreign keys. * * @return ForeignKey[] */ - protected abstract function getTableForeignKeys(string $database, string $name, string $schema = 'public'): array; + protected abstract function getTableForeignKeys(string $database, string $schema, string $name): array; /** * Get table reverse foreign keys. * * @return ForeignKey[] */ - protected abstract function getTableReverseForeignKeys(string $database, string $name, string $schema = 'public'): array; + protected abstract function getTableReverseForeignKeys(string $database, string $schema, string $name): array; /** * Start a transaction for schema manipulation. @@ -215,11 +260,11 @@ protected abstract function getTableReverseForeignKeys(string $database, string * If the database vendor doesn't support DDL statements transactions * then no transactions will be done at all. */ - public function modify(string $database, string $schema = 'public'): SchemaTransaction + public function modify(string $database, ?string $schema = null): SchemaTransaction { return new SchemaTransaction( $database, - $schema, + $schema ?? $this->getDefaultSchema(), function (SchemaTransaction $transaction) { $browser = new ChangeLogBrowser(); $browser->addVisitor(new SchemaWriterLogVisitor($this)); @@ -228,6 +273,23 @@ function (SchemaTransaction $transaction) { ); } + /** + * Extract schema name from identifier. + * + * @return string[] + * First value is schema name, second is stripped identifier if it contained schema name. + */ + protected function detectSchema(string $name, ?string $schema): array + { + if (null !== $schema) { + return [$schema, $name]; + } + if (false !== ($index = \strpos($name, '.'))) { + return [\substr($name, 0, $index - 1), \substr($name, $index)]; + } + return [$this->getDefaultSchema(), $name]; + } + /** * Get the expression factory. */ @@ -263,7 +325,11 @@ protected function table(string|AbstractChange $name, ?string $schema = null): E } } - if (!$this->supportsSchema()) { + if ($this->supportsSchema()) { + if (null === $schema) { + list($schema, $name) = $this->detectSchema($name, $schema); + } + } else { $schema = null; }