Skip to content

Commit c9ef507

Browse files
committed
Preserve replica identity index across ALTER TABLE rewrite
If an index was explicitly set as replica identity index, this setting was lost when a table was rewritten by ALTER TABLE. Because this setting is part of pg_index but actually controlled by ALTER TABLE (not part of CREATE INDEX, say), we have to do some extra work to restore it. Based-on-patch-by: Quan Zongliang <quanzongliang@gmail.com> Reviewed-by: Euler Taveira <euler.taveira@2ndquadrant.com> Discussion: https://www.postgresql.org/message-id/flat/c70fcab2-4866-0d9f-1d01-e75e189db342@gmail.com
1 parent 630590d commit c9ef507

File tree

5 files changed

+133
-0
lines changed

5 files changed

+133
-0
lines changed

src/backend/commands/tablecmds.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ typedef struct AlteredTableInfo
174174
List *changedConstraintDefs; /* string definitions of same */
175175
List *changedIndexOids; /* OIDs of indexes to rebuild */
176176
List *changedIndexDefs; /* string definitions of same */
177+
char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
177178
} AlteredTableInfo;
178179

179180
/* Struct describing one new constraint to check in Phase 3 scan */
@@ -11138,6 +11139,22 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
1113811139
return address;
1113911140
}
1114011141

11142+
/*
11143+
* Subroutine for ATExecAlterColumnType: remember that a replica identity
11144+
* needs to be reset.
11145+
*/
11146+
static void
11147+
RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
11148+
{
11149+
if (!get_index_isreplident(indoid))
11150+
return;
11151+
11152+
if (tab->replicaIdentityIndex)
11153+
elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
11154+
11155+
tab->replicaIdentityIndex = get_rel_name(indoid);
11156+
}
11157+
1114111158
/*
1114211159
* Subroutine for ATExecAlterColumnType: remember that a constraint needs
1114311160
* to be rebuilt (which we might already know).
@@ -11156,11 +11173,16 @@ RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
1115611173
{
1115711174
/* OK, capture the constraint's existing definition string */
1115811175
char *defstring = pg_get_constraintdef_command(conoid);
11176+
Oid indoid;
1115911177

1116011178
tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
1116111179
conoid);
1116211180
tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
1116311181
defstring);
11182+
11183+
indoid = get_constraint_index(conoid);
11184+
if (OidIsValid(indoid))
11185+
RememberReplicaIdentityForRebuilding(indoid, tab);
1116411186
}
1116511187
}
1116611188

@@ -11203,6 +11225,8 @@ RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
1120311225
indoid);
1120411226
tab->changedIndexDefs = lappend(tab->changedIndexDefs,
1120511227
defstring);
11228+
11229+
RememberReplicaIdentityForRebuilding(indoid, tab);
1120611230
}
1120711231
}
1120811232
}
@@ -11311,6 +11335,24 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
1131111335
add_exact_object_address(&obj, objects);
1131211336
}
1131311337

11338+
/*
11339+
* Queue up command to restore replica identity index marking
11340+
*/
11341+
if (tab->replicaIdentityIndex)
11342+
{
11343+
AlterTableCmd *cmd = makeNode(AlterTableCmd);
11344+
ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
11345+
11346+
subcmd->identity_type = REPLICA_IDENTITY_INDEX;
11347+
subcmd->name = tab->replicaIdentityIndex;
11348+
cmd->subtype = AT_ReplicaIdentity;
11349+
cmd->def = (Node *) subcmd;
11350+
11351+
/* do it after indexes and constraints */
11352+
tab->subcmds[AT_PASS_OLD_CONSTR] =
11353+
lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
11354+
}
11355+
1131411356
/*
1131511357
* It should be okay to use DROP_RESTRICT here, since nothing else should
1131611358
* be depending on these objects.

src/backend/utils/cache/lsyscache.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3228,6 +3228,29 @@ get_index_column_opclass(Oid index_oid, int attno)
32283228
return opclass;
32293229
}
32303230

3231+
/*
3232+
* get_index_isreplident
3233+
*
3234+
* Given the index OID, return pg_index.indisreplident.
3235+
*/
3236+
bool
3237+
get_index_isreplident(Oid index_oid)
3238+
{
3239+
HeapTuple tuple;
3240+
Form_pg_index rd_index;
3241+
bool result;
3242+
3243+
tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid));
3244+
if (!HeapTupleIsValid(tuple))
3245+
return false;
3246+
3247+
rd_index = (Form_pg_index) GETSTRUCT(tuple);
3248+
result = rd_index->indisreplident;
3249+
ReleaseSysCache(tuple);
3250+
3251+
return result;
3252+
}
3253+
32313254
/*
32323255
* get_index_isvalid
32333256
*

src/include/utils/lsyscache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ extern char *get_namespace_name_or_temp(Oid nspid);
181181
extern Oid get_range_subtype(Oid rangeOid);
182182
extern Oid get_range_collation(Oid rangeOid);
183183
extern Oid get_index_column_opclass(Oid index_oid, int attno);
184+
extern bool get_index_isreplident(Oid index_oid);
184185
extern bool get_index_isvalid(Oid index_oid);
185186

186187
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)

src/test/regress/expected/replica_identity.out

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,5 +179,51 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass;
179179
n
180180
(1 row)
181181

182+
---
183+
-- Test that ALTER TABLE rewrite preserves nondefault replica identity
184+
---
185+
-- constraint variant
186+
CREATE TABLE test_replica_identity2 (id int UNIQUE NOT NULL);
187+
ALTER TABLE test_replica_identity2 REPLICA IDENTITY USING INDEX test_replica_identity2_id_key;
188+
\d test_replica_identity2
189+
Table "public.test_replica_identity2"
190+
Column | Type | Collation | Nullable | Default
191+
--------+---------+-----------+----------+---------
192+
id | integer | | not null |
193+
Indexes:
194+
"test_replica_identity2_id_key" UNIQUE CONSTRAINT, btree (id) REPLICA IDENTITY
195+
196+
ALTER TABLE test_replica_identity2 ALTER COLUMN id TYPE bigint;
197+
\d test_replica_identity2
198+
Table "public.test_replica_identity2"
199+
Column | Type | Collation | Nullable | Default
200+
--------+--------+-----------+----------+---------
201+
id | bigint | | not null |
202+
Indexes:
203+
"test_replica_identity2_id_key" UNIQUE CONSTRAINT, btree (id) REPLICA IDENTITY
204+
205+
-- straight index variant
206+
CREATE TABLE test_replica_identity3 (id int NOT NULL);
207+
CREATE UNIQUE INDEX test_replica_identity3_id_key ON test_replica_identity3 (id);
208+
ALTER TABLE test_replica_identity3 REPLICA IDENTITY USING INDEX test_replica_identity3_id_key;
209+
\d test_replica_identity3
210+
Table "public.test_replica_identity3"
211+
Column | Type | Collation | Nullable | Default
212+
--------+---------+-----------+----------+---------
213+
id | integer | | not null |
214+
Indexes:
215+
"test_replica_identity3_id_key" UNIQUE, btree (id) REPLICA IDENTITY
216+
217+
ALTER TABLE test_replica_identity3 ALTER COLUMN id TYPE bigint;
218+
\d test_replica_identity3
219+
Table "public.test_replica_identity3"
220+
Column | Type | Collation | Nullable | Default
221+
--------+--------+-----------+----------+---------
222+
id | bigint | | not null |
223+
Indexes:
224+
"test_replica_identity3_id_key" UNIQUE, btree (id) REPLICA IDENTITY
225+
182226
DROP TABLE test_replica_identity;
227+
DROP TABLE test_replica_identity2;
228+
DROP TABLE test_replica_identity3;
183229
DROP TABLE test_replica_identity_othertable;

src/test/regress/sql/replica_identity.sql

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,5 +75,26 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass;
7575
ALTER TABLE test_replica_identity REPLICA IDENTITY NOTHING;
7676
SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass;
7777

78+
---
79+
-- Test that ALTER TABLE rewrite preserves nondefault replica identity
80+
---
81+
82+
-- constraint variant
83+
CREATE TABLE test_replica_identity2 (id int UNIQUE NOT NULL);
84+
ALTER TABLE test_replica_identity2 REPLICA IDENTITY USING INDEX test_replica_identity2_id_key;
85+
\d test_replica_identity2
86+
ALTER TABLE test_replica_identity2 ALTER COLUMN id TYPE bigint;
87+
\d test_replica_identity2
88+
89+
-- straight index variant
90+
CREATE TABLE test_replica_identity3 (id int NOT NULL);
91+
CREATE UNIQUE INDEX test_replica_identity3_id_key ON test_replica_identity3 (id);
92+
ALTER TABLE test_replica_identity3 REPLICA IDENTITY USING INDEX test_replica_identity3_id_key;
93+
\d test_replica_identity3
94+
ALTER TABLE test_replica_identity3 ALTER COLUMN id TYPE bigint;
95+
\d test_replica_identity3
96+
7897
DROP TABLE test_replica_identity;
98+
DROP TABLE test_replica_identity2;
99+
DROP TABLE test_replica_identity3;
79100
DROP TABLE test_replica_identity_othertable;

0 commit comments

Comments
 (0)