1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-21 00:42:43 +03:00
Files
postgres/src/backend/catalog/pg_inherits.c
Robert Haas e26c539e9f Wrap calls to SearchSysCache and related functions using macros.
The purpose of this change is to eliminate the need for every caller
of SearchSysCache, SearchSysCacheCopy, SearchSysCacheExists,
GetSysCacheOid, and SearchSysCacheList to know the maximum number
of allowable keys for a syscache entry (currently 4).  This will
make it far easier to increase the maximum number of keys in a
future release should we choose to do so, and it makes the code
shorter, too.

Design and review by Tom Lane.
2010-02-14 18:42:19 +00:00

373 lines
10 KiB
C

/*-------------------------------------------------------------------------
*
* pg_inherits.c
* routines to support manipulation of the pg_inherits relation
*
* Note: currently, this module only contains inquiry functions; the actual
* creation and deletion of pg_inherits entries is done in tablecmds.c.
* Perhaps someday that code should be moved here, but it'd have to be
* disentangled from other stuff such as pg_depend updates.
*
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_inherits.c,v 1.7 2010/02/14 18:42:13 rhaas Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "catalog/indexing.h"
#include "catalog/pg_class.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_inherits_fn.h"
#include "parser/parse_type.h"
#include "storage/lmgr.h"
#include "utils/fmgroids.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
static int oid_cmp(const void *p1, const void *p2);
/*
* find_inheritance_children
*
* Returns a list containing the OIDs of all relations which
* inherit *directly* from the relation with OID 'parentrelId'.
*
* The specified lock type is acquired on each child relation (but not on the
* given rel; caller should already have locked it). If lockmode is NoLock
* then no locks are acquired, but caller must beware of race conditions
* against possible DROPs of child relations.
*/
List *
find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
{
List *list = NIL;
Relation relation;
SysScanDesc scan;
ScanKeyData key[1];
HeapTuple inheritsTuple;
Oid inhrelid;
Oid *oidarr;
int maxoids,
numoids,
i;
/*
* Can skip the scan if pg_class shows the relation has never had a
* subclass.
*/
if (!has_subclass(parentrelId))
return NIL;
/*
* Scan pg_inherits and build a working array of subclass OIDs.
*/
maxoids = 32;
oidarr = (Oid *) palloc(maxoids * sizeof(Oid));
numoids = 0;
relation = heap_open(InheritsRelationId, AccessShareLock);
ScanKeyInit(&key[0],
Anum_pg_inherits_inhparent,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(parentrelId));
scan = systable_beginscan(relation, InheritsParentIndexId, true,
SnapshotNow, 1, key);
while ((inheritsTuple = systable_getnext(scan)) != NULL)
{
inhrelid = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhrelid;
if (numoids >= maxoids)
{
maxoids *= 2;
oidarr = (Oid *) repalloc(oidarr, maxoids * sizeof(Oid));
}
oidarr[numoids++] = inhrelid;
}
systable_endscan(scan);
heap_close(relation, AccessShareLock);
/*
* If we found more than one child, sort them by OID. This ensures
* reasonably consistent behavior regardless of the vagaries of an
* indexscan. This is important since we need to be sure all backends
* lock children in the same order to avoid needless deadlocks.
*/
if (numoids > 1)
qsort(oidarr, numoids, sizeof(Oid), oid_cmp);
/*
* Acquire locks and build the result list.
*/
for (i = 0; i < numoids; i++)
{
inhrelid = oidarr[i];
if (lockmode != NoLock)
{
/* Get the lock to synchronize against concurrent drop */
LockRelationOid(inhrelid, lockmode);
/*
* Now that we have the lock, double-check to see if the relation
* really exists or not. If not, assume it was dropped while we
* waited to acquire lock, and ignore it.
*/
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(inhrelid)))
{
/* Release useless lock */
UnlockRelationOid(inhrelid, lockmode);
/* And ignore this relation */
continue;
}
}
list = lappend_oid(list, inhrelid);
}
pfree(oidarr);
return list;
}
/*
* find_all_inheritors -
* Returns a list of relation OIDs including the given rel plus
* all relations that inherit from it, directly or indirectly.
* Optionally, it also returns the number of parents found for
* each such relation within the inheritance tree rooted at the
* given rel.
*
* The specified lock type is acquired on all child relations (but not on the
* given rel; caller should already have locked it). If lockmode is NoLock
* then no locks are acquired, but caller must beware of race conditions
* against possible DROPs of child relations.
*/
List *
find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
{
List *rels_list, *rel_numparents;
ListCell *l;
/*
* We build a list starting with the given rel and adding all direct and
* indirect children. We can use a single list as both the record of
* already-found rels and the agenda of rels yet to be scanned for more
* children. This is a bit tricky but works because the foreach() macro
* doesn't fetch the next list element until the bottom of the loop.
*/
rels_list = list_make1_oid(parentrelId);
rel_numparents = list_make1_int(0);
foreach(l, rels_list)
{
Oid currentrel = lfirst_oid(l);
List *currentchildren;
ListCell *lc;
/* Get the direct children of this rel */
currentchildren = find_inheritance_children(currentrel, lockmode);
/*
* Add to the queue only those children not already seen. This avoids
* making duplicate entries in case of multiple inheritance paths from
* the same parent. (It'll also keep us from getting into an infinite
* loop, though theoretically there can't be any cycles in the
* inheritance graph anyway.)
*/
foreach(lc, currentchildren)
{
Oid child_oid = lfirst_oid(lc);
bool found = false;
ListCell *lo;
ListCell *li;
/* if the rel is already there, bump number-of-parents counter */
forboth(lo, rels_list, li, rel_numparents)
{
if (lfirst_oid(lo) == child_oid)
{
lfirst_int(li)++;
found = true;
break;
}
}
/* if it's not there, add it. expect 1 parent, initially. */
if (!found)
{
rels_list = lappend_oid(rels_list, child_oid);
rel_numparents = lappend_int(rel_numparents, 1);
}
}
}
if (numparents)
*numparents = rel_numparents;
else
list_free(rel_numparents);
return rels_list;
}
/*
* has_subclass - does this relation have any children?
*
* In the current implementation, has_subclass returns whether a
* particular class *might* have a subclass. It will not return the
* correct result if a class had a subclass which was later dropped.
* This is because relhassubclass in pg_class is not updated when a
* subclass is dropped, primarily because of concurrency concerns.
*
* Currently has_subclass is only used as an efficiency hack to skip
* unnecessary inheritance searches, so this is OK.
*
* Although this doesn't actually touch pg_inherits, it seems reasonable
* to keep it here since it's normally used with the other routines here.
*/
bool
has_subclass(Oid relationId)
{
HeapTuple tuple;
bool result;
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationId));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for relation %u", relationId);
result = ((Form_pg_class) GETSTRUCT(tuple))->relhassubclass;
ReleaseSysCache(tuple);
return result;
}
/*
* Given two type OIDs, determine whether the first is a complex type
* (class type) that inherits from the second.
*/
bool
typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId)
{
bool result = false;
Oid subclassRelid;
Oid superclassRelid;
Relation inhrel;
List *visited,
*queue;
ListCell *queue_item;
/* We need to work with the associated relation OIDs */
subclassRelid = typeidTypeRelid(subclassTypeId);
if (subclassRelid == InvalidOid)
return false; /* not a complex type */
superclassRelid = typeidTypeRelid(superclassTypeId);
if (superclassRelid == InvalidOid)
return false; /* not a complex type */
/* No point in searching if the superclass has no subclasses */
if (!has_subclass(superclassRelid))
return false;
/*
* Begin the search at the relation itself, so add its relid to the queue.
*/
queue = list_make1_oid(subclassRelid);
visited = NIL;
inhrel = heap_open(InheritsRelationId, AccessShareLock);
/*
* Use queue to do a breadth-first traversal of the inheritance graph from
* the relid supplied up to the root. Notice that we append to the queue
* inside the loop --- this is okay because the foreach() macro doesn't
* advance queue_item until the next loop iteration begins.
*/
foreach(queue_item, queue)
{
Oid this_relid = lfirst_oid(queue_item);
ScanKeyData skey;
SysScanDesc inhscan;
HeapTuple inhtup;
/*
* If we've seen this relid already, skip it. This avoids extra work
* in multiple-inheritance scenarios, and also protects us from an
* infinite loop in case there is a cycle in pg_inherits (though
* theoretically that shouldn't happen).
*/
if (list_member_oid(visited, this_relid))
continue;
/*
* Okay, this is a not-yet-seen relid. Add it to the list of
* already-visited OIDs, then find all the types this relid inherits
* from and add them to the queue.
*/
visited = lappend_oid(visited, this_relid);
ScanKeyInit(&skey,
Anum_pg_inherits_inhrelid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(this_relid));
inhscan = systable_beginscan(inhrel, InheritsRelidSeqnoIndexId, true,
SnapshotNow, 1, &skey);
while ((inhtup = systable_getnext(inhscan)) != NULL)
{
Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inhtup);
Oid inhparent = inh->inhparent;
/* If this is the target superclass, we're done */
if (inhparent == superclassRelid)
{
result = true;
break;
}
/* Else add to queue */
queue = lappend_oid(queue, inhparent);
}
systable_endscan(inhscan);
if (result)
break;
}
/* clean up ... */
heap_close(inhrel, AccessShareLock);
list_free(visited);
list_free(queue);
return result;
}
/* qsort comparison function */
static int
oid_cmp(const void *p1, const void *p2)
{
Oid v1 = *((const Oid *) p1);
Oid v2 = *((const Oid *) p2);
if (v1 < v2)
return -1;
if (v1 > v2)
return 1;
return 0;
}