v1-0001-Add-relispublishable-column-to-pg_class-catalog.patch
application/octet-stream
Filename: v1-0001-Add-relispublishable-column-to-pg_class-catalog.patch
Type: application/octet-stream
Part: 0
From bec0384109927a3d65320cf395cbb970b6d9f05c Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumarb@google.com>
Date: Tue, 16 Dec 2025 14:02:55 +0530
Subject: [PATCH v1] Add relispublishable column to pg_class catalog
Previously, determining if a relation was eligible for logical
replication was performed dynamically via is_publishable_class().
This function relied on hard-coded OID checks and and relkind that
is performed at runtime.
This patch change by adding a "relispublishable" boolean column to
pg_class. With this we don't need to check OID and relkind dynamically
we can just rely on ispublishable field.
Author: Dilip Kumar based on suggestion by Amit Kapila
---
src/backend/bootstrap/bootparse.y | 1 +
src/backend/catalog/heap.c | 4 ++
src/backend/catalog/pg_publication.c | 49 ++-------------------
src/backend/catalog/toasting.c | 1 +
src/backend/commands/cluster.c | 1 +
src/backend/commands/tablecmds.c | 15 +++++++
src/backend/replication/pgoutput/pgoutput.c | 4 +-
src/backend/utils/cache/relcache.c | 2 +-
src/include/catalog/heap.h | 1 +
src/include/catalog/pg_class.h | 3 ++
src/include/catalog/pg_publication.h | 1 -
11 files changed, 33 insertions(+), 49 deletions(-)
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 9833f52c1be..01e4bd301e6 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -243,6 +243,7 @@ Boot_CreateStmt:
false,
true,
false,
+ false,
InvalidOid,
NULL);
elog(DEBUG4, "relation created with OID %u", id);
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 265cc3e5fbf..5760794853c 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -953,6 +953,7 @@ InsertPgClassTuple(Relation pg_class_desc,
values[Anum_pg_class_relrewrite - 1] = ObjectIdGetDatum(rd_rel->relrewrite);
values[Anum_pg_class_relfrozenxid - 1] = TransactionIdGetDatum(rd_rel->relfrozenxid);
values[Anum_pg_class_relminmxid - 1] = MultiXactIdGetDatum(rd_rel->relminmxid);
+ values[Anum_pg_class_relispublishable - 1] = BoolGetDatum(rd_rel->relispublishable);
if (relacl != (Datum) 0)
values[Anum_pg_class_relacl - 1] = relacl;
else
@@ -1138,6 +1139,7 @@ heap_create_with_catalog(const char *relname,
bool use_user_acl,
bool allow_system_table_mods,
bool is_internal,
+ bool ispublisable,
Oid relrewrite,
ObjectAddress *typaddress)
{
@@ -1329,6 +1331,8 @@ heap_create_with_catalog(const char *relname,
Assert(relid == RelationGetRelid(new_rel_desc));
new_rel_desc->rd_rel->relrewrite = relrewrite;
+ new_rel_desc->rd_rel->relispublishable = (ispublisable &&
+ relid >= FirstNormalObjectId);
/*
* Decide whether to create a pg_type entry for the relation's rowtype.
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 7aa3f179924..13aa7b273b3 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -111,47 +111,6 @@ check_publication_add_schema(Oid schemaid)
errdetail("Temporary schemas cannot be replicated.")));
}
-/*
- * Returns if relation represented by oid and Form_pg_class entry
- * is publishable.
- *
- * Does same checks as check_publication_add_relation() above except for
- * RELKIND_SEQUENCE, but does not need relation to be opened and also does
- * not throw errors. Here, the additional check is to support ALL SEQUENCES
- * publication.
- *
- * XXX This also excludes all tables with relid < FirstNormalObjectId,
- * ie all tables created during initdb. This mainly affects the preinstalled
- * information_schema. IsCatalogRelationOid() only excludes tables with
- * relid < FirstUnpinnedObjectId, making that test rather redundant,
- * but really we should get rid of the FirstNormalObjectId test not
- * IsCatalogRelationOid. We can't do so today because we don't want
- * information_schema tables to be considered publishable; but this test
- * is really inadequate for that, since the information_schema could be
- * dropped and reloaded and then it'll be considered publishable. The best
- * long-term solution may be to add a "relispublishable" bool to pg_class,
- * and depend on that instead of OID checks.
- */
-static bool
-is_publishable_class(Oid relid, Form_pg_class reltuple)
-{
- return (reltuple->relkind == RELKIND_RELATION ||
- reltuple->relkind == RELKIND_PARTITIONED_TABLE ||
- reltuple->relkind == RELKIND_SEQUENCE) &&
- !IsCatalogRelationOid(relid) &&
- reltuple->relpersistence == RELPERSISTENCE_PERMANENT &&
- relid >= FirstNormalObjectId;
-}
-
-/*
- * Another variant of is_publishable_class(), taking a Relation.
- */
-bool
-is_publishable_relation(Relation rel)
-{
- return is_publishable_class(RelationGetRelid(rel), rel->rd_rel);
-}
-
/*
* SQL-callable variant of the above
*
@@ -169,7 +128,7 @@ pg_relation_is_publishable(PG_FUNCTION_ARGS)
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (!HeapTupleIsValid(tuple))
PG_RETURN_NULL();
- result = is_publishable_class(relid, (Form_pg_class) GETSTRUCT(tuple));
+ result = ((Form_pg_class) GETSTRUCT(tuple))->relispublishable;
ReleaseSysCache(tuple);
PG_RETURN_BOOL(result);
}
@@ -890,7 +849,7 @@ GetAllPublicationRelations(char relkind, bool pubviaroot)
Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
Oid relid = relForm->oid;
- if (is_publishable_class(relid, relForm) &&
+ if (relForm->relispublishable &&
!(relForm->relispartition && pubviaroot))
result = lappend_oid(result, relid);
}
@@ -911,7 +870,7 @@ GetAllPublicationRelations(char relkind, bool pubviaroot)
Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
Oid relid = relForm->oid;
- if (is_publishable_class(relid, relForm) &&
+ if (relForm->relispublishable &&
!relForm->relispartition)
result = lappend_oid(result, relid);
}
@@ -1018,7 +977,7 @@ GetSchemaPublicationRelations(Oid schemaid, PublicationPartOpt pub_partopt)
Oid relid = relForm->oid;
char relkind;
- if (!is_publishable_class(relid, relForm))
+ if (!relForm->relispublishable)
continue;
relkind = get_rel_relkind(relid);
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 874a8fc89ad..429ba2dcae7 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -263,6 +263,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
false,
true,
true,
+ false,
OIDOldToast,
NULL);
Assert(toast_relid != InvalidOid);
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 2120c85ccb4..7528ae9e2e3 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -774,6 +774,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod,
false,
true,
true,
+ false,
OIDOldHeap,
NULL);
Assert(OIDNewHeap != InvalidOid);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 953fadb9c6b..22d508c3ed7 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -790,6 +790,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
ObjectAddress address;
LOCKMODE parentLockmode;
Oid accessMethodId = InvalidOid;
+ bool ispublishable;
/*
* Truncate relname to appropriate length (probably a waste of time, as
@@ -1051,6 +1052,18 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
accessMethodId = get_table_am_oid(default_table_access_method, false);
}
+ /*
+ * Determine if the relation is eligible for logical replication.
+ *
+ * To be publishable, the relation must be a regular table, a partitioned
+ * table, or a sequence. Additionally, it must be a permanent relation
+ * created during normal processing to exclude system catalogs.
+ */
+ ispublishable = ((relkind == RELKIND_RELATION ||
+ relkind == RELKIND_PARTITIONED_TABLE ||
+ relkind == RELKIND_SEQUENCE) &&
+ stmt->relation->relpersistence == RELPERSISTENCE_PERMANENT);
+
/*
* Create the relation. Inherited defaults and CHECK constraints are
* passed in for immediate handling --- since they don't need parsing,
@@ -1076,6 +1089,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
true,
allowSystemTableMods,
false,
+ ispublishable,
InvalidOid,
typaddress);
@@ -22529,6 +22543,7 @@ createPartitionTable(List **wqueue, RangeVar *newPartName,
true,
allowSystemTableMods,
true,
+ parent_rel->rd_rel->relispublishable,
InvalidOid,
NULL);
diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c
index 787998abb8a..ead0b393d87 100644
--- a/src/backend/replication/pgoutput/pgoutput.c
+++ b/src/backend/replication/pgoutput/pgoutput.c
@@ -1491,7 +1491,7 @@ pgoutput_change(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
TupleTableSlot *old_slot = NULL;
TupleTableSlot *new_slot = NULL;
- if (!is_publishable_relation(relation))
+ if (!relation->rd_rel->relispublishable)
return;
/*
@@ -1675,7 +1675,7 @@ pgoutput_truncate(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
Relation relation = relations[i];
Oid relid = RelationGetRelid(relation);
- if (!is_publishable_relation(relation))
+ if (!relation->rd_rel->relispublishable)
continue;
relentry = get_rel_sync_entry(data, relation);
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 2d0cb7bcfd4..8d0596a9c8f 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -5804,7 +5804,7 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
* If not publishable, it publishes no actions. (pgoutput_change() will
* ignore it.)
*/
- if (!is_publishable_relation(relation))
+ if (!relation->rd_rel->relispublishable)
{
memset(pubdesc, 0, sizeof(PublicationDesc));
pubdesc->rf_valid_for_update = true;
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index dbd339e9df4..3ecdb670e2c 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -83,6 +83,7 @@ extern Oid heap_create_with_catalog(const char *relname,
bool use_user_acl,
bool allow_system_table_mods,
bool is_internal,
+ bool ispublishable,
Oid relrewrite,
ObjectAddress *typaddress);
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index 07d182da796..bf959094a85 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -122,6 +122,9 @@ CATALOG(pg_class,1259,RelationRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83,Relat
/* is relation a partition? */
bool relispartition BKI_DEFAULT(f);
+ /* is relation publishable */
+ bool relispublishable BKI_DEFAULT(f);
+
/* link to original rel during table rewrite; otherwise 0 */
Oid relrewrite BKI_DEFAULT(0) BKI_LOOKUP_OPT(pg_class);
diff --git a/src/include/catalog/pg_publication.h b/src/include/catalog/pg_publication.h
index 22f48bb8975..9c4613b62af 100644
--- a/src/include/catalog/pg_publication.h
+++ b/src/include/catalog/pg_publication.h
@@ -183,7 +183,6 @@ extern List *GetPubPartitionOptionRelations(List *result,
extern Oid GetTopMostAncestorInPublication(Oid puboid, List *ancestors,
int *ancestor_level);
-extern bool is_publishable_relation(Relation rel);
extern bool is_schema_publication(Oid pubid);
extern bool check_and_fetch_column_list(Publication *pub, Oid relid,
MemoryContext mcxt, Bitmapset **cols);
--
2.49.0