From 878905788911e1cf6fea0e0b24f1d688343f245b Mon Sep 17 00:00:00 2001
From: Khanna <Shubham.Khanna@fujitsu.com>
Date: Fri, 16 Aug 2024 16:04:30 +0530
Subject: [PATCH v28 2/2] Tap tests for 'include-generated-columns'

Tap tests for for all combinations of replication related to generated columns,
i.e. both with and without 'include_generated_columns' option enabled.
---
 src/test/subscription/t/011_generated.pl | 334 +++++++++++++++++++++--
 1 file changed, 313 insertions(+), 21 deletions(-)

diff --git a/src/test/subscription/t/011_generated.pl b/src/test/subscription/t/011_generated.pl
index 8b2e5f4708..493c0d12dc 100644
--- a/src/test/subscription/t/011_generated.pl
+++ b/src/test/subscription/t/011_generated.pl
@@ -20,35 +20,29 @@ $node_subscriber->start;
 
 my $publisher_connstr = $node_publisher->connstr . ' dbname=postgres';
 
-$node_publisher->safe_psql('postgres',
-	"CREATE TABLE tab1 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED)"
-);
-
-$node_subscriber->safe_psql('postgres',
-	"CREATE TABLE tab1 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 22) STORED, c int)"
-);
-
-# data for initial sync
+$node_publisher->safe_psql(
+	'postgres', qq(
+	CREATE TABLE tab1 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED);
+	INSERT INTO tab1 (a) VALUES (1), (2), (3);
+	CREATE PUBLICATION pub1 FOR ALL TABLES;
+));
 
-$node_publisher->safe_psql('postgres',
-	"INSERT INTO tab1 (a) VALUES (1), (2), (3)");
-
-$node_publisher->safe_psql('postgres',
-	"CREATE PUBLICATION pub1 FOR ALL TABLES");
-$node_subscriber->safe_psql('postgres',
-	"CREATE SUBSCRIPTION sub1 CONNECTION '$publisher_connstr' PUBLICATION pub1"
-);
+$node_subscriber->safe_psql(
+	'postgres', qq(
+	CREATE TABLE tab1 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 22) STORED, c int);
+	CREATE SUBSCRIPTION sub1 CONNECTION '$publisher_connstr' PUBLICATION pub1;
+));
 
-# Wait for initial sync of all subscriptions
+# Wait for initial sync of node_subscriber
 $node_subscriber->wait_for_subscription_sync;
 
+# Check initial sync data is synchronized after initial sync.
 my $result = $node_subscriber->safe_psql('postgres', "SELECT a, b FROM tab1");
 is( $result, qq(1|22
 2|44
 3|66), 'generated columns initial sync');
 
-# data to replicate
-
+# Insert data to verify incremental sync
 $node_publisher->safe_psql('postgres', "INSERT INTO tab1 VALUES (4), (5)");
 
 $node_publisher->safe_psql('postgres', "UPDATE tab1 SET a = 6 WHERE a = 5");
@@ -75,7 +69,7 @@ END $$;
 
 CREATE TRIGGER test1 BEFORE INSERT OR UPDATE ON tab1
   FOR EACH ROW
-  EXECUTE PROCEDURE tab1_trigger_func();
+	EXECUTE PROCEDURE tab1_trigger_func();
 
 ALTER TABLE tab1 ENABLE REPLICA TRIGGER test1;
 });
@@ -96,4 +90,302 @@ is( $result, qq(1|22|
 8|176|18
 9|198|19), 'generated columns replicated with trigger');
 
+$node_subscriber->safe_psql('postgres', "CREATE DATABASE test");
+
+# =============================================================================
+# Testcase start: Publisher table with a generated column (b) and subscriber
+# table a with regular column (b).
+
+# Create table and publication.
+$node_publisher->safe_psql(
+	'postgres', qq(
+	CREATE TABLE tab_gen_to_nogen (a int, b int GENERATED ALWAYS AS (a * 2) STORED);
+	INSERT INTO tab_gen_to_nogen (a) VALUES (1), (2), (3);
+	CREATE PUBLICATION regress_pub_gen_to_nogen FOR TABLE tab_gen_to_nogen;
+));
+
+# Create subscription with include_generated_columns as false.
+$node_subscriber->safe_psql(
+	'postgres', qq(
+	CREATE TABLE tab_gen_to_nogen (a int, b int);
+	CREATE SUBSCRIPTION regress_sub1_gen_to_nogen CONNECTION '$publisher_connstr'
+		PUBLICATION regress_pub_gen_to_nogen WITH (include_generated_columns = false, copy_data = true);
+));
+
+# Create subscription with include_generated_columns as true.
+$node_subscriber->safe_psql(
+	'test', qq(
+	CREATE TABLE tab_gen_to_nogen (a int, b int);
+	CREATE SUBSCRIPTION regress_sub2_gen_to_nogen CONNECTION '$publisher_connstr'
+		PUBLICATION regress_pub_gen_to_nogen WITH (include_generated_columns = true, copy_data = false);
+));
+
+# Wait for initial sync.
+$node_subscriber->wait_for_subscription_sync;
+
+# Verify that column 'b' is not replicated for the subscription
+# regress_sub1_gen_to_nogen when include_generated_columns is set to false
+# during initial sync.
+$result = $node_subscriber->safe_psql('postgres',
+	"SELECT a, b FROM tab_gen_to_nogen");
+is( $result, qq(1|
+2|
+3|), 'tab_gen_to_nogen, when include_generated_columns=false');
+
+# Insert data to verify incremental sync
+$node_publisher->safe_psql('postgres',
+	"INSERT INTO tab_gen_to_nogen VALUES (4), (5)");
+
+# Verify that column 'b' is not replicated for the subscription
+# regress_sub1_gen_to_nogen when include_generated_columns is set to false
+# during incremental sync.
+$node_publisher->wait_for_catchup('regress_sub1_gen_to_nogen');
+$result =
+  $node_subscriber->safe_psql('postgres',
+	"SELECT a, b FROM tab_gen_to_nogen ORDER BY a");
+is( $result, qq(1|
+2|
+3|
+4|
+5|),
+	'confirm generated columns are not replicated when the subscriber-side column is not generated'
+);
+
+# Verify that column 'b' is replicated for the subscription
+# regress_sub2_gen_to_nogen when include_generated_columns is set to true.
+$node_publisher->wait_for_catchup('regress_sub2_gen_to_nogen');
+$result =
+  $node_subscriber->safe_psql('test',
+	"SELECT a, b FROM tab_gen_to_nogen ORDER BY a");
+is( $result, qq(4|8
+5|10),
+	'confirm generated columns are replicated when the subscriber-side column is not generated'
+);
+
+# cleanup
+$node_subscriber->safe_psql('postgres',
+	"DROP SUBSCRIPTION regress_sub1_gen_to_nogen");
+$node_subscriber->safe_psql('test',
+	"DROP SUBSCRIPTION regress_sub2_gen_to_nogen");
+$node_publisher->safe_psql('postgres',
+	"DROP PUBLICATION regress_pub_gen_to_nogen");
+
+# Testcase end: Publisher table with a generated column (b) and subscriber
+# table a with regular column (b).
+# =============================================================================
+
+# =============================================================================
+# Testcase start: Publisher table with a generated column (b) on the publisher,
+# where column (b) is not present on the subscriber.
+
+# Create table and publication.
+$node_publisher->safe_psql(
+	'postgres', qq(
+	CREATE TABLE tab_gen_to_missing (a int, b int GENERATED ALWAYS AS (a * 2) STORED);
+	INSERT INTO tab_gen_to_missing (a) VALUES (1), (2), (3);
+	CREATE PUBLICATION regress_pub_gen_to_missing FOR TABLE tab_gen_to_missing;
+));
+
+# Create subscription with include_generated_columns as false.
+$node_subscriber->safe_psql(
+	'postgres', qq(
+	CREATE TABLE tab_gen_to_missing (a int);
+	CREATE SUBSCRIPTION regress_sub1_gen_to_missing CONNECTION '$publisher_connstr'
+		PUBLICATION regress_pub_gen_to_missing WITH (include_generated_columns = false, copy_data = true);
+));
+
+# Create subscription with include_generated_columns as true.
+$node_subscriber->safe_psql(
+	'test', qq(
+	CREATE TABLE tab_gen_to_missing (a int);
+	CREATE SUBSCRIPTION regress_sub2_gen_to_missing CONNECTION '$publisher_connstr'
+		PUBLICATION regress_pub_gen_to_missing WITH (include_generated_columns = true, copy_data = false);
+));
+
+# Wait for initial sync.
+$node_subscriber->wait_for_subscription_sync;
+
+# Verify that initial sync data is correctly synchronized to the subscription
+# regress_sub1_gen_to_missing with copy_data set to true after the initial sync.
+$result =
+  $node_subscriber->safe_psql('postgres', "SELECT a FROM tab_gen_to_missing");
+is( $result, qq(1
+2
+3), 'tab_gen_to_missing initial sync, when include_generated_columns=false');
+
+# Insert data to verify incremental sync.
+$node_publisher->safe_psql('postgres',
+	"INSERT INTO tab_gen_to_missing VALUES (4), (5)");
+
+# Verify that column 'b' is not replicated for the subscription
+# regress_sub1_gen_to_missing when include_generated_columns is set to false.
+$node_publisher->wait_for_catchup('regress_sub1_gen_to_missing');
+$result =
+  $node_subscriber->safe_psql('postgres',
+	"SELECT a FROM tab_gen_to_missing ORDER BY a");
+is( $result, qq(1
+2
+3
+4
+5), 'missing generated column, include_generated_columns = false');
+
+my $offset = -s $node_subscriber->logfile;
+
+# Verify that an error is thrown during incremental data sync for the
+# subscription regress_sub2_gen_to_missing when include_generated_columns is
+# set to true.
+$node_subscriber->wait_for_log(
+	qr/ERROR: ( [A-Z0-9]+:)? logical replication target relation "public.tab_gen_to_missing" is missing replicated column: "b"/,
+	$offset);
+
+# cleanup
+$node_subscriber->safe_psql('postgres',
+	"DROP SUBSCRIPTION regress_sub1_gen_to_missing");
+$node_publisher->safe_psql('postgres',
+	"DROP PUBLICATION regress_pub_gen_to_missing");
+
+# Testcase end: Publisher table with a generated column (b) on the publisher,
+# where column (b) is not present on the subscriber.
+# =============================================================================
+
+# =============================================================================
+# Testcase start: Subscriber table with a generated column (b) on the
+# subscriber, where column (b) is not present on the publisher.
+
+# Create table and publication.
+$node_publisher->safe_psql(
+	'postgres', qq(
+	CREATE TABLE tab_missing_to_gen (a int);
+	INSERT INTO tab_missing_to_gen (a) VALUES (1), (2), (3);
+	CREATE PUBLICATION regress_pub_missing_to_gen FOR TABLE tab_missing_to_gen;
+));
+
+# Create subscription with include_generated_columns as false.
+$node_subscriber->safe_psql(
+	'postgres', qq(
+	CREATE TABLE tab_missing_to_gen (a int, b int GENERATED ALWAYS AS (a * 22) STORED);
+	CREATE SUBSCRIPTION regress_sub1_missing_to_gen CONNECTION '$publisher_connstr'
+		PUBLICATION regress_pub_missing_to_gen WITH (include_generated_columns = false, copy_data = true);
+));
+
+# Create subscription with include_generated_columns as true.
+$node_subscriber->safe_psql(
+	'test', qq(
+	CREATE TABLE tab_missing_to_gen (a int, b int GENERATED ALWAYS AS (a * 22) STORED);
+	CREATE SUBSCRIPTION regress_sub2_missing_to_gen CONNECTION '$publisher_connstr'
+		PUBLICATION regress_pub_missing_to_gen WITH (include_generated_columns = true, copy_data = false);
+));
+
+# Wait for initial sync.
+$node_subscriber->wait_for_subscription_sync;
+
+# Verify that initial sync data is correctly synchronized to the subscription
+# regress_sub1_gen_to_missing with copy_data set to true following the initial
+# sync.
+$result = $node_subscriber->safe_psql('postgres',
+	"SELECT a, b FROM tab_missing_to_gen");
+is( $result, qq(1|22
+2|44
+3|66), 'tab_missing_to_gen initial sync, when include_generated_columns=false'
+);
+
+# Insert data to verify incremental sync.
+$node_publisher->safe_psql('postgres',
+	"INSERT INTO tab_missing_to_gen VALUES (4), (5)");
+
+# Verify that column 'b' is not replicated but is generated as usual for the
+# subscription regress_sub1_gen_to_missing when include_generated_columns is
+# set to false.
+$node_publisher->wait_for_catchup('regress_sub1_missing_to_gen');
+$result =
+  $node_subscriber->safe_psql('postgres',
+	"SELECT a, b FROM tab_missing_to_gen ORDER BY a");
+is( $result, qq(1|22
+2|44
+3|66
+4|88
+5|110),
+	'confirm when publisher col is missing, subscriber generated columns are generated as normal'
+);
+
+# Verify that column 'b' is not replicated but is generated as expected for the
+# subscription regress_sub1_gen_to_missing when include_generated_columns is set
+# to true.
+$node_publisher->wait_for_catchup('regress_sub2_missing_to_gen');
+$result =
+  $node_subscriber->safe_psql('test',
+	"SELECT a, b FROM tab_missing_to_gen ORDER BY a");
+is( $result, qq(4|88
+5|110),
+	'confirm when publisher col is missing, subscriber generated columns are generated as normal'
+);
+
+# cleanup
+$node_subscriber->safe_psql('postgres',
+	"DROP SUBSCRIPTION regress_sub1_missing_to_gen");
+$node_publisher->safe_psql('postgres',
+	"DROP PUBLICATION regress_pub_missing_to_gen");
+
+# Testcase end: Subscriber table with a generated column (b) on the
+# subscriber, where column (b) is not present on the publisher.
+# =============================================================================
+
+# =============================================================================
+# Testcase start: Publisher table with a non-generated column (b) on the
+# publisher and generated column(b) on subscriber.
+
+# Create table and publication.
+$node_publisher->safe_psql(
+	'postgres', qq(
+	CREATE TABLE tab_nogen_to_gen (a int, b int);
+	INSERT INTO tab_nogen_to_gen (a, b) VALUES (1, 1), (2, 2), (3, 3);
+	CREATE PUBLICATION regress_pub_nogen_to_gen FOR TABLE tab_nogen_to_gen;
+));
+
+$offset = -s $node_subscriber->logfile;
+
+# Create subscription with include_generated_columns as false.
+$node_subscriber->safe_psql(
+	'postgres', qq(
+	CREATE TABLE tab_nogen_to_gen (a int, b int GENERATED ALWAYS AS (a * 22) STORED);
+	CREATE SUBSCRIPTION regress_sub1_nogen_to_gen CONNECTION '$publisher_connstr'
+		PUBLICATION regress_pub_nogen_to_gen WITH (include_generated_columns = false, copy_data = true);
+));
+
+# Verify that an error occurs during the initial sync of data from a
+# non-generated to a generated column for the subscription
+# regress_sub1_nogen_to_gen when include_generated_columns is set to false.
+$node_subscriber->wait_for_log(
+	qr/ERROR: ( [A-Z0-9]:)? logical replication target relation "public.tab_nogen_to_gen" is missing replicated column: "b"/,
+	$offset);
+$node_subscriber->safe_psql('postgres',
+	"DROP SUBSCRIPTION regress_sub1_nogen_to_gen");
+
+# Create subscription with include_generated_columns as true.
+$node_subscriber->safe_psql(
+	'test', qq(
+	CREATE TABLE tab_nogen_to_gen (a int, b int GENERATED ALWAYS AS (a * 22) STORED);
+	CREATE SUBSCRIPTION regress_sub2_nogen_to_gen CONNECTION '$publisher_connstr'
+		PUBLICATION regress_pub_nogen_to_gen  WITH (include_generated_columns = true, copy_data = false);
+));
+
+$offset = -s $node_subscriber->logfile;
+
+# Verify that an error occurs during incremental sync of data from a
+# non-generated to a generated column for the subscription
+# regress_sub2_nogen_to_gen when include_generated_columns is set to true.
+$node_publisher->safe_psql('postgres',
+	"INSERT INTO tab_nogen_to_gen VALUES (4), (5)");
+$node_subscriber->wait_for_log(
+	qr/ERROR: ( [A-Z0-9]:)? logical replication target relation "public.tab_nogen_to_gen" is missing replicated column: "b"/,
+	$offset);
+
+# cleanup
+$node_publisher->safe_psql('postgres',
+	"DROP PUBLICATION regress_pub_nogen_to_gen");
+
+# Testcase end: Replication of table with non-generated column(b) on publisher
+# and generated column(b) on subscriber.
+# =============================================================================
+
 done_testing();
-- 
2.34.1

