#include "access/heapam.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"
*
* 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)
+find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
{
List *list = NIL;
Relation relation;
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(parentrelId));
scan = heap_beginscan(relation, SnapshotNow, 1, key);
+
while ((inheritsTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
inhrelid = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhrelid;
+
+ 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 (!SearchSysCacheExists(RELOID,
+ ObjectIdGetDatum(inhrelid),
+ 0, 0, 0))
+ {
+ /* Release useless lock */
+ UnlockRelationOid(inhrelid, lockmode);
+ /* And ignore this relation */
+ continue;
+ }
+ }
+
list = lappend_oid(list, inhrelid);
}
+
heap_endscan(scan);
heap_close(relation, AccessShareLock);
+
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.
+ *
+ * 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)
+find_all_inheritors(Oid parentrelId, LOCKMODE lockmode)
{
List *rels_list;
ListCell *l;
List *currentchildren;
/* Get the direct children of this rel */
- currentchildren = find_inheritance_children(currentrel);
+ currentchildren = find_inheritance_children(currentrel, lockmode);
/*
* Add to the queue only those children not already seen. This avoids
#include "access/heapam.h"
#include "catalog/namespace.h"
-#include "catalog/pg_inherits.h"
+#include "catalog/pg_inherits_fn.h"
#include "commands/lockcmds.h"
#include "miscadmin.h"
#include "parser/parse_clause.h"
reloid = RangeVarGetRelid(relation, false);
+ /* XXX NoLock here is not really a good idea */
if (recurse)
- children_and_self = find_all_inheritors(reloid);
+ children_and_self = find_all_inheritors(reloid, NoLock);
else
children_and_self = list_make1_oid(reloid);
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_inherits.h"
+#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_tablespace.h"
ListCell *child;
List *children;
- children = find_all_inheritors(myrelid);
+ children = find_all_inheritors(myrelid, AccessExclusiveLock);
foreach(child, children)
{
if (list_member_oid(relids, childrelid))
continue;
- rel = heap_open(childrelid, AccessExclusiveLock);
+ /* find_all_inheritors already got lock */
+ rel = heap_open(childrelid, NoLock);
truncate_check_rel(rel);
rels = lappend(rels, rel);
relids = lappend_oid(relids, childrelid);
ListCell *child;
List *children;
- children = find_all_inheritors(myrelid);
+ children = find_all_inheritors(myrelid, AccessExclusiveLock);
/*
* find_all_inheritors does the recursive search of the inheritance
* tables; else the rename would put them out of step.
*/
if (!recursing &&
- find_inheritance_children(myrelid) != NIL)
+ find_inheritance_children(myrelid, NoLock) != NIL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("inherited column \"%s\" must be renamed in child tables too",
ListCell *child;
List *children;
- children = find_all_inheritors(relid);
+ children = find_all_inheritors(relid, AccessExclusiveLock);
/*
* find_all_inheritors does the recursive search of the inheritance
if (childrelid == relid)
continue;
- childrel = relation_open(childrelid, AccessExclusiveLock);
+ /* find_all_inheritors already got lock */
+ childrel = relation_open(childrelid, NoLock);
CheckTableNotInUse(childrel, "ALTER TABLE");
ATPrepCmd(wqueue, childrel, cmd, false, true);
relation_close(childrel, NoLock);
ListCell *child;
List *children;
- children = find_inheritance_children(relid);
+ children = find_inheritance_children(relid, AccessExclusiveLock);
foreach(child, children)
{
Oid childrelid = lfirst_oid(child);
Relation childrel;
- childrel = relation_open(childrelid, AccessExclusiveLock);
+ /* find_inheritance_children already got lock */
+ childrel = relation_open(childrelid, NoLock);
CheckTableNotInUse(childrel, "ALTER TABLE");
ATPrepCmd(wqueue, childrel, cmd, true, true);
relation_close(childrel, NoLock);
* If we are told not to recurse, there had better not be any child
* tables; else the addition would put them out of step.
*/
- if (find_inheritance_children(RelationGetRelid(rel)) != NIL)
+ if (find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("column must be added to child tables too")));
* routines, we have to do this one level of recursion at a time; we can't
* use find_all_inheritors to do it in one pass.
*/
- children = find_inheritance_children(RelationGetRelid(rel));
+ children = find_inheritance_children(RelationGetRelid(rel),
+ AccessExclusiveLock);
if (children)
{
Relation childrel;
Form_pg_attribute childatt;
- childrel = heap_open(childrelid, AccessExclusiveLock);
+ /* find_inheritance_children already got lock */
+ childrel = heap_open(childrelid, NoLock);
CheckTableNotInUse(childrel, "ALTER TABLE");
tuple = SearchSysCacheCopyAttName(childrelid, colName);
* routines, we have to do this one level of recursion at a time; we can't
* use find_all_inheritors to do it in one pass.
*/
- children = find_inheritance_children(RelationGetRelid(rel));
+ children = find_inheritance_children(RelationGetRelid(rel),
+ AccessExclusiveLock);
/*
* If we are told not to recurse, there had better not be any child
Relation childrel;
AlteredTableInfo *childtab;
- childrel = heap_open(childrelid, AccessExclusiveLock);
+ /* find_inheritance_children already got lock */
+ childrel = heap_open(childrelid, NoLock);
CheckTableNotInUse(childrel, "ALTER TABLE");
/* Find or create work queue entry for this table */
* use find_all_inheritors to do it in one pass.
*/
if (is_check_constraint)
- children = find_inheritance_children(RelationGetRelid(rel));
+ children = find_inheritance_children(RelationGetRelid(rel),
+ AccessExclusiveLock);
else
children = NIL;
Oid childrelid = lfirst_oid(child);
Relation childrel;
- childrel = heap_open(childrelid, AccessExclusiveLock);
+ /* find_inheritance_children already got lock */
+ childrel = heap_open(childrelid, NoLock);
CheckTableNotInUse(childrel, "ALTER TABLE");
ScanKeyInit(&key,
if (recurse)
ATSimpleRecursion(wqueue, rel, cmd, recurse);
else if (!recursing &&
- find_inheritance_children(RelationGetRelid(rel)) != NIL)
+ find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("type of inherited column \"%s\" must be changed in child tables too",
* exclusive locks on the entire inheritance tree, which is a cure worse
* than the disease. find_all_inheritors() will cope with circularity
* anyway, so don't sweat it too much.
+ *
+ * We use weakest lock we can on child's children, namely AccessShareLock.
*/
- children = find_all_inheritors(RelationGetRelid(child_rel));
+ children = find_all_inheritors(RelationGetRelid(child_rel),
+ AccessShareLock);
if (list_member_oid(children, RelationGetRelid(parent_rel)))
ereport(ERROR,
#include "access/heapam.h"
#include "access/sysattr.h"
#include "catalog/namespace.h"
-#include "catalog/pg_inherits.h"
+#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
return;
}
- /* Scan for all members of inheritance set */
- inhOIDs = find_all_inheritors(parentOID);
+ /*
+ * The rewriter should already have obtained an appropriate lock on each
+ * relation named in the query. However, for each child relation we add
+ * to the query, we must obtain an appropriate lock, because this will be
+ * the first use of those relations in the parse/rewrite/plan pipeline.
+ *
+ * If the parent relation is the query's result relation, then we need
+ * RowExclusiveLock. Otherwise, if it's accessed FOR UPDATE/SHARE, we
+ * need RowShareLock; otherwise AccessShareLock. We can't just grab
+ * AccessShareLock because then the executor would be trying to upgrade
+ * the lock, leading to possible deadlocks. (This code should match the
+ * parser and rewriter.)
+ */
+ oldrc = get_rowmark(parse, rti);
+ if (rti == parse->resultRelation)
+ lockmode = RowExclusiveLock;
+ else if (oldrc)
+ lockmode = RowShareLock;
+ else
+ lockmode = AccessShareLock;
+
+ /* Scan for all members of inheritance set, acquire needed locks */
+ inhOIDs = find_all_inheritors(parentOID, lockmode);
/*
* Check that there's at least one descendant, else treat as no-child
}
/*
- * Find out if parent relation is selected FOR UPDATE/SHARE. If so,
- * we need to mark its RowMarkClause as isParent = true, and generate
- * a new RowMarkClause for each child.
+ * If parent relation is selected FOR UPDATE/SHARE, we need to mark its
+ * RowMarkClause as isParent = true, and generate a new RowMarkClause for
+ * each child.
*/
- oldrc = get_rowmark(parse, rti);
if (oldrc)
oldrc->isParent = true;
/*
* Must open the parent relation to examine its tupdesc. We need not lock
- * it since the rewriter already obtained at least AccessShareLock on each
- * relation used in the query.
+ * it; we assume the rewriter already did.
*/
oldrelation = heap_open(parentOID, NoLock);
- /*
- * However, for each child relation we add to the query, we must obtain an
- * appropriate lock, because this will be the first use of those relations
- * in the parse/rewrite/plan pipeline.
- *
- * If the parent relation is the query's result relation, then we need
- * RowExclusiveLock. Otherwise, if it's accessed FOR UPDATE/SHARE, we
- * need RowShareLock; otherwise AccessShareLock. We can't just grab
- * AccessShareLock because then the executor would be trying to upgrade
- * the lock, leading to possible deadlocks. (This code should match the
- * parser and rewriter.)
- */
- if (rti == parse->resultRelation)
- lockmode = RowExclusiveLock;
- else if (oldrc)
- lockmode = RowShareLock;
- else
- lockmode = AccessShareLock;
-
/* Scan the inheritance set and expand it */
appinfos = NIL;
foreach(l, inhOIDs)
Index childRTindex;
AppendRelInfo *appinfo;
- /* Open rel, acquire the appropriate lock type */
+ /* Open rel if needed; we already have required locks */
if (childOID != parentOID)
- newrelation = heap_open(childOID, lockmode);
+ newrelation = heap_open(childOID, NoLock);
else
newrelation = oldrelation;
#include "postgres.h"
#include "catalog/pg_cast.h"
-#include "catalog/pg_inherits.h"
+#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#define PG_INHERITS_H
#include "catalog/genbki.h"
-#include "nodes/pg_list.h"
/* ----------------
* pg_inherits definition. cpp turns this into
* ----------------
*/
-/*
- * prototypes for functions in pg_inherits.c
- */
-extern List *find_inheritance_children(Oid parentrelId);
-extern List *find_all_inheritors(Oid parentrelId);
-extern bool has_subclass(Oid relationId);
-extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId);
-
#endif /* PG_INHERITS_H */
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * pg_inherits_fn.h
+ * prototypes for functions in catalog/pg_inherits.c
+ *
+ *
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_INHERITS_FN_H
+#define PG_INHERITS_FN_H
+
+#include "nodes/pg_list.h"
+#include "storage/lock.h"
+
+extern List *find_inheritance_children(Oid parentrelId, LOCKMODE lockmode);
+extern List *find_all_inheritors(Oid parentrelId, LOCKMODE lockmode);
+extern bool has_subclass(Oid relationId);
+extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId);
+
+#endif /* PG_INHERITS_FN_H */