Here are my review comments for v14-0002.

======
src/backend/replication/logical/tablesync.c

make_copy_attnamelist:

nitpick - remove excessive parentheses in palloc0 call.

nitpick - Code is fine AFAICT except it's not immediately obvious
localgenlist is indexed by the *remote* attribute number. So I renamed
'attrnum' variable in my nitpicks diff. OTOH, if you think no change
is necessary, that is OK to (in that case maybe add a comment).

~~~

1. fetch_remote_table_info

+ if ((server_version >= 120000 && server_version <= 160000) ||
+ !MySubscription->includegencols)
+ appendStringInfo(&cmd, " AND a.attgenerated = ''");

Should this say < 180000 instead of <= 160000?

~~~

copy_table:

nitpick - uppercase in comment

nitpick - missing space after "if"

~~~

2. copy_table

+ attnamelist = make_copy_attnamelist(relmapentry, remotegenlist);
+
  /* Start copy on the publisher. */
  initStringInfo(&cmd);

- /* Regular table with no row filter */
- if (lrel.relkind == RELKIND_RELATION && qual == NIL)
+ /* check if remote column list has generated columns */
+ if(MySubscription->includegencols)
+ {
+ for (int i = 0; i < relmapentry->remoterel.natts; i++)
+ {
+ if(remotegenlist[i])
+ {
+ remote_has_gencol = true;
+ break;
+ }
+ }
+ }
+

There is some subtle logic going on here:

For example, the comment here says "Check if the remote column list
has generated columns", and it then proceeds to iterate the remote
attributes checking the remotegenlist[i]. But the remotegenlist[] was
returned from a prior call to make_copy_attnamelist() and according to
the make_copy_attnamelist logic, it is NOT returning all remote
generated-cols in that list. Specifically, it is stripping some of
them -- "Do not include generated columns of the subscription table in
the [remotegenlist] column list.".

So, actually this loop seems to be only finding cases (setting
remote_has_gen = true) where the remote column is generated but the
match local column is *not* generated. Maybe this was the intended
logic all along but then certainly the comment should be improved to
describe it better.

~~~

3.
+ /*
+ * Regular table with no row filter and 'include_generated_columns'
+ * specified as 'false' during creation of subscription.
+ */
+ if (lrel.relkind == RELKIND_RELATION && qual == NIL && !remote_has_gencol)

nitpick - This comment also needs improving. For example, just because
remote_has_gencol is false, it does not follow that
'include_generated_columns' was specified as 'false' -- maybe the
parameter was 'true' but the table just had no generated columns
anyway... I've modified the comment already in my nitpicks diff, but
probably you can improve on that.

~

nitpick - "else" comment is modified slightly too. Please see the nitpicks diff.

~

4.
In hindsight, I felt your variable 'remote_has_gencol' was not
well-named because it is not for saying the remote table has a
generated column -- it is saying the remote table has a generated
column **that we have to copy**. So, rather it should be named
something like 'gencol_copy_needed' (but I didn't change this name in
the nitpick diffs...)

======
src/test/subscription/t/004_sync.pl

nitpick - changes to comment style to make the test case separations
much more obvious
nitpick - minor comment wording tweaks

5.
Here, you are confirming we get an ERROR when replicating from a
non-generated column to a generated column. But I think your patch
also added exactly that same test scenario in the 011_generated (as
the sub5 test). So, maybe this one here should be removed?

======
src/test/subscription/t/011_generated.pl

nitpick - comment wrapping at 80 chars
nitpick - add/remove blank lines for readability
nitpick - typo /subsriber/subscriber/
nitpick - prior to the ALTER test, tab6 is unsubscribed. So add
another test to verify its initial data
nitpick - sometimes the msg 'add a new table to existing publication'
is misplaced
nitpick - the tests for tab6 and tab5 were in opposite to the expected
order, so swapped them.

======
99.
Please see also the attached diff which implements all the nitpicks
described in this post.

======
Kind Regards,
Peter Smith.
Fujitsu Australia
diff --git a/src/backend/replication/logical/tablesync.c 
b/src/backend/replication/logical/tablesync.c
index 38f3621..c264353 100644
--- a/src/backend/replication/logical/tablesync.c
+++ b/src/backend/replication/logical/tablesync.c
@@ -704,30 +704,30 @@ make_copy_attnamelist(LogicalRepRelMapEntry *rel, bool 
*remotegenlist)
        TupleDesc       desc;
 
        desc = RelationGetDescr(rel->localrel);
-       localgenlist = palloc0((rel->remoterel.natts * sizeof(bool)));
+       localgenlist = palloc0(rel->remoterel.natts * sizeof(bool));
 
        /*
         * This loop checks for generated columns on subscription table.
         */
        for (int i = 0; i < desc->natts; i++)
        {
-               int                     attnum;
+               int                     remote_attnum;
                Form_pg_attribute attr = TupleDescAttr(desc, i);
 
                if (!attr->attgenerated)
                        continue;
 
-               attnum = logicalrep_rel_att_by_name(&rel->remoterel,
+               remote_attnum = logicalrep_rel_att_by_name(&rel->remoterel,
                                                                                
        NameStr(attr->attname));
 
-               if (attnum >= 0)
+               if (remote_attnum >= 0)
                {
                        /*
                         * Check if the subscription table generated column has 
same
                         * name as a non-generated column in the corresponding
                         * publication table.
                         */
-                       if (!remotegenlist[attnum])
+                       if (!remotegenlist[remote_attnum])
                                ereport(ERROR,
                                                
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                                                 errmsg("logical replication 
target relation \"%s.%s\" has a generated column \"%s\" "
@@ -739,7 +739,7 @@ make_copy_attnamelist(LogicalRepRelMapEntry *rel, bool 
*remotegenlist)
                         * the subscription table. Later, we use this 
information to
                         * skip adding this column to the column list for COPY.
                         */
-                       localgenlist[attnum] = true;
+                       localgenlist[remote_attnum] = true;
                }
        }
 
@@ -1205,12 +1205,12 @@ copy_table(Relation rel)
        /* Start copy on the publisher. */
        initStringInfo(&cmd);
 
-       /* check if remote column list has generated columns */
+       /* Check if remote column list has generated columns */
        if(MySubscription->includegencols)
        {
                for (int i = 0; i < relmapentry->remoterel.natts; i++)
                {
-                       if(remotegenlist[i])
+                       if (remotegenlist[i])
                        {
                                remote_has_gencol = true;
                                break;
@@ -1219,8 +1219,8 @@ copy_table(Relation rel)
        }
 
        /*
-        * Regular table with no row filter and 'include_generated_columns'
-        * specified as 'false' during creation of subscription.
+        * Regular table with no row filter and copy of generated columns is
+        * not necessary.
         */
        if (lrel.relkind == RELKIND_RELATION && qual == NIL && 
!remote_has_gencol)
        {
@@ -1258,8 +1258,9 @@ copy_table(Relation rel)
                 * SELECT query with OR'ed row filters for COPY.
                 *
                 * We also need to use this same COPY (SELECT ...) syntax when
-                * 'include_generated_columns' is specified as true, because 
copy
-                * of generated columns is not supported by the normal COPY.
+                * 'include_generated_columns' is specified as true and the 
remote
+                * table has generated columns, because copy of generated 
columns is
+                * not supported by the normal COPY.
                 */
                int i = 0;
 
diff --git a/src/test/subscription/t/004_sync.pl 
b/src/test/subscription/t/004_sync.pl
index 68052e1..62462c0 100644
--- a/src/test/subscription/t/004_sync.pl
+++ b/src/test/subscription/t/004_sync.pl
@@ -176,18 +176,24 @@ ok( $node_publisher->poll_query_until(
 $node_publisher->safe_psql('postgres', "DROP TABLE tab_rep");
 $node_subscriber->safe_psql('postgres', "DROP TABLE tab_rep");
 
-# When a subscription table have a column missing as specified on publication 
table
+#
+# TEST CASE:
+#
+# When a subscription table has a column missing that was specified on
+# the publication table.
+#
+
 # setup structure with existing data on publisher
 $node_publisher->safe_psql('postgres', "CREATE TABLE tab_rep (a int, b int)");
 $node_publisher->safe_psql('postgres',
        "INSERT INTO tab_rep VALUES (1, 1), (2, 2), (3, 3)");
 
-# add table on subscriber
+# add table on subscriber; note column 'b' is missing
 $node_subscriber->safe_psql('postgres', "CREATE TABLE tab_rep (a int)");
 
 my $offset = -s $node_subscriber->logfile;
 
-# recreate the subscription again
+# create the subscription
 $node_subscriber->safe_psql('postgres',
        "CREATE SUBSCRIPTION tap_sub CONNECTION '$publisher_connstr' 
PUBLICATION tap_pub"
 );
@@ -201,15 +207,23 @@ $node_subscriber->wait_for_log(
 $node_subscriber->safe_psql('postgres', "DROP SUBSCRIPTION tap_sub");
 $node_subscriber->safe_psql('postgres', "DROP TABLE tab_rep");
 
-# When a subscription table have a generated column corresponding to 
non-generated column on publication table
-# create table on subscriber side with generated column
+#
+# TEST CASE:
+#
+# When a subscription table has a generated column corresponding to a
+# non-generated column on publication table
+#
+
+# create table on subscriber side with generated column 'b'
 $node_subscriber->safe_psql('postgres',
        "CREATE TABLE tab_rep (a int, b int GENERATED ALWAYS AS (a * 2) 
STORED)");
+
+# create the subscription
 $node_subscriber->safe_psql('postgres',
        "CREATE SUBSCRIPTION tap_sub CONNECTION '$publisher_connstr' 
PUBLICATION tap_pub"
 );
 
-# check for missing column error
+# check for generated column mismatch error
 $node_subscriber->wait_for_log(
        qr/ERROR: ( [A-Z0-9]+:)? logical replication target relation 
"public.tab_rep" has a generated column "b" but corresponding column on source 
relation is not a generated column/,
        $offset);
diff --git a/src/test/subscription/t/011_generated.pl 
b/src/test/subscription/t/011_generated.pl
index 2628ad3..9569f57 100644
--- a/src/test/subscription/t/011_generated.pl
+++ b/src/test/subscription/t/011_generated.pl
@@ -35,33 +35,37 @@ $node_publisher->safe_psql('postgres',
        "CREATE TABLE tab2 (a int, b int GENERATED ALWAYS AS (a * 2) STORED)");
 $node_subscriber->safe_psql('postgres', "CREATE TABLE tab2 (a int, b int)");
 
-# publisher-side tab3 has generated col 'b' but subscriber-side tab3 has 
DIFFERENT COMPUTATION generated col 'b'.
+# tab3:
+# publisher-side tab3 has generated col 'b' but
+# subscriber-side tab3 has DIFFERENT COMPUTATION generated col 'b'.
 $node_publisher->safe_psql('postgres',
        "CREATE TABLE tab3 (a int, b int GENERATED ALWAYS AS (a + 10) STORED)");
 $node_subscriber->safe_psql('postgres',
        "CREATE TABLE tab3 (a int, b int GENERATED ALWAYS AS (a + 20) STORED)");
 
-# publisher-side tab4 has generated cols 'b' and 'c' but subscriber-side tab4 
has non-generated col 'b', and generated-col 'c'
-# where columns on the subscriber are in a different order
+# tab4:
+# publisher-side tab4 has generated cols 'b' and 'c' but
+# subscriber-side tab4 has non-generated col 'b', and generated-col 'c'
+# where columns on publisher/subscriber are in a different order
 $node_publisher->safe_psql('postgres',
        "CREATE TABLE tab4 (a int, b int GENERATED ALWAYS AS (a * 2) STORED, c 
int GENERATED ALWAYS AS (a * 2) STORED)"
 );
-
 $node_subscriber->safe_psql('postgres',
        "CREATE TABLE tab4 (c int GENERATED ALWAYS AS (a * 22) STORED, a int, b 
int)"
 );
 
-# publisher-side tab5 has non-generated col 'b' but subscriber-side tab5 has 
generated col 'b'
+# tab5:
+# publisher-side tab5 has non-generated col 'b' but
+# subscriber-side tab5 has generated col 'b'
 $node_publisher->safe_psql('postgres', "CREATE TABLE tab5 (a int, b int)");
-
 $node_subscriber->safe_psql('postgres',
        "CREATE TABLE tab5 (a int, b int GENERATED ALWAYS AS (a * 22) STORED)");
 
-# test for alter subscription ... refresh publication
+# tab6:
+# tables for testing ALTER SUBSCRIPTIO ... REFRESH PUBLICATION
 $node_publisher->safe_psql('postgres',
        "CREATE TABLE tab6 (a int, b int GENERATED ALWAYS AS (a * 2) STORED, c 
int GENERATED ALWAYS AS (a * 2) STORED)"
 );
-
 $node_subscriber->safe_psql('postgres',
        "CREATE TABLE tab6 (a int, b int, c int GENERATED ALWAYS AS (a * 22) 
STORED)"
 );
@@ -129,6 +133,10 @@ is( $result, qq(1|2|22
 2|4|44
 3|6|66), 'generated column initial sync');
 
+$result = $node_subscriber->safe_psql('postgres',
+       "SELECT a, b, c FROM tab6 ORDER BY a");
+is( $result, qq(), 'unsubscribed table initial data');
+
 # data to replicate
 
 $node_publisher->safe_psql('postgres', "INSERT INTO tab1 VALUES (4), (5)");
@@ -178,9 +186,11 @@ is( $result, qq(1|21
        'confirm generated columns are NOT replicated when the subscriber-side 
column is also generated'
 );
 
+#
 # TEST tab4: the publisher-side cols 'b' and 'c' are generated and 
subscriber-side
 # col 'b' is not generated and col 'c' is generated. So confirmed that the 
different
-# order of columns on subsriber-side replicate data to correct columns.
+# order of columns on subscriber-side replicate data to correct columns.
+#
 $node_publisher->safe_psql('postgres', "INSERT INTO tab4 VALUES (4), (5)");
 $node_publisher->wait_for_catchup('sub4');
 $result =
@@ -192,12 +202,26 @@ is( $result, qq(1|2|22
 4|8|88
 5|10|110), 'replicate generated columns with different order on subscriber');
 
-# TEST for ALTER SUBSCRIPTION ... REFRESH PUBLICATION
-# Add a new table to publication
+#
+# TEST tab5: publisher-side col 'b' is not-generated and subscriber-side col 
'b'
+# is generated, so confirmed that col 'b' IS NOT replicated and it will throw 
an error.
+# The subscription sub5 is created here, instead of earlier with the other 
subscriptions,
+# because sub5 will cause the tablesync worker to restart repetitively.
+#
+my $offset = -s $node_subscriber->logfile;
+$node_subscriber->safe_psql('postgres',
+       "CREATE SUBSCRIPTION sub5 CONNECTION '$publisher_connstr' PUBLICATION 
pub5 WITH (include_generated_columns = true)"
+);
+$node_subscriber->wait_for_log(
+       qr/ERROR: ( [A-Z0-9]:)? logical replication target relation 
"public.tab5" has a generated column "b" but corresponding column on source 
relation is not a generated column/,
+       $offset);
+$node_subscriber->safe_psql('postgres', "DROP SUBSCRIPTION sub5");
+
+#
+# TEST tab6: After ALTER SUBSCRIPTION ... REFRESH PUBLICATION
+#
 $node_publisher->safe_psql('postgres',
        "ALTER PUBLICATION pub4 ADD TABLE tab6");
-
-# Refresh publication after table is added to publication
 $node_subscriber->safe_psql('postgres',
        "ALTER SUBSCRIPTION sub4 REFRESH PUBLICATION");
 $node_publisher->wait_for_catchup('sub4');
@@ -207,7 +231,10 @@ is( $result, qq(1|2|22
 2|4|44
 3|6|66), 'add new table to existing publication');
 
-# TEST: drop generated column on subscriber side
+#
+# TEST tab6: Drop the generated column's expression on subscriber side.
+# This changes the generated column into a non-generated column.
+#
 $node_subscriber->safe_psql('postgres',
        "ALTER TABLE tab6 ALTER COLUMN c DROP EXPRESSION");
 $node_publisher->safe_psql('postgres',
@@ -218,21 +245,7 @@ is( $result, qq(1|2|22
 2|4|44
 3|6|66
 4|8|8
-5|10|10), 'add new table to existing publication');
-
-# TEST tab5: publisher-side col 'b' is not-generated and subscriber-side col 
'b' is generated.
-# so confirmed that col 'b' IS NOT replicated and it will throw an error.
-# SUBSCRIPTION sub5 is created separately as sub5 will cause table sync worker 
to restart
-# repetitively
-my $offset = -s $node_subscriber->logfile;
-$node_subscriber->safe_psql('postgres',
-       "CREATE SUBSCRIPTION sub5 CONNECTION '$publisher_connstr' PUBLICATION 
pub5 WITH (include_generated_columns = true)"
-);
-$node_subscriber->wait_for_log(
-       qr/ERROR: ( [A-Z0-9]:)? logical replication target relation 
"public.tab5" has a generated column "b" but corresponding column on source 
relation is not a generated column/,
-       $offset);
-$node_subscriber->safe_psql('postgres', "DROP SUBSCRIPTION sub5");
-
+5|10|10), 'after drop generated column expression');
 
 # try it with a subscriber-side trigger
 

Reply via email to