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