Skip to content

Commit 9519e53

Browse files
committed
Merge branch '3.9.x' into 4.1.x
* 3.9.x: fix doctrine#6198: stop considering generated column definition as being its default value (doctrine#6199) Fix condition on Ascii String for SQL Server (doctrine#6389) Add DBTypes that are missing for TypeMapping (doctrine#6463)
2 parents 6ef1518 + 5f43767 commit 9519e53

8 files changed

+134
-8
lines changed

src/Driver/AbstractPostgreSQLDriver.php

+23
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,39 @@
77
use Doctrine\DBAL\Driver;
88
use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface;
99
use Doctrine\DBAL\Driver\API\PostgreSQL\ExceptionConverter;
10+
use Doctrine\DBAL\Platforms\Exception\InvalidPlatformVersion;
11+
use Doctrine\DBAL\Platforms\PostgreSQL120Platform;
1012
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
1113
use Doctrine\DBAL\ServerVersionProvider;
1214

15+
use function preg_match;
16+
use function version_compare;
17+
1318
/**
1419
* Abstract base implementation of the {@see Driver} interface for PostgreSQL based drivers.
1520
*/
1621
abstract class AbstractPostgreSQLDriver implements Driver
1722
{
1823
public function getDatabasePlatform(ServerVersionProvider $versionProvider): PostgreSQLPlatform
1924
{
25+
$version = $versionProvider->getServerVersion();
26+
27+
if (preg_match('/^(?P<major>\d+)(?:\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?)?/', $version, $versionParts) === 0) {
28+
throw InvalidPlatformVersion::new(
29+
$version,
30+
'<major_version>.<minor_version>.<patch_version>',
31+
);
32+
}
33+
34+
$majorVersion = $versionParts['major'];
35+
$minorVersion = $versionParts['minor'] ?? 0;
36+
$patchVersion = $versionParts['patch'] ?? 0;
37+
$version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion;
38+
39+
if (version_compare($version, '12.0', '>=')) {
40+
return new PostgreSQL120Platform();
41+
}
42+
2043
return new PostgreSQLPlatform();
2144
}
2245

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\DBAL\Platforms;
6+
7+
/**
8+
* Provides the behavior, features and SQL dialect of the PostgreSQL 12.0 database platform.
9+
*/
10+
class PostgreSQL120Platform extends PostgreSQLPlatform
11+
{
12+
public function getDefaultColumnValueSQLSnippet(): string
13+
{
14+
// in case of GENERATED ALWAYS AS (foobar) STORED column (added in PostgreSQL 12.0)
15+
// PostgreSQL's pg_get_expr(adbin, adrelid) will return the 'foobar' part
16+
// which is not the 'default' value of the column but its 'definition'
17+
// so in that case we force it to NULL as DBAL will use that column only for the
18+
// 'default' value
19+
return <<<'SQL'
20+
SELECT
21+
CASE
22+
WHEN a.attgenerated = 's' THEN NULL
23+
ELSE pg_get_expr(adbin, adrelid)
24+
END
25+
FROM pg_attrdef
26+
WHERE c.oid = pg_attrdef.adrelid
27+
AND pg_attrdef.adnum=a.attnum
28+
SQL;
29+
}
30+
}

src/Platforms/PostgreSQLPlatform.php

+13
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,19 @@ public function getTruncateTableSQL(string $tableName, bool $cascade = false): s
676676
return $sql;
677677
}
678678

679+
/**
680+
* Get the snippet used to retrieve the default value for a given column
681+
*/
682+
public function getDefaultColumnValueSQLSnippet(): string
683+
{
684+
return <<<'SQL'
685+
SELECT pg_get_expr(adbin, adrelid)
686+
FROM pg_attrdef
687+
WHERE c.oid = pg_attrdef.adrelid
688+
AND pg_attrdef.adnum=a.attnum
689+
SQL;
690+
}
691+
679692
protected function initializeDoctrineTypeMappings(): void
680693
{
681694
$this->doctrineTypeMapping = [

src/Platforms/SQLServerPlatform.php

+2
Original file line numberDiff line numberDiff line change
@@ -1080,12 +1080,14 @@ protected function initializeDoctrineTypeMappings(): void
10801080
'smalldatetime' => Types::DATETIME_MUTABLE,
10811081
'smallint' => Types::SMALLINT,
10821082
'smallmoney' => Types::INTEGER,
1083+
'sysname' => Types::STRING,
10831084
'text' => Types::TEXT,
10841085
'time' => Types::TIME_MUTABLE,
10851086
'tinyint' => Types::SMALLINT,
10861087
'uniqueidentifier' => Types::GUID,
10871088
'varbinary' => Types::BINARY,
10881089
'varchar' => Types::STRING,
1090+
'xml' => Types::TEXT,
10891091
];
10901092
}
10911093

src/Schema/PostgreSQLSchemaManager.php

+3-7
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ protected function selectTableColumns(string $databaseName, ?string $tableName =
417417
$sql .= ' c.relname AS table_name, n.nspname AS schema_name,';
418418
}
419419

420-
$sql .= <<<'SQL'
420+
$sql .= sprintf(<<<'SQL'
421421
a.attnum,
422422
quote_ident(a.attname) AS field,
423423
t.typname AS type,
@@ -434,11 +434,7 @@ protected function selectTableColumns(string $databaseName, ?string $tableName =
434434
AND pg_index.indkey[0] = a.attnum
435435
AND pg_index.indisprimary = 't'
436436
) AS pri,
437-
(SELECT pg_get_expr(adbin, adrelid)
438-
FROM pg_attrdef
439-
WHERE c.oid = pg_attrdef.adrelid
440-
AND pg_attrdef.adnum=a.attnum
441-
) AS default,
437+
(%s) AS default,
442438
(SELECT pg_description.description
443439
FROM pg_description WHERE pg_description.objoid = c.oid AND a.attnum = pg_description.objsubid
444440
) AS comment
@@ -453,7 +449,7 @@ protected function selectTableColumns(string $databaseName, ?string $tableName =
453449
ON d.objid = c.oid
454450
AND d.deptype = 'e'
455451
AND d.classid = (SELECT oid FROM pg_class WHERE relname = 'pg_class')
456-
SQL;
452+
SQL, $this->platform->getDefaultColumnValueSQLSnippet());
457453

458454
$conditions = array_merge([
459455
'a.attnum > 0',

tests/Driver/VersionAwarePlatformDriverTest.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Doctrine\DBAL\Platforms\MySQL80Platform;
1515
use Doctrine\DBAL\Platforms\MySQL84Platform;
1616
use Doctrine\DBAL\Platforms\MySQLPlatform;
17+
use Doctrine\DBAL\Platforms\PostgreSQL120Platform;
1718
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
1819
use PHPUnit\Framework\Attributes\DataProvider;
1920
use PHPUnit\Framework\TestCase;
@@ -69,7 +70,8 @@ public static function postgreSQLVersionProvider(): array
6970
return [
7071
['10.0', PostgreSQLPlatform::class],
7172
['11.0', PostgreSQLPlatform::class],
72-
['13.3', PostgreSQLPlatform::class],
73+
['12.0', PostgreSQL120Platform::class],
74+
['13.3', PostgreSQL120Platform::class],
7375
];
7476
}
7577

tests/Functional/Schema/PostgreSQLSchemaManagerTest.php

+26
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Doctrine\DBAL\Tests\Functional\Schema;
66

77
use Doctrine\DBAL\Platforms\AbstractPlatform;
8+
use Doctrine\DBAL\Platforms\PostgreSQL120Platform;
89
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
910
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
1011
use Doctrine\DBAL\Schema\Schema;
@@ -290,6 +291,31 @@ public function testBooleanDefault(): void
290291
);
291292
}
292293

294+
public function testGeneratedColumn(): void
295+
{
296+
if (! $this->connection->getDatabasePlatform() instanceof PostgreSQL120Platform) {
297+
self::markTestSkipped('Generated columns are not supported in Postgres 11 and earlier');
298+
}
299+
300+
$table = new Table('ddc6198_generated_always_as');
301+
$table->addColumn('id', Types::INTEGER);
302+
$table->addColumn(
303+
'idIsOdd',
304+
Types::BOOLEAN,
305+
['columnDefinition' => 'boolean GENERATED ALWAYS AS (id % 2 = 1) STORED', 'notNull' => false],
306+
);
307+
308+
$this->dropAndCreateTable($table);
309+
310+
$databaseTable = $this->schemaManager->introspectTable($table->getName());
311+
312+
self::assertTrue(
313+
$this->schemaManager->createComparator()
314+
->compareTables($table, $databaseTable)
315+
->isEmpty(),
316+
);
317+
}
318+
293319
/**
294320
* PostgreSQL stores BINARY columns as BLOB
295321
*/

tests/Platforms/SQLServerPlatformTest.php

+34
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,34 @@ public function testGeneratesTypeDeclarationsForStrings(): void
127127
);
128128
}
129129

130+
public function testGeneratesTypeDeclarationsForAsciiStrings(): void
131+
{
132+
self::assertEquals(
133+
'CHAR(10)',
134+
$this->platform->getAsciiStringTypeDeclarationSQL(
135+
['length' => 10, 'fixed' => true],
136+
),
137+
);
138+
self::assertEquals(
139+
'VARCHAR(50)',
140+
$this->platform->getAsciiStringTypeDeclarationSQL(['length' => 50]),
141+
);
142+
self::assertEquals(
143+
'VARCHAR(50)',
144+
$this->platform->getAsciiStringTypeDeclarationSQL(
145+
['length' => 50, 'fixed' => false],
146+
),
147+
);
148+
self::assertEquals(
149+
'VARCHAR(255)',
150+
$this->platform->getAsciiStringTypeDeclarationSQL([]),
151+
);
152+
self::assertEquals(
153+
'VARCHAR(255)',
154+
$this->platform->getAsciiStringTypeDeclarationSQL(['fixed' => false]),
155+
);
156+
}
157+
130158
public function testSupportsIdentityColumns(): void
131159
{
132160
self::assertTrue($this->platform->supportsIdentityColumns());
@@ -777,6 +805,12 @@ public function testInitializesDoctrineTypeMappings(): void
777805

778806
self::assertTrue($this->platform->hasDoctrineTypeMappingFor('uniqueidentifier'));
779807
self::assertSame(Types::GUID, $this->platform->getDoctrineTypeMapping('uniqueidentifier'));
808+
809+
self::assertTrue($this->platform->hasDoctrineTypeMappingFor('sysname'));
810+
self::assertSame(Types::STRING, $this->platform->getDoctrineTypeMapping('sysname'));
811+
812+
self::assertTrue($this->platform->hasDoctrineTypeMappingFor('xml'));
813+
self::assertSame(Types::TEXT, $this->platform->getDoctrineTypeMapping('xml'));
780814
}
781815

782816
protected function getExpectedFixedLengthStringTypeDeclarationSQLNoLength(): string

0 commit comments

Comments
 (0)