From 9b799635db437aafd56a73220c6d56e86e31d10e Mon Sep 17 00:00:00 2001
From: "houzj.fnst" <houzj.fnst@fujitsu.com>
Date: Tue, 14 Dec 2021 14:52:22 +0800
Subject: [PATCH] fix replication after ATTACH or DETACH partition

---
 src/backend/replication/pgoutput/pgoutput.c | 11 ++++
 src/test/subscription/t/013_partition.pl    | 71 ++++++++++++++++++++-
 2 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c
index 6f6a203dea..81376e2be7 100644
--- a/src/backend/replication/pgoutput/pgoutput.c
+++ b/src/backend/replication/pgoutput/pgoutput.c
@@ -1337,6 +1337,11 @@ rel_sync_cache_relation_cb(Datum arg, Oid relid)
 	/*
 	 * Reset schema sent status as the relation definition may have changed.
 	 * Also free any objects that depended on the earlier definition.
+	 *
+	 * Besides, it's possible that the relation was ATTACHed to or DETACHed
+	 * from a published partitioned table, in which case the publication
+	 * actions of this relation would become invalid. So, we also need to
+	 * invalidate the publication actions here.
 	 */
 	if (entry != NULL)
 	{
@@ -1354,6 +1359,12 @@ rel_sync_cache_relation_cb(Datum arg, Oid relid)
 			free_conversion_map(entry->map);
 		}
 		entry->map = NULL;
+
+		entry->replicate_valid = false;
+		entry->pubactions.pubinsert = false;
+		entry->pubactions.pubupdate = false;
+		entry->pubactions.pubdelete = false;
+		entry->pubactions.pubtruncate = false;
 	}
 }
 
diff --git a/src/test/subscription/t/013_partition.pl b/src/test/subscription/t/013_partition.pl
index 6005178a32..2403bbfe4e 100644
--- a/src/test/subscription/t/013_partition.pl
+++ b/src/test/subscription/t/013_partition.pl
@@ -6,7 +6,7 @@ use strict;
 use warnings;
 use PostgreSQL::Test::Cluster;
 use PostgreSQL::Test::Utils;
-use Test::More tests => 63;
+use Test::More tests => 66;
 
 # setup
 
@@ -347,6 +347,33 @@ $result =
   $node_subscriber2->safe_psql('postgres', "SELECT a FROM tab1 ORDER BY 1");
 is($result, qq(), 'truncate of tab1 replicated');
 
+# check the replication after attaching to or detaching from a published
+# partitioned table
+$node_publisher->safe_psql('postgres',
+	"ALTER TABLE tab1 DETACH PARTITION tab1_2");
+
+# the change on tab1_2 won't be replicated after detaching tab1_2
+# from the partitioned table.
+$node_publisher->safe_psql('postgres',
+	"INSERT INTO tab1_2 (a) VALUES (4)");
+
+$node_publisher->safe_psql('postgres',
+	"ALTER TABLE tab1 ATTACH PARTITION tab1_2 FOR VALUES IN (4, 5, 6)");
+
+# the change on tab1_2 will be replicated after attaching tab1_2 to
+# the partitioned table
+$node_publisher->safe_psql('postgres',
+	"INSERT INTO tab1_2 (a) VALUES (5)");
+
+$node_publisher->wait_for_catchup('sub1');
+
+$result =
+  $node_subscriber1->safe_psql('postgres', "SELECT a FROM tab1_2 ORDER BY 1");
+is($result, qq(5), 'insert into tab1_2 replicated');
+
+# clean the data
+$node_publisher->safe_psql('postgres', "TRUNCATE tab1_2");
+
 # Check that subscriber handles cases where update/delete target tuple
 # is missing.  We have to look for the DEBUG1 log messages about that,
 # so temporarily bump up the log verbosity.
@@ -456,6 +483,9 @@ $node_subscriber2->safe_psql('postgres',
 $node_subscriber2->safe_psql('postgres',
 	"CREATE TABLE tab2 (a int PRIMARY KEY, c text DEFAULT 'sub2_tab2', b text)"
 );
+$node_subscriber2->safe_psql('postgres',
+	"CREATE TABLE tab2_2 (a int PRIMARY KEY, c text DEFAULT 'sub2_tab2_2', b text)"
+);
 $node_subscriber2->safe_psql('postgres',
 	"CREATE TABLE tab3 (a int PRIMARY KEY, c text DEFAULT 'sub2_tab3', b text)"
 );
@@ -707,6 +737,45 @@ pub_tab2|3|yyy
 pub_tab2|5|zzz
 xxx_c|6|aaa), 'inserts into tab2 replicated');
 
+# check the replication after attaching to or detaching from a published
+# partitioned table
+$node_publisher->safe_psql('postgres',
+	"ALTER TABLE tab2 DETACH PARTITION tab2_2"
+);
+
+# the change on tab2_2 won't be replicated to sub_viaroot after detaching tab2_2
+# from the partitioned table
+$node_publisher->safe_psql('postgres',
+	"INSERT INTO tab2_2 VALUES (7, 'bbb')");
+
+$node_publisher->safe_psql('postgres',
+	"ALTER TABLE tab2 ATTACH PARTITION tab2_2 FOR VALUES IN (5, 6, 7, 8)"
+);
+
+# the change on tab2_2 will be replicated to sub_viaroot again after
+# attaching tab2_2 to the partitioned table
+$node_publisher->safe_psql('postgres',
+	"INSERT INTO tab2_2 (a, b) VALUES (8, 'aaa')");
+
+$node_publisher->wait_for_catchup('sub_viaroot');
+$node_publisher->wait_for_catchup('sub2');
+
+$result = $node_subscriber1->safe_psql('postgres',
+	"SELECT c, a, b FROM tab2 ORDER BY 1, 2");
+is( $result, qq(pub_tab2|1|xxx
+pub_tab2|3|yyy
+pub_tab2|5|zzz
+pub_tab2|8|aaa
+xxx_c|6|aaa), 'insert values (8, aaa) replicated');
+
+$result = $node_subscriber2->safe_psql('postgres',
+	"SELECT c, a, b FROM tab2 ORDER BY 1, 2");
+is( $result, qq(pub_tab2|1|xxx
+pub_tab2|3|yyy
+pub_tab2|5|zzz
+pub_tab2|8|aaa
+xxx_c|6|aaa), 'insert values (8, aaa) replicated');
+
 # Check that subscriber handles cases where update/delete target tuple
 # is missing.  We have to look for the DEBUG1 log messages about that,
 # so temporarily bump up the log verbosity.
-- 
2.18.4

