]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Flush table's relcache during ALTER TABLE ADD PRIMARY KEY USING INDEX.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 22 Jan 2022 18:32:40 +0000 (13:32 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 22 Jan 2022 18:32:40 +0000 (13:32 -0500)
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

src/backend/catalog/index.c
src/test/regress/expected/publication.out
src/test/regress/sql/publication.sql

index 3ece136b3d2af89464539c6d830579c341bce315..5368023de32bd9a999ea198728c11e97a0a8fc6c 100644 (file)
@@ -2025,6 +2025,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);
 
@@ -2038,6 +2039,7 @@ index_constraint_create(Relation heapRelation,
                {
                        indexForm->indisprimary = true;
                        dirty = true;
+                       marked_as_primary = true;
                }
 
                if (deferrable && indexForm->indimmediate)
@@ -2050,6 +2052,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);
                }
index 06c7b157d6afb9fd549f3e76f1f7d128d9b13f99..856ea29e373dafa84ea13835325feabe2433e41f 100644 (file)
@@ -204,6 +204,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
index 1071334825a39fbf1ef94e356164f6a2f88d7c6d..6d3f471dd2df4e033d346740c85b124a0bf2f132 100644 (file)
@@ -105,6 +105,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