Flush table's relcache during ALTER TABLE ADD PRIMARY KEY USING INDEX.

Previously, unless we had to add a NOT NULL constraint to the column,
this command resulted in updating only the index's relcache entry.
That's problematic when replication behavior is being driven off the
existence of a primary key: other sessions (and ours too for that
matter) failed to recalculate their opinion of whether the table can
be replicated.  Add a relcache invalidation to fix it.

This has been broken since pg_class.relhaspkey was removed in v11.
Before that, updating the table's relhaspkey value sufficed to cause
a cache flush.  Hence, backpatch to v11.

Report and patch by Hou Zhijie

Discussion: https://postgr.es/m/OS0PR01MB5716EBE01F112C62F8F9B786947B9@OS0PR01MB5716.jpnprd01.prod.outlook.com
This commit is contained in:
Tom Lane 2022-01-22 13:32:40 -05:00
parent 1f655fdc39
commit d8fbbb925b
3 changed files with 38 additions and 0 deletions

View File

@ -2071,6 +2071,7 @@ index_constraint_create(Relation heapRelation,
HeapTuple indexTuple;
Form_pg_index indexForm;
bool dirty = false;
bool marked_as_primary = false;
pg_index = table_open(IndexRelationId, RowExclusiveLock);
@ -2084,6 +2085,7 @@ index_constraint_create(Relation heapRelation,
{
indexForm->indisprimary = true;
dirty = true;
marked_as_primary = true;
}
if (deferrable && indexForm->indimmediate)
@ -2096,6 +2098,15 @@ index_constraint_create(Relation heapRelation,
{
CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);
/*
* When we mark an existing index as primary, force a relcache
* flush on its parent table, so that all sessions will become
* aware that the table now has a primary key. This is important
* because it affects some replication behaviors.
*/
if (marked_as_primary)
CacheInvalidateRelcache(heapRelation);
InvokeObjectPostAlterHookArg(IndexRelationId, indexRelationId, 0,
InvalidOid, is_internal);
}

View File

@ -349,6 +349,20 @@ Publications:
"testpib_ins_trunct"
"testpub_fortbl"
-- verify relation cache invalidation when a primary key is added using
-- an existing index
CREATE TABLE pub_test.testpub_addpk (id int not null, data int);
ALTER PUBLICATION testpub_default ADD TABLE pub_test.testpub_addpk;
INSERT INTO pub_test.testpub_addpk VALUES(1, 11);
CREATE UNIQUE INDEX testpub_addpk_id_idx ON pub_test.testpub_addpk(id);
-- fail:
UPDATE pub_test.testpub_addpk SET id = 2;
ERROR: cannot update table "testpub_addpk" because it does not have a replica identity and publishes updates
HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE.
ALTER TABLE pub_test.testpub_addpk ADD PRIMARY KEY USING INDEX testpub_addpk_id_idx;
-- now it should work:
UPDATE pub_test.testpub_addpk SET id = 2;
DROP TABLE pub_test.testpub_addpk;
-- permissions
SET ROLE regress_publication_user2;
CREATE PUBLICATION testpub2; -- fail

View File

@ -193,6 +193,19 @@ ALTER PUBLICATION testpub_default DROP TABLE pub_test.testpub_nopk;
\d+ testpub_tbl1
-- verify relation cache invalidation when a primary key is added using
-- an existing index
CREATE TABLE pub_test.testpub_addpk (id int not null, data int);
ALTER PUBLICATION testpub_default ADD TABLE pub_test.testpub_addpk;
INSERT INTO pub_test.testpub_addpk VALUES(1, 11);
CREATE UNIQUE INDEX testpub_addpk_id_idx ON pub_test.testpub_addpk(id);
-- fail:
UPDATE pub_test.testpub_addpk SET id = 2;
ALTER TABLE pub_test.testpub_addpk ADD PRIMARY KEY USING INDEX testpub_addpk_id_idx;
-- now it should work:
UPDATE pub_test.testpub_addpk SET id = 2;
DROP TABLE pub_test.testpub_addpk;
-- permissions
SET ROLE regress_publication_user2;
CREATE PUBLICATION testpub2; -- fail