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