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