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