Replace is_publishable_class() with relispublishable column in pg_class
Hello hackers,
I would like to propose an improvement to the way PostgreSQL
determines if a relation is eligible for logical replication.
Currently, this check is performed dynamically via
is_publishable_class(), which relies on hard-coded OID checks and
relkind. As noted in the existing comments atop
is_publishable_class[1]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) { .. }, the long-term solution is to add a boolean
column to pg_class and depend on that instead of OID checks.
Motivation
========
1) The check is performed frequently in the logical decoding path
(e.g., in pgoutput_change and pgoutput_truncate). Moving this to a
cached catalog attribute in pg_class allows for a simple check.
2) As suggested by Amit Kpila [2]/messages/by-id/CAA4eK1K8Aqm+jP_EMPF8H_3UJSEExdwDCaphq6=unZZMdcmD0A@mail.gmail.com, for the upcoming Conflict Log Table
feature, we need a clean way to exclude these internal conflict log
tables from publication. A catalog flag allows us to set this property
at relation creation rather than adding more special cases.
Proposed Changes
===============
The attached patch implements the following:
1) Catalog Update: Adds relispublishable (bool) to pg_class.
2) Creation Logic: Input parameter is added to
heap_create_with_catalog() so that caller can pass whether the
relation is publishable or not.
3) Update pgoutput and other relevant places to utilize the new
pg_class column directly.
[1]: 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) { .. }
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)
{
..
}
[2]: /messages/by-id/CAA4eK1K8Aqm+jP_EMPF8H_3UJSEExdwDCaphq6=unZZMdcmD0A@mail.gmail.com
--
Regards,
Dilip Kumar
Google
Attachments:
v1-0001-Add-relispublishable-column-to-pg_class-catalog.patchapplication/octet-stream; name=v1-0001-Add-relispublishable-column-to-pg_class-catalog.patchDownload
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
Hi,
On 2025-12-16 21:19:21 +0530, Dilip Kumar wrote:
Motivation
========
1) The check is performed frequently in the logical decoding path
(e.g., in pgoutput_change and pgoutput_truncate). Moving this to a
cached catalog attribute in pg_class allows for a simple check.
You could solve this more resource-efficiently by putting the information in
the relcache entry.
2) As suggested by Amit Kpila [2], for the upcoming Conflict Log Table
feature, we need a clean way to exclude these internal conflict log
tables from publication. A catalog flag allows us to set this property
at relation creation rather than adding more special cases.
Seems like the issue here also could be addressed the same way?
Greetings,
Andres Freund
Hi,
On 2025-12-16 11:28:11 -0500, Andres Freund wrote:
On 2025-12-16 21:19:21 +0530, Dilip Kumar wrote:
2) As suggested by Amit Kpila [2], for the upcoming Conflict Log Table
feature, we need a clean way to exclude these internal conflict log
tables from publication. A catalog flag allows us to set this property
at relation creation rather than adding more special cases.Seems like the issue here also could be addressed the same way?
Actually, wouldn't a table-level property be completely inappropriate for
that? Imagine one publication that's used for HA (or major version upgrade)
and doesn't use a conflict table, which replicates all tables (including the
conflict table of another pub/sub). And a subscription doing bi-direction
replication that *does* obviously use the conflict table. In one of those
cases you want to replicate changes to the conflict table, in the other
not. So a table / pg_class property would be inappropriate, no?
Greetings,
Andres Freund
On Tue, Dec 16, 2025 at 9:58 PM Andres Freund <andres@anarazel.de> wrote:
Hi,
On 2025-12-16 21:19:21 +0530, Dilip Kumar wrote:
Motivation
========
1) The check is performed frequently in the logical decoding path
(e.g., in pgoutput_change and pgoutput_truncate). Moving this to a
cached catalog attribute in pg_class allows for a simple check.You could solve this more resource-efficiently by putting the information in
the relcache entry.
I feel the proposed solution (marking in pg_class) is required not
only to improve performance by avoiding OID checks in frequently
executed paths, but also to ensure consistent behavior when publishing
tables.
The existing approach does not provide a foolproof solution. As an
example, tables in information_schema are currently not eligible for
publication; but if information_schema is dropped and recreated, those
tables become eligible because their relid no longer falls under
FirstNormalObjectId (see steps in [1]****Pub****: create publication pub1; ALTER PUBLICATION pub1 ADD TABLE information_schema.sql_sizing; select * from information_schema.sql_sizing where sizing_id=97;). There may be other
existing/future scenarios with similar behavior. In my opinion, the
proposed solution is a good approach to ensure consistency in such
cases.
[1]: ****Pub****: create publication pub1; ALTER PUBLICATION pub1 ADD TABLE information_schema.sql_sizing; select * from information_schema.sql_sizing where sizing_id=97;
****Pub****:
create publication pub1;
ALTER PUBLICATION pub1 ADD TABLE information_schema.sql_sizing;
select * from information_schema.sql_sizing where sizing_id=97;
****Sub****:
create subscription sub1 connection '...' publication pub1 with
(copy_data=false);
select * from information_schema.sql_sizing where sizing_id=97;
****Pub****:
alter table information_schema.sql_sizing replica identity full;
--this is not replicated.
UPDATE information_schema.sql_sizing set supported_value=12 where sizing_id=97;
****Sub****:
postgres=# select supported_value from information_schema.sql_sizing
where sizing_id=97;
supported_value
-----------------
0
~~
Then drop and recreate and try to perform the above update again, it
gets replicated:
drop schema information_schema cascade;
./psql -d postgres -f ./../../src/backend/catalog/information_schema.sql -p 5433
****Pub****:
ALTER PUBLICATION pub1 ADD TABLE information_schema.sql_sizing;
select * from information_schema.sql_sizing where sizing_id=97;
alter table information_schema.sql_sizing replica identity full;
--This is replicated
UPDATE information_schema.sql_sizing set supported_value=14 where sizing_id=97;
****Sub****:
--This shows supported_value as 14
postgres=# select supported_value from information_schema.sql_sizing
where sizing_id=97;
supported_value
-----------------
14
thanks
Shveta
On Tue, Dec 16, 2025 at 11:15 PM Andres Freund <andres@anarazel.de> wrote:
Hi,
On 2025-12-16 11:28:11 -0500, Andres Freund wrote:
On 2025-12-16 21:19:21 +0530, Dilip Kumar wrote:
2) As suggested by Amit Kpila [2], for the upcoming Conflict Log Table
feature, we need a clean way to exclude these internal conflict log
tables from publication. A catalog flag allows us to set this property
at relation creation rather than adding more special cases.Seems like the issue here also could be addressed the same way?
Yeah we can do that, in fact the current patch already does that. The
problem is the conflict log tables are created as regular tables and
in order to identify whether any table is used as a conflict log table
we need to scan the pg_subscription. So in order to simplify this it
seems the table level property could be a better idea considering the
comments atop is_publishable_class() which says "the long-term
solution is to add a boolean
column to pg_class and depend on that instead of OID checks.".
Another motivation I am seeing is that we can extend this solution to
the SQL level syntax whether users can create tables by marking that
as non-publishable so that even when publication is created with ALL
TABLE options such tables can be excluded.
Actually, wouldn't a table-level property be completely inappropriate for
that? Imagine one publication that's used for HA (or major version upgrade)
and doesn't use a conflict table, which replicates all tables (including the
conflict table of another pub/sub). And a subscription doing bi-direction
replication that *does* obviously use the conflict table. In one of those
cases you want to replicate changes to the conflict table, in the other
not. So a table / pg_class property would be inappropriate, no?
Not sure I completely got this point, but let me explain the
requirement, so the idea is that the conflict log table which are
created for the subscription if the conflict log table option is set
then all the conflict occurred on the node would be inserted into this
table, so you can consider this as a log which we want to use for
better lookup instead of looking into the server logs. So IMHO this
table stores very node specific conflict information which might not
make any sense for other nodes.
--
Regards,
Dilip Kumar
Google
Hi,
On 2025-12-17 09:19:57 +0530, Dilip Kumar wrote:
On Tue, Dec 16, 2025 at 11:15 PM Andres Freund <andres@anarazel.de> wrote:
Actually, wouldn't a table-level property be completely inappropriate for
that? Imagine one publication that's used for HA (or major version upgrade)
and doesn't use a conflict table, which replicates all tables (including the
conflict table of another pub/sub). And a subscription doing bi-direction
replication that *does* obviously use the conflict table. In one of those
cases you want to replicate changes to the conflict table, in the other
not. So a table / pg_class property would be inappropriate, no?Not sure I completely got this point, but let me explain the
requirement, so the idea is that the conflict log table which are
created for the subscription if the conflict log table option is set
then all the conflict occurred on the node would be inserted into this
table, so you can consider this as a log which we want to use for
better lookup instead of looking into the server logs. So IMHO this
table stores very node specific conflict information which might not
make any sense for other nodes.
Imagine you have a bi-directional replication setup between A <-> B. Then you
want to upgrade A to a new major version A'. To minimize downtime, you want to
use logical replication for that. For the logical replication A -> A', you
*would* want to logically replicate the conflict log table, because the
history of logical conflicts is actually important.
Greetings,
Andres Freund
On Wed, Dec 17, 2025 at 9:52 AM Andres Freund <andres@anarazel.de> wrote:
On 2025-12-17 09:19:57 +0530, Dilip Kumar wrote:
On Tue, Dec 16, 2025 at 11:15 PM Andres Freund <andres@anarazel.de> wrote:
Actually, wouldn't a table-level property be completely inappropriate for
that? Imagine one publication that's used for HA (or major version upgrade)
and doesn't use a conflict table, which replicates all tables (including the
conflict table of another pub/sub). And a subscription doing bi-direction
replication that *does* obviously use the conflict table. In one of those
cases you want to replicate changes to the conflict table, in the other
not. So a table / pg_class property would be inappropriate, no?Not sure I completely got this point, but let me explain the
requirement, so the idea is that the conflict log table which are
created for the subscription if the conflict log table option is set
then all the conflict occurred on the node would be inserted into this
table, so you can consider this as a log which we want to use for
better lookup instead of looking into the server logs. So IMHO this
table stores very node specific conflict information which might not
make any sense for other nodes.Imagine you have a bi-directional replication setup between A <-> B. Then you
want to upgrade A to a new major version A'. To minimize downtime, you want to
use logical replication for that. For the logical replication A -> A', you
*would* want to logically replicate the conflict log table, because the
history of logical conflicts is actually important.
Yes, we need it for this case but still we need a way to distinguish
cases where we won't need to send conflict table changes for FOR ALL
TABLES publication (say a bi-directional replication set up). For
cases where user wants to allow conflict table changes to be
replicated, we want users to explicitly mention conflict_table in a
publication either as a publication option or explicitly as part of
FOR TABLE syntax, otherwise, its changes won't be replicated. So, to
ignore changes for other publications like FOR ALL TABLES, such a flag
(relispublishable) could be useful. The other way to identify whether
to replicate the changes in conflict table is as mentioned by Dilip
that for the first time when replicating a change for a table, we need
to scan pg_subscription to identify whether the current table is a
conflict log table and then cache it as relsync cache entry. We need
to do this because the conflict table is created as a regular table
along with subscriptions. Do you have any better ideas?
--
With Regards,
Amit Kapila.
Hi,
Here is a completely different idea. This may solve the immediate
problem re the replication of the Conflict Log Table (CLT) at least...
0. AFAIK, anything named with a "pg_" prefix generally means that the
named thing is intended for PG internal use, right?
1. So, the CLT should be automatically named to have a "pg_" prefix,
indicating that it really is an internal table, albeit not a system
catalog. IIRC, this kind of name was a recent suggestion anyhow.
2. Then, the CREATE PUBLICATION pub1 FOR ALL TABLES should be modified
to automatically *exclude* any tables having a "pg_" prefix.
3. But, if you *really* still want to publish the CLT, then you can do it:
3a) publish it explicitly
CREATE PUBLICATION pub1 FOR TABLE pg_clt_for_sub_1234
3b) invent a new option so that FOR ALL TABLES won't exclude it:
CREATE PUBLICATION pub1 FOR ALL TABLES WITH (publish_internal_tables=true);
======
Kind Regards,
Peter Smith.
Fujitsu Australia.
On Wed, Dec 17, 2025 at 12:37 PM Peter Smith <smithpb2250@gmail.com> wrote:
Here is a completely different idea. This may solve the immediate
problem re the replication of the Conflict Log Table (CLT) at least...0. AFAIK, anything named with a "pg_" prefix generally means that the
named thing is intended for PG internal use, right?1. So, the CLT should be automatically named to have a "pg_" prefix,
indicating that it really is an internal table, albeit not a system
catalog. IIRC, this kind of name was a recent suggestion anyhow.
We would also like to give user a provision to provide
conflict_table|conflict_log name. In general also, I am not sure if
assuming this is a good idea as there could be some other internal
table if not now then in future where we would like to allow
replication by default.
--
With Regards,
Amit Kapila.
On Wed, Dec 17, 2025 at 12:37 PM Peter Smith <smithpb2250@gmail.com> wrote:
Here is a completely different idea. This may solve the immediate
problem re the replication of the Conflict Log Table (CLT) at least...
Another idea could be that at the startup (pgoutput_startup), we can
form a conflict_table cache and then use it to determine whether to
send the change or not? Along with that we need to consider whether
publication explicitly indicates that a conflict_table changes needs
to be replicated or not.
--
With Regards,
Amit Kapila.
On 17.12.25 08:07, Peter Smith wrote:
0. AFAIK, anything named with a "pg_" prefix generally means that the
named thing is intended for PG internal use, right?
This might be a convention, but it's not enforced by the system, except
for some object types like schemas and tablespaces.
On Wed, Dec 17, 2025 at 5:53 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
On Wed, Dec 17, 2025 at 12:37 PM Peter Smith <smithpb2250@gmail.com> wrote:
Here is a completely different idea. This may solve the immediate
problem re the replication of the Conflict Log Table (CLT) at least...Another idea could be that at the startup (pgoutput_startup), we can
form a conflict_table cache and then use it to determine whether to
send the change or not? Along with that we need to consider whether
publication explicitly indicates that a conflict_table changes needs
to be replicated or not.
Yet another idea to cache catalog table information to avoid look up
overhead is to declare unique_index on dbid+cat_relid similar to
existing index SubscriptionNameIndexId. Then after the first time
looking up the entry for the catalog table, keep a corresponding flag
in the RelSyncCache table entry.
--
With Regards,
Amit Kapila.