LCOV - code coverage report
Current view: top level - src/backend/catalog - pg_inherits.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 95.0 % 161 153
Test Date: 2026-04-07 14:16:30 Functions: 100.0 % 9 9
Legend: Lines:     hit not hit

            Line data    Source code
       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              :  */
      38              : typedef struct SeenRelsEntry
      39              : {
      40              :     Oid         rel_id;         /* relation oid */
      41              :     int         list_index;     /* its position in output list(s) */
      42              : } SeenRelsEntry;
      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              :  */
      58              : List *
      59        64214 : find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
      60              : {
      61        64214 :     return find_inheritance_children_extended(parentrelId, true, lockmode,
      62              :                                               NULL, NULL);
      63              : }
      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              :  */
      82              : List *
      83        81095 : find_inheritance_children_extended(Oid parentrelId, bool omit_detached,
      84              :                                    LOCKMODE lockmode, bool *detached_exist,
      85              :                                    TransactionId *detached_xmin)
      86              : {
      87        81095 :     List       *list = NIL;
      88              :     Relation    relation;
      89              :     SysScanDesc scan;
      90              :     ScanKeyData key[1];
      91              :     HeapTuple   inheritsTuple;
      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              :      */
     102        81095 :     if (!has_subclass(parentrelId))
     103        56951 :         return NIL;
     104              : 
     105              :     /*
     106              :      * Scan pg_inherits and build a working array of subclass OIDs.
     107              :      */
     108        24144 :     maxoids = 32;
     109        24144 :     oidarr = (Oid *) palloc(maxoids * sizeof(Oid));
     110        24144 :     numoids = 0;
     111              : 
     112        24144 :     relation = table_open(InheritsRelationId, AccessShareLock);
     113              : 
     114        24144 :     ScanKeyInit(&key[0],
     115              :                 Anum_pg_inherits_inhparent,
     116              :                 BTEqualStrategyNumber, F_OIDEQ,
     117              :                 ObjectIdGetDatum(parentrelId));
     118              : 
     119        24144 :     scan = systable_beginscan(relation, InheritsParentIndexId, true,
     120              :                               NULL, 1, key);
     121              : 
     122       107308 :     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              :          */
     139        83164 :         if (((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhdetachpending)
     140              :         {
     141           58 :             if (detached_exist)
     142           56 :                 *detached_exist = true;
     143              : 
     144           58 :             if (omit_detached && ActiveSnapshotSet())
     145              :             {
     146              :                 TransactionId xmin;
     147              :                 Snapshot    snap;
     148              : 
     149           47 :                 xmin = HeapTupleHeaderGetXmin(inheritsTuple->t_data);
     150           47 :                 snap = GetActiveSnapshot();
     151              : 
     152           47 :                 if (!XidInMVCCSnapshot(xmin, snap))
     153              :                 {
     154           35 :                     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              :                          */
     165           33 :                         if (*detached_xmin != InvalidTransactionId)
     166              :                         {
     167            0 :                             elog(WARNING, "more than one partition pending detach found for table with OID %u",
     168              :                                  parentrelId);
     169            0 :                             if (TransactionIdFollows(xmin, *detached_xmin))
     170            0 :                                 *detached_xmin = xmin;
     171              :                         }
     172              :                         else
     173           33 :                             *detached_xmin = xmin;
     174              :                     }
     175              : 
     176              :                     /* Don't add the partition to the output list */
     177           35 :                     continue;
     178              :                 }
     179              :             }
     180              :         }
     181              : 
     182        83129 :         inhrelid = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhrelid;
     183        83129 :         if (numoids >= maxoids)
     184              :         {
     185          760 :             maxoids *= 2;
     186          760 :             oidarr = (Oid *) repalloc(oidarr, maxoids * sizeof(Oid));
     187              :         }
     188        83129 :         oidarr[numoids++] = inhrelid;
     189              :     }
     190              : 
     191        24144 :     systable_endscan(scan);
     192              : 
     193        24144 :     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        24144 :     if (numoids > 1)
     202        13778 :         qsort(oidarr, numoids, sizeof(Oid), oid_cmp);
     203              : 
     204              :     /*
     205              :      * Acquire locks and build the result list.
     206              :      */
     207       107273 :     for (i = 0; i < numoids; i++)
     208              :     {
     209        83129 :         inhrelid = oidarr[i];
     210              : 
     211        83129 :         if (lockmode != NoLock)
     212              :         {
     213              :             /* Get the lock to synchronize against concurrent drop */
     214        14358 :             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              :              */
     221        14358 :             if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(inhrelid)))
     222              :             {
     223              :                 /* Release useless lock */
     224            1 :                 UnlockRelationOid(inhrelid, lockmode);
     225              :                 /* And ignore this relation */
     226            1 :                 continue;
     227              :             }
     228              :         }
     229              : 
     230        83128 :         list = lappend_oid(list, inhrelid);
     231              :     }
     232              : 
     233        24144 :     pfree(oidarr);
     234              : 
     235        24144 :     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              :  */
     255              : List *
     256        35777 : find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
     257              : {
     258              :     /* hash table for O(1) rel_oid -> rel_numparents cell lookup */
     259              :     HTAB       *seen_rels;
     260              :     HASHCTL     ctl;
     261              :     List       *rels_list,
     262              :                *rel_numparents;
     263              :     ListCell   *l;
     264              : 
     265        35777 :     ctl.keysize = sizeof(Oid);
     266        35777 :     ctl.entrysize = sizeof(SeenRelsEntry);
     267        35777 :     ctl.hcxt = CurrentMemoryContext;
     268              : 
     269        35777 :     seen_rels = hash_create("find_all_inheritors temporary table",
     270              :                             32, /* start small and extend */
     271              :                             &ctl,
     272              :                             HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
     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              :      */
     283        35777 :     rels_list = list_make1_oid(parentrelId);
     284        35777 :     rel_numparents = list_make1_int(0);
     285              : 
     286        88865 :     foreach(l, rels_list)
     287              :     {
     288        53088 :         Oid         currentrel = lfirst_oid(l);
     289              :         List       *currentchildren;
     290              :         ListCell   *lc;
     291              : 
     292              :         /* Get the direct children of this rel */
     293        53088 :         currentchildren = find_inheritance_children(currentrel, lockmode);
     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        70785 :         foreach(lc, currentchildren)
     303              :         {
     304        17697 :             Oid         child_oid = lfirst_oid(lc);
     305              :             bool        found;
     306              :             SeenRelsEntry *hash_entry;
     307              : 
     308        17697 :             hash_entry = hash_search(seen_rels, &child_oid, HASH_ENTER, &found);
     309        17697 :             if (found)
     310              :             {
     311              :                 /* if the rel is already there, bump number-of-parents counter */
     312              :                 ListCell   *numparents_cell;
     313              : 
     314          386 :                 numparents_cell = list_nth_cell(rel_numparents,
     315              :                                                 hash_entry->list_index);
     316          386 :                 lfirst_int(numparents_cell)++;
     317              :             }
     318              :             else
     319              :             {
     320              :                 /* if it's not there, add it. expect 1 parent, initially. */
     321        17311 :                 hash_entry->list_index = list_length(rels_list);
     322        17311 :                 rels_list = lappend_oid(rels_list, child_oid);
     323        17311 :                 rel_numparents = lappend_int(rel_numparents, 1);
     324              :             }
     325              :         }
     326              :     }
     327              : 
     328        35777 :     if (numparents)
     329          853 :         *numparents = rel_numparents;
     330              :     else
     331        34924 :         list_free(rel_numparents);
     332              : 
     333        35777 :     hash_destroy(seen_rels);
     334              : 
     335        35777 :     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              :  */
     355              : bool
     356        81288 : has_subclass(Oid relationId)
     357              : {
     358              :     HeapTuple   tuple;
     359              :     bool        result;
     360              : 
     361        81288 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationId));
     362        81288 :     if (!HeapTupleIsValid(tuple))
     363            0 :         elog(ERROR, "cache lookup failed for relation %u", relationId);
     364              : 
     365        81288 :     result = ((Form_pg_class) GETSTRUCT(tuple))->relhassubclass;
     366        81288 :     ReleaseSysCache(tuple);
     367        81288 :     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              :  */
     377              : bool
     378          617 : has_superclass(Oid relationId)
     379              : {
     380              :     Relation    catalog;
     381              :     SysScanDesc scan;
     382              :     ScanKeyData skey;
     383              :     bool        result;
     384              : 
     385          617 :     catalog = table_open(InheritsRelationId, AccessShareLock);
     386          617 :     ScanKeyInit(&skey, Anum_pg_inherits_inhrelid, BTEqualStrategyNumber,
     387              :                 F_OIDEQ, ObjectIdGetDatum(relationId));
     388          617 :     scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
     389              :                               NULL, 1, &skey);
     390          617 :     result = HeapTupleIsValid(systable_getnext(scan));
     391          617 :     systable_endscan(scan);
     392          617 :     table_close(catalog, AccessShareLock);
     393              : 
     394          617 :     return result;
     395              : }
     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              :  */
     406              : bool
     407       536117 : typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId)
     408              : {
     409       536117 :     bool        result = false;
     410              :     Oid         subclassRelid;
     411              :     Oid         superclassRelid;
     412              :     Relation    inhrel;
     413              :     List       *visited,
     414              :                *queue;
     415              :     ListCell   *queue_item;
     416              : 
     417              :     /* We need to work with the associated relation OIDs */
     418       536117 :     subclassRelid = typeOrDomainTypeRelid(subclassTypeId);
     419       536117 :     if (subclassRelid == InvalidOid)
     420       526811 :         return false;           /* not a complex type or domain over one */
     421         9306 :     superclassRelid = typeidTypeRelid(superclassTypeId);
     422         9306 :     if (superclassRelid == InvalidOid)
     423         9218 :         return false;           /* not a complex type */
     424              : 
     425              :     /* No point in searching if the superclass has no subclasses */
     426           88 :     if (!has_subclass(superclassRelid))
     427            8 :         return false;
     428              : 
     429              :     /*
     430              :      * Begin the search at the relation itself, so add its relid to the queue.
     431              :      */
     432           80 :     queue = list_make1_oid(subclassRelid);
     433           80 :     visited = NIL;
     434              : 
     435           80 :     inhrel = table_open(InheritsRelationId, AccessShareLock);
     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           80 :     foreach(queue_item, queue)
     444              :     {
     445           80 :         Oid         this_relid = lfirst_oid(queue_item);
     446              :         ScanKeyData skey;
     447              :         SysScanDesc inhscan;
     448              :         HeapTuple   inhtup;
     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              :          */
     456           80 :         if (list_member_oid(visited, this_relid))
     457            0 :             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              :          */
     464           80 :         visited = lappend_oid(visited, this_relid);
     465              : 
     466           80 :         ScanKeyInit(&skey,
     467              :                     Anum_pg_inherits_inhrelid,
     468              :                     BTEqualStrategyNumber, F_OIDEQ,
     469              :                     ObjectIdGetDatum(this_relid));
     470              : 
     471           80 :         inhscan = systable_beginscan(inhrel, InheritsRelidSeqnoIndexId, true,
     472              :                                      NULL, 1, &skey);
     473              : 
     474           88 :         while ((inhtup = systable_getnext(inhscan)) != NULL)
     475              :         {
     476           88 :             Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inhtup);
     477           88 :             Oid         inhparent = inh->inhparent;
     478              : 
     479              :             /* If this is the target superclass, we're done */
     480           88 :             if (inhparent == superclassRelid)
     481              :             {
     482           80 :                 result = true;
     483           80 :                 break;
     484              :             }
     485              : 
     486              :             /* Else add to queue */
     487            8 :             queue = lappend_oid(queue, inhparent);
     488              :         }
     489              : 
     490           80 :         systable_endscan(inhscan);
     491              : 
     492           80 :         if (result)
     493           80 :             break;
     494              :     }
     495              : 
     496              :     /* clean up ... */
     497           80 :     table_close(inhrel, AccessShareLock);
     498              : 
     499           80 :     list_free(visited);
     500           80 :     list_free(queue);
     501              : 
     502           80 :     return result;
     503              : }
     504              : 
     505              : /*
     506              :  * Create a single pg_inherits row with the given data
     507              :  */
     508              : void
     509        12166 : StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
     510              : {
     511              :     Datum       values[Natts_pg_inherits];
     512              :     bool        nulls[Natts_pg_inherits];
     513              :     HeapTuple   tuple;
     514              :     Relation    inhRelation;
     515              : 
     516        12166 :     inhRelation = table_open(InheritsRelationId, RowExclusiveLock);
     517              : 
     518              :     /*
     519              :      * Make the pg_inherits entry
     520              :      */
     521        12166 :     values[Anum_pg_inherits_inhrelid - 1] = ObjectIdGetDatum(relationId);
     522        12166 :     values[Anum_pg_inherits_inhparent - 1] = ObjectIdGetDatum(parentOid);
     523        12166 :     values[Anum_pg_inherits_inhseqno - 1] = Int32GetDatum(seqNumber);
     524        12166 :     values[Anum_pg_inherits_inhdetachpending - 1] = BoolGetDatum(false);
     525              : 
     526        12166 :     memset(nulls, 0, sizeof(nulls));
     527              : 
     528        12166 :     tuple = heap_form_tuple(RelationGetDescr(inhRelation), values, nulls);
     529              : 
     530        12166 :     CatalogTupleInsert(inhRelation, tuple);
     531              : 
     532        12166 :     heap_freetuple(tuple);
     533              : 
     534        12166 :     table_close(inhRelation, RowExclusiveLock);
     535        12166 : }
     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              :  */
     552              : bool
     553        16931 : DeleteInheritsTuple(Oid inhrelid, Oid inhparent, bool expect_detach_pending,
     554              :                     const char *childname)
     555              : {
     556        16931 :     bool        found = false;
     557              :     Relation    catalogRelation;
     558              :     ScanKeyData key;
     559              :     SysScanDesc scan;
     560              :     HeapTuple   inheritsTuple;
     561              : 
     562              :     /*
     563              :      * Find pg_inherits entries by inhrelid.
     564              :      */
     565        16931 :     catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
     566        16931 :     ScanKeyInit(&key,
     567              :                 Anum_pg_inherits_inhrelid,
     568              :                 BTEqualStrategyNumber, F_OIDEQ,
     569              :                 ObjectIdGetDatum(inhrelid));
     570        16931 :     scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
     571              :                               true, NULL, 1, &key);
     572              : 
     573        19553 :     while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
     574              :     {
     575              :         Oid         parent;
     576              : 
     577              :         /* Compare inhparent if it was given, and do the actual deletion. */
     578         2622 :         parent = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhparent;
     579         2622 :         if (!OidIsValid(inhparent) || parent == inhparent)
     580              :         {
     581              :             bool        detach_pending;
     582              : 
     583         2586 :             detach_pending =
     584         2586 :                 ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhdetachpending;
     585              : 
     586              :             /*
     587              :              * Raise error depending on state.  This should only happen for
     588              :              * partitions, but we have no way to cross-check.
     589              :              */
     590         2586 :             if (detach_pending && !expect_detach_pending)
     591            0 :                 ereport(ERROR,
     592              :                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     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.")));
     597         2586 :             if (!detach_pending && expect_detach_pending)
     598            0 :                 ereport(ERROR,
     599              :                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     600              :                          errmsg("cannot complete detaching partition \"%s\"",
     601              :                                 childname ? childname : "unknown relation"),
     602              :                          errdetail("There's no pending concurrent detach.")));
     603              : 
     604         2586 :             CatalogTupleDelete(catalogRelation, &inheritsTuple->t_self);
     605         2586 :             found = true;
     606              :         }
     607              :     }
     608              : 
     609              :     /* Done */
     610        16931 :     systable_endscan(scan);
     611        16931 :     table_close(catalogRelation, RowExclusiveLock);
     612              : 
     613        16931 :     return found;
     614              : }
     615              : 
     616              : /*
     617              :  * Return whether the pg_inherits tuple for a partition has the "detach
     618              :  * pending" flag set.
     619              :  */
     620              : bool
     621          897 : PartitionHasPendingDetach(Oid partoid)
     622              : {
     623              :     Relation    catalogRelation;
     624              :     ScanKeyData key;
     625              :     SysScanDesc scan;
     626              :     HeapTuple   inheritsTuple;
     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              :      */
     633          897 :     catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
     634          897 :     ScanKeyInit(&key,
     635              :                 Anum_pg_inherits_inhrelid,
     636              :                 BTEqualStrategyNumber, F_OIDEQ,
     637              :                 ObjectIdGetDatum(partoid));
     638          897 :     scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
     639              :                               true, NULL, 1, &key);
     640              : 
     641          897 :     while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
     642              :     {
     643              :         bool        detached;
     644              : 
     645          897 :         detached =
     646          897 :             ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhdetachpending;
     647              : 
     648              :         /* Done */
     649          897 :         systable_endscan(scan);
     650          897 :         table_close(catalogRelation, RowExclusiveLock);
     651              : 
     652          897 :         return detached;
     653              :     }
     654              : 
     655            0 :     elog(ERROR, "relation %u is not a partition", partoid);
     656              :     return false;               /* keep compiler quiet */
     657              : }
        

Generated by: LCOV version 2.0-1