On Thu, 5 Mar 2020 at 09:45, Peter Eisentraut < peter.eisentr...@2ndquadrant.com> wrote:
> On 2020-02-11 00:38, Quan Zongliang wrote: > > new patch attached. > > I didn't like so much how the updating of the replica identity was > hacked into the middle of ATRewriteCatalogs(). I have an alternative > proposal in the attached patch that queues up an ALTER TABLE ... REPLICA > IDENTITY command into the normal ALTER TABLE processing. I have also > added tests to the test suite. > > LGTM. Tests are ok. I've rebased it (because 61d7c7bce3686ec02bd64abac742dd35ed9b9b01). Are you planning to backpatch it? IMHO you should because it is a bug (since REPLICA IDENTITY was introduced in 9.4). This patch can be applied as-is in 12 but not to other older branches. I attached new patches. Regards, -- Euler Taveira http://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
From e35463df17f25940eabe92142237c2a85fb15b6f Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <peter@eisentraut.org> Date: Thu, 5 Mar 2020 10:11:13 +0100 Subject: [PATCH] Preserve replica identity index across ALTER TABLE rewrite --- src/backend/commands/tablecmds.c | 42 +++++++++++++++++ src/backend/utils/cache/lsyscache.c | 23 ++++++++++ src/include/utils/lsyscache.h | 1 + .../regress/expected/replica_identity.out | 46 +++++++++++++++++++ src/test/regress/sql/replica_identity.sql | 21 +++++++++ 5 files changed, 133 insertions(+) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38386fb9cf..80725dcf92 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -175,6 +175,7 @@ typedef struct AlteredTableInfo List *changedConstraintDefs; /* string definitions of same */ List *changedIndexOids; /* OIDs of indexes to rebuild */ List *changedIndexDefs; /* string definitions of same */ + char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */ } AlteredTableInfo; /* Struct describing one new constraint to check in Phase 3 scan */ @@ -10314,6 +10315,22 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, return address; } +/* + * Subroutine for ATExecAlterColumnType: remember that a replica identity + * needs to be reset. + */ +static void +RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab) +{ + if (!get_index_is_replident(indoid)) + return; + + if (tab->replicaIdentityIndex) + elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid); + + tab->replicaIdentityIndex = get_rel_name(indoid); +} + /* * Subroutine for ATExecAlterColumnType: remember that a constraint needs * to be rebuilt (which we might already know). @@ -10332,11 +10349,16 @@ RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab) { /* OK, capture the constraint's existing definition string */ char *defstring = pg_get_constraintdef_command(conoid); + Oid indoid; tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids, conoid); tab->changedConstraintDefs = lappend(tab->changedConstraintDefs, defstring); + + indoid = get_constraint_index(conoid); + if (OidIsValid(indoid)) + RememberReplicaIdentityForRebuilding(indoid, tab); } } @@ -10379,6 +10401,8 @@ RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab) indoid); tab->changedIndexDefs = lappend(tab->changedIndexDefs, defstring); + + RememberReplicaIdentityForRebuilding(indoid, tab); } } } @@ -10593,6 +10617,24 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode) add_exact_object_address(&obj, objects); } + /* + * Queue up command to restore replica identity index marking + */ + if (tab->replicaIdentityIndex) + { + AlterTableCmd *cmd = makeNode(AlterTableCmd); + ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt); + + subcmd->identity_type = REPLICA_IDENTITY_INDEX; + subcmd->name = tab->replicaIdentityIndex; + cmd->subtype = AT_ReplicaIdentity; + cmd->def = (Node *) subcmd; + + /* do it after indexes and constraints */ + tab->subcmds[AT_PASS_OLD_CONSTR] = + lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd); + } + /* * It should be okay to use DROP_RESTRICT here, since nothing else should * be depending on these objects. diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index a4da935371..4a2510e2ed 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -3156,3 +3156,26 @@ get_range_collation(Oid rangeOid) else return InvalidOid; } + +/* + * get_index_is_replident + * + * Returns indisreplident field. + */ +bool +get_index_is_replident(Oid index_oid) +{ + HeapTuple tuple; + Form_pg_index rd_index; + bool result; + + tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid)); + if (!HeapTupleIsValid(tuple)) + return false; + + rd_index = (Form_pg_index) GETSTRUCT(tuple); + result = rd_index->indisreplident; + ReleaseSysCache(tuple); + + return result; +} diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index c97c926992..b17931e297 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -178,6 +178,7 @@ extern char *get_namespace_name(Oid nspid); extern char *get_namespace_name_or_temp(Oid nspid); extern Oid get_range_subtype(Oid rangeOid); extern Oid get_range_collation(Oid rangeOid); +extern bool get_index_is_replident(Oid index_oid); #define type_is_array(typid) (get_element_type(typid) != InvalidOid) /* type_is_array_domain accepts both plain arrays and domains over arrays */ diff --git a/src/test/regress/expected/replica_identity.out b/src/test/regress/expected/replica_identity.out index 67c34a92a4..65c7c9ac9f 100644 --- a/src/test/regress/expected/replica_identity.out +++ b/src/test/regress/expected/replica_identity.out @@ -186,5 +186,51 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; n (1 row) +--- +-- Test that ALTER TABLE rewrite preserves nondefault replica identity +--- +-- constraint variant +CREATE TABLE test_replica_identity2 (id int UNIQUE NOT NULL); +ALTER TABLE test_replica_identity2 REPLICA IDENTITY USING INDEX test_replica_identity2_id_key; +\d test_replica_identity2 + Table "public.test_replica_identity2" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + id | integer | | not null | +Indexes: + "test_replica_identity2_id_key" UNIQUE CONSTRAINT, btree (id) REPLICA IDENTITY + +ALTER TABLE test_replica_identity2 ALTER COLUMN id TYPE bigint; +\d test_replica_identity2 + Table "public.test_replica_identity2" + Column | Type | Collation | Nullable | Default +--------+--------+-----------+----------+--------- + id | bigint | | not null | +Indexes: + "test_replica_identity2_id_key" UNIQUE CONSTRAINT, btree (id) REPLICA IDENTITY + +-- straight index variant +CREATE TABLE test_replica_identity3 (id int NOT NULL); +CREATE UNIQUE INDEX test_replica_identity3_id_key ON test_replica_identity3 (id); +ALTER TABLE test_replica_identity3 REPLICA IDENTITY USING INDEX test_replica_identity3_id_key; +\d test_replica_identity3 + Table "public.test_replica_identity3" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + id | integer | | not null | +Indexes: + "test_replica_identity3_id_key" UNIQUE, btree (id) REPLICA IDENTITY + +ALTER TABLE test_replica_identity3 ALTER COLUMN id TYPE bigint; +\d test_replica_identity3 + Table "public.test_replica_identity3" + Column | Type | Collation | Nullable | Default +--------+--------+-----------+----------+--------- + id | bigint | | not null | +Indexes: + "test_replica_identity3_id_key" UNIQUE, btree (id) REPLICA IDENTITY + DROP TABLE test_replica_identity; +DROP TABLE test_replica_identity2; +DROP TABLE test_replica_identity3; DROP TABLE test_replica_identity_othertable; diff --git a/src/test/regress/sql/replica_identity.sql b/src/test/regress/sql/replica_identity.sql index 3d2171c733..5277ff39cd 100644 --- a/src/test/regress/sql/replica_identity.sql +++ b/src/test/regress/sql/replica_identity.sql @@ -79,5 +79,26 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; ALTER TABLE test_replica_identity REPLICA IDENTITY NOTHING; SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; +--- +-- Test that ALTER TABLE rewrite preserves nondefault replica identity +--- + +-- constraint variant +CREATE TABLE test_replica_identity2 (id int UNIQUE NOT NULL); +ALTER TABLE test_replica_identity2 REPLICA IDENTITY USING INDEX test_replica_identity2_id_key; +\d test_replica_identity2 +ALTER TABLE test_replica_identity2 ALTER COLUMN id TYPE bigint; +\d test_replica_identity2 + +-- straight index variant +CREATE TABLE test_replica_identity3 (id int NOT NULL); +CREATE UNIQUE INDEX test_replica_identity3_id_key ON test_replica_identity3 (id); +ALTER TABLE test_replica_identity3 REPLICA IDENTITY USING INDEX test_replica_identity3_id_key; +\d test_replica_identity3 +ALTER TABLE test_replica_identity3 ALTER COLUMN id TYPE bigint; +\d test_replica_identity3 + DROP TABLE test_replica_identity; +DROP TABLE test_replica_identity2; +DROP TABLE test_replica_identity3; DROP TABLE test_replica_identity_othertable; -- 2.20.1
From 8a032fc0a5aaf28593b4aee583431cc1409e5773 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <peter@eisentraut.org> Date: Thu, 5 Mar 2020 10:11:13 +0100 Subject: [PATCH] Preserve replica identity index across ALTER TABLE rewrite --- src/backend/commands/tablecmds.c | 42 +++++++++++++++++ src/backend/utils/cache/lsyscache.c | 23 ++++++++++ src/include/utils/lsyscache.h | 1 + .../regress/expected/replica_identity.out | 46 +++++++++++++++++++ src/test/regress/sql/replica_identity.sql | 21 +++++++++ 5 files changed, 133 insertions(+) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index d07476d8dd..ce24955d55 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -174,6 +174,7 @@ typedef struct AlteredTableInfo List *changedConstraintDefs; /* string definitions of same */ List *changedIndexOids; /* OIDs of indexes to rebuild */ List *changedIndexDefs; /* string definitions of same */ + char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */ } AlteredTableInfo; /* Struct describing one new constraint to check in Phase 3 scan */ @@ -11138,6 +11139,22 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, return address; } +/* + * Subroutine for ATExecAlterColumnType: remember that a replica identity + * needs to be reset. + */ +static void +RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab) +{ + if (!get_index_is_replident(indoid)) + return; + + if (tab->replicaIdentityIndex) + elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid); + + tab->replicaIdentityIndex = get_rel_name(indoid); +} + /* * Subroutine for ATExecAlterColumnType: remember that a constraint needs * to be rebuilt (which we might already know). @@ -11156,11 +11173,16 @@ RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab) { /* OK, capture the constraint's existing definition string */ char *defstring = pg_get_constraintdef_command(conoid); + Oid indoid; tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids, conoid); tab->changedConstraintDefs = lappend(tab->changedConstraintDefs, defstring); + + indoid = get_constraint_index(conoid); + if (OidIsValid(indoid)) + RememberReplicaIdentityForRebuilding(indoid, tab); } } @@ -11203,6 +11225,8 @@ RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab) indoid); tab->changedIndexDefs = lappend(tab->changedIndexDefs, defstring); + + RememberReplicaIdentityForRebuilding(indoid, tab); } } } @@ -11311,6 +11335,24 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode) add_exact_object_address(&obj, objects); } + /* + * Queue up command to restore replica identity index marking + */ + if (tab->replicaIdentityIndex) + { + AlterTableCmd *cmd = makeNode(AlterTableCmd); + ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt); + + subcmd->identity_type = REPLICA_IDENTITY_INDEX; + subcmd->name = tab->replicaIdentityIndex; + cmd->subtype = AT_ReplicaIdentity; + cmd->def = (Node *) subcmd; + + /* do it after indexes and constraints */ + tab->subcmds[AT_PASS_OLD_CONSTR] = + lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd); + } + /* * It should be okay to use DROP_RESTRICT here, since nothing else should * be depending on these objects. diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 167d2592d8..c351a59e53 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -3250,3 +3250,26 @@ get_index_isvalid(Oid index_oid) return isvalid; } + +/* + * get_index_is_replident + * + * Returns indisreplident field. + */ +bool +get_index_is_replident(Oid index_oid) +{ + HeapTuple tuple; + Form_pg_index rd_index; + bool result; + + tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid)); + if (!HeapTupleIsValid(tuple)) + return false; + + rd_index = (Form_pg_index) GETSTRUCT(tuple); + result = rd_index->indisreplident; + ReleaseSysCache(tuple); + + return result; +} diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 810dbeeab9..f853724431 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -182,6 +182,7 @@ extern Oid get_range_subtype(Oid rangeOid); extern Oid get_range_collation(Oid rangeOid); extern Oid get_index_column_opclass(Oid index_oid, int attno); extern bool get_index_isvalid(Oid index_oid); +extern bool get_index_is_replident(Oid index_oid); #define type_is_array(typid) (get_element_type(typid) != InvalidOid) /* type_is_array_domain accepts both plain arrays and domains over arrays */ diff --git a/src/test/regress/expected/replica_identity.out b/src/test/regress/expected/replica_identity.out index 175ecd2879..a586347eee 100644 --- a/src/test/regress/expected/replica_identity.out +++ b/src/test/regress/expected/replica_identity.out @@ -179,5 +179,51 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; n (1 row) +--- +-- Test that ALTER TABLE rewrite preserves nondefault replica identity +--- +-- constraint variant +CREATE TABLE test_replica_identity2 (id int UNIQUE NOT NULL); +ALTER TABLE test_replica_identity2 REPLICA IDENTITY USING INDEX test_replica_identity2_id_key; +\d test_replica_identity2 + Table "public.test_replica_identity2" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + id | integer | | not null | +Indexes: + "test_replica_identity2_id_key" UNIQUE CONSTRAINT, btree (id) REPLICA IDENTITY + +ALTER TABLE test_replica_identity2 ALTER COLUMN id TYPE bigint; +\d test_replica_identity2 + Table "public.test_replica_identity2" + Column | Type | Collation | Nullable | Default +--------+--------+-----------+----------+--------- + id | bigint | | not null | +Indexes: + "test_replica_identity2_id_key" UNIQUE CONSTRAINT, btree (id) REPLICA IDENTITY + +-- straight index variant +CREATE TABLE test_replica_identity3 (id int NOT NULL); +CREATE UNIQUE INDEX test_replica_identity3_id_key ON test_replica_identity3 (id); +ALTER TABLE test_replica_identity3 REPLICA IDENTITY USING INDEX test_replica_identity3_id_key; +\d test_replica_identity3 + Table "public.test_replica_identity3" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + id | integer | | not null | +Indexes: + "test_replica_identity3_id_key" UNIQUE, btree (id) REPLICA IDENTITY + +ALTER TABLE test_replica_identity3 ALTER COLUMN id TYPE bigint; +\d test_replica_identity3 + Table "public.test_replica_identity3" + Column | Type | Collation | Nullable | Default +--------+--------+-----------+----------+--------- + id | bigint | | not null | +Indexes: + "test_replica_identity3_id_key" UNIQUE, btree (id) REPLICA IDENTITY + DROP TABLE test_replica_identity; +DROP TABLE test_replica_identity2; +DROP TABLE test_replica_identity3; DROP TABLE test_replica_identity_othertable; diff --git a/src/test/regress/sql/replica_identity.sql b/src/test/regress/sql/replica_identity.sql index b08a3623b8..a5ac8f5567 100644 --- a/src/test/regress/sql/replica_identity.sql +++ b/src/test/regress/sql/replica_identity.sql @@ -75,5 +75,26 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; ALTER TABLE test_replica_identity REPLICA IDENTITY NOTHING; SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; +--- +-- Test that ALTER TABLE rewrite preserves nondefault replica identity +--- + +-- constraint variant +CREATE TABLE test_replica_identity2 (id int UNIQUE NOT NULL); +ALTER TABLE test_replica_identity2 REPLICA IDENTITY USING INDEX test_replica_identity2_id_key; +\d test_replica_identity2 +ALTER TABLE test_replica_identity2 ALTER COLUMN id TYPE bigint; +\d test_replica_identity2 + +-- straight index variant +CREATE TABLE test_replica_identity3 (id int NOT NULL); +CREATE UNIQUE INDEX test_replica_identity3_id_key ON test_replica_identity3 (id); +ALTER TABLE test_replica_identity3 REPLICA IDENTITY USING INDEX test_replica_identity3_id_key; +\d test_replica_identity3 +ALTER TABLE test_replica_identity3 ALTER COLUMN id TYPE bigint; +\d test_replica_identity3 + DROP TABLE test_replica_identity; +DROP TABLE test_replica_identity2; +DROP TABLE test_replica_identity3; DROP TABLE test_replica_identity_othertable; -- 2.20.1
From e454c38afe989cd89608cc838bc6bfb804e46b29 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <peter@eisentraut.org> Date: Thu, 5 Mar 2020 10:11:13 +0100 Subject: [PATCH] Preserve replica identity index across ALTER TABLE rewrite --- src/backend/commands/tablecmds.c | 46 +++++++++++++++++++ src/backend/utils/cache/lsyscache.c | 23 ++++++++++ src/include/utils/lsyscache.h | 1 + .../regress/expected/replica_identity.out | 46 +++++++++++++++++++ src/test/regress/sql/replica_identity.sql | 21 +++++++++ 5 files changed, 137 insertions(+) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 9c6ca7ff9c..380c52e8df 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -168,6 +168,7 @@ typedef struct AlteredTableInfo List *changedConstraintDefs; /* string definitions of same */ List *changedIndexOids; /* OIDs of indexes to rebuild */ List *changedIndexDefs; /* string definitions of same */ + char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */ } AlteredTableInfo; /* Struct describing one new constraint to check in Phase 3 scan */ @@ -8596,6 +8597,22 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, return address; } +/* + * Subroutine for ATExecAlterColumnType: remember that a replica identity + * needs to be reset. + */ +static void +RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab) +{ + if (!get_index_is_replident(indoid)) + return; + + if (tab->replicaIdentityIndex) + elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid); + + tab->replicaIdentityIndex = get_rel_name(indoid); +} + /* * Subroutine for ATExecAlterColumnType: remember that a constraint needs * to be rebuilt (which we might already know). @@ -8615,6 +8632,7 @@ RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab, { /* OK, capture the constraint's existing definition string */ char *defstring = pg_get_constraintdef_command(conoid); + Oid indoid; /* * Put NORMAL dependencies at the front of the list and AUTO @@ -8630,6 +8648,10 @@ RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab, tab->changedConstraintOids); tab->changedConstraintDefs = lcons(defstring, tab->changedConstraintDefs); + + indoid = get_constraint_index(conoid); + if (OidIsValid(indoid)) + RememberReplicaIdentityForRebuilding(indoid, tab); } else { @@ -8637,6 +8659,10 @@ RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab, conoid); tab->changedConstraintDefs = lappend(tab->changedConstraintDefs, defstring); + + indoid = get_constraint_index(conoid); + if (OidIsValid(indoid)) + RememberReplicaIdentityForRebuilding(indoid, tab); } } } @@ -8681,6 +8707,8 @@ RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab) indoid); tab->changedIndexDefs = lappend(tab->changedIndexDefs, defstring); + + RememberReplicaIdentityForRebuilding(indoid, tab); } } } @@ -8874,6 +8902,24 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode) wqueue, lockmode, tab->rewrite); } + /* + * Queue up command to restore replica identity index marking + */ + if (tab->replicaIdentityIndex) + { + AlterTableCmd *cmd = makeNode(AlterTableCmd); + ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt); + + subcmd->identity_type = REPLICA_IDENTITY_INDEX; + subcmd->name = tab->replicaIdentityIndex; + cmd->subtype = AT_ReplicaIdentity; + cmd->def = (Node *) subcmd; + + /* do it after indexes and constraints */ + tab->subcmds[AT_PASS_OLD_CONSTR] = + lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd); + } + /* * Now we can drop the existing constraints and indexes --- constraints * first, since some of them might depend on the indexes. In fact, we diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index e39c1d45cd..c49efd5e54 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -3088,3 +3088,26 @@ get_range_collation(Oid rangeOid) else return InvalidOid; } + +/* + * get_index_is_replident + * + * Returns indisreplident field. + */ +bool +get_index_is_replident(Oid index_oid) +{ + HeapTuple tuple; + Form_pg_index rd_index; + bool result; + + tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid)); + if (!HeapTupleIsValid(tuple)) + return false; + + rd_index = (Form_pg_index) GETSTRUCT(tuple); + result = rd_index->indisreplident; + ReleaseSysCache(tuple); + + return result; +} diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index b4757a3466..54a4dc8200 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -160,6 +160,7 @@ extern char *get_namespace_name(Oid nspid); extern char *get_namespace_name_or_temp(Oid nspid); extern Oid get_range_subtype(Oid rangeOid); extern Oid get_range_collation(Oid rangeOid); +extern bool get_index_is_replident(Oid index_oid); #define type_is_array(typid) (get_element_type(typid) != InvalidOid) /* type_is_array_domain accepts both plain arrays and domains over arrays */ diff --git a/src/test/regress/expected/replica_identity.out b/src/test/regress/expected/replica_identity.out index 27e2b67b8d..1071af9247 100644 --- a/src/test/regress/expected/replica_identity.out +++ b/src/test/regress/expected/replica_identity.out @@ -187,5 +187,51 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; n (1 row) +--- +-- Test that ALTER TABLE rewrite preserves nondefault replica identity +--- +-- constraint variant +CREATE TABLE test_replica_identity2 (id int UNIQUE NOT NULL); +ALTER TABLE test_replica_identity2 REPLICA IDENTITY USING INDEX test_replica_identity2_id_key; +\d test_replica_identity2 +Table "public.test_replica_identity2" + Column | Type | Modifiers +--------+---------+----------- + id | integer | not null +Indexes: + "test_replica_identity2_id_key" UNIQUE CONSTRAINT, btree (id) REPLICA IDENTITY + +ALTER TABLE test_replica_identity2 ALTER COLUMN id TYPE bigint; +\d test_replica_identity2 +Table "public.test_replica_identity2" + Column | Type | Modifiers +--------+--------+----------- + id | bigint | not null +Indexes: + "test_replica_identity2_id_key" UNIQUE CONSTRAINT, btree (id) REPLICA IDENTITY + +-- straight index variant +CREATE TABLE test_replica_identity3 (id int NOT NULL); +CREATE UNIQUE INDEX test_replica_identity3_id_key ON test_replica_identity3 (id); +ALTER TABLE test_replica_identity3 REPLICA IDENTITY USING INDEX test_replica_identity3_id_key; +\d test_replica_identity3 +Table "public.test_replica_identity3" + Column | Type | Modifiers +--------+---------+----------- + id | integer | not null +Indexes: + "test_replica_identity3_id_key" UNIQUE, btree (id) REPLICA IDENTITY + +ALTER TABLE test_replica_identity3 ALTER COLUMN id TYPE bigint; +\d test_replica_identity3 +Table "public.test_replica_identity3" + Column | Type | Modifiers +--------+--------+----------- + id | bigint | not null +Indexes: + "test_replica_identity3_id_key" UNIQUE, btree (id) REPLICA IDENTITY + DROP TABLE test_replica_identity; +DROP TABLE test_replica_identity2; +DROP TABLE test_replica_identity3; DROP TABLE test_replica_identity_othertable; diff --git a/src/test/regress/sql/replica_identity.sql b/src/test/regress/sql/replica_identity.sql index 3d2171c733..5277ff39cd 100644 --- a/src/test/regress/sql/replica_identity.sql +++ b/src/test/regress/sql/replica_identity.sql @@ -79,5 +79,26 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; ALTER TABLE test_replica_identity REPLICA IDENTITY NOTHING; SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; +--- +-- Test that ALTER TABLE rewrite preserves nondefault replica identity +--- + +-- constraint variant +CREATE TABLE test_replica_identity2 (id int UNIQUE NOT NULL); +ALTER TABLE test_replica_identity2 REPLICA IDENTITY USING INDEX test_replica_identity2_id_key; +\d test_replica_identity2 +ALTER TABLE test_replica_identity2 ALTER COLUMN id TYPE bigint; +\d test_replica_identity2 + +-- straight index variant +CREATE TABLE test_replica_identity3 (id int NOT NULL); +CREATE UNIQUE INDEX test_replica_identity3_id_key ON test_replica_identity3 (id); +ALTER TABLE test_replica_identity3 REPLICA IDENTITY USING INDEX test_replica_identity3_id_key; +\d test_replica_identity3 +ALTER TABLE test_replica_identity3 ALTER COLUMN id TYPE bigint; +\d test_replica_identity3 + DROP TABLE test_replica_identity; +DROP TABLE test_replica_identity2; +DROP TABLE test_replica_identity3; DROP TABLE test_replica_identity_othertable; -- 2.20.1
From 0463753e67d959a22e919d5173206869c1fa2ebd Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <peter@eisentraut.org> Date: Thu, 5 Mar 2020 10:11:13 +0100 Subject: [PATCH] Preserve replica identity index across ALTER TABLE rewrite --- src/backend/commands/tablecmds.c | 46 +++++++++++++++++++ src/backend/utils/cache/lsyscache.c | 23 ++++++++++ src/include/utils/lsyscache.h | 1 + .../regress/expected/replica_identity.out | 46 +++++++++++++++++++ src/test/regress/sql/replica_identity.sql | 21 +++++++++ 5 files changed, 137 insertions(+) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 7fcc3db14c..244e237d57 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -166,6 +166,7 @@ typedef struct AlteredTableInfo List *changedConstraintDefs; /* string definitions of same */ List *changedIndexOids; /* OIDs of indexes to rebuild */ List *changedIndexDefs; /* string definitions of same */ + char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */ } AlteredTableInfo; /* Struct describing one new constraint to check in Phase 3 scan */ @@ -8566,6 +8567,22 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, return address; } +/* + * Subroutine for ATExecAlterColumnType: remember that a replica identity + * needs to be reset. + */ +static void +RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab) +{ + if (!get_index_is_replident(indoid)) + return; + + if (tab->replicaIdentityIndex) + elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid); + + tab->replicaIdentityIndex = get_rel_name(indoid); +} + /* * Subroutine for ATExecAlterColumnType: remember that a constraint needs * to be rebuilt (which we might already know). @@ -8585,6 +8602,7 @@ RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab, { /* OK, capture the constraint's existing definition string */ char *defstring = pg_get_constraintdef_command(conoid); + Oid indoid; /* * Put NORMAL dependencies at the front of the list and AUTO @@ -8600,6 +8618,10 @@ RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab, tab->changedConstraintOids); tab->changedConstraintDefs = lcons(defstring, tab->changedConstraintDefs); + + indoid = get_constraint_index(conoid); + if (OidIsValid(indoid)) + RememberReplicaIdentityForRebuilding(indoid, tab); } else { @@ -8607,6 +8629,10 @@ RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab, conoid); tab->changedConstraintDefs = lappend(tab->changedConstraintDefs, defstring); + + indoid = get_constraint_index(conoid); + if (OidIsValid(indoid)) + RememberReplicaIdentityForRebuilding(indoid, tab); } } } @@ -8651,6 +8677,8 @@ RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab) indoid); tab->changedIndexDefs = lappend(tab->changedIndexDefs, defstring); + + RememberReplicaIdentityForRebuilding(indoid, tab); } } } @@ -8844,6 +8872,24 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode) wqueue, lockmode, tab->rewrite); } + /* + * Queue up command to restore replica identity index marking + */ + if (tab->replicaIdentityIndex) + { + AlterTableCmd *cmd = makeNode(AlterTableCmd); + ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt); + + subcmd->identity_type = REPLICA_IDENTITY_INDEX; + subcmd->name = tab->replicaIdentityIndex; + cmd->subtype = AT_ReplicaIdentity; + cmd->def = (Node *) subcmd; + + /* do it after indexes and constraints */ + tab->subcmds[AT_PASS_OLD_CONSTR] = + lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd); + } + /* * Now we can drop the existing constraints and indexes --- constraints * first, since some of them might depend on the indexes. In fact, we diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index b0b024dc3e..5a07ce4118 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -3045,3 +3045,26 @@ get_range_collation(Oid rangeOid) else return InvalidOid; } + +/* + * get_index_is_replident + * + * Returns indisreplident field. + */ +bool +get_index_is_replident(Oid index_oid) +{ + HeapTuple tuple; + Form_pg_index rd_index; + bool result; + + tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid)); + if (!HeapTupleIsValid(tuple)) + return false; + + rd_index = (Form_pg_index) GETSTRUCT(tuple); + result = rd_index->indisreplident; + ReleaseSysCache(tuple); + + return result; +} diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 489b196ecf..b8f5a9bf00 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -158,6 +158,7 @@ extern char *get_namespace_name(Oid nspid); extern char *get_namespace_name_or_temp(Oid nspid); extern Oid get_range_subtype(Oid rangeOid); extern Oid get_range_collation(Oid rangeOid); +extern bool get_index_is_replident(Oid index_oid); #define type_is_array(typid) (get_element_type(typid) != InvalidOid) /* type_is_array_domain accepts both plain arrays and domains over arrays */ diff --git a/src/test/regress/expected/replica_identity.out b/src/test/regress/expected/replica_identity.out index 0da54a0544..332f301413 100644 --- a/src/test/regress/expected/replica_identity.out +++ b/src/test/regress/expected/replica_identity.out @@ -187,5 +187,51 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; n (1 row) +--- +-- Test that ALTER TABLE rewrite preserves nondefault replica identity +--- +-- constraint variant +CREATE TABLE test_replica_identity2 (id int UNIQUE NOT NULL); +ALTER TABLE test_replica_identity2 REPLICA IDENTITY USING INDEX test_replica_identity2_id_key; +\d test_replica_identity2 +Table "public.test_replica_identity2" + Column | Type | Modifiers +--------+---------+----------- + id | integer | not null +Indexes: + "test_replica_identity2_id_key" UNIQUE CONSTRAINT, btree (id) REPLICA IDENTITY + +ALTER TABLE test_replica_identity2 ALTER COLUMN id TYPE bigint; +\d test_replica_identity2 +Table "public.test_replica_identity2" + Column | Type | Modifiers +--------+--------+----------- + id | bigint | not null +Indexes: + "test_replica_identity2_id_key" UNIQUE CONSTRAINT, btree (id) REPLICA IDENTITY + +-- straight index variant +CREATE TABLE test_replica_identity3 (id int NOT NULL); +CREATE UNIQUE INDEX test_replica_identity3_id_key ON test_replica_identity3 (id); +ALTER TABLE test_replica_identity3 REPLICA IDENTITY USING INDEX test_replica_identity3_id_key; +\d test_replica_identity3 +Table "public.test_replica_identity3" + Column | Type | Modifiers +--------+---------+----------- + id | integer | not null +Indexes: + "test_replica_identity3_id_key" UNIQUE, btree (id) REPLICA IDENTITY + +ALTER TABLE test_replica_identity3 ALTER COLUMN id TYPE bigint; +\d test_replica_identity3 +Table "public.test_replica_identity3" + Column | Type | Modifiers +--------+--------+----------- + id | bigint | not null +Indexes: + "test_replica_identity3_id_key" UNIQUE, btree (id) REPLICA IDENTITY + DROP TABLE test_replica_identity; +DROP TABLE test_replica_identity2; +DROP TABLE test_replica_identity3; DROP TABLE test_replica_identity_othertable; diff --git a/src/test/regress/sql/replica_identity.sql b/src/test/regress/sql/replica_identity.sql index bb71fdac96..ab4c72b23a 100644 --- a/src/test/regress/sql/replica_identity.sql +++ b/src/test/regress/sql/replica_identity.sql @@ -79,5 +79,26 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; ALTER TABLE test_replica_identity REPLICA IDENTITY NOTHING; SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; +--- +-- Test that ALTER TABLE rewrite preserves nondefault replica identity +--- + +-- constraint variant +CREATE TABLE test_replica_identity2 (id int UNIQUE NOT NULL); +ALTER TABLE test_replica_identity2 REPLICA IDENTITY USING INDEX test_replica_identity2_id_key; +\d test_replica_identity2 +ALTER TABLE test_replica_identity2 ALTER COLUMN id TYPE bigint; +\d test_replica_identity2 + +-- straight index variant +CREATE TABLE test_replica_identity3 (id int NOT NULL); +CREATE UNIQUE INDEX test_replica_identity3_id_key ON test_replica_identity3 (id); +ALTER TABLE test_replica_identity3 REPLICA IDENTITY USING INDEX test_replica_identity3_id_key; +\d test_replica_identity3 +ALTER TABLE test_replica_identity3 ALTER COLUMN id TYPE bigint; +\d test_replica_identity3 + DROP TABLE test_replica_identity; +DROP TABLE test_replica_identity2; +DROP TABLE test_replica_identity3; DROP TABLE test_replica_identity_othertable; -- 2.20.1
From 3760d0f2060249e750200daadabbbe557cd105a1 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <peter@eisentraut.org> Date: Thu, 5 Mar 2020 10:11:13 +0100 Subject: [PATCH] Preserve replica identity index across ALTER TABLE rewrite --- src/backend/commands/tablecmds.c | 46 +++++++++++++++++++ src/backend/utils/cache/lsyscache.c | 23 ++++++++++ src/include/utils/lsyscache.h | 1 + .../regress/expected/replica_identity.out | 46 +++++++++++++++++++ src/test/regress/sql/replica_identity.sql | 21 +++++++++ 5 files changed, 137 insertions(+) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 1a005760d8..25011f671e 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -173,6 +173,7 @@ typedef struct AlteredTableInfo List *changedConstraintDefs; /* string definitions of same */ List *changedIndexOids; /* OIDs of indexes to rebuild */ List *changedIndexDefs; /* string definitions of same */ + char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */ } AlteredTableInfo; /* Struct describing one new constraint to check in Phase 3 scan */ @@ -9420,6 +9421,22 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, return address; } +/* + * Subroutine for ATExecAlterColumnType: remember that a replica identity + * needs to be reset. + */ +static void +RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab) +{ + if (!get_index_is_replident(indoid)) + return; + + if (tab->replicaIdentityIndex) + elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid); + + tab->replicaIdentityIndex = get_rel_name(indoid); +} + /* * Subroutine for ATExecAlterColumnType: remember that a constraint needs * to be rebuilt (which we might already know). @@ -9439,6 +9456,7 @@ RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab, { /* OK, capture the constraint's existing definition string */ char *defstring = pg_get_constraintdef_command(conoid); + Oid indoid; /* * Put NORMAL dependencies at the front of the list and AUTO @@ -9454,6 +9472,10 @@ RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab, tab->changedConstraintOids); tab->changedConstraintDefs = lcons(defstring, tab->changedConstraintDefs); + + indoid = get_constraint_index(conoid); + if (OidIsValid(indoid)) + RememberReplicaIdentityForRebuilding(indoid, tab); } else { @@ -9461,6 +9483,10 @@ RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab, conoid); tab->changedConstraintDefs = lappend(tab->changedConstraintDefs, defstring); + + indoid = get_constraint_index(conoid); + if (OidIsValid(indoid)) + RememberReplicaIdentityForRebuilding(indoid, tab); } } } @@ -9505,6 +9531,8 @@ RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab) indoid); tab->changedIndexDefs = lappend(tab->changedIndexDefs, defstring); + + RememberReplicaIdentityForRebuilding(indoid, tab); } } } @@ -9697,6 +9725,24 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode) wqueue, lockmode, tab->rewrite); } + /* + * Queue up command to restore replica identity index marking + */ + if (tab->replicaIdentityIndex) + { + AlterTableCmd *cmd = makeNode(AlterTableCmd); + ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt); + + subcmd->identity_type = REPLICA_IDENTITY_INDEX; + subcmd->name = tab->replicaIdentityIndex; + cmd->subtype = AT_ReplicaIdentity; + cmd->def = (Node *) subcmd; + + /* do it after indexes and constraints */ + tab->subcmds[AT_PASS_OLD_CONSTR] = + lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd); + } + /* * Now we can drop the existing constraints and indexes --- constraints * first, since some of them might depend on the indexes. In fact, we diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 88415e6892..dd7e606c16 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -3138,3 +3138,26 @@ get_range_collation(Oid rangeOid) else return InvalidOid; } + +/* + * get_index_is_replident + * + * Returns indisreplident field. + */ +bool +get_index_is_replident(Oid index_oid) +{ + HeapTuple tuple; + Form_pg_index rd_index; + bool result; + + tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid)); + if (!HeapTupleIsValid(tuple)) + return false; + + rd_index = (Form_pg_index) GETSTRUCT(tuple); + result = rd_index->indisreplident; + ReleaseSysCache(tuple); + + return result; +} diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index e9ef5cba34..dbc28ffd2a 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -178,6 +178,7 @@ extern char *get_namespace_name(Oid nspid); extern char *get_namespace_name_or_temp(Oid nspid); extern Oid get_range_subtype(Oid rangeOid); extern Oid get_range_collation(Oid rangeOid); +extern bool get_index_is_replident(Oid index_oid); #define type_is_array(typid) (get_element_type(typid) != InvalidOid) /* type_is_array_domain accepts both plain arrays and domains over arrays */ diff --git a/src/test/regress/expected/replica_identity.out b/src/test/regress/expected/replica_identity.out index 67c34a92a4..65c7c9ac9f 100644 --- a/src/test/regress/expected/replica_identity.out +++ b/src/test/regress/expected/replica_identity.out @@ -186,5 +186,51 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; n (1 row) +--- +-- Test that ALTER TABLE rewrite preserves nondefault replica identity +--- +-- constraint variant +CREATE TABLE test_replica_identity2 (id int UNIQUE NOT NULL); +ALTER TABLE test_replica_identity2 REPLICA IDENTITY USING INDEX test_replica_identity2_id_key; +\d test_replica_identity2 + Table "public.test_replica_identity2" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + id | integer | | not null | +Indexes: + "test_replica_identity2_id_key" UNIQUE CONSTRAINT, btree (id) REPLICA IDENTITY + +ALTER TABLE test_replica_identity2 ALTER COLUMN id TYPE bigint; +\d test_replica_identity2 + Table "public.test_replica_identity2" + Column | Type | Collation | Nullable | Default +--------+--------+-----------+----------+--------- + id | bigint | | not null | +Indexes: + "test_replica_identity2_id_key" UNIQUE CONSTRAINT, btree (id) REPLICA IDENTITY + +-- straight index variant +CREATE TABLE test_replica_identity3 (id int NOT NULL); +CREATE UNIQUE INDEX test_replica_identity3_id_key ON test_replica_identity3 (id); +ALTER TABLE test_replica_identity3 REPLICA IDENTITY USING INDEX test_replica_identity3_id_key; +\d test_replica_identity3 + Table "public.test_replica_identity3" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + id | integer | | not null | +Indexes: + "test_replica_identity3_id_key" UNIQUE, btree (id) REPLICA IDENTITY + +ALTER TABLE test_replica_identity3 ALTER COLUMN id TYPE bigint; +\d test_replica_identity3 + Table "public.test_replica_identity3" + Column | Type | Collation | Nullable | Default +--------+--------+-----------+----------+--------- + id | bigint | | not null | +Indexes: + "test_replica_identity3_id_key" UNIQUE, btree (id) REPLICA IDENTITY + DROP TABLE test_replica_identity; +DROP TABLE test_replica_identity2; +DROP TABLE test_replica_identity3; DROP TABLE test_replica_identity_othertable; diff --git a/src/test/regress/sql/replica_identity.sql b/src/test/regress/sql/replica_identity.sql index 3d2171c733..5277ff39cd 100644 --- a/src/test/regress/sql/replica_identity.sql +++ b/src/test/regress/sql/replica_identity.sql @@ -79,5 +79,26 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; ALTER TABLE test_replica_identity REPLICA IDENTITY NOTHING; SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; +--- +-- Test that ALTER TABLE rewrite preserves nondefault replica identity +--- + +-- constraint variant +CREATE TABLE test_replica_identity2 (id int UNIQUE NOT NULL); +ALTER TABLE test_replica_identity2 REPLICA IDENTITY USING INDEX test_replica_identity2_id_key; +\d test_replica_identity2 +ALTER TABLE test_replica_identity2 ALTER COLUMN id TYPE bigint; +\d test_replica_identity2 + +-- straight index variant +CREATE TABLE test_replica_identity3 (id int NOT NULL); +CREATE UNIQUE INDEX test_replica_identity3_id_key ON test_replica_identity3 (id); +ALTER TABLE test_replica_identity3 REPLICA IDENTITY USING INDEX test_replica_identity3_id_key; +\d test_replica_identity3 +ALTER TABLE test_replica_identity3 ALTER COLUMN id TYPE bigint; +\d test_replica_identity3 + DROP TABLE test_replica_identity; +DROP TABLE test_replica_identity2; +DROP TABLE test_replica_identity3; DROP TABLE test_replica_identity_othertable; -- 2.20.1
From 11a3201c02afae1c8a4550610afdc1d88c6cca6b Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <peter@eisentraut.org> Date: Thu, 5 Mar 2020 10:11:13 +0100 Subject: [PATCH] Preserve replica identity index across ALTER TABLE rewrite --- src/backend/commands/tablecmds.c | 42 +++++++++++++++++ src/backend/utils/cache/lsyscache.c | 23 ++++++++++ src/include/utils/lsyscache.h | 1 + .../regress/expected/replica_identity.out | 46 +++++++++++++++++++ src/test/regress/sql/replica_identity.sql | 21 +++++++++ 5 files changed, 133 insertions(+) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 3eb861bfbf..347869d468 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -176,6 +176,7 @@ typedef struct AlteredTableInfo List *changedConstraintDefs; /* string definitions of same */ List *changedIndexOids; /* OIDs of indexes to rebuild */ List *changedIndexDefs; /* string definitions of same */ + char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */ } AlteredTableInfo; /* Struct describing one new constraint to check in Phase 3 scan */ @@ -11562,6 +11563,22 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, return address; } +/* + * Subroutine for ATExecAlterColumnType: remember that a replica identity + * needs to be reset. + */ +static void +RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab) +{ + if (!get_index_is_replident(indoid)) + return; + + if (tab->replicaIdentityIndex) + elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid); + + tab->replicaIdentityIndex = get_rel_name(indoid); +} + /* * Subroutine for ATExecAlterColumnType: remember that a constraint needs * to be rebuilt (which we might already know). @@ -11580,11 +11597,16 @@ RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab) { /* OK, capture the constraint's existing definition string */ char *defstring = pg_get_constraintdef_command(conoid); + Oid indoid; tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids, conoid); tab->changedConstraintDefs = lappend(tab->changedConstraintDefs, defstring); + + indoid = get_constraint_index(conoid); + if (OidIsValid(indoid)) + RememberReplicaIdentityForRebuilding(indoid, tab); } } @@ -11627,6 +11649,8 @@ RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab) indoid); tab->changedIndexDefs = lappend(tab->changedIndexDefs, defstring); + + RememberReplicaIdentityForRebuilding(indoid, tab); } } } @@ -11735,6 +11759,24 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode) add_exact_object_address(&obj, objects); } + /* + * Queue up command to restore replica identity index marking + */ + if (tab->replicaIdentityIndex) + { + AlterTableCmd *cmd = makeNode(AlterTableCmd); + ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt); + + subcmd->identity_type = REPLICA_IDENTITY_INDEX; + subcmd->name = tab->replicaIdentityIndex; + cmd->subtype = AT_ReplicaIdentity; + cmd->def = (Node *) subcmd; + + /* do it after indexes and constraints */ + tab->subcmds[AT_PASS_OLD_CONSTR] = + lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd); + } + /* * It should be okay to use DROP_RESTRICT here, since nothing else should * be depending on these objects. diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 400e7689fe..4fb3a08183 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -3250,3 +3250,26 @@ get_index_isvalid(Oid index_oid) return isvalid; } + +/* + * get_index_is_replident + * + * Returns indisreplident field. + */ +bool +get_index_is_replident(Oid index_oid) +{ + HeapTuple tuple; + Form_pg_index rd_index; + bool result; + + tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid)); + if (!HeapTupleIsValid(tuple)) + return false; + + rd_index = (Form_pg_index) GETSTRUCT(tuple); + result = rd_index->indisreplident; + ReleaseSysCache(tuple); + + return result; +} diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 131d10eab0..7290b3135f 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -182,6 +182,7 @@ extern Oid get_range_subtype(Oid rangeOid); extern Oid get_range_collation(Oid rangeOid); extern Oid get_index_column_opclass(Oid index_oid, int attno); extern bool get_index_isvalid(Oid index_oid); +extern bool get_index_is_replident(Oid index_oid); #define type_is_array(typid) (get_element_type(typid) != InvalidOid) /* type_is_array_domain accepts both plain arrays and domains over arrays */ diff --git a/src/test/regress/expected/replica_identity.out b/src/test/regress/expected/replica_identity.out index 739608aab4..79002197a7 100644 --- a/src/test/regress/expected/replica_identity.out +++ b/src/test/regress/expected/replica_identity.out @@ -179,5 +179,51 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; n (1 row) +--- +-- Test that ALTER TABLE rewrite preserves nondefault replica identity +--- +-- constraint variant +CREATE TABLE test_replica_identity2 (id int UNIQUE NOT NULL); +ALTER TABLE test_replica_identity2 REPLICA IDENTITY USING INDEX test_replica_identity2_id_key; +\d test_replica_identity2 + Table "public.test_replica_identity2" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + id | integer | | not null | +Indexes: + "test_replica_identity2_id_key" UNIQUE CONSTRAINT, btree (id) REPLICA IDENTITY + +ALTER TABLE test_replica_identity2 ALTER COLUMN id TYPE bigint; +\d test_replica_identity2 + Table "public.test_replica_identity2" + Column | Type | Collation | Nullable | Default +--------+--------+-----------+----------+--------- + id | bigint | | not null | +Indexes: + "test_replica_identity2_id_key" UNIQUE CONSTRAINT, btree (id) REPLICA IDENTITY + +-- straight index variant +CREATE TABLE test_replica_identity3 (id int NOT NULL); +CREATE UNIQUE INDEX test_replica_identity3_id_key ON test_replica_identity3 (id); +ALTER TABLE test_replica_identity3 REPLICA IDENTITY USING INDEX test_replica_identity3_id_key; +\d test_replica_identity3 + Table "public.test_replica_identity3" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + id | integer | | not null | +Indexes: + "test_replica_identity3_id_key" UNIQUE, btree (id) REPLICA IDENTITY + +ALTER TABLE test_replica_identity3 ALTER COLUMN id TYPE bigint; +\d test_replica_identity3 + Table "public.test_replica_identity3" + Column | Type | Collation | Nullable | Default +--------+--------+-----------+----------+--------- + id | bigint | | not null | +Indexes: + "test_replica_identity3_id_key" UNIQUE, btree (id) REPLICA IDENTITY + DROP TABLE test_replica_identity; +DROP TABLE test_replica_identity2; +DROP TABLE test_replica_identity3; DROP TABLE test_replica_identity_othertable; diff --git a/src/test/regress/sql/replica_identity.sql b/src/test/regress/sql/replica_identity.sql index b08a3623b8..a5ac8f5567 100644 --- a/src/test/regress/sql/replica_identity.sql +++ b/src/test/regress/sql/replica_identity.sql @@ -75,5 +75,26 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; ALTER TABLE test_replica_identity REPLICA IDENTITY NOTHING; SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; +--- +-- Test that ALTER TABLE rewrite preserves nondefault replica identity +--- + +-- constraint variant +CREATE TABLE test_replica_identity2 (id int UNIQUE NOT NULL); +ALTER TABLE test_replica_identity2 REPLICA IDENTITY USING INDEX test_replica_identity2_id_key; +\d test_replica_identity2 +ALTER TABLE test_replica_identity2 ALTER COLUMN id TYPE bigint; +\d test_replica_identity2 + +-- straight index variant +CREATE TABLE test_replica_identity3 (id int NOT NULL); +CREATE UNIQUE INDEX test_replica_identity3_id_key ON test_replica_identity3 (id); +ALTER TABLE test_replica_identity3 REPLICA IDENTITY USING INDEX test_replica_identity3_id_key; +\d test_replica_identity3 +ALTER TABLE test_replica_identity3 ALTER COLUMN id TYPE bigint; +\d test_replica_identity3 + DROP TABLE test_replica_identity; +DROP TABLE test_replica_identity2; +DROP TABLE test_replica_identity3; DROP TABLE test_replica_identity_othertable; -- 2.20.1