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