PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pg_inherits.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * pg_inherits.c
4 * routines to support manipulation of the pg_inherits relation
5 *
6 * Note: currently, this module mostly contains inquiry functions; actual
7 * creation and deletion of pg_inherits entries is mostly done in tablecmds.c.
8 * Perhaps someday that code should be moved here, but it'd have to be
9 * disentangled from other stuff such as pg_depend updates.
10 *
11 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
12 * Portions Copyright (c) 1994, Regents of the University of California
13 *
14 *
15 * IDENTIFICATION
16 * src/backend/catalog/pg_inherits.c
17 *
18 *-------------------------------------------------------------------------
19 */
20#include "postgres.h"
21
22#include "access/genam.h"
23#include "access/htup_details.h"
24#include "access/table.h"
25#include "catalog/indexing.h"
26#include "catalog/pg_inherits.h"
27#include "parser/parse_type.h"
28#include "storage/lmgr.h"
29#include "utils/builtins.h"
30#include "utils/fmgroids.h"
31#include "utils/hsearch.h"
32#include "utils/snapmgr.h"
33#include "utils/syscache.h"
34
35/*
36 * Entry of a hash table used in find_all_inheritors. See below.
37 */
38typedef struct SeenRelsEntry
39{
40 Oid rel_id; /* relation oid */
41 int list_index; /* its position in output list(s) */
43
44/*
45 * find_inheritance_children
46 *
47 * Returns a list containing the OIDs of all relations which
48 * inherit *directly* from the relation with OID 'parentrelId'.
49 *
50 * The specified lock type is acquired on each child relation (but not on the
51 * given rel; caller should already have locked it). If lockmode is NoLock
52 * then no locks are acquired, but caller must beware of race conditions
53 * against possible DROPs of child relations.
54 *
55 * Partitions marked as being detached are omitted; see
56 * find_inheritance_children_extended for details.
57 */
58List *
64
65/*
66 * find_inheritance_children_extended
67 *
68 * As find_inheritance_children, with more options regarding detached
69 * partitions.
70 *
71 * If a partition's pg_inherits row is marked "detach pending",
72 * *detached_exist (if not null) is set true.
73 *
74 * If omit_detached is true and there is an active snapshot (not the same as
75 * the catalog snapshot used to scan pg_inherits!) and a pg_inherits tuple
76 * marked "detach pending" is visible to that snapshot, then that partition is
77 * omitted from the output list. This makes partitions invisible depending on
78 * whether the transaction that marked those partitions as detached appears
79 * committed to the active snapshot. In addition, *detached_xmin (if not null)
80 * is set to the xmin of the row of the detached partition.
81 */
82List *
84 LOCKMODE lockmode, bool *detached_exist,
86{
87 List *list = NIL;
88 Relation relation;
89 SysScanDesc scan;
90 ScanKeyData key[1];
92 Oid inhrelid;
93 Oid *oidarr;
94 int maxoids,
95 numoids,
96 i;
97
98 /*
99 * Can skip the scan if pg_class shows the relation has never had a
100 * subclass.
101 */
103 return NIL;
104
105 /*
106 * Scan pg_inherits and build a working array of subclass OIDs.
107 */
108 maxoids = 32;
109 oidarr = (Oid *) palloc(maxoids * sizeof(Oid));
110 numoids = 0;
111
113
114 ScanKeyInit(&key[0],
118
119 scan = systable_beginscan(relation, InheritsParentIndexId, true,
120 NULL, 1, key);
121
122 while ((inheritsTuple = systable_getnext(scan)) != NULL)
123 {
124 /*
125 * Cope with partitions concurrently being detached. When we see a
126 * partition marked "detach pending", we omit it from the returned set
127 * of visible partitions if caller requested that and the tuple's xmin
128 * does not appear in progress to the active snapshot. (If there's no
129 * active snapshot set, that means we're not running a user query, so
130 * it's OK to always include detached partitions in that case; if the
131 * xmin is still running to the active snapshot, then the partition
132 * has not been detached yet and so we include it.)
133 *
134 * The reason for this hack is that we want to avoid seeing the
135 * partition as alive in RI queries during REPEATABLE READ or
136 * SERIALIZABLE transactions: such queries use a different snapshot
137 * than the one used by regular (user) queries.
138 */
140 {
141 if (detached_exist)
142 *detached_exist = true;
143
144 if (omit_detached && ActiveSnapshotSet())
145 {
146 TransactionId xmin;
148
151
152 if (!XidInMVCCSnapshot(xmin, snap))
153 {
154 if (detached_xmin)
155 {
156 /*
157 * Two detached partitions should not occur (see
158 * checks in MarkInheritDetached), but if they do,
159 * track the newer of the two. Make sure to warn the
160 * user, so that they can clean up. Since this is
161 * just a cross-check against potentially corrupt
162 * catalogs, we don't make it a full-fledged error
163 * message.
164 */
166 {
167 elog(WARNING, "more than one partition pending detach found for table with OID %u",
170 *detached_xmin = xmin;
171 }
172 else
173 *detached_xmin = xmin;
174 }
175
176 /* Don't add the partition to the output list */
177 continue;
178 }
179 }
180 }
181
182 inhrelid = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhrelid;
183 if (numoids >= maxoids)
184 {
185 maxoids *= 2;
186 oidarr = (Oid *) repalloc(oidarr, maxoids * sizeof(Oid));
187 }
188 oidarr[numoids++] = inhrelid;
189 }
190
191 systable_endscan(scan);
192
193 table_close(relation, AccessShareLock);
194
195 /*
196 * If we found more than one child, sort them by OID. This ensures
197 * reasonably consistent behavior regardless of the vagaries of an
198 * indexscan. This is important since we need to be sure all backends
199 * lock children in the same order to avoid needless deadlocks.
200 */
201 if (numoids > 1)
202 qsort(oidarr, numoids, sizeof(Oid), oid_cmp);
203
204 /*
205 * Acquire locks and build the result list.
206 */
207 for (i = 0; i < numoids; i++)
208 {
209 inhrelid = oidarr[i];
210
211 if (lockmode != NoLock)
212 {
213 /* Get the lock to synchronize against concurrent drop */
214 LockRelationOid(inhrelid, lockmode);
215
216 /*
217 * Now that we have the lock, double-check to see if the relation
218 * really exists or not. If not, assume it was dropped while we
219 * waited to acquire lock, and ignore it.
220 */
222 {
223 /* Release useless lock */
224 UnlockRelationOid(inhrelid, lockmode);
225 /* And ignore this relation */
226 continue;
227 }
228 }
229
230 list = lappend_oid(list, inhrelid);
231 }
232
233 pfree(oidarr);
234
235 return list;
236}
237
238
239/*
240 * find_all_inheritors -
241 * Returns a list of relation OIDs including the given rel plus
242 * all relations that inherit from it, directly or indirectly.
243 * Optionally, it also returns the number of parents found for
244 * each such relation within the inheritance tree rooted at the
245 * given rel.
246 *
247 * The specified lock type is acquired on all child relations (but not on the
248 * given rel; caller should already have locked it). If lockmode is NoLock
249 * then no locks are acquired, but caller must beware of race conditions
250 * against possible DROPs of child relations.
251 *
252 * NB - No current callers of this routine are interested in children being
253 * concurrently detached, so there's no provision to include them.
254 */
255List *
257{
258 /* hash table for O(1) rel_oid -> rel_numparents cell lookup */
260 HASHCTL ctl;
263 ListCell *l;
264
265 ctl.keysize = sizeof(Oid);
266 ctl.entrysize = sizeof(SeenRelsEntry);
268
269 seen_rels = hash_create("find_all_inheritors temporary table",
270 32, /* start small and extend */
271 &ctl,
273
274 /*
275 * We build a list starting with the given rel and adding all direct and
276 * indirect children. We can use a single list as both the record of
277 * already-found rels and the agenda of rels yet to be scanned for more
278 * children. This is a bit tricky but works because the foreach() macro
279 * doesn't fetch the next list element until the bottom of the loop. Note
280 * that we can't keep pointers into the output lists; but an index is
281 * sufficient.
282 */
285
286 foreach(l, rels_list)
287 {
290 ListCell *lc;
291
292 /* Get the direct children of this rel */
294
295 /*
296 * Add to the queue only those children not already seen. This avoids
297 * making duplicate entries in case of multiple inheritance paths from
298 * the same parent. (It'll also keep us from getting into an infinite
299 * loop, though theoretically there can't be any cycles in the
300 * inheritance graph anyway.)
301 */
302 foreach(lc, currentchildren)
303 {
305 bool found;
307
309 if (found)
310 {
311 /* if the rel is already there, bump number-of-parents counter */
313
315 hash_entry->list_index);
317 }
318 else
319 {
320 /* if it's not there, add it. expect 1 parent, initially. */
321 hash_entry->list_index = list_length(rels_list);
324 }
325 }
326 }
327
328 if (numparents)
330 else
332
334
335 return rels_list;
336}
337
338
339/*
340 * has_subclass - does this relation have any children?
341 *
342 * In the current implementation, has_subclass returns whether a
343 * particular class *might* have a subclass. It will not return the
344 * correct result if a class had a subclass which was later dropped.
345 * This is because relhassubclass in pg_class is not updated immediately
346 * when a subclass is dropped, primarily because of concurrency concerns.
347 *
348 * Currently has_subclass is only used as an efficiency hack to skip
349 * unnecessary inheritance searches, so this is OK. Note that ANALYZE
350 * on a childless table will clean up the obsolete relhassubclass flag.
351 *
352 * Although this doesn't actually touch pg_inherits, it seems reasonable
353 * to keep it here since it's normally used with the other routines here.
354 */
355bool
357{
358 HeapTuple tuple;
359 bool result;
360
362 if (!HeapTupleIsValid(tuple))
363 elog(ERROR, "cache lookup failed for relation %u", relationId);
364
366 ReleaseSysCache(tuple);
367 return result;
368}
369
370/*
371 * has_superclass - does this relation inherit from another?
372 *
373 * Unlike has_subclass, this can be relied on to give an accurate answer.
374 * However, the caller must hold a lock on the given relation so that it
375 * can't be concurrently added to or removed from an inheritance hierarchy.
376 */
377bool
396
397/*
398 * Given two type OIDs, determine whether the first is a complex type
399 * (class type) that inherits from the second.
400 *
401 * This essentially asks whether the first type is guaranteed to be coercible
402 * to the second. Therefore, we allow the first type to be a domain over a
403 * complex type that inherits from the second; that creates no difficulties.
404 * But the second type cannot be a domain.
405 */
406bool
408{
409 bool result = false;
413 List *visited,
414 *queue;
416
417 /* We need to work with the associated relation OIDs */
420 return false; /* not a complex type or domain over one */
423 return false; /* not a complex type */
424
425 /* No point in searching if the superclass has no subclasses */
427 return false;
428
429 /*
430 * Begin the search at the relation itself, so add its relid to the queue.
431 */
433 visited = NIL;
434
436
437 /*
438 * Use queue to do a breadth-first traversal of the inheritance graph from
439 * the relid supplied up to the root. Notice that we append to the queue
440 * inside the loop --- this is okay because the foreach() macro doesn't
441 * advance queue_item until the next loop iteration begins.
442 */
443 foreach(queue_item, queue)
444 {
449
450 /*
451 * If we've seen this relid already, skip it. This avoids extra work
452 * in multiple-inheritance scenarios, and also protects us from an
453 * infinite loop in case there is a cycle in pg_inherits (though
454 * theoretically that shouldn't happen).
455 */
457 continue;
458
459 /*
460 * Okay, this is a not-yet-seen relid. Add it to the list of
461 * already-visited OIDs, then find all the types this relid inherits
462 * from and add them to the queue.
463 */
465
470
472 NULL, 1, &skey);
473
474 while ((inhtup = systable_getnext(inhscan)) != NULL)
475 {
477 Oid inhparent = inh->inhparent;
478
479 /* If this is the target superclass, we're done */
480 if (inhparent == superclassRelid)
481 {
482 result = true;
483 break;
484 }
485
486 /* Else add to queue */
487 queue = lappend_oid(queue, inhparent);
488 }
489
491
492 if (result)
493 break;
494 }
495
496 /* clean up ... */
498
500 list_free(queue);
501
502 return result;
503}
504
505/*
506 * Create a single pg_inherits row with the given data
507 */
508void
536
537/*
538 * DeleteInheritsTuple
539 *
540 * Delete pg_inherits tuples with the given inhrelid. inhparent may be given
541 * as InvalidOid, in which case all tuples matching inhrelid are deleted;
542 * otherwise only delete tuples with the specified inhparent.
543 *
544 * expect_detach_pending is the expected state of the inhdetachpending flag.
545 * If the catalog row does not match that state, an error is raised.
546 *
547 * childname is the partition name, if a table; pass NULL for regular
548 * inheritance or when working with other relation kinds.
549 *
550 * Returns whether at least one row was deleted.
551 */
552bool
554 const char *childname)
555{
556 bool found = false;
558 ScanKeyData key;
559 SysScanDesc scan;
561
562 /*
563 * Find pg_inherits entries by inhrelid.
564 */
566 ScanKeyInit(&key,
569 ObjectIdGetDatum(inhrelid));
571 true, NULL, 1, &key);
572
574 {
575 Oid parent;
576
577 /* Compare inhparent if it was given, and do the actual deletion. */
578 parent = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhparent;
579 if (!OidIsValid(inhparent) || parent == inhparent)
580 {
581 bool detach_pending;
582
585
586 /*
587 * Raise error depending on state. This should only happen for
588 * partitions, but we have no way to cross-check.
589 */
593 errmsg("cannot detach partition \"%s\"",
594 childname ? childname : "unknown relation"),
595 errdetail("The partition is being detached concurrently or has an unfinished detach."),
596 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation.")));
600 errmsg("cannot complete detaching partition \"%s\"",
601 childname ? childname : "unknown relation"),
602 errdetail("There's no pending concurrent detach.")));
603
605 found = true;
606 }
607 }
608
609 /* Done */
610 systable_endscan(scan);
612
613 return found;
614}
615
616/*
617 * Return whether the pg_inherits tuple for a partition has the "detach
618 * pending" flag set.
619 */
620bool
622{
624 ScanKeyData key;
625 SysScanDesc scan;
627
628 /* We don't have a good way to verify it is in fact a partition */
629
630 /*
631 * Find the pg_inherits entry by inhrelid. (There should only be one.)
632 */
634 ScanKeyInit(&key,
637 ObjectIdGetDatum(partoid));
639 true, NULL, 1, &key);
640
642 {
643 bool detached;
644
645 detached =
647
648 /* Done */
649 systable_endscan(scan);
651
652 return detached;
653 }
654
655 elog(ERROR, "relation %u is not a partition", partoid);
656 return false; /* keep compiler quiet */
657}
static Datum values[MAXATTR]
Definition bootstrap.c:189
int32_t int32
Definition c.h:620
uint32 TransactionId
Definition c.h:736
#define OidIsValid(objectId)
Definition c.h:858
uint32 result
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition dynahash.c:888
HTAB * hash_create(const char *tabname, int64 nelem, const HASHCTL *info, int flags)
Definition dynahash.c:359
void hash_destroy(HTAB *hashp)
Definition dynahash.c:801
int errcode(int sqlerrcode)
Definition elog.c:874
int errhint(const char *fmt,...) pg_attribute_printf(1
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define WARNING
Definition elog.h:36
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:227
#define ereport(elevel,...)
Definition elog.h:151
void systable_endscan(SysScanDesc sysscan)
Definition genam.c:604
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:515
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition genam.c:388
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition heaptuple.c:1025
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1372
@ HASH_ENTER
Definition hsearch.h:109
#define HASH_CONTEXT
Definition hsearch.h:97
#define HASH_ELEM
Definition hsearch.h:90
#define HASH_BLOBS
Definition hsearch.h:92
#define HeapTupleIsValid(tuple)
Definition htup.h:78
static TransactionId HeapTupleHeaderGetXmin(const HeapTupleHeaderData *tup)
static void * GETSTRUCT(const HeapTupleData *tuple)
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition indexing.c:233
void CatalogTupleDelete(Relation heapRel, const ItemPointerData *tid)
Definition indexing.c:365
int i
Definition isn.c:77
List * lappend_int(List *list, int datum)
Definition list.c:357
List * lappend_oid(List *list, Oid datum)
Definition list.c:375
void list_free(List *list)
Definition list.c:1546
bool list_member_oid(const List *list, Oid datum)
Definition list.c:722
void UnlockRelationOid(Oid relid, LOCKMODE lockmode)
Definition lmgr.c:229
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition lmgr.c:107
int LOCKMODE
Definition lockdefs.h:26
#define NoLock
Definition lockdefs.h:34
#define AccessShareLock
Definition lockdefs.h:36
#define RowExclusiveLock
Definition lockdefs.h:38
void * repalloc(void *pointer, Size size)
Definition mcxt.c:1632
void pfree(void *pointer)
Definition mcxt.c:1616
void * palloc(Size size)
Definition mcxt.c:1387
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
static char * errmsg
int oid_cmp(const void *p1, const void *p2)
Definition oid.c:287
Oid typeOrDomainTypeRelid(Oid type_id)
Definition parse_type.c:689
Oid typeidTypeRelid(Oid type_id)
Definition parse_type.c:668
FormData_pg_class * Form_pg_class
Definition pg_class.h:160
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
bool has_subclass(Oid relationId)
bool DeleteInheritsTuple(Oid inhrelid, Oid inhparent, bool expect_detach_pending, const char *childname)
List * find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
Definition pg_inherits.c:59
void StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
List * find_inheritance_children_extended(Oid parentrelId, bool omit_detached, LOCKMODE lockmode, bool *detached_exist, TransactionId *detached_xmin)
Definition pg_inherits.c:83
bool has_superclass(Oid relationId)
bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId)
bool PartitionHasPendingDetach(Oid partoid)
END_CATALOG_STRUCT typedef FormData_pg_inherits * Form_pg_inherits
Definition pg_inherits.h:49
static int list_length(const List *l)
Definition pg_list.h:152
#define NIL
Definition pg_list.h:68
#define lfirst_int(lc)
Definition pg_list.h:173
#define list_make1_oid(x1)
Definition pg_list.h:274
static ListCell * list_nth_cell(const List *list, int n)
Definition pg_list.h:309
#define list_make1_int(x1)
Definition pg_list.h:259
#define lfirst_oid(lc)
Definition pg_list.h:174
#define qsort(a, b, c, d)
Definition port.h:495
static Datum BoolGetDatum(bool X)
Definition postgres.h:112
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
uint64_t Datum
Definition postgres.h:70
static Datum Int32GetDatum(int32 X)
Definition postgres.h:212
#define InvalidOid
unsigned int Oid
static int fb(int x)
tree ctl
Definition radixtree.h:1838
#define RelationGetDescr(relation)
Definition rel.h:540
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition scankey.c:76
bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
Definition snapmgr.c:1869
bool ActiveSnapshotSet(void)
Definition snapmgr.c:812
Snapshot GetActiveSnapshot(void)
Definition snapmgr.c:800
#define BTEqualStrategyNumber
Definition stratnum.h:31
Definition pg_list.h:54
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:265
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:221
#define SearchSysCacheExists1(cacheId, key1)
Definition syscache.h:100
void table_close(Relation relation, LOCKMODE lockmode)
Definition table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition table.c:40
static bool TransactionIdFollows(TransactionId id1, TransactionId id2)
Definition transam.h:297
#define InvalidTransactionId
Definition transam.h:31