Hi Shubham,
here are my review comments for patch v4-0001.
======
src/backend/replication/logical/relation.c
logicalrep_report_missing_and_gen_attrs:
1.
static void
-logicalrep_report_missing_attrs(LogicalRepRelation *remoterel,
- Bitmapset *missingatts)
+logicalrep_report_missing_and_gen_attrs(LogicalRepRelation *remoterel,
+ Bitmapset *atts,
+ bool ismissing)
Maybe the function should be called
'logicalrep_report_missing_or_gen_attrs' (not 'and')
~
2.
- if (!bms_is_empty(missingatts))
+ if (!bms_is_empty(atts))
I felt this should be an Assert because the code becomes easier to
read if you check this before making the call in the first place. See
my NITPICKS patch.
~
3.
+ if (attcnt == 1)
+ appendStringInfo(&attsbuf, _("\"%s\""),
remoterel->attnames[i]);
else
- appendStringInfo(&missingattsbuf, _(", \"%s\""),
+ appendStringInfo(&attsbuf, _(", \"%s\""),
remoterel->attnames[i]);
}
This code can be simplified (e.g. remove the 'else' etc if you just
check > 1 instead). See my NITPICKS patch.
SUGGESTION
if (attcnt > 1)
appendStringInfo(&attsbuf, _(", "));
appendStringInfo(&attsbuf, _("\"%s\""), remoterel->attnames[i]);
~~~
logicalrep_rel_open:
4.
+ /*
+ * Include it in generatedattrs if publishing to a generated
+ * column.
+ */
+ if (attr->attgenerated)
+ generatedattrs = bms_add_member(generatedattrs, attnum);
That comment can be simpler if indeed it is needed at all.
SUGGESTION:
/* Remember which subscriber columns are generated. */
~
5.
As I reported above (#2), I think it is better to check for empty BMS
in the caller because then the code is easier to read. Also, you need
to comment on which of these 2 errors will take precedence because if
there are simultaneous problems you are still only reporting one kind
of error at a time.
SUGGESTION:
/*
* Report any missing or generated columns. Note, if there are both
* kinds then the 'missing' error takes precedence.
*/
if (!bms_is_empty(missingatts))
logicalrep_report_missing_and_gen_attrs(remoterel, missingatts,
true);
if (!bms_is_empty(generatedattrs))
logicalrep_report_missing_and_gen_attrs(remoterel, generatedattrs,
false);
======
src/test/subscription/t/011_generated.pl
6.
+# =============================================================================
+# The following test cases exercise logical replication for the combinations
+# where there is a generated column on one or both sides of pub/sub:
+# - regular -> generated and generated -> generated
+# - regular -> missing
+# =============================================================================
6a.
This comment is not quite right. You can't say "where there is a
generated column on one or both sides of pub/sub" because that is not
true for the "regular -> missing" case. See NITPICKS for a suggested
comment.
~
6b.
IMO you should also be testing the "generated -> missing" combination.
You don't need more tests -- just more columns.
~
6c
You also need to include a test where there are BOTH generated and
missing to show the 'missing' error takes precedence. Again, you don't
need more separate test cases to achieve this -- just need more
columns in the tables.
~~~
7.
+# --------------------------------------------------
+# A "regular -> generated" and "generated -> generated" replication fails,
+# reporting an error that the generated column on the subscriber side
+# cannot be replicated.
/and/or/
~~~
8.
+# --------------------------------------------------
+# A "regular -> missing" replication fails, reporting an error
+# that the subscriber side is missing replicated columns.
+#
+# Testcase: regular -> missing
+# Publisher table has regular columns 'c2' and 'c3'.
+# Subscriber table is missing columns 'c2' and 'c3'.
+# --------------------------------------------------
I've also added the "generated -> missing" combination and addressed
the review comment about intercluding a test where there are BOTH
missing and generated columns, so you can see which error takes
precedence. Please see the NITPICKS diff.
======
Kind Regards,
Peter Smith.
Fujitsu Australia
diff --git a/src/backend/replication/logical/relation.c
b/src/backend/replication/logical/relation.c
index 2c7be7d..21f4f12 100644
--- a/src/backend/replication/logical/relation.c
+++ b/src/backend/replication/logical/relation.c
@@ -224,49 +224,46 @@ logicalrep_rel_att_by_name(LogicalRepRelation *remoterel,
const char *attname)
* generated columns.'
*/
static void
-logicalrep_report_missing_and_gen_attrs(LogicalRepRelation *remoterel,
+logicalrep_report_missing_or_gen_attrs(LogicalRepRelation *remoterel,
Bitmapset *atts,
bool ismissing)
{
- if (!bms_is_empty(atts))
- {
- StringInfoData attsbuf;
- int attcnt = 0;
- int i;
+ StringInfoData attsbuf;
+ int attcnt = 0;
+ int i;
- initStringInfo(&attsbuf);
+ Assert(!bms_is_empty(atts));
- i = -1;
- while ((i = bms_next_member(atts, i)) >= 0)
- {
- attcnt++;
- if (attcnt == 1)
- appendStringInfo(&attsbuf, _("\"%s\""),
-
remoterel->attnames[i]);
- else
- appendStringInfo(&attsbuf, _(", \"%s\""),
-
remoterel->attnames[i]);
- }
+ initStringInfo(&attsbuf);
- if (ismissing)
- ereport(ERROR,
-
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg_plural("logical replication
target relation \"%s.%s\" is missing replicated column: %s",
- "logical
replication target relation \"%s.%s\" is missing replicated columns: %s",
- attcnt,
-
remoterel->nspname,
-
remoterel->relname,
-
attsbuf.data)));
- else
- ereport(ERROR,
-
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg_plural("cannot replicate to
target relation \"%s.%s\" generated column: %s",
- "cannot
replicate to target relation \"%s.%s\" generated columns: %s",
- attcnt,
-
remoterel->nspname,
-
remoterel->relname,
-
attsbuf.data)));
+ i = -1;
+ while ((i = bms_next_member(atts, i)) >= 0)
+ {
+ attcnt++;
+ if (attcnt > 1)
+ appendStringInfo(&attsbuf, _(", "));
+
+ appendStringInfo(&attsbuf, _("\"%s\""), remoterel->attnames[i]);
}
+
+ if (ismissing)
+ ereport(ERROR,
+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg_plural("logical replication target
relation \"%s.%s\" is missing replicated column: %s",
+ "logical replication
target relation \"%s.%s\" is missing replicated columns: %s",
+ attcnt,
+ remoterel->nspname,
+ remoterel->relname,
+ attsbuf.data)));
+ else
+ ereport(ERROR,
+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg_plural("cannot replicate to target
relation \"%s.%s\" generated column: %s",
+ "cannot replicate to
target relation \"%s.%s\" generated columns: %s",
+ attcnt,
+ remoterel->nspname,
+ remoterel->relname,
+ attsbuf.data)));
}
/*
@@ -447,10 +444,7 @@ logicalrep_rel_open(LogicalRepRelId remoteid, LOCKMODE
lockmode)
entry->attrmap->attnums[i] = attnum;
if (attnum >= 0)
{
- /*
- * Include it in generatedattrs if publishing
to a generated
- * column.
- */
+ /* Remember which subscriber columns are
generated. */
if (attr->attgenerated)
generatedattrs =
bms_add_member(generatedattrs, attnum);
@@ -458,12 +452,16 @@ logicalrep_rel_open(LogicalRepRelId remoteid, LOCKMODE
lockmode)
}
}
- /* Report replicating to generated columns. */
- logicalrep_report_missing_and_gen_attrs(remoterel,
generatedattrs,
-
false);
- /* Report missing columns. */
- logicalrep_report_missing_and_gen_attrs(remoterel, missingatts,
-
true);
+ /*
+ * Report any missing or generated columns. Note, if there are
both
+ * kinds then the 'missing' error takes precedence.
+ */
+ if (!bms_is_empty(missingatts))
+ logicalrep_report_missing_or_gen_attrs(remoterel,
missingatts,
+
true);
+ if (!bms_is_empty(generatedattrs))
+ logicalrep_report_missing_or_gen_attrs(remoterel,
generatedattrs,
+
false);
/* be tidy */
bms_free(generatedattrs);
diff --git a/src/test/subscription/t/011_generated.pl
b/src/test/subscription/t/011_generated.pl
index 2a2df36..5a360de 100644
--- a/src/test/subscription/t/011_generated.pl
+++ b/src/test/subscription/t/011_generated.pl
@@ -327,14 +327,17 @@ $node_subscriber->safe_psql('postgres', "DROP
SUBSCRIPTION sub1");
$node_publisher->safe_psql('postgres', "DROP PUBLICATION pub1");
# =============================================================================
-# The following test cases exercise logical replication for the combinations
-# where there is a generated column on one or both sides of pub/sub:
-# - regular -> generated and generated -> generated
+# The following tests for expected errors when attempting to replicate to a
+# missing or a generated subscriber column. Test the folowing combinations
+# - regular -> generated
+# - generated -> generated
# - regular -> missing
+# - generated -> missing
+# - test if both errors exist, then the missing error is reported first
# =============================================================================
# --------------------------------------------------
-# A "regular -> generated" and "generated -> generated" replication fails,
+# A "regular -> generated" or "generated -> generated" replication fails,
# reporting an error that the generated column on the subscriber side
# cannot be replicated.
#
@@ -369,33 +372,37 @@ $node_subscriber->safe_psql('postgres', "DROP
SUBSCRIPTION sub1");
$node_publisher->safe_psql('postgres', "DROP PUBLICATION pub1");
# --------------------------------------------------
-# A "regular -> missing" replication fails, reporting an error
-# that the subscriber side is missing replicated columns.
+# A "regular -> missing" or "generated -> missing" replication fails,
+# reporting an error that the subscriber side is missing replicated columns.
#
-# Testcase: regular -> missing
-# Publisher table has regular columns 'c2' and 'c3'.
-# Subscriber table is missing columns 'c2' and 'c3'.
+# Testcase: regular -> missing and generated -> missing
+# Publisher table has regular column 'c4' and generated column 'c5'.
+# Subscriber table is missing columns 'c4' and 'c5'.
+#
+# Notice how the first 3 columns of t2 are identical to the previous test,
+# so this table also has generated column errors, but they are not reported
+# because the 'missing' column error takes precedence.
# --------------------------------------------------
# Create table and publication. Insert data into the table.
$node_publisher->safe_psql(
'postgres', qq(
- CREATE TABLE t2(c1 int, c2 int, c3 int);
- CREATE PUBLICATION pub1 for table t2(c1, c2, c3);
+ CREATE TABLE t2(c1 int, c2 int, c3 int GENERATED ALWAYS AS (c1 * 2)
STORED, c4 int, c5 int GENERATED ALWAYS AS (c1 * 2) STORED);
+ CREATE PUBLICATION pub1 for table t2(c1, c2, c3, c4, c5);
INSERT INTO t2 VALUES (1);
));
# Create table and subscription.
$node_subscriber->safe_psql(
'postgres', qq(
- CREATE TABLE t2(c1 int);
+ CREATE TABLE t2(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.
$offset = -s $node_subscriber->logfile;
$node_subscriber->wait_for_log(
- qr/ERROR: ( [A-Z0-9]:)? logical replication target relation "public.t2"
is missing replicated columns: "c2", "c3"/,
+ qr/ERROR: ( [A-Z0-9]:)? logical replication target relation "public.t2"
is missing replicated columns: "c4", "c5"/,
$offset);
# cleanup