From 9b880007d888b72ca6219f1e063489ef0814f7d2 Mon Sep 17 00:00:00 2001
From: Shubham Khanna <shubham.khanna@fujitsu.com>
Date: Thu, 7 Nov 2024 15:54:19 +0530
Subject: [PATCH v1] Error-message-improvement

The error message was misleading, as it failed to clarify that the replication
of regular column on the publisher to the corresponding generated column on
the subscriber is not supported.

This patch improves the error handling and reporting mechanism to make it clear
that the replication of regular column on the subscriber is not supported,
resolving the misleading "missing column" error.
---
 src/backend/replication/logical/relation.c | 33 +++++++++++++++++-
 src/test/subscription/t/011_generated.pl   | 39 ++++++++++++++++++++++
 2 files changed, 71 insertions(+), 1 deletion(-)

diff --git a/src/backend/replication/logical/relation.c b/src/backend/replication/logical/relation.c
index f5a0ef2bd9..5dca494355 100644
--- a/src/backend/replication/logical/relation.c
+++ b/src/backend/replication/logical/relation.c
@@ -380,6 +380,10 @@ logicalrep_rel_open(LogicalRepRelId remoteid, LOCKMODE lockmode)
 		MemoryContext oldctx;
 		int			i;
 		Bitmapset  *missingatts;
+		StringInfoData gencolsattsbuf;
+		int			generatedatts = 0;
+
+		initStringInfo(&gencolsattsbuf);
 
 		/* Release the no-longer-useful attrmap, if any. */
 		if (entry->attrmap)
@@ -421,7 +425,7 @@ logicalrep_rel_open(LogicalRepRelId remoteid, LOCKMODE lockmode)
 			int			attnum;
 			Form_pg_attribute attr = TupleDescAttr(desc, i);
 
-			if (attr->attisdropped || attr->attgenerated)
+			if (attr->attisdropped)
 			{
 				entry->attrmap->attnums[i] = -1;
 				continue;
@@ -432,9 +436,36 @@ logicalrep_rel_open(LogicalRepRelId remoteid, LOCKMODE lockmode)
 
 			entry->attrmap->attnums[i] = attnum;
 			if (attnum >= 0)
+
+			{
+				if (attr->attgenerated)
+
+					/*
+					 * Check if the subscription table generated column has
+					 * same name as a non-generated column in the
+					 * corresponding publication table.
+					 */
+				{
+					generatedatts++;
+					if (generatedatts == 1)
+						appendStringInfo(&gencolsattsbuf, _("\"%s\""),
+										 NameStr(attr->attname));
+					else
+						appendStringInfo(&gencolsattsbuf, _(", \"%s\""),
+										 NameStr(attr->attname));
+				}
+
 				missingatts = bms_del_member(missingatts, attnum);
+			}
 		}
 
+		if (generatedatts != 0)
+			ereport(ERROR,
+					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+					 errmsg_plural("replicating to a target relation's generated column \"%s\" for \"%s.%s\" is not supported",
+								   "replicating to a target relation's generated column \"%s\" for \"%s.%s\" is not supported",
+								   generatedatts, gencolsattsbuf.data, remoterel->nspname, remoterel->relname)));
+
 		logicalrep_report_missing_attrs(remoterel, missingatts);
 
 		/* be tidy */
diff --git a/src/test/subscription/t/011_generated.pl b/src/test/subscription/t/011_generated.pl
index 211b54c316..111aad452e 100644
--- a/src/test/subscription/t/011_generated.pl
+++ b/src/test/subscription/t/011_generated.pl
@@ -326,4 +326,43 @@ is( $result, qq(|2|
 $node_subscriber->safe_psql('postgres', "DROP SUBSCRIPTION sub1");
 $node_publisher->safe_psql('postgres', "DROP PUBLICATION pub1");
 
+# =============================================================================
+# Exercise logical replication of a regular column to a subscriber side
+# generated column.
+#
+# A "normal --> generated" replication fails, reporting an error that the
+# replication of a generated column on subscriber side is not supported.
+# =============================================================================
+
+# --------------------------------------------------
+# Test Case: normal --> generated
+# Publisher table has regular columns 'c2' and 'c3'.
+# Subscriber table has generated columns 'c2' and 'c3'.
+# --------------------------------------------------
+
+# Create table and publication. Insert data into the table.
+$node_publisher->safe_psql(
+	'postgres', qq(
+	CREATE TABLE t1(c1 int, c2 int, c3 int);
+	CREATE PUBLICATION pub1 for table t1(c1, c2, c3);
+	INSERT INTO t1 VALUES (1);
+));
+
+# Create table and subscription.
+$node_subscriber->safe_psql(
+	'postgres', qq(
+	CREATE TABLE t1(c1 int, c2 int GENERATED ALWAYS AS (c1 + 2) STORED, c3 int GENERATED ALWAYS AS (c1 + 2) STORED);
+	CREATE SUBSCRIPTION sub1 CONNECTION '$publisher_connstr' PUBLICATION pub1;
+));
+
+# Verify that an error occurs.
+my $offset = -s $node_subscriber->logfile;
+$node_subscriber->wait_for_log(
+	qr/ERROR: ( [A-Z0-9]:)? replicating to a target relation's generated column ""c2", "c3"" for "public.t1" is not supported/,
+	$offset);
+
+# cleanup
+$node_subscriber->safe_psql('postgres', "DROP SUBSCRIPTION sub1");
+$node_publisher->safe_psql('postgres', "DROP PUBLICATION pub1");
+
 done_testing();
-- 
2.41.0.windows.3

