PostgreSQL Source Code  git master
partition.c
Go to the documentation of this file.
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"
26 #include "nodes/makefuncs.h"
27 #include "optimizer/optimizer.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
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
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  {
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 *
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);
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;
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(lfirst_oid(l)) == indexId)
175  {
176  list_free(idxlist);
177  return partIdx;
178  }
179  }
180 
181  return InvalidOid;
182 }
183 
184 /*
185  * map_partition_varattnos - maps varattno of any Vars in expr from the
186  * attno's of 'from_rel' to the attno's of 'to_rel' partition, each of which
187  * may be either a leaf partition or a partitioned table, but both of which
188  * must be from the same partitioning hierarchy.
189  *
190  * Even though all of the same column names must be present in all relations
191  * in the hierarchy, and they must also have the same types, the attnos may
192  * be different.
193  *
194  * If found_whole_row is not NULL, *found_whole_row returns whether a
195  * whole-row variable was found in the input expression.
196  *
197  * Note: this will work on any node tree, so really the argument and result
198  * should be declared "Node *". But a substantial majority of the callers
199  * are working on Lists, so it's less messy to do the casts internally.
200  */
201 List *
202 map_partition_varattnos(List *expr, int fromrel_varno,
203  Relation to_rel, Relation from_rel,
204  bool *found_whole_row)
205 {
206  bool my_found_whole_row = false;
207 
208  if (expr != NIL)
209  {
210  AttrNumber *part_attnos;
211 
212  part_attnos = convert_tuples_by_name_map(RelationGetDescr(to_rel),
213  RelationGetDescr(from_rel));
214  expr = (List *) map_variable_attnos((Node *) expr,
215  fromrel_varno, 0,
216  part_attnos,
217  RelationGetDescr(from_rel)->natts,
218  RelationGetForm(to_rel)->reltype,
219  &my_found_whole_row);
220  }
221 
222  if (found_whole_row)
223  *found_whole_row = my_found_whole_row;
224 
225  return expr;
226 }
227 
228 /*
229  * Checks if any of the 'attnums' is a partition key attribute for rel
230  *
231  * Sets *used_in_expr if any of the 'attnums' is found to be referenced in some
232  * partition key expression. It's possible for a column to be both used
233  * directly and as part of an expression; if that happens, *used_in_expr may
234  * end up as either true or false. That's OK for current uses of this
235  * function, because *used_in_expr is only used to tailor the error message
236  * text.
237  */
238 bool
239 has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr)
240 {
242  int partnatts;
243  List *partexprs;
244  ListCell *partexprs_item;
245  int i;
246 
247  if (attnums == NULL || rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
248  return false;
249 
250  key = RelationGetPartitionKey(rel);
251  partnatts = get_partition_natts(key);
252  partexprs = get_partition_exprs(key);
253 
254  partexprs_item = list_head(partexprs);
255  for (i = 0; i < partnatts; i++)
256  {
257  AttrNumber partattno = get_partition_col_attnum(key, i);
258 
259  if (partattno != 0)
260  {
262  attnums))
263  {
264  if (used_in_expr)
265  *used_in_expr = false;
266  return true;
267  }
268  }
269  else
270  {
271  /* Arbitrary expression */
272  Node *expr = (Node *) lfirst(partexprs_item);
273  Bitmapset *expr_attrs = NULL;
274 
275  /* Find all attributes referenced */
276  pull_varattnos(expr, 1, &expr_attrs);
277  partexprs_item = lnext(partexprs, partexprs_item);
278 
279  if (bms_overlap(attnums, expr_attrs))
280  {
281  if (used_in_expr)
282  *used_in_expr = true;
283  return true;
284  }
285  }
286  }
287 
288  return false;
289 }
290 
291 /*
292  * get_default_partition_oid
293  *
294  * Given a relation OID, return the OID of the default partition, if one
295  * exists. Use get_default_oid_from_partdesc where possible, for
296  * efficiency.
297  */
298 Oid
300 {
301  HeapTuple tuple;
302  Oid defaultPartId = InvalidOid;
303 
304  tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(parentId));
305 
306  if (HeapTupleIsValid(tuple))
307  {
308  Form_pg_partitioned_table part_table_form;
309 
310  part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
311  defaultPartId = part_table_form->partdefid;
312  ReleaseSysCache(tuple);
313  }
314 
315  return defaultPartId;
316 }
317 
318 /*
319  * update_default_partition_oid
320  *
321  * Update pg_partitioned_table.partdefid with a new default partition OID.
322  */
323 void
324 update_default_partition_oid(Oid parentId, Oid defaultPartId)
325 {
326  HeapTuple tuple;
327  Relation pg_partitioned_table;
328  Form_pg_partitioned_table part_table_form;
329 
330  pg_partitioned_table = table_open(PartitionedRelationId, RowExclusiveLock);
331 
332  tuple = SearchSysCacheCopy1(PARTRELID, ObjectIdGetDatum(parentId));
333 
334  if (!HeapTupleIsValid(tuple))
335  elog(ERROR, "cache lookup failed for partition key of relation %u",
336  parentId);
337 
338  part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
339  part_table_form->partdefid = defaultPartId;
340  CatalogTupleUpdate(pg_partitioned_table, &tuple->t_self, tuple);
341 
342  heap_freetuple(tuple);
343  table_close(pg_partitioned_table, RowExclusiveLock);
344 }
345 
346 /*
347  * get_proposed_default_constraint
348  *
349  * This function returns the negation of new_part_constraints, which
350  * would be an integral part of the default partition constraints after
351  * addition of the partition to which the new_part_constraints belongs.
352  */
353 List *
354 get_proposed_default_constraint(List *new_part_constraints)
355 {
356  Expr *defPartConstraint;
357 
358  defPartConstraint = make_ands_explicit(new_part_constraints);
359 
360  /*
361  * Derive the partition constraints of default partition by negating the
362  * given partition constraints. The partition constraint never evaluates
363  * to NULL, so negating it like this is safe.
364  */
365  defPartConstraint = makeBoolExpr(NOT_EXPR,
366  list_make1(defPartConstraint),
367  -1);
368 
369  /* Simplify, to put the negated expression into canonical form */
370  defPartConstraint =
371  (Expr *) eval_const_expressions(NULL,
372  (Node *) defPartConstraint);
373  defPartConstraint = canonicalize_qual(defPartConstraint, true);
374 
375  return make_ands_implicit(defPartConstraint);
376 }
#define NIL
Definition: pg_list.h:65
Oid index_get_partition(Relation partition, Oid indexId)
Definition: partition.c:154
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:133
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:525
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
static Oid get_partition_parent_worker(Relation inhRel, Oid relid)
Definition: partition.c:74
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:321
#define RelationGetDescr(relation)
Definition: rel.h:445
#define RelationGetForm(relation)
Definition: rel.h:413
void update_default_partition_oid(Oid parentId, Oid defaultPartId)
Definition: partition.c:324
#define AccessShareLock
Definition: lockdefs.h:36
Definition: nodes.h:525
static int get_partition_natts(PartitionKey key)
Definition: partcache.h:63
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition: clauses.c:2253
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27
Form_pg_class rd_rel
Definition: rel.h:83
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
unsigned int Oid
Definition: postgres_ext.h:31
FormData_pg_partitioned_table * Form_pg_partitioned_table
List * lappend_oid(List *list, Oid datum)
Definition: list.c:358
#define OidIsValid(objectId)
Definition: c.h:638
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:352
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
Definition: var.c:219
Expr * makeBoolExpr(BoolExprType boolop, List *args, int location)
Definition: makefuncs.c:367
#define list_make1(x1)
Definition: pg_list.h:227
static List * get_partition_exprs(PartitionKey key)
Definition: partcache.h:69
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:444
List * map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel, bool *found_whole_row)
Definition: partition.c:202
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
Oid get_partition_parent(Oid relid)
Definition: partition.c:51
ItemPointerData t_self
Definition: htup.h:65
bool has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr)
Definition: partition.c:239
List * get_proposed_default_constraint(List *new_part_constraints)
Definition: partition.c:354
#define RowExclusiveLock
Definition: lockdefs.h:38
Expr * canonicalize_qual(Expr *qual, bool is_check)
Definition: prepqual.c:292
static ListCell * list_head(const List *l)
Definition: pg_list.h:125
Node * map_variable_attnos(Node *node, int target_varno, int sublevels_up, const AttrNumber *attno_map, int map_length, Oid to_rowtype, bool *found_whole_row)
Oid get_default_partition_oid(Oid parentId)
Definition: partition.c:299
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1124
static int16 get_partition_col_attnum(PartitionKey key, int col)
Definition: partcache.h:78
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1172
Expr * make_ands_explicit(List *andclauses)
Definition: makefuncs.c:705
List * make_ands_implicit(Expr *clause)
Definition: makefuncs.c:716
#define InvalidOid
Definition: postgres_ext.h:36
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define lfirst(lc)
Definition: pg_list.h:190
#define InheritsRelidSeqnoIndexId
Definition: indexing.h:171
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:224
FormData_pg_inherits * Form_pg_inherits
Definition: pg_inherits.h:44
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4348
#define RelationGetPartitionKey(relation)
Definition: rel.h:600
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:494
FormData_pg_class * Form_pg_class
Definition: pg_class.h:150
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:174
#define Int32GetDatum(X)
Definition: postgres.h:479
void list_free(List *list)
Definition: list.c:1377
#define elog(elevel,...)
Definition: elog.h:226
int i
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
AttrNumber * convert_tuples_by_name_map(TupleDesc indesc, TupleDesc outdesc)
Definition: tupconvert.c:245
Definition: pg_list.h:50
List * get_partition_ancestors(Oid relid)
Definition: partition.c:116
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:427
int16 AttrNumber
Definition: attnum.h:21
static void get_partition_ancestors_worker(Relation inhRel, Oid relid, List **ancestors)
Definition: partition.c:135
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define lfirst_oid(lc)
Definition: pg_list.h:192