On Fri, Sep 4, 2026 at 9:00 PM Noah Misch <[email protected]> wrote:
>
> On Thu, Nov 20, 2025 at 03:09:36PM -0800, Masahiko Sawada wrote:
> > Pushed.
>
> This became commit 266543a.  It doesn't account for the publisher version, so
> replicating v18->v19 w/ publish_via_partition_root fails at tablesync w/
> "ERROR: cannot copy from partitioned table".

Good catch. I've attached the patch to fix it.

> I think commit 266543a also changes the behavior w/ foreign table partitions.
> A v19 subscriber will fail w/ "cannot copy from foreign table", whereas an
> older subscriber would have completed tablesync but handled no further foreign
> table changes.  This consequence of 266543a is a net good, since the v18
> subscriber behavior is mostly a bug.  Let's keep it.  If someone really needs
> the v18 subscriber behavior, adding a no-op row filter achieves that.

Agreed.

Regards,

-- 
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com
From c9f09d70b02b95e929261d0a293c692e7247fc2f Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <[email protected]>
Date: Tue, 8 Sep 2026 10:18:13 -0700
Subject: [PATCH v1] Fix tablesync failure for partitioned tables on older
 publishers.

Commit 266543a made initial table synchronization use "COPY table TO"
for a partitioned table, but decided that from the relation kind
alone. A partitioned table is only accepted there since v19, so a v19
subscriber replicating from an older publisher with
publish_via_partition_root failed at table sync.

Check the publisher version, and fall back to the "COPY (SELECT ...)
TO" variant otherwise.

Backpatch to v19, where 266543a changed the initial table
synchronization.

Reported-by: Noah Misch <[email protected]>
Reviewed-by:
Discussion: https://postgr.es/m/[email protected]
Backpatch-through: 19
---
 src/backend/replication/logical/tablesync.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/src/backend/replication/logical/tablesync.c b/src/backend/replication/logical/tablesync.c
index e5101997cd3..4015e861a64 100644
--- a/src/backend/replication/logical/tablesync.c
+++ b/src/backend/replication/logical/tablesync.c
@@ -1084,6 +1084,7 @@ copy_table(Relation rel)
 	ParseState *pstate;
 	List	   *options = NIL;
 	bool		gencol_published = false;
+	int			server_version = walrcv_server_version(LogRepWorkerWalRcvConn);
 
 	/* Get the publisher relation info. */
 	fetch_remote_table_info(get_namespace_name(RelationGetNamespace(rel)),
@@ -1100,9 +1101,14 @@ copy_table(Relation rel)
 	/* Start copy on the publisher. */
 	initStringInfo(&cmd);
 
-	/* Regular or partitioned table with no row filter or generated columns */
-	if ((lrel.relkind == RELKIND_RELATION || lrel.relkind == RELKIND_PARTITIONED_TABLE)
-		&& qual == NIL && !gencol_published)
+	/*
+	 * Regular or partitioned table with no row filter or generated columns.
+	 *
+	 * "COPY table TO" on a partitioned table is supported since v19.
+	 */
+	if ((lrel.relkind == RELKIND_RELATION ||
+		 (lrel.relkind == RELKIND_PARTITIONED_TABLE && server_version >= 190000)) &&
+		qual == NIL && !gencol_published)
 	{
 		appendStringInfo(&cmd, "COPY %s",
 						 quote_qualified_identifier(lrel.nspname, lrel.relname));
@@ -1182,8 +1188,7 @@ copy_table(Relation rel)
 	 * Prior to v16, initial table synchronization will use text format even
 	 * if the binary option is enabled for a subscription.
 	 */
-	if (walrcv_server_version(LogRepWorkerWalRcvConn) >= 160000 &&
-		MySubscription->binary)
+	if (server_version >= 160000 && MySubscription->binary)
 	{
 		appendStringInfoString(&cmd, " WITH (FORMAT binary)");
 		options = list_make1(makeDefElem("format",
-- 
2.55.0

Reply via email to