public inbox for [email protected]  
help / color / mirror / Atom feed
From: Álvaro Herrera <[email protected]>
To: Shlok Kyal <[email protected]>
Cc: vignesh C <[email protected]>
Cc: Sergey Tatarintsev <[email protected]>
Cc: [email protected]
Subject: Re: Restrict publishing of partitioned table with a foreign table as partition
Date: Sun, 6 Apr 2025 22:27:05 +0200
Message-ID: <[email protected]> (raw)
In-Reply-To: <CANhcyEXVPUAir9G1G1gT9k-xAMzhiV7GRX_fr1rkRhURNKDbxg@mail.gmail.com>

On 2025-Apr-01, Shlok Kyal wrote:

> I have modified the comment in create_publication.sgml and also added
> comment in the restrictions section of logical-replication.sgml.
> I have also added a more detailed explanation in comment of
> 'check_foreign_tables'
> 
> I have attached the updated v11 patch.

Sadly I don't have time to describe the changes proposed here right now,
but I'll do that early tomorrow.  (Some minor changes are still needed,
particularly the comments to publication_check_foreign_parts which are
mostly unchanged from what your patch has.  I'll do another review round
tomorrow.)

-- 
Álvaro Herrera         PostgreSQL Developer  —  https://www.EnterpriseDB.com/

From c6433548f3f47b23b81384f3cc80c0df307f6415 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Herrera?= <[email protected]>
Date: Sun, 6 Apr 2025 21:27:39 +0200
Subject: [PATCH] some changes

---
 doc/src/sgml/logical-replication.sgml     |  18 +-
 doc/src/sgml/ref/create_publication.sgml  |  24 ++-
 src/backend/catalog/pg_publication.c      | 235 ++++++++++------------
 src/backend/commands/foreigncmds.c        |  49 -----
 src/backend/commands/publicationcmds.c    |  42 ++--
 src/backend/commands/tablecmds.c          | 109 +++++++---
 src/backend/partitioning/partdesc.c       |  32 +++
 src/include/catalog/pg_publication.h      |   6 +-
 src/include/partitioning/partdesc.h       |   1 +
 src/test/regress/expected/publication.out |  30 +--
 10 files changed, 287 insertions(+), 259 deletions(-)

diff --git a/doc/src/sgml/logical-replication.sgml b/doc/src/sgml/logical-replication.sgml
index ce60a1b391c..67503aec871 100644
--- a/doc/src/sgml/logical-replication.sgml
+++ b/doc/src/sgml/logical-replication.sgml
@@ -2154,18 +2154,18 @@ CONTEXT:  processing remote data for replication origin "pg_16395" during "INSER
 
    <listitem>
     <para>
-     Replication is supported only for tables, including partitioned tables,
-     except when they contain foreign partitions. If
-     <literal>publish_via_partition_root</literal> is set to
-     <literal>true</literal> for a publication table with foreign partitions, or
-     if an attempt is made to replicate such a table, an error is thrown.
-     Additionally, when replicating a partitioned table where
-     <literal>publish_via_partition_root</literal> is set to
-     <literal>false</literal> and foreign partitions are present, all partitions
-     are replicated except the foreign partitions.
+     Replication is only supported for tables, including partitioned tables.
      Attempts to replicate other types of relations, such as views, materialized
      views, or foreign tables, will result in an error.
     </para>
+    <para>
+     Replication is not supported for foreign tables.  When used as partitions
+     of partitioned tables, publishing of the partitioned table is only allowed
+     if the <literal>publish_via_partition_root</literal> is set to
+     <literal>false</literal>.  In this mode, changes to foreign partitions are
+     ignored for the purposes of replication, and data contained in them
+     is not included during initial synchronization.
+    </para>
    </listitem>
 
    <listitem>
diff --git a/doc/src/sgml/ref/create_publication.sgml b/doc/src/sgml/ref/create_publication.sgml
index 47216fc4789..9f412e6693a 100644
--- a/doc/src/sgml/ref/create_publication.sgml
+++ b/doc/src/sgml/ref/create_publication.sgml
@@ -248,20 +248,26 @@ CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
           that of the individual partitions.
          </para>
 
+         <para>
+          If this parameter is enabled, <literal>TRUNCATE</literal>
+          operations performed directly on partitions are not replicated.
+         </para>
+
+         <para>
+          If this parameter is enabled, foreign tables and partitioned tables
+          containing partitions that are foreign tables may not be
+          added to the publication.  Conversely, foreign tables may not be
+          attached to a partitioned table that is included in a publication
+          with this parameter enabled.  Lastly, this parameter may not be
+          changed on publications that include partitioned tables with foreign
+          tables as partitions.
+         </para>
+
          <para>
           This parameter also affects how row filters and column lists are
           chosen for partitions; see below for details.
          </para>
 
-         <para>
-          If this is enabled, <literal>TRUNCATE</literal> operations performed
-          directly on partitions are not replicated.
-         </para>
-
-         <para>
-          If this is enabled, a foreign table or a partitioned table with a
-          foreign partition is not allowed in the publication.
-         </para>
         </listitem>
        </varlistentry>
       </variablelist></para>
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 77299b75cde..51e463c112b 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -55,7 +55,7 @@ typedef struct
  * error if not.
  */
 static void
-check_publication_add_relation(Relation targetrel, Publication *pub)
+check_publication_add_relation(Publication *pub, Relation targetrel)
 {
 	/* Must be a regular or partitioned table */
 	if (RelationGetForm(targetrel)->relkind != RELKIND_RELATION &&
@@ -68,15 +68,18 @@ check_publication_add_relation(Relation targetrel, Publication *pub)
 
 	/*
 	 * publish_via_root_partition cannot be true if it is a partitioned table
-	 * and has any foreign partition. See check_foreign_tables for details.
+	 * and has any foreign partition. See publication_check_foreign_parts for
+	 * details.
 	 */
-	if (pub->pubviaroot && check_partrel_has_foreign_table(targetrel))
+	if (pub->pubviaroot &&
+		targetrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
+		RelationHasForeignPartition(targetrel))
 		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("cannot set parameter \"%s\" to true for publication \"%s\"",
-						"publish_via_partition_root", pub->name),
-				 errdetail("partition table \"%s\" in publication contains a foreign partition",
-						   RelationGetRelationName(targetrel))));
+				errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				errmsg("cannot add partitioned table \"%s\" to publication \"%s\", which has \"%s\" set to \"%s\"",
+					   RelationGetRelationName(targetrel), pub->name,
+					   "publish_via_partition_root", "true"),
+				errdetail("The table contains a partition that's a foreign table."));
 
 	/* Can't be system table */
 	if (IsCatalogRelation(targetrel))
@@ -125,6 +128,103 @@ check_publication_add_schema(Oid schemaid)
 				 errdetail("Temporary schemas cannot be replicated.")));
 }
 
+/*
+ * publication_check_foreign_parts
+ *		Helper function to ensure we don't publish foreign tables
+ *
+ * Foreign tables may not be published.
+ *
+ * Protect against including foreign tables that are partitions of partitioned
+ * tables published by the given publication when publish_via_root_partition is
+ * true. This will not work correctly as the initial data from the foreign
+ * table can be replicated by the tablesync worker even though replication of
+ * foreign table is not supported because when publish_via_root_partition is
+ * true, the root table is included in pg_subscription_rel catalog table and
+ * tablesync worker cannot distinguish data from foreign partition. So we
+ * disallow the case here and in all DDL commands that would end up creating
+ * such a case indirectly.
+ *
+ * When publish_via_root_partition is set to false, leaf partitions are included
+ * in pg_subscription_rel catalog table. So, when we include a partition table
+ * with foreign partition in a publication, we skip including foreign partitions
+ * to pg_subscription_rel catalog table. So, the foreign partitions are not
+ * replicated.
+ *
+ *
+ *
+ * If valid schemaid provided, check if the schema has a partition table which
+ * has a foreign partition. The partition tables in a schema can have partitions
+ * in other schema. We also need to check if such partitions are foreign
+ * partition.
+ *
+ * If valid schemaid is not provided, we get all partition tables and check if
+ * it has any foreign partition. We take a lock on partition tables so no new
+ * foreign partitions are added concurrently.
+ *
+ * We take a ShareLock on pg_partitioned_table to restrict addition of new
+ * partitioned table which may contain a foreign partition while publication is
+ * being created.
+ */
+void
+publication_check_foreign_parts(Oid schemaid, char *pubname)
+{
+	Relation	classRel;
+	Relation	partRel;
+	ScanKeyData key[3];
+	int			keycount = 0;
+	TableScanDesc scan;
+	HeapTuple	tuple;
+
+	classRel = table_open(RelationRelationId, AccessShareLock);
+	partRel = table_open(PartitionedRelationId, ShareLock);
+
+	/* Get the root nodes of partitioned table */
+	ScanKeyInit(&key[keycount++],
+				Anum_pg_class_relkind,
+				BTEqualStrategyNumber, F_CHAREQ,
+				CharGetDatum(RELKIND_PARTITIONED_TABLE));
+
+	ScanKeyInit(&key[keycount++],
+				Anum_pg_class_relispartition,
+				BTEqualStrategyNumber, F_BOOLEQ,
+				BoolGetDatum(false));
+
+	/* If schema id is provided check partitioned table in that schema */
+	if (OidIsValid(schemaid))
+		ScanKeyInit(&key[keycount++],
+					Anum_pg_class_relnamespace,
+					BTEqualStrategyNumber, F_OIDEQ,
+					schemaid);
+
+	scan = table_beginscan_catalog(classRel, keycount, key);
+	while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+	{
+		Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
+		Relation	pubrel = table_open(relForm->oid, AccessShareLock);
+
+		if (pubrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
+			RelationHasForeignPartition(pubrel))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("cannot set parameter \"%s\" to \"%s\" for publication \"%s\"",
+							"publish_via_partition_root", "true", pubname),
+					 errtable(pubrel),
+					 errdetail("Published partitioned table \"%s\" contains a partition that is a foreign table.",
+							   get_rel_name(relForm->oid))));
+
+		/*
+		 * Keep lock till end of transaction: must prevent this table from
+		 * being attached a foreign table until we're done.  XXX does this
+		 * prevent addition of a partition in a partitioned child?
+		 */
+		table_close(pubrel, NoLock);
+	}
+
+	table_endscan(scan);
+	table_close(classRel, AccessShareLock);
+	table_close(partRel, NoLock);
+}
+
 /*
  * Returns if relation represented by oid and Form_pg_class entry
  * is publishable.
@@ -473,7 +573,7 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
 						RelationGetRelationName(targetrel), pub->name)));
 	}
 
-	check_publication_add_relation(targetrel, pub);
+	check_publication_add_relation(pub, targetrel);
 
 	/* Validate and translate column names into a Bitmapset of attnums. */
 	attnums = pub_collist_validate(pri->relation, pri->columns);
@@ -718,7 +818,7 @@ publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists)
 	 * partition
 	 */
 	if (pub->pubviaroot)
-		check_foreign_tables(schemaid, pub->name);
+		publication_check_foreign_parts(schemaid, pub->name);
 
 	/* Form a tuple */
 	memset(values, 0, sizeof(values));
@@ -1349,118 +1449,3 @@ pg_get_publication_tables(PG_FUNCTION_ARGS)
 
 	SRF_RETURN_DONE(funcctx);
 }
-
-/* Check if a partitioned table has a foreign partition */
-bool
-check_partrel_has_foreign_table(Relation rel)
-{
-	bool		has_foreign_tbl = false;
-
-	if (RelationGetForm(rel)->relkind == RELKIND_PARTITIONED_TABLE)
-	{
-		PartitionDesc pd = RelationGetPartitionDesc(rel, true);
-
-		for (int i = 0; i < pd->nparts; i++)
-		{
-			Relation	childrel = table_open(pd->oids[i], AccessShareLock);
-
-			if (RelationGetForm(childrel)->relkind == RELKIND_FOREIGN_TABLE)
-				has_foreign_tbl = true;
-			else
-				has_foreign_tbl = check_partrel_has_foreign_table(childrel);
-
-			table_close(childrel, NoLock);
-
-			if (has_foreign_tbl)
-				break;
-		}
-	}
-
-	return has_foreign_tbl;
-}
-
-/*
- * Protect against including foreign tables that are partitions of partitioned
- * tables published by the given publication when publish_via_root_partition is
- * true. This will not work correctly as the initial data from the foreign
- * table can be replicated by the tablesync worker even though replication of
- * foreign table is not supported because when publish_via_root_partition is
- * true, the root table is included in pg_subscription_rel catalog table and
- * tablesync worker cannot distinguish data from foreign partition. So we
- * disallow the case here and in all DDL commands that would end up creating
- * such a case indirectly.
- *
- * When publish_via_root_partition is set to false, leaf partitions are included
- * in pg_subscription_rel catalog table. So, when we include a partition table
- * with foreign partition in a publication, we skip including foreign partitions
- * to pg_subscription_rel catalog table. So, the foreign partitions are not
- * replicated.
- *
- *
- * check_foreign_tables
- *
- * If valid schemaid provided, check if the schema has a partition table which
- * has a foreign partition. The partition tables in a schema can have partitions
- * in other schema. We also need to check if such partitions are foreign
- * partition.
- *
- * If valid schemaid is not provided, we get all partition tables and check if
- * it has any foreign partition. We take a lock on partition tables so no new
- * foreign partitions are added concurrently.
- *
- * We take a ShareLock on pg_partitioned_table to restrict addition of new
- * partitioned table which may contain a foreign partition while publication is
- * being created.
- */
-void
-check_foreign_tables(Oid schemaid, char *pubname)
-{
-	Relation	classRel;
-	Relation	partRel;
-	ScanKeyData key[3];
-	int			keycount = 0;
-	TableScanDesc scan;
-	HeapTuple	tuple;
-
-	classRel = table_open(RelationRelationId, AccessShareLock);
-	partRel = table_open(PartitionedRelationId, ShareLock);
-
-	/* Get the root nodes of partitioned table */
-	ScanKeyInit(&key[keycount++],
-				Anum_pg_class_relkind,
-				BTEqualStrategyNumber, F_CHAREQ,
-				CharGetDatum(RELKIND_PARTITIONED_TABLE));
-
-	ScanKeyInit(&key[keycount++],
-				Anum_pg_class_relispartition,
-				BTEqualStrategyNumber, F_BOOLEQ,
-				BoolGetDatum(false));
-
-	/* If schema id is provided check partitioned table in that schema */
-	if (OidIsValid(schemaid))
-		ScanKeyInit(&key[keycount++],
-					Anum_pg_class_relnamespace,
-					BTEqualStrategyNumber, F_OIDEQ,
-					schemaid);
-
-	scan = table_beginscan_catalog(classRel, keycount, key);
-	while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
-	{
-		Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
-		Relation	pubrel = table_open(relForm->oid, AccessShareLock);
-
-		if (check_partrel_has_foreign_table(pubrel))
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-					 errmsg("cannot set parameter \"%s\" to true for publication \"%s\"",
-							"publish_via_partition_root", pubname),
-					 errdetail("partition table \"%s\" in publication contains a foreign partition",
-							   get_rel_name(relForm->oid))));
-
-		table_close(pubrel, NoLock);
-	}
-
-	table_endscan(scan);
-	table_close(classRel, AccessShareLock);
-	table_close(partRel, NoLock);
-}
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 98c1c82db61..c6843a9c30a 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -1424,55 +1424,6 @@ CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
 
 	ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
 
-	/*
-	 * Check if it is a foreign partition and the partitioned table is not
-	 * published or published with publish_via_partition_root option as false.
-	 * See check_foreign_tables for details.
-	 */
-	if (stmt->base.partbound != NULL)
-	{
-		RangeVar   *root = linitial_node(RangeVar, stmt->base.inhRelations);
-		Relation	rootrel = table_openrv(root, AccessShareLock);
-
-		if (RelationGetForm(rootrel)->relkind == RELKIND_PARTITIONED_TABLE)
-		{
-			Oid			schemaid = RelationGetNamespace(rootrel);
-			List	   *puboids = GetRelationPublications(rootrel->rd_id);
-			List	   *ancestors;
-
-			puboids = list_concat_unique_oid(puboids, GetAllTablesPublications());
-			puboids = list_concat_unique_oid(puboids, GetSchemaPublications(schemaid));
-			ancestors = get_partition_ancestors(rootrel->rd_id);
-
-			foreach_oid(ancestor, ancestors)
-			{
-				puboids = list_concat_unique_oid(puboids,
-												 GetRelationPublications(ancestor));
-				schemaid = get_rel_namespace(ancestor);
-				puboids = list_concat_unique_oid(puboids,
-												 GetSchemaPublications(schemaid));
-			}
-			list_free(ancestors);
-
-			foreach_oid(puboid, puboids)
-			{
-				Publication *pub = GetPublication(puboid);
-
-				if (pub->pubviaroot)
-					ereport(ERROR,
-							(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-							 errmsg("cannot create table foreign partition \"%s\"",
-									get_rel_name(relid)),
-							 errdetail("partition table \"%s\" is published with option publish_via_partition_root",
-									   RelationGetRelationName(rootrel))));
-			}
-
-			list_free(puboids);
-		}
-
-		table_close(rootrel, AccessShareLock);
-	}
-
 	/*
 	 * For now the owner cannot be specified on create. Use effective user ID.
 	 */
diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c
index b06960e8e22..54f2b505e3b 100644
--- a/src/backend/commands/publicationcmds.c
+++ b/src/backend/commands/publicationcmds.c
@@ -35,6 +35,7 @@
 #include "commands/publicationcmds.h"
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
+#include "partitioning/partdesc.h"
 #include "parser/parse_clause.h"
 #include "parser/parse_collate.h"
 #include "parser/parse_relation.h"
@@ -917,7 +918,7 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
 	{
 		/* Check if any partitioned table has foreign partition */
 		if (publish_via_partition_root)
-			check_foreign_tables(InvalidOid, stmt->pubname);
+			publication_check_foreign_parts(InvalidOid, stmt->pubname);
 
 		/* Invalidate relcache so that publication info is rebuilt. */
 		CacheInvalidateRelcacheAll();
@@ -1086,45 +1087,42 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
 
 	/*
 	 * If publish_via_partition_root is set to true, check if the publication
-	 * already have any foreign partition. See check_foreign_tables for details.
+	 * has any foreign partition.  See publication_check_foreign_parts for
+	 * details.
 	 */
 	if (publish_via_partition_root_given && publish_via_partition_root)
 	{
-		List	   *schemaoids = NIL;
-		List	   *relids = NIL;
-
 		char	   *pubname = stmt->pubname;
+		List	   *schemaoids;
+		List	   *relids;
 
 		if (pubform->puballtables)
-			check_foreign_tables(InvalidOid, pubname);
+			publication_check_foreign_parts(InvalidOid, pubname);
 
 		schemaoids = GetPublicationSchemas(pubform->oid);
-
 		foreach_oid(schemaoid, schemaoids)
-			check_foreign_tables(schemaoid, pubname);
-
-		list_free(schemaoids);
+			publication_check_foreign_parts(schemaoid, pubname);
 
 		relids = GetPublicationRelations(pubform->oid, PUBLICATION_PART_ROOT);
-
 		foreach_oid(relid, relids)
 		{
-			Relation	pubrel = table_open(relid, AccessShareLock);
+			Relation	pubrel;
 
-			if (check_partrel_has_foreign_table(pubrel))
-			{
+			if (get_rel_relkind(relid) != RELKIND_PARTITIONED_TABLE)
+				continue;
+
+			pubrel = table_open(relid, AccessShareLock);
+
+			if (RelationHasForeignPartition(pubrel))
 				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("cannot set parameter \"%s\" to true for publication \"%s\"",
-								"publish_via_partition_root", pubname),
-						 errdetail("partition table \"%s\" in publication contains a foreign partition",
-								   get_rel_name(relid))));
-			}
+						errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						errmsg("cannot set parameter \"%s\" to \"%s\" for publication \"%s\"",
+							   "publish_via_partition_root", "true", pubname),
+						errdetail("Published partitioned table \"%s\" contains a partition that is a foreign table.",
+								  RelationGetRelationName(pubrel)));
 
 			table_close(pubrel, NoLock);
 		}
-
-		list_free(relids);
 	}
 
 	/* Everything ok, form a new tuple. */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index eed4487e2d9..b594b01e774 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -1129,6 +1129,53 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 					 errmsg("\"%s\" is not partitioned",
 							RelationGetRelationName(parent))));
 
+		/*
+		 * If we're creating a partition that's a foreign table, verify that
+		 * the parent table is not in a publication with
+		 * publish_via_partition_root enabled.  For details, see
+		 * publication_check_foreign_parts.
+		 */
+		if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+		{
+			Oid			schemaid;
+			List	   *puboids;
+			List	   *ancestors;
+
+			/* Start with publications of all tables */
+			puboids = GetAllTablesPublications();
+
+			/* capture all publications that include this relation directly */
+			puboids = GetRelationPublications(parent->rd_id);
+			schemaid = RelationGetNamespace(parent);
+			puboids = list_concat(puboids, GetSchemaPublications(schemaid));
+
+			/* and do the same for its ancestors, if any */
+			ancestors = get_partition_ancestors(parent->rd_id);
+			foreach_oid(ancestor, ancestors)
+			{
+				puboids = list_concat(puboids, GetRelationPublications(ancestor));
+				schemaid = get_rel_namespace(ancestor);
+				puboids = list_concat(puboids, GetSchemaPublications(schemaid));
+			}
+
+			/* Check the publish_via_partition_root bit for each of those */
+			list_sort(puboids, list_oid_cmp);
+			list_deduplicate_oid(puboids);
+			foreach_oid(puboid, puboids)
+			{
+				Publication *pub = GetPublication(puboid);
+
+				if (pub->pubviaroot)
+					ereport(ERROR,
+							errcode(ERRCODE_WRONG_OBJECT_TYPE),
+							errmsg("cannot create foreign table \"%s\" as a partition of \"%s\"",
+								   RelationGetRelationName(rel), RelationGetRelationName(parent)),
+							errdetail("Partitioned table \"%s\" is published with option \"%s\" in publication \"%s\".",
+									  RelationGetRelationName(parent),
+									  "publish_via_partition_root", pub->name));
+			}
+		}
+
 		/*
 		 * The partition constraint of the default partition depends on the
 		 * partition bounds of every other partition. It is possible that
@@ -20093,58 +20140,68 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
 				 errmsg("cannot attach temporary relation of another session as partition")));
 
 	/*
-	 * Check if attachrel is a foreign table or a partitioned table with
-	 * foreign partition and rel is not part of publication with option
-	 * publish_via_partition_root as true.
+	 * If the relation to attach is a foreign table, or a partitioned table
+	 * that contains a foreign table as partition, then verify that the
+	 * parent table is not in a publication with publish_via_partition_root
+	 * enabled.  See publication_check_foreign_parts.
 	 */
 	if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE ||
-		check_partrel_has_foreign_table(attachrel))
+		(attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
+		 RelationHasForeignPartition(attachrel)))
 	{
-		Oid			schemaid = RelationGetNamespace(rel);
-		List	   *puboids = GetRelationPublications(rel->rd_id);
+		Oid			schemaid;
+		List	   *puboids;
 		List	   *ancestors;
-		char	   *relname = get_rel_name(rel->rd_id);
-		char	   *attachrelname = get_rel_name(attachrel->rd_id);
 
-		puboids = list_concat_unique_oid(puboids, GetAllTablesPublications());
-		puboids = list_concat_unique_oid(puboids, GetSchemaPublications(schemaid));
+		/* Start with publications of all tables */
+		puboids = GetAllTablesPublications();
+
+		/* capture all publications that include this relation directly */
+		puboids = list_concat(puboids, GetRelationPublications(rel->rd_id));
+		schemaid = RelationGetNamespace(rel);
+		puboids = list_concat(puboids, GetSchemaPublications(schemaid));
+
+		/* and do the same for its ancestors, if any */
 		ancestors = get_partition_ancestors(rel->rd_id);
-
 		foreach_oid(ancestor, ancestors)
 		{
-			puboids = list_concat_unique_oid(puboids,
-											 GetRelationPublications(ancestor));
+			puboids = list_concat(puboids, GetRelationPublications(ancestor));
 			schemaid = get_rel_namespace(ancestor);
-			puboids = list_concat_unique_oid(puboids,
-											 GetSchemaPublications(schemaid));
+			puboids = list_concat(puboids, GetSchemaPublications(schemaid));
 		}
 
-		list_free(ancestors);
-
+		/* Now check the publish_via_partition_root bit for each of those */
+		list_sort(puboids, list_oid_cmp);
+		list_deduplicate_oid(puboids);
 		foreach_oid(puboid, puboids)
 		{
-			Publication *pub = GetPublication(puboid);
+			Publication *pub;
 
+			pub = GetPublication(puboid);
 			if (pub->pubviaroot)
 			{
 				if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
 					ereport(ERROR,
 							(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 							 errmsg("cannot attach foreign table \"%s\" to partition table \"%s\"",
-									attachrelname, relname),
-							 errdetail("partition table \"%s\" is published with option publish_via_partition_root",
-									   relname)));
+									RelationGetRelationName(attachrel),
+									RelationGetRelationName(rel)),
+							 errdetail("Partitioned table \"%s\" is published with option \"%s\" in publication \"%s\".",
+									   RelationGetRelationName(rel),
+									   "publish_via_partition_root",
+									   pub->name)));
 				else
 					ereport(ERROR,
 							(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 							 errmsg("cannot attach table \"%s\" with foreign partition to partition table \"%s\"",
-									attachrelname, relname),
-							 errdetail("partition table \"%s\" is published with option publish_via_partition_root",
-									   relname)));
+									RelationGetRelationName(attachrel),
+									RelationGetRelationName(rel)),
+							 errdetail("Partitioned table \"%s\" is published with option \"%s\" in publication \"%s\".",
+									   RelationGetRelationName(rel),
+									   "publish_via_partition_root",
+									   pub->name)));
 			}
 		}
-
-		list_free(puboids);
 	}
 
 	/*
diff --git a/src/backend/partitioning/partdesc.c b/src/backend/partitioning/partdesc.c
index 328b4d450e4..b53139bafdd 100644
--- a/src/backend/partitioning/partdesc.c
+++ b/src/backend/partitioning/partdesc.c
@@ -506,3 +506,35 @@ get_default_oid_from_partdesc(PartitionDesc partdesc)
 
 	return InvalidOid;
 }
+
+/*
+ * Return true if the given partitioned table ultimately contains a
+ * partition that is a foreign table, false otherwise.
+ */
+bool
+RelationHasForeignPartition(Relation rel)
+{
+	PartitionDesc pd = RelationGetPartitionDesc(rel, true);
+
+	for (int i = 0; i < pd->nparts; i++)
+	{
+		if (pd->is_leaf[i])
+		{
+			if (get_rel_relkind(pd->oids[i]) == RELKIND_FOREIGN_TABLE)
+				return true;
+		}
+		else
+		{
+			Relation	part;
+			bool		ret;
+
+			part = table_open(pd->oids[i], AccessShareLock);
+			ret = RelationHasForeignPartition(part);
+			table_close(part, NoLock);
+			if (ret)
+				return true;
+		}
+	}
+
+	return false;
+}
diff --git a/src/include/catalog/pg_publication.h b/src/include/catalog/pg_publication.h
index e2919da2541..71ad5a6f846 100644
--- a/src/include/catalog/pg_publication.h
+++ b/src/include/catalog/pg_publication.h
@@ -179,6 +179,8 @@ extern Oid	GetTopMostAncestorInPublication(Oid puboid, List *ancestors,
 
 extern bool is_publishable_relation(Relation rel);
 extern bool is_schema_publication(Oid pubid);
+extern void publication_check_foreign_parts(Oid schemaid, char *pubname);
+
 extern bool check_and_fetch_column_list(Publication *pub, Oid relid,
 										MemoryContext mcxt, Bitmapset **cols);
 extern ObjectAddress publication_add_relation(Oid pubid, PublicationRelInfo *pri,
@@ -192,8 +194,4 @@ extern Bitmapset *pub_collist_to_bitmapset(Bitmapset *columns, Datum pubcols,
 extern Bitmapset *pub_form_cols_map(Relation relation,
 									PublishGencolsType include_gencols_type);
 
-extern bool check_partrel_has_foreign_table(Relation rel);
-
-extern void check_foreign_tables(Oid schemaid, char *pubname);
-
 #endif							/* PG_PUBLICATION_H */
diff --git a/src/include/partitioning/partdesc.h b/src/include/partitioning/partdesc.h
index 34533f7004c..5fbafdc06f9 100644
--- a/src/include/partitioning/partdesc.h
+++ b/src/include/partitioning/partdesc.h
@@ -71,5 +71,6 @@ extern PartitionDesc PartitionDirectoryLookup(PartitionDirectory, Relation);
 extern void DestroyPartitionDirectory(PartitionDirectory pdir);
 
 extern Oid	get_default_oid_from_partdesc(PartitionDesc partdesc);
+extern bool RelationHasForeignPartition(Relation rel);
 
 #endif							/* PARTDESC_H */
diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out
index 6a0c245bd60..4395563fcd5 100644
--- a/src/test/regress/expected/publication.out
+++ b/src/test/regress/expected/publication.out
@@ -1939,32 +1939,32 @@ ALTER TABLE sch3.tmain ATTACH PARTITION sch3.part2 FOR VALUES FROM (5) TO (10);
 -- Can't create publications with publish_via_partition_root = true, if table
 -- has a foreign partition
 CREATE PUBLICATION pub1 FOR TABLE sch3.tmain WITH (publish_via_partition_root);
-ERROR:  cannot set parameter "publish_via_partition_root" to true for publication "pub1"
-DETAIL:  partition table "tmain" in publication contains a foreign partition
+ERROR:  cannot add partitioned table "tmain" to publication "pub1", which has "publish_via_partition_root" set to "true"
+DETAIL:  The table contains a partition that's a foreign table.
 CREATE PUBLICATION pub1 FOR TABLES IN SCHEMA sch3 WITH (publish_via_partition_root);
-ERROR:  cannot set parameter "publish_via_partition_root" to true for publication "pub1"
-DETAIL:  partition table "tmain" in publication contains a foreign partition
+ERROR:  cannot set parameter "publish_via_partition_root" to "true" for publication "pub1"
+DETAIL:  Published partitioned table "tmain" contains a partition that is a foreign table.
 CREATE PUBLICATION pub1 FOR ALL TABLES WITH (publish_via_partition_root);
-ERROR:  cannot set parameter "publish_via_partition_root" to true for publication "pub1"
-DETAIL:  partition table "tmain" in publication contains a foreign partition
+ERROR:  cannot set parameter "publish_via_partition_root" to "true" for publication "pub1"
+DETAIL:  Published partitioned table "tmain" contains a partition that is a foreign table.
 -- Test when a partitioned table with foreign table as a partition is attached
 -- to partitioned table which is already published
 ALTER TABLE sch3.tmain DETACH PARTITION sch3.part2;
 CREATE PUBLICATION pub1 FOR TABLE sch3.tmain WITH (publish_via_partition_root);
 ALTER TABLE sch3.tmain ATTACH PARTITION sch3.part2 FOR VALUES FROM (5) TO (10);
 ERROR:  cannot attach table "part2" with foreign partition to partition table "tmain"
-DETAIL:  partition table "tmain" is published with option publish_via_partition_root
+DETAIL:  Partitioned table "tmain" is published with option "publish_via_partition_root" in publication "pub1".
 -- Can't create foreign partition of published table with
 -- publish_via_partition_root = true
 CREATE FOREIGN TABLE sch3.part3_1 PARTITION OF sch3.tmain FOR VALUES FROM (10) TO (15) SERVER fdw_server;
-ERROR:  cannot create table foreign partition "part3_1"
-DETAIL:  partition table "tmain" is published with option publish_via_partition_root
+ERROR:  cannot create foreign table "part3_1" as a partition of "tmain"
+DETAIL:  Partitioned table "tmain" is published with option "publish_via_partition_root" in publication "pub1".
 -- Can't attach foreign partition to published table with
 -- publish_via_partition_root = true
 CREATE FOREIGN TABLE sch3.part3_2(id int) SERVER fdw_server;
 ALTER TABLE sch3.tmain ATTACH PARTITION sch3.part3_2 FOR VALUES FROM (15) TO (20);
 ERROR:  cannot attach foreign table "part3_2" to partition table "tmain"
-DETAIL:  partition table "tmain" is published with option publish_via_partition_root
+DETAIL:  Partitioned table "tmain" is published with option "publish_via_partition_root" in publication "pub1".
 CREATE SCHEMA sch4;
 CREATE TABLE sch4.tmain(id int) PARTITION BY RANGE(id);
 -- publication created with FOR TABLES IN SCHEMA
@@ -1973,13 +1973,13 @@ CREATE PUBLICATION pub1 FOR TABLES IN SCHEMA sch4 WITH (publish_via_partition_ro
 -- Can't create foreign partition of published table with
 -- publish_via_partition_root = true
 CREATE FOREIGN TABLE sch3.part3_1 PARTITION OF sch4.tmain FOR VALUES FROM (10) TO (15) SERVER fdw_server;
-ERROR:  cannot create table foreign partition "part3_1"
-DETAIL:  partition table "tmain" is published with option publish_via_partition_root
+ERROR:  cannot create foreign table "part3_1" as a partition of "tmain"
+DETAIL:  Partitioned table "tmain" is published with option "publish_via_partition_root" in publication "pub1".
 -- Can't attach foreign partition to published table with
 -- publish_via_partition_root = true
 ALTER TABLE sch4.tmain ATTACH PARTITION sch3.part3_2 FOR VALUES FROM (15) TO (20);
 ERROR:  cannot attach foreign table "part3_2" to partition table "tmain"
-DETAIL:  partition table "tmain" is published with option publish_via_partition_root
+DETAIL:  Partitioned table "tmain" is published with option "publish_via_partition_root" in publication "pub1".
 DROP PUBLICATION pub1;
 -- Test with publish_via_partition_root = false
 -- Foreign partitions are skipped by default
@@ -2004,8 +2004,8 @@ SELECT pubname, tablename FROM pg_publication_tables WHERE schemaname in ('sch3'
 -- Can't alter publish_via_partition_root to true, if publication already have
 -- foreign partition
 ALTER PUBLICATION pub1 SET (publish_via_partition_root);
-ERROR:  cannot set parameter "publish_via_partition_root" to true for publication "pub1"
-DETAIL:  partition table "tmain" in publication contains a foreign partition
+ERROR:  cannot set parameter "publish_via_partition_root" to "true" for publication "pub1"
+DETAIL:  Published partitioned table "tmain" contains a partition that is a foreign table.
 DROP PUBLICATION pub1;
 DROP PUBLICATION pub2;
 DROP PUBLICATION pub3;
-- 
2.39.5



Attachments:

  [text/plain] 0001-some-changes.patch.txt (33.1K, ../[email protected]/2-0001-some-changes.patch.txt)
  download | inline diff:
From c6433548f3f47b23b81384f3cc80c0df307f6415 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Herrera?= <[email protected]>
Date: Sun, 6 Apr 2025 21:27:39 +0200
Subject: [PATCH] some changes

---
 doc/src/sgml/logical-replication.sgml     |  18 +-
 doc/src/sgml/ref/create_publication.sgml  |  24 ++-
 src/backend/catalog/pg_publication.c      | 235 ++++++++++------------
 src/backend/commands/foreigncmds.c        |  49 -----
 src/backend/commands/publicationcmds.c    |  42 ++--
 src/backend/commands/tablecmds.c          | 109 +++++++---
 src/backend/partitioning/partdesc.c       |  32 +++
 src/include/catalog/pg_publication.h      |   6 +-
 src/include/partitioning/partdesc.h       |   1 +
 src/test/regress/expected/publication.out |  30 +--
 10 files changed, 287 insertions(+), 259 deletions(-)

diff --git a/doc/src/sgml/logical-replication.sgml b/doc/src/sgml/logical-replication.sgml
index ce60a1b391c..67503aec871 100644
--- a/doc/src/sgml/logical-replication.sgml
+++ b/doc/src/sgml/logical-replication.sgml
@@ -2154,18 +2154,18 @@ CONTEXT:  processing remote data for replication origin "pg_16395" during "INSER
 
    <listitem>
     <para>
-     Replication is supported only for tables, including partitioned tables,
-     except when they contain foreign partitions. If
-     <literal>publish_via_partition_root</literal> is set to
-     <literal>true</literal> for a publication table with foreign partitions, or
-     if an attempt is made to replicate such a table, an error is thrown.
-     Additionally, when replicating a partitioned table where
-     <literal>publish_via_partition_root</literal> is set to
-     <literal>false</literal> and foreign partitions are present, all partitions
-     are replicated except the foreign partitions.
+     Replication is only supported for tables, including partitioned tables.
      Attempts to replicate other types of relations, such as views, materialized
      views, or foreign tables, will result in an error.
     </para>
+    <para>
+     Replication is not supported for foreign tables.  When used as partitions
+     of partitioned tables, publishing of the partitioned table is only allowed
+     if the <literal>publish_via_partition_root</literal> is set to
+     <literal>false</literal>.  In this mode, changes to foreign partitions are
+     ignored for the purposes of replication, and data contained in them
+     is not included during initial synchronization.
+    </para>
    </listitem>
 
    <listitem>
diff --git a/doc/src/sgml/ref/create_publication.sgml b/doc/src/sgml/ref/create_publication.sgml
index 47216fc4789..9f412e6693a 100644
--- a/doc/src/sgml/ref/create_publication.sgml
+++ b/doc/src/sgml/ref/create_publication.sgml
@@ -248,20 +248,26 @@ CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
           that of the individual partitions.
          </para>
 
+         <para>
+          If this parameter is enabled, <literal>TRUNCATE</literal>
+          operations performed directly on partitions are not replicated.
+         </para>
+
+         <para>
+          If this parameter is enabled, foreign tables and partitioned tables
+          containing partitions that are foreign tables may not be
+          added to the publication.  Conversely, foreign tables may not be
+          attached to a partitioned table that is included in a publication
+          with this parameter enabled.  Lastly, this parameter may not be
+          changed on publications that include partitioned tables with foreign
+          tables as partitions.
+         </para>
+
          <para>
           This parameter also affects how row filters and column lists are
           chosen for partitions; see below for details.
          </para>
 
-         <para>
-          If this is enabled, <literal>TRUNCATE</literal> operations performed
-          directly on partitions are not replicated.
-         </para>
-
-         <para>
-          If this is enabled, a foreign table or a partitioned table with a
-          foreign partition is not allowed in the publication.
-         </para>
         </listitem>
        </varlistentry>
       </variablelist></para>
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 77299b75cde..51e463c112b 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -55,7 +55,7 @@ typedef struct
  * error if not.
  */
 static void
-check_publication_add_relation(Relation targetrel, Publication *pub)
+check_publication_add_relation(Publication *pub, Relation targetrel)
 {
 	/* Must be a regular or partitioned table */
 	if (RelationGetForm(targetrel)->relkind != RELKIND_RELATION &&
@@ -68,15 +68,18 @@ check_publication_add_relation(Relation targetrel, Publication *pub)
 
 	/*
 	 * publish_via_root_partition cannot be true if it is a partitioned table
-	 * and has any foreign partition. See check_foreign_tables for details.
+	 * and has any foreign partition. See publication_check_foreign_parts for
+	 * details.
 	 */
-	if (pub->pubviaroot && check_partrel_has_foreign_table(targetrel))
+	if (pub->pubviaroot &&
+		targetrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
+		RelationHasForeignPartition(targetrel))
 		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("cannot set parameter \"%s\" to true for publication \"%s\"",
-						"publish_via_partition_root", pub->name),
-				 errdetail("partition table \"%s\" in publication contains a foreign partition",
-						   RelationGetRelationName(targetrel))));
+				errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				errmsg("cannot add partitioned table \"%s\" to publication \"%s\", which has \"%s\" set to \"%s\"",
+					   RelationGetRelationName(targetrel), pub->name,
+					   "publish_via_partition_root", "true"),
+				errdetail("The table contains a partition that's a foreign table."));
 
 	/* Can't be system table */
 	if (IsCatalogRelation(targetrel))
@@ -125,6 +128,103 @@ check_publication_add_schema(Oid schemaid)
 				 errdetail("Temporary schemas cannot be replicated.")));
 }
 
+/*
+ * publication_check_foreign_parts
+ *		Helper function to ensure we don't publish foreign tables
+ *
+ * Foreign tables may not be published.
+ *
+ * Protect against including foreign tables that are partitions of partitioned
+ * tables published by the given publication when publish_via_root_partition is
+ * true. This will not work correctly as the initial data from the foreign
+ * table can be replicated by the tablesync worker even though replication of
+ * foreign table is not supported because when publish_via_root_partition is
+ * true, the root table is included in pg_subscription_rel catalog table and
+ * tablesync worker cannot distinguish data from foreign partition. So we
+ * disallow the case here and in all DDL commands that would end up creating
+ * such a case indirectly.
+ *
+ * When publish_via_root_partition is set to false, leaf partitions are included
+ * in pg_subscription_rel catalog table. So, when we include a partition table
+ * with foreign partition in a publication, we skip including foreign partitions
+ * to pg_subscription_rel catalog table. So, the foreign partitions are not
+ * replicated.
+ *
+ *
+ *
+ * If valid schemaid provided, check if the schema has a partition table which
+ * has a foreign partition. The partition tables in a schema can have partitions
+ * in other schema. We also need to check if such partitions are foreign
+ * partition.
+ *
+ * If valid schemaid is not provided, we get all partition tables and check if
+ * it has any foreign partition. We take a lock on partition tables so no new
+ * foreign partitions are added concurrently.
+ *
+ * We take a ShareLock on pg_partitioned_table to restrict addition of new
+ * partitioned table which may contain a foreign partition while publication is
+ * being created.
+ */
+void
+publication_check_foreign_parts(Oid schemaid, char *pubname)
+{
+	Relation	classRel;
+	Relation	partRel;
+	ScanKeyData key[3];
+	int			keycount = 0;
+	TableScanDesc scan;
+	HeapTuple	tuple;
+
+	classRel = table_open(RelationRelationId, AccessShareLock);
+	partRel = table_open(PartitionedRelationId, ShareLock);
+
+	/* Get the root nodes of partitioned table */
+	ScanKeyInit(&key[keycount++],
+				Anum_pg_class_relkind,
+				BTEqualStrategyNumber, F_CHAREQ,
+				CharGetDatum(RELKIND_PARTITIONED_TABLE));
+
+	ScanKeyInit(&key[keycount++],
+				Anum_pg_class_relispartition,
+				BTEqualStrategyNumber, F_BOOLEQ,
+				BoolGetDatum(false));
+
+	/* If schema id is provided check partitioned table in that schema */
+	if (OidIsValid(schemaid))
+		ScanKeyInit(&key[keycount++],
+					Anum_pg_class_relnamespace,
+					BTEqualStrategyNumber, F_OIDEQ,
+					schemaid);
+
+	scan = table_beginscan_catalog(classRel, keycount, key);
+	while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+	{
+		Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
+		Relation	pubrel = table_open(relForm->oid, AccessShareLock);
+
+		if (pubrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
+			RelationHasForeignPartition(pubrel))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("cannot set parameter \"%s\" to \"%s\" for publication \"%s\"",
+							"publish_via_partition_root", "true", pubname),
+					 errtable(pubrel),
+					 errdetail("Published partitioned table \"%s\" contains a partition that is a foreign table.",
+							   get_rel_name(relForm->oid))));
+
+		/*
+		 * Keep lock till end of transaction: must prevent this table from
+		 * being attached a foreign table until we're done.  XXX does this
+		 * prevent addition of a partition in a partitioned child?
+		 */
+		table_close(pubrel, NoLock);
+	}
+
+	table_endscan(scan);
+	table_close(classRel, AccessShareLock);
+	table_close(partRel, NoLock);
+}
+
 /*
  * Returns if relation represented by oid and Form_pg_class entry
  * is publishable.
@@ -473,7 +573,7 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
 						RelationGetRelationName(targetrel), pub->name)));
 	}
 
-	check_publication_add_relation(targetrel, pub);
+	check_publication_add_relation(pub, targetrel);
 
 	/* Validate and translate column names into a Bitmapset of attnums. */
 	attnums = pub_collist_validate(pri->relation, pri->columns);
@@ -718,7 +818,7 @@ publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists)
 	 * partition
 	 */
 	if (pub->pubviaroot)
-		check_foreign_tables(schemaid, pub->name);
+		publication_check_foreign_parts(schemaid, pub->name);
 
 	/* Form a tuple */
 	memset(values, 0, sizeof(values));
@@ -1349,118 +1449,3 @@ pg_get_publication_tables(PG_FUNCTION_ARGS)
 
 	SRF_RETURN_DONE(funcctx);
 }
-
-/* Check if a partitioned table has a foreign partition */
-bool
-check_partrel_has_foreign_table(Relation rel)
-{
-	bool		has_foreign_tbl = false;
-
-	if (RelationGetForm(rel)->relkind == RELKIND_PARTITIONED_TABLE)
-	{
-		PartitionDesc pd = RelationGetPartitionDesc(rel, true);
-
-		for (int i = 0; i < pd->nparts; i++)
-		{
-			Relation	childrel = table_open(pd->oids[i], AccessShareLock);
-
-			if (RelationGetForm(childrel)->relkind == RELKIND_FOREIGN_TABLE)
-				has_foreign_tbl = true;
-			else
-				has_foreign_tbl = check_partrel_has_foreign_table(childrel);
-
-			table_close(childrel, NoLock);
-
-			if (has_foreign_tbl)
-				break;
-		}
-	}
-
-	return has_foreign_tbl;
-}
-
-/*
- * Protect against including foreign tables that are partitions of partitioned
- * tables published by the given publication when publish_via_root_partition is
- * true. This will not work correctly as the initial data from the foreign
- * table can be replicated by the tablesync worker even though replication of
- * foreign table is not supported because when publish_via_root_partition is
- * true, the root table is included in pg_subscription_rel catalog table and
- * tablesync worker cannot distinguish data from foreign partition. So we
- * disallow the case here and in all DDL commands that would end up creating
- * such a case indirectly.
- *
- * When publish_via_root_partition is set to false, leaf partitions are included
- * in pg_subscription_rel catalog table. So, when we include a partition table
- * with foreign partition in a publication, we skip including foreign partitions
- * to pg_subscription_rel catalog table. So, the foreign partitions are not
- * replicated.
- *
- *
- * check_foreign_tables
- *
- * If valid schemaid provided, check if the schema has a partition table which
- * has a foreign partition. The partition tables in a schema can have partitions
- * in other schema. We also need to check if such partitions are foreign
- * partition.
- *
- * If valid schemaid is not provided, we get all partition tables and check if
- * it has any foreign partition. We take a lock on partition tables so no new
- * foreign partitions are added concurrently.
- *
- * We take a ShareLock on pg_partitioned_table to restrict addition of new
- * partitioned table which may contain a foreign partition while publication is
- * being created.
- */
-void
-check_foreign_tables(Oid schemaid, char *pubname)
-{
-	Relation	classRel;
-	Relation	partRel;
-	ScanKeyData key[3];
-	int			keycount = 0;
-	TableScanDesc scan;
-	HeapTuple	tuple;
-
-	classRel = table_open(RelationRelationId, AccessShareLock);
-	partRel = table_open(PartitionedRelationId, ShareLock);
-
-	/* Get the root nodes of partitioned table */
-	ScanKeyInit(&key[keycount++],
-				Anum_pg_class_relkind,
-				BTEqualStrategyNumber, F_CHAREQ,
-				CharGetDatum(RELKIND_PARTITIONED_TABLE));
-
-	ScanKeyInit(&key[keycount++],
-				Anum_pg_class_relispartition,
-				BTEqualStrategyNumber, F_BOOLEQ,
-				BoolGetDatum(false));
-
-	/* If schema id is provided check partitioned table in that schema */
-	if (OidIsValid(schemaid))
-		ScanKeyInit(&key[keycount++],
-					Anum_pg_class_relnamespace,
-					BTEqualStrategyNumber, F_OIDEQ,
-					schemaid);
-
-	scan = table_beginscan_catalog(classRel, keycount, key);
-	while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
-	{
-		Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
-		Relation	pubrel = table_open(relForm->oid, AccessShareLock);
-
-		if (check_partrel_has_foreign_table(pubrel))
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-					 errmsg("cannot set parameter \"%s\" to true for publication \"%s\"",
-							"publish_via_partition_root", pubname),
-					 errdetail("partition table \"%s\" in publication contains a foreign partition",
-							   get_rel_name(relForm->oid))));
-
-		table_close(pubrel, NoLock);
-	}
-
-	table_endscan(scan);
-	table_close(classRel, AccessShareLock);
-	table_close(partRel, NoLock);
-}
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 98c1c82db61..c6843a9c30a 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -1424,55 +1424,6 @@ CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
 
 	ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
 
-	/*
-	 * Check if it is a foreign partition and the partitioned table is not
-	 * published or published with publish_via_partition_root option as false.
-	 * See check_foreign_tables for details.
-	 */
-	if (stmt->base.partbound != NULL)
-	{
-		RangeVar   *root = linitial_node(RangeVar, stmt->base.inhRelations);
-		Relation	rootrel = table_openrv(root, AccessShareLock);
-
-		if (RelationGetForm(rootrel)->relkind == RELKIND_PARTITIONED_TABLE)
-		{
-			Oid			schemaid = RelationGetNamespace(rootrel);
-			List	   *puboids = GetRelationPublications(rootrel->rd_id);
-			List	   *ancestors;
-
-			puboids = list_concat_unique_oid(puboids, GetAllTablesPublications());
-			puboids = list_concat_unique_oid(puboids, GetSchemaPublications(schemaid));
-			ancestors = get_partition_ancestors(rootrel->rd_id);
-
-			foreach_oid(ancestor, ancestors)
-			{
-				puboids = list_concat_unique_oid(puboids,
-												 GetRelationPublications(ancestor));
-				schemaid = get_rel_namespace(ancestor);
-				puboids = list_concat_unique_oid(puboids,
-												 GetSchemaPublications(schemaid));
-			}
-			list_free(ancestors);
-
-			foreach_oid(puboid, puboids)
-			{
-				Publication *pub = GetPublication(puboid);
-
-				if (pub->pubviaroot)
-					ereport(ERROR,
-							(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-							 errmsg("cannot create table foreign partition \"%s\"",
-									get_rel_name(relid)),
-							 errdetail("partition table \"%s\" is published with option publish_via_partition_root",
-									   RelationGetRelationName(rootrel))));
-			}
-
-			list_free(puboids);
-		}
-
-		table_close(rootrel, AccessShareLock);
-	}
-
 	/*
 	 * For now the owner cannot be specified on create. Use effective user ID.
 	 */
diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c
index b06960e8e22..54f2b505e3b 100644
--- a/src/backend/commands/publicationcmds.c
+++ b/src/backend/commands/publicationcmds.c
@@ -35,6 +35,7 @@
 #include "commands/publicationcmds.h"
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
+#include "partitioning/partdesc.h"
 #include "parser/parse_clause.h"
 #include "parser/parse_collate.h"
 #include "parser/parse_relation.h"
@@ -917,7 +918,7 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
 	{
 		/* Check if any partitioned table has foreign partition */
 		if (publish_via_partition_root)
-			check_foreign_tables(InvalidOid, stmt->pubname);
+			publication_check_foreign_parts(InvalidOid, stmt->pubname);
 
 		/* Invalidate relcache so that publication info is rebuilt. */
 		CacheInvalidateRelcacheAll();
@@ -1086,45 +1087,42 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
 
 	/*
 	 * If publish_via_partition_root is set to true, check if the publication
-	 * already have any foreign partition. See check_foreign_tables for details.
+	 * has any foreign partition.  See publication_check_foreign_parts for
+	 * details.
 	 */
 	if (publish_via_partition_root_given && publish_via_partition_root)
 	{
-		List	   *schemaoids = NIL;
-		List	   *relids = NIL;
-
 		char	   *pubname = stmt->pubname;
+		List	   *schemaoids;
+		List	   *relids;
 
 		if (pubform->puballtables)
-			check_foreign_tables(InvalidOid, pubname);
+			publication_check_foreign_parts(InvalidOid, pubname);
 
 		schemaoids = GetPublicationSchemas(pubform->oid);
-
 		foreach_oid(schemaoid, schemaoids)
-			check_foreign_tables(schemaoid, pubname);
-
-		list_free(schemaoids);
+			publication_check_foreign_parts(schemaoid, pubname);
 
 		relids = GetPublicationRelations(pubform->oid, PUBLICATION_PART_ROOT);
-
 		foreach_oid(relid, relids)
 		{
-			Relation	pubrel = table_open(relid, AccessShareLock);
+			Relation	pubrel;
 
-			if (check_partrel_has_foreign_table(pubrel))
-			{
+			if (get_rel_relkind(relid) != RELKIND_PARTITIONED_TABLE)
+				continue;
+
+			pubrel = table_open(relid, AccessShareLock);
+
+			if (RelationHasForeignPartition(pubrel))
 				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("cannot set parameter \"%s\" to true for publication \"%s\"",
-								"publish_via_partition_root", pubname),
-						 errdetail("partition table \"%s\" in publication contains a foreign partition",
-								   get_rel_name(relid))));
-			}
+						errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						errmsg("cannot set parameter \"%s\" to \"%s\" for publication \"%s\"",
+							   "publish_via_partition_root", "true", pubname),
+						errdetail("Published partitioned table \"%s\" contains a partition that is a foreign table.",
+								  RelationGetRelationName(pubrel)));
 
 			table_close(pubrel, NoLock);
 		}
-
-		list_free(relids);
 	}
 
 	/* Everything ok, form a new tuple. */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index eed4487e2d9..b594b01e774 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -1129,6 +1129,53 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 					 errmsg("\"%s\" is not partitioned",
 							RelationGetRelationName(parent))));
 
+		/*
+		 * If we're creating a partition that's a foreign table, verify that
+		 * the parent table is not in a publication with
+		 * publish_via_partition_root enabled.  For details, see
+		 * publication_check_foreign_parts.
+		 */
+		if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+		{
+			Oid			schemaid;
+			List	   *puboids;
+			List	   *ancestors;
+
+			/* Start with publications of all tables */
+			puboids = GetAllTablesPublications();
+
+			/* capture all publications that include this relation directly */
+			puboids = GetRelationPublications(parent->rd_id);
+			schemaid = RelationGetNamespace(parent);
+			puboids = list_concat(puboids, GetSchemaPublications(schemaid));
+
+			/* and do the same for its ancestors, if any */
+			ancestors = get_partition_ancestors(parent->rd_id);
+			foreach_oid(ancestor, ancestors)
+			{
+				puboids = list_concat(puboids, GetRelationPublications(ancestor));
+				schemaid = get_rel_namespace(ancestor);
+				puboids = list_concat(puboids, GetSchemaPublications(schemaid));
+			}
+
+			/* Check the publish_via_partition_root bit for each of those */
+			list_sort(puboids, list_oid_cmp);
+			list_deduplicate_oid(puboids);
+			foreach_oid(puboid, puboids)
+			{
+				Publication *pub = GetPublication(puboid);
+
+				if (pub->pubviaroot)
+					ereport(ERROR,
+							errcode(ERRCODE_WRONG_OBJECT_TYPE),
+							errmsg("cannot create foreign table \"%s\" as a partition of \"%s\"",
+								   RelationGetRelationName(rel), RelationGetRelationName(parent)),
+							errdetail("Partitioned table \"%s\" is published with option \"%s\" in publication \"%s\".",
+									  RelationGetRelationName(parent),
+									  "publish_via_partition_root", pub->name));
+			}
+		}
+
 		/*
 		 * The partition constraint of the default partition depends on the
 		 * partition bounds of every other partition. It is possible that
@@ -20093,58 +20140,68 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
 				 errmsg("cannot attach temporary relation of another session as partition")));
 
 	/*
-	 * Check if attachrel is a foreign table or a partitioned table with
-	 * foreign partition and rel is not part of publication with option
-	 * publish_via_partition_root as true.
+	 * If the relation to attach is a foreign table, or a partitioned table
+	 * that contains a foreign table as partition, then verify that the
+	 * parent table is not in a publication with publish_via_partition_root
+	 * enabled.  See publication_check_foreign_parts.
 	 */
 	if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE ||
-		check_partrel_has_foreign_table(attachrel))
+		(attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
+		 RelationHasForeignPartition(attachrel)))
 	{
-		Oid			schemaid = RelationGetNamespace(rel);
-		List	   *puboids = GetRelationPublications(rel->rd_id);
+		Oid			schemaid;
+		List	   *puboids;
 		List	   *ancestors;
-		char	   *relname = get_rel_name(rel->rd_id);
-		char	   *attachrelname = get_rel_name(attachrel->rd_id);
 
-		puboids = list_concat_unique_oid(puboids, GetAllTablesPublications());
-		puboids = list_concat_unique_oid(puboids, GetSchemaPublications(schemaid));
+		/* Start with publications of all tables */
+		puboids = GetAllTablesPublications();
+
+		/* capture all publications that include this relation directly */
+		puboids = list_concat(puboids, GetRelationPublications(rel->rd_id));
+		schemaid = RelationGetNamespace(rel);
+		puboids = list_concat(puboids, GetSchemaPublications(schemaid));
+
+		/* and do the same for its ancestors, if any */
 		ancestors = get_partition_ancestors(rel->rd_id);
-
 		foreach_oid(ancestor, ancestors)
 		{
-			puboids = list_concat_unique_oid(puboids,
-											 GetRelationPublications(ancestor));
+			puboids = list_concat(puboids, GetRelationPublications(ancestor));
 			schemaid = get_rel_namespace(ancestor);
-			puboids = list_concat_unique_oid(puboids,
-											 GetSchemaPublications(schemaid));
+			puboids = list_concat(puboids, GetSchemaPublications(schemaid));
 		}
 
-		list_free(ancestors);
-
+		/* Now check the publish_via_partition_root bit for each of those */
+		list_sort(puboids, list_oid_cmp);
+		list_deduplicate_oid(puboids);
 		foreach_oid(puboid, puboids)
 		{
-			Publication *pub = GetPublication(puboid);
+			Publication *pub;
 
+			pub = GetPublication(puboid);
 			if (pub->pubviaroot)
 			{
 				if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
 					ereport(ERROR,
 							(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 							 errmsg("cannot attach foreign table \"%s\" to partition table \"%s\"",
-									attachrelname, relname),
-							 errdetail("partition table \"%s\" is published with option publish_via_partition_root",
-									   relname)));
+									RelationGetRelationName(attachrel),
+									RelationGetRelationName(rel)),
+							 errdetail("Partitioned table \"%s\" is published with option \"%s\" in publication \"%s\".",
+									   RelationGetRelationName(rel),
+									   "publish_via_partition_root",
+									   pub->name)));
 				else
 					ereport(ERROR,
 							(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 							 errmsg("cannot attach table \"%s\" with foreign partition to partition table \"%s\"",
-									attachrelname, relname),
-							 errdetail("partition table \"%s\" is published with option publish_via_partition_root",
-									   relname)));
+									RelationGetRelationName(attachrel),
+									RelationGetRelationName(rel)),
+							 errdetail("Partitioned table \"%s\" is published with option \"%s\" in publication \"%s\".",
+									   RelationGetRelationName(rel),
+									   "publish_via_partition_root",
+									   pub->name)));
 			}
 		}
-
-		list_free(puboids);
 	}
 
 	/*
diff --git a/src/backend/partitioning/partdesc.c b/src/backend/partitioning/partdesc.c
index 328b4d450e4..b53139bafdd 100644
--- a/src/backend/partitioning/partdesc.c
+++ b/src/backend/partitioning/partdesc.c
@@ -506,3 +506,35 @@ get_default_oid_from_partdesc(PartitionDesc partdesc)
 
 	return InvalidOid;
 }
+
+/*
+ * Return true if the given partitioned table ultimately contains a
+ * partition that is a foreign table, false otherwise.
+ */
+bool
+RelationHasForeignPartition(Relation rel)
+{
+	PartitionDesc pd = RelationGetPartitionDesc(rel, true);
+
+	for (int i = 0; i < pd->nparts; i++)
+	{
+		if (pd->is_leaf[i])
+		{
+			if (get_rel_relkind(pd->oids[i]) == RELKIND_FOREIGN_TABLE)
+				return true;
+		}
+		else
+		{
+			Relation	part;
+			bool		ret;
+
+			part = table_open(pd->oids[i], AccessShareLock);
+			ret = RelationHasForeignPartition(part);
+			table_close(part, NoLock);
+			if (ret)
+				return true;
+		}
+	}
+
+	return false;
+}
diff --git a/src/include/catalog/pg_publication.h b/src/include/catalog/pg_publication.h
index e2919da2541..71ad5a6f846 100644
--- a/src/include/catalog/pg_publication.h
+++ b/src/include/catalog/pg_publication.h
@@ -179,6 +179,8 @@ extern Oid	GetTopMostAncestorInPublication(Oid puboid, List *ancestors,
 
 extern bool is_publishable_relation(Relation rel);
 extern bool is_schema_publication(Oid pubid);
+extern void publication_check_foreign_parts(Oid schemaid, char *pubname);
+
 extern bool check_and_fetch_column_list(Publication *pub, Oid relid,
 										MemoryContext mcxt, Bitmapset **cols);
 extern ObjectAddress publication_add_relation(Oid pubid, PublicationRelInfo *pri,
@@ -192,8 +194,4 @@ extern Bitmapset *pub_collist_to_bitmapset(Bitmapset *columns, Datum pubcols,
 extern Bitmapset *pub_form_cols_map(Relation relation,
 									PublishGencolsType include_gencols_type);
 
-extern bool check_partrel_has_foreign_table(Relation rel);
-
-extern void check_foreign_tables(Oid schemaid, char *pubname);
-
 #endif							/* PG_PUBLICATION_H */
diff --git a/src/include/partitioning/partdesc.h b/src/include/partitioning/partdesc.h
index 34533f7004c..5fbafdc06f9 100644
--- a/src/include/partitioning/partdesc.h
+++ b/src/include/partitioning/partdesc.h
@@ -71,5 +71,6 @@ extern PartitionDesc PartitionDirectoryLookup(PartitionDirectory, Relation);
 extern void DestroyPartitionDirectory(PartitionDirectory pdir);
 
 extern Oid	get_default_oid_from_partdesc(PartitionDesc partdesc);
+extern bool RelationHasForeignPartition(Relation rel);
 
 #endif							/* PARTDESC_H */
diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out
index 6a0c245bd60..4395563fcd5 100644
--- a/src/test/regress/expected/publication.out
+++ b/src/test/regress/expected/publication.out
@@ -1939,32 +1939,32 @@ ALTER TABLE sch3.tmain ATTACH PARTITION sch3.part2 FOR VALUES FROM (5) TO (10);
 -- Can't create publications with publish_via_partition_root = true, if table
 -- has a foreign partition
 CREATE PUBLICATION pub1 FOR TABLE sch3.tmain WITH (publish_via_partition_root);
-ERROR:  cannot set parameter "publish_via_partition_root" to true for publication "pub1"
-DETAIL:  partition table "tmain" in publication contains a foreign partition
+ERROR:  cannot add partitioned table "tmain" to publication "pub1", which has "publish_via_partition_root" set to "true"
+DETAIL:  The table contains a partition that's a foreign table.
 CREATE PUBLICATION pub1 FOR TABLES IN SCHEMA sch3 WITH (publish_via_partition_root);
-ERROR:  cannot set parameter "publish_via_partition_root" to true for publication "pub1"
-DETAIL:  partition table "tmain" in publication contains a foreign partition
+ERROR:  cannot set parameter "publish_via_partition_root" to "true" for publication "pub1"
+DETAIL:  Published partitioned table "tmain" contains a partition that is a foreign table.
 CREATE PUBLICATION pub1 FOR ALL TABLES WITH (publish_via_partition_root);
-ERROR:  cannot set parameter "publish_via_partition_root" to true for publication "pub1"
-DETAIL:  partition table "tmain" in publication contains a foreign partition
+ERROR:  cannot set parameter "publish_via_partition_root" to "true" for publication "pub1"
+DETAIL:  Published partitioned table "tmain" contains a partition that is a foreign table.
 -- Test when a partitioned table with foreign table as a partition is attached
 -- to partitioned table which is already published
 ALTER TABLE sch3.tmain DETACH PARTITION sch3.part2;
 CREATE PUBLICATION pub1 FOR TABLE sch3.tmain WITH (publish_via_partition_root);
 ALTER TABLE sch3.tmain ATTACH PARTITION sch3.part2 FOR VALUES FROM (5) TO (10);
 ERROR:  cannot attach table "part2" with foreign partition to partition table "tmain"
-DETAIL:  partition table "tmain" is published with option publish_via_partition_root
+DETAIL:  Partitioned table "tmain" is published with option "publish_via_partition_root" in publication "pub1".
 -- Can't create foreign partition of published table with
 -- publish_via_partition_root = true
 CREATE FOREIGN TABLE sch3.part3_1 PARTITION OF sch3.tmain FOR VALUES FROM (10) TO (15) SERVER fdw_server;
-ERROR:  cannot create table foreign partition "part3_1"
-DETAIL:  partition table "tmain" is published with option publish_via_partition_root
+ERROR:  cannot create foreign table "part3_1" as a partition of "tmain"
+DETAIL:  Partitioned table "tmain" is published with option "publish_via_partition_root" in publication "pub1".
 -- Can't attach foreign partition to published table with
 -- publish_via_partition_root = true
 CREATE FOREIGN TABLE sch3.part3_2(id int) SERVER fdw_server;
 ALTER TABLE sch3.tmain ATTACH PARTITION sch3.part3_2 FOR VALUES FROM (15) TO (20);
 ERROR:  cannot attach foreign table "part3_2" to partition table "tmain"
-DETAIL:  partition table "tmain" is published with option publish_via_partition_root
+DETAIL:  Partitioned table "tmain" is published with option "publish_via_partition_root" in publication "pub1".
 CREATE SCHEMA sch4;
 CREATE TABLE sch4.tmain(id int) PARTITION BY RANGE(id);
 -- publication created with FOR TABLES IN SCHEMA
@@ -1973,13 +1973,13 @@ CREATE PUBLICATION pub1 FOR TABLES IN SCHEMA sch4 WITH (publish_via_partition_ro
 -- Can't create foreign partition of published table with
 -- publish_via_partition_root = true
 CREATE FOREIGN TABLE sch3.part3_1 PARTITION OF sch4.tmain FOR VALUES FROM (10) TO (15) SERVER fdw_server;
-ERROR:  cannot create table foreign partition "part3_1"
-DETAIL:  partition table "tmain" is published with option publish_via_partition_root
+ERROR:  cannot create foreign table "part3_1" as a partition of "tmain"
+DETAIL:  Partitioned table "tmain" is published with option "publish_via_partition_root" in publication "pub1".
 -- Can't attach foreign partition to published table with
 -- publish_via_partition_root = true
 ALTER TABLE sch4.tmain ATTACH PARTITION sch3.part3_2 FOR VALUES FROM (15) TO (20);
 ERROR:  cannot attach foreign table "part3_2" to partition table "tmain"
-DETAIL:  partition table "tmain" is published with option publish_via_partition_root
+DETAIL:  Partitioned table "tmain" is published with option "publish_via_partition_root" in publication "pub1".
 DROP PUBLICATION pub1;
 -- Test with publish_via_partition_root = false
 -- Foreign partitions are skipped by default
@@ -2004,8 +2004,8 @@ SELECT pubname, tablename FROM pg_publication_tables WHERE schemaname in ('sch3'
 -- Can't alter publish_via_partition_root to true, if publication already have
 -- foreign partition
 ALTER PUBLICATION pub1 SET (publish_via_partition_root);
-ERROR:  cannot set parameter "publish_via_partition_root" to true for publication "pub1"
-DETAIL:  partition table "tmain" in publication contains a foreign partition
+ERROR:  cannot set parameter "publish_via_partition_root" to "true" for publication "pub1"
+DETAIL:  Published partitioned table "tmain" contains a partition that is a foreign table.
 DROP PUBLICATION pub1;
 DROP PUBLICATION pub2;
 DROP PUBLICATION pub3;
-- 
2.39.5



view thread (3+ messages)  latest in thread

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: [email protected]
  Cc: [email protected], [email protected], [email protected], [email protected], [email protected]
  Subject: Re: Restrict publishing of partitioned table with a foreign table as partition
  In-Reply-To: <[email protected]>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox