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-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"
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 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
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
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  {
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 *
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
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
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(lfirst_oid(l)) == indexId)
174  {
175  list_free(idxlist);
176  return partIdx;
177  }
178  }
179 
180  return InvalidOid;
181 }
182 
183 /*
184  * map_partition_varattnos - maps varattnos of all Vars in 'expr' (that have
185  * varno 'fromrel_varno') from the attnums of 'from_rel' to the attnums of
186  * 'to_rel', each of which may be either a leaf partition or a partitioned
187  * table, but both of which must be from the same partitioning hierarchy.
188  *
189  * We need this because even though all of the same column names must be
190  * present in all relations in the hierarchy, and they must also have the
191  * same types, the attnums may be different.
192  *
193  * Note: this will work on any node tree, so really the argument and result
194  * should be declared "Node *". But a substantial majority of the callers
195  * are working on Lists, so it's less messy to do the casts internally.
196  */
197 List *
198 map_partition_varattnos(List *expr, int fromrel_varno,
199  Relation to_rel, Relation from_rel)
200 {
201  if (expr != NIL)
202  {
203  AttrMap *part_attmap;
204  bool found_whole_row;
205 
206  part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel),
207  RelationGetDescr(from_rel));
208  expr = (List *) map_variable_attnos((Node *) expr,
209  fromrel_varno, 0,
210  part_attmap,
211  RelationGetForm(to_rel)->reltype,
212  &found_whole_row);
213  /* Since we provided a to_rowtype, we may ignore found_whole_row. */
214  }
215 
216  return expr;
217 }
218 
219 /*
220  * Checks if any of the 'attnums' is a partition key attribute for rel
221  *
222  * Sets *used_in_expr if any of the 'attnums' is found to be referenced in some
223  * partition key expression. It's possible for a column to be both used
224  * directly and as part of an expression; if that happens, *used_in_expr may
225  * end up as either true or false. That's OK for current uses of this
226  * function, because *used_in_expr is only used to tailor the error message
227  * text.
228  */
229 bool
230 has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr)
231 {
233  int partnatts;
234  List *partexprs;
235  ListCell *partexprs_item;
236  int i;
237 
238  if (attnums == NULL || rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
239  return false;
240 
241  key = RelationGetPartitionKey(rel);
242  partnatts = get_partition_natts(key);
243  partexprs = get_partition_exprs(key);
244 
245  partexprs_item = list_head(partexprs);
246  for (i = 0; i < partnatts; i++)
247  {
248  AttrNumber partattno = get_partition_col_attnum(key, i);
249 
250  if (partattno != 0)
251  {
253  attnums))
254  {
255  if (used_in_expr)
256  *used_in_expr = false;
257  return true;
258  }
259  }
260  else
261  {
262  /* Arbitrary expression */
263  Node *expr = (Node *) lfirst(partexprs_item);
264  Bitmapset *expr_attrs = NULL;
265 
266  /* Find all attributes referenced */
267  pull_varattnos(expr, 1, &expr_attrs);
268  partexprs_item = lnext(partexprs, partexprs_item);
269 
270  if (bms_overlap(attnums, expr_attrs))
271  {
272  if (used_in_expr)
273  *used_in_expr = true;
274  return true;
275  }
276  }
277  }
278 
279  return false;
280 }
281 
282 /*
283  * get_default_partition_oid
284  *
285  * Given a relation OID, return the OID of the default partition, if one
286  * exists. Use get_default_oid_from_partdesc where possible, for
287  * efficiency.
288  */
289 Oid
291 {
292  HeapTuple tuple;
293  Oid defaultPartId = InvalidOid;
294 
295  tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(parentId));
296 
297  if (HeapTupleIsValid(tuple))
298  {
299  Form_pg_partitioned_table part_table_form;
300 
301  part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
302  defaultPartId = part_table_form->partdefid;
303  ReleaseSysCache(tuple);
304  }
305 
306  return defaultPartId;
307 }
308 
309 /*
310  * update_default_partition_oid
311  *
312  * Update pg_partitioned_table.partdefid with a new default partition OID.
313  */
314 void
315 update_default_partition_oid(Oid parentId, Oid defaultPartId)
316 {
317  HeapTuple tuple;
318  Relation pg_partitioned_table;
319  Form_pg_partitioned_table part_table_form;
320 
321  pg_partitioned_table = table_open(PartitionedRelationId, RowExclusiveLock);
322 
323  tuple = SearchSysCacheCopy1(PARTRELID, ObjectIdGetDatum(parentId));
324 
325  if (!HeapTupleIsValid(tuple))
326  elog(ERROR, "cache lookup failed for partition key of relation %u",
327  parentId);
328 
329  part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
330  part_table_form->partdefid = defaultPartId;
331  CatalogTupleUpdate(pg_partitioned_table, &tuple->t_self, tuple);
332 
333  heap_freetuple(tuple);
334  table_close(pg_partitioned_table, RowExclusiveLock);
335 }
336 
337 /*
338  * get_proposed_default_constraint
339  *
340  * This function returns the negation of new_part_constraints, which
341  * would be an integral part of the default partition constraints after
342  * addition of the partition to which the new_part_constraints belongs.
343  */
344 List *
345 get_proposed_default_constraint(List *new_part_constraints)
346 {
347  Expr *defPartConstraint;
348 
349  defPartConstraint = make_ands_explicit(new_part_constraints);
350 
351  /*
352  * Derive the partition constraints of default partition by negating the
353  * given partition constraints. The partition constraint never evaluates
354  * to NULL, so negating it like this is safe.
355  */
356  defPartConstraint = makeBoolExpr(NOT_EXPR,
357  list_make1(defPartConstraint),
358  -1);
359 
360  /* Simplify, to put the negated expression into canonical form */
361  defPartConstraint =
362  (Expr *) eval_const_expressions(NULL,
363  (Node *) defPartConstraint);
364  defPartConstraint = canonicalize_qual(defPartConstraint, true);
365 
366  return make_ands_implicit(defPartConstraint);
367 }
#define NIL
Definition: pg_list.h:65
Oid index_get_partition(Relation partition, Oid indexId)
Definition: partition.c:153
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:133
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:529
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
static Oid get_partition_parent_worker(Relation inhRel, Oid relid)
Definition: partition.c:73
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:321
#define RelationGetDescr(relation)
Definition: rel.h:461
#define RelationGetForm(relation)
Definition: rel.h:429
void update_default_partition_oid(Oid parentId, Oid defaultPartId)
Definition: partition.c:315
#define AccessShareLock
Definition: lockdefs.h:36
Definition: nodes.h:526
static int get_partition_natts(PartitionKey key)
Definition: partcache.h:64
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition: clauses.c:2255
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27
Node * map_variable_attnos(Node *node, int target_varno, int sublevels_up, const AttrMap *attno_map, Oid to_rowtype, bool *found_whole_row)
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:54
Form_pg_class rd_rel
Definition: rel.h:89
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:644
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:356
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
Definition: attmap.h:34
#define list_make1(x1)
Definition: pg_list.h:227
static List * get_partition_exprs(PartitionKey key)
Definition: partcache.h:70
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:448
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
Oid get_partition_parent(Oid relid)
Definition: partition.c:50
ItemPointerData t_self
Definition: htup.h:65
bool has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr)
Definition: partition.c:230
List * get_proposed_default_constraint(List *new_part_constraints)
Definition: partition.c:345
#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
Oid get_default_partition_oid(Oid parentId)
Definition: partition.c:290
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1116
static int16 get_partition_col_attnum(PartitionKey key, int col)
Definition: partcache.h:79
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1164
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
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc)
Definition: attmap.c:174
#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:4330
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:494
FormData_pg_class * Form_pg_class
Definition: pg_class.h:153
#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:214
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
Definition: pg_list.h:50
List * get_partition_ancestors(Oid relid)
Definition: partition.c:115
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:427
int16 AttrNumber
Definition: attnum.h:21
List * map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel)
Definition: partition.c:198
static void get_partition_ancestors_worker(Relation inhRel, Oid relid, List **ancestors)
Definition: partition.c:134
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define lfirst_oid(lc)
Definition: pg_list.h:192