Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * partition.c
4 : * Partitioning related data structures and functions.
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/catalog/partition.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : #include "postgres.h"
16 :
17 : #include "access/attmap.h"
18 : #include "access/genam.h"
19 : #include "access/htup_details.h"
20 : #include "access/sysattr.h"
21 : #include "access/table.h"
22 : #include "catalog/indexing.h"
23 : #include "catalog/partition.h"
24 : #include "catalog/pg_inherits.h"
25 : #include "catalog/pg_partitioned_table.h"
26 : #include "nodes/makefuncs.h"
27 : #include "optimizer/optimizer.h"
28 : #include "rewrite/rewriteManip.h"
29 : #include "utils/fmgroids.h"
30 : #include "utils/partcache.h"
31 : #include "utils/rel.h"
32 : #include "utils/syscache.h"
33 :
34 : static Oid get_partition_parent_worker(Relation inhRel, Oid relid,
35 : bool *detach_pending);
36 : static void get_partition_ancestors_worker(Relation inhRel, Oid relid,
37 : List **ancestors);
38 :
39 : /*
40 : * get_partition_parent
41 : * Obtain direct parent of given relation
42 : *
43 : * Returns inheritance parent of a partition by scanning pg_inherits
44 : *
45 : * If the partition is in the process of being detached, an error is thrown,
46 : * unless even_if_detached is passed as true.
47 : *
48 : * Note: Because this function assumes that the relation whose OID is passed
49 : * as an argument will have precisely one parent, it should only be called
50 : * when it is known that the relation is a partition.
51 : */
52 : Oid
53 12086 : get_partition_parent(Oid relid, bool even_if_detached)
54 : {
55 : Relation catalogRelation;
56 : Oid result;
57 : bool detach_pending;
58 :
59 12086 : catalogRelation = table_open(InheritsRelationId, AccessShareLock);
60 :
61 12086 : result = get_partition_parent_worker(catalogRelation, relid,
62 : &detach_pending);
63 :
64 12086 : if (!OidIsValid(result))
65 0 : elog(ERROR, "could not find tuple for parent of relation %u", relid);
66 :
67 12086 : if (detach_pending && !even_if_detached)
68 0 : elog(ERROR, "relation %u has no parent because it's being detached",
69 : relid);
70 :
71 12086 : table_close(catalogRelation, AccessShareLock);
72 :
73 12086 : return result;
74 : }
75 :
76 : /*
77 : * get_partition_parent_worker
78 : * Scan the pg_inherits relation to return the OID of the parent of the
79 : * given relation
80 : *
81 : * If the partition is being detached, *detach_pending is set true (but the
82 : * original parent is still returned.)
83 : */
84 : static Oid
85 28286 : get_partition_parent_worker(Relation inhRel, Oid relid, bool *detach_pending)
86 : {
87 : SysScanDesc scan;
88 : ScanKeyData key[2];
89 28286 : Oid result = InvalidOid;
90 : HeapTuple tuple;
91 :
92 28286 : *detach_pending = false;
93 :
94 28286 : ScanKeyInit(&key[0],
95 : Anum_pg_inherits_inhrelid,
96 : BTEqualStrategyNumber, F_OIDEQ,
97 : ObjectIdGetDatum(relid));
98 28286 : ScanKeyInit(&key[1],
99 : Anum_pg_inherits_inhseqno,
100 : BTEqualStrategyNumber, F_INT4EQ,
101 : Int32GetDatum(1));
102 :
103 28286 : scan = systable_beginscan(inhRel, InheritsRelidSeqnoIndexId, true,
104 : NULL, 2, key);
105 28286 : tuple = systable_getnext(scan);
106 28286 : if (HeapTupleIsValid(tuple))
107 : {
108 19726 : Form_pg_inherits form = (Form_pg_inherits) GETSTRUCT(tuple);
109 :
110 : /* Let caller know of partition being detached */
111 19726 : if (form->inhdetachpending)
112 74 : *detach_pending = true;
113 19726 : result = form->inhparent;
114 : }
115 :
116 28286 : systable_endscan(scan);
117 :
118 28286 : return result;
119 : }
120 :
121 : /*
122 : * get_partition_ancestors
123 : * Obtain ancestors of given relation
124 : *
125 : * Returns a list of ancestors of the given relation. The list is ordered:
126 : * The first element is the immediate parent and the last one is the topmost
127 : * parent in the partition hierarchy.
128 : *
129 : * Note: Because this function assumes that the relation whose OID is passed
130 : * as an argument and each ancestor will have precisely one parent, it should
131 : * only be called when it is known that the relation is a partition.
132 : */
133 : List *
134 8562 : get_partition_ancestors(Oid relid)
135 : {
136 8562 : List *result = NIL;
137 : Relation inhRel;
138 :
139 8562 : inhRel = table_open(InheritsRelationId, AccessShareLock);
140 :
141 8562 : get_partition_ancestors_worker(inhRel, relid, &result);
142 :
143 8562 : table_close(inhRel, AccessShareLock);
144 :
145 8562 : return result;
146 : }
147 :
148 : /*
149 : * get_partition_ancestors_worker
150 : * recursive worker for get_partition_ancestors
151 : */
152 : static void
153 16200 : get_partition_ancestors_worker(Relation inhRel, Oid relid, List **ancestors)
154 : {
155 : Oid parentOid;
156 : bool detach_pending;
157 :
158 : /*
159 : * Recursion ends at the topmost level, ie., when there's no parent; also
160 : * when the partition is being detached.
161 : */
162 16200 : parentOid = get_partition_parent_worker(inhRel, relid, &detach_pending);
163 16200 : if (parentOid == InvalidOid || detach_pending)
164 8562 : return;
165 :
166 7638 : *ancestors = lappend_oid(*ancestors, parentOid);
167 7638 : get_partition_ancestors_worker(inhRel, parentOid, ancestors);
168 : }
169 :
170 : /*
171 : * index_get_partition
172 : * Return the OID of index of the given partition that is a child
173 : * of the given index, or InvalidOid if there isn't one.
174 : */
175 : Oid
176 1226 : index_get_partition(Relation partition, Oid indexId)
177 : {
178 1226 : List *idxlist = RelationGetIndexList(partition);
179 : ListCell *l;
180 :
181 1782 : foreach(l, idxlist)
182 : {
183 1432 : Oid partIdx = lfirst_oid(l);
184 : HeapTuple tup;
185 : Form_pg_class classForm;
186 : bool ispartition;
187 :
188 1432 : tup = SearchSysCache1(RELOID, ObjectIdGetDatum(partIdx));
189 1432 : if (!HeapTupleIsValid(tup))
190 0 : elog(ERROR, "cache lookup failed for relation %u", partIdx);
191 1432 : classForm = (Form_pg_class) GETSTRUCT(tup);
192 1432 : ispartition = classForm->relispartition;
193 1432 : ReleaseSysCache(tup);
194 1432 : if (!ispartition)
195 526 : continue;
196 906 : if (get_partition_parent(partIdx, false) == indexId)
197 : {
198 876 : list_free(idxlist);
199 876 : return partIdx;
200 : }
201 : }
202 :
203 350 : list_free(idxlist);
204 350 : return InvalidOid;
205 : }
206 :
207 : /*
208 : * map_partition_varattnos - maps varattnos of all Vars in 'expr' (that have
209 : * varno 'fromrel_varno') from the attnums of 'from_rel' to the attnums of
210 : * 'to_rel', each of which may be either a leaf partition or a partitioned
211 : * table, but both of which must be from the same partitioning hierarchy.
212 : *
213 : * We need this because even though all of the same column names must be
214 : * present in all relations in the hierarchy, and they must also have the
215 : * same types, the attnums may be different.
216 : *
217 : * Note: this will work on any node tree, so really the argument and result
218 : * should be declared "Node *". But a substantial majority of the callers
219 : * are working on Lists, so it's less messy to do the casts internally.
220 : */
221 : List *
222 7690 : map_partition_varattnos(List *expr, int fromrel_varno,
223 : Relation to_rel, Relation from_rel)
224 : {
225 7690 : if (expr != NIL)
226 : {
227 : AttrMap *part_attmap;
228 : bool found_whole_row;
229 :
230 6384 : part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel),
231 : RelationGetDescr(from_rel),
232 : false);
233 6384 : expr = (List *) map_variable_attnos((Node *) expr,
234 : fromrel_varno, 0,
235 : part_attmap,
236 6384 : RelationGetForm(to_rel)->reltype,
237 : &found_whole_row);
238 : /* Since we provided a to_rowtype, we may ignore found_whole_row. */
239 : }
240 :
241 7690 : return expr;
242 : }
243 :
244 : /*
245 : * Checks if any of the 'attnums' is a partition key attribute for rel
246 : *
247 : * Sets *used_in_expr if any of the 'attnums' is found to be referenced in some
248 : * partition key expression. It's possible for a column to be both used
249 : * directly and as part of an expression; if that happens, *used_in_expr may
250 : * end up as either true or false. That's OK for current uses of this
251 : * function, because *used_in_expr is only used to tailor the error message
252 : * text.
253 : */
254 : bool
255 19392 : has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr)
256 : {
257 : PartitionKey key;
258 : int partnatts;
259 : List *partexprs;
260 : ListCell *partexprs_item;
261 : int i;
262 :
263 19392 : if (attnums == NULL || rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
264 17114 : return false;
265 :
266 2278 : key = RelationGetPartitionKey(rel);
267 2278 : partnatts = get_partition_natts(key);
268 2278 : partexprs = get_partition_exprs(key);
269 :
270 2278 : partexprs_item = list_head(partexprs);
271 3626 : for (i = 0; i < partnatts; i++)
272 : {
273 2436 : AttrNumber partattno = get_partition_col_attnum(key, i);
274 :
275 2436 : if (partattno != 0)
276 : {
277 2326 : if (bms_is_member(partattno - FirstLowInvalidHeapAttributeNumber,
278 : attnums))
279 : {
280 1052 : if (used_in_expr)
281 30 : *used_in_expr = false;
282 1052 : return true;
283 : }
284 : }
285 : else
286 : {
287 : /* Arbitrary expression */
288 110 : Node *expr = (Node *) lfirst(partexprs_item);
289 110 : Bitmapset *expr_attrs = NULL;
290 :
291 : /* Find all attributes referenced */
292 110 : pull_varattnos(expr, 1, &expr_attrs);
293 110 : partexprs_item = lnext(partexprs, partexprs_item);
294 :
295 110 : if (bms_overlap(attnums, expr_attrs))
296 : {
297 36 : if (used_in_expr)
298 18 : *used_in_expr = true;
299 36 : return true;
300 : }
301 : }
302 : }
303 :
304 1190 : return false;
305 : }
306 :
307 : /*
308 : * get_default_partition_oid
309 : *
310 : * Given a relation OID, return the OID of the default partition, if one
311 : * exists. Use get_default_oid_from_partdesc where possible, for
312 : * efficiency.
313 : */
314 : Oid
315 8464 : get_default_partition_oid(Oid parentId)
316 : {
317 : HeapTuple tuple;
318 8464 : Oid defaultPartId = InvalidOid;
319 :
320 8464 : tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(parentId));
321 :
322 8464 : if (HeapTupleIsValid(tuple))
323 : {
324 : Form_pg_partitioned_table part_table_form;
325 :
326 8464 : part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
327 8464 : defaultPartId = part_table_form->partdefid;
328 8464 : ReleaseSysCache(tuple);
329 : }
330 :
331 8464 : return defaultPartId;
332 : }
333 :
334 : /*
335 : * update_default_partition_oid
336 : *
337 : * Update pg_partitioned_table.partdefid with a new default partition OID.
338 : */
339 : void
340 930 : update_default_partition_oid(Oid parentId, Oid defaultPartId)
341 : {
342 : HeapTuple tuple;
343 : Relation pg_partitioned_table;
344 : Form_pg_partitioned_table part_table_form;
345 :
346 930 : pg_partitioned_table = table_open(PartitionedRelationId, RowExclusiveLock);
347 :
348 930 : tuple = SearchSysCacheCopy1(PARTRELID, ObjectIdGetDatum(parentId));
349 :
350 930 : if (!HeapTupleIsValid(tuple))
351 0 : elog(ERROR, "cache lookup failed for partition key of relation %u",
352 : parentId);
353 :
354 930 : part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
355 930 : part_table_form->partdefid = defaultPartId;
356 930 : CatalogTupleUpdate(pg_partitioned_table, &tuple->t_self, tuple);
357 :
358 930 : heap_freetuple(tuple);
359 930 : table_close(pg_partitioned_table, RowExclusiveLock);
360 930 : }
361 :
362 : /*
363 : * get_proposed_default_constraint
364 : *
365 : * This function returns the negation of new_part_constraints, which
366 : * would be an integral part of the default partition constraints after
367 : * addition of the partition to which the new_part_constraints belongs.
368 : */
369 : List *
370 494 : get_proposed_default_constraint(List *new_part_constraints)
371 : {
372 : Expr *defPartConstraint;
373 :
374 494 : defPartConstraint = make_ands_explicit(new_part_constraints);
375 :
376 : /*
377 : * Derive the partition constraints of default partition by negating the
378 : * given partition constraints. The partition constraint never evaluates
379 : * to NULL, so negating it like this is safe.
380 : */
381 494 : defPartConstraint = makeBoolExpr(NOT_EXPR,
382 494 : list_make1(defPartConstraint),
383 : -1);
384 :
385 : /* Simplify, to put the negated expression into canonical form */
386 : defPartConstraint =
387 494 : (Expr *) eval_const_expressions(NULL,
388 : (Node *) defPartConstraint);
389 494 : defPartConstraint = canonicalize_qual(defPartConstraint, true);
390 :
391 494 : return make_ands_implicit(defPartConstraint);
392 : }
|