From 4a5b61e2977e53f9fe316cd7c013c4a12f514fba Mon Sep 17 00:00:00 2001
From: "houzj.fnst" <houzj.fnst@cn.fujitsu.com>
Date: Thu, 11 Nov 2021 18:31:08 +0800
Subject: [PATCH] Invalidate relcache when setting REPLICA IDENTITY

When changing REPLICA IDENTITY INDEX to another one, the target table's
relcache would not be invalidated which could cause unexpected behaviour when
replicating UPDATE change.

So, Invalidate the relcache in this case to make sure the the bitmap of index
attribute is rebuilt

Author: Tang Haiying, Hou Zhijie
Reviewed-by: Amit Kapila
Backpatch-through: 10, where it was introduced
Discussion: https://www.postgresql.org/message-id/OS0PR01MB61133CA11630DAE45BC6AD95FB939%40OS0PR01MB6113.jpnprd01.prod.outlook.com

---
 src/backend/commands/tablecmds.c    | 10 ++++++
 src/test/subscription/t/100_bugs.pl | 69 ++++++++++++++++++++++++++++++++++++-
 2 files changed, 78 insertions(+), 1 deletion(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index d492f27..067494e 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -14815,6 +14815,7 @@ relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
 	Form_pg_index pg_index_form;
 
 	ListCell   *index;
+	bool		need_rel_inval = false;
 
 	/*
 	 * Check whether relreplident has changed, and update it if so.
@@ -14891,10 +14892,19 @@ relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
 			CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
 			InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
 										 InvalidOid, is_internal);
+			need_rel_inval = true;
 		}
 		heap_freetuple(pg_index_tuple);
 	}
 
+	/*
+	 * Invalidate the relcache for the table, so that after we commit all
+	 * sessions will refresh the table's replica identity index before
+	 * attempting any update on the table.
+	 */
+	if (need_rel_inval)
+		CacheInvalidateRelcache(rel);
+
 	table_close(pg_index, RowExclusiveLock);
 }
 
diff --git a/src/test/subscription/t/100_bugs.pl b/src/test/subscription/t/100_bugs.pl
index d1e407a..a7a4e6a 100644
--- a/src/test/subscription/t/100_bugs.pl
+++ b/src/test/subscription/t/100_bugs.pl
@@ -3,7 +3,7 @@ use strict;
 use warnings;
 use PostgresNode;
 use TestLib;
-use Test::More tests => 5;
+use Test::More tests => 7;
 
 # Bug #15114
 
@@ -153,3 +153,70 @@ is($node_twoways->safe_psql('d2', "SELECT count(f) FROM t"),
 	$rows * 2, "2x$rows rows in t");
 is($node_twoways->safe_psql('d2', "SELECT count(f) FROM t2"),
 	$rows * 2, "2x$rows rows in t2");
+
+# Test the replication of the table after changing REPLICA IDENTITY INDEX
+
+# The bug was that when changing REPLICA IDENTITY INDEX to another one, the
+# target table's relcache would not be invalidated. This could cause unexpected
+# behaviour when replicating UPDATE change.
+$node_publisher = get_new_node('publisher3');
+$node_publisher->init(allows_streaming => 'logical');
+$node_publisher->start;
+
+$node_subscriber = get_new_node('subscriber3');
+$node_subscriber->init(allows_streaming => 'logical');
+$node_subscriber->start;
+
+$node_publisher->safe_psql('postgres',
+	"CREATE TABLE tab_replidentity_index(a int not null, b int not null)");
+$node_publisher->safe_psql('postgres',
+	"CREATE UNIQUE INDEX idx_replidentity_index_a ON tab_replidentity_index(a)"
+);
+$node_publisher->safe_psql('postgres',
+	"CREATE UNIQUE INDEX idx_replidentity_index_b ON tab_replidentity_index(b)"
+);
+
+# use index idx_replidentity_index_a as REPLICA IDENTITY in publisher.
+$node_publisher->safe_psql('postgres',
+	"ALTER TABLE tab_replidentity_index REPLICA IDENTITY USING INDEX idx_replidentity_index_a;"
+);
+$node_publisher->safe_psql('postgres',
+	"INSERT INTO tab_replidentity_index VALUES(1, 1)");
+
+$node_subscriber->safe_psql('postgres',
+	"CREATE TABLE tab_replidentity_index(a int not null, b int not null)");
+$node_subscriber->safe_psql('postgres',
+	"CREATE UNIQUE INDEX idx_replidentity_index_b ON tab_replidentity_index(b)"
+);
+
+$publisher_connstr = $node_publisher->connstr . ' dbname=postgres';
+$node_publisher->safe_psql('postgres',
+	"CREATE PUBLICATION tap_pub FOR TABLE tab_replidentity_index");
+$node_subscriber->safe_psql('postgres',
+	"CREATE SUBSCRIPTION tap_sub CONNECTION '$publisher_connstr' PUBLICATION tap_pub"
+);
+
+$node_publisher->wait_for_catchup('tap_sub');
+
+is($node_subscriber->safe_psql('postgres', "SELECT * FROM tab_replidentity_index"),
+	qq(1|1),
+	"check initial data on subscriber");
+
+# use index idx_replidentity_index_a as REPLICA IDENTITY in subcriber.
+$node_subscriber->safe_psql('postgres', qq[
+	ALTER TABLE tab_replidentity_index REPLICA IDENTITY USING INDEX idx_replidentity_index_b;
+]);
+
+# Set publisher's REPLICA IDENTITY to idx_replidentity_index_b, then UPDATE the table.
+$node_publisher->safe_psql('postgres', qq[
+	ALTER TABLE tab_replidentity_index REPLICA IDENTITY USING INDEX idx_replidentity_index_b;
+	UPDATE tab_replidentity_index SET a = -a;
+]);
+
+$node_publisher->wait_for_catchup('tap_sub');
+is( $node_subscriber->safe_psql('postgres', "SELECT * FROM tab_replidentity_index"),
+	qq(-1|1),
+	"update works with REPLICA IDENTITY");
+
+$node_publisher->stop('fast');
+$node_subscriber->stop('fast');
-- 
2.7.2.windows.1

