From 98356e015914ac0db63f1623b0282a1cddd897d1 Mon Sep 17 00:00:00 2001 From: Fujii Masao Date: Thu, 6 Aug 2026 11:29:47 +0900 Subject: [PATCH v5] Distinguish publication exclusions in object addresses. A pg_publication_rel entry can represent either an explicitly published relation or a table excluded from a FOR ALL TABLES publication, but the object address code treated every entry as a published relation without checking prexcept. EXCEPT entries thus appeared as ordinary published-table mappings in dependency messages, pg_identify_object(), and pg_identify_object_as_address(). Report such entries as "publication excluded relation" instead, with object identities indicating that the table is excluded from the publication. pg_get_object_address() also accepts this new object type, so that the output of pg_identify_object_as_address() can still be passed back to it to look up the same object. "publication relation" now resolves only non-EXCEPT entries, and "publication excluded relation" only EXCEPT entries. Oversight in commit fd366065e06. Author: Fujii Masao Author: Vignesh C Reviewed-by: shveta malik Reviewed-by: Peter Smith Reviewed-by: Zhijie Hou Reviewed-by: Nisha Moond Reviewed-by: Amit Kapila Reviewed-by: Chao Li Discussion: https://postgr.es/m/CAHGQGwHfESBexa7fq99EvFCf31av=O9h9udnw22ymxmm6LMZzw@mail.gmail.com Backpatch-through: 19, where it was introduced --- src/backend/catalog/aclchk.c | 2 + src/backend/catalog/objectaddress.c | 141 ++++++++++++++++--- src/backend/commands/dropcmds.c | 1 + src/backend/commands/event_trigger.c | 2 + src/backend/commands/seclabel.c | 1 + src/include/nodes/parsenodes.h | 1 + src/test/regress/expected/object_address.out | 26 +++- src/test/regress/expected/publication.out | 35 +++++ src/test/regress/sql/object_address.sql | 20 ++- src/test/regress/sql/publication.sql | 29 ++++ 10 files changed, 235 insertions(+), 23 deletions(-) diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index e67358f3858..2bbcc3e824b 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -2765,6 +2765,7 @@ aclcheck_error(AclResult aclerr, ObjectType objtype, case OBJECT_DEFAULT: case OBJECT_DEFACL: case OBJECT_DOMCONSTRAINT: + case OBJECT_PUBLICATION_EXCLUDED_REL: case OBJECT_PUBLICATION_NAMESPACE: case OBJECT_PUBLICATION_REL: case OBJECT_ROLE: @@ -2906,6 +2907,7 @@ aclcheck_error(AclResult aclerr, ObjectType objtype, case OBJECT_DEFACL: case OBJECT_DOMCONSTRAINT: case OBJECT_PARAMETER_ACL: + case OBJECT_PUBLICATION_EXCLUDED_REL: case OBJECT_PUBLICATION_NAMESPACE: case OBJECT_PUBLICATION_REL: case OBJECT_ROLE: diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index b08e076e65f..1e24f4b80be 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -823,6 +823,9 @@ static const struct object_type_map { "publication relation", OBJECT_PUBLICATION_REL }, + { + "publication excluded relation", OBJECT_PUBLICATION_EXCLUDED_REL + }, { "subscription", OBJECT_SUBSCRIPTION }, @@ -863,7 +866,8 @@ static ObjectAddress get_object_address_opf_member(ObjectType objtype, static ObjectAddress get_object_address_usermapping(List *object, bool missing_ok); -static ObjectAddress get_object_address_publication_rel(List *object, +static ObjectAddress get_object_address_publication_rel(ObjectType objtype, + List *object, Relation *relp, bool missing_ok); static ObjectAddress get_object_address_publication_schema(List *object, @@ -880,6 +884,9 @@ static void getRelationTypeDescription(StringInfo buffer, Oid relid, int32 objectSubId, bool missing_ok); static void getProcedureTypeDescription(StringInfo buffer, Oid procid, bool missing_ok); +static void getPublicationRelationTypeDescription(StringInfo buffer, + Oid pubreloid, + bool missing_ok); static void getConstraintTypeDescription(StringInfo buffer, Oid constroid, bool missing_ok); static void getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **object, @@ -1112,8 +1119,10 @@ get_object_address(ObjectType objtype, Node *object, address = get_object_address_publication_schema(castNode(List, object), missing_ok); break; + case OBJECT_PUBLICATION_EXCLUDED_REL: case OBJECT_PUBLICATION_REL: - address = get_object_address_publication_rel(castNode(List, object), + address = get_object_address_publication_rel(objtype, + castNode(List, object), &relation, missing_ok); break; @@ -1860,12 +1869,12 @@ get_object_address_usermapping(List *object, bool missing_ok) } /* - * Find the ObjectAddress for a publication relation. The first element of - * the object parameter is the relation name, the second is the - * publication name. + * Find the ObjectAddress for a published or excluded publication relation. + * The first element of the object parameter is the relation name, the second + * is the publication name. */ static ObjectAddress -get_object_address_publication_rel(List *object, +get_object_address_publication_rel(ObjectType objtype, List *object, Relation *relp, bool missing_ok) { ObjectAddress address; @@ -1873,6 +1882,9 @@ get_object_address_publication_rel(List *object, List *relname; char *pubname; Publication *pub; + Form_pg_publication_rel prform; + HeapTuple tup; + bool isexcept; ObjectAddressSet(address, PublicationRelRelationId, InvalidOid); @@ -1885,6 +1897,9 @@ get_object_address_publication_rel(List *object, /* fetch publication name from input list */ pubname = strVal(lsecond(object)); + Assert(objtype == OBJECT_PUBLICATION_REL || + objtype == OBJECT_PUBLICATION_EXCLUDED_REL); + /* Now look up the pg_publication tuple */ pub = GetPublicationByName(pubname, missing_ok); if (!pub) @@ -1893,22 +1908,54 @@ get_object_address_publication_rel(List *object, return address; } - /* Find the publication relation mapping in syscache. */ - address.objectId = - GetSysCacheOid2(PUBLICATIONRELMAP, Anum_pg_publication_rel_oid, - ObjectIdGetDatum(RelationGetRelid(relation)), - ObjectIdGetDatum(pub->oid)); - if (!OidIsValid(address.objectId)) + /* + * Find the publication relation mapping in syscache. Fetch the tuple + * rather than just its OID, so that prexcept can be checked without a + * second lookup. + */ + tup = SearchSysCache2(PUBLICATIONRELMAP, + ObjectIdGetDatum(RelationGetRelid(relation)), + ObjectIdGetDatum(pub->oid)); + if (!HeapTupleIsValid(tup)) { if (!missing_ok) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("publication relation \"%s\" in publication \"%s\" does not exist", - RelationGetRelationName(relation), pubname))); + { + if (objtype == OBJECT_PUBLICATION_EXCLUDED_REL) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("publication excluded relation \"%s\" in publication \"%s\" does not exist", + RelationGetRelationName(relation), pubname))); + else + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("publication relation \"%s\" in publication \"%s\" does not exist", + RelationGetRelationName(relation), pubname))); + } relation_close(relation, AccessShareLock); return address; } + prform = (Form_pg_publication_rel) GETSTRUCT(tup); + address.objectId = prform->oid; + isexcept = prform->prexcept; + ReleaseSysCache(tup); + + /* + * The same relation and publication pair identifies either a published or + * an excluded relation, so reject an entry of the kind that was not asked + * for. + */ + if (objtype == OBJECT_PUBLICATION_EXCLUDED_REL && !isexcept) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not an excluded relation of publication \"%s\"", + RelationGetRelationName(relation), pubname))); + else if (objtype == OBJECT_PUBLICATION_REL && isexcept) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a published relation of publication \"%s\"", + RelationGetRelationName(relation), pubname))); + *relp = relation; return address; } @@ -2235,6 +2282,7 @@ pg_get_object_address(PG_FUNCTION_ARGS) pg_fallthrough; case OBJECT_DOMCONSTRAINT: case OBJECT_CAST: + case OBJECT_PUBLICATION_EXCLUDED_REL: case OBJECT_PUBLICATION_REL: case OBJECT_DEFACL: case OBJECT_TRANSFORM: @@ -2326,6 +2374,7 @@ pg_get_object_address(PG_FUNCTION_ARGS) case OBJECT_TRANSFORM: objnode = (Node *) list_make2(typename, linitial(args)); break; + case OBJECT_PUBLICATION_EXCLUDED_REL: case OBJECT_PUBLICATION_REL: objnode = (Node *) list_make2(name, linitial(args)); break; @@ -2554,6 +2603,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address, case OBJECT_AMPROC: case OBJECT_DEFAULT: case OBJECT_DEFACL: + case OBJECT_PUBLICATION_EXCLUDED_REL: case OBJECT_PUBLICATION_NAMESPACE: case OBJECT_PUBLICATION_REL: case OBJECT_USER_MAPPING: @@ -4020,9 +4070,18 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok) initStringInfo(&rel); getRelationDescription(&rel, prform->prrelid, false); - /* translator: first %s is, e.g., "table %s" */ - appendStringInfo(&buffer, _("publication of %s in publication %s"), - rel.data, pubname); + if (prform->prexcept) + { + /* translator: first %s is, e.g., "table %s" */ + appendStringInfo(&buffer, _("exclusion of %s from publication %s"), + rel.data, pubname); + } + else + { + /* translator: first %s is, e.g., "table %s" */ + appendStringInfo(&buffer, _("publication of %s in publication %s"), + rel.data, pubname); + } pfree(rel.data); ReleaseSysCache(tup); break; @@ -4654,7 +4713,8 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok) break; case PublicationRelRelationId: - appendStringInfoString(&buffer, "publication relation"); + getPublicationRelationTypeDescription(&buffer, object->objectId, + missing_ok); break; case SubscriptionRelationId: @@ -4808,6 +4868,41 @@ getProcedureTypeDescription(StringInfo buffer, Oid procid, ReleaseSysCache(procTup); } +/* + * subroutine for getObjectTypeDescription: describe a publication relation + * + * Appends "publication excluded relation" for EXCEPT entries, or + * "publication relation" for published relations, to buffer. + */ +static void +getPublicationRelationTypeDescription(StringInfo buffer, Oid pubreloid, + bool missing_ok) +{ + HeapTuple tup; + Form_pg_publication_rel prform; + + tup = SearchSysCache1(PUBLICATIONREL, ObjectIdGetDatum(pubreloid)); + if (!HeapTupleIsValid(tup)) + { + if (!missing_ok) + elog(ERROR, "cache lookup failed for publication table %u", + pubreloid); + + /* fallback to "publication relation" for an undefined object */ + appendStringInfoString(buffer, "publication relation"); + return; + } + + prform = (Form_pg_publication_rel) GETSTRUCT(tup); + + if (prform->prexcept) + appendStringInfoString(buffer, "publication excluded relation"); + else + appendStringInfoString(buffer, "publication relation"); + + ReleaseSysCache(tup); +} + /* * Obtain a given object's identity, as a palloc'ed string. * @@ -5949,7 +6044,11 @@ getObjectIdentityParts(const ObjectAddress *object, pubname = get_publication_name(prform->prpubid, false); getRelationIdentity(&buffer, prform->prrelid, objname, false); - appendStringInfo(&buffer, " in publication %s", pubname); + if (prform->prexcept) + appendStringInfo(&buffer, " excluded from publication %s", + pubname); + else + appendStringInfo(&buffer, " in publication %s", pubname); if (objargs) *objargs = list_make1(pubname); diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c index 92526012d2a..228959e1050 100644 --- a/src/backend/commands/dropcmds.c +++ b/src/backend/commands/dropcmds.c @@ -504,6 +504,7 @@ does_not_exist_skipping(ObjectType objtype, Node *object) case OBJECT_DOMCONSTRAINT: case OBJECT_LARGEOBJECT: case OBJECT_PARAMETER_ACL: + case OBJECT_PUBLICATION_EXCLUDED_REL: case OBJECT_PUBLICATION_NAMESPACE: case OBJECT_PUBLICATION_REL: case OBJECT_TABCONSTRAINT: diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index d868c7f42c3..08f8f4c3f24 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -2316,6 +2316,7 @@ stringify_grant_objtype(ObjectType objtype) case OBJECT_OPFAMILY: case OBJECT_POLICY: case OBJECT_PUBLICATION: + case OBJECT_PUBLICATION_EXCLUDED_REL: case OBJECT_PUBLICATION_NAMESPACE: case OBJECT_PUBLICATION_REL: case OBJECT_ROLE: @@ -2400,6 +2401,7 @@ stringify_adefprivs_objtype(ObjectType objtype) case OBJECT_PARAMETER_ACL: case OBJECT_POLICY: case OBJECT_PUBLICATION: + case OBJECT_PUBLICATION_EXCLUDED_REL: case OBJECT_PUBLICATION_NAMESPACE: case OBJECT_PUBLICATION_REL: case OBJECT_ROLE: diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c index 5b80396723c..77e3640e626 100644 --- a/src/backend/commands/seclabel.c +++ b/src/backend/commands/seclabel.c @@ -80,6 +80,7 @@ SecLabelSupportsObjectType(ObjectType objtype) case OBJECT_OPFAMILY: case OBJECT_PARAMETER_ACL: case OBJECT_POLICY: + case OBJECT_PUBLICATION_EXCLUDED_REL: case OBJECT_PUBLICATION_NAMESPACE: case OBJECT_PUBLICATION_REL: case OBJECT_RULE: diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index aea7311faad..0debcd193ab 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -2395,6 +2395,7 @@ typedef enum ObjectType OBJECT_POLICY, OBJECT_PROCEDURE, OBJECT_PUBLICATION, + OBJECT_PUBLICATION_EXCLUDED_REL, OBJECT_PUBLICATION_NAMESPACE, OBJECT_PUBLICATION_REL, OBJECT_ROLE, diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out index 4c266d7fa67..101bf3cdfac 100644 --- a/src/test/regress/expected/object_address.out +++ b/src/test/regress/expected/object_address.out @@ -111,7 +111,8 @@ BEGIN ('text search template'), ('text search configuration'), ('policy'), ('user mapping'), ('default acl'), ('transform'), ('operator of access method'), ('function of access method'), - ('publication namespace'), ('publication relation') + ('publication namespace'), ('publication relation'), + ('publication excluded relation') LOOP FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}') LOOP @@ -331,6 +332,12 @@ WARNING: error for publication relation,{addr_nsp,zwei},{}: argument list lengt WARNING: error for publication relation,{addr_nsp,zwei},{integer}: relation "addr_nsp.zwei" does not exist WARNING: error for publication relation,{eins,zwei,drei},{}: argument list length must be exactly 1 WARNING: error for publication relation,{eins,zwei,drei},{integer}: cross-database references are not implemented: "eins.zwei.drei" +WARNING: error for publication excluded relation,{eins},{}: argument list length must be exactly 1 +WARNING: error for publication excluded relation,{eins},{integer}: relation "eins" does not exist +WARNING: error for publication excluded relation,{addr_nsp,zwei},{}: argument list length must be exactly 1 +WARNING: error for publication excluded relation,{addr_nsp,zwei},{integer}: relation "addr_nsp.zwei" does not exist +WARNING: error for publication excluded relation,{eins,zwei,drei},{}: argument list length must be exactly 1 +WARNING: error for publication excluded relation,{eins,zwei,drei},{integer}: cross-database references are not implemented: "eins.zwei.drei" -- these object types cannot be qualified names SELECT pg_get_object_address('language', '{one}', '{}'); ERROR: language "one" does not exist @@ -509,6 +516,23 @@ subscription|NULL|regress_addr_sub|regress_addr_sub|t publication|NULL|addr_pub|addr_pub|t publication relation|NULL|NULL|addr_nsp.gentable in publication addr_pub|t publication namespace|NULL|NULL|addr_nsp in publication addr_pub_schema|t +-- A FOR ALL TABLES publication is listed by \d for every table in the +-- database, which would disturb the other tests running concurrently in this +-- parallel group. Create it in a transaction that is rolled back, so that it +-- is never visible to another session. +BEGIN; +SET LOCAL client_min_messages = 'ERROR'; +CREATE PUBLICATION addr_pub_except FOR ALL TABLES EXCEPT (TABLE addr_nsp.gentable); +SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.objsubid)).*, + ROW(pg_identify_object(addr1.classid, addr1.objid, addr1.objsubid)) = + ROW(pg_identify_object(addr2.classid, addr2.objid, addr2.objsubid)) AS roundtrip +FROM pg_get_object_address('publication excluded relation', + '{addr_nsp, gentable}', + '{addr_pub_except}') AS addr1, + pg_identify_object_as_address(classid, objid, objsubid) AS ioa (typ, nms, args), + pg_get_object_address(typ, nms, ioa.args) AS addr2; +publication excluded relation|NULL|NULL|addr_nsp.gentable excluded from publication addr_pub_except|t +ROLLBACK; --- --- Cleanup resources --- diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out index fe602c4538e..d51b27d7d02 100644 --- a/src/test/regress/expected/publication.out +++ b/src/test/regress/expected/publication.out @@ -274,6 +274,41 @@ Excluded from publications: "testpub_foralltables_excepttable" "testpub_foralltables_excepttable1" +-- Check object address handling for an EXCEPT entry. +\a\t +SELECT (pg_identify_object('pg_publication_rel'::regclass, pr.oid, 0)).* +FROM pg_publication_rel pr +JOIN pg_publication p ON p.oid = pr.prpubid +JOIN pg_class c ON c.oid = pr.prrelid +WHERE p.pubname = 'testpub_foralltables_excepttable1' + AND c.relname = 'testpub_tbl1'; +publication excluded relation|||public.testpub_tbl1 excluded from publication testpub_foralltables_excepttable1 +-- testpub_describe publishes testpub_tbl1, testpub_foralltables_excepttable1 +-- excludes it; an entry of one kind must not be resolved as the other. +CREATE PUBLICATION testpub_describe FOR TABLE testpub_tbl1; +SELECT pg_get_object_address('publication excluded relation', + '{public, testpub_tbl1}', '{testpub_describe}'); +ERROR: "testpub_tbl1" is not an excluded relation of publication "testpub_describe" +SELECT pg_get_object_address('publication relation', + '{public, testpub_tbl1}', + '{testpub_foralltables_excepttable1}'); +ERROR: "testpub_tbl1" is not a published relation of publication "testpub_foralltables_excepttable1" +-- No entry of either kind. testpub_default publishes nothing. +SELECT pg_get_object_address('publication excluded relation', + '{public, testpub_tbl1}', '{testpub_default}'); +ERROR: publication excluded relation "testpub_tbl1" in publication "testpub_default" does not exist +-- Check pg_describe_object output for both included and excluded entries +SELECT p.pubname, + pg_describe_object('pg_publication_rel'::regclass, pr.oid, 0) AS description, + pr.prexcept +FROM pg_publication_rel pr +JOIN pg_publication p ON p.oid = pr.prpubid +WHERE p.pubname IN ('testpub_describe', 'testpub_foralltables_excepttable1') +ORDER BY p.pubname; +testpub_describe|publication of table testpub_tbl1 in publication testpub_describe|f +testpub_foralltables_excepttable1|exclusion of table testpub_tbl1 from publication testpub_foralltables_excepttable1|t +DROP PUBLICATION testpub_describe; +\a\t -- fail - first table in the EXCEPT list should use TABLE keyword CREATE PUBLICATION testpub_foralltables_excepttable2 FOR ALL TABLES EXCEPT (testpub_tbl1, testpub_tbl2); ERROR: syntax error at or near "testpub_tbl1" diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql index 653a53038e3..3e4638b905b 100644 --- a/src/test/regress/sql/object_address.sql +++ b/src/test/regress/sql/object_address.sql @@ -103,7 +103,8 @@ BEGIN ('text search template'), ('text search configuration'), ('policy'), ('user mapping'), ('default acl'), ('transform'), ('operator of access method'), ('function of access method'), - ('publication namespace'), ('publication relation') + ('publication namespace'), ('publication relation'), + ('publication excluded relation') LOOP FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}') LOOP @@ -225,6 +226,23 @@ FROM objects, pg_get_object_address(typ, nms, ioa.args) AS addr2 ORDER BY addr1.classid, addr1.objid, addr1.objsubid; +-- A FOR ALL TABLES publication is listed by \d for every table in the +-- database, which would disturb the other tests running concurrently in this +-- parallel group. Create it in a transaction that is rolled back, so that it +-- is never visible to another session. +BEGIN; +SET LOCAL client_min_messages = 'ERROR'; +CREATE PUBLICATION addr_pub_except FOR ALL TABLES EXCEPT (TABLE addr_nsp.gentable); +SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.objsubid)).*, + ROW(pg_identify_object(addr1.classid, addr1.objid, addr1.objsubid)) = + ROW(pg_identify_object(addr2.classid, addr2.objid, addr2.objsubid)) AS roundtrip +FROM pg_get_object_address('publication excluded relation', + '{addr_nsp, gentable}', + '{addr_pub_except}') AS addr1, + pg_identify_object_as_address(classid, objid, objsubid) AS ioa (typ, nms, args), + pg_get_object_address(typ, nms, ioa.args) AS addr2; +ROLLBACK; + --- --- Cleanup resources --- diff --git a/src/test/regress/sql/publication.sql b/src/test/regress/sql/publication.sql index 80c244c9138..074482605d9 100644 --- a/src/test/regress/sql/publication.sql +++ b/src/test/regress/sql/publication.sql @@ -123,6 +123,35 @@ CREATE PUBLICATION testpub_foralltables_excepttable1 FOR ALL TABLES EXCEPT (TABL -- Check that the table description shows the publications where it is listed -- in the EXCEPT clause \d testpub_tbl1 +-- Check object address handling for an EXCEPT entry. +\a\t +SELECT (pg_identify_object('pg_publication_rel'::regclass, pr.oid, 0)).* +FROM pg_publication_rel pr +JOIN pg_publication p ON p.oid = pr.prpubid +JOIN pg_class c ON c.oid = pr.prrelid +WHERE p.pubname = 'testpub_foralltables_excepttable1' + AND c.relname = 'testpub_tbl1'; +-- testpub_describe publishes testpub_tbl1, testpub_foralltables_excepttable1 +-- excludes it; an entry of one kind must not be resolved as the other. +CREATE PUBLICATION testpub_describe FOR TABLE testpub_tbl1; +SELECT pg_get_object_address('publication excluded relation', + '{public, testpub_tbl1}', '{testpub_describe}'); +SELECT pg_get_object_address('publication relation', + '{public, testpub_tbl1}', + '{testpub_foralltables_excepttable1}'); +-- No entry of either kind. testpub_default publishes nothing. +SELECT pg_get_object_address('publication excluded relation', + '{public, testpub_tbl1}', '{testpub_default}'); +-- Check pg_describe_object output for both included and excluded entries +SELECT p.pubname, + pg_describe_object('pg_publication_rel'::regclass, pr.oid, 0) AS description, + pr.prexcept +FROM pg_publication_rel pr +JOIN pg_publication p ON p.oid = pr.prpubid +WHERE p.pubname IN ('testpub_describe', 'testpub_foralltables_excepttable1') +ORDER BY p.pubname; +DROP PUBLICATION testpub_describe; +\a\t -- fail - first table in the EXCEPT list should use TABLE keyword CREATE PUBLICATION testpub_foralltables_excepttable2 FOR ALL TABLES EXCEPT (testpub_tbl1, testpub_tbl2); -- 2.55.0