Skip to content

Commit

Permalink
Merge pull request #6753 from morozov/non-default-schema-table-commen…
Browse files Browse the repository at this point in the history
…t-introspection

Fix introspection of table options in non-default schemas
  • Loading branch information
morozov authored Feb 2, 2025
2 parents da19422 + bf5168f commit a1932ce
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 53 deletions.
9 changes: 8 additions & 1 deletion src/Platforms/SQLServerPlatform.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
use function preg_match;
use function preg_match_all;
use function sprintf;
use function str_contains;
use function str_ends_with;
use function str_replace;
use function str_starts_with;
Expand Down Expand Up @@ -1777,11 +1778,17 @@ private function generateIdentifierName($identifier): string

protected function getCommentOnTableSQL(string $tableName, ?string $comment): string
{
if (str_contains($tableName, '.')) {
[$schemaName, $tableName] = explode('.', $tableName);
} else {
$schemaName = 'dbo';
}

return $this->getAddExtendedPropertySQL(
'MS_Description',
$comment,
'SCHEMA',
$this->quoteStringLiteral('dbo'),
$this->quoteStringLiteral($schemaName),
'TABLE',
$this->quoteStringLiteral($this->unquoteSingleIdentifier($tableName)),
);
Expand Down
10 changes: 8 additions & 2 deletions src/Schema/PostgreSQLSchemaManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,8 @@ protected function selectForeignKeyColumns(string $databaseName, ?string $tableN
protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array
{
$sql = <<<'SQL'
SELECT c.relname,
SELECT n.nspname AS schema_name,
c.relname AS table_name,
CASE c.relpersistence WHEN 'u' THEN true ELSE false END as unlogged,
obj_description(c.oid, 'pg_class') AS comment
FROM pg_class c
Expand All @@ -745,7 +746,12 @@ protected function fetchTableOptionsByTable(string $databaseName, ?string $table

$sql .= ' WHERE ' . implode(' AND ', $conditions);

return $this->_conn->fetchAllAssociativeIndexed($sql);
$tableOptions = [];
foreach ($this->_conn->iterateAssociative($sql) as $row) {
$tableOptions[$this->_getPortableTableDefinition($row)] = $row;
}

return $tableOptions;
}

/**
Expand Down
26 changes: 15 additions & 11 deletions src/Schema/SQLServerSchemaManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,15 @@ protected function _getPortableTableForeignKeysList($tableForeignKeys)
$name = $tableForeignKey['ForeignKey'];

if (! isset($foreignKeys[$name])) {
$referencedTableName = $tableForeignKey['ReferenceTableName'];

if ($tableForeignKey['ReferenceSchemaName'] !== 'dbo') {
$referencedTableName = $tableForeignKey['ReferenceSchemaName'] . '.' . $referencedTableName;
}

$foreignKeys[$name] = [
'local_columns' => [$tableForeignKey['ColumnName']],
'foreign_table' => $tableForeignKey['ReferenceTableName'],
'foreign_table' => $referencedTableName,
'foreign_columns' => [$tableForeignKey['ReferenceColumnName']],
'name' => $name,
'options' => [
Expand Down Expand Up @@ -556,31 +562,29 @@ protected function fetchTableOptionsByTable(string $databaseName, ?string $table
{
$sql = <<<'SQL'
SELECT
tbl.name,
scm.name AS schema_name,
tbl.name AS table_name,
p.value AS [table_comment]
FROM
sys.tables AS tbl
JOIN sys.schemas AS scm
ON tbl.schema_id = scm.schema_id
INNER JOIN sys.extended_properties AS p ON p.major_id=tbl.object_id AND p.minor_id=0 AND p.class=1
SQL;

$conditions = ["SCHEMA_NAME(tbl.schema_id) = N'dbo'", "p.name = N'MS_Description'"];
$params = [];
$conditions = ["p.name = N'MS_Description'"];

if ($tableName !== null) {
$conditions[] = "tbl.name = N'" . $tableName . "'";
$conditions[] = $this->getTableWhereClause($tableName, 'scm.name', 'tbl.name');
}

$sql .= ' WHERE ' . implode(' AND ', $conditions);

/** @var array<string,array<string,mixed>> $metadata */
$metadata = $this->_conn->executeQuery($sql, $params)
->fetchAllAssociativeIndexed();

$tableOptions = [];
foreach ($metadata as $table => $data) {
foreach ($this->_conn->iterateAssociative($sql) as $data) {
$data = array_change_key_case($data, CASE_LOWER);

$tableOptions[$table] = [
$tableOptions[$this->_getPortableTableDefinition($data)] = [
'comment' => $data['table_comment'],
];
}
Expand Down
39 changes: 0 additions & 39 deletions tests/Functional/Schema/PostgreSQLSchemaManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@

use function array_map;
use function array_merge;
use function array_pop;
use function array_unshift;
use function assert;
use function count;
Expand Down Expand Up @@ -163,44 +162,6 @@ public function testAlterTableAutoIncrementDrop(callable $comparatorFactory): vo
self::assertFalse($tableFinal->getColumn('id')->getAutoincrement());
}

public function testTableWithSchema(): void
{
$this->connection->executeStatement('CREATE SCHEMA nested');

$nestedRelatedTable = new Table('nested.schemarelated');
$column = $nestedRelatedTable->addColumn('id', Types::INTEGER);
$column->setAutoincrement(true);
$nestedRelatedTable->setPrimaryKey(['id']);

$nestedSchemaTable = new Table('nested.schematable');
$column = $nestedSchemaTable->addColumn('id', Types::INTEGER);
$column->setAutoincrement(true);
$nestedSchemaTable->setPrimaryKey(['id']);
$nestedSchemaTable->addForeignKeyConstraint($nestedRelatedTable, ['id'], ['id']);

$this->schemaManager->createTable($nestedRelatedTable);
$this->schemaManager->createTable($nestedSchemaTable);

$tableNames = $this->schemaManager->listTableNames();
self::assertContains('nested.schematable', $tableNames);

$tables = $this->schemaManager->listTables();
self::assertNotNull($this->findTableByName($tables, 'nested.schematable'));

$nestedSchemaTable = $this->schemaManager->introspectTable('nested.schematable');
self::assertTrue($nestedSchemaTable->hasColumn('id'));

$primaryKey = $nestedSchemaTable->getPrimaryKey();

self::assertNotNull($primaryKey);
self::assertEquals(['id'], $primaryKey->getColumns());

$relatedFks = $nestedSchemaTable->getForeignKeys();
self::assertCount(1, $relatedFks);
$relatedFk = array_pop($relatedFks);
self::assertEquals('nested.schemarelated', $relatedFk->getForeignTableName());
}

public function testListSameTableNameColumnsWithDifferentSchema(): void
{
$this->connection->executeStatement('CREATE SCHEMA another');
Expand Down
43 changes: 43 additions & 0 deletions tests/Functional/Schema/SchemaManagerFunctionalTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -1905,6 +1905,49 @@ protected function findTableByName(array $tables, string $name): ?Table

return null;
}

public function testTableWithSchema(): void
{
if (! $this->connection->getDatabasePlatform()->supportsSchemas()) {
self::markTestSkipped('The currently used database platform does not support schemas.');
}

$this->connection->executeStatement('CREATE SCHEMA nested');

$nestedRelatedTable = new Table('nested.schemarelated');
$column = $nestedRelatedTable->addColumn('id', Types::INTEGER);
$column->setAutoincrement(true);
$nestedRelatedTable->setPrimaryKey(['id']);

$nestedSchemaTable = new Table('nested.schematable');
$column = $nestedSchemaTable->addColumn('id', Types::INTEGER);
$column->setAutoincrement(true);
$nestedSchemaTable->setPrimaryKey(['id']);
$nestedSchemaTable->addForeignKeyConstraint($nestedRelatedTable->getName(), ['id'], ['id']);
$nestedSchemaTable->setComment('This is a comment');

$this->schemaManager->createTable($nestedRelatedTable);
$this->schemaManager->createTable($nestedSchemaTable);

$tableNames = $this->schemaManager->listTableNames();
self::assertContains('nested.schematable', $tableNames);

$tables = $this->schemaManager->listTables();
self::assertNotNull($this->findTableByName($tables, 'nested.schematable'));

$nestedSchemaTable = $this->schemaManager->introspectTable('nested.schematable');
self::assertTrue($nestedSchemaTable->hasColumn('id'));

$primaryKey = $nestedSchemaTable->getPrimaryKey();
self::assertNotNull($primaryKey);
self::assertEquals(['id'], $primaryKey->getColumns());

$relatedFks = array_values($nestedSchemaTable->getForeignKeys());
self::assertCount(1, $relatedFks);
$relatedFk = $relatedFks[0];
self::assertEquals('nested.schemarelated', $relatedFk->getForeignTableName());
self::assertEquals('This is a comment', $nestedSchemaTable->getComment());
}
}

interface ListTableColumnsDispatchEventListener
Expand Down

0 comments on commit a1932ce

Please sign in to comment.