Hi, hackers!
I am looking at subscription creation command:
CREATE SUBSCRIPTION sub CONNECTION '...' PUBLICATION pub WITH (origin =
none, copy_data = on);
For now we log a warning if the publisher has subscribed to the same
table from some other publisher.
However, in case of publication with publish_via_partition_root option,
we will not raise such warinigs
because SQL command in check_publications_origin() checks only directly
published tables.
For example:
CREATE TABLE t(id int) PARTITION BY RANGE(id);
CREATE TABLE part1 PARTITION OF t FOR VALUES FROM (0) TO (5);
CREATE TABLE part2 PARTITION OF t FOR VALUES FROM (5) TO (10);
-- subscribe to part2
CREATE SUBSCRIPTION sub_part2 CONNECTION '...' PUBLICATION pub_part2;
CREATE PUBLICATION pub_t FOR TABLE t;
CREATE PUBLICATION pub_t_via_root FOR TABLE t WITH
(publish_via_partition_root);
and now this command will raise a warning:
CREATE SUBSCRIPTION sub1 CONNECTION '...' PUBLICATION pub_t WITH (origin
= none, copy_data = on);
but not this:
CREATE SUBSCRIPTION sub1 CONNECTION '...' PUBLICATION pub_t_via_root
WITH (origin = none, copy_data = on);
We also do not take into account cases of foreign partitions:
CREATE TABLE t(id int) PARTITION BY RANGE(id);
CREATE TABLE part1 PARTITION OF t FOR VALUES FROM (0) TO (5);
CREATE FOREIGN TABLE part2 PARTITION OF t FOR VALUES FROM (5) TO (10)
SERVER fdw_server;
CREATE PUBLICATION pub_t FOR TABLE t;
Maybe we should raise WARNING (or even ERROR) in such cases?
I would also note that the (origin = none) will work as expected, but in
case of (origin = any)
it will lead to inappropriate behavior - we will perform an initial sync
of "t", but we unable to
replicate further updates for "part2".
I have attached patch, demonstrating this problems
diff --git a/src/test/subscription/Makefile b/src/test/subscription/Makefile
index ce1ca43009..e5e9bb469c 100644
--- a/src/test/subscription/Makefile
+++ b/src/test/subscription/Makefile
@@ -13,7 +13,7 @@ subdir = src/test/subscription
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
-EXTRA_INSTALL = contrib/hstore
+EXTRA_INSTALL = contrib/hstore contrib/postgres_fdw
export with_icu
diff --git a/src/test/subscription/t/030_origin.pl b/src/test/subscription/t/030_origin.pl
index adfae1a56e..d310e79ba8 100644
--- a/src/test/subscription/t/030_origin.pl
+++ b/src/test/subscription/t/030_origin.pl
@@ -255,6 +255,134 @@ $node_A->safe_psql('postgres', "DROP TABLE tab_new");
$node_B->safe_psql('postgres', "DROP TABLE tab_new");
$node_A->safe_psql('postgres', "DROP SUBSCRIPTION $subname_AB2");
+###############################################################################
+# Setting the origin option to NONE must raise WARNING if we subscribe to a
+# partitioned table, if this table contains any remotely originated data, even
+# if a publication was created with the publish_via_partition_root option.
+#
+# node_A node_C
+# _____________________ __________
+# | ts.t |---------------->| ts.t |
+# |_____________________| |__________|
+# | ts.part1 | ts.part2 |
+# |__________|__________|
+# node_B ^
+# __________ |
+# | ts.part2 |----------------------
+# |__________|
+#
+###############################################################################
+$node_B->safe_psql('postgres', qq(
+CREATE SCHEMA ts;
+CREATE TABLE ts.part2(id int);
+CREATE PUBLICATION pub_b FOR TABLE ts.part2;
+));
+$node_A->safe_psql('postgres', qq(
+CREATE SCHEMA ts;
+CREATE TABLE ts.t(id int) PARTITION BY RANGE(id);
+CREATE TABLE ts.part1 PARTITION OF ts.t FOR VALUES FROM (0) TO (5);
+CREATE TABLE ts.part2 PARTITION OF ts.t FOR VALUES FROM (5) TO (10);
+));
+$node_A->safe_psql('postgres',"CREATE SUBSCRIPTION sub_b CONNECTION '$node_B_connstr' PUBLICATION pub_b;");
+$node_A->safe_psql('postgres',"
+CREATE PUBLICATION pub_a FOR TABLE ts.t;
+CREATE PUBLICATION pub_a_via_root FOR TABLE ts.t WITH (publish_via_partition_root);
+CREATE PUBLICATION pub_a_schema FOR TABLES IN SCHEMA ts WITH (publish_via_partition_root);
+");
+
+$node_C->safe_psql('postgres',"
+CREATE SCHEMA ts;
+CREATE TABLE ts.t(id int) PARTITION BY RANGE(id);
+CREATE TABLE ts.part1 PARTITION OF ts.t FOR VALUES FROM (0) TO (5);
+CREATE TABLE ts.part2 PARTITION OF ts.t FOR VALUES FROM (5) TO (10);");
+
+my $pub;
+# in this case, only "pub_a" will raise WARNING. However, all of them must do it.
+my @publications = ("pub_a", "pub_a_via_root", "pub_a_schema");
+foreach $pub (@publications)
+{
+ note("check warning for publication $pub");
+ ($result, $stdout, $stderr) = $node_C->psql('postgres',"
+ CREATE SUBSCRIPTION sub_c CONNECTION '$node_A_connstr' PUBLICATION $pub WITH (origin = none, copy_data = on);
+ ");
+
+ like(
+ $stderr,
+ qr/WARNING: ( [A-Z0-9]+:)? subscription "sub_c" requested copy_data with origin = NONE but might copy data that had a different origin/,
+ "subscribe to $pub must raise WARNING"
+ );
+ $node_C->psql('postgres',"DROP SUBSCRIPTION IF EXISTS sub_c");
+}
+$node_A->safe_psql('postgres',qq(
+DROP SCHEMA ts CASCADE;
+DROP PUBLICATION pub_a;
+DROP PUBLICATION pub_a_via_root;
+DROP PUBLICATION pub_a_schema;
+));
+$node_B->safe_psql('postgres',"DROP SCHEMA ts CASCADE");
+$node_C->safe_psql('postgres',"DROP SCHEMA ts CASCADE");
+
+###############################################################################
+# Also, we must check foreign partitions if the origin option is set to NONE.
+# Maybe we should raise ERROR in these cases for any origin value?
+###############################################################################
+$node_A->safe_psql('postgres',"CREATE EXTENSION postgres_fdw");
+$node_B->safe_psql('postgres',"CREATE TABLE part22(id int);");
+my $fdwconnstr = "dbname 'postgres', host '" . $node_B->host . "', port '" . $node_B->port . "'";
+$node_A->safe_psql('postgres',qq(
+CREATE SERVER fdw FOREIGN DATA WRAPPER postgres_fdw OPTIONS ($fdwconnstr);
+CREATE USER MAPPING FOR CURRENT_USER SERVER fdw;
+));
+
+# Create a partitioned table:
+# _______________________________________
+# | t |
+# |_______________________________________|
+# | part1 | part2 |
+# |_______|_______________________________|
+# | part21 | part22 (foreign) |
+# |____________|__________________|
+$node_A->safe_psql('postgres',qq(
+CREATE TABLE t(id int) PARTITION BY RANGE(id);
+CREATE TABLE part1 PARTITION OF t FOR VALUES FROM (0) TO (5);
+CREATE TABLE part2 PARTITION OF t FOR VALUES FROM (5) TO (15) PARTITION BY RANGE(id);
+CREATE TABLE part21 PARTITION OF part2 FOR VALUES FROM (5) TO (10);
+CREATE FOREIGN TABLE part22 PARTITION OF part2 FOR VALUES FROM (10) TO (15) SERVER fdw;
+INSERT INTO t SELECT id FROM generate_series(0,14) id;
+));
+$node_C->safe_psql('postgres',qq(
+CREATE TABLE t(id int) PARTITION BY RANGE(id);
+CREATE TABLE part1 PARTITION OF t FOR VALUES FROM (0) TO (5);
+CREATE TABLE part2 PARTITION OF t FOR VALUES FROM (5) TO (15) PARTITION BY RANGE(id);
+CREATE TABLE part21 PARTITION OF part2 FOR VALUES FROM (5) TO (10);
+CREATE TABLE part22 PARTITION OF part2 FOR VALUES FROM (10) TO (15);
+));
+
+$node_A->safe_psql('postgres',qq(
+CREATE PUBLICATION fdwpub FOR TABLE t;
+CREATE PUBLICATION fdwpub_via_root FOR TABLE t WITH (publish_via_partition_root);
+));
+
+# Here, both commands must raise WARNING.
+@publications = ("fdwpub", "fdwpub_via_root");
+foreach $pub (@publications)
+{
+ note("check warning for publication $pub");
+ ($result, $stdout, $stderr) = $node_C->psql('postgres',"
+ CREATE SUBSCRIPTION sub_c CONNECTION '$node_A_connstr' PUBLICATION $pub WITH (origin = none, copy_data = on);
+ ");
+
+ like(
+ $stderr,
+ qr/WARNING: ( [A-Z0-9]+:)? subscription "sub_c" requested copy_data with origin = NONE but might copy data that had a different origin/,
+ "subscribe to $pub must raise WARNING"
+ );
+ $node_C->psql('postgres',"DROP SUBSCRIPTION IF EXISTS sub_c");
+}
+$node_A->safe_psql('postgres',"DROP TABLE t CASCADE");
+$node_B->safe_psql('postgres',"DROP TABLE part22");
+$node_C->safe_psql('postgres',"DROP TABLE t CASCADE");
+
# shutdown
$node_B->stop('fast');
$node_A->stop('fast');