2009-05-12 02:56:05 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
2019-01-02 18:44:25 +01:00
|
|
|
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
|
2009-05-12 02:56:05 +02:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/catalog/pg_inherits.c
|
2009-05-12 02:56:05 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
2009-12-29 23:00:14 +01:00
|
|
|
#include "access/genam.h"
|
2009-05-12 02:56:05 +02:00
|
|
|
#include "access/heapam.h"
|
2012-08-30 22:15:44 +02:00
|
|
|
#include "access/htup_details.h"
|
2009-12-29 23:00:14 +01:00
|
|
|
#include "catalog/indexing.h"
|
2009-05-12 02:56:05 +02:00
|
|
|
#include "catalog/pg_inherits.h"
|
|
|
|
#include "parser/parse_type.h"
|
2009-05-12 05:11:02 +02:00
|
|
|
#include "storage/lmgr.h"
|
2017-03-01 17:55:28 +01:00
|
|
|
#include "utils/builtins.h"
|
2009-05-12 02:56:05 +02:00
|
|
|
#include "utils/fmgroids.h"
|
2017-05-17 01:33:31 +02:00
|
|
|
#include "utils/memutils.h"
|
2009-05-12 02:56:05 +02:00
|
|
|
#include "utils/syscache.h"
|
|
|
|
#include "utils/tqual.h"
|
|
|
|
|
2017-03-27 18:07:48 +02:00
|
|
|
/*
|
|
|
|
* Entry of a hash table used in find_all_inheritors. See below.
|
|
|
|
*/
|
|
|
|
typedef struct SeenRelsEntry
|
|
|
|
{
|
2017-05-17 22:31:56 +02:00
|
|
|
Oid rel_id; /* relation oid */
|
|
|
|
ListCell *numparents_cell; /* corresponding list cell */
|
2017-03-27 18:07:48 +02:00
|
|
|
} SeenRelsEntry;
|
2009-05-12 02:56:05 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* find_inheritance_children
|
|
|
|
*
|
|
|
|
* Returns a list containing the OIDs of all relations which
|
|
|
|
* inherit *directly* from the relation with OID 'parentrelId'.
|
2009-05-12 05:11:02 +02:00
|
|
|
*
|
|
|
|
* 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.
|
2009-05-12 02:56:05 +02:00
|
|
|
*/
|
|
|
|
List *
|
2009-05-12 05:11:02 +02:00
|
|
|
find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
|
2009-05-12 02:56:05 +02:00
|
|
|
{
|
|
|
|
List *list = NIL;
|
|
|
|
Relation relation;
|
2009-12-29 23:00:14 +01:00
|
|
|
SysScanDesc scan;
|
2009-05-12 02:56:05 +02:00
|
|
|
ScanKeyData key[1];
|
|
|
|
HeapTuple inheritsTuple;
|
|
|
|
Oid inhrelid;
|
2009-12-29 23:00:14 +01:00
|
|
|
Oid *oidarr;
|
|
|
|
int maxoids,
|
|
|
|
numoids,
|
|
|
|
i;
|
2009-05-12 02:56:05 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Can skip the scan if pg_class shows the relation has never had a
|
|
|
|
* subclass.
|
|
|
|
*/
|
|
|
|
if (!has_subclass(parentrelId))
|
|
|
|
return NIL;
|
|
|
|
|
|
|
|
/*
|
2009-12-29 23:00:14 +01:00
|
|
|
* Scan pg_inherits and build a working array of subclass OIDs.
|
2009-05-12 02:56:05 +02:00
|
|
|
*/
|
2009-12-29 23:00:14 +01:00
|
|
|
maxoids = 32;
|
|
|
|
oidarr = (Oid *) palloc(maxoids * sizeof(Oid));
|
|
|
|
numoids = 0;
|
|
|
|
|
2009-05-12 02:56:05 +02:00
|
|
|
relation = heap_open(InheritsRelationId, AccessShareLock);
|
2009-12-29 23:00:14 +01:00
|
|
|
|
2009-05-12 02:56:05 +02:00
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_inherits_inhparent,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(parentrelId));
|
2009-05-12 05:11:02 +02:00
|
|
|
|
2009-12-29 23:00:14 +01:00
|
|
|
scan = systable_beginscan(relation, InheritsParentIndexId, true,
|
Use an MVCC snapshot, rather than SnapshotNow, for catalog scans.
SnapshotNow scans have the undesirable property that, in the face of
concurrent updates, the scan can fail to see either the old or the new
versions of the row. In many cases, we work around this by requiring
DDL operations to hold AccessExclusiveLock on the object being
modified; in some cases, the existing locking is inadequate and random
failures occur as a result. This commit doesn't change anything
related to locking, but will hopefully pave the way to allowing lock
strength reductions in the future.
The major issue has held us back from making this change in the past
is that taking an MVCC snapshot is significantly more expensive than
using a static special snapshot such as SnapshotNow. However, testing
of various worst-case scenarios reveals that this problem is not
severe except under fairly extreme workloads. To mitigate those
problems, we avoid retaking the MVCC snapshot for each new scan;
instead, we take a new snapshot only when invalidation messages have
been processed. The catcache machinery already requires that
invalidation messages be sent before releasing the related heavyweight
lock; else other backends might rely on locally-cached data rather
than scanning the catalog at all. Thus, making snapshot reuse
dependent on the same guarantees shouldn't break anything that wasn't
already subtly broken.
Patch by me. Review by Michael Paquier and Andres Freund.
2013-07-02 15:47:01 +02:00
|
|
|
NULL, 1, key);
|
2009-12-29 23:00:14 +01:00
|
|
|
|
|
|
|
while ((inheritsTuple = systable_getnext(scan)) != NULL)
|
2009-05-12 02:56:05 +02:00
|
|
|
{
|
|
|
|
inhrelid = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhrelid;
|
2009-12-29 23:00:14 +01:00
|
|
|
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];
|
2009-05-12 05:11:02 +02:00
|
|
|
|
|
|
|
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
|
2009-06-11 16:49:15 +02:00
|
|
|
* really exists or not. If not, assume it was dropped while we
|
|
|
|
* waited to acquire lock, and ignore it.
|
2009-05-12 05:11:02 +02:00
|
|
|
*/
|
2010-02-14 19:42:19 +01:00
|
|
|
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(inhrelid)))
|
2009-05-12 05:11:02 +02:00
|
|
|
{
|
|
|
|
/* Release useless lock */
|
|
|
|
UnlockRelationOid(inhrelid, lockmode);
|
|
|
|
/* And ignore this relation */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-12 02:56:05 +02:00
|
|
|
list = lappend_oid(list, inhrelid);
|
|
|
|
}
|
2009-05-12 05:11:02 +02:00
|
|
|
|
2009-12-29 23:00:14 +01:00
|
|
|
pfree(oidarr);
|
2009-05-12 05:11:02 +02:00
|
|
|
|
2009-05-12 02:56:05 +02:00
|
|
|
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.
|
2010-02-01 20:28:56 +01:00
|
|
|
* Optionally, it also returns the number of parents found for
|
|
|
|
* each such relation within the inheritance tree rooted at the
|
|
|
|
* given rel.
|
2009-05-12 05:11:02 +02:00
|
|
|
*
|
|
|
|
* 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.
|
2009-05-12 02:56:05 +02:00
|
|
|
*/
|
|
|
|
List *
|
2010-02-01 20:28:56 +01:00
|
|
|
find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
|
2009-05-12 02:56:05 +02:00
|
|
|
{
|
2017-03-27 18:07:48 +02:00
|
|
|
/* hash table for O(1) rel_oid -> rel_numparents cell lookup */
|
2017-05-17 22:31:56 +02:00
|
|
|
HTAB *seen_rels;
|
|
|
|
HASHCTL ctl;
|
2010-02-26 03:01:40 +01:00
|
|
|
List *rels_list,
|
|
|
|
*rel_numparents;
|
2009-05-12 02:56:05 +02:00
|
|
|
ListCell *l;
|
|
|
|
|
2017-03-27 18:07:48 +02:00
|
|
|
memset(&ctl, 0, sizeof(ctl));
|
|
|
|
ctl.keysize = sizeof(Oid);
|
|
|
|
ctl.entrysize = sizeof(SeenRelsEntry);
|
2017-05-17 01:33:31 +02:00
|
|
|
ctl.hcxt = CurrentMemoryContext;
|
2017-03-27 18:07:48 +02:00
|
|
|
|
2017-05-17 01:33:31 +02:00
|
|
|
seen_rels = hash_create("find_all_inheritors temporary table",
|
|
|
|
32, /* start small and extend */
|
|
|
|
&ctl,
|
|
|
|
HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
|
2017-03-27 18:07:48 +02:00
|
|
|
|
2009-05-12 02:56:05 +02:00
|
|
|
/*
|
|
|
|
* 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);
|
2010-02-01 20:28:56 +01:00
|
|
|
rel_numparents = list_make1_int(0);
|
2009-05-12 02:56:05 +02:00
|
|
|
|
|
|
|
foreach(l, rels_list)
|
|
|
|
{
|
|
|
|
Oid currentrel = lfirst_oid(l);
|
|
|
|
List *currentchildren;
|
2010-02-01 20:28:56 +01:00
|
|
|
ListCell *lc;
|
2009-05-12 02:56:05 +02:00
|
|
|
|
|
|
|
/* Get the direct children of this rel */
|
2009-05-12 05:11:02 +02:00
|
|
|
currentchildren = find_inheritance_children(currentrel, lockmode);
|
2009-05-12 02:56:05 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.)
|
|
|
|
*/
|
2010-02-01 20:28:56 +01:00
|
|
|
foreach(lc, currentchildren)
|
|
|
|
{
|
2010-02-26 03:01:40 +01:00
|
|
|
Oid child_oid = lfirst_oid(lc);
|
2017-05-17 22:31:56 +02:00
|
|
|
bool found;
|
|
|
|
SeenRelsEntry *hash_entry;
|
2010-02-01 20:28:56 +01:00
|
|
|
|
2017-03-27 18:07:48 +02:00
|
|
|
hash_entry = hash_search(seen_rels, &child_oid, HASH_ENTER, &found);
|
|
|
|
if (found)
|
2010-02-01 20:28:56 +01:00
|
|
|
{
|
2017-03-27 18:07:48 +02:00
|
|
|
/* if the rel is already there, bump number-of-parents counter */
|
|
|
|
lfirst_int(hash_entry->numparents_cell)++;
|
2010-02-01 20:28:56 +01:00
|
|
|
}
|
2017-03-27 18:07:48 +02:00
|
|
|
else
|
2010-02-01 20:28:56 +01:00
|
|
|
{
|
2017-03-27 18:07:48 +02:00
|
|
|
/* if it's not there, add it. expect 1 parent, initially. */
|
2010-02-01 20:28:56 +01:00
|
|
|
rels_list = lappend_oid(rels_list, child_oid);
|
|
|
|
rel_numparents = lappend_int(rel_numparents, 1);
|
2017-03-27 18:07:48 +02:00
|
|
|
hash_entry->numparents_cell = rel_numparents->tail;
|
2010-02-01 20:28:56 +01:00
|
|
|
}
|
|
|
|
}
|
2009-05-12 02:56:05 +02:00
|
|
|
}
|
|
|
|
|
2010-02-01 20:28:56 +01:00
|
|
|
if (numparents)
|
|
|
|
*numparents = rel_numparents;
|
|
|
|
else
|
|
|
|
list_free(rel_numparents);
|
2017-03-27 18:07:48 +02:00
|
|
|
|
|
|
|
hash_destroy(seen_rels);
|
|
|
|
|
2009-05-12 02:56:05 +02:00
|
|
|
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.
|
2011-09-02 20:29:31 +02:00
|
|
|
* This is because relhassubclass in pg_class is not updated immediately
|
|
|
|
* when a subclass is dropped, primarily because of concurrency concerns.
|
2009-05-12 02:56:05 +02:00
|
|
|
*
|
|
|
|
* Currently has_subclass is only used as an efficiency hack to skip
|
2011-09-02 20:29:31 +02:00
|
|
|
* unnecessary inheritance searches, so this is OK. Note that ANALYZE
|
|
|
|
* on a childless table will clean up the obsolete relhassubclass flag.
|
2009-05-12 02:56:05 +02:00
|
|
|
*
|
|
|
|
* 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;
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationId));
|
2009-05-12 02:56:05 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
elog(ERROR, "cache lookup failed for relation %u", relationId);
|
|
|
|
|
|
|
|
result = ((Form_pg_class) GETSTRUCT(tuple))->relhassubclass;
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-06-28 19:55:03 +02:00
|
|
|
/*
|
|
|
|
* has_superclass - does this relation inherit from another? The caller
|
|
|
|
* should hold a lock on the given relation so that it can't be concurrently
|
|
|
|
* added to or removed from an inheritance hierarchy.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
has_superclass(Oid relationId)
|
|
|
|
{
|
|
|
|
Relation catalog;
|
|
|
|
SysScanDesc scan;
|
|
|
|
ScanKeyData skey;
|
|
|
|
bool result;
|
|
|
|
|
|
|
|
catalog = heap_open(InheritsRelationId, AccessShareLock);
|
|
|
|
ScanKeyInit(&skey, Anum_pg_inherits_inhrelid, BTEqualStrategyNumber,
|
|
|
|
F_OIDEQ, ObjectIdGetDatum(relationId));
|
|
|
|
scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
|
|
|
|
NULL, 1, &skey);
|
|
|
|
result = HeapTupleIsValid(systable_getnext(scan));
|
|
|
|
systable_endscan(scan);
|
|
|
|
heap_close(catalog, AccessShareLock);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2009-05-12 02:56:05 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Given two type OIDs, determine whether the first is a complex type
|
|
|
|
* (class type) that inherits from the second.
|
2017-10-26 19:47:45 +02:00
|
|
|
*
|
|
|
|
* This essentially asks whether the first type is guaranteed to be coercible
|
|
|
|
* to the second. Therefore, we allow the first type to be a domain over a
|
|
|
|
* complex type that inherits from the second; that creates no difficulties.
|
|
|
|
* But the second type cannot be a domain.
|
2009-05-12 02:56:05 +02:00
|
|
|
*/
|
|
|
|
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 */
|
2017-10-26 19:47:45 +02:00
|
|
|
subclassRelid = typeOrDomainTypeRelid(subclassTypeId);
|
2009-05-12 02:56:05 +02:00
|
|
|
if (subclassRelid == InvalidOid)
|
2017-10-26 19:47:45 +02:00
|
|
|
return false; /* not a complex type or domain over one */
|
2009-05-12 02:56:05 +02:00
|
|
|
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;
|
2009-12-29 23:00:14 +01:00
|
|
|
SysScanDesc inhscan;
|
2009-05-12 02:56:05 +02:00
|
|
|
HeapTuple inhtup;
|
|
|
|
|
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* 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).
|
2009-05-12 02:56:05 +02:00
|
|
|
*/
|
|
|
|
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));
|
|
|
|
|
2009-12-29 23:00:14 +01:00
|
|
|
inhscan = systable_beginscan(inhrel, InheritsRelidSeqnoIndexId, true,
|
Use an MVCC snapshot, rather than SnapshotNow, for catalog scans.
SnapshotNow scans have the undesirable property that, in the face of
concurrent updates, the scan can fail to see either the old or the new
versions of the row. In many cases, we work around this by requiring
DDL operations to hold AccessExclusiveLock on the object being
modified; in some cases, the existing locking is inadequate and random
failures occur as a result. This commit doesn't change anything
related to locking, but will hopefully pave the way to allowing lock
strength reductions in the future.
The major issue has held us back from making this change in the past
is that taking an MVCC snapshot is significantly more expensive than
using a static special snapshot such as SnapshotNow. However, testing
of various worst-case scenarios reveals that this problem is not
severe except under fairly extreme workloads. To mitigate those
problems, we avoid retaking the MVCC snapshot for each new scan;
instead, we take a new snapshot only when invalidation messages have
been processed. The catcache machinery already requires that
invalidation messages be sent before releasing the related heavyweight
lock; else other backends might rely on locally-cached data rather
than scanning the catalog at all. Thus, making snapshot reuse
dependent on the same guarantees shouldn't break anything that wasn't
already subtly broken.
Patch by me. Review by Michael Paquier and Andres Freund.
2013-07-02 15:47:01 +02:00
|
|
|
NULL, 1, &skey);
|
2009-05-12 02:56:05 +02:00
|
|
|
|
2009-12-29 23:00:14 +01:00
|
|
|
while ((inhtup = systable_getnext(inhscan)) != NULL)
|
2009-05-12 02:56:05 +02:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2009-12-29 23:00:14 +01:00
|
|
|
systable_endscan(inhscan);
|
2009-05-12 02:56:05 +02:00
|
|
|
|
|
|
|
if (result)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* clean up ... */
|
|
|
|
heap_close(inhrel, AccessShareLock);
|
|
|
|
|
|
|
|
list_free(visited);
|
|
|
|
list_free(queue);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a single pg_inherits row with the given data
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
|
|
|
|
{
|
|
|
|
Datum values[Natts_pg_inherits];
|
|
|
|
bool nulls[Natts_pg_inherits];
|
|
|
|
HeapTuple tuple;
|
|
|
|
Relation inhRelation;
|
|
|
|
|
|
|
|
inhRelation = heap_open(InheritsRelationId, RowExclusiveLock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make the pg_inherits entry
|
|
|
|
*/
|
|
|
|
values[Anum_pg_inherits_inhrelid - 1] = ObjectIdGetDatum(relationId);
|
|
|
|
values[Anum_pg_inherits_inhparent - 1] = ObjectIdGetDatum(parentOid);
|
|
|
|
values[Anum_pg_inherits_inhseqno - 1] = Int32GetDatum(seqNumber);
|
|
|
|
|
|
|
|
memset(nulls, 0, sizeof(nulls));
|
|
|
|
|
|
|
|
tuple = heap_form_tuple(RelationGetDescr(inhRelation), values, nulls);
|
|
|
|
|
|
|
|
CatalogTupleInsert(inhRelation, tuple);
|
|
|
|
|
|
|
|
heap_freetuple(tuple);
|
|
|
|
|
|
|
|
heap_close(inhRelation, RowExclusiveLock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* DeleteInheritsTuple
|
|
|
|
*
|
|
|
|
* Delete pg_inherits tuples with the given inhrelid. inhparent may be given
|
|
|
|
* as InvalidOid, in which case all tuples matching inhrelid are deleted;
|
|
|
|
* otherwise only delete tuples with the specified inhparent.
|
|
|
|
*
|
|
|
|
* Returns whether at least one row was deleted.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
DeleteInheritsTuple(Oid inhrelid, Oid inhparent)
|
|
|
|
{
|
2018-04-26 20:47:16 +02:00
|
|
|
bool found = false;
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
Relation catalogRelation;
|
|
|
|
ScanKeyData key;
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple inheritsTuple;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find pg_inherits entries by inhrelid.
|
|
|
|
*/
|
|
|
|
catalogRelation = heap_open(InheritsRelationId, RowExclusiveLock);
|
|
|
|
ScanKeyInit(&key,
|
|
|
|
Anum_pg_inherits_inhrelid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(inhrelid));
|
|
|
|
scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
|
|
|
|
true, NULL, 1, &key);
|
|
|
|
|
|
|
|
while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
|
|
|
|
{
|
|
|
|
Oid parent;
|
|
|
|
|
|
|
|
/* Compare inhparent if it was given, and do the actual deletion. */
|
|
|
|
parent = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhparent;
|
|
|
|
if (!OidIsValid(inhparent) || parent == inhparent)
|
|
|
|
{
|
|
|
|
CatalogTupleDelete(catalogRelation, &inheritsTuple->t_self);
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Done */
|
|
|
|
systable_endscan(scan);
|
|
|
|
heap_close(catalogRelation, RowExclusiveLock);
|
|
|
|
|
|
|
|
return found;
|
|
|
|
}
|