Age Owner 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
1521 michael 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);
1499 45 1432 : relispartition = get_rel_relispartition(relid);
46 :
47 : /* Only allow relation types that can appear in partition trees. */
492 peter 48 1432 : if (!relispartition && !RELKIND_HAS_PARTITIONS(relkind))
1521 michael 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
1622 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 :
1500 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 */
1622 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 :
109 michael 91 GNC 83 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
109 michael 92 UNC 0 : elog(ERROR, "return type must be a row type");
109 michael 93 GNC 83 : funcctx->tuple_desc = tupdesc;
1622 michael 94 ECB :
1364 tgl 95 : /* The only state we need is the partition list */
1364 tgl 96 GIC 83 : funcctx->user_fctx = (void *) partitions;
1622 michael 97 ECB :
1622 michael 98 GIC 83 : MemoryContextSwitchTo(oldcxt);
99 : }
1622 michael 100 ECB :
101 : /* stuff done on every call of the function */
1622 michael 102 GIC 455 : funcctx = SRF_PERCALL_SETUP();
1364 tgl 103 CBC 455 : partitions = (List *) funcctx->user_fctx;
1622 michael 104 ECB :
1364 tgl 105 CBC 455 : if (funcctx->call_cntr < list_length(partitions))
1622 michael 106 ECB : {
107 : Datum result;
267 peter 108 GNC 372 : Datum values[PG_PARTITION_TREE_COLS] = {0};
109 372 : bool nulls[PG_PARTITION_TREE_COLS] = {0};
110 : HeapTuple tuple;
1622 michael 111 GIC 372 : Oid parentid = InvalidOid;
1364 tgl 112 372 : Oid relid = list_nth_oid(partitions, funcctx->call_cntr);
1622 michael 113 372 : char relkind = get_rel_relkind(relid);
114 372 : int level = 0;
1364 tgl 115 CBC 372 : List *ancestors = get_partition_ancestors(relid);
116 : ListCell *lc;
117 :
1622 michael 118 ECB : /*
119 : * Form tuple with appropriate data.
120 : */
121 :
122 : /* relid */
1622 michael 123 GIC 372 : values[0] = ObjectIdGetDatum(relid);
1622 michael 124 ECB :
125 : /* parentid */
1622 michael 126 GIC 372 : if (ancestors != NIL)
1622 michael 127 CBC 307 : parentid = linitial_oid(ancestors);
1622 michael 128 GIC 372 : if (OidIsValid(parentid))
1622 michael 129 CBC 307 : values[1] = ObjectIdGetDatum(parentid);
130 : else
131 65 : nulls[1] = true;
1622 michael 132 ECB :
133 : /* isleaf */
492 peter 134 GIC 372 : values[2] = BoolGetDatum(!RELKIND_HAS_PARTITIONS(relkind));
135 :
1622 michael 136 ECB : /* level */
1622 michael 137 GIC 372 : if (relid != rootrelid)
1622 michael 138 ECB : {
1622 michael 139 CBC 412 : foreach(lc, ancestors)
1622 michael 140 ECB : {
1622 michael 141 GIC 412 : level++;
142 412 : if (lfirst_oid(lc) == rootrelid)
143 289 : break;
1622 michael 144 ECB : }
145 : }
1622 michael 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);
1622 michael 155 ECB : }
156 :
1521 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
1521 michael 165 CBC 48 : pg_partition_root(PG_FUNCTION_ARGS)
166 : {
1521 michael 167 GIC 48 : Oid relid = PG_GETARG_OID(0);
168 : Oid rootrelid;
169 : List *ancestors;
170 :
1521 michael 171 CBC 48 : if (!check_rel_can_be_partition(relid))
172 18 : PG_RETURN_NULL();
173 :
1479 michael 174 ECB : /* fetch the list of ancestors */
1521 michael 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 : */
1479 181 30 : if (ancestors == NIL)
182 6 : PG_RETURN_OID(relid);
183 :
1521 michael 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));
1521 michael 192 CBC 24 : PG_RETURN_OID(rootrelid);
193 : }
1497 alvherre 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
1497 alvherre 202 CBC 3006 : pg_partition_ancestors(PG_FUNCTION_ARGS)
203 : {
204 3006 : Oid relid = PG_GETARG_OID(0);
1497 alvherre 205 ECB : FuncCallContext *funcctx;
206 : List *ancestors;
207 :
1497 alvherre 208 GIC 3006 : if (SRF_IS_FIRSTCALL())
1497 alvherre 209 ECB : {
210 : MemoryContext oldcxt;
211 :
1497 alvherre 212 GIC 1292 : funcctx = SRF_FIRSTCALL_INIT();
1497 alvherre 213 ECB :
1497 alvherre 214 GIC 1292 : if (!check_rel_can_be_partition(relid))
1497 alvherre 215 CBC 287 : SRF_RETURN_DONE(funcctx);
216 :
1497 alvherre 217 GIC 1005 : oldcxt = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1497 alvherre 218 ECB :
1497 alvherre 219 CBC 1005 : ancestors = get_partition_ancestors(relid);
1497 alvherre 220 GIC 1005 : ancestors = lcons_oid(relid, ancestors);
1497 alvherre 221 ECB :
222 : /* The only state we need is the ancestors list */
1364 tgl 223 CBC 1005 : funcctx->user_fctx = (void *) ancestors;
224 :
1497 alvherre 225 1005 : MemoryContextSwitchTo(oldcxt);
226 : }
227 :
228 2719 : funcctx = SRF_PERCALL_SETUP();
1364 tgl 229 GIC 2719 : ancestors = (List *) funcctx->user_fctx;
230 :
231 2719 : if (funcctx->call_cntr < list_length(ancestors))
232 : {
186 drowley 233 GNC 1714 : Oid resultrel = list_nth_oid(ancestors, funcctx->call_cntr);
234 :
235 1714 : SRF_RETURN_NEXT(funcctx, ObjectIdGetDatum(resultrel));
236 : }
237 :
1497 alvherre 238 GIC 1005 : SRF_RETURN_DONE(funcctx);
239 : }
|