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