Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * partitionfuncs.c
4 : : * Functions for accessing partition-related metadata
5 : : *
6 : : * Portions Copyright (c) 1996-2024, 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
1892 michael@paquier.xyz 34 :CBC 1196 : check_rel_can_be_partition(Oid relid)
35 : : {
36 : : char relkind;
37 : : bool relispartition;
38 : :
39 : : /* Check if relation exists */
40 [ + + ]: 1196 : if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid)))
41 : 9 : return false;
42 : :
43 : 1187 : relkind = get_rel_relkind(relid);
1870 44 : 1187 : relispartition = get_rel_relispartition(relid);
45 : :
46 : : /* Only allow relation types that can appear in partition trees. */
863 peter@eisentraut.org 47 [ + + + + : 1187 : if (!relispartition && !RELKIND_HAS_PARTITIONS(relkind))
+ + ]
1892 michael@paquier.xyz 48 : 237 : return false;
49 : :
50 : 950 : 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
1993 62 : 473 : pg_partition_tree(PG_FUNCTION_ARGS)
63 : : {
64 : : #define PG_PARTITION_TREE_COLS 4
65 : 473 : 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 [ + + ]: 473 : if (SRF_IS_FIRSTCALL())
71 : : {
72 : : MemoryContext oldcxt;
73 : : TupleDesc tupdesc;
74 : :
75 : : /* create a function context for cross-call persistence */
76 : 101 : funcctx = SRF_FIRSTCALL_INIT();
77 : :
1871 78 [ + + ]: 101 : if (!check_rel_can_be_partition(rootrelid))
79 : 18 : SRF_RETURN_DONE(funcctx);
80 : :
81 : : /* switch to memory context appropriate for multiple function calls */
1993 82 : 83 : 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 : 83 : partitions = find_all_inheritors(rootrelid, AccessShareLock, NULL);
89 : :
480 90 [ - + ]: 83 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
480 michael@paquier.xyz 91 [ # # ]:UBC 0 : elog(ERROR, "return type must be a row type");
480 michael@paquier.xyz 92 :CBC 83 : funcctx->tuple_desc = tupdesc;
93 : :
94 : : /* The only state we need is the partition list */
1735 tgl@sss.pgh.pa.us 95 : 83 : funcctx->user_fctx = (void *) partitions;
96 : :
1993 michael@paquier.xyz 97 : 83 : MemoryContextSwitchTo(oldcxt);
98 : : }
99 : :
100 : : /* stuff done on every call of the function */
101 : 455 : funcctx = SRF_PERCALL_SETUP();
1735 tgl@sss.pgh.pa.us 102 : 455 : partitions = (List *) funcctx->user_fctx;
103 : :
104 [ + + ]: 455 : if (funcctx->call_cntr < list_length(partitions))
105 : : {
106 : : Datum result;
638 peter@eisentraut.org 107 : 372 : Datum values[PG_PARTITION_TREE_COLS] = {0};
108 : 372 : bool nulls[PG_PARTITION_TREE_COLS] = {0};
109 : : HeapTuple tuple;
1993 michael@paquier.xyz 110 : 372 : Oid parentid = InvalidOid;
1735 tgl@sss.pgh.pa.us 111 : 372 : Oid relid = list_nth_oid(partitions, funcctx->call_cntr);
1993 michael@paquier.xyz 112 : 372 : char relkind = get_rel_relkind(relid);
113 : 372 : int level = 0;
1735 tgl@sss.pgh.pa.us 114 : 372 : List *ancestors = get_partition_ancestors(relid);
115 : : ListCell *lc;
116 : :
117 : : /*
118 : : * Form tuple with appropriate data.
119 : : */
120 : :
121 : : /* relid */
1993 michael@paquier.xyz 122 : 372 : values[0] = ObjectIdGetDatum(relid);
123 : :
124 : : /* parentid */
125 [ + + ]: 372 : if (ancestors != NIL)
126 : 307 : parentid = linitial_oid(ancestors);
127 [ + + ]: 372 : if (OidIsValid(parentid))
128 : 307 : values[1] = ObjectIdGetDatum(parentid);
129 : : else
130 : 65 : nulls[1] = true;
131 : :
132 : : /* isleaf */
863 peter@eisentraut.org 133 [ + + + + ]: 372 : values[2] = BoolGetDatum(!RELKIND_HAS_PARTITIONS(relkind));
134 : :
135 : : /* level */
1993 michael@paquier.xyz 136 [ + + ]: 372 : if (relid != rootrelid)
137 : : {
138 [ + - + - : 412 : foreach(lc, ancestors)
+ - ]
139 : : {
140 : 412 : level++;
141 [ + + ]: 412 : if (lfirst_oid(lc) == rootrelid)
142 : 289 : break;
143 : : }
144 : : }
145 : 372 : values[3] = Int32GetDatum(level);
146 : :
147 : 372 : tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
148 : 372 : result = HeapTupleGetDatum(tuple);
149 : 372 : SRF_RETURN_NEXT(funcctx, result);
150 : : }
151 : :
152 : : /* done when there are no more elements left */
153 : 83 : 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
1892 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 : : */
1850 180 [ + + ]: 30 : if (ancestors == NIL)
181 : 6 : PG_RETURN_OID(relid);
182 : :
1892 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 [ - + ]: 24 : 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
1868 alvherre@alvh.no-ip. 201 : 2500 : pg_partition_ancestors(PG_FUNCTION_ARGS)
202 : : {
203 : 2500 : Oid relid = PG_GETARG_OID(0);
204 : : FuncCallContext *funcctx;
205 : : List *ancestors;
206 : :
207 [ + + ]: 2500 : if (SRF_IS_FIRSTCALL())
208 : : {
209 : : MemoryContext oldcxt;
210 : :
211 : 1047 : funcctx = SRF_FIRSTCALL_INIT();
212 : :
213 [ + + ]: 1047 : if (!check_rel_can_be_partition(relid))
214 : 210 : SRF_RETURN_DONE(funcctx);
215 : :
216 : 837 : oldcxt = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
217 : :
218 : 837 : ancestors = get_partition_ancestors(relid);
219 : 837 : ancestors = lcons_oid(relid, ancestors);
220 : :
221 : : /* The only state we need is the ancestors list */
1735 tgl@sss.pgh.pa.us 222 : 837 : funcctx->user_fctx = (void *) ancestors;
223 : :
1868 alvherre@alvh.no-ip. 224 : 837 : MemoryContextSwitchTo(oldcxt);
225 : : }
226 : :
227 : 2290 : funcctx = SRF_PERCALL_SETUP();
1735 tgl@sss.pgh.pa.us 228 : 2290 : ancestors = (List *) funcctx->user_fctx;
229 : :
230 [ + + ]: 2290 : if (funcctx->call_cntr < list_length(ancestors))
231 : : {
557 drowley@postgresql.o 232 : 1453 : Oid resultrel = list_nth_oid(ancestors, funcctx->call_cntr);
233 : :
234 : 1453 : SRF_RETURN_NEXT(funcctx, ObjectIdGetDatum(resultrel));
235 : : }
236 : :
1868 alvherre@alvh.no-ip. 237 : 837 : SRF_RETURN_DONE(funcctx);
238 : : }
|