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 12507 : check_rel_can_be_partition(Oid relid)
35 : {
36 : char relkind;
37 : bool relispartition;
38 :
39 : /* Check if relation exists */
40 12507 : if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid)))
41 9 : return false;
42 :
43 12498 : relkind = get_rel_relkind(relid);
44 12498 : relispartition = get_rel_relispartition(relid);
45 :
46 : /* Only allow relation types that can appear in partition trees. */
47 12498 : if (!relispartition && !RELKIND_HAS_PARTITIONS(relkind))
48 7829 : return false;
49 :
50 4669 : 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 649 : pg_partition_tree(PG_FUNCTION_ARGS)
63 : {
64 : #define PG_PARTITION_TREE_COLS 4
65 649 : 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 649 : if (SRF_IS_FIRSTCALL())
71 : {
72 : MemoryContext oldcxt;
73 : TupleDesc tupdesc;
74 :
75 : /* create a function context for cross-call persistence */
76 133 : funcctx = SRF_FIRSTCALL_INIT();
77 :
78 133 : 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 107 : 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 107 : partitions = find_all_inheritors(rootrelid, AccessShareLock, NULL);
89 :
90 107 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
91 0 : elog(ERROR, "return type must be a row type");
92 107 : funcctx->tuple_desc = tupdesc;
93 :
94 : /* The only state we need is the partition list */
95 107 : funcctx->user_fctx = partitions;
96 :
97 107 : MemoryContextSwitchTo(oldcxt);
98 : }
99 :
100 : /* stuff done on every call of the function */
101 623 : funcctx = SRF_PERCALL_SETUP();
102 623 : partitions = (List *) funcctx->user_fctx;
103 :
104 623 : if (funcctx->call_cntr < list_length(partitions))
105 : {
106 : Datum result;
107 516 : Datum values[PG_PARTITION_TREE_COLS] = {0};
108 516 : bool nulls[PG_PARTITION_TREE_COLS] = {0};
109 : HeapTuple tuple;
110 516 : Oid parentid = InvalidOid;
111 516 : Oid relid = list_nth_oid(partitions, funcctx->call_cntr);
112 516 : char relkind = get_rel_relkind(relid);
113 516 : int level = 0;
114 516 : List *ancestors = get_partition_ancestors(relid);
115 : ListCell *lc;
116 :
117 : /*
118 : * Form tuple with appropriate data.
119 : */
120 :
121 : /* relid */
122 516 : values[0] = ObjectIdGetDatum(relid);
123 :
124 : /* parentid */
125 516 : if (ancestors != NIL)
126 429 : parentid = linitial_oid(ancestors);
127 516 : if (OidIsValid(parentid))
128 429 : values[1] = ObjectIdGetDatum(parentid);
129 : else
130 87 : nulls[1] = true;
131 :
132 : /* isleaf */
133 516 : values[2] = BoolGetDatum(!RELKIND_HAS_PARTITIONS(relkind));
134 :
135 : /* level */
136 516 : if (relid != rootrelid)
137 : {
138 586 : foreach(lc, ancestors)
139 : {
140 586 : level++;
141 586 : if (lfirst_oid(lc) == rootrelid)
142 409 : break;
143 : }
144 : }
145 516 : values[3] = Int32GetDatum(level);
146 :
147 516 : tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
148 516 : result = HeapTupleGetDatum(tuple);
149 516 : SRF_RETURN_NEXT(funcctx, result);
150 : }
151 :
152 : /* done when there are no more elements left */
153 107 : 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 3166 : pg_partition_root(PG_FUNCTION_ARGS)
165 : {
166 3166 : Oid relid = PG_GETARG_OID(0);
167 : Oid rootrelid;
168 : List *ancestors;
169 :
170 3166 : if (!check_rel_can_be_partition(relid))
171 2298 : PG_RETURN_NULL();
172 :
173 : /* fetch the list of ancestors */
174 868 : ancestors = get_partition_ancestors(relid);
175 :
176 : /*
177 : * If the input relation is already the top-most parent, just return
178 : * itself.
179 : */
180 868 : if (ancestors == NIL)
181 248 : PG_RETURN_OID(relid);
182 :
183 620 : rootrelid = llast_oid(ancestors);
184 620 : 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 620 : 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 15808 : pg_partition_ancestors(PG_FUNCTION_ARGS)
202 : {
203 15808 : Oid relid = PG_GETARG_OID(0);
204 : FuncCallContext *funcctx;
205 : List *ancestors;
206 :
207 15808 : if (SRF_IS_FIRSTCALL())
208 : {
209 : MemoryContext oldcxt;
210 :
211 9208 : funcctx = SRF_FIRSTCALL_INIT();
212 :
213 9208 : if (!check_rel_can_be_partition(relid))
214 5514 : SRF_RETURN_DONE(funcctx);
215 :
216 3694 : oldcxt = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
217 :
218 3694 : ancestors = get_partition_ancestors(relid);
219 3694 : ancestors = lcons_oid(relid, ancestors);
220 :
221 : /* The only state we need is the ancestors list */
222 3694 : funcctx->user_fctx = ancestors;
223 :
224 3694 : MemoryContextSwitchTo(oldcxt);
225 : }
226 :
227 10294 : funcctx = SRF_PERCALL_SETUP();
228 10294 : ancestors = (List *) funcctx->user_fctx;
229 :
230 10294 : if (funcctx->call_cntr < list_length(ancestors))
231 : {
232 6604 : Oid resultrel = list_nth_oid(ancestors, funcctx->call_cntr);
233 :
234 6604 : SRF_RETURN_NEXT(funcctx, ObjectIdGetDatum(resultrel));
235 : }
236 :
237 3690 : SRF_RETURN_DONE(funcctx);
238 : }
|