From b3f9e4435125be9939b9b65f5b823b51edb3f18d Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumarb@google.com>
Date: Fri, 26 Sep 2025 17:14:50 +0530
Subject: [PATCH v1] non publishable rel

---
 src/backend/access/common/reloptions.c | 13 ++++++++-
 src/backend/catalog/pg_publication.c   | 38 ++++++++++++++++++++------
 src/bin/psql/tab-complete.in.c         |  1 +
 src/include/utils/rel.h                | 11 ++++++++
 4 files changed, 54 insertions(+), 9 deletions(-)

diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 35150bf237b..6aa8acd83ea 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -166,6 +166,15 @@ static relopt_bool boolRelOpts[] =
 		},
 		true
 	},
+	{
+		{
+			"non_publishable_table",
+			"Mark table as non publishable so that this will can not be included in publication",
+			RELOPT_KIND_HEAP,
+			AccessExclusiveLock
+		},
+		true
+	},
 	/* list terminator */
 	{{NULL}}
 };
@@ -1915,7 +1924,9 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
 		{"vacuum_truncate", RELOPT_TYPE_BOOL,
 		offsetof(StdRdOptions, vacuum_truncate), offsetof(StdRdOptions, vacuum_truncate_set)},
 		{"vacuum_max_eager_freeze_failure_rate", RELOPT_TYPE_REAL,
-		offsetof(StdRdOptions, vacuum_max_eager_freeze_failure_rate)}
+		offsetof(StdRdOptions, vacuum_max_eager_freeze_failure_rate)},
+		{"non_publishable_table", RELOPT_TYPE_BOOL,
+		offsetof(StdRdOptions, non_publishable_table)}
 	};
 
 	return (bytea *) build_reloptions(reloptions, validate, kind,
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index b911efcf9cb..27f8b685817 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -146,7 +146,8 @@ is_publishable_class(Oid relid, Form_pg_class reltuple)
 bool
 is_publishable_relation(Relation rel)
 {
-	return is_publishable_class(RelationGetRelid(rel), rel->rd_rel);
+	return is_publishable_class(RelationGetRelid(rel), rel->rd_rel) &&
+		   !RelationIsNotPublishable(rel);
 }
 
 /*
@@ -162,12 +163,16 @@ pg_relation_is_publishable(PG_FUNCTION_ARGS)
 	Oid			relid = PG_GETARG_OID(0);
 	HeapTuple	tuple;
 	bool		result;
+	Relation	rel;
 
-	tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
-	if (!HeapTupleIsValid(tuple))
+	rel = try_table_open(relid, AccessShareLock);
+	if (rel == NULL)
 		PG_RETURN_NULL();
-	result = is_publishable_class(relid, (Form_pg_class) GETSTRUCT(tuple));
-	ReleaseSysCache(tuple);
+
+	result = is_publishable_relation(rel);
+
+	table_close(rel, AccessShareLock);
+
 	PG_RETURN_BOOL(result);
 }
 
@@ -882,10 +887,15 @@ GetAllTablesPublicationRelations(bool pubviaroot)
 	{
 		Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
 		Oid			relid = relForm->oid;
+		Relation	rel;
+
+		rel = table_open(relid, AccessShareLock);
 
-		if (is_publishable_class(relid, relForm) &&
+		if (is_publishable_relation(rel) &&
 			!(relForm->relispartition && pubviaroot))
 			result = lappend_oid(result, relid);
+
+		table_close(rel, AccessShareLock);
 	}
 
 	table_endscan(scan);
@@ -903,10 +913,15 @@ GetAllTablesPublicationRelations(bool pubviaroot)
 		{
 			Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
 			Oid			relid = relForm->oid;
+			Relation	rel;
 
-			if (is_publishable_class(relid, relForm) &&
+			rel = table_open(relid, AccessShareLock);
+
+			if (is_publishable_relation(rel) &&
 				!relForm->relispartition)
 				result = lappend_oid(result, relid);
+
+			table_close(rel, AccessShareLock);
 		}
 
 		table_endscan(scan);
@@ -1010,9 +1025,16 @@ GetSchemaPublicationRelations(Oid schemaid, PublicationPartOpt pub_partopt)
 		Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
 		Oid			relid = relForm->oid;
 		char		relkind;
+		Relation	rel;
+
+		rel = table_open(relid, AccessShareLock);
 
-		if (!is_publishable_class(relid, relForm))
+		if (!is_publishable_relation(rel))
+		{
+			table_close(rel, AccessShareLock);
 			continue;
+		}
+		table_close(rel, AccessShareLock);
 
 		relkind = get_rel_relkind(relid);
 		if (relkind == RELKIND_RELATION)
diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c
index 6b20a4404b2..74d4c164f62 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -1411,6 +1411,7 @@ static const char *const table_storage_parameters[] = {
 	"autovacuum_vacuum_threshold",
 	"fillfactor",
 	"log_autovacuum_min_duration",
+	"non_publishable_table",
 	"parallel_workers",
 	"toast.autovacuum_enabled",
 	"toast.autovacuum_freeze_max_age",
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 21990436373..033c365f469 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -348,6 +348,7 @@ typedef struct StdRdOptions
 	StdRdOptIndexCleanup vacuum_index_cleanup;	/* controls index vacuuming */
 	bool		vacuum_truncate;	/* enables vacuum to truncate a relation */
 	bool		vacuum_truncate_set;	/* whether vacuum_truncate is set */
+	bool		non_publishable_table;	/* table will not be published */
 
 	/*
 	 * Fraction of pages in a relation that vacuum can eagerly scan and fail
@@ -400,6 +401,16 @@ typedef struct StdRdOptions
 	  (relation)->rd_rel->relkind == RELKIND_MATVIEW) ? \
 	 ((StdRdOptions *) (relation)->rd_options)->user_catalog_table : false)
 
+/*
+ * RelationIsNonPublishable
+ *		Returns whether the relation can be added in publication or not.
+ */
+#define RelationIsNotPublishable(relation)	\
+	((relation)->rd_options && \
+	 ((relation)->rd_rel->relkind == RELKIND_RELATION || \
+	  (relation)->rd_rel->relkind == RELKIND_MATVIEW) ? \
+	 ((StdRdOptions *) (relation)->rd_options)->non_publishable_table : false)
+
 /*
  * RelationGetParallelWorkers
  *		Returns the relation's parallel_workers reloption setting.
-- 
2.49.0

