From 3f51a894e47acc889655ddc23a1a81ab68ad2e14 Mon Sep 17 00:00:00 2001 From: Peter Smith Date: Mon, 3 Aug 2026 13:54:23 +1000 Subject: [PATCH v5] Add C function get_partition_root Adds a C equivalent of the existing SQL function pg_partition_root. Author: Peter Smith Reviewed-by: shveta malik Reviewed-by: Chao Li Discussion: https://www.postgresql.org/message-id/flat/CAJpy0uDrs7ag3QwU7QgDNXOo9trLxgq881Qu1TgzuJVwsNpnHA%40mail.gmail.com#2438fc93ce2aa09eabdda0b16af3f5c9 --- src/backend/catalog/partition.c | 73 +++++++++++++++++++++++--- src/backend/catalog/pg_depend.c | 4 +- src/backend/utils/adt/partitionfuncs.c | 14 +---- src/include/catalog/partition.h | 1 + 4 files changed, 69 insertions(+), 23 deletions(-) diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 28f3cade6ff..80aba23b846 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -29,12 +29,14 @@ #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); /* * get_partition_parent @@ -118,6 +120,62 @@ 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. + * + * 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; + + /* + * 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; + } + + /* Sanity check: The root must be a partitioned table */ + Assert(RELKIND_HAS_PARTITIONS(get_rel_relkind(root_relid))); + + return root_relid; +} + /* * get_partition_ancestors * Obtain ancestors of given relation @@ -135,10 +193,11 @@ get_partition_ancestors(Oid relid) { List *result = NIL; Relation inhRel; + bool detach_pending = false; 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 +209,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..e7cfbdfd259 100644 --- a/src/backend/utils/adt/partitionfuncs.c +++ b/src/backend/utils/adt/partitionfuncs.c @@ -165,23 +165,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..e4358d3c0de 100644 --- a/src/include/catalog/partition.h +++ b/src/include/catalog/partition.h @@ -21,6 +21,7 @@ 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