LCOV - code coverage report
Current view: top level - src/backend/catalog - pg_inherits.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 87.4 % 238 208
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 9 9
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 50.4 % 117 59

             Branch data     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                 :        8211 : find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
      59                 :             : {
      60                 :        8211 :         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                 :       11459 : find_inheritance_children_extended(Oid parentrelId, bool omit_detached,
      83                 :             :                                                                    LOCKMODE lockmode, bool *detached_exist,
      84                 :             :                                                                    TransactionId *detached_xmin)
      85                 :             : {
      86                 :       11459 :         List       *list = NIL;
      87                 :       11459 :         Relation        relation;
      88                 :       11459 :         SysScanDesc scan;
      89                 :       11459 :         ScanKeyData key[1];
      90                 :       11459 :         HeapTuple       inheritsTuple;
      91                 :       11459 :         Oid                     inhrelid;
      92                 :       11459 :         Oid                *oidarr;
      93                 :       11459 :         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         [ +  + ]:       11459 :         if (!has_subclass(parentrelId))
     102                 :        6882 :                 return NIL;
     103                 :             : 
     104                 :             :         /*
     105                 :             :          * Scan pg_inherits and build a working array of subclass OIDs.
     106                 :             :          */
     107                 :        4577 :         maxoids = 32;
     108                 :        4577 :         oidarr = (Oid *) palloc(maxoids * sizeof(Oid));
     109                 :        4577 :         numoids = 0;
     110                 :             : 
     111                 :        4577 :         relation = table_open(InheritsRelationId, AccessShareLock);
     112                 :             : 
     113                 :        9154 :         ScanKeyInit(&key[0],
     114                 :             :                                 Anum_pg_inherits_inhparent,
     115                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
     116                 :        4577 :                                 ObjectIdGetDatum(parentrelId));
     117                 :             : 
     118                 :        9154 :         scan = systable_beginscan(relation, InheritsParentIndexId, true,
     119                 :        4577 :                                                           NULL, 1, key);
     120                 :             : 
     121         [ +  + ]:       13407 :         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         [ +  - ]:        8830 :                 if (((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhdetachpending)
     139                 :             :                 {
     140         [ #  # ]:           0 :                         if (detached_exist)
     141                 :           0 :                                 *detached_exist = true;
     142                 :             : 
     143   [ #  #  #  # ]:           0 :                         if (omit_detached && ActiveSnapshotSet())
     144                 :             :                         {
     145                 :           0 :                                 TransactionId xmin;
     146                 :           0 :                                 Snapshot        snap;
     147                 :             : 
     148                 :           0 :                                 xmin = HeapTupleHeaderGetXmin(inheritsTuple->t_data);
     149                 :           0 :                                 snap = GetActiveSnapshot();
     150                 :             : 
     151         [ #  # ]:           0 :                                 if (!XidInMVCCSnapshot(xmin, snap))
     152                 :             :                                 {
     153         [ #  # ]:           0 :                                         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         [ #  # ]:           0 :                                                 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                 :           0 :                                                 }
     171                 :             :                                                 else
     172                 :           0 :                                                         *detached_xmin = xmin;
     173                 :           0 :                                         }
     174                 :             : 
     175                 :             :                                         /* Don't add the partition to the output list */
     176                 :           0 :                                         continue;
     177                 :             :                                 }
     178      [ #  #  # ]:           0 :                         }
     179                 :           0 :                 }
     180                 :             : 
     181                 :        8830 :                 inhrelid = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhrelid;
     182         [ +  - ]:        8830 :                 if (numoids >= maxoids)
     183                 :             :                 {
     184                 :           0 :                         maxoids *= 2;
     185                 :           0 :                         oidarr = (Oid *) repalloc(oidarr, maxoids * sizeof(Oid));
     186                 :           0 :                 }
     187                 :        8830 :                 oidarr[numoids++] = inhrelid;
     188                 :             :         }
     189                 :             : 
     190                 :        4577 :         systable_endscan(scan);
     191                 :             : 
     192                 :        4577 :         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         [ +  + ]:        4577 :         if (numoids > 1)
     201                 :        2574 :                 qsort(oidarr, numoids, sizeof(Oid), oid_cmp);
     202                 :             : 
     203                 :             :         /*
     204                 :             :          * Acquire locks and build the result list.
     205                 :             :          */
     206         [ +  + ]:       13407 :         for (i = 0; i < numoids; i++)
     207                 :             :         {
     208                 :        8830 :                 inhrelid = oidarr[i];
     209                 :             : 
     210         [ +  + ]:        8830 :                 if (lockmode != NoLock)
     211                 :             :                 {
     212                 :             :                         /* Get the lock to synchronize against concurrent drop */
     213                 :        3108 :                         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         [ +  - ]:        3108 :                         if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(inhrelid)))
     221                 :             :                         {
     222                 :             :                                 /* Release useless lock */
     223                 :           0 :                                 UnlockRelationOid(inhrelid, lockmode);
     224                 :             :                                 /* And ignore this relation */
     225                 :           0 :                                 continue;
     226                 :             :                         }
     227                 :        3108 :                 }
     228                 :             : 
     229                 :        8830 :                 list = lappend_oid(list, inhrelid);
     230                 :        8830 :         }
     231                 :             : 
     232                 :        4577 :         pfree(oidarr);
     233                 :             : 
     234                 :        4577 :         return list;
     235                 :       11459 : }
     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                 :        3524 : find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
     256                 :             : {
     257                 :             :         /* hash table for O(1) rel_oid -> rel_numparents cell lookup */
     258                 :        3524 :         HTAB       *seen_rels;
     259                 :        3524 :         HASHCTL         ctl;
     260                 :        3524 :         List       *rels_list,
     261                 :             :                            *rel_numparents;
     262                 :        3524 :         ListCell   *l;
     263                 :             : 
     264                 :        3524 :         ctl.keysize = sizeof(Oid);
     265                 :        3524 :         ctl.entrysize = sizeof(SeenRelsEntry);
     266                 :        3524 :         ctl.hcxt = CurrentMemoryContext;
     267                 :             : 
     268                 :        3524 :         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                 :        3524 :         rels_list = list_make1_oid(parentrelId);
     283                 :        3524 :         rel_numparents = list_make1_int(0);
     284                 :             : 
     285   [ +  -  +  +  :       10296 :         foreach(l, rels_list)
                   +  + ]
     286                 :             :         {
     287                 :        6772 :                 Oid                     currentrel = lfirst_oid(l);
     288                 :        6772 :                 List       *currentchildren;
     289                 :        6772 :                 ListCell   *lc;
     290                 :             : 
     291                 :             :                 /* Get the direct children of this rel */
     292                 :        6772 :                 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   [ +  +  +  +  :       10091 :                 foreach(lc, currentchildren)
                   +  + ]
     302                 :             :                 {
     303                 :        3319 :                         Oid                     child_oid = lfirst_oid(lc);
     304                 :        3319 :                         bool            found;
     305                 :        3319 :                         SeenRelsEntry *hash_entry;
     306                 :             : 
     307                 :        3319 :                         hash_entry = hash_search(seen_rels, &child_oid, HASH_ENTER, &found);
     308         [ +  + ]:        3319 :                         if (found)
     309                 :             :                         {
     310                 :             :                                 /* if the rel is already there, bump number-of-parents counter */
     311                 :          71 :                                 ListCell   *numparents_cell;
     312                 :             : 
     313                 :         142 :                                 numparents_cell = list_nth_cell(rel_numparents,
     314                 :          71 :                                                                                                 hash_entry->list_index);
     315                 :          71 :                                 lfirst_int(numparents_cell)++;
     316                 :          71 :                         }
     317                 :             :                         else
     318                 :             :                         {
     319                 :             :                                 /* if it's not there, add it. expect 1 parent, initially. */
     320                 :        3248 :                                 hash_entry->list_index = list_length(rels_list);
     321                 :        3248 :                                 rels_list = lappend_oid(rels_list, child_oid);
     322                 :        3248 :                                 rel_numparents = lappend_int(rel_numparents, 1);
     323                 :             :                         }
     324                 :        3319 :                 }
     325                 :        6772 :         }
     326                 :             : 
     327         [ +  + ]:        3524 :         if (numparents)
     328                 :         200 :                 *numparents = rel_numparents;
     329                 :             :         else
     330                 :        3324 :                 list_free(rel_numparents);
     331                 :             : 
     332                 :        3524 :         hash_destroy(seen_rels);
     333                 :             : 
     334                 :        7048 :         return rels_list;
     335                 :        3524 : }
     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                 :       11481 : has_subclass(Oid relationId)
     356                 :             : {
     357                 :       11481 :         HeapTuple       tuple;
     358                 :       11481 :         bool            result;
     359                 :             : 
     360                 :       11481 :         tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationId));
     361         [ +  - ]:       11481 :         if (!HeapTupleIsValid(tuple))
     362   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for relation %u", relationId);
     363                 :             : 
     364                 :       11481 :         result = ((Form_pg_class) GETSTRUCT(tuple))->relhassubclass;
     365                 :       11481 :         ReleaseSysCache(tuple);
     366                 :       22962 :         return result;
     367                 :       11481 : }
     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                 :         142 : has_superclass(Oid relationId)
     378                 :             : {
     379                 :         142 :         Relation        catalog;
     380                 :         142 :         SysScanDesc scan;
     381                 :         142 :         ScanKeyData skey;
     382                 :         142 :         bool            result;
     383                 :             : 
     384                 :         142 :         catalog = table_open(InheritsRelationId, AccessShareLock);
     385                 :         142 :         ScanKeyInit(&skey, Anum_pg_inherits_inhrelid, BTEqualStrategyNumber,
     386                 :         142 :                                 F_OIDEQ, ObjectIdGetDatum(relationId));
     387                 :         142 :         scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
     388                 :             :                                                           NULL, 1, &skey);
     389                 :         142 :         result = HeapTupleIsValid(systable_getnext(scan));
     390                 :         142 :         systable_endscan(scan);
     391                 :         142 :         table_close(catalog, AccessShareLock);
     392                 :             : 
     393                 :         284 :         return result;
     394                 :         142 : }
     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                 :       56295 : typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId)
     407                 :             : {
     408                 :       56295 :         bool            result = false;
     409                 :       56295 :         Oid                     subclassRelid;
     410                 :       56295 :         Oid                     superclassRelid;
     411                 :       56295 :         Relation        inhrel;
     412                 :       56295 :         List       *visited,
     413                 :             :                            *queue;
     414                 :       56295 :         ListCell   *queue_item;
     415                 :             : 
     416                 :             :         /* We need to work with the associated relation OIDs */
     417                 :       56295 :         subclassRelid = typeOrDomainTypeRelid(subclassTypeId);
     418         [ +  + ]:       56295 :         if (subclassRelid == InvalidOid)
     419                 :       54149 :                 return false;                   /* not a complex type or domain over one */
     420                 :        2146 :         superclassRelid = typeidTypeRelid(superclassTypeId);
     421         [ +  + ]:        2146 :         if (superclassRelid == InvalidOid)
     422                 :        2124 :                 return false;                   /* not a complex type */
     423                 :             : 
     424                 :             :         /* No point in searching if the superclass has no subclasses */
     425         [ +  + ]:          22 :         if (!has_subclass(superclassRelid))
     426                 :           2 :                 return false;
     427                 :             : 
     428                 :             :         /*
     429                 :             :          * Begin the search at the relation itself, so add its relid to the queue.
     430                 :             :          */
     431                 :          20 :         queue = list_make1_oid(subclassRelid);
     432                 :          20 :         visited = NIL;
     433                 :             : 
     434                 :          20 :         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   [ +  -  -  +  :          40 :         foreach(queue_item, queue)
                   -  + ]
     443                 :             :         {
     444                 :          20 :                 Oid                     this_relid = lfirst_oid(queue_item);
     445                 :          20 :                 ScanKeyData skey;
     446                 :          20 :                 SysScanDesc inhscan;
     447                 :          20 :                 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         [ -  + ]:          20 :                 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                 :          20 :                 visited = lappend_oid(visited, this_relid);
     464                 :             : 
     465                 :          20 :                 ScanKeyInit(&skey,
     466                 :             :                                         Anum_pg_inherits_inhrelid,
     467                 :             :                                         BTEqualStrategyNumber, F_OIDEQ,
     468                 :          20 :                                         ObjectIdGetDatum(this_relid));
     469                 :             : 
     470                 :          20 :                 inhscan = systable_beginscan(inhrel, InheritsRelidSeqnoIndexId, true,
     471                 :             :                                                                          NULL, 1, &skey);
     472                 :             : 
     473         [ -  + ]:          22 :                 while ((inhtup = systable_getnext(inhscan)) != NULL)
     474                 :             :                 {
     475                 :          22 :                         Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inhtup);
     476                 :          22 :                         Oid                     inhparent = inh->inhparent;
     477                 :             : 
     478                 :             :                         /* If this is the target superclass, we're done */
     479         [ +  + ]:          22 :                         if (inhparent == superclassRelid)
     480                 :             :                         {
     481                 :          20 :                                 result = true;
     482                 :          20 :                                 break;
     483                 :             :                         }
     484                 :             : 
     485                 :             :                         /* Else add to queue */
     486                 :           2 :                         queue = lappend_oid(queue, inhparent);
     487      [ -  +  + ]:          22 :                 }
     488                 :             : 
     489                 :          20 :                 systable_endscan(inhscan);
     490                 :             : 
     491         [ +  - ]:          20 :                 if (result)
     492                 :          20 :                         break;
     493      [ -  +  - ]:          20 :         }
     494                 :             : 
     495                 :             :         /* clean up ... */
     496                 :          20 :         table_close(inhrel, AccessShareLock);
     497                 :             : 
     498                 :          20 :         list_free(visited);
     499                 :          20 :         list_free(queue);
     500                 :             : 
     501                 :          20 :         return result;
     502                 :       56295 : }
     503                 :             : 
     504                 :             : /*
     505                 :             :  * Create a single pg_inherits row with the given data
     506                 :             :  */
     507                 :             : void
     508                 :        2397 : StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
     509                 :             : {
     510                 :        2397 :         Datum           values[Natts_pg_inherits];
     511                 :        2397 :         bool            nulls[Natts_pg_inherits];
     512                 :        2397 :         HeapTuple       tuple;
     513                 :        2397 :         Relation        inhRelation;
     514                 :             : 
     515                 :        2397 :         inhRelation = table_open(InheritsRelationId, RowExclusiveLock);
     516                 :             : 
     517                 :             :         /*
     518                 :             :          * Make the pg_inherits entry
     519                 :             :          */
     520                 :        2397 :         values[Anum_pg_inherits_inhrelid - 1] = ObjectIdGetDatum(relationId);
     521                 :        2397 :         values[Anum_pg_inherits_inhparent - 1] = ObjectIdGetDatum(parentOid);
     522                 :        2397 :         values[Anum_pg_inherits_inhseqno - 1] = Int32GetDatum(seqNumber);
     523                 :        2397 :         values[Anum_pg_inherits_inhdetachpending - 1] = BoolGetDatum(false);
     524                 :             : 
     525                 :        2397 :         memset(nulls, 0, sizeof(nulls));
     526                 :             : 
     527                 :        2397 :         tuple = heap_form_tuple(RelationGetDescr(inhRelation), values, nulls);
     528                 :             : 
     529                 :        2397 :         CatalogTupleInsert(inhRelation, tuple);
     530                 :             : 
     531                 :        2397 :         heap_freetuple(tuple);
     532                 :             : 
     533                 :        2397 :         table_close(inhRelation, RowExclusiveLock);
     534                 :        2397 : }
     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                 :        2929 : DeleteInheritsTuple(Oid inhrelid, Oid inhparent, bool expect_detach_pending,
     553                 :             :                                         const char *childname)
     554                 :             : {
     555                 :        2929 :         bool            found = false;
     556                 :        2929 :         Relation        catalogRelation;
     557                 :        2929 :         ScanKeyData key;
     558                 :        2929 :         SysScanDesc scan;
     559                 :        2929 :         HeapTuple       inheritsTuple;
     560                 :             : 
     561                 :             :         /*
     562                 :             :          * Find pg_inherits entries by inhrelid.
     563                 :             :          */
     564                 :        2929 :         catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
     565                 :        2929 :         ScanKeyInit(&key,
     566                 :             :                                 Anum_pg_inherits_inhrelid,
     567                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
     568                 :        2929 :                                 ObjectIdGetDatum(inhrelid));
     569                 :        2929 :         scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
     570                 :             :                                                           true, NULL, 1, &key);
     571                 :             : 
     572         [ +  + ]:        3512 :         while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
     573                 :             :         {
     574                 :         583 :                 Oid                     parent;
     575                 :             : 
     576                 :             :                 /* Compare inhparent if it was given, and do the actual deletion. */
     577                 :         583 :                 parent = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhparent;
     578   [ +  +  +  + ]:         583 :                 if (!OidIsValid(inhparent) || parent == inhparent)
     579                 :             :                 {
     580                 :         574 :                         bool            detach_pending;
     581                 :             : 
     582                 :         574 :                         detach_pending =
     583                 :         574 :                                 ((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   [ +  +  +  - ]:         574 :                         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   [ +  +  +  - ]:         574 :                         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                 :         574 :                         CatalogTupleDelete(catalogRelation, &inheritsTuple->t_self);
     604                 :         574 :                         found = true;
     605                 :         574 :                 }
     606                 :         583 :         }
     607                 :             : 
     608                 :             :         /* Done */
     609                 :        2929 :         systable_endscan(scan);
     610                 :        2929 :         table_close(catalogRelation, RowExclusiveLock);
     611                 :             : 
     612                 :        5858 :         return found;
     613                 :        2929 : }
     614                 :             : 
     615                 :             : /*
     616                 :             :  * Return whether the pg_inherits tuple for a partition has the "detach
     617                 :             :  * pending" flag set.
     618                 :             :  */
     619                 :             : bool
     620                 :         164 : PartitionHasPendingDetach(Oid partoid)
     621                 :             : {
     622                 :         164 :         Relation        catalogRelation;
     623                 :         164 :         ScanKeyData key;
     624                 :         164 :         SysScanDesc scan;
     625                 :         164 :         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                 :         164 :         catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
     633                 :         164 :         ScanKeyInit(&key,
     634                 :             :                                 Anum_pg_inherits_inhrelid,
     635                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
     636                 :         164 :                                 ObjectIdGetDatum(partoid));
     637                 :         164 :         scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
     638                 :             :                                                           true, NULL, 1, &key);
     639                 :             : 
     640         [ +  - ]:         164 :         while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
     641                 :             :         {
     642                 :         164 :                 bool            detached;
     643                 :             : 
     644                 :         164 :                 detached =
     645                 :         164 :                         ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhdetachpending;
     646                 :             : 
     647                 :             :                 /* Done */
     648                 :         164 :                 systable_endscan(scan);
     649                 :         164 :                 table_close(catalogRelation, RowExclusiveLock);
     650                 :             : 
     651                 :         164 :                 return detached;
     652                 :         164 :         }
     653                 :             : 
     654   [ #  #  #  # ]:           0 :         elog(ERROR, "relation %u is not a partition", partoid);
     655                 :           0 :         return false;                           /* keep compiler quiet */
     656                 :         164 : }
        

Generated by: LCOV version 2.3.2-1