LCOV - code coverage report
Current view: top level - src/backend/catalog - pg_inherits.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 93.8 % 161 151
Test Date: 2026-03-01 19:14:57 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/snapmgr.h"
      32              : #include "utils/syscache.h"
      33              : 
      34              : /*
      35              :  * Entry of a hash table used in find_all_inheritors. See below.
      36              :  */
      37              : typedef struct SeenRelsEntry
      38              : {
      39              :     Oid         rel_id;         /* relation oid */
      40              :     int         list_index;     /* its position in output list(s) */
      41              : } SeenRelsEntry;
      42              : 
      43              : /*
      44              :  * find_inheritance_children
      45              :  *
      46              :  * Returns a list containing the OIDs of all relations which
      47              :  * inherit *directly* from the relation with OID 'parentrelId'.
      48              :  *
      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.
      53              :  *
      54              :  * Partitions marked as being detached are omitted; see
      55              :  * find_inheritance_children_extended for details.
      56              :  */
      57              : List *
      58        50581 : find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
      59              : {
      60        50581 :     return find_inheritance_children_extended(parentrelId, true, lockmode,
      61              :                                               NULL, NULL);
      62              : }
      63              : 
      64              : /*
      65              :  * find_inheritance_children_extended
      66              :  *
      67              :  * As find_inheritance_children, with more options regarding detached
      68              :  * partitions.
      69              :  *
      70              :  * If a partition's pg_inherits row is marked "detach pending",
      71              :  * *detached_exist (if not null) is set true.
      72              :  *
      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.
      80              :  */
      81              : List *
      82        63359 : find_inheritance_children_extended(Oid parentrelId, bool omit_detached,
      83              :                                    LOCKMODE lockmode, bool *detached_exist,
      84              :                                    TransactionId *detached_xmin)
      85              : {
      86        63359 :     List       *list = NIL;
      87              :     Relation    relation;
      88              :     SysScanDesc scan;
      89              :     ScanKeyData key[1];
      90              :     HeapTuple   inheritsTuple;
      91              :     Oid         inhrelid;
      92              :     Oid        *oidarr;
      93              :     int         maxoids,
      94              :                 numoids,
      95              :                 i;
      96              : 
      97              :     /*
      98              :      * Can skip the scan if pg_class shows the relation has never had a
      99              :      * subclass.
     100              :      */
     101        63359 :     if (!has_subclass(parentrelId))
     102        45497 :         return NIL;
     103              : 
     104              :     /*
     105              :      * Scan pg_inherits and build a working array of subclass OIDs.
     106              :      */
     107        17862 :     maxoids = 32;
     108        17862 :     oidarr = (Oid *) palloc(maxoids * sizeof(Oid));
     109        17862 :     numoids = 0;
     110              : 
     111        17862 :     relation = table_open(InheritsRelationId, AccessShareLock);
     112              : 
     113        17862 :     ScanKeyInit(&key[0],
     114              :                 Anum_pg_inherits_inhparent,
     115              :                 BTEqualStrategyNumber, F_OIDEQ,
     116              :                 ObjectIdGetDatum(parentrelId));
     117              : 
     118        17862 :     scan = systable_beginscan(relation, InheritsParentIndexId, true,
     119              :                               NULL, 1, key);
     120              : 
     121        51980 :     while ((inheritsTuple = systable_getnext(scan)) != NULL)
     122              :     {
     123              :         /*
     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.)
     132              :          *
     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.
     137              :          */
     138        34118 :         if (((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhdetachpending)
     139              :         {
     140           58 :             if (detached_exist)
     141           56 :                 *detached_exist = true;
     142              : 
     143           58 :             if (omit_detached && ActiveSnapshotSet())
     144              :             {
     145              :                 TransactionId xmin;
     146              :                 Snapshot    snap;
     147              : 
     148           47 :                 xmin = HeapTupleHeaderGetXmin(inheritsTuple->t_data);
     149           47 :                 snap = GetActiveSnapshot();
     150              : 
     151           47 :                 if (!XidInMVCCSnapshot(xmin, snap))
     152              :                 {
     153           35 :                     if (detached_xmin)
     154              :                     {
     155              :                         /*
     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
     162              :                          * message.
     163              :                          */
     164           33 :                         if (*detached_xmin != InvalidTransactionId)
     165              :                         {
     166            0 :                             elog(WARNING, "more than one partition pending detach found for table with OID %u",
     167              :                                  parentrelId);
     168            0 :                             if (TransactionIdFollows(xmin, *detached_xmin))
     169            0 :                                 *detached_xmin = xmin;
     170              :                         }
     171              :                         else
     172           33 :                             *detached_xmin = xmin;
     173              :                     }
     174              : 
     175              :                     /* Don't add the partition to the output list */
     176           35 :                     continue;
     177              :                 }
     178              :             }
     179              :         }
     180              : 
     181        34083 :         inhrelid = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhrelid;
     182        34083 :         if (numoids >= maxoids)
     183              :         {
     184            0 :             maxoids *= 2;
     185            0 :             oidarr = (Oid *) repalloc(oidarr, maxoids * sizeof(Oid));
     186              :         }
     187        34083 :         oidarr[numoids++] = inhrelid;
     188              :     }
     189              : 
     190        17862 :     systable_endscan(scan);
     191              : 
     192        17862 :     table_close(relation, AccessShareLock);
     193              : 
     194              :     /*
     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.
     199              :      */
     200        17862 :     if (numoids > 1)
     201        10061 :         qsort(oidarr, numoids, sizeof(Oid), oid_cmp);
     202              : 
     203              :     /*
     204              :      * Acquire locks and build the result list.
     205              :      */
     206        51945 :     for (i = 0; i < numoids; i++)
     207              :     {
     208        34083 :         inhrelid = oidarr[i];
     209              : 
     210        34083 :         if (lockmode != NoLock)
     211              :         {
     212              :             /* Get the lock to synchronize against concurrent drop */
     213         9885 :             LockRelationOid(inhrelid, lockmode);
     214              : 
     215              :             /*
     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.
     219              :              */
     220         9885 :             if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(inhrelid)))
     221              :             {
     222              :                 /* Release useless lock */
     223            1 :                 UnlockRelationOid(inhrelid, lockmode);
     224              :                 /* And ignore this relation */
     225            1 :                 continue;
     226              :             }
     227              :         }
     228              : 
     229        34082 :         list = lappend_oid(list, inhrelid);
     230              :     }
     231              : 
     232        17862 :     pfree(oidarr);
     233              : 
     234        17862 :     return list;
     235              : }
     236              : 
     237              : 
     238              : /*
     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
     244              :  *      given rel.
     245              :  *
     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.
     250              :  *
     251              :  * NB - No current callers of this routine are interested in children being
     252              :  * concurrently detached, so there's no provision to include them.
     253              :  */
     254              : List *
     255        29087 : find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
     256              : {
     257              :     /* hash table for O(1) rel_oid -> rel_numparents cell lookup */
     258              :     HTAB       *seen_rels;
     259              :     HASHCTL     ctl;
     260              :     List       *rels_list,
     261              :                *rel_numparents;
     262              :     ListCell   *l;
     263              : 
     264        29087 :     ctl.keysize = sizeof(Oid);
     265        29087 :     ctl.entrysize = sizeof(SeenRelsEntry);
     266        29087 :     ctl.hcxt = CurrentMemoryContext;
     267              : 
     268        29087 :     seen_rels = hash_create("find_all_inheritors temporary table",
     269              :                             32, /* start small and extend */
     270              :                             &ctl,
     271              :                             HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
     272              : 
     273              :     /*
     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
     280              :      * sufficient.
     281              :      */
     282        29087 :     rels_list = list_make1_oid(parentrelId);
     283        29087 :     rel_numparents = list_make1_int(0);
     284              : 
     285        71021 :     foreach(l, rels_list)
     286              :     {
     287        41934 :         Oid         currentrel = lfirst_oid(l);
     288              :         List       *currentchildren;
     289              :         ListCell   *lc;
     290              : 
     291              :         /* Get the direct children of this rel */
     292        41934 :         currentchildren = find_inheritance_children(currentrel, lockmode);
     293              : 
     294              :         /*
     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.)
     300              :          */
     301        55019 :         foreach(lc, currentchildren)
     302              :         {
     303        13085 :             Oid         child_oid = lfirst_oid(lc);
     304              :             bool        found;
     305              :             SeenRelsEntry *hash_entry;
     306              : 
     307        13085 :             hash_entry = hash_search(seen_rels, &child_oid, HASH_ENTER, &found);
     308        13085 :             if (found)
     309              :             {
     310              :                 /* if the rel is already there, bump number-of-parents counter */
     311              :                 ListCell   *numparents_cell;
     312              : 
     313          238 :                 numparents_cell = list_nth_cell(rel_numparents,
     314              :                                                 hash_entry->list_index);
     315          238 :                 lfirst_int(numparents_cell)++;
     316              :             }
     317              :             else
     318              :             {
     319              :                 /* if it's not there, add it. expect 1 parent, initially. */
     320        12847 :                 hash_entry->list_index = list_length(rels_list);
     321        12847 :                 rels_list = lappend_oid(rels_list, child_oid);
     322        12847 :                 rel_numparents = lappend_int(rel_numparents, 1);
     323              :             }
     324              :         }
     325              :     }
     326              : 
     327        29087 :     if (numparents)
     328          642 :         *numparents = rel_numparents;
     329              :     else
     330        28445 :         list_free(rel_numparents);
     331              : 
     332        29087 :     hash_destroy(seen_rels);
     333              : 
     334        29087 :     return rels_list;
     335              : }
     336              : 
     337              : 
     338              : /*
     339              :  * has_subclass - does this relation have any children?
     340              :  *
     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.
     346              :  *
     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.
     350              :  *
     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.
     353              :  */
     354              : bool
     355        63425 : has_subclass(Oid relationId)
     356              : {
     357              :     HeapTuple   tuple;
     358              :     bool        result;
     359              : 
     360        63425 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationId));
     361        63425 :     if (!HeapTupleIsValid(tuple))
     362            0 :         elog(ERROR, "cache lookup failed for relation %u", relationId);
     363              : 
     364        63425 :     result = ((Form_pg_class) GETSTRUCT(tuple))->relhassubclass;
     365        63425 :     ReleaseSysCache(tuple);
     366        63425 :     return result;
     367              : }
     368              : 
     369              : /*
     370              :  * has_superclass - does this relation inherit from another?
     371              :  *
     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.
     375              :  */
     376              : bool
     377          448 : has_superclass(Oid relationId)
     378              : {
     379              :     Relation    catalog;
     380              :     SysScanDesc scan;
     381              :     ScanKeyData skey;
     382              :     bool        result;
     383              : 
     384          448 :     catalog = table_open(InheritsRelationId, AccessShareLock);
     385          448 :     ScanKeyInit(&skey, Anum_pg_inherits_inhrelid, BTEqualStrategyNumber,
     386              :                 F_OIDEQ, ObjectIdGetDatum(relationId));
     387          448 :     scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
     388              :                               NULL, 1, &skey);
     389          448 :     result = HeapTupleIsValid(systable_getnext(scan));
     390          448 :     systable_endscan(scan);
     391          448 :     table_close(catalog, AccessShareLock);
     392              : 
     393          448 :     return result;
     394              : }
     395              : 
     396              : /*
     397              :  * Given two type OIDs, determine whether the first is a complex type
     398              :  * (class type) that inherits from the second.
     399              :  *
     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.
     404              :  */
     405              : bool
     406       447155 : typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId)
     407              : {
     408       447155 :     bool        result = false;
     409              :     Oid         subclassRelid;
     410              :     Oid         superclassRelid;
     411              :     Relation    inhrel;
     412              :     List       *visited,
     413              :                *queue;
     414              :     ListCell   *queue_item;
     415              : 
     416              :     /* We need to work with the associated relation OIDs */
     417       447155 :     subclassRelid = typeOrDomainTypeRelid(subclassTypeId);
     418       447155 :     if (subclassRelid == InvalidOid)
     419       440179 :         return false;           /* not a complex type or domain over one */
     420         6976 :     superclassRelid = typeidTypeRelid(superclassTypeId);
     421         6976 :     if (superclassRelid == InvalidOid)
     422         6910 :         return false;           /* not a complex type */
     423              : 
     424              :     /* No point in searching if the superclass has no subclasses */
     425           66 :     if (!has_subclass(superclassRelid))
     426            6 :         return false;
     427              : 
     428              :     /*
     429              :      * Begin the search at the relation itself, so add its relid to the queue.
     430              :      */
     431           60 :     queue = list_make1_oid(subclassRelid);
     432           60 :     visited = NIL;
     433              : 
     434           60 :     inhrel = table_open(InheritsRelationId, AccessShareLock);
     435              : 
     436              :     /*
     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.
     441              :      */
     442           60 :     foreach(queue_item, queue)
     443              :     {
     444           60 :         Oid         this_relid = lfirst_oid(queue_item);
     445              :         ScanKeyData skey;
     446              :         SysScanDesc inhscan;
     447              :         HeapTuple   inhtup;
     448              : 
     449              :         /*
     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).
     454              :          */
     455           60 :         if (list_member_oid(visited, this_relid))
     456            0 :             continue;
     457              : 
     458              :         /*
     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.
     462              :          */
     463           60 :         visited = lappend_oid(visited, this_relid);
     464              : 
     465           60 :         ScanKeyInit(&skey,
     466              :                     Anum_pg_inherits_inhrelid,
     467              :                     BTEqualStrategyNumber, F_OIDEQ,
     468              :                     ObjectIdGetDatum(this_relid));
     469              : 
     470           60 :         inhscan = systable_beginscan(inhrel, InheritsRelidSeqnoIndexId, true,
     471              :                                      NULL, 1, &skey);
     472              : 
     473           66 :         while ((inhtup = systable_getnext(inhscan)) != NULL)
     474              :         {
     475           66 :             Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inhtup);
     476           66 :             Oid         inhparent = inh->inhparent;
     477              : 
     478              :             /* If this is the target superclass, we're done */
     479           66 :             if (inhparent == superclassRelid)
     480              :             {
     481           60 :                 result = true;
     482           60 :                 break;
     483              :             }
     484              : 
     485              :             /* Else add to queue */
     486            6 :             queue = lappend_oid(queue, inhparent);
     487              :         }
     488              : 
     489           60 :         systable_endscan(inhscan);
     490              : 
     491           60 :         if (result)
     492           60 :             break;
     493              :     }
     494              : 
     495              :     /* clean up ... */
     496           60 :     table_close(inhrel, AccessShareLock);
     497              : 
     498           60 :     list_free(visited);
     499           60 :     list_free(queue);
     500              : 
     501           60 :     return result;
     502              : }
     503              : 
     504              : /*
     505              :  * Create a single pg_inherits row with the given data
     506              :  */
     507              : void
     508         9047 : StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
     509              : {
     510              :     Datum       values[Natts_pg_inherits];
     511              :     bool        nulls[Natts_pg_inherits];
     512              :     HeapTuple   tuple;
     513              :     Relation    inhRelation;
     514              : 
     515         9047 :     inhRelation = table_open(InheritsRelationId, RowExclusiveLock);
     516              : 
     517              :     /*
     518              :      * Make the pg_inherits entry
     519              :      */
     520         9047 :     values[Anum_pg_inherits_inhrelid - 1] = ObjectIdGetDatum(relationId);
     521         9047 :     values[Anum_pg_inherits_inhparent - 1] = ObjectIdGetDatum(parentOid);
     522         9047 :     values[Anum_pg_inherits_inhseqno - 1] = Int32GetDatum(seqNumber);
     523         9047 :     values[Anum_pg_inherits_inhdetachpending - 1] = BoolGetDatum(false);
     524              : 
     525         9047 :     memset(nulls, 0, sizeof(nulls));
     526              : 
     527         9047 :     tuple = heap_form_tuple(RelationGetDescr(inhRelation), values, nulls);
     528              : 
     529         9047 :     CatalogTupleInsert(inhRelation, tuple);
     530              : 
     531         9047 :     heap_freetuple(tuple);
     532              : 
     533         9047 :     table_close(inhRelation, RowExclusiveLock);
     534         9047 : }
     535              : 
     536              : /*
     537              :  * DeleteInheritsTuple
     538              :  *
     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.
     542              :  *
     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.
     545              :  *
     546              :  * childname is the partition name, if a table; pass NULL for regular
     547              :  * inheritance or when working with other relation kinds.
     548              :  *
     549              :  * Returns whether at least one row was deleted.
     550              :  */
     551              : bool
     552        13346 : DeleteInheritsTuple(Oid inhrelid, Oid inhparent, bool expect_detach_pending,
     553              :                     const char *childname)
     554              : {
     555        13346 :     bool        found = false;
     556              :     Relation    catalogRelation;
     557              :     ScanKeyData key;
     558              :     SysScanDesc scan;
     559              :     HeapTuple   inheritsTuple;
     560              : 
     561              :     /*
     562              :      * Find pg_inherits entries by inhrelid.
     563              :      */
     564        13346 :     catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
     565        13346 :     ScanKeyInit(&key,
     566              :                 Anum_pg_inherits_inhrelid,
     567              :                 BTEqualStrategyNumber, F_OIDEQ,
     568              :                 ObjectIdGetDatum(inhrelid));
     569        13346 :     scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
     570              :                               true, NULL, 1, &key);
     571              : 
     572        15359 :     while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
     573              :     {
     574              :         Oid         parent;
     575              : 
     576              :         /* Compare inhparent if it was given, and do the actual deletion. */
     577         2013 :         parent = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhparent;
     578         2013 :         if (!OidIsValid(inhparent) || parent == inhparent)
     579              :         {
     580              :             bool        detach_pending;
     581              : 
     582         1986 :             detach_pending =
     583         1986 :                 ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhdetachpending;
     584              : 
     585              :             /*
     586              :              * Raise error depending on state.  This should only happen for
     587              :              * partitions, but we have no way to cross-check.
     588              :              */
     589         1986 :             if (detach_pending && !expect_detach_pending)
     590            0 :                 ereport(ERROR,
     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         1986 :             if (!detach_pending && expect_detach_pending)
     597            0 :                 ereport(ERROR,
     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.")));
     602              : 
     603         1986 :             CatalogTupleDelete(catalogRelation, &inheritsTuple->t_self);
     604         1986 :             found = true;
     605              :         }
     606              :     }
     607              : 
     608              :     /* Done */
     609        13346 :     systable_endscan(scan);
     610        13346 :     table_close(catalogRelation, RowExclusiveLock);
     611              : 
     612        13346 :     return found;
     613              : }
     614              : 
     615              : /*
     616              :  * Return whether the pg_inherits tuple for a partition has the "detach
     617              :  * pending" flag set.
     618              :  */
     619              : bool
     620          694 : PartitionHasPendingDetach(Oid partoid)
     621              : {
     622              :     Relation    catalogRelation;
     623              :     ScanKeyData key;
     624              :     SysScanDesc scan;
     625              :     HeapTuple   inheritsTuple;
     626              : 
     627              :     /* We don't have a good way to verify it is in fact a partition */
     628              : 
     629              :     /*
     630              :      * Find the pg_inherits entry by inhrelid.  (There should only be one.)
     631              :      */
     632          694 :     catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
     633          694 :     ScanKeyInit(&key,
     634              :                 Anum_pg_inherits_inhrelid,
     635              :                 BTEqualStrategyNumber, F_OIDEQ,
     636              :                 ObjectIdGetDatum(partoid));
     637          694 :     scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
     638              :                               true, NULL, 1, &key);
     639              : 
     640          694 :     while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
     641              :     {
     642              :         bool        detached;
     643              : 
     644          694 :         detached =
     645          694 :             ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhdetachpending;
     646              : 
     647              :         /* Done */
     648          694 :         systable_endscan(scan);
     649          694 :         table_close(catalogRelation, RowExclusiveLock);
     650              : 
     651          694 :         return detached;
     652              :     }
     653              : 
     654            0 :     elog(ERROR, "relation %u is not a partition", partoid);
     655              :     return false;               /* keep compiler quiet */
     656              : }
        

Generated by: LCOV version 2.0-1