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