Skip to content

Commit 7bc24ba

Browse files
Fix 2193 Postgres GENERATED ALWAYS (#2195)
* Update the postgres generated clause to GENERATED BY DEFAULT to allow database seeding. * Copied insert statements from PdoAdapter and updated to include OVERRIDING SYSTEM VALUE to allow database seeding when a column was created with GENERATED ALWAYS. * Update PostgresAdapter unit tests with expected output. * Update PostgresAdapter unit tests with default generated clause. * Only override system value if Postgres version is greater than or equal to 10. * Add checks for Postgres version and coding standard fix. * Another Postgres version check.
1 parent 9fb8416 commit 7bc24ba

File tree

3 files changed

+116
-6
lines changed

3 files changed

+116
-6
lines changed

src/Phinx/Db/Adapter/PostgresAdapter.php

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ class PostgresAdapter extends PdoAdapter
2323
{
2424
public const GENERATED_ALWAYS = 'ALWAYS';
2525
public const GENERATED_BY_DEFAULT = 'BY DEFAULT';
26+
/**
27+
* Allow insert when a column was created with the GENERATED ALWAYS clause.
28+
* This is required for seeding the database.
29+
*/
30+
public const OVERRIDE_SYSTEM_VALUE = 'OVERRIDING SYSTEM VALUE';
2631

2732
/**
2833
* @var string[]
@@ -1586,4 +1591,85 @@ public function setSearchPath(): void
15861591
)
15871592
);
15881593
}
1594+
1595+
/**
1596+
* @inheritDoc
1597+
*/
1598+
public function insert(Table $table, array $row): void
1599+
{
1600+
$sql = sprintf(
1601+
'INSERT INTO %s ',
1602+
$this->quoteTableName($table->getName())
1603+
);
1604+
$columns = array_keys($row);
1605+
$sql .= '(' . implode(', ', array_map([$this, 'quoteColumnName'], $columns)) . ')';
1606+
1607+
foreach ($row as $column => $value) {
1608+
if (is_bool($value)) {
1609+
$row[$column] = $this->castToBool($value);
1610+
}
1611+
}
1612+
1613+
$override = '';
1614+
if ($this->useIdentity) {
1615+
$override = self::OVERRIDE_SYSTEM_VALUE . ' ';
1616+
}
1617+
1618+
if ($this->isDryRunEnabled()) {
1619+
$sql .= ' ' . $override . 'VALUES (' . implode(', ', array_map([$this, 'quoteValue'], $row)) . ');';
1620+
$this->output->writeln($sql);
1621+
} else {
1622+
$sql .= ' ' . $override . 'VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')';
1623+
$stmt = $this->getConnection()->prepare($sql);
1624+
$stmt->execute(array_values($row));
1625+
}
1626+
}
1627+
1628+
/**
1629+
* @inheritDoc
1630+
*/
1631+
public function bulkinsert(Table $table, array $rows): void
1632+
{
1633+
$sql = sprintf(
1634+
'INSERT INTO %s ',
1635+
$this->quoteTableName($table->getName())
1636+
);
1637+
$current = current($rows);
1638+
$keys = array_keys($current);
1639+
1640+
$override = '';
1641+
if ($this->useIdentity) {
1642+
$override = self::OVERRIDE_SYSTEM_VALUE . ' ';
1643+
}
1644+
1645+
$sql .= '(' . implode(', ', array_map([$this, 'quoteColumnName'], $keys)) . ') ' . $override . 'VALUES ';
1646+
1647+
if ($this->isDryRunEnabled()) {
1648+
$values = array_map(function ($row) {
1649+
return '(' . implode(', ', array_map([$this, 'quoteValue'], $row)) . ')';
1650+
}, $rows);
1651+
$sql .= implode(', ', $values) . ';';
1652+
$this->output->writeln($sql);
1653+
} else {
1654+
$count_keys = count($keys);
1655+
$query = '(' . implode(', ', array_fill(0, $count_keys, '?')) . ')';
1656+
$count_vars = count($rows);
1657+
$queries = array_fill(0, $count_vars, $query);
1658+
$sql .= implode(',', $queries);
1659+
$stmt = $this->getConnection()->prepare($sql);
1660+
$vals = [];
1661+
1662+
foreach ($rows as $row) {
1663+
foreach ($row as $v) {
1664+
if (is_bool($v)) {
1665+
$vals[] = $this->castToBool($v);
1666+
} else {
1667+
$vals[] = $v;
1668+
}
1669+
}
1670+
}
1671+
1672+
$stmt->execute($vals);
1673+
}
1674+
}
15891675
}

src/Phinx/Db/Table/Column.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ class Column
9292
*
9393
* @var ?string
9494
*/
95-
protected $generated = PostgresAdapter::GENERATED_ALWAYS;
95+
protected $generated = PostgresAdapter::GENERATED_BY_DEFAULT;
9696

9797
/**
9898
* @var int|null

tests/Phinx/Db/Adapter/PostgresAdapterTest.php

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -563,16 +563,16 @@ public function testAddColumnWithAutoIdentity()
563563
foreach ($columns as $column) {
564564
if ($column->getName() === 'id') {
565565
$this->assertTrue($column->getIdentity());
566-
$this->assertEquals(PostgresAdapter::GENERATED_ALWAYS, $column->getGenerated());
566+
$this->assertEquals(PostgresAdapter::GENERATED_BY_DEFAULT, $column->getGenerated());
567567
}
568568
}
569569
}
570570

571571
public function providerAddColumnIdentity(): array
572572
{
573573
return [
574-
[PostgresAdapter::GENERATED_ALWAYS, false], //testAddColumnWithIdentityAlways
575-
[PostgresAdapter::GENERATED_BY_DEFAULT, true], //testAddColumnWithIdentityDefault
574+
[PostgresAdapter::GENERATED_ALWAYS, true], //testAddColumnWithIdentityAlways
575+
[PostgresAdapter::GENERATED_BY_DEFAULT, false], //testAddColumnWithIdentityDefault
576576
[null, true], //testAddColumnWithoutIdentity
577577
];
578578
}
@@ -2233,7 +2233,7 @@ public function testDumpCreateTable()
22332233
->save();
22342234

22352235
if ($this->usingPostgres10()) {
2236-
$expectedOutput = 'CREATE TABLE "public"."table1" ("id" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY, "column1" CHARACTER VARYING (255) ' .
2236+
$expectedOutput = 'CREATE TABLE "public"."table1" ("id" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY, "column1" CHARACTER VARYING (255) ' .
22372237
'NULL, "column2" INTEGER NULL, "column3" CHARACTER VARYING (255) NOT NULL DEFAULT \'test\', CONSTRAINT ' .
22382238
'"table1_pkey" PRIMARY KEY ("id"));';
22392239
} else {
@@ -2265,7 +2265,7 @@ public function testDumpCreateTableWithSchema()
22652265
->save();
22662266

22672267
if ($this->usingPostgres10()) {
2268-
$expectedOutput = 'CREATE TABLE "schema1"."table1" ("id" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY, "column1" CHARACTER VARYING (255) ' .
2268+
$expectedOutput = 'CREATE TABLE "schema1"."table1" ("id" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY, "column1" CHARACTER VARYING (255) ' .
22692269
'NULL, "column2" INTEGER NULL, "column3" CHARACTER VARYING (255) NOT NULL DEFAULT \'test\', CONSTRAINT ' .
22702270
'"table1_pkey" PRIMARY KEY ("id"));';
22712271
} else {
@@ -2312,10 +2312,19 @@ public function testDumpInsert()
23122312
]);
23132313

23142314
$expectedOutput = <<<'OUTPUT'
2315+
INSERT INTO "public"."table1" ("string_col") OVERRIDING SYSTEM VALUE VALUES ('test data');
2316+
INSERT INTO "public"."table1" ("string_col") OVERRIDING SYSTEM VALUE VALUES (null);
2317+
INSERT INTO "public"."table1" ("int_col") OVERRIDING SYSTEM VALUE VALUES (23);
2318+
OUTPUT;
2319+
2320+
if (!$this->usingPostgres10()) {
2321+
$expectedOutput = <<<'OUTPUT'
23152322
INSERT INTO "public"."table1" ("string_col") VALUES ('test data');
23162323
INSERT INTO "public"."table1" ("string_col") VALUES (null);
23172324
INSERT INTO "public"."table1" ("int_col") VALUES (23);
23182325
OUTPUT;
2326+
}
2327+
23192328
$actualOutput = $consoleOutput->fetch();
23202329
$this->assertStringContainsString(
23212330
$expectedOutput,
@@ -2359,8 +2368,15 @@ public function testDumpBulkinsert()
23592368
]);
23602369

23612370
$expectedOutput = <<<'OUTPUT'
2371+
INSERT INTO "public"."table1" ("string_col", "int_col") OVERRIDING SYSTEM VALUE VALUES ('test_data1', 23), (null, 42);
2372+
OUTPUT;
2373+
2374+
if (!$this->usingPostgres10()) {
2375+
$expectedOutput = <<<'OUTPUT'
23622376
INSERT INTO "public"."table1" ("string_col", "int_col") VALUES ('test_data1', 23), (null, 42);
23632377
OUTPUT;
2378+
}
2379+
23642380
$actualOutput = $consoleOutput->fetch();
23652381
$this->assertStringContainsString(
23662382
$expectedOutput,
@@ -2395,8 +2411,16 @@ public function testDumpCreateTableAndThenInsert()
23952411

23962412
$expectedOutput = <<<'OUTPUT'
23972413
CREATE TABLE "schema1"."table1" ("column1" CHARACTER VARYING (255) NOT NULL, "column2" INTEGER NULL, CONSTRAINT "table1_pkey" PRIMARY KEY ("column1"));
2414+
INSERT INTO "schema1"."table1" ("column1", "column2") OVERRIDING SYSTEM VALUE VALUES ('id1', 1);
2415+
OUTPUT;
2416+
2417+
if (!$this->usingPostgres10()) {
2418+
$expectedOutput = <<<'OUTPUT'
2419+
CREATE TABLE "schema1"."table1" ("column1" CHARACTER VARYING (255) NOT NULL, "column2" INTEGER NULL, CONSTRAINT "table1_pkey" PRIMARY KEY ("column1"));
23982420
INSERT INTO "schema1"."table1" ("column1", "column2") VALUES ('id1', 1);
23992421
OUTPUT;
2422+
}
2423+
24002424
$actualOutput = $consoleOutput->fetch();
24012425
$this->assertStringContainsString($expectedOutput, $actualOutput, 'Passing the --dry-run option does not dump create and then insert table queries to the output');
24022426
}

0 commit comments

Comments
 (0)