LCOV - code coverage report
Current view: top level - src/backend/catalog - pg_inherits.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 124 128 96.9 %
Date: 2019-11-22 07:06:56 Functions: 7 7 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 only contains inquiry functions; the actual
       7             :  * creation and deletion of pg_inherits entries is 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-2019, 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/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             : List *
      55       44384 : find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
      56             : {
      57       44384 :     List       *list = NIL;
      58             :     Relation    relation;
      59             :     SysScanDesc scan;
      60             :     ScanKeyData key[1];
      61             :     HeapTuple   inheritsTuple;
      62             :     Oid         inhrelid;
      63             :     Oid        *oidarr;
      64             :     int         maxoids,
      65             :                 numoids,
      66             :                 i;
      67             : 
      68             :     /*
      69             :      * Can skip the scan if pg_class shows the relation has never had a
      70             :      * subclass.
      71             :      */
      72       44384 :     if (!has_subclass(parentrelId))
      73       25294 :         return NIL;
      74             : 
      75             :     /*
      76             :      * Scan pg_inherits and build a working array of subclass OIDs.
      77             :      */
      78       19090 :     maxoids = 32;
      79       19090 :     oidarr = (Oid *) palloc(maxoids * sizeof(Oid));
      80       19090 :     numoids = 0;
      81             : 
      82       19090 :     relation = table_open(InheritsRelationId, AccessShareLock);
      83             : 
      84       19090 :     ScanKeyInit(&key[0],
      85             :                 Anum_pg_inherits_inhparent,
      86             :                 BTEqualStrategyNumber, F_OIDEQ,
      87             :                 ObjectIdGetDatum(parentrelId));
      88             : 
      89       19090 :     scan = systable_beginscan(relation, InheritsParentIndexId, true,
      90             :                               NULL, 1, key);
      91             : 
      92       71020 :     while ((inheritsTuple = systable_getnext(scan)) != NULL)
      93             :     {
      94       32840 :         inhrelid = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhrelid;
      95       32840 :         if (numoids >= maxoids)
      96             :         {
      97           0 :             maxoids *= 2;
      98           0 :             oidarr = (Oid *) repalloc(oidarr, maxoids * sizeof(Oid));
      99             :         }
     100       32840 :         oidarr[numoids++] = inhrelid;
     101             :     }
     102             : 
     103       19090 :     systable_endscan(scan);
     104             : 
     105       19090 :     table_close(relation, AccessShareLock);
     106             : 
     107             :     /*
     108             :      * If we found more than one child, sort them by OID.  This ensures
     109             :      * reasonably consistent behavior regardless of the vagaries of an
     110             :      * indexscan.  This is important since we need to be sure all backends
     111             :      * lock children in the same order to avoid needless deadlocks.
     112             :      */
     113       19090 :     if (numoids > 1)
     114        9168 :         qsort(oidarr, numoids, sizeof(Oid), oid_cmp);
     115             : 
     116             :     /*
     117             :      * Acquire locks and build the result list.
     118             :      */
     119       51930 :     for (i = 0; i < numoids; i++)
     120             :     {
     121       32840 :         inhrelid = oidarr[i];
     122             : 
     123       32840 :         if (lockmode != NoLock)
     124             :         {
     125             :             /* Get the lock to synchronize against concurrent drop */
     126        8682 :             LockRelationOid(inhrelid, lockmode);
     127             : 
     128             :             /*
     129             :              * Now that we have the lock, double-check to see if the relation
     130             :              * really exists or not.  If not, assume it was dropped while we
     131             :              * waited to acquire lock, and ignore it.
     132             :              */
     133        8682 :             if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(inhrelid)))
     134             :             {
     135             :                 /* Release useless lock */
     136           2 :                 UnlockRelationOid(inhrelid, lockmode);
     137             :                 /* And ignore this relation */
     138           2 :                 continue;
     139             :             }
     140             :         }
     141             : 
     142       32838 :         list = lappend_oid(list, inhrelid);
     143             :     }
     144             : 
     145       19090 :     pfree(oidarr);
     146             : 
     147       19090 :     return list;
     148             : }
     149             : 
     150             : 
     151             : /*
     152             :  * find_all_inheritors -
     153             :  *      Returns a list of relation OIDs including the given rel plus
     154             :  *      all relations that inherit from it, directly or indirectly.
     155             :  *      Optionally, it also returns the number of parents found for
     156             :  *      each such relation within the inheritance tree rooted at the
     157             :  *      given rel.
     158             :  *
     159             :  * The specified lock type is acquired on all child relations (but not on the
     160             :  * given rel; caller should already have locked it).  If lockmode is NoLock
     161             :  * then no locks are acquired, but caller must beware of race conditions
     162             :  * against possible DROPs of child relations.
     163             :  */
     164             : List *
     165        7074 : find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
     166             : {
     167             :     /* hash table for O(1) rel_oid -> rel_numparents cell lookup */
     168             :     HTAB       *seen_rels;
     169             :     HASHCTL     ctl;
     170             :     List       *rels_list,
     171             :                *rel_numparents;
     172             :     ListCell   *l;
     173             : 
     174        7074 :     memset(&ctl, 0, sizeof(ctl));
     175        7074 :     ctl.keysize = sizeof(Oid);
     176        7074 :     ctl.entrysize = sizeof(SeenRelsEntry);
     177        7074 :     ctl.hcxt = CurrentMemoryContext;
     178             : 
     179        7074 :     seen_rels = hash_create("find_all_inheritors temporary table",
     180             :                             32, /* start small and extend */
     181             :                             &ctl,
     182             :                             HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
     183             : 
     184             :     /*
     185             :      * We build a list starting with the given rel and adding all direct and
     186             :      * indirect children.  We can use a single list as both the record of
     187             :      * already-found rels and the agenda of rels yet to be scanned for more
     188             :      * children.  This is a bit tricky but works because the foreach() macro
     189             :      * doesn't fetch the next list element until the bottom of the loop.  Note
     190             :      * that we can't keep pointers into the output lists; but an index is
     191             :      * sufficient.
     192             :      */
     193        7074 :     rels_list = list_make1_oid(parentrelId);
     194        7074 :     rel_numparents = list_make1_int(0);
     195             : 
     196       21944 :     foreach(l, rels_list)
     197             :     {
     198       14870 :         Oid         currentrel = lfirst_oid(l);
     199             :         List       *currentchildren;
     200             :         ListCell   *lc;
     201             : 
     202             :         /* Get the direct children of this rel */
     203       14870 :         currentchildren = find_inheritance_children(currentrel, lockmode);
     204             : 
     205             :         /*
     206             :          * Add to the queue only those children not already seen. This avoids
     207             :          * making duplicate entries in case of multiple inheritance paths from
     208             :          * the same parent.  (It'll also keep us from getting into an infinite
     209             :          * loop, though theoretically there can't be any cycles in the
     210             :          * inheritance graph anyway.)
     211             :          */
     212       22962 :         foreach(lc, currentchildren)
     213             :         {
     214        8092 :             Oid         child_oid = lfirst_oid(lc);
     215             :             bool        found;
     216             :             SeenRelsEntry *hash_entry;
     217             : 
     218        8092 :             hash_entry = hash_search(seen_rels, &child_oid, HASH_ENTER, &found);
     219        8092 :             if (found)
     220             :             {
     221             :                 /* if the rel is already there, bump number-of-parents counter */
     222             :                 ListCell   *numparents_cell;
     223             : 
     224         296 :                 numparents_cell = list_nth_cell(rel_numparents,
     225             :                                                 hash_entry->list_index);
     226         296 :                 lfirst_int(numparents_cell)++;
     227             :             }
     228             :             else
     229             :             {
     230             :                 /* if it's not there, add it. expect 1 parent, initially. */
     231        7796 :                 hash_entry->list_index = list_length(rels_list);
     232        7796 :                 rels_list = lappend_oid(rels_list, child_oid);
     233        7796 :                 rel_numparents = lappend_int(rel_numparents, 1);
     234             :             }
     235             :         }
     236             :     }
     237             : 
     238        7074 :     if (numparents)
     239         558 :         *numparents = rel_numparents;
     240             :     else
     241        6516 :         list_free(rel_numparents);
     242             : 
     243        7074 :     hash_destroy(seen_rels);
     244             : 
     245        7074 :     return rels_list;
     246             : }
     247             : 
     248             : 
     249             : /*
     250             :  * has_subclass - does this relation have any children?
     251             :  *
     252             :  * In the current implementation, has_subclass returns whether a
     253             :  * particular class *might* have a subclass. It will not return the
     254             :  * correct result if a class had a subclass which was later dropped.
     255             :  * This is because relhassubclass in pg_class is not updated immediately
     256             :  * when a subclass is dropped, primarily because of concurrency concerns.
     257             :  *
     258             :  * Currently has_subclass is only used as an efficiency hack to skip
     259             :  * unnecessary inheritance searches, so this is OK.  Note that ANALYZE
     260             :  * on a childless table will clean up the obsolete relhassubclass flag.
     261             :  *
     262             :  * Although this doesn't actually touch pg_inherits, it seems reasonable
     263             :  * to keep it here since it's normally used with the other routines here.
     264             :  */
     265             : bool
     266      261176 : has_subclass(Oid relationId)
     267             : {
     268             :     HeapTuple   tuple;
     269             :     bool        result;
     270             : 
     271      261176 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationId));
     272      261176 :     if (!HeapTupleIsValid(tuple))
     273           0 :         elog(ERROR, "cache lookup failed for relation %u", relationId);
     274             : 
     275      261176 :     result = ((Form_pg_class) GETSTRUCT(tuple))->relhassubclass;
     276      261176 :     ReleaseSysCache(tuple);
     277      261176 :     return result;
     278             : }
     279             : 
     280             : /*
     281             :  * has_superclass - does this relation inherit from another?  The caller
     282             :  * should hold a lock on the given relation so that it can't be concurrently
     283             :  * added to or removed from an inheritance hierarchy.
     284             :  */
     285             : bool
     286         298 : has_superclass(Oid relationId)
     287             : {
     288             :     Relation    catalog;
     289             :     SysScanDesc scan;
     290             :     ScanKeyData skey;
     291             :     bool        result;
     292             : 
     293         298 :     catalog = table_open(InheritsRelationId, AccessShareLock);
     294         298 :     ScanKeyInit(&skey, Anum_pg_inherits_inhrelid, BTEqualStrategyNumber,
     295             :                 F_OIDEQ, ObjectIdGetDatum(relationId));
     296         298 :     scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
     297             :                               NULL, 1, &skey);
     298         298 :     result = HeapTupleIsValid(systable_getnext(scan));
     299         298 :     systable_endscan(scan);
     300         298 :     table_close(catalog, AccessShareLock);
     301             : 
     302         298 :     return result;
     303             : }
     304             : 
     305             : /*
     306             :  * Given two type OIDs, determine whether the first is a complex type
     307             :  * (class type) that inherits from the second.
     308             :  *
     309             :  * This essentially asks whether the first type is guaranteed to be coercible
     310             :  * to the second.  Therefore, we allow the first type to be a domain over a
     311             :  * complex type that inherits from the second; that creates no difficulties.
     312             :  * But the second type cannot be a domain.
     313             :  */
     314             : bool
     315      627132 : typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId)
     316             : {
     317      627132 :     bool        result = false;
     318             :     Oid         subclassRelid;
     319             :     Oid         superclassRelid;
     320             :     Relation    inhrel;
     321             :     List       *visited,
     322             :                *queue;
     323             :     ListCell   *queue_item;
     324             : 
     325             :     /* We need to work with the associated relation OIDs */
     326      627132 :     subclassRelid = typeOrDomainTypeRelid(subclassTypeId);
     327      627132 :     if (subclassRelid == InvalidOid)
     328      620336 :         return false;           /* not a complex type or domain over one */
     329        6796 :     superclassRelid = typeidTypeRelid(superclassTypeId);
     330        6796 :     if (superclassRelid == InvalidOid)
     331        6716 :         return false;           /* not a complex type */
     332             : 
     333             :     /* No point in searching if the superclass has no subclasses */
     334          80 :     if (!has_subclass(superclassRelid))
     335           8 :         return false;
     336             : 
     337             :     /*
     338             :      * Begin the search at the relation itself, so add its relid to the queue.
     339             :      */
     340          72 :     queue = list_make1_oid(subclassRelid);
     341          72 :     visited = NIL;
     342             : 
     343          72 :     inhrel = table_open(InheritsRelationId, AccessShareLock);
     344             : 
     345             :     /*
     346             :      * Use queue to do a breadth-first traversal of the inheritance graph from
     347             :      * the relid supplied up to the root.  Notice that we append to the queue
     348             :      * inside the loop --- this is okay because the foreach() macro doesn't
     349             :      * advance queue_item until the next loop iteration begins.
     350             :      */
     351          72 :     foreach(queue_item, queue)
     352             :     {
     353          72 :         Oid         this_relid = lfirst_oid(queue_item);
     354             :         ScanKeyData skey;
     355             :         SysScanDesc inhscan;
     356             :         HeapTuple   inhtup;
     357             : 
     358             :         /*
     359             :          * If we've seen this relid already, skip it.  This avoids extra work
     360             :          * in multiple-inheritance scenarios, and also protects us from an
     361             :          * infinite loop in case there is a cycle in pg_inherits (though
     362             :          * theoretically that shouldn't happen).
     363             :          */
     364          72 :         if (list_member_oid(visited, this_relid))
     365           0 :             continue;
     366             : 
     367             :         /*
     368             :          * Okay, this is a not-yet-seen relid. Add it to the list of
     369             :          * already-visited OIDs, then find all the types this relid inherits
     370             :          * from and add them to the queue.
     371             :          */
     372          72 :         visited = lappend_oid(visited, this_relid);
     373             : 
     374          72 :         ScanKeyInit(&skey,
     375             :                     Anum_pg_inherits_inhrelid,
     376             :                     BTEqualStrategyNumber, F_OIDEQ,
     377             :                     ObjectIdGetDatum(this_relid));
     378             : 
     379          72 :         inhscan = systable_beginscan(inhrel, InheritsRelidSeqnoIndexId, true,
     380             :                                      NULL, 1, &skey);
     381             : 
     382         152 :         while ((inhtup = systable_getnext(inhscan)) != NULL)
     383             :         {
     384          80 :             Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inhtup);
     385          80 :             Oid         inhparent = inh->inhparent;
     386             : 
     387             :             /* If this is the target superclass, we're done */
     388          80 :             if (inhparent == superclassRelid)
     389             :             {
     390          72 :                 result = true;
     391          72 :                 break;
     392             :             }
     393             : 
     394             :             /* Else add to queue */
     395           8 :             queue = lappend_oid(queue, inhparent);
     396             :         }
     397             : 
     398          72 :         systable_endscan(inhscan);
     399             : 
     400          72 :         if (result)
     401          72 :             break;
     402             :     }
     403             : 
     404             :     /* clean up ... */
     405          72 :     table_close(inhrel, AccessShareLock);
     406             : 
     407          72 :     list_free(visited);
     408          72 :     list_free(queue);
     409             : 
     410          72 :     return result;
     411             : }
     412             : 
     413             : /*
     414             :  * Create a single pg_inherits row with the given data
     415             :  */
     416             : void
     417        6382 : StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
     418             : {
     419             :     Datum       values[Natts_pg_inherits];
     420             :     bool        nulls[Natts_pg_inherits];
     421             :     HeapTuple   tuple;
     422             :     Relation    inhRelation;
     423             : 
     424        6382 :     inhRelation = table_open(InheritsRelationId, RowExclusiveLock);
     425             : 
     426             :     /*
     427             :      * Make the pg_inherits entry
     428             :      */
     429        6382 :     values[Anum_pg_inherits_inhrelid - 1] = ObjectIdGetDatum(relationId);
     430        6382 :     values[Anum_pg_inherits_inhparent - 1] = ObjectIdGetDatum(parentOid);
     431        6382 :     values[Anum_pg_inherits_inhseqno - 1] = Int32GetDatum(seqNumber);
     432             : 
     433        6382 :     memset(nulls, 0, sizeof(nulls));
     434             : 
     435        6382 :     tuple = heap_form_tuple(RelationGetDescr(inhRelation), values, nulls);
     436             : 
     437        6382 :     CatalogTupleInsert(inhRelation, tuple);
     438             : 
     439        6382 :     heap_freetuple(tuple);
     440             : 
     441        6382 :     table_close(inhRelation, RowExclusiveLock);
     442        6382 : }
     443             : 
     444             : /*
     445             :  * DeleteInheritsTuple
     446             :  *
     447             :  * Delete pg_inherits tuples with the given inhrelid.  inhparent may be given
     448             :  * as InvalidOid, in which case all tuples matching inhrelid are deleted;
     449             :  * otherwise only delete tuples with the specified inhparent.
     450             :  *
     451             :  * Returns whether at least one row was deleted.
     452             :  */
     453             : bool
     454       12540 : DeleteInheritsTuple(Oid inhrelid, Oid inhparent)
     455             : {
     456       12540 :     bool        found = false;
     457             :     Relation    catalogRelation;
     458             :     ScanKeyData key;
     459             :     SysScanDesc scan;
     460             :     HeapTuple   inheritsTuple;
     461             : 
     462             :     /*
     463             :      * Find pg_inherits entries by inhrelid.
     464             :      */
     465       12540 :     catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
     466       12540 :     ScanKeyInit(&key,
     467             :                 Anum_pg_inherits_inhrelid,
     468             :                 BTEqualStrategyNumber, F_OIDEQ,
     469             :                 ObjectIdGetDatum(inhrelid));
     470       12540 :     scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
     471             :                               true, NULL, 1, &key);
     472             : 
     473       26054 :     while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
     474             :     {
     475             :         Oid         parent;
     476             : 
     477             :         /* Compare inhparent if it was given, and do the actual deletion. */
     478         974 :         parent = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhparent;
     479         974 :         if (!OidIsValid(inhparent) || parent == inhparent)
     480             :         {
     481         958 :             CatalogTupleDelete(catalogRelation, &inheritsTuple->t_self);
     482         958 :             found = true;
     483             :         }
     484             :     }
     485             : 
     486             :     /* Done */
     487       12540 :     systable_endscan(scan);
     488       12540 :     table_close(catalogRelation, RowExclusiveLock);
     489             : 
     490       12540 :     return found;
     491             : }

Generated by: LCOV version 1.13