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