Skip to content

Commit

Permalink
Autoincrement via identity columns on PostgreSQL
Browse files Browse the repository at this point in the history
  • Loading branch information
morozov committed May 15, 2022
1 parent d40bde4 commit 267c186
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 68 deletions.
38 changes: 12 additions & 26 deletions src/Platforms/PostgreSQLPlatform.php
Original file line number Diff line number Diff line change
Expand Up @@ -330,19 +330,13 @@ public function getAlterTableSQL(TableDiff $diff): array

if ($columnDiff->hasChanged('autoincrement')) {
if ($column->getAutoincrement()) {
// add autoincrement
$seqName = $this->getIdentitySequenceName($diff->name, $oldColumnName);

$sql[] = 'CREATE SEQUENCE ' . $seqName;
$sql[] = "SELECT setval('" . $seqName . "', (SELECT MAX(" . $oldColumnName . ') FROM '
. $diff->getName($this)->getQuotedName($this) . '))';
$query = 'ALTER ' . $oldColumnName . " SET DEFAULT nextval('" . $seqName . "')";
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
$query = 'ADD GENERATED BY DEFAULT AS IDENTITY';
} else {
// Drop autoincrement, but do NOT drop the sequence. It might be re-used by other tables or have
$query = 'ALTER ' . $oldColumnName . ' DROP DEFAULT';
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
$query = 'DROP IDENTITY';
}

$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this)
. ' ALTER ' . $oldColumnName . ' ' . $query;
}

$newComment = $column->getComment();
Expand Down Expand Up @@ -657,35 +651,23 @@ public function getBooleanTypeDeclarationSQL(array $column): string
*/
public function getIntegerTypeDeclarationSQL(array $column): string
{
if (! empty($column['autoincrement'])) {
return 'SERIAL';
}

return 'INT';
return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($column);
}

/**
* {@inheritDoc}
*/
public function getBigIntTypeDeclarationSQL(array $column): string
{
if (! empty($column['autoincrement'])) {
return 'BIGSERIAL';
}

return 'BIGINT';
return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column);
}

/**
* {@inheritDoc}
*/
public function getSmallIntTypeDeclarationSQL(array $column): string
{
if (! empty($column['autoincrement'])) {
return 'SMALLSERIAL';
}

return 'SMALLINT';
return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column);
}

/**
Expand Down Expand Up @@ -733,6 +715,10 @@ public function getTimeTypeDeclarationSQL(array $column): string
*/
protected function _getCommonIntegerTypeDeclarationSQL(array $column): string
{
if (! empty($column['autoincrement'])) {
return ' GENERATED BY DEFAULT AS IDENTITY';
}

return '';
}

Expand Down
14 changes: 3 additions & 11 deletions src/Schema/PostgreSQLSchemaManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -254,18 +254,9 @@ protected function _getPortableTableColumnDefinition(array $tableColumn): Column
$length = (int) $matches[1];
}

$matches = [];

$autoincrement = false;
$autoincrement = $tableColumn['attidentity'] === 'd';

if (
$tableColumn['default'] !== null
&& preg_match("/^nextval\('(.*)'(::.*)?\)$/", $tableColumn['default'], $matches) === 1
) {
$tableColumn['sequence'] = $matches[1];
$tableColumn['default'] = null;
$autoincrement = true;
}
$matches = [];

if ($tableColumn['default'] !== null) {
if (preg_match("/^['(](.*)[')]::/", $tableColumn['default'], $matches) === 1) {
Expand Down Expand Up @@ -441,6 +432,7 @@ protected function selectDatabaseColumns(string $databaseName, ?string $tableNam
(SELECT format_type(t2.typbasetype, t2.typtypmod) FROM
pg_catalog.pg_type t2 WHERE t2.typtype = 'd' AND t2.oid = a.atttypid) AS domain_complete_type,
a.attnotnull AS isnotnull,
a.attidentity,
(SELECT 't'
FROM pg_index
WHERE c.oid = pg_index.indrelid
Expand Down
26 changes: 2 additions & 24 deletions tests/Functional/Schema/PostgreSQLSchemaManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace Doctrine\DBAL\Tests\Functional\Schema;

use Doctrine\DBAL\Exception\DatabaseObjectNotFoundException;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
Expand Down Expand Up @@ -54,25 +53,8 @@ public function testSupportDomainTypeFallback(): void
self::assertInstanceOf(MoneyType::class, $table->getColumn('value')->getType());
}

public function testDetectsAutoIncrement(): void
{
$autoincTable = new Table('autoinc_table');
$column = $autoincTable->addColumn('id', 'integer');
$column->setAutoincrement(true);
$this->dropAndCreateTable($autoincTable);
$autoincTable = $this->schemaManager->listTableDetails('autoinc_table');

self::assertTrue($autoincTable->getColumn('id')->getAutoincrement());
}

public function testAlterTableAutoIncrementAdd(): void
{
// see https://github.com/doctrine/dbal/issues/4745
try {
$this->schemaManager->dropSequence('autoinc_table_add_id_seq');
} catch (DatabaseObjectNotFoundException $e) {
}

$tableFrom = new Table('autoinc_table_add');
$tableFrom->addColumn('id', 'integer');
$this->dropAndCreateTable($tableFrom);
Expand All @@ -89,11 +71,7 @@ public function testAlterTableAutoIncrementAdd(): void
self::assertNotNull($diff);

$sql = $platform->getAlterTableSQL($diff);
self::assertEquals([
'CREATE SEQUENCE autoinc_table_add_id_seq',
"SELECT setval('autoinc_table_add_id_seq', (SELECT MAX(id) FROM autoinc_table_add))",
"ALTER TABLE autoinc_table_add ALTER id SET DEFAULT nextval('autoinc_table_add_id_seq')",
], $sql);
self::assertEquals(['ALTER TABLE autoinc_table_add ALTER id ADD GENERATED BY DEFAULT AS IDENTITY'], $sql);

$this->schemaManager->alterTable($diff);
$tableFinal = $this->schemaManager->listTableDetails('autoinc_table_add');
Expand All @@ -118,7 +96,7 @@ public function testAlterTableAutoIncrementDrop(): void
self::assertNotNull($diff);

self::assertEquals(
['ALTER TABLE autoinc_table_drop ALTER id DROP DEFAULT'],
['ALTER TABLE autoinc_table_drop ALTER id DROP IDENTITY'],
$platform->getAlterTableSQL($diff)
);

Expand Down
15 changes: 8 additions & 7 deletions tests/Platforms/PostgreSQLPlatformTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ public function createPlatform(): AbstractPlatform

public function getGenerateTableSql(): string
{
return 'CREATE TABLE test (id SERIAL NOT NULL, test VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id))';
return 'CREATE TABLE test (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, test VARCHAR(255) DEFAULT NULL'
. ', PRIMARY KEY(id))';
}

/**
Expand Down Expand Up @@ -207,7 +208,7 @@ public function testGenerateTableWithAutoincrement(): void
$column->setAutoincrement(true);

self::assertEquals(
['CREATE TABLE autoinc_table (id SERIAL NOT NULL)'],
['CREATE TABLE autoinc_table (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL)'],
$this->platform->getCreateTableSQL($table)
);
}
Expand All @@ -218,8 +219,8 @@ public function testGenerateTableWithAutoincrement(): void
public static function serialTypes(): iterable
{
return [
['integer', 'SERIAL'],
['bigint', 'BIGSERIAL'],
['integer', 'INT GENERATED BY DEFAULT AS IDENTITY'],
['bigint', 'BIGINT GENERATED BY DEFAULT AS IDENTITY'],
];
}

Expand Down Expand Up @@ -276,11 +277,11 @@ public function testGeneratesTypeDeclarationForIntegers(): void
$this->platform->getIntegerTypeDeclarationSQL([])
);
self::assertEquals(
'SERIAL',
'INT GENERATED BY DEFAULT AS IDENTITY',
$this->platform->getIntegerTypeDeclarationSQL(['autoincrement' => true])
);
self::assertEquals(
'SERIAL',
'INT GENERATED BY DEFAULT AS IDENTITY',
$this->platform->getIntegerTypeDeclarationSQL(
['autoincrement' => true, 'primary' => true]
)
Expand Down Expand Up @@ -911,7 +912,7 @@ public function testReturnsJsonTypeDeclarationSQL(): void
public function testReturnsSmallIntTypeDeclarationSQL(): void
{
self::assertSame(
'SMALLSERIAL',
'SMALLINT GENERATED BY DEFAULT AS IDENTITY',
$this->platform->getSmallIntTypeDeclarationSQL(['autoincrement' => true])
);

Expand Down

0 comments on commit 267c186

Please sign in to comment.