LCOV - code coverage report
Current view: top level - src/backend/catalog - pg_inherits.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 97 103 94.2 %
Date: 2017-09-29 13:40:31 Functions: 5 5 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-2017, 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/heapam.h"
      24             : #include "access/htup_details.h"
      25             : #include "catalog/indexing.h"
      26             : #include "catalog/pg_inherits.h"
      27             : #include "catalog/pg_inherits_fn.h"
      28             : #include "parser/parse_type.h"
      29             : #include "storage/lmgr.h"
      30             : #include "utils/builtins.h"
      31             : #include "utils/fmgroids.h"
      32             : #include "utils/memutils.h"
      33             : #include "utils/syscache.h"
      34             : #include "utils/tqual.h"
      35             : 
      36             : /*
      37             :  * Entry of a hash table used in find_all_inheritors. See below.
      38             :  */
      39             : typedef struct SeenRelsEntry
      40             : {
      41             :     Oid         rel_id;         /* relation oid */
      42             :     ListCell   *numparents_cell;    /* corresponding list cell */
      43             : } SeenRelsEntry;
      44             : 
      45             : /*
      46             :  * find_inheritance_children
      47             :  *
      48             :  * Returns a list containing the OIDs of all relations which
      49             :  * inherit *directly* from the relation with OID 'parentrelId'.
      50             :  *
      51             :  * The specified lock type is acquired on each child relation (but not on the
      52             :  * given rel; caller should already have locked it).  If lockmode is NoLock
      53             :  * then no locks are acquired, but caller must beware of race conditions
      54             :  * against possible DROPs of child relations.
      55             :  */
      56             : List *
      57        3269 : find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
      58             : {
      59        3269 :     List       *list = NIL;
      60             :     Relation    relation;
      61             :     SysScanDesc scan;
      62             :     ScanKeyData key[1];
      63             :     HeapTuple   inheritsTuple;
      64             :     Oid         inhrelid;
      65             :     Oid        *oidarr;
      66             :     int         maxoids,
      67             :                 numoids,
      68             :                 i;
      69             : 
      70             :     /*
      71             :      * Can skip the scan if pg_class shows the relation has never had a
      72             :      * subclass.
      73             :      */
      74        3269 :     if (!has_subclass(parentrelId))
      75        1902 :         return NIL;
      76             : 
      77             :     /*
      78             :      * Scan pg_inherits and build a working array of subclass OIDs.
      79             :      */
      80        1367 :     maxoids = 32;
      81        1367 :     oidarr = (Oid *) palloc(maxoids * sizeof(Oid));
      82        1367 :     numoids = 0;
      83             : 
      84        1367 :     relation = heap_open(InheritsRelationId, AccessShareLock);
      85             : 
      86        1367 :     ScanKeyInit(&key[0],
      87             :                 Anum_pg_inherits_inhparent,
      88             :                 BTEqualStrategyNumber, F_OIDEQ,
      89             :                 ObjectIdGetDatum(parentrelId));
      90             : 
      91        1367 :     scan = systable_beginscan(relation, InheritsParentIndexId, true,
      92             :                               NULL, 1, key);
      93             : 
      94        5133 :     while ((inheritsTuple = systable_getnext(scan)) != NULL)
      95             :     {
      96        2399 :         inhrelid = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhrelid;
      97        2399 :         if (numoids >= maxoids)
      98             :         {
      99           0 :             maxoids *= 2;
     100           0 :             oidarr = (Oid *) repalloc(oidarr, maxoids * sizeof(Oid));
     101             :         }
     102        2399 :         oidarr[numoids++] = inhrelid;
     103             :     }
     104             : 
     105        1367 :     systable_endscan(scan);
     106             : 
     107        1367 :     heap_close(relation, AccessShareLock);
     108             : 
     109             :     /*
     110             :      * If we found more than one child, sort them by OID.  This ensures
     111             :      * reasonably consistent behavior regardless of the vagaries of an
     112             :      * indexscan.  This is important since we need to be sure all backends
     113             :      * lock children in the same order to avoid needless deadlocks.
     114             :      */
     115        1367 :     if (numoids > 1)
     116         637 :         qsort(oidarr, numoids, sizeof(Oid), oid_cmp);
     117             : 
     118             :     /*
     119             :      * Acquire locks and build the result list.
     120             :      */
     121        3766 :     for (i = 0; i < numoids; i++)
     122             :     {
     123        2399 :         inhrelid = oidarr[i];
     124             : 
     125        2399 :         if (lockmode != NoLock)
     126             :         {
     127             :             /* Get the lock to synchronize against concurrent drop */
     128        1488 :             LockRelationOid(inhrelid, lockmode);
     129             : 
     130             :             /*
     131             :              * Now that we have the lock, double-check to see if the relation
     132             :              * really exists or not.  If not, assume it was dropped while we
     133             :              * waited to acquire lock, and ignore it.
     134             :              */
     135        1488 :             if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(inhrelid)))
     136             :             {
     137             :                 /* Release useless lock */
     138           0 :                 UnlockRelationOid(inhrelid, lockmode);
     139             :                 /* And ignore this relation */
     140           0 :                 continue;
     141             :             }
     142             :         }
     143             : 
     144        2399 :         list = lappend_oid(list, inhrelid);
     145             :     }
     146             : 
     147        1367 :     pfree(oidarr);
     148             : 
     149        1367 :     return list;
     150             : }
     151             : 
     152             : 
     153             : /*
     154             :  * find_all_inheritors -
     155             :  *      Returns a list of relation OIDs including the given rel plus
     156             :  *      all relations that inherit from it, directly or indirectly.
     157             :  *      Optionally, it also returns the number of parents found for
     158             :  *      each such relation within the inheritance tree rooted at the
     159             :  *      given rel.
     160             :  *
     161             :  * The specified lock type is acquired on all child relations (but not on the
     162             :  * given rel; caller should already have locked it).  If lockmode is NoLock
     163             :  * then no locks are acquired, but caller must beware of race conditions
     164             :  * against possible DROPs of child relations.
     165             :  */
     166             : List *
     167         812 : find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
     168             : {
     169             :     /* hash table for O(1) rel_oid -> rel_numparents cell lookup */
     170             :     HTAB       *seen_rels;
     171             :     HASHCTL     ctl;
     172             :     List       *rels_list,
     173             :                *rel_numparents;
     174             :     ListCell   *l;
     175             : 
     176         812 :     memset(&ctl, 0, sizeof(ctl));
     177         812 :     ctl.keysize = sizeof(Oid);
     178         812 :     ctl.entrysize = sizeof(SeenRelsEntry);
     179         812 :     ctl.hcxt = CurrentMemoryContext;
     180             : 
     181         812 :     seen_rels = hash_create("find_all_inheritors temporary table",
     182             :                             32, /* start small and extend */
     183             :                             &ctl,
     184             :                             HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
     185             : 
     186             :     /*
     187             :      * We build a list starting with the given rel and adding all direct and
     188             :      * indirect children.  We can use a single list as both the record of
     189             :      * already-found rels and the agenda of rels yet to be scanned for more
     190             :      * children.  This is a bit tricky but works because the foreach() macro
     191             :      * doesn't fetch the next list element until the bottom of the loop.
     192             :      */
     193         812 :     rels_list = list_make1_oid(parentrelId);
     194         812 :     rel_numparents = list_make1_int(0);
     195             : 
     196        2901 :     foreach(l, rels_list)
     197             :     {
     198        2089 :         Oid         currentrel = lfirst_oid(l);
     199             :         List       *currentchildren;
     200             :         ListCell   *lc;
     201             : 
     202             :         /* Get the direct children of this rel */
     203        2089 :         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        3427 :         foreach(lc, currentchildren)
     213             :         {
     214        1338 :             Oid         child_oid = lfirst_oid(lc);
     215             :             bool        found;
     216             :             SeenRelsEntry *hash_entry;
     217             : 
     218        1338 :             hash_entry = hash_search(seen_rels, &child_oid, HASH_ENTER, &found);
     219        1338 :             if (found)
     220             :             {
     221             :                 /* if the rel is already there, bump number-of-parents counter */
     222          61 :                 lfirst_int(hash_entry->numparents_cell)++;
     223             :             }
     224             :             else
     225             :             {
     226             :                 /* if it's not there, add it. expect 1 parent, initially. */
     227        1277 :                 rels_list = lappend_oid(rels_list, child_oid);
     228        1277 :                 rel_numparents = lappend_int(rel_numparents, 1);
     229        1277 :                 hash_entry->numparents_cell = rel_numparents->tail;
     230             :             }
     231             :         }
     232             :     }
     233             : 
     234         812 :     if (numparents)
     235          37 :         *numparents = rel_numparents;
     236             :     else
     237         775 :         list_free(rel_numparents);
     238             : 
     239         812 :     hash_destroy(seen_rels);
     240             : 
     241         812 :     return rels_list;
     242             : }
     243             : 
     244             : 
     245             : /*
     246             :  * has_subclass - does this relation have any children?
     247             :  *
     248             :  * In the current implementation, has_subclass returns whether a
     249             :  * particular class *might* have a subclass. It will not return the
     250             :  * correct result if a class had a subclass which was later dropped.
     251             :  * This is because relhassubclass in pg_class is not updated immediately
     252             :  * when a subclass is dropped, primarily because of concurrency concerns.
     253             :  *
     254             :  * Currently has_subclass is only used as an efficiency hack to skip
     255             :  * unnecessary inheritance searches, so this is OK.  Note that ANALYZE
     256             :  * on a childless table will clean up the obsolete relhassubclass flag.
     257             :  *
     258             :  * Although this doesn't actually touch pg_inherits, it seems reasonable
     259             :  * to keep it here since it's normally used with the other routines here.
     260             :  */
     261             : bool
     262       18054 : has_subclass(Oid relationId)
     263             : {
     264             :     HeapTuple   tuple;
     265             :     bool        result;
     266             : 
     267       18054 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationId));
     268       18054 :     if (!HeapTupleIsValid(tuple))
     269           0 :         elog(ERROR, "cache lookup failed for relation %u", relationId);
     270             : 
     271       18054 :     result = ((Form_pg_class) GETSTRUCT(tuple))->relhassubclass;
     272       18054 :     ReleaseSysCache(tuple);
     273       18054 :     return result;
     274             : }
     275             : 
     276             : /*
     277             :  * has_superclass - does this relation inherit from another?  The caller
     278             :  * should hold a lock on the given relation so that it can't be concurrently
     279             :  * added to or removed from an inheritance hierarchy.
     280             :  */
     281             : bool
     282           4 : has_superclass(Oid relationId)
     283             : {
     284             :     Relation    catalog;
     285             :     SysScanDesc scan;
     286             :     ScanKeyData skey;
     287             :     bool        result;
     288             : 
     289           4 :     catalog = heap_open(InheritsRelationId, AccessShareLock);
     290           4 :     ScanKeyInit(&skey, Anum_pg_inherits_inhrelid, BTEqualStrategyNumber,
     291             :                 F_OIDEQ, ObjectIdGetDatum(relationId));
     292           4 :     scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
     293             :                               NULL, 1, &skey);
     294           4 :     result = HeapTupleIsValid(systable_getnext(scan));
     295           4 :     systable_endscan(scan);
     296           4 :     heap_close(catalog, AccessShareLock);
     297             : 
     298           4 :     return result;
     299             : }
     300             : 
     301             : /*
     302             :  * Given two type OIDs, determine whether the first is a complex type
     303             :  * (class type) that inherits from the second.
     304             :  */
     305             : bool
     306       27202 : typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId)
     307             : {
     308       27202 :     bool        result = false;
     309             :     Oid         subclassRelid;
     310             :     Oid         superclassRelid;
     311             :     Relation    inhrel;
     312             :     List       *visited,
     313             :                *queue;
     314             :     ListCell   *queue_item;
     315             : 
     316             :     /* We need to work with the associated relation OIDs */
     317       27202 :     subclassRelid = typeidTypeRelid(subclassTypeId);
     318       27202 :     if (subclassRelid == InvalidOid)
     319       26589 :         return false;           /* not a complex type */
     320         613 :     superclassRelid = typeidTypeRelid(superclassTypeId);
     321         613 :     if (superclassRelid == InvalidOid)
     322         601 :         return false;           /* not a complex type */
     323             : 
     324             :     /* No point in searching if the superclass has no subclasses */
     325          12 :     if (!has_subclass(superclassRelid))
     326           2 :         return false;
     327             : 
     328             :     /*
     329             :      * Begin the search at the relation itself, so add its relid to the queue.
     330             :      */
     331          10 :     queue = list_make1_oid(subclassRelid);
     332          10 :     visited = NIL;
     333             : 
     334          10 :     inhrel = heap_open(InheritsRelationId, AccessShareLock);
     335             : 
     336             :     /*
     337             :      * Use queue to do a breadth-first traversal of the inheritance graph from
     338             :      * the relid supplied up to the root.  Notice that we append to the queue
     339             :      * inside the loop --- this is okay because the foreach() macro doesn't
     340             :      * advance queue_item until the next loop iteration begins.
     341             :      */
     342          10 :     foreach(queue_item, queue)
     343             :     {
     344          10 :         Oid         this_relid = lfirst_oid(queue_item);
     345             :         ScanKeyData skey;
     346             :         SysScanDesc inhscan;
     347             :         HeapTuple   inhtup;
     348             : 
     349             :         /*
     350             :          * If we've seen this relid already, skip it.  This avoids extra work
     351             :          * in multiple-inheritance scenarios, and also protects us from an
     352             :          * infinite loop in case there is a cycle in pg_inherits (though
     353             :          * theoretically that shouldn't happen).
     354             :          */
     355          10 :         if (list_member_oid(visited, this_relid))
     356           0 :             continue;
     357             : 
     358             :         /*
     359             :          * Okay, this is a not-yet-seen relid. Add it to the list of
     360             :          * already-visited OIDs, then find all the types this relid inherits
     361             :          * from and add them to the queue.
     362             :          */
     363          10 :         visited = lappend_oid(visited, this_relid);
     364             : 
     365          10 :         ScanKeyInit(&skey,
     366             :                     Anum_pg_inherits_inhrelid,
     367             :                     BTEqualStrategyNumber, F_OIDEQ,
     368             :                     ObjectIdGetDatum(this_relid));
     369             : 
     370          10 :         inhscan = systable_beginscan(inhrel, InheritsRelidSeqnoIndexId, true,
     371             :                                      NULL, 1, &skey);
     372             : 
     373          22 :         while ((inhtup = systable_getnext(inhscan)) != NULL)
     374             :         {
     375          12 :             Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inhtup);
     376          12 :             Oid         inhparent = inh->inhparent;
     377             : 
     378             :             /* If this is the target superclass, we're done */
     379          12 :             if (inhparent == superclassRelid)
     380             :             {
     381          10 :                 result = true;
     382          10 :                 break;
     383             :             }
     384             : 
     385             :             /* Else add to queue */
     386           2 :             queue = lappend_oid(queue, inhparent);
     387             :         }
     388             : 
     389          10 :         systable_endscan(inhscan);
     390             : 
     391          10 :         if (result)
     392          10 :             break;
     393             :     }
     394             : 
     395             :     /* clean up ... */
     396          10 :     heap_close(inhrel, AccessShareLock);
     397             : 
     398          10 :     list_free(visited);
     399          10 :     list_free(queue);
     400             : 
     401          10 :     return result;
     402             : }

Generated by: LCOV version 1.11