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-2023, 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  bool *detach_pending);
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  * If the partition is in the process of being detached, an error is thrown,
47  * unless even_if_detached is passed as true.
48  *
49  * Note: Because this function assumes that the relation whose OID is passed
50  * as an argument will have precisely one parent, it should only be called
51  * when it is known that the relation is a partition.
52  */
53 Oid
54 get_partition_parent(Oid relid, bool even_if_detached)
55 {
56  Relation catalogRelation;
57  Oid result;
58  bool detach_pending;
59 
60  catalogRelation = table_open(InheritsRelationId, AccessShareLock);
61 
62  result = get_partition_parent_worker(catalogRelation, relid,
63  &detach_pending);
64 
65  if (!OidIsValid(result))
66  elog(ERROR, "could not find tuple for parent of relation %u", relid);
67 
68  if (detach_pending && !even_if_detached)
69  elog(ERROR, "relation %u has no parent because it's being detached",
70  relid);
71 
72  table_close(catalogRelation, AccessShareLock);
73 
74  return result;
75 }
76 
77 /*
78  * get_partition_parent_worker
79  * Scan the pg_inherits relation to return the OID of the parent of the
80  * given relation
81  *
82  * If the partition is being detached, *detach_pending is set true (but the
83  * original parent is still returned.)
84  */
85 static Oid
86 get_partition_parent_worker(Relation inhRel, Oid relid, bool *detach_pending)
87 {
88  SysScanDesc scan;
89  ScanKeyData key[2];
90  Oid result = InvalidOid;
91  HeapTuple tuple;
92 
93  *detach_pending = false;
94 
95  ScanKeyInit(&key[0],
96  Anum_pg_inherits_inhrelid,
97  BTEqualStrategyNumber, F_OIDEQ,
98  ObjectIdGetDatum(relid));
99  ScanKeyInit(&key[1],
100  Anum_pg_inherits_inhseqno,
101  BTEqualStrategyNumber, F_INT4EQ,
102  Int32GetDatum(1));
103 
104  scan = systable_beginscan(inhRel, InheritsRelidSeqnoIndexId, true,
105  NULL, 2, key);
106  tuple = systable_getnext(scan);
107  if (HeapTupleIsValid(tuple))
108  {
110 
111  /* Let caller know of partition being detached */
112  if (form->inhdetachpending)
113  *detach_pending = true;
114  result = form->inhparent;
115  }
116 
117  systable_endscan(scan);
118 
119  return result;
120 }
121 
122 /*
123  * get_partition_ancestors
124  * Obtain ancestors of given relation
125  *
126  * Returns a list of ancestors of the given relation.
127  *
128  * Note: Because this function assumes that the relation whose OID is passed
129  * as an argument and each ancestor will have precisely one parent, it should
130  * only be called when it is known that the relation is a partition.
131  */
132 List *
134 {
135  List *result = NIL;
136  Relation inhRel;
137 
138  inhRel = table_open(InheritsRelationId, AccessShareLock);
139 
140  get_partition_ancestors_worker(inhRel, relid, &result);
141 
142  table_close(inhRel, AccessShareLock);
143 
144  return result;
145 }
146 
147 /*
148  * get_partition_ancestors_worker
149  * recursive worker for get_partition_ancestors
150  */
151 static void
152 get_partition_ancestors_worker(Relation inhRel, Oid relid, List **ancestors)
153 {
154  Oid parentOid;
155  bool detach_pending;
156 
157  /*
158  * Recursion ends at the topmost level, ie., when there's no parent; also
159  * when the partition is being detached.
160  */
161  parentOid = get_partition_parent_worker(inhRel, relid, &detach_pending);
162  if (parentOid == InvalidOid || detach_pending)
163  return;
164 
165  *ancestors = lappend_oid(*ancestors, parentOid);
166  get_partition_ancestors_worker(inhRel, parentOid, ancestors);
167 }
168 
169 /*
170  * index_get_partition
171  * Return the OID of index of the given partition that is a child
172  * of the given index, or InvalidOid if there isn't one.
173  */
174 Oid
175 index_get_partition(Relation partition, Oid indexId)
176 {
177  List *idxlist = RelationGetIndexList(partition);
178  ListCell *l;
179 
180  foreach(l, idxlist)
181  {
182  Oid partIdx = lfirst_oid(l);
183  HeapTuple tup;
184  Form_pg_class classForm;
185  bool ispartition;
186 
187  tup = SearchSysCache1(RELOID, ObjectIdGetDatum(partIdx));
188  if (!HeapTupleIsValid(tup))
189  elog(ERROR, "cache lookup failed for relation %u", partIdx);
190  classForm = (Form_pg_class) GETSTRUCT(tup);
191  ispartition = classForm->relispartition;
192  ReleaseSysCache(tup);
193  if (!ispartition)
194  continue;
195  if (get_partition_parent(partIdx, false) == indexId)
196  {
197  list_free(idxlist);
198  return partIdx;
199  }
200  }
201 
202  list_free(idxlist);
203  return InvalidOid;
204 }
205 
206 /*
207  * map_partition_varattnos - maps varattnos of all Vars in 'expr' (that have
208  * varno 'fromrel_varno') from the attnums of 'from_rel' to the attnums of
209  * 'to_rel', each of which may be either a leaf partition or a partitioned
210  * table, but both of which must be from the same partitioning hierarchy.
211  *
212  * We need this because even though all of the same column names must be
213  * present in all relations in the hierarchy, and they must also have the
214  * same types, the attnums may be different.
215  *
216  * Note: this will work on any node tree, so really the argument and result
217  * should be declared "Node *". But a substantial majority of the callers
218  * are working on Lists, so it's less messy to do the casts internally.
219  */
220 List *
221 map_partition_varattnos(List *expr, int fromrel_varno,
222  Relation to_rel, Relation from_rel)
223 {
224  if (expr != NIL)
225  {
226  AttrMap *part_attmap;
227  bool found_whole_row;
228 
229  part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel),
230  RelationGetDescr(from_rel),
231  false);
232  expr = (List *) map_variable_attnos((Node *) expr,
233  fromrel_varno, 0,
234  part_attmap,
235  RelationGetForm(to_rel)->reltype,
236  &found_whole_row);
237  /* Since we provided a to_rowtype, we may ignore found_whole_row. */
238  }
239 
240  return expr;
241 }
242 
243 /*
244  * Checks if any of the 'attnums' is a partition key attribute for rel
245  *
246  * Sets *used_in_expr if any of the 'attnums' is found to be referenced in some
247  * partition key expression. It's possible for a column to be both used
248  * directly and as part of an expression; if that happens, *used_in_expr may
249  * end up as either true or false. That's OK for current uses of this
250  * function, because *used_in_expr is only used to tailor the error message
251  * text.
252  */
253 bool
254 has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr)
255 {
257  int partnatts;
258  List *partexprs;
259  ListCell *partexprs_item;
260  int i;
261 
262  if (attnums == NULL || rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
263  return false;
264 
266  partnatts = get_partition_natts(key);
267  partexprs = get_partition_exprs(key);
268 
269  partexprs_item = list_head(partexprs);
270  for (i = 0; i < partnatts; i++)
271  {
273 
274  if (partattno != 0)
275  {
277  attnums))
278  {
279  if (used_in_expr)
280  *used_in_expr = false;
281  return true;
282  }
283  }
284  else
285  {
286  /* Arbitrary expression */
287  Node *expr = (Node *) lfirst(partexprs_item);
288  Bitmapset *expr_attrs = NULL;
289 
290  /* Find all attributes referenced */
291  pull_varattnos(expr, 1, &expr_attrs);
292  partexprs_item = lnext(partexprs, partexprs_item);
293 
294  if (bms_overlap(attnums, expr_attrs))
295  {
296  if (used_in_expr)
297  *used_in_expr = true;
298  return true;
299  }
300  }
301  }
302 
303  return false;
304 }
305 
306 /*
307  * get_default_partition_oid
308  *
309  * Given a relation OID, return the OID of the default partition, if one
310  * exists. Use get_default_oid_from_partdesc where possible, for
311  * efficiency.
312  */
313 Oid
315 {
316  HeapTuple tuple;
317  Oid defaultPartId = InvalidOid;
318 
319  tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(parentId));
320 
321  if (HeapTupleIsValid(tuple))
322  {
323  Form_pg_partitioned_table part_table_form;
324 
325  part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
326  defaultPartId = part_table_form->partdefid;
327  ReleaseSysCache(tuple);
328  }
329 
330  return defaultPartId;
331 }
332 
333 /*
334  * update_default_partition_oid
335  *
336  * Update pg_partitioned_table.partdefid with a new default partition OID.
337  */
338 void
339 update_default_partition_oid(Oid parentId, Oid defaultPartId)
340 {
341  HeapTuple tuple;
342  Relation pg_partitioned_table;
343  Form_pg_partitioned_table part_table_form;
344 
345  pg_partitioned_table = table_open(PartitionedRelationId, RowExclusiveLock);
346 
347  tuple = SearchSysCacheCopy1(PARTRELID, ObjectIdGetDatum(parentId));
348 
349  if (!HeapTupleIsValid(tuple))
350  elog(ERROR, "cache lookup failed for partition key of relation %u",
351  parentId);
352 
353  part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
354  part_table_form->partdefid = defaultPartId;
355  CatalogTupleUpdate(pg_partitioned_table, &tuple->t_self, tuple);
356 
357  heap_freetuple(tuple);
358  table_close(pg_partitioned_table, RowExclusiveLock);
359 }
360 
361 /*
362  * get_proposed_default_constraint
363  *
364  * This function returns the negation of new_part_constraints, which
365  * would be an integral part of the default partition constraints after
366  * addition of the partition to which the new_part_constraints belongs.
367  */
368 List *
369 get_proposed_default_constraint(List *new_part_constraints)
370 {
371  Expr *defPartConstraint;
372 
373  defPartConstraint = make_ands_explicit(new_part_constraints);
374 
375  /*
376  * Derive the partition constraints of default partition by negating the
377  * given partition constraints. The partition constraint never evaluates
378  * to NULL, so negating it like this is safe.
379  */
380  defPartConstraint = makeBoolExpr(NOT_EXPR,
381  list_make1(defPartConstraint),
382  -1);
383 
384  /* Simplify, to put the negated expression into canonical form */
385  defPartConstraint =
386  (Expr *) eval_const_expressions(NULL,
387  (Node *) defPartConstraint);
388  defPartConstraint = canonicalize_qual(defPartConstraint, true);
389 
390  return make_ands_implicit(defPartConstraint);
391 }
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:178
int16 AttrNumber
Definition: attnum.h:21
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:460
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:527
#define OidIsValid(objectId)
Definition: c.h:764
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition: clauses.c:2171
#define ERROR
Definition: elog.h:39
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:599
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:506
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:387
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1426
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:313
int i
Definition: isn.c:73
List * lappend_oid(List *list, Oid datum)
Definition: list.c:374
void list_free(List *list)
Definition: list.c:1545
#define AccessShareLock
Definition: lockdefs.h:36
#define RowExclusiveLock
Definition: lockdefs.h:38
List * make_ands_implicit(Expr *clause)
Definition: makefuncs.c:722
Expr * make_ands_explicit(List *andclauses)
Definition: makefuncs.c:711
Expr * makeBoolExpr(BoolExprType boolop, List *args, int location)
Definition: makefuncs.c:372
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:54
static int16 get_partition_col_attnum(PartitionKey key, int col)
Definition: partcache.h:80
static int get_partition_natts(PartitionKey key)
Definition: partcache.h:65
static List * get_partition_exprs(PartitionKey key)
Definition: partcache.h:71
List * get_partition_ancestors(Oid relid)
Definition: partition.c:133
bool has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr)
Definition: partition.c:254
List * map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel)
Definition: partition.c:221
List * get_proposed_default_constraint(List *new_part_constraints)
Definition: partition.c:369
void update_default_partition_oid(Oid parentId, Oid defaultPartId)
Definition: partition.c:339
Oid get_default_partition_oid(Oid parentId)
Definition: partition.c:314
Oid index_get_partition(Relation partition, Oid indexId)
Definition: partition.c:175
Oid get_partition_parent(Oid relid, bool even_if_detached)
Definition: partition.c:54
static Oid get_partition_parent_worker(Relation inhRel, Oid relid, bool *detach_pending)
Definition: partition.c:86
static void get_partition_ancestors_worker(Relation inhRel, Oid relid, List **ancestors)
Definition: partition.c:152
FormData_pg_class * Form_pg_class
Definition: pg_class.h:153
FormData_pg_inherits * Form_pg_inherits
Definition: pg_inherits.h:45
#define lfirst(lc)
Definition: pg_list.h:172
#define NIL
Definition: pg_list.h:68
#define list_make1(x1)
Definition: pg_list.h:212
static ListCell * list_head(const List *l)
Definition: pg_list.h:128
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:343
#define lfirst_oid(lc)
Definition: pg_list.h:174
FormData_pg_partitioned_table * Form_pg_partitioned_table
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:212
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
Expr * canonicalize_qual(Expr *qual, bool is_check)
Definition: prepqual.c:294
@ NOT_EXPR
Definition: primnodes.h:858
#define RelationGetForm(relation)
Definition: rel.h:498
#define RelationGetDescr(relation)
Definition: rel.h:530
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4740
Node * map_variable_attnos(Node *node, int target_varno, int sublevels_up, const AttrMap *attno_map, Oid to_rowtype, bool *found_whole_row)
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
#define BTEqualStrategyNumber
Definition: stratnum.h:31
Definition: attmap.h:35
ItemPointerData t_self
Definition: htup.h:65
Definition: pg_list.h:54
Definition: nodes.h:129
Form_pg_class rd_rel
Definition: rel.h:111
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:868
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:820
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:182
@ RELOID
Definition: syscache.h:89
@ PARTRELID
Definition: syscache.h:77
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
Definition: var.c:291