1/*-------------------------------------------------------------------------
4 * routines to support manipulation of the pg_inherits relation
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.
11 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
12 * Portions Copyright (c) 1994, Regents of the University of California
16 * src/backend/catalog/pg_inherits.c
18 *-------------------------------------------------------------------------
30#include "utils/fmgroids.h"
35 * Entry of a hash table used in find_all_inheritors. See below.
44 * find_inheritance_children
46 * Returns a list containing the OIDs of all relations which
47 * inherit *directly* from the relation with OID 'parentrelId'.
49 * The specified lock type is acquired on each child relation (but not on the
50 * given rel; caller should already have locked it). If lockmode is NoLock
51 * then no locks are acquired, but caller must beware of race conditions
52 * against possible DROPs of child relations.
54 * Partitions marked as being detached are omitted; see
55 * find_inheritance_children_extended for details.
65 * find_inheritance_children_extended
67 * As find_inheritance_children, with more options regarding detached
70 * If a partition's pg_inherits row is marked "detach pending",
71 * *detached_exist (if not null) is set true.
73 * If omit_detached is true and there is an active snapshot (not the same as
74 * the catalog snapshot used to scan pg_inherits!) and a pg_inherits tuple
75 * marked "detach pending" is visible to that snapshot, then that partition is
76 * omitted from the output list. This makes partitions invisible depending on
77 * whether the transaction that marked those partitions as detached appears
78 * committed to the active snapshot. In addition, *detached_xmin (if not null)
79 * is set to the xmin of the row of the detached partition.
83 LOCKMODE lockmode,
bool *detached_exist,
98 * Can skip the scan if pg_class shows the relation has never had a
105 * Scan pg_inherits and build a working array of subclass OIDs.
114 Anum_pg_inherits_inhparent,
124 * Cope with partitions concurrently being detached. When we see a
125 * partition marked "detach pending", we omit it from the returned set
126 * of visible partitions if caller requested that and the tuple's xmin
127 * does not appear in progress to the active snapshot. (If there's no
128 * active snapshot set, that means we're not running a user query, so
129 * it's OK to always include detached partitions in that case; if the
130 * xmin is still running to the active snapshot, then the partition
131 * has not been detached yet and so we include it.)
133 * The reason for this hack is that we want to avoid seeing the
134 * partition as alive in RI queries during REPEATABLE READ or
135 * SERIALIZABLE transactions: such queries use a different snapshot
136 * than the one used by regular (user) queries.
141 *detached_exist =
true;
156 * Two detached partitions should not occur (see
157 * checks in MarkInheritDetached), but if they do,
158 * track the newer of the two. Make sure to warn the
159 * user, so that they can clean up. Since this is
160 * just a cross-check against potentially corrupt
161 * catalogs, we don't make it a full-fledged error
166 elog(
WARNING,
"more than one partition pending detach found for table with OID %u",
169 *detached_xmin = xmin;
172 *detached_xmin = xmin;
175 /* Don't add the partition to the output list */
182 if (numoids >= maxoids)
187 oidarr[numoids++] = inhrelid;
195 * If we found more than one child, sort them by OID. This ensures
196 * reasonably consistent behavior regardless of the vagaries of an
197 * indexscan. This is important since we need to be sure all backends
198 * lock children in the same order to avoid needless deadlocks.
204 * Acquire locks and build the result list.
206 for (
i = 0;
i < numoids;
i++)
208 inhrelid = oidarr[
i];
212 /* Get the lock to synchronize against concurrent drop */
216 * Now that we have the lock, double-check to see if the relation
217 * really exists or not. If not, assume it was dropped while we
218 * waited to acquire lock, and ignore it.
222 /* Release useless lock */
224 /* And ignore this relation */
239 * find_all_inheritors -
240 * Returns a list of relation OIDs including the given rel plus
241 * all relations that inherit from it, directly or indirectly.
242 * Optionally, it also returns the number of parents found for
243 * each such relation within the inheritance tree rooted at the
246 * The specified lock type is acquired on all child relations (but not on the
247 * given rel; caller should already have locked it). If lockmode is NoLock
248 * then no locks are acquired, but caller must beware of race conditions
249 * against possible DROPs of child relations.
251 * NB - No current callers of this routine are interested in children being
252 * concurrently detached, so there's no provision to include them.
257 /* hash table for O(1) rel_oid -> rel_numparents cell lookup */
264 ctl.keysize =
sizeof(
Oid);
268 seen_rels =
hash_create(
"find_all_inheritors temporary table",
269 32,
/* start small and extend */
274 * We build a list starting with the given rel and adding all direct and
275 * indirect children. We can use a single list as both the record of
276 * already-found rels and the agenda of rels yet to be scanned for more
277 * children. This is a bit tricky but works because the foreach() macro
278 * doesn't fetch the next list element until the bottom of the loop. Note
279 * that we can't keep pointers into the output lists; but an index is
285 foreach(l, rels_list)
288 List *currentchildren;
291 /* Get the direct children of this rel */
295 * Add to the queue only those children not already seen. This avoids
296 * making duplicate entries in case of multiple inheritance paths from
297 * the same parent. (It'll also keep us from getting into an infinite
298 * loop, though theoretically there can't be any cycles in the
299 * inheritance graph anyway.)
301 foreach(lc, currentchildren)
310 /* if the rel is already there, bump number-of-parents counter */
319 /* if it's not there, add it. expect 1 parent, initially. */
328 *numparents = rel_numparents;
339 * has_subclass - does this relation have any children?
341 * In the current implementation, has_subclass returns whether a
342 * particular class *might* have a subclass. It will not return the
343 * correct result if a class had a subclass which was later dropped.
344 * This is because relhassubclass in pg_class is not updated immediately
345 * when a subclass is dropped, primarily because of concurrency concerns.
347 * Currently has_subclass is only used as an efficiency hack to skip
348 * unnecessary inheritance searches, so this is OK. Note that ANALYZE
349 * on a childless table will clean up the obsolete relhassubclass flag.
351 * Although this doesn't actually touch pg_inherits, it seems reasonable
352 * to keep it here since it's normally used with the other routines here.
362 elog(
ERROR,
"cache lookup failed for relation %u", relationId);
370 * has_superclass - does this relation inherit from another?
372 * Unlike has_subclass, this can be relied on to give an accurate answer.
373 * However, the caller must hold a lock on the given relation so that it
374 * can't be concurrently added to or removed from an inheritance hierarchy.
397 * Given two type OIDs, determine whether the first is a complex type
398 * (class type) that inherits from the second.
400 * This essentially asks whether the first type is guaranteed to be coercible
401 * to the second. Therefore, we allow the first type to be a domain over a
402 * complex type that inherits from the second; that creates no difficulties.
403 * But the second type cannot be a domain.
416 /* We need to work with the associated relation OIDs */
419 return false;
/* not a complex type or domain over one */
422 return false;
/* not a complex type */
424 /* No point in searching if the superclass has no subclasses */
429 * Begin the search at the relation itself, so add its relid to the queue.
437 * Use queue to do a breadth-first traversal of the inheritance graph from
438 * the relid supplied up to the root. Notice that we append to the queue
439 * inside the loop --- this is okay because the foreach() macro doesn't
440 * advance queue_item until the next loop iteration begins.
442 foreach(queue_item, queue)
450 * If we've seen this relid already, skip it. This avoids extra work
451 * in multiple-inheritance scenarios, and also protects us from an
452 * infinite loop in case there is a cycle in pg_inherits (though
453 * theoretically that shouldn't happen).
459 * Okay, this is a not-yet-seen relid. Add it to the list of
460 * already-visited OIDs, then find all the types this relid inherits
461 * from and add them to the queue.
466 Anum_pg_inherits_inhrelid,
476 Oid inhparent = inh->inhparent;
478 /* If this is the target superclass, we're done */
479 if (inhparent == superclassRelid)
485 /* Else add to queue */
505 * Create a single pg_inherits row with the given data
511 bool nulls[Natts_pg_inherits];
518 * Make the pg_inherits entry
525 memset(nulls, 0,
sizeof(nulls));
537 * DeleteInheritsTuple
539 * Delete pg_inherits tuples with the given inhrelid. inhparent may be given
540 * as InvalidOid, in which case all tuples matching inhrelid are deleted;
541 * otherwise only delete tuples with the specified inhparent.
543 * expect_detach_pending is the expected state of the inhdetachpending flag.
544 * If the catalog row does not match that state, an error is raised.
546 * childname is the partition name, if a table; pass NULL for regular
547 * inheritance or when working with other relation kinds.
549 * Returns whether at least one row was deleted.
553 const char *childname)
562 * Find pg_inherits entries by inhrelid.
566 Anum_pg_inherits_inhrelid,
570 true, NULL, 1, &
key);
576 /* Compare inhparent if it was given, and do the actual deletion. */
578 if (!
OidIsValid(inhparent) || parent == inhparent)
586 * Raise error depending on state. This should only happen for
587 * partitions, but we have no way to cross-check.
589 if (detach_pending && !expect_detach_pending)
591 (
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
592 errmsg(
"cannot detach partition \"%s\"",
593 childname ? childname :
"unknown relation"),
594 errdetail(
"The partition is being detached concurrently or has an unfinished detach."),
595 errhint(
"Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation.")));
596 if (!detach_pending && expect_detach_pending)
598 (
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
599 errmsg(
"cannot complete detaching partition \"%s\"",
600 childname ? childname :
"unknown relation"),
601 errdetail(
"There's no pending concurrent detach.")));
616 * Return whether the pg_inherits tuple for a partition has the "detach
627 /* We don't have a good way to verify it is in fact a partition */
630 * Find the pg_inherits entry by inhrelid. (There should only be one.)
634 Anum_pg_inherits_inhrelid,
638 true, NULL, 1, &
key);
654 elog(
ERROR,
"relation %u is not a partition", partoid);
655 return false;
/* keep compiler quiet */
static Datum values[MAXATTR]
#define OidIsValid(objectId)
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
HTAB * hash_create(const char *tabname, int64 nelem, const HASHCTL *info, int flags)
void hash_destroy(HTAB *hashp)
int errdetail(const char *fmt,...)
int errhint(const char *fmt,...)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
void systable_endscan(SysScanDesc sysscan)
HeapTuple systable_getnext(SysScanDesc sysscan)
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
void heap_freetuple(HeapTuple htup)
#define HeapTupleIsValid(tuple)
static TransactionId HeapTupleHeaderGetXmin(const HeapTupleHeaderData *tup)
static void * GETSTRUCT(const HeapTupleData *tuple)
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
List * lappend_int(List *list, int datum)
List * lappend_oid(List *list, Oid datum)
void list_free(List *list)
bool list_member_oid(const List *list, Oid datum)
void UnlockRelationOid(Oid relid, LOCKMODE lockmode)
void LockRelationOid(Oid relid, LOCKMODE lockmode)
void * repalloc(void *pointer, Size size)
void pfree(void *pointer)
MemoryContext CurrentMemoryContext
int oid_cmp(const void *p1, const void *p2)
Oid typeOrDomainTypeRelid(Oid type_id)
Oid typeidTypeRelid(Oid type_id)
FormData_pg_class * Form_pg_class
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)
struct SeenRelsEntry SeenRelsEntry
List * find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
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)
bool has_superclass(Oid relationId)
bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId)
bool PartitionHasPendingDetach(Oid partoid)
FormData_pg_inherits * Form_pg_inherits
static int list_length(const List *l)
#define list_make1_oid(x1)
static ListCell * list_nth_cell(const List *list, int n)
#define list_make1_int(x1)
#define qsort(a, b, c, d)
static Datum BoolGetDatum(bool X)
static Datum ObjectIdGetDatum(Oid X)
static Datum Int32GetDatum(int32 X)
#define RelationGetDescr(relation)
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
bool ActiveSnapshotSet(void)
Snapshot GetActiveSnapshot(void)
#define BTEqualStrategyNumber
void ReleaseSysCache(HeapTuple tuple)
HeapTuple SearchSysCache1(int cacheId, Datum key1)
#define SearchSysCacheExists1(cacheId, key1)
void table_close(Relation relation, LOCKMODE lockmode)
Relation table_open(Oid relationId, LOCKMODE lockmode)
bool TransactionIdFollows(TransactionId id1, TransactionId id2)
#define InvalidTransactionId