Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Postgres/src/backend/catalog/   (Postgres Database Version 18.4©)  Datei vom 11.4.2026 mit Größe 19 kB image not shown  

Quelle  pg_inherits.c

  Sprache: C
 

/*-------------------------------------------------------------------------
 *
 * pg_inherits.c
 *   routines to support manipulation of the pg_inherits relation
 *
 * Note: currently, this module mostly contains inquiry functions; actual
 * creation and deletion of pg_inherits entries is mostly done in tablecmds.c.
 * Perhaps someday that code should be moved here, but it'd have to be
 * disentangled from other stuff such as pg_depend updates.
 *
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *   src/backend/catalog/pg_inherits.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "access/genam.h"
#include "access/htup_details.h"
#include "access/table.h"
#include "catalog/indexing.h"
#include "catalog/pg_inherits.h"
#include "parser/parse_type.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"

/*
 * Entry of a hash table used in find_all_inheritors. See below.
 */

typedef struct SeenRelsEntry
{
 Oid   rel_id;   /* relation oid */
 int   list_index;  /* its position in output list(s) */
} SeenRelsEntry;

/*
 * find_inheritance_children
 *
 * Returns a list containing the OIDs of all relations which
 * inherit *directly* from the relation with OID 'parentrelId'.
 *
 * The specified lock type is acquired on each child relation (but not on the
 * given rel; caller should already have locked it).  If lockmode is NoLock
 * then no locks are acquired, but caller must beware of race conditions
 * against possible DROPs of child relations.
 *
 * Partitions marked as being detached are omitted; see
 * find_inheritance_children_extended for details.
 */

List *
find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
{
 return find_inheritance_children_extended(parentrelId, true, lockmode,
             NULL, NULL);
}

/*
 * find_inheritance_children_extended
 *
 * As find_inheritance_children, with more options regarding detached
 * partitions.
 *
 * If a partition's pg_inherits row is marked "detach pending",
 * *detached_exist (if not null) is set true.
 *
 * If omit_detached is true and there is an active snapshot (not the same as
 * the catalog snapshot used to scan pg_inherits!) and a pg_inherits tuple
 * marked "detach pending" is visible to that snapshot, then that partition is
 * omitted from the output list.  This makes partitions invisible depending on
 * whether the transaction that marked those partitions as detached appears
 * committed to the active snapshot.  In addition, *detached_xmin (if not null)
 * is set to the xmin of the row of the detached partition.
 */

List *
find_inheritance_children_extended(Oid parentrelId, bool omit_detached,
           LOCKMODE lockmode, bool *detached_exist,
           TransactionId *detached_xmin)
{
 List    *list = NIL;
 Relation relation;
 SysScanDesc scan;
 ScanKeyData key[1];
 HeapTuple inheritsTuple;
 Oid   inhrelid;
 Oid     *oidarr;
 int   maxoids,
    numoids,
    i;

 /*
  * Can skip the scan if pg_class shows the relation has never had a
  * subclass.
 */

 if (!has_subclass(parentrelId))
  return NIL;

 /*
  * Scan pg_inherits and build a working array of subclass OIDs.
 */

 maxoids = 32;
 oidarr = (Oid *) palloc(maxoids * sizeof(Oid));
 numoids = 0;

 relation = table_open(InheritsRelationId, AccessShareLock);

 ScanKeyInit(&key[0],
    Anum_pg_inherits_inhparent,
    BTEqualStrategyNumber, F_OIDEQ,
    ObjectIdGetDatum(parentrelId));

 scan = systable_beginscan(relation, InheritsParentIndexId, true,
         NULL, 1, key);

 while ((inheritsTuple = systable_getnext(scan)) != NULL)
 {
  /*
   * Cope with partitions concurrently being detached.  When we see a
   * partition marked "detach pending", we omit it from the returned set
   * of visible partitions if caller requested that and the tuple's xmin
   * does not appear in progress to the active snapshot.  (If there's no
   * active snapshot set, that means we're not running a user query, so
   * it's OK to always include detached partitions in that case; if the
   * xmin is still running to the active snapshot, then the partition
   * has not been detached yet and so we include it.)
   *
   * The reason for this hack is that we want to avoid seeing the
   * partition as alive in RI queries during REPEATABLE READ or
   * SERIALIZABLE transactions: such queries use a different snapshot
   * than the one used by regular (user) queries.
 */

  if (((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhdetachpending)
  {
   if (detached_exist)
    *detached_exist = true;

   if (omit_detached && ActiveSnapshotSet())
   {
    TransactionId xmin;
    Snapshot snap;

    xmin = HeapTupleHeaderGetXmin(inheritsTuple->t_data);
    snap = GetActiveSnapshot();

    if (!XidInMVCCSnapshot(xmin, snap))
    {
     if (detached_xmin)
     {
      /*
       * Two detached partitions should not occur (see
       * checks in MarkInheritDetached), but if they do,
       * track the newer of the two.  Make sure to warn the
       * user, so that they can clean up.  Since this is
       * just a cross-check against potentially corrupt
       * catalogs, we don't make it a full-fledged error
       * message.
 */

      if (*detached_xmin != InvalidTransactionId)
      {
       elog(WARNING, "more than one partition pending detach found for table with OID %u",
         parentrelId);
       if (TransactionIdFollows(xmin, *detached_xmin))
        *detached_xmin = xmin;
      }
      else
       *detached_xmin = xmin;
     }

     /* Don't add the partition to the output list */
     continue;
    }
   }
  }

  inhrelid = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhrelid;
  if (numoids >= maxoids)
  {
   maxoids *= 2;
   oidarr = (Oid *) repalloc(oidarr, maxoids * sizeof(Oid));
  }
  oidarr[numoids++] = inhrelid;
 }

 systable_endscan(scan);

 table_close(relation, AccessShareLock);

 /*
  * If we found more than one child, sort them by OID.  This ensures
  * reasonably consistent behavior regardless of the vagaries of an
  * indexscan.  This is important since we need to be sure all backends
  * lock children in the same order to avoid needless deadlocks.
 */

 if (numoids > 1)
  qsort(oidarr, numoids, sizeof(Oid), oid_cmp);

 /*
  * Acquire locks and build the result list.
 */

 for (i = 0; i < numoids; i++)
 {
  inhrelid = oidarr[i];

  if (lockmode != NoLock)
  {
   /* Get the lock to synchronize against concurrent drop */
   LockRelationOid(inhrelid, lockmode);

   /*
    * Now that we have the lock, double-check to see if the relation
    * really exists or not.  If not, assume it was dropped while we
    * waited to acquire lock, and ignore it.
 */

   if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(inhrelid)))
   {
    /* Release useless lock */
    UnlockRelationOid(inhrelid, lockmode);
    /* And ignore this relation */
    continue;
   }
  }

  list = lappend_oid(list, inhrelid);
 }

 pfree(oidarr);

 return list;
}


/*
 * find_all_inheritors -
 *  Returns a list of relation OIDs including the given rel plus
 *  all relations that inherit from it, directly or indirectly.
 *  Optionally, it also returns the number of parents found for
 *  each such relation within the inheritance tree rooted at the
 *  given rel.
 *
 * The specified lock type is acquired on all child relations (but not on the
 * given rel; caller should already have locked it).  If lockmode is NoLock
 * then no locks are acquired, but caller must beware of race conditions
 * against possible DROPs of child relations.
 *
 * NB - No current callers of this routine are interested in children being
 * concurrently detached, so there's no provision to include them.
 */

List *
find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
{
 /* hash table for O(1) rel_oid -> rel_numparents cell lookup */
 HTAB    *seen_rels;
 HASHCTL  ctl;
 List    *rels_list,
      *rel_numparents;
 ListCell   *l;

 ctl.keysize = sizeof(Oid);
 ctl.entrysize = sizeof(SeenRelsEntry);
 ctl.hcxt = CurrentMemoryContext;

 seen_rels = hash_create("find_all_inheritors temporary table",
       32/* start small and extend */
       &ctl,
       HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);

 /*
  * We build a list starting with the given rel and adding all direct and
  * indirect children.  We can use a single list as both the record of
  * already-found rels and the agenda of rels yet to be scanned for more
  * children.  This is a bit tricky but works because the foreach() macro
  * doesn't fetch the next list element until the bottom of the loop.  Note
  * that we can't keep pointers into the output lists; but an index is
  * sufficient.
 */

 rels_list = list_make1_oid(parentrelId);
 rel_numparents = list_make1_int(0);

 foreach(l, rels_list)
 {
  Oid   currentrel = lfirst_oid(l);
  List    *currentchildren;
  ListCell   *lc;

  /* Get the direct children of this rel */
  currentchildren = find_inheritance_children(currentrel, lockmode);

  /*
   * Add to the queue only those children not already seen. This avoids
   * making duplicate entries in case of multiple inheritance paths from
   * the same parent.  (It'll also keep us from getting into an infinite
   * loop, though theoretically there can't be any cycles in the
   * inheritance graph anyway.)
 */

  foreach(lc, currentchildren)
  {
   Oid   child_oid = lfirst_oid(lc);
   bool  found;
   SeenRelsEntry *hash_entry;

   hash_entry = hash_search(seen_rels, &child_oid, HASH_ENTER, &found);
   if (found)
   {
    /* if the rel is already there, bump number-of-parents counter */
    ListCell   *numparents_cell;

    numparents_cell = list_nth_cell(rel_numparents,
            hash_entry->list_index);
    lfirst_int(numparents_cell)++;
   }
   else
   {
    /* if it's not there, add it. expect 1 parent, initially. */
    hash_entry->list_index = list_length(rels_list);
    rels_list = lappend_oid(rels_list, child_oid);
    rel_numparents = lappend_int(rel_numparents, 1);
   }
  }
 }

 if (numparents)
  *numparents = rel_numparents;
 else
  list_free(rel_numparents);

 hash_destroy(seen_rels);

 return rels_list;
}


/*
 * has_subclass - does this relation have any children?
 *
 * In the current implementation, has_subclass returns whether a
 * particular class *might* have a subclass. It will not return the
 * correct result if a class had a subclass which was later dropped.
 * This is because relhassubclass in pg_class is not updated immediately
 * when a subclass is dropped, primarily because of concurrency concerns.
 *
 * Currently has_subclass is only used as an efficiency hack to skip
 * unnecessary inheritance searches, so this is OK.  Note that ANALYZE
 * on a childless table will clean up the obsolete relhassubclass flag.
 *
 * Although this doesn't actually touch pg_inherits, it seems reasonable
 * to keep it here since it's normally used with the other routines here.
 */

bool
has_subclass(Oid relationId)
{
 HeapTuple tuple;
 bool  result;

 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationId));
 if (!HeapTupleIsValid(tuple))
  elog(ERROR, "cache lookup failed for relation %u", relationId);

 result = ((Form_pg_class) GETSTRUCT(tuple))->relhassubclass;
 ReleaseSysCache(tuple);
 return result;
}

/*
 * has_superclass - does this relation inherit from another?
 *
 * Unlike has_subclass, this can be relied on to give an accurate answer.
 * However, the caller must hold a lock on the given relation so that it
 * can't be concurrently added to or removed from an inheritance hierarchy.
 */

bool
has_superclass(Oid relationId)
{
 Relation catalog;
 SysScanDesc scan;
 ScanKeyData skey;
 bool  result;

 catalog = table_open(InheritsRelationId, AccessShareLock);
 ScanKeyInit(&skey, Anum_pg_inherits_inhrelid, BTEqualStrategyNumber,
    F_OIDEQ, ObjectIdGetDatum(relationId));
 scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
         NULL, 1, &skey);
 result = HeapTupleIsValid(systable_getnext(scan));
 systable_endscan(scan);
 table_close(catalog, AccessShareLock);

 return result;
}

/*
 * Given two type OIDs, determine whether the first is a complex type
 * (class type) that inherits from the second.
 *
 * This essentially asks whether the first type is guaranteed to be coercible
 * to the second.  Therefore, we allow the first type to be a domain over a
 * complex type that inherits from the second; that creates no difficulties.
 * But the second type cannot be a domain.
 */

bool
typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId)
{
 bool  result = false;
 Oid   subclassRelid;
 Oid   superclassRelid;
 Relation inhrel;
 List    *visited,
      *queue;
 ListCell   *queue_item;

 /* We need to work with the associated relation OIDs */
 subclassRelid = typeOrDomainTypeRelid(subclassTypeId);
 if (subclassRelid == InvalidOid)
  return false;   /* not a complex type or domain over one */
 superclassRelid = typeidTypeRelid(superclassTypeId);
 if (superclassRelid == InvalidOid)
  return false;   /* not a complex type */

 /* No point in searching if the superclass has no subclasses */
 if (!has_subclass(superclassRelid))
  return false;

 /*
  * Begin the search at the relation itself, so add its relid to the queue.
 */

 queue = list_make1_oid(subclassRelid);
 visited = NIL;

 inhrel = table_open(InheritsRelationId, AccessShareLock);

 /*
  * Use queue to do a breadth-first traversal of the inheritance graph from
  * the relid supplied up to the root.  Notice that we append to the queue
  * inside the loop --- this is okay because the foreach() macro doesn't
  * advance queue_item until the next loop iteration begins.
 */

 foreach(queue_item, queue)
 {
  Oid   this_relid = lfirst_oid(queue_item);
  ScanKeyData skey;
  SysScanDesc inhscan;
  HeapTuple inhtup;

  /*
   * If we've seen this relid already, skip it.  This avoids extra work
   * in multiple-inheritance scenarios, and also protects us from an
   * infinite loop in case there is a cycle in pg_inherits (though
   * theoretically that shouldn't happen).
 */

  if (list_member_oid(visited, this_relid))
   continue;

  /*
   * Okay, this is a not-yet-seen relid. Add it to the list of
   * already-visited OIDs, then find all the types this relid inherits
   * from and add them to the queue.
 */

  visited = lappend_oid(visited, this_relid);

  ScanKeyInit(&skey,
     Anum_pg_inherits_inhrelid,
     BTEqualStrategyNumber, F_OIDEQ,
     ObjectIdGetDatum(this_relid));

  inhscan = systable_beginscan(inhrel, InheritsRelidSeqnoIndexId, true,
          NULL, 1, &skey);

  while ((inhtup = systable_getnext(inhscan)) != NULL)
  {
   Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inhtup);
   Oid   inhparent = inh->inhparent;

   /* If this is the target superclass, we're done */
   if (inhparent == superclassRelid)
   {
    result = true;
    break;
   }

   /* Else add to queue */
   queue = lappend_oid(queue, inhparent);
  }

  systable_endscan(inhscan);

  if (result)
   break;
 }

 /* clean up ... */
 table_close(inhrel, AccessShareLock);

 list_free(visited);
 list_free(queue);

 return result;
}

/*
 * Create a single pg_inherits row with the given data
 */

void
StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
{
 Datum  values[Natts_pg_inherits];
 bool  nulls[Natts_pg_inherits];
 HeapTuple tuple;
 Relation inhRelation;

 inhRelation = table_open(InheritsRelationId, RowExclusiveLock);

 /*
  * Make the pg_inherits entry
 */

 values[Anum_pg_inherits_inhrelid - 1] = ObjectIdGetDatum(relationId);
 values[Anum_pg_inherits_inhparent - 1] = ObjectIdGetDatum(parentOid);
 values[Anum_pg_inherits_inhseqno - 1] = Int32GetDatum(seqNumber);
 values[Anum_pg_inherits_inhdetachpending - 1] = BoolGetDatum(false);

 memset(nulls, 0sizeof(nulls));

 tuple = heap_form_tuple(RelationGetDescr(inhRelation), values, nulls);

 CatalogTupleInsert(inhRelation, tuple);

 heap_freetuple(tuple);

 table_close(inhRelation, RowExclusiveLock);
}

/*
 * DeleteInheritsTuple
 *
 * Delete pg_inherits tuples with the given inhrelid.  inhparent may be given
 * as InvalidOid, in which case all tuples matching inhrelid are deleted;
 * otherwise only delete tuples with the specified inhparent.
 *
 * expect_detach_pending is the expected state of the inhdetachpending flag.
 * If the catalog row does not match that state, an error is raised.
 *
 * childname is the partition name, if a table; pass NULL for regular
 * inheritance or when working with other relation kinds.
 *
 * Returns whether at least one row was deleted.
 */

bool
DeleteInheritsTuple(Oid inhrelid, Oid inhparent, bool expect_detach_pending,
     const char *childname)
{
 bool  found = false;
 Relation catalogRelation;
 ScanKeyData key;
 SysScanDesc scan;
 HeapTuple inheritsTuple;

 /*
  * Find pg_inherits entries by inhrelid.
 */

 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
 ScanKeyInit(&key,
    Anum_pg_inherits_inhrelid,
    BTEqualStrategyNumber, F_OIDEQ,
    ObjectIdGetDatum(inhrelid));
 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
         true, NULL, 1, &key);

 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
 {
  Oid   parent;

  /* Compare inhparent if it was given, and do the actual deletion. */
  parent = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhparent;
  if (!OidIsValid(inhparent) || parent == inhparent)
  {
   bool  detach_pending;

   detach_pending =
    ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhdetachpending;

   /*
    * Raise error depending on state.  This should only happen for
    * partitions, but we have no way to cross-check.
 */

   if (detach_pending && !expect_detach_pending)
    ereport(ERROR,
      (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
       errmsg("cannot detach partition \"%s\"",
        childname ? childname : "unknown relation"),
       errdetail("The partition is being detached concurrently or has an unfinished detach."),
       errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation.")));
   if (!detach_pending && expect_detach_pending)
    ereport(ERROR,
      (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
       errmsg("cannot complete detaching partition \"%s\"",
        childname ? childname : "unknown relation"),
       errdetail("There's no pending concurrent detach.")));

   CatalogTupleDelete(catalogRelation, &inheritsTuple->t_self);
   found = true;
  }
 }

 /* Done */
 systable_endscan(scan);
 table_close(catalogRelation, RowExclusiveLock);

 return found;
}

/*
 * Return whether the pg_inherits tuple for a partition has the "detach
 * pending" flag set.
 */

bool
PartitionHasPendingDetach(Oid partoid)
{
 Relation catalogRelation;
 ScanKeyData key;
 SysScanDesc scan;
 HeapTuple inheritsTuple;

 /* We don't have a good way to verify it is in fact a partition */

 /*
  * Find the pg_inherits entry by inhrelid.  (There should only be one.)
 */

 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
 ScanKeyInit(&key,
    Anum_pg_inherits_inhrelid,
    BTEqualStrategyNumber, F_OIDEQ,
    ObjectIdGetDatum(partoid));
 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
         true, NULL, 1, &key);

 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
 {
  bool  detached;

  detached =
   ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhdetachpending;

  /* Done */
  systable_endscan(scan);
  table_close(catalogRelation, RowExclusiveLock);

  return detached;
 }

 elog(ERROR, "relation %u is not a partition", partoid);
 return false;    /* keep compiler quiet */
}

Messung V0.5 in Prozent
C=92 H=95 G=93

¤ Dauer der Verarbeitung: 0.17 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Normalansicht

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.