Skip to content

Commit

Permalink
Merge pull request #5576 from morozov/issues/5575-reserved-keyword-ta…
Browse files Browse the repository at this point in the history
…ble-introspection

Rework introspection of table indexes and foreign keys on Postgres
  • Loading branch information
morozov committed Aug 16, 2022
2 parents 36b9ffc + d0d7f63 commit 94e0164
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 40 deletions.
9 changes: 6 additions & 3 deletions src/Schema/AbstractSchemaManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -289,11 +289,12 @@ public function listTableIndexes($table)
protected function doListTableIndexes($table): array
{
$database = $this->getDatabase(__METHOD__);
$table = $this->normalizeName($table);

return $this->_getPortableTableIndexesList(
$this->selectIndexColumns(
$database,
$this->normalizeName($table)
$table
)->fetchAllAssociative(),
$table
);
Expand Down Expand Up @@ -381,7 +382,7 @@ protected function filterAssetNames($assetNames)
/**
* Lists the tables for this connection.
*
* @return Table[]
* @return list<Table>
*
* @throws Exception
*/
Expand Down Expand Up @@ -489,7 +490,9 @@ protected function doListTableDetails($name): Table
*/
protected function normalizeName(string $name): string
{
return $name;
$identifier = new Identifier($name);

return $identifier->getName();
}

/**
Expand Down
28 changes: 15 additions & 13 deletions src/Schema/PostgreSQLSchemaManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -653,19 +653,21 @@ protected function selectIndexColumns(string $databaseName, ?string $tableName =
$sql = 'SELECT';

if ($tableName === null) {
$sql .= ' pg_index.indrelid::REGCLASS AS table_name, tn.nspname AS schema_name,';
$sql .= ' tc.relname AS table_name, tn.nspname AS schema_name,';
}

$sql .= <<<'SQL'
quote_ident(relname) AS relname,
pg_index.indisunique,
pg_index.indisprimary,
pg_index.indkey,
pg_index.indrelid,
pg_get_expr(indpred, indrelid) AS where
FROM pg_index, pg_class
JOIN pg_namespace tn ON tn.oid = pg_class.relnamespace
WHERE pg_class.oid IN (
quote_ident(ic.relname) AS relname,
i.indisunique,
i.indisprimary,
i.indkey,
i.indrelid,
pg_get_expr(indpred, indrelid) AS "where"
FROM pg_index i
JOIN pg_class AS tc ON tc.oid = i.indrelid
JOIN pg_namespace tn ON tn.oid = tc.relnamespace
JOIN pg_class AS ic ON ic.oid = i.indexrelid
WHERE ic.oid IN (
SELECT indexrelid
FROM pg_index i, pg_class c, pg_namespace n
SQL;
Expand All @@ -675,7 +677,7 @@ protected function selectIndexColumns(string $databaseName, ?string $tableName =
'c.relnamespace = n.oid',
], $this->buildQueryConditions($tableName));

$sql .= ' WHERE ' . implode(' AND ', $conditions) . ') AND pg_index.indexrelid = pg_class.oid';
$sql .= ' WHERE ' . implode(' AND ', $conditions) . ')';

return $this->_conn->executeQuery($sql);
}
Expand All @@ -685,7 +687,7 @@ protected function selectForeignKeyColumns(string $databaseName, ?string $tableN
$sql = 'SELECT';

if ($tableName === null) {
$sql .= ' r.conrelid :: REGCLASS as table_name, tn.nspname AS schema_name,';
$sql .= ' tc.relname AS table_name, tn.nspname AS schema_name,';
}

$sql .= <<<'SQL'
Expand All @@ -697,7 +699,7 @@ protected function selectForeignKeyColumns(string $databaseName, ?string $tableN
WHERE r.conrelid IN
(
SELECT c.oid
FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n
FROM pg_class c, pg_namespace n
SQL;

$conditions = array_merge(['n.oid = c.relnamespace'], $this->buildQueryConditions($tableName));
Expand Down
4 changes: 3 additions & 1 deletion src/Schema/SqliteSchemaManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,9 @@ public function dropForeignKey($foreignKey, $table)
*/
public function listTableForeignKeys($table, $database = null)
{
$columns = $this->selectForeignKeyColumns('', $this->normalizeName($table))
$table = $this->normalizeName($table);

$columns = $this->selectForeignKeyColumns('', $table)
->fetchAllAssociative();

if (count($columns) > 0) {
Expand Down
25 changes: 2 additions & 23 deletions tests/Functional/Schema/PostgreSQLSchemaManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -304,34 +304,13 @@ protected function assertVarBinaryColumnIsValid(Table $table, string $columnName
self::assertInstanceOf(BlobType::class, $table->getColumn($columnName)->getType());
}

public function testListQuotedTable(): void
{
$offlineTable = new Table('user');
$offlineTable->addColumn('id', 'integer');
$offlineTable->addColumn('username', 'string');
$offlineTable->addColumn('fk', 'integer');
$offlineTable->setPrimaryKey(['id']);
$offlineTable->addForeignKeyConstraint($offlineTable, ['fk'], ['id']);

$this->dropAndCreateTable($offlineTable);

$onlineTable = $this->schemaManager->listTableDetails('"user"');

$comparator = new Comparator();

self::assertFalse($comparator->diffTable($offlineTable, $onlineTable));
}

public function testListTableDetailsWhenCurrentSchemaNameQuoted(): void
{
$this->connection->executeStatement('CREATE SCHEMA "001_test"');
$this->connection->executeStatement('SET search_path TO "001_test"');
$this->markConnectionNotReusable();

try {
$this->testListQuotedTable();
} finally {
$this->connection->close();
}
$this->testIntrospectReservedKeywordTableViaListTableDetails();
}

public function testListTablesExcludesViews(): void
Expand Down
47 changes: 47 additions & 0 deletions tests/Functional/Schema/SchemaManagerFunctionalTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Doctrine\DBAL\Schema\Comparator;
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\SchemaDiff;
use Doctrine\DBAL\Schema\Sequence;
use Doctrine\DBAL\Schema\Table;
Expand Down Expand Up @@ -1668,6 +1669,52 @@ public function testCreatedCompositeForeignKeyOrderIsCorrectAfterCreation(): voi
self::assertSame($foreignColumns, array_map('strtolower', $foreignKey->getForeignColumns()));
}

public function testIntrospectReservedKeywordTableViaListTableDetails(): void
{
$this->createReservedKeywordTables();

$user = $this->schemaManager->listTableDetails('"user"');
self::assertCount(2, $user->getColumns());
self::assertCount(2, $user->getIndexes());
self::assertCount(1, $user->getForeignKeys());
}

public function testIntrospectReservedKeywordTableViaListTables(): void
{
$this->createReservedKeywordTables();

$tables = $this->schemaManager->listTables();

$user = $this->findTableByName($tables, 'user');
self::assertNotNull($user);
self::assertCount(2, $user->getColumns());
self::assertCount(2, $user->getIndexes());
self::assertCount(1, $user->getForeignKeys());
}

private function createReservedKeywordTables(): void
{
$platform = $this->connection->getDatabasePlatform();

$this->dropTableIfExists($platform->quoteIdentifier('user'));
$this->dropTableIfExists($platform->quoteIdentifier('group'));

$schema = new Schema();

$user = $schema->createTable('user');
$user->addColumn('id', 'integer');
$user->addColumn('group_id', 'integer');
$user->setPrimaryKey(['id']);
$user->addForeignKeyConstraint('group', ['group_id'], ['id']);

$group = $schema->createTable('group');
$group->addColumn('id', 'integer');
$group->setPrimaryKey(['id']);

$schemaManager = $this->connection->createSchemaManager();
$schemaManager->createSchemaObjects($schema);
}

/**
* @param list<Table> $tables
*/
Expand Down

0 comments on commit 94e0164

Please sign in to comment.