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