Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * partitionfuncs.c
4 : * Functions for accessing partition-related metadata
5 : *
6 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/utils/adt/partitionfuncs.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 :
16 : #include "postgres.h"
17 :
18 : #include "access/htup_details.h"
19 : #include "catalog/partition.h"
20 : #include "catalog/pg_class.h"
21 : #include "catalog/pg_inherits.h"
22 : #include "funcapi.h"
23 : #include "utils/fmgrprotos.h"
24 : #include "utils/lsyscache.h"
25 : #include "utils/syscache.h"
26 :
27 : /*
28 : * Checks if a given relation can be part of a partition tree. Returns
29 : * false if the relation cannot be processed, in which case it is up to
30 : * the caller to decide what to do, by either raising an error or doing
31 : * something else.
32 : */
33 : static bool
34 7122 : check_rel_can_be_partition(Oid relid)
35 : {
36 : char relkind;
37 : bool relispartition;
38 :
39 : /* Check if relation exists */
40 7122 : if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid)))
41 9 : return false;
42 :
43 7113 : relkind = get_rel_relkind(relid);
44 7113 : relispartition = get_rel_relispartition(relid);
45 :
46 : /* Only allow relation types that can appear in partition trees. */
47 7113 : if (!relispartition && !RELKIND_HAS_PARTITIONS(relkind))
48 4213 : return false;
49 :
50 2900 : return true;
51 : }
52 :
53 : /*
54 : * pg_partition_tree
55 : *
56 : * Produce a view with one row per member of a partition tree, beginning
57 : * from the top-most parent given by the caller. This gives information
58 : * about each partition, its immediate partitioned parent, if it is
59 : * a leaf partition and its level in the hierarchy.
60 : */
61 : Datum
62 553 : pg_partition_tree(PG_FUNCTION_ARGS)
63 : {
64 : #define PG_PARTITION_TREE_COLS 4
65 553 : Oid rootrelid = PG_GETARG_OID(0);
66 : FuncCallContext *funcctx;
67 : List *partitions;
68 :
69 : /* stuff done only on the first call of the function */
70 553 : if (SRF_IS_FIRSTCALL())
71 : {
72 : MemoryContext oldcxt;
73 : TupleDesc tupdesc;
74 :
75 : /* create a function context for cross-call persistence */
76 121 : funcctx = SRF_FIRSTCALL_INIT();
77 :
78 121 : if (!check_rel_can_be_partition(rootrelid))
79 26 : SRF_RETURN_DONE(funcctx);
80 :
81 : /* switch to memory context appropriate for multiple function calls */
82 95 : oldcxt = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
83 :
84 : /*
85 : * Find all members of inheritance set. We only need AccessShareLock
86 : * on the children for the partition information lookup.
87 : */
88 95 : partitions = find_all_inheritors(rootrelid, AccessShareLock, NULL);
89 :
90 95 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
91 0 : elog(ERROR, "return type must be a row type");
92 95 : funcctx->tuple_desc = tupdesc;
93 :
94 : /* The only state we need is the partition list */
95 95 : funcctx->user_fctx = partitions;
96 :
97 95 : MemoryContextSwitchTo(oldcxt);
98 : }
99 :
100 : /* stuff done on every call of the function */
101 527 : funcctx = SRF_PERCALL_SETUP();
102 527 : partitions = (List *) funcctx->user_fctx;
103 :
104 527 : if (funcctx->call_cntr < list_length(partitions))
105 : {
106 : Datum result;
107 432 : Datum values[PG_PARTITION_TREE_COLS] = {0};
108 432 : bool nulls[PG_PARTITION_TREE_COLS] = {0};
109 : HeapTuple tuple;
110 432 : Oid parentid = InvalidOid;
111 432 : Oid relid = list_nth_oid(partitions, funcctx->call_cntr);
112 432 : char relkind = get_rel_relkind(relid);
113 432 : int level = 0;
114 432 : List *ancestors = get_partition_ancestors(relid);
115 : ListCell *lc;
116 :
117 : /*
118 : * Form tuple with appropriate data.
119 : */
120 :
121 : /* relid */
122 432 : values[0] = ObjectIdGetDatum(relid);
123 :
124 : /* parentid */
125 432 : if (ancestors != NIL)
126 357 : parentid = linitial_oid(ancestors);
127 432 : if (OidIsValid(parentid))
128 357 : values[1] = ObjectIdGetDatum(parentid);
129 : else
130 75 : nulls[1] = true;
131 :
132 : /* isleaf */
133 432 : values[2] = BoolGetDatum(!RELKIND_HAS_PARTITIONS(relkind));
134 :
135 : /* level */
136 432 : if (relid != rootrelid)
137 : {
138 478 : foreach(lc, ancestors)
139 : {
140 478 : level++;
141 478 : if (lfirst_oid(lc) == rootrelid)
142 337 : break;
143 : }
144 : }
145 432 : values[3] = Int32GetDatum(level);
146 :
147 432 : tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
148 432 : result = HeapTupleGetDatum(tuple);
149 432 : SRF_RETURN_NEXT(funcctx, result);
150 : }
151 :
152 : /* done when there are no more elements left */
153 95 : SRF_RETURN_DONE(funcctx);
154 : }
155 :
156 : /*
157 : * pg_partition_root
158 : *
159 : * Returns the top-most parent of the partition tree to which a given
160 : * relation belongs, or NULL if it's not (or cannot be) part of any
161 : * partition tree.
162 : */
163 : Datum
164 48 : pg_partition_root(PG_FUNCTION_ARGS)
165 : {
166 48 : Oid relid = PG_GETARG_OID(0);
167 : Oid rootrelid;
168 : List *ancestors;
169 :
170 48 : if (!check_rel_can_be_partition(relid))
171 18 : PG_RETURN_NULL();
172 :
173 : /* fetch the list of ancestors */
174 30 : ancestors = get_partition_ancestors(relid);
175 :
176 : /*
177 : * If the input relation is already the top-most parent, just return
178 : * itself.
179 : */
180 30 : if (ancestors == NIL)
181 6 : PG_RETURN_OID(relid);
182 :
183 24 : rootrelid = llast_oid(ancestors);
184 24 : list_free(ancestors);
185 :
186 : /*
187 : * "rootrelid" must contain a valid OID, given that the input relation is
188 : * a valid partition tree member as checked above.
189 : */
190 : Assert(OidIsValid(rootrelid));
191 24 : PG_RETURN_OID(rootrelid);
192 : }
193 :
194 : /*
195 : * pg_partition_ancestors
196 : *
197 : * Produces a view with one row per ancestor of the given partition,
198 : * including the input relation itself.
199 : */
200 : Datum
201 11928 : pg_partition_ancestors(PG_FUNCTION_ARGS)
202 : {
203 11928 : Oid relid = PG_GETARG_OID(0);
204 : FuncCallContext *funcctx;
205 : List *ancestors;
206 :
207 11928 : if (SRF_IS_FIRSTCALL())
208 : {
209 : MemoryContext oldcxt;
210 :
211 6953 : funcctx = SRF_FIRSTCALL_INIT();
212 :
213 6953 : if (!check_rel_can_be_partition(relid))
214 4178 : SRF_RETURN_DONE(funcctx);
215 :
216 2775 : oldcxt = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
217 :
218 2775 : ancestors = get_partition_ancestors(relid);
219 2775 : ancestors = lcons_oid(relid, ancestors);
220 :
221 : /* The only state we need is the ancestors list */
222 2775 : funcctx->user_fctx = ancestors;
223 :
224 2775 : MemoryContextSwitchTo(oldcxt);
225 : }
226 :
227 7750 : funcctx = SRF_PERCALL_SETUP();
228 7750 : ancestors = (List *) funcctx->user_fctx;
229 :
230 7750 : if (funcctx->call_cntr < list_length(ancestors))
231 : {
232 4977 : Oid resultrel = list_nth_oid(ancestors, funcctx->call_cntr);
233 :
234 4977 : SRF_RETURN_NEXT(funcctx, ObjectIdGetDatum(resultrel));
235 : }
236 :
237 2773 : SRF_RETURN_DONE(funcctx);
238 : }
|