From 46e5b94ff54c25e514a96bbec95259e4b66e593a Mon Sep 17 00:00:00 2001 From: Allan Simon Date: Thu, 19 Oct 2023 01:40:19 +0200 Subject: [PATCH] fix #6198: stop considering generated column definition as being its default value The `pg_get_expr(adbin, adrelid)` expression in Postgresql's internal table `pg_attrdef` is used to know a column definition for most column, if this returns something, it means this column's default value. So DBAL has been considering has ALWAYS meaning it contains its default. However for generated columns, it contains the value definition and not its default value, so we change the selectTableColumns' query to correctly set the 'default' column to `null` for generated columns It will help setting correctly the column's attributes which in turn will help generate correct schema diff. --- src/Driver/AbstractPostgreSQLDriver.php | 5 ++++ src/Platforms/PostgreSQL120Platform.php | 30 +++++++++++++++++++ src/Platforms/PostgreSQLPlatform.php | 13 ++++++++ src/Schema/PostgreSQLSchemaManager.php | 10 ++----- .../Driver/VersionAwarePlatformDriverTest.php | 2 ++ .../Schema/PostgreSQLSchemaManagerTest.php | 30 +++++++++++++++++++ 6 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 src/Platforms/PostgreSQL120Platform.php diff --git a/src/Driver/AbstractPostgreSQLDriver.php b/src/Driver/AbstractPostgreSQLDriver.php index 099630d3368..b7e93c58bfc 100644 --- a/src/Driver/AbstractPostgreSQLDriver.php +++ b/src/Driver/AbstractPostgreSQLDriver.php @@ -8,6 +8,7 @@ use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\PostgreSQL100Platform; +use Doctrine\DBAL\Platforms\PostgreSQL120Platform; use Doctrine\DBAL\Platforms\PostgreSQL94Platform; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\DBAL\Schema\PostgreSQLSchemaManager; @@ -40,6 +41,10 @@ public function createDatabasePlatformForVersion($version) $patchVersion = $versionParts['patch'] ?? 0; $version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion; + if (version_compare($version, '12.0', '>=')) { + return new PostgreSQL120Platform(); + } + if (version_compare($version, '10.0', '>=')) { return new PostgreSQL100Platform(); } diff --git a/src/Platforms/PostgreSQL120Platform.php b/src/Platforms/PostgreSQL120Platform.php new file mode 100644 index 00000000000..0836a0feb30 --- /dev/null +++ b/src/Platforms/PostgreSQL120Platform.php @@ -0,0 +1,30 @@ +_platform->getDefaultColumnValueSQLSnippet()); $conditions = array_merge([ 'a.attnum > 0', diff --git a/tests/Driver/VersionAwarePlatformDriverTest.php b/tests/Driver/VersionAwarePlatformDriverTest.php index 8a84607f954..2388805112d 100644 --- a/tests/Driver/VersionAwarePlatformDriverTest.php +++ b/tests/Driver/VersionAwarePlatformDriverTest.php @@ -15,6 +15,7 @@ use Doctrine\DBAL\Platforms\MySQL80Platform; use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\PostgreSQL100Platform; +use Doctrine\DBAL\Platforms\PostgreSQL120Platform; use Doctrine\DBAL\Platforms\PostgreSQL94Platform; use Doctrine\DBAL\VersionAwarePlatformDriver; use Doctrine\Deprecations\PHPUnit\VerifyDeprecations; @@ -86,6 +87,7 @@ public static function postgreSQLVersionProvider(): array ['9.4.0', PostgreSQL94Platform::class], ['9.4.1', PostgreSQL94Platform::class], ['10', PostgreSQL100Platform::class], + ['12', PostgreSQL120Platform::class], ]; } diff --git a/tests/Functional/Schema/PostgreSQLSchemaManagerTest.php b/tests/Functional/Schema/PostgreSQLSchemaManagerTest.php index 33ad395c5e8..63b25e42d69 100644 --- a/tests/Functional/Schema/PostgreSQLSchemaManagerTest.php +++ b/tests/Functional/Schema/PostgreSQLSchemaManagerTest.php @@ -5,6 +5,7 @@ use Doctrine\DBAL\Driver\ServerInfoAwareConnection; use Doctrine\DBAL\Exception\DatabaseObjectNotFoundException; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Platforms\PostgreSQL120Platform; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\Comparator; @@ -320,6 +321,35 @@ public function testBooleanDefault(callable $comparatorFactory): void self::assertFalse($diff); } + /** + * @param callable(AbstractSchemaManager):Comparator $comparatorFactory + * + * @dataProvider \Doctrine\DBAL\Tests\Functional\Schema\ComparatorTestUtils::comparatorProvider + */ + public function testGeneratedColumn(callable $comparatorFactory): void + { + $wrappedConnection = $this->connection->getWrappedConnection(); + if (! $this->connection->getDatabasePlatform() instanceof PostgreSQL120Platform) { + self::markTestSkipped('Generated columns are not supported in Postgres 11 and earlier'); + } + + $table = new Table('ddc6198_generated_always_as'); + $table->addColumn('id', Types::INTEGER); + $table->addColumn( + 'idIsOdd', + Types::BOOLEAN, + ['columnDefinition' => 'boolean GENERATED ALWAYS AS (id % 2 = 1) STORED', 'notNull' => false], + ); + + $this->dropAndCreateTable($table); + + $databaseTable = $this->schemaManager->introspectTable($table->getName()); + + $diff = $comparatorFactory($this->schemaManager)->diffTable($table, $databaseTable); + + self::assertFalse($diff); + } + /** * PostgreSQL stores BINARY columns as BLOB */