From 9c6fd6501953f119b3f0266f4d3e492f76138b88 Mon Sep 17 00:00:00 2001 From: Peter Smith Date: Sat, 15 Aug 2026 18:45:31 +1000 Subject: [PATCH v7] Add C function get_partition_root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a C equivalent of the existing SQL function pg_partition_root. Author: Peter Smith Reviewed-by: shveta malik Reviewed-by: Chao Li Reviewed=by: Álvaro Herrera Discussion: https://www.postgresql.org/message-id/flat/CAJpy0uDrs7ag3QwU7QgDNXOo9trLxgq881Qu1TgzuJVwsNpnHA%40mail.gmail.com#2438fc93ce2aa09eabdda0b16af3f5c9 --- src/backend/catalog/partition.c | 104 +++++++++++++++++++++++-- src/backend/catalog/pg_depend.c | 4 +- src/backend/utils/adt/partitionfuncs.c | 40 +--------- src/include/catalog/partition.h | 2 + 4 files changed, 101 insertions(+), 49 deletions(-) diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 28f3cade6ff..e2c833a896f 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -29,12 +29,40 @@ #include "utils/fmgroids.h" #include "utils/partcache.h" #include "utils/rel.h" +#include "utils/lsyscache.h" #include "utils/syscache.h" static Oid get_partition_parent_worker(Relation inhRel, Oid relid, bool *detach_pending); static void get_partition_ancestors_worker(Relation inhRel, Oid relid, - List **ancestors); + List **ancestors, + bool *detach_pending); + +/* + * Checks if a given relation can be part of a partition tree. Returns + * false if the relation cannot be processed, in which case it is up to + * the caller to decide what to do, by either raising an error or doing + * something else. + */ +bool +check_rel_can_be_partition(Oid relid) +{ + char relkind; + bool relispartition; + + /* Check if relation exists */ + if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid))) + return false; + + relkind = get_rel_relkind(relid); + relispartition = get_rel_relispartition(relid); + + /* Only allow relation types that can appear in partition trees. */ + if (!relispartition && !RELKIND_HAS_PARTITIONS(relkind)) + return false; + + return true; +} /* * get_partition_parent @@ -118,6 +146,63 @@ get_partition_parent_worker(Relation inhRel, Oid relid, bool *detach_pending) return result; } +/* + * get_partition_root + * Obtain root partitioned table OID of the specified relation + * + * If the partition is in the process of being detached, return InvalidOid, + * unless `even_if_detached` is passed as true (in which case return the + * original `relid`). + * + * Note: This should only be called when it is known that the relation is a + * partition or partitioned table. + */ +Oid +get_partition_root(Oid relid, bool even_if_detached) +{ + Oid root_relid; + List *ancestors = NIL; + Relation inhRel; + bool detach_pending = false; + + /* Validate relid is member of a partition tree */ + Assert(check_rel_can_be_partition(relid)); + + /* + * Fetch the list of ancestors. This is same as get_partition_ancestors, + * but calling directly to get_partition_ancestors_worker exposes the + * `detach_pending` flag. + */ + inhRel = table_open(InheritsRelationId, AccessShareLock); + get_partition_ancestors_worker(inhRel, relid, &ancestors, &detach_pending); + table_close(inhRel, AccessShareLock); + + if (ancestors) + { + /* By definition, the last ancestor is the topmost parent */ + root_relid = llast_oid(ancestors); + list_free(ancestors); + } + else + { + /* + * NIL ancestors can mean either: + * 1. a detach is pending. + * 2. relid was already the topmost parent. + */ + elog(DEBUG1, "get_partition_root found ancestors=NIL with " + "detach_pending=%s for relid %u", + detach_pending ? "true" : "false", relid); + + if (detach_pending) + return even_if_detached ? relid : InvalidOid; + else + root_relid = relid; + } + + return root_relid; +} + /* * get_partition_ancestors * Obtain ancestors of given relation @@ -135,10 +220,15 @@ get_partition_ancestors(Oid relid) { List *result = NIL; Relation inhRel; + bool detach_pending = false; + + /* Validate relid is member of a partition tree */ + Assert(check_rel_can_be_partition(relid) || + get_rel_relkind(relid) == RELKIND_INDEX); inhRel = table_open(InheritsRelationId, AccessShareLock); - get_partition_ancestors_worker(inhRel, relid, &result); + get_partition_ancestors_worker(inhRel, relid, &result, &detach_pending); table_close(inhRel, AccessShareLock); @@ -150,21 +240,21 @@ get_partition_ancestors(Oid relid) * recursive worker for get_partition_ancestors */ static void -get_partition_ancestors_worker(Relation inhRel, Oid relid, List **ancestors) +get_partition_ancestors_worker(Relation inhRel, Oid relid, List **ancestors, + bool *detach_pending) { Oid parentOid; - bool detach_pending; /* * Recursion ends at the topmost level, ie., when there's no parent; also * when the partition is being detached. */ - parentOid = get_partition_parent_worker(inhRel, relid, &detach_pending); - if (parentOid == InvalidOid || detach_pending) + parentOid = get_partition_parent_worker(inhRel, relid, detach_pending); + if (parentOid == InvalidOid || *detach_pending) return; *ancestors = lappend_oid(*ancestors, parentOid); - get_partition_ancestors_worker(inhRel, parentOid, ancestors); + get_partition_ancestors_worker(inhRel, parentOid, ancestors, detach_pending); } /* diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c index 9a7a401aced..a42c4f33387 100644 --- a/src/backend/catalog/pg_depend.c +++ b/src/backend/catalog/pg_depend.c @@ -1157,15 +1157,13 @@ getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok) */ if (RelationGetForm(rel)->relispartition) { - List *ancestors = get_partition_ancestors(relid); const char *attname = get_attname(relid, attnum, false); - relid = llast_oid(ancestors); + relid = get_partition_root(relid, true); attnum = get_attnum(relid, attname); if (attnum == InvalidAttrNumber) elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u", attname, relid); - list_free(ancestors); } seqlist = getOwnedSequences_internal(relid, attnum, DEPENDENCY_INTERNAL); diff --git a/src/backend/utils/adt/partitionfuncs.c b/src/backend/utils/adt/partitionfuncs.c index e9db027aa2e..c2c63e1e22c 100644 --- a/src/backend/utils/adt/partitionfuncs.c +++ b/src/backend/utils/adt/partitionfuncs.c @@ -24,32 +24,6 @@ #include "utils/lsyscache.h" #include "utils/syscache.h" -/* - * Checks if a given relation can be part of a partition tree. Returns - * false if the relation cannot be processed, in which case it is up to - * the caller to decide what to do, by either raising an error or doing - * something else. - */ -static bool -check_rel_can_be_partition(Oid relid) -{ - char relkind; - bool relispartition; - - /* Check if relation exists */ - if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid))) - return false; - - relkind = get_rel_relkind(relid); - relispartition = get_rel_relispartition(relid); - - /* Only allow relation types that can appear in partition trees. */ - if (!relispartition && !RELKIND_HAS_PARTITIONS(relkind)) - return false; - - return true; -} - /* * pg_partition_tree * @@ -165,23 +139,11 @@ pg_partition_root(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); Oid rootrelid; - List *ancestors; if (!check_rel_can_be_partition(relid)) PG_RETURN_NULL(); - /* fetch the list of ancestors */ - ancestors = get_partition_ancestors(relid); - - /* - * If the input relation is already the top-most parent, just return - * itself. - */ - if (ancestors == NIL) - PG_RETURN_OID(relid); - - rootrelid = llast_oid(ancestors); - list_free(ancestors); + rootrelid = get_partition_root(relid, true); /* * "rootrelid" must contain a valid OID, given that the input relation is diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h index a93cf081dd2..5419d1c5533 100644 --- a/src/include/catalog/partition.h +++ b/src/include/catalog/partition.h @@ -19,8 +19,10 @@ /* Seed for the extended hash function */ #define HASH_PARTITION_SEED UINT64CONST(0x7A5B22367996DCFD) +extern bool check_rel_can_be_partition(Oid relid); extern Oid get_partition_parent(Oid relid, bool even_if_detached); extern List *get_partition_ancestors(Oid relid); +extern Oid get_partition_root(Oid relid, bool even_if_detached); extern Oid index_get_partition(Relation partition, Oid indexId); extern List *map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel); -- 2.47.3