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