LCOV - code coverage report
Current view: top level - src/backend/catalog - pg_inherits.c (source / functions) Hit Total Coverage
Test: PostgreSQL 16beta1 Lines: 151 161 93.8 %
Date: 2023-05-30 17:15:13 Functions: 9 9 100.0 %
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-2023, 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/memutils.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       48112 : find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
      60             : {
      61       48112 :     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       67868 : find_inheritance_children_extended(Oid parentrelId, bool omit_detached,
      84             :                                    LOCKMODE lockmode, bool *detached_exist,
      85             :                                    TransactionId *detached_xmin)
      86             : {
      87       67868 :     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       67868 :     if (!has_subclass(parentrelId))
     103       40958 :         return NIL;
     104             : 
     105             :     /*
     106             :      * Scan pg_inherits and build a working array of subclass OIDs.
     107             :      */
     108       26910 :     maxoids = 32;
     109       26910 :     oidarr = (Oid *) palloc(maxoids * sizeof(Oid));
     110       26910 :     numoids = 0;
     111             : 
     112       26910 :     relation = table_open(InheritsRelationId, AccessShareLock);
     113             : 
     114       26910 :     ScanKeyInit(&key[0],
     115             :                 Anum_pg_inherits_inhparent,
     116             :                 BTEqualStrategyNumber, F_OIDEQ,
     117             :                 ObjectIdGetDatum(parentrelId));
     118             : 
     119       26910 :     scan = systable_beginscan(relation, InheritsParentIndexId, true,
     120             :                               NULL, 1, key);
     121             : 
     122       78956 :     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       52046 :         if (((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhdetachpending)
     140             :         {
     141         116 :             if (detached_exist)
     142         112 :                 *detached_exist = true;
     143             : 
     144         116 :             if (omit_detached && ActiveSnapshotSet())
     145             :             {
     146             :                 TransactionId xmin;
     147             :                 Snapshot    snap;
     148             : 
     149          94 :                 xmin = HeapTupleHeaderGetXmin(inheritsTuple->t_data);
     150          94 :                 snap = GetActiveSnapshot();
     151             : 
     152          94 :                 if (!XidInMVCCSnapshot(xmin, snap))
     153             :                 {
     154          70 :                     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          66 :                         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          66 :                             *detached_xmin = xmin;
     174             :                     }
     175             : 
     176             :                     /* Don't add the partition to the output list */
     177          70 :                     continue;
     178             :                 }
     179             :             }
     180             :         }
     181             : 
     182       51976 :         inhrelid = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhrelid;
     183       51976 :         if (numoids >= maxoids)
     184             :         {
     185           0 :             maxoids *= 2;
     186           0 :             oidarr = (Oid *) repalloc(oidarr, maxoids * sizeof(Oid));
     187             :         }
     188       51976 :         oidarr[numoids++] = inhrelid;
     189             :     }
     190             : 
     191       26910 :     systable_endscan(scan);
     192             : 
     193       26910 :     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       26910 :     if (numoids > 1)
     202       15488 :         qsort(oidarr, numoids, sizeof(Oid), oid_cmp);
     203             : 
     204             :     /*
     205             :      * Acquire locks and build the result list.
     206             :      */
     207       78886 :     for (i = 0; i < numoids; i++)
     208             :     {
     209       51976 :         inhrelid = oidarr[i];
     210             : 
     211       51976 :         if (lockmode != NoLock)
     212             :         {
     213             :             /* Get the lock to synchronize against concurrent drop */
     214       14800 :             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       14800 :             if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(inhrelid)))
     222             :             {
     223             :                 /* Release useless lock */
     224           2 :                 UnlockRelationOid(inhrelid, lockmode);
     225             :                 /* And ignore this relation */
     226           2 :                 continue;
     227             :             }
     228             :         }
     229             : 
     230       51974 :         list = lappend_oid(list, inhrelid);
     231             :     }
     232             : 
     233       26910 :     pfree(oidarr);
     234             : 
     235       26910 :     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       22050 : 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       22050 :     ctl.keysize = sizeof(Oid);
     266       22050 :     ctl.entrysize = sizeof(SeenRelsEntry);
     267       22050 :     ctl.hcxt = CurrentMemoryContext;
     268             : 
     269       22050 :     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       22050 :     rels_list = list_make1_oid(parentrelId);
     284       22050 :     rel_numparents = list_make1_int(0);
     285             : 
     286       64494 :     foreach(l, rels_list)
     287             :     {
     288       42444 :         Oid         currentrel = lfirst_oid(l);
     289             :         List       *currentchildren;
     290             :         ListCell   *lc;
     291             : 
     292             :         /* Get the direct children of this rel */
     293       42444 :         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       63274 :         foreach(lc, currentchildren)
     303             :         {
     304       20830 :             Oid         child_oid = lfirst_oid(lc);
     305             :             bool        found;
     306             :             SeenRelsEntry *hash_entry;
     307             : 
     308       20830 :             hash_entry = hash_search(seen_rels, &child_oid, HASH_ENTER, &found);
     309       20830 :             if (found)
     310             :             {
     311             :                 /* if the rel is already there, bump number-of-parents counter */
     312             :                 ListCell   *numparents_cell;
     313             : 
     314         436 :                 numparents_cell = list_nth_cell(rel_numparents,
     315             :                                                 hash_entry->list_index);
     316         436 :                 lfirst_int(numparents_cell)++;
     317             :             }
     318             :             else
     319             :             {
     320             :                 /* if it's not there, add it. expect 1 parent, initially. */
     321       20394 :                 hash_entry->list_index = list_length(rels_list);
     322       20394 :                 rels_list = lappend_oid(rels_list, child_oid);
     323       20394 :                 rel_numparents = lappend_int(rel_numparents, 1);
     324             :             }
     325             :         }
     326             :     }
     327             : 
     328       22050 :     if (numparents)
     329        1010 :         *numparents = rel_numparents;
     330             :     else
     331       21040 :         list_free(rel_numparents);
     332             : 
     333       22050 :     hash_destroy(seen_rels);
     334             : 
     335       22050 :     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      367552 : has_subclass(Oid relationId)
     357             : {
     358             :     HeapTuple   tuple;
     359             :     bool        result;
     360             : 
     361      367552 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationId));
     362      367552 :     if (!HeapTupleIsValid(tuple))
     363           0 :         elog(ERROR, "cache lookup failed for relation %u", relationId);
     364             : 
     365      367552 :     result = ((Form_pg_class) GETSTRUCT(tuple))->relhassubclass;
     366      367552 :     ReleaseSysCache(tuple);
     367      367552 :     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         554 : has_superclass(Oid relationId)
     379             : {
     380             :     Relation    catalog;
     381             :     SysScanDesc scan;
     382             :     ScanKeyData skey;
     383             :     bool        result;
     384             : 
     385         554 :     catalog = table_open(InheritsRelationId, AccessShareLock);
     386         554 :     ScanKeyInit(&skey, Anum_pg_inherits_inhrelid, BTEqualStrategyNumber,
     387             :                 F_OIDEQ, ObjectIdGetDatum(relationId));
     388         554 :     scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
     389             :                               NULL, 1, &skey);
     390         554 :     result = HeapTupleIsValid(systable_getnext(scan));
     391         554 :     systable_endscan(scan);
     392         554 :     table_close(catalog, AccessShareLock);
     393             : 
     394         554 :     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     1215338 : typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId)
     408             : {
     409     1215338 :     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     1215338 :     subclassRelid = typeOrDomainTypeRelid(subclassTypeId);
     419     1215338 :     if (subclassRelid == InvalidOid)
     420     1202992 :         return false;           /* not a complex type or domain over one */
     421       12346 :     superclassRelid = typeidTypeRelid(superclassTypeId);
     422       12346 :     if (superclassRelid == InvalidOid)
     423       12226 :         return false;           /* not a complex type */
     424             : 
     425             :     /* No point in searching if the superclass has no subclasses */
     426         120 :     if (!has_subclass(superclassRelid))
     427          12 :         return false;
     428             : 
     429             :     /*
     430             :      * Begin the search at the relation itself, so add its relid to the queue.
     431             :      */
     432         108 :     queue = list_make1_oid(subclassRelid);
     433         108 :     visited = NIL;
     434             : 
     435         108 :     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         108 :     foreach(queue_item, queue)
     444             :     {
     445         108 :         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         108 :         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         108 :         visited = lappend_oid(visited, this_relid);
     465             : 
     466         108 :         ScanKeyInit(&skey,
     467             :                     Anum_pg_inherits_inhrelid,
     468             :                     BTEqualStrategyNumber, F_OIDEQ,
     469             :                     ObjectIdGetDatum(this_relid));
     470             : 
     471         108 :         inhscan = systable_beginscan(inhrel, InheritsRelidSeqnoIndexId, true,
     472             :                                      NULL, 1, &skey);
     473             : 
     474         120 :         while ((inhtup = systable_getnext(inhscan)) != NULL)
     475             :         {
     476         120 :             Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inhtup);
     477         120 :             Oid         inhparent = inh->inhparent;
     478             : 
     479             :             /* If this is the target superclass, we're done */
     480         120 :             if (inhparent == superclassRelid)
     481             :             {
     482         108 :                 result = true;
     483         108 :                 break;
     484             :             }
     485             : 
     486             :             /* Else add to queue */
     487          12 :             queue = lappend_oid(queue, inhparent);
     488             :         }
     489             : 
     490         108 :         systable_endscan(inhscan);
     491             : 
     492         108 :         if (result)
     493         108 :             break;
     494             :     }
     495             : 
     496             :     /* clean up ... */
     497         108 :     table_close(inhrel, AccessShareLock);
     498             : 
     499         108 :     list_free(visited);
     500         108 :     list_free(queue);
     501             : 
     502         108 :     return result;
     503             : }
     504             : 
     505             : /*
     506             :  * Create a single pg_inherits row with the given data
     507             :  */
     508             : void
     509       13082 : 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       13082 :     inhRelation = table_open(InheritsRelationId, RowExclusiveLock);
     517             : 
     518             :     /*
     519             :      * Make the pg_inherits entry
     520             :      */
     521       13082 :     values[Anum_pg_inherits_inhrelid - 1] = ObjectIdGetDatum(relationId);
     522       13082 :     values[Anum_pg_inherits_inhparent - 1] = ObjectIdGetDatum(parentOid);
     523       13082 :     values[Anum_pg_inherits_inhseqno - 1] = Int32GetDatum(seqNumber);
     524       13082 :     values[Anum_pg_inherits_inhdetachpending - 1] = BoolGetDatum(false);
     525             : 
     526       13082 :     memset(nulls, 0, sizeof(nulls));
     527             : 
     528       13082 :     tuple = heap_form_tuple(RelationGetDescr(inhRelation), values, nulls);
     529             : 
     530       13082 :     CatalogTupleInsert(inhRelation, tuple);
     531             : 
     532       13082 :     heap_freetuple(tuple);
     533             : 
     534       13082 :     table_close(inhRelation, RowExclusiveLock);
     535       13082 : }
     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       21040 : DeleteInheritsTuple(Oid inhrelid, Oid inhparent, bool expect_detach_pending,
     554             :                     const char *childname)
     555             : {
     556       21040 :     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       21040 :     catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
     566       21040 :     ScanKeyInit(&key,
     567             :                 Anum_pg_inherits_inhrelid,
     568             :                 BTEqualStrategyNumber, F_OIDEQ,
     569             :                 ObjectIdGetDatum(inhrelid));
     570       21040 :     scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
     571             :                               true, NULL, 1, &key);
     572             : 
     573       23528 :     while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
     574             :     {
     575             :         Oid         parent;
     576             : 
     577             :         /* Compare inhparent if it was given, and do the actual deletion. */
     578        2488 :         parent = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhparent;
     579        2488 :         if (!OidIsValid(inhparent) || parent == inhparent)
     580             :         {
     581             :             bool        detach_pending;
     582             : 
     583        2464 :             detach_pending =
     584        2464 :                 ((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        2464 :             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        2464 :             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        2464 :             CatalogTupleDelete(catalogRelation, &inheritsTuple->t_self);
     605        2464 :             found = true;
     606             :         }
     607             :     }
     608             : 
     609             :     /* Done */
     610       21040 :     systable_endscan(scan);
     611       21040 :     table_close(catalogRelation, RowExclusiveLock);
     612             : 
     613       21040 :     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        1250 : 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        1250 :     catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
     634        1250 :     ScanKeyInit(&key,
     635             :                 Anum_pg_inherits_inhrelid,
     636             :                 BTEqualStrategyNumber, F_OIDEQ,
     637             :                 ObjectIdGetDatum(partoid));
     638        1250 :     scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
     639             :                               true, NULL, 1, &key);
     640             : 
     641        1250 :     while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
     642             :     {
     643             :         bool        detached;
     644             : 
     645        1250 :         detached =
     646        1250 :             ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhdetachpending;
     647             : 
     648             :         /* Done */
     649        1250 :         systable_endscan(scan);
     650        1250 :         table_close(catalogRelation, RowExclusiveLock);
     651             : 
     652        1250 :         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 1.14