*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.315 2008/11/06 20:51:14 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.316 2008/11/15 19:43:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
foreach(l, plannedstmt->rowMarks)
{
RowMarkClause *rc = (RowMarkClause *) lfirst(l);
- Oid relid = getrelid(rc->rti, rangeTable);
+ Oid relid;
Relation relation;
ExecRowMark *erm;
+ /* ignore "parent" rowmarks; they are irrelevant at runtime */
+ if (rc->isParent)
+ continue;
+
+ relid = getrelid(rc->rti, rangeTable);
relation = heap_open(relid, RowShareLock);
erm = (ExecRowMark *) palloc(sizeof(ExecRowMark));
erm->relation = relation;
erm->rti = rc->rti;
+ erm->prti = rc->prti;
erm->forUpdate = rc->forUpdate;
erm->noWait = rc->noWait;
- /* We'll set up ctidAttno below */
+ /* We'll locate the junk attrs below */
erm->ctidAttNo = InvalidAttrNumber;
+ erm->toidAttNo = InvalidAttrNumber;
estate->es_rowMarks = lappend(estate->es_rowMarks, erm);
}
elog(ERROR, "could not find junk ctid column");
}
- /* For SELECT FOR UPDATE/SHARE, find the ctid attrs now */
+ /* For SELECT FOR UPDATE/SHARE, find the junk attrs now */
foreach(l, estate->es_rowMarks)
{
ExecRowMark *erm = (ExecRowMark *) lfirst(l);
char resname[32];
- snprintf(resname, sizeof(resname), "ctid%u", erm->rti);
+ /* always need the ctid */
+ snprintf(resname, sizeof(resname), "ctid%u",
+ erm->prti);
erm->ctidAttNo = ExecFindJunkAttribute(j, resname);
if (!AttributeNumberIsValid(erm->ctidAttNo))
elog(ERROR, "could not find junk \"%s\" column",
resname);
+ /* if child relation, need tableoid too */
+ if (erm->rti != erm->prti)
+ {
+ snprintf(resname, sizeof(resname), "tableoid%u",
+ erm->prti);
+ erm->toidAttNo = ExecFindJunkAttribute(j, resname);
+ if (!AttributeNumberIsValid(erm->toidAttNo))
+ elog(ERROR, "could not find junk \"%s\" column",
+ resname);
+ }
}
}
}
LockTupleMode lockmode;
HTSU_Result test;
+ /* if child rel, must check whether it produced this row */
+ if (erm->rti != erm->prti)
+ {
+ Oid tableoid;
+
+ datum = ExecGetJunkAttribute(slot,
+ erm->toidAttNo,
+ &isNull);
+ /* shouldn't ever get a null result... */
+ if (isNull)
+ elog(ERROR, "tableoid is NULL");
+ tableoid = DatumGetObjectId(datum);
+
+ if (tableoid != RelationGetRelid(erm->relation))
+ {
+ /* this child is inactive right now */
+ continue;
+ }
+ }
+
+ /* okay, fetch the tuple by ctid */
datum = ExecGetJunkAttribute(slot,
erm->ctidAttNo,
&isNull);
/* shouldn't ever get a null result... */
if (isNull)
elog(ERROR, "ctid is NULL");
-
tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
if (erm->forUpdate)
relation = NULL;
foreach(l, estate->es_rowMarks)
{
- if (((ExecRowMark *) lfirst(l))->rti == rti)
+ ExecRowMark *erm = lfirst(l);
+
+ if (erm->rti == rti)
{
- relation = ((ExecRowMark *) lfirst(l))->relation;
+ relation = erm->relation;
break;
}
}
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.411 2008/11/11 18:13:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.412 2008/11/15 19:43:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
RowMarkClause *newnode = makeNode(RowMarkClause);
COPY_SCALAR_FIELD(rti);
+ COPY_SCALAR_FIELD(prti);
COPY_SCALAR_FIELD(forUpdate);
COPY_SCALAR_FIELD(noWait);
+ COPY_SCALAR_FIELD(isParent);
return newnode;
}
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.336 2008/11/11 18:13:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.337 2008/11/15 19:43:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
_equalRowMarkClause(RowMarkClause *a, RowMarkClause *b)
{
COMPARE_SCALAR_FIELD(rti);
+ COMPARE_SCALAR_FIELD(prti);
COMPARE_SCALAR_FIELD(forUpdate);
COMPARE_SCALAR_FIELD(noWait);
+ COMPARE_SCALAR_FIELD(isParent);
return true;
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.344 2008/11/11 18:13:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.345 2008/11/15 19:43:46 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
WRITE_NODE_TYPE("ROWMARKCLAUSE");
WRITE_UINT_FIELD(rti);
+ WRITE_UINT_FIELD(prti);
WRITE_BOOL_FIELD(forUpdate);
WRITE_BOOL_FIELD(noWait);
+ WRITE_BOOL_FIELD(isParent);
}
static void
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.216 2008/10/06 17:39:26 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.217 2008/11/15 19:43:46 tgl Exp $
*
* NOTES
* Path and Plan nodes do not have any readfuncs support, because we
READ_LOCALS(RowMarkClause);
READ_UINT_FIELD(rti);
+ READ_UINT_FIELD(prti);
READ_BOOL_FIELD(forUpdate);
READ_BOOL_FIELD(noWait);
+ READ_BOOL_FIELD(isParent);
READ_DONE();
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.176 2008/11/11 18:13:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.177 2008/11/15 19:43:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
int nattrs;
ListCell *l;
- /*
- * XXX for now, can't handle inherited expansion of FOR UPDATE/SHARE; can
- * we do better? (This will take some redesign because the executor
- * currently supposes that every rowMark relation is involved in every row
- * returned by the query.)
- */
- if (get_rowmark(root->parse, parentRTindex))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("SELECT FOR UPDATE/SHARE is not supported for inheritance queries")));
-
/*
* Initialize to compute size estimates for whole append relation.
*
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.93 2008/11/02 01:45:28 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.94 2008/11/15 19:43:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
char *resname;
TargetEntry *tle;
+ /* ignore child rels */
+ if (rc->rti != rc->prti)
+ continue;
+
+ /* always need the ctid */
var = makeVar(rc->rti,
SelfItemPointerAttributeNumber,
TIDOID,
true);
tlist = lappend(tlist, tle);
+
+ /* if parent of inheritance tree, need the tableoid too */
+ if (rc->isParent)
+ {
+ var = makeVar(rc->rti,
+ TableOidAttributeNumber,
+ OIDOID,
+ -1,
+ 0);
+
+ resname = (char *) palloc(32);
+ snprintf(resname, 32, "tableoid%u", rc->rti);
+
+ tle = makeTargetEntry((Expr *) var,
+ list_length(tlist) + 1,
+ resname,
+ true);
+
+ tlist = lappend(tlist, tle);
+ }
}
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.161 2008/11/11 18:13:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.162 2008/11/15 19:43:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
Query *parse = root->parse;
Oid parentOID;
+ RowMarkClause *oldrc;
Relation oldrelation;
LOCKMODE lockmode;
List *inhOIDs;
return;
}
+ /*
+ * 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.
+ */
+ 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
* in the parse/rewrite/plan pipeline.
*
* If the parent relation is the query's result relation, then we need
- * RowExclusiveLock. Otherwise, check to see if the relation is accessed
- * FOR UPDATE/SHARE or not. 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.)
+ * 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 (get_rowmark(parse, rti))
+ else if (oldrc)
lockmode = RowShareLock;
else
lockmode = AccessShareLock;
appinfo->parent_reloid = parentOID;
appinfos = lappend(appinfos, appinfo);
+ /*
+ * Build a RowMarkClause if parent is marked FOR UPDATE/SHARE.
+ */
+ if (oldrc)
+ {
+ RowMarkClause *newrc = makeNode(RowMarkClause);
+
+ newrc->rti = childRTindex;
+ newrc->prti = rti;
+ newrc->forUpdate = oldrc->forUpdate;
+ newrc->noWait = oldrc->noWait;
+ newrc->isParent = false;
+
+ parse->rowMarks = lappend(parse->rowMarks, newrc);
+ }
+
/* Close child relations, but keep locks */
if (childOID != parentOID)
heap_close(newrelation, NoLock);
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.382 2008/10/07 01:47:54 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.383 2008/11/15 19:43:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* Make a new RowMarkClause */
rc = makeNode(RowMarkClause);
rc->rti = rtindex;
+ rc->prti = rtindex;
rc->forUpdate = forUpdate;
rc->noWait = noWait;
+ rc->isParent = false;
qry->rowMarks = lappend(qry->rowMarks, rc);
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.117 2008/10/22 20:17:52 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.118 2008/11/15 19:43:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
RowMarkClause *rc = (RowMarkClause *) lfirst(l);
rc->rti += offset;
+ rc->prti += offset;
}
}
query_tree_walker(qry, OffsetVarNodes_walker,
if (rc->rti == rt_index)
rc->rti = new_index;
+ if (rc->prti == rt_index)
+ rc->prti = new_index;
}
}
query_tree_walker(qry, ChangeVarNodes_walker,
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.507 2008/11/14 02:09:51 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.508 2008/11/15 19:43:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200811133
+#define CATALOG_VERSION_NO 200811151
#endif
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.194 2008/10/31 19:37:56 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.195 2008/11/15 19:43:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
} EState;
-/* es_rowMarks is a list of these structs: */
+/*
+ * es_rowMarks is a list of these structs. See RowMarkClause for details
+ * about rti and prti. toidAttno is not used in a "plain" rowmark.
+ */
typedef struct ExecRowMark
{
Relation relation; /* opened and RowShareLock'd relation */
Index rti; /* its range table index */
+ Index prti; /* parent range table index, if child */
bool forUpdate; /* true = FOR UPDATE, false = FOR SHARE */
bool noWait; /* NOWAIT option */
AttrNumber ctidAttNo; /* resno of its ctid junk attribute */
+ AttrNumber toidAttNo; /* resno of tableoid junk attribute, if any */
} ExecRowMark;
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.377 2008/10/31 08:39:22 heikki Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.378 2008/11/15 19:43:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* RowMarkClause -
* representation of FOR UPDATE/SHARE clauses
*
- * We create a separate RowMarkClause node for each target relation
+ * We create a separate RowMarkClause node for each target relation. In the
+ * output of the parser and rewriter, all RowMarkClauses have rti == prti and
+ * isParent == false. When the planner discovers that a target relation
+ * is the root of an inheritance tree, it sets isParent true, and adds an
+ * additional RowMarkClause to the list for each child relation (including
+ * the target rel itself in its role as a child). The child entries have
+ * rti == child rel's RT index, prti == parent's RT index, and can therefore
+ * be recognized as children by the fact that prti != rti.
*/
typedef struct RowMarkClause
{
NodeTag type;
Index rti; /* range table index of target relation */
+ Index prti; /* range table index of parent relation */
bool forUpdate; /* true = FOR UPDATE, false = FOR SHARE */
bool noWait; /* NOWAIT option */
+ bool isParent; /* set by planner when expanding inheritance */
} RowMarkClause;
/*
(3 rows)
BEGIN;
-DECLARE c1 CURSOR FOR SELECT * FROM uctest;
+DECLARE c1 CURSOR FOR SELECT * FROM uctest FOR UPDATE;
FETCH 1 FROM c1;
f1 | f2
----+-------
SELECT * FROM uctest;
BEGIN;
-DECLARE c1 CURSOR FOR SELECT * FROM uctest;
+DECLARE c1 CURSOR FOR SELECT * FROM uctest FOR UPDATE;
FETCH 1 FROM c1;
UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1;
FETCH 1 FROM c1;