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