diff --git a/doc/src/sgml/ref/select.sgml b/doc/src/sgml/ref/select.sgml
index f862d5f873..b15aa361a0 100644
--- a/doc/src/sgml/ref/select.sgml
+++ b/doc/src/sgml/ref/select.sgml
@@ -1,5 +1,5 @@
@@ -30,7 +30,7 @@ SELECT [ ALL | DISTINCT [ ON ( expressionexpression [ ASC | DESC | USING operator ] [, ...] ]
[ LIMIT { count | ALL } ]
[ OFFSET start ]
- [ FOR { UPDATE | SHARE } [ OF table_name [, ...] ] [ NOWAIT ] ]
+ [ FOR { UPDATE | SHARE } [ OF table_name [, ...] ] [ NOWAIT ] [...] ]
where from_item can be one of:
@@ -142,8 +142,8 @@ where from_item can be one of:
- If the FOR UPDATE or FOR SHARE
- clause is specified, the
+ If FOR UPDATE or FOR SHARE
+ is specified, the
SELECT statement locks the selected rows
against concurrent updates. (See below.)
@@ -852,19 +852,27 @@ FOR SHARE [ OF table_name [, ...] ]
from performing SELECT FOR SHARE.
-
- It is currently not allowed for a single SELECT
- statement to include both FOR UPDATE and
- FOR SHARE, nor can different parts of the statement use
- both NOWAIT> and normal waiting mode.
-
-
If specific tables are named in FOR UPDATE
or FOR SHARE,
then only rows coming from those tables are locked; any other
tables used in the SELECT are simply read as
- usual.
+ usual. A FOR UPDATE or FOR SHARE
+ clause without a table list affects all tables used in the command.
+ If FOR UPDATE or FOR SHARE is
+ applied to a view or sub-query, it affects all tables used in
+ the view or sub-query.
+
+
+
+ Multiple FOR UPDATE and FOR SHARE
+ clauses can be written if it is necessary to specify different locking
+ behavior for different tables. If the same table is mentioned (or
+ implicitly affected) by both FOR UPDATE and
+ FOR SHARE clauses, then it is processed as
+ FOR UPDATE. Similarly, a table is processed
+ as NOWAIT> if that is specified in any of the clauses
+ affecting it.
diff --git a/doc/src/sgml/ref/select_into.sgml b/doc/src/sgml/ref/select_into.sgml
index d9967197b8..283fd4f8b2 100644
--- a/doc/src/sgml/ref/select_into.sgml
+++ b/doc/src/sgml/ref/select_into.sgml
@@ -1,5 +1,5 @@
@@ -31,7 +31,7 @@ SELECT [ ALL | DISTINCT [ ON ( expressionexpression [ ASC | DESC | USING operator ] [, ...] ]
[ LIMIT { count | ALL } ]
[ OFFSET start ]
- [ FOR { UPDATE | SHARE } [ OF table_name [, ...] ] [ NOWAIT ] ]
+ [ FOR { UPDATE | SHARE } [ OF table_name [, ...] ] [ NOWAIT ] [...] ]
diff --git a/doc/src/sgml/sql.sgml b/doc/src/sgml/sql.sgml
index 86539ded11..b27400b4a4 100644
--- a/doc/src/sgml/sql.sgml
+++ b/doc/src/sgml/sql.sgml
@@ -1,4 +1,4 @@
-
+
SQL
@@ -863,7 +863,7 @@ SELECT [ ALL | DISTINCT [ ON ( expressionexpression [ ASC | DESC | USING operator ] [, ...] ]
[ LIMIT { count | ALL } ]
[ OFFSET start ]
- [ FOR { UPDATE | SHARE } [ OF table_name [, ...] ] [ NOWAIT ] ]
+ [ FOR { UPDATE | SHARE } [ OF table_name [, ...] ] [ NOWAIT ] [...] ]
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 31951db4c2..e8e0c8bd56 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -26,7 +26,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.269 2006/03/05 15:58:25 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.270 2006/04/30 18:30:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -452,6 +452,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
Relation intoRelationDesc;
bool do_select_into;
TupleDesc tupType;
+ ListCell *l;
/*
* Do permissions checks. It's sufficient to examine the query's top
@@ -486,7 +487,6 @@ InitPlan(QueryDesc *queryDesc, int eflags)
* parseTree->resultRelations identifies them all
*/
ResultRelInfo *resultRelInfo;
- ListCell *l;
numResultRelations = list_length(resultRelations);
resultRelInfos = (ResultRelInfo *)
@@ -549,26 +549,21 @@ InitPlan(QueryDesc *queryDesc, int eflags)
* Have to lock relations selected FOR UPDATE/FOR SHARE
*/
estate->es_rowMarks = NIL;
- estate->es_forUpdate = parseTree->forUpdate;
- estate->es_rowNoWait = parseTree->rowNoWait;
- if (parseTree->rowMarks != NIL)
+ foreach(l, parseTree->rowMarks)
{
- ListCell *l;
+ RowMarkClause *rc = (RowMarkClause *) lfirst(l);
+ Oid relid = getrelid(rc->rti, rangeTable);
+ Relation relation;
+ ExecRowMark *erm;
- foreach(l, parseTree->rowMarks)
- {
- Index rti = lfirst_int(l);
- Oid relid = getrelid(rti, rangeTable);
- Relation relation;
- ExecRowMark *erm;
-
- relation = heap_open(relid, RowShareLock);
- erm = (ExecRowMark *) palloc(sizeof(ExecRowMark));
- erm->relation = relation;
- erm->rti = rti;
- snprintf(erm->resname, sizeof(erm->resname), "ctid%u", rti);
- estate->es_rowMarks = lappend(estate->es_rowMarks, erm);
- }
+ relation = heap_open(relid, RowShareLock);
+ erm = (ExecRowMark *) palloc(sizeof(ExecRowMark));
+ erm->relation = relation;
+ erm->rti = rc->rti;
+ erm->forUpdate = rc->forUpdate;
+ erm->noWait = rc->noWait;
+ snprintf(erm->resname, sizeof(erm->resname), "ctid%u", rc->rti);
+ estate->es_rowMarks = lappend(estate->es_rowMarks, erm);
}
/*
@@ -1222,7 +1217,7 @@ lnext: ;
tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
- if (estate->es_forUpdate)
+ if (erm->forUpdate)
lockmode = LockTupleExclusive;
else
lockmode = LockTupleShared;
@@ -1230,7 +1225,7 @@ lnext: ;
test = heap_lock_tuple(erm->relation, &tuple, &buffer,
&update_ctid, &update_xmax,
estate->es_snapshot->curcid,
- lockmode, estate->es_rowNoWait);
+ lockmode, erm->noWait);
ReleaseBuffer(buffer);
switch (test)
{
@@ -2258,8 +2253,6 @@ EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq)
epqstate->es_param_exec_vals = (ParamExecData *)
palloc0(estate->es_topPlan->nParamExec * sizeof(ParamExecData));
epqstate->es_rowMarks = estate->es_rowMarks;
- epqstate->es_forUpdate = estate->es_forUpdate;
- epqstate->es_rowNoWait = estate->es_rowNoWait;
epqstate->es_instrument = estate->es_instrument;
epqstate->es_select_into = estate->es_select_into;
epqstate->es_into_oids = estate->es_into_oids;
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 3696b5a208..d1a294f9bb 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.133 2006/03/05 15:58:26 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.134 2006/04/30 18:30:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -204,8 +204,6 @@ CreateExecutorState(void)
estate->es_processed = 0;
estate->es_lastoid = InvalidOid;
estate->es_rowMarks = NIL;
- estate->es_forUpdate = false;
- estate->es_rowNoWait = false;
estate->es_instrument = false;
estate->es_select_into = false;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index b060d38b5e..6bf342ab92 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.334 2006/04/22 01:25:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.335 2006/04/30 18:30:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1409,6 +1409,18 @@ _copyGroupClause(GroupClause *from)
return newnode;
}
+static RowMarkClause *
+_copyRowMarkClause(RowMarkClause *from)
+{
+ RowMarkClause *newnode = makeNode(RowMarkClause);
+
+ COPY_SCALAR_FIELD(rti);
+ COPY_SCALAR_FIELD(forUpdate);
+ COPY_SCALAR_FIELD(noWait);
+
+ return newnode;
+}
+
static A_Expr *
_copyAExpr(A_Expr *from)
{
@@ -1650,7 +1662,7 @@ _copyLockingClause(LockingClause *from)
COPY_NODE_FIELD(lockedRels);
COPY_SCALAR_FIELD(forUpdate);
- COPY_SCALAR_FIELD(nowait);
+ COPY_SCALAR_FIELD(noWait);
return newnode;
}
@@ -1673,9 +1685,6 @@ _copyQuery(Query *from)
COPY_SCALAR_FIELD(hasSubLinks);
COPY_NODE_FIELD(rtable);
COPY_NODE_FIELD(jointree);
- COPY_NODE_FIELD(rowMarks);
- COPY_SCALAR_FIELD(forUpdate);
- COPY_SCALAR_FIELD(rowNoWait);
COPY_NODE_FIELD(targetList);
COPY_NODE_FIELD(groupClause);
COPY_NODE_FIELD(havingQual);
@@ -1683,6 +1692,7 @@ _copyQuery(Query *from)
COPY_NODE_FIELD(sortClause);
COPY_NODE_FIELD(limitOffset);
COPY_NODE_FIELD(limitCount);
+ COPY_NODE_FIELD(rowMarks);
COPY_NODE_FIELD(setOperations);
COPY_NODE_FIELD(resultRelations);
@@ -3284,6 +3294,9 @@ copyObject(void *from)
case T_GroupClause:
retval = _copyGroupClause(from);
break;
+ case T_RowMarkClause:
+ retval = _copyRowMarkClause(from);
+ break;
case T_FkConstraint:
retval = _copyFkConstraint(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 94799d0c85..7f7eb06fbc 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.270 2006/04/22 01:25:59 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.271 2006/04/30 18:30:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -665,9 +665,6 @@ _equalQuery(Query *a, Query *b)
COMPARE_SCALAR_FIELD(hasSubLinks);
COMPARE_NODE_FIELD(rtable);
COMPARE_NODE_FIELD(jointree);
- COMPARE_NODE_FIELD(rowMarks);
- COMPARE_SCALAR_FIELD(forUpdate);
- COMPARE_SCALAR_FIELD(rowNoWait);
COMPARE_NODE_FIELD(targetList);
COMPARE_NODE_FIELD(groupClause);
COMPARE_NODE_FIELD(havingQual);
@@ -675,6 +672,7 @@ _equalQuery(Query *a, Query *b)
COMPARE_NODE_FIELD(sortClause);
COMPARE_NODE_FIELD(limitOffset);
COMPARE_NODE_FIELD(limitCount);
+ COMPARE_NODE_FIELD(rowMarks);
COMPARE_NODE_FIELD(setOperations);
COMPARE_NODE_FIELD(resultRelations);
@@ -1688,7 +1686,7 @@ _equalLockingClause(LockingClause *a, LockingClause *b)
{
COMPARE_NODE_FIELD(lockedRels);
COMPARE_SCALAR_FIELD(forUpdate);
- COMPARE_SCALAR_FIELD(nowait);
+ COMPARE_SCALAR_FIELD(noWait);
return true;
}
@@ -1723,6 +1721,16 @@ _equalSortClause(SortClause *a, SortClause *b)
return true;
}
+static bool
+_equalRowMarkClause(RowMarkClause *a, RowMarkClause *b)
+{
+ COMPARE_SCALAR_FIELD(rti);
+ COMPARE_SCALAR_FIELD(forUpdate);
+ COMPARE_SCALAR_FIELD(noWait);
+
+ return true;
+}
+
static bool
_equalFkConstraint(FkConstraint *a, FkConstraint *b)
{
@@ -2297,6 +2305,9 @@ equal(void *a, void *b)
/* GroupClause is equivalent to SortClause */
retval = _equalSortClause(a, b);
break;
+ case T_RowMarkClause:
+ retval = _equalRowMarkClause(a, b);
+ break;
case T_FkConstraint:
retval = _equalFkConstraint(a, b);
break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 314d68d2ef..25d3a11279 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.273 2006/04/22 01:25:59 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.274 2006/04/30 18:30:39 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
@@ -1418,7 +1418,7 @@ _outLockingClause(StringInfo str, LockingClause *node)
WRITE_NODE_FIELD(lockedRels);
WRITE_BOOL_FIELD(forUpdate);
- WRITE_BOOL_FIELD(nowait);
+ WRITE_BOOL_FIELD(noWait);
}
static void
@@ -1514,9 +1514,6 @@ _outQuery(StringInfo str, Query *node)
WRITE_BOOL_FIELD(hasSubLinks);
WRITE_NODE_FIELD(rtable);
WRITE_NODE_FIELD(jointree);
- WRITE_NODE_FIELD(rowMarks);
- WRITE_BOOL_FIELD(forUpdate);
- WRITE_BOOL_FIELD(rowNoWait);
WRITE_NODE_FIELD(targetList);
WRITE_NODE_FIELD(groupClause);
WRITE_NODE_FIELD(havingQual);
@@ -1524,6 +1521,7 @@ _outQuery(StringInfo str, Query *node)
WRITE_NODE_FIELD(sortClause);
WRITE_NODE_FIELD(limitOffset);
WRITE_NODE_FIELD(limitCount);
+ WRITE_NODE_FIELD(rowMarks);
WRITE_NODE_FIELD(setOperations);
WRITE_NODE_FIELD(resultRelations);
}
@@ -1546,6 +1544,16 @@ _outGroupClause(StringInfo str, GroupClause *node)
WRITE_OID_FIELD(sortop);
}
+static void
+_outRowMarkClause(StringInfo str, RowMarkClause *node)
+{
+ WRITE_NODE_TYPE("ROWMARKCLAUSE");
+
+ WRITE_UINT_FIELD(rti);
+ WRITE_BOOL_FIELD(forUpdate);
+ WRITE_BOOL_FIELD(noWait);
+}
+
static void
_outSetOperationStmt(StringInfo str, SetOperationStmt *node)
{
@@ -2113,6 +2121,9 @@ _outNode(StringInfo str, void *obj)
case T_GroupClause:
_outGroupClause(str, obj);
break;
+ case T_RowMarkClause:
+ _outRowMarkClause(str, obj);
+ break;
case T_SetOperationStmt:
_outSetOperationStmt(str, obj);
break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 9fdf02fd56..dd6995a172 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.188 2006/04/22 01:25:59 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.189 2006/04/30 18:30:39 tgl Exp $
*
* NOTES
* Path and Plan nodes do not have any readfuncs support, because we
@@ -147,9 +147,6 @@ _readQuery(void)
READ_BOOL_FIELD(hasSubLinks);
READ_NODE_FIELD(rtable);
READ_NODE_FIELD(jointree);
- READ_NODE_FIELD(rowMarks);
- READ_BOOL_FIELD(forUpdate);
- READ_BOOL_FIELD(rowNoWait);
READ_NODE_FIELD(targetList);
READ_NODE_FIELD(groupClause);
READ_NODE_FIELD(havingQual);
@@ -157,6 +154,7 @@ _readQuery(void)
READ_NODE_FIELD(sortClause);
READ_NODE_FIELD(limitOffset);
READ_NODE_FIELD(limitCount);
+ READ_NODE_FIELD(rowMarks);
READ_NODE_FIELD(setOperations);
READ_NODE_FIELD(resultRelations);
@@ -219,6 +217,21 @@ _readGroupClause(void)
READ_DONE();
}
+/*
+ * _readRowMarkClause
+ */
+static RowMarkClause *
+_readRowMarkClause(void)
+{
+ READ_LOCALS(RowMarkClause);
+
+ READ_UINT_FIELD(rti);
+ READ_BOOL_FIELD(forUpdate);
+ READ_BOOL_FIELD(noWait);
+
+ READ_DONE();
+}
+
/*
* _readSetOperationStmt
*/
@@ -934,6 +947,8 @@ parseNodeString(void)
return_value = _readSortClause();
else if (MATCH("GROUPCLAUSE", 11))
return_value = _readGroupClause();
+ else if (MATCH("ROWMARKCLAUSE", 13))
+ return_value = _readRowMarkClause();
else if (MATCH("SETOPERATIONSTMT", 16))
return_value = _readSetOperationStmt();
else if (MATCH("ALIAS", 5))
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 9ce72837b9..f35e87962d 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.144 2006/03/05 15:58:28 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.145 2006/04/30 18:30:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -268,7 +268,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
* currently supposes that every rowMark relation is involved in every
* row returned by the query.)
*/
- if (list_member_int(root->parse->rowMarks, parentRTindex))
+ if (get_rowmark(root->parse, parentRTindex))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("SELECT FOR UPDATE/SHARE is not supported for inheritance queries")));
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 9750789f0c..174b20f622 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.209 2006/04/25 16:54:09 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.210 2006/04/30 18:30:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -843,7 +843,7 @@ create_indexscan_plan(PlannerInfo *root,
if (best_path->indexinfo->indpred)
{
if (baserelid != root->parse->resultRelation &&
- !list_member_int(root->parse->rowMarks, baserelid))
+ get_rowmark(root->parse, baserelid) == NULL)
if (predicate_implied_by(clausel,
best_path->indexinfo->indpred))
continue;
@@ -962,7 +962,7 @@ create_bitmap_scan_plan(PlannerInfo *root,
if (ipath->indexinfo->indpred)
{
if (baserelid != root->parse->resultRelation &&
- !list_member_int(root->parse->rowMarks, baserelid))
+ get_rowmark(root->parse, baserelid) == NULL)
if (predicate_implied_by(clausel,
ipath->indexinfo->indpred))
continue;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index ae538c9cc0..f3b6df46f2 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.37 2006/03/07 01:00:15 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.38 2006/04/30 18:30:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -409,29 +409,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
/*
* Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes
- * already adjusted the marker values, so just list_concat the
- * list.)
- *
- * Executor can't handle multiple FOR UPDATE/SHARE/NOWAIT flags,
- * so complain if they are valid but different
+ * already adjusted the marker rtindexes, so just concat the lists.)
*/
- if (parse->rowMarks && subquery->rowMarks)
- {
- if (parse->forUpdate != subquery->forUpdate)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot use both FOR UPDATE and FOR SHARE in one query")));
- if (parse->rowNoWait != subquery->rowNoWait)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot use both wait and NOWAIT in one query")));
- }
parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks);
- if (subquery->rowMarks)
- {
- parse->forUpdate = subquery->forUpdate;
- parse->rowNoWait = subquery->rowNoWait;
- }
/*
* We also have to fix the relid sets of any parent InClauseInfo
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 5a37b86eb5..0e06d0e888 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.81 2006/04/05 22:11:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.82 2006/04/30 18:30:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -116,7 +116,7 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
* invalid. This is also checked at parse time, but that's
* insufficient because of rule substitution, query pullup, etc.
*/
- CheckSelectLocking(parse, parse->forUpdate);
+ CheckSelectLocking(parse);
/*
* Currently the executor only supports FOR UPDATE/SHARE at top level
@@ -128,19 +128,19 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
foreach(l, parse->rowMarks)
{
- Index rti = lfirst_int(l);
+ RowMarkClause *rc = (RowMarkClause *) lfirst(l);
Var *var;
char *resname;
TargetEntry *tle;
- var = makeVar(rti,
+ var = makeVar(rc->rti,
SelfItemPointerAttributeNumber,
TIDOID,
-1,
0);
resname = (char *) palloc(32);
- snprintf(resname, 32, "ctid%u", rti);
+ snprintf(resname, 32, "ctid%u", rc->rti);
tle = makeTargetEntry((Expr *) var,
list_length(tlist) + 1,
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 8c0457295c..3b9e740cad 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -22,7 +22,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.131 2006/03/05 15:58:31 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.132 2006/04/30 18:30:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -816,7 +816,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
*/
if (rti == parse->resultRelation)
lockmode = RowExclusiveLock;
- else if (list_member_int(parse->rowMarks, rti))
+ else if (get_rowmark(parse, rti))
lockmode = RowShareLock;
else
lockmode = AccessShareLock;
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index d69c0f1786..566c9a0488 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.333 2006/04/22 01:25:59 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.334 2006/04/30 18:30:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1805,6 +1805,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
{
Query *qry = makeNode(Query);
Node *qual;
+ ListCell *l;
qry->commandType = CMD_SELECT;
@@ -1870,8 +1871,10 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
parseCheckAggregates(pstate, qry);
- if (stmt->lockingClause)
- transformLockingClause(qry, stmt->lockingClause);
+ foreach(l, stmt->lockingClause)
+ {
+ transformLockingClause(qry, (LockingClause *) lfirst(l));
+ }
return qry;
}
@@ -1899,10 +1902,11 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
List *sortClause;
Node *limitOffset;
Node *limitCount;
- LockingClause *lockingClause;
+ List *lockingClause;
Node *node;
ListCell *left_tlist,
- *dtlist;
+ *dtlist,
+ *l;
List *targetvars,
*targetnames,
*sv_relnamespace,
@@ -1942,7 +1946,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
stmt->sortClause = NIL;
stmt->limitOffset = NULL;
stmt->limitCount = NULL;
- stmt->lockingClause = NULL;
+ stmt->lockingClause = NIL;
/* We don't support FOR UPDATE/SHARE with set ops at the moment. */
if (lockingClause)
@@ -2084,8 +2088,10 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
parseCheckAggregates(pstate, qry);
- if (lockingClause)
- transformLockingClause(qry, lockingClause);
+ foreach(l, lockingClause)
+ {
+ transformLockingClause(qry, (LockingClause *) lfirst(l));
+ }
return qry;
}
@@ -2743,44 +2749,34 @@ transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt)
/* exported so planner can check again after rewriting, query pullup, etc */
void
-CheckSelectLocking(Query *qry, bool forUpdate)
+CheckSelectLocking(Query *qry)
{
- const char *operation;
-
- if (forUpdate)
- operation = "SELECT FOR UPDATE";
- else
- operation = "SELECT FOR SHARE";
-
if (qry->setOperations)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- /* translator: %s is a SQL command, like SELECT FOR UPDATE */
- errmsg("%s is not allowed with UNION/INTERSECT/EXCEPT", operation)));
+ errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT")));
if (qry->distinctClause != NIL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- /* translator: %s is a SQL command, like SELECT FOR UPDATE */
- errmsg("%s is not allowed with DISTINCT clause", operation)));
+ errmsg("SELECT FOR UPDATE/SHARE is not allowed with DISTINCT clause")));
if (qry->groupClause != NIL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- /* translator: %s is a SQL command, like SELECT FOR UPDATE */
- errmsg("%s is not allowed with GROUP BY clause", operation)));
+ errmsg("SELECT FOR UPDATE/SHARE is not allowed with GROUP BY clause")));
if (qry->havingQual != NULL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- /* translator: %s is a SQL command, like SELECT FOR UPDATE */
- errmsg("%s is not allowed with HAVING clause", operation)));
+ errmsg("SELECT FOR UPDATE/SHARE is not allowed with HAVING clause")));
if (qry->hasAggs)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- /* translator: %s is a SQL command, like SELECT FOR UPDATE */
- errmsg("%s is not allowed with aggregate functions", operation)));
+ errmsg("SELECT FOR UPDATE/SHARE is not allowed with aggregate functions")));
}
/*
- * Convert FOR UPDATE/SHARE name list into rowMarks list of integer relids
+ * Transform a FOR UPDATE/SHARE clause
+ *
+ * This basically involves replacing names by integer relids.
*
* NB: if you need to change this, see also markQueryForLocking()
* in rewriteHandler.c.
@@ -2789,35 +2785,18 @@ static void
transformLockingClause(Query *qry, LockingClause *lc)
{
List *lockedRels = lc->lockedRels;
- List *rowMarks;
ListCell *l;
ListCell *rt;
Index i;
LockingClause *allrels;
- if (qry->rowMarks)
- {
- if (lc->forUpdate != qry->forUpdate)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot use both FOR UPDATE and FOR SHARE in one query")));
- if (lc->nowait != qry->rowNoWait)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot use both wait and NOWAIT in one query")));
- }
- qry->forUpdate = lc->forUpdate;
- qry->rowNoWait = lc->nowait;
-
- CheckSelectLocking(qry, lc->forUpdate);
+ CheckSelectLocking(qry);
/* make a clause we can pass down to subqueries to select all rels */
allrels = makeNode(LockingClause);
allrels->lockedRels = NIL; /* indicates all rels */
allrels->forUpdate = lc->forUpdate;
- allrels->nowait = lc->nowait;
-
- rowMarks = qry->rowMarks;
+ allrels->noWait = lc->noWait;
if (lockedRels == NIL)
{
@@ -2831,8 +2810,7 @@ transformLockingClause(Query *qry, LockingClause *lc)
switch (rte->rtekind)
{
case RTE_RELATION:
- /* use list_append_unique to avoid duplicates */
- rowMarks = list_append_unique_int(rowMarks, i);
+ applyLockingClause(qry, i, lc->forUpdate, lc->noWait);
rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
break;
case RTE_SUBQUERY:
@@ -2867,8 +2845,8 @@ transformLockingClause(Query *qry, LockingClause *lc)
switch (rte->rtekind)
{
case RTE_RELATION:
- /* use list_append_unique to avoid duplicates */
- rowMarks = list_append_unique_int(rowMarks, i);
+ applyLockingClause(qry, i,
+ lc->forUpdate, lc->noWait);
rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
break;
case RTE_SUBQUERY:
@@ -2909,8 +2887,41 @@ transformLockingClause(Query *qry, LockingClause *lc)
relname)));
}
}
+}
- qry->rowMarks = rowMarks;
+/*
+ * Record locking info for a single rangetable item
+ */
+void
+applyLockingClause(Query *qry, Index rtindex, bool forUpdate, bool noWait)
+{
+ RowMarkClause *rc;
+
+ /* Check for pre-existing entry for same rtindex */
+ if ((rc = get_rowmark(qry, rtindex)) != NULL)
+ {
+ /*
+ * If the same RTE is specified both FOR UPDATE and FOR SHARE,
+ * treat it as FOR UPDATE. (Reasonable, since you can't take
+ * both a shared and exclusive lock at the same time; it'll
+ * end up being exclusive anyway.)
+ *
+ * We also consider that NOWAIT wins if it's specified both ways.
+ * This is a bit more debatable but raising an error doesn't
+ * seem helpful. (Consider for instance SELECT FOR UPDATE NOWAIT
+ * from a view that internally contains a plain FOR UPDATE spec.)
+ */
+ rc->forUpdate |= forUpdate;
+ rc->noWait |= noWait;
+ return;
+ }
+
+ /* Make a new RowMarkClause */
+ rc = makeNode(RowMarkClause);
+ rc->rti = rtindex;
+ rc->forUpdate = forUpdate;
+ rc->noWait = noWait;
+ qry->rowMarks = lappend(qry->rowMarks, rc);
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8870261e44..69e7a20142 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.543 2006/04/27 00:33:45 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.544 2006/04/30 18:30:39 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -96,7 +96,7 @@ static List *check_func_name(List *names);
static List *extractArgTypes(List *parameters);
static SelectStmt *findLeftmostSelect(SelectStmt *node);
static void insertSelectOptions(SelectStmt *stmt,
- List *sortClause, Node *lockingClause,
+ List *sortClause, List *lockingClause,
Node *limitOffset, Node *limitCount);
static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg);
static Node *doNegate(Node *n, int location);
@@ -253,7 +253,8 @@ static void doNegateFloat(Value *v);
%type OnCommitOption
%type OptWithOids
-%type for_locking_clause opt_for_locking_clause
+%type for_locking_item
+%type for_locking_clause opt_for_locking_clause for_locking_items
%type locked_rels_list
%type opt_all
@@ -5400,7 +5401,7 @@ select_no_parens:
simple_select { $$ = $1; }
| select_clause sort_clause
{
- insertSelectOptions((SelectStmt *) $1, $2, NULL,
+ insertSelectOptions((SelectStmt *) $1, $2, NIL,
NULL, NULL);
$$ = $1;
}
@@ -5644,12 +5645,27 @@ having_clause:
;
for_locking_clause:
+ for_locking_items { $$ = $1; }
+ | FOR READ ONLY { $$ = NIL; }
+ ;
+
+opt_for_locking_clause:
+ for_locking_clause { $$ = $1; }
+ | /* EMPTY */ { $$ = NIL; }
+ ;
+
+for_locking_items:
+ for_locking_item { $$ = list_make1($1); }
+ | for_locking_items for_locking_item { $$ = lappend($1, $2); }
+ ;
+
+for_locking_item:
FOR UPDATE locked_rels_list opt_nowait
{
LockingClause *n = makeNode(LockingClause);
n->lockedRels = $3;
n->forUpdate = TRUE;
- n->nowait = $4;
+ n->noWait = $4;
$$ = (Node *) n;
}
| FOR SHARE locked_rels_list opt_nowait
@@ -5657,15 +5673,9 @@ for_locking_clause:
LockingClause *n = makeNode(LockingClause);
n->lockedRels = $3;
n->forUpdate = FALSE;
- n->nowait = $4;
+ n->noWait = $4;
$$ = (Node *) n;
}
- | FOR READ ONLY { $$ = NULL; }
- ;
-
-opt_for_locking_clause:
- for_locking_clause { $$ = $1; }
- | /* EMPTY */ { $$ = NULL; }
;
locked_rels_list:
@@ -8976,7 +8986,7 @@ findLeftmostSelect(SelectStmt *node)
*/
static void
insertSelectOptions(SelectStmt *stmt,
- List *sortClause, Node *lockingClause,
+ List *sortClause, List *lockingClause,
Node *limitOffset, Node *limitCount)
{
/*
@@ -8991,14 +9001,8 @@ insertSelectOptions(SelectStmt *stmt,
errmsg("multiple ORDER BY clauses not allowed")));
stmt->sortClause = sortClause;
}
- if (lockingClause)
- {
- if (stmt->lockingClause)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("multiple FOR UPDATE/FOR SHARE clauses not allowed")));
- stmt->lockingClause = (LockingClause *) lockingClause;
- }
+ /* We can handle multiple locking clauses, though */
+ stmt->lockingClause = list_concat(stmt->lockingClause, lockingClause);
if (limitOffset)
{
if (stmt->limitOffset)
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index d3e138e8ec..10f71712ff 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.122 2006/03/23 00:19:30 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.123 2006/04/30 18:30:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1001,6 +1001,8 @@ addRangeTableEntryForJoin(ParseState *pstate,
/*
* Has the specified refname been selected FOR UPDATE/FOR SHARE?
+ *
+ * Note: we pay no attention to whether it's FOR UPDATE vs FOR SHARE.
*/
static bool
isLockedRel(ParseState *pstate, char *refname)
@@ -1008,9 +1010,13 @@ isLockedRel(ParseState *pstate, char *refname)
/* Outer loop to check parent query levels as well as this one */
while (pstate != NULL)
{
- if (pstate->p_locking_clause)
+ ListCell *l;
+
+ foreach(l, pstate->p_locking_clause)
{
- if (pstate->p_locking_clause->lockedRels == NIL)
+ LockingClause *lc = (LockingClause *) lfirst(l);
+
+ if (lc->lockedRels == NIL)
{
/* all tables used in query */
return true;
@@ -1018,11 +1024,11 @@ isLockedRel(ParseState *pstate, char *refname)
else
{
/* just the named tables */
- ListCell *l;
+ ListCell *l2;
- foreach(l, pstate->p_locking_clause->lockedRels)
+ foreach(l2, lc->lockedRels)
{
- char *rname = strVal(lfirst(l));
+ char *rname = strVal(lfirst(l2));
if (strcmp(refname, rname) == 0)
return true;
@@ -1702,6 +1708,26 @@ get_tle_by_resno(List *tlist, AttrNumber resno)
return NULL;
}
+/*
+ * Given a Query and rangetable index, return relation's RowMarkClause if any
+ *
+ * Returns NULL if relation is not selected FOR UPDATE/SHARE
+ */
+RowMarkClause *
+get_rowmark(Query *qry, Index rtindex)
+{
+ ListCell *l;
+
+ foreach(l, qry->rowMarks)
+ {
+ RowMarkClause *rc = (RowMarkClause *) lfirst(l);
+
+ if (rc->rti == rtindex)
+ return rc;
+ }
+ return NULL;
+}
+
/*
* given relation and att name, return attnum of variable
*
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index fec4552c9c..5965bf5ae4 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.80 2006/04/04 19:35:35 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.81 2006/04/30 18:30:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -432,7 +432,7 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod)
stmt->sortClause != NIL ||
stmt->limitOffset != NULL ||
stmt->limitCount != NULL ||
- stmt->lockingClause != NULL ||
+ stmt->lockingClause != NIL ||
stmt->op != SETOP_NONE)
goto fail;
if (list_length(stmt->targetList) != 1)
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 6d1ace66f1..d3052f8da2 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.162 2006/04/05 22:11:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.163 2006/04/30 18:30:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -130,7 +130,7 @@ AcquireRewriteLocks(Query *parsetree)
*/
if (rt_index == parsetree->resultRelation)
lockmode = RowExclusiveLock;
- else if (list_member_int(parsetree->rowMarks, rt_index))
+ else if (get_rowmark(parsetree, rt_index))
lockmode = RowShareLock;
else
lockmode = AccessShareLock;
@@ -907,6 +907,7 @@ ApplyRetrieveRule(Query *parsetree,
Query *rule_action;
RangeTblEntry *rte,
*subrte;
+ RowMarkClause *rc;
if (list_length(rule->actions) != 1)
elog(ERROR, "expected just one rule action");
@@ -954,20 +955,20 @@ ApplyRetrieveRule(Query *parsetree,
/*
* FOR UPDATE/SHARE of view?
*/
- if (list_member_int(parsetree->rowMarks, rt_index))
+ if ((rc = get_rowmark(parsetree, rt_index)) != NULL)
{
/*
* Remove the view from the list of rels that will actually be marked
- * FOR UPDATE/SHARE by the executor. It will still be access- checked
+ * FOR UPDATE/SHARE by the executor. It will still be access-checked
* for write access, though.
*/
- parsetree->rowMarks = list_delete_int(parsetree->rowMarks, rt_index);
+ parsetree->rowMarks = list_delete_ptr(parsetree->rowMarks, rc);
/*
* Set up the view's referenced tables as if FOR UPDATE/SHARE.
*/
- markQueryForLocking(rule_action, parsetree->forUpdate,
- parsetree->rowNoWait, true);
+ markQueryForLocking(rule_action, rc->forUpdate,
+ rc->noWait, true);
}
return parsetree;
@@ -987,20 +988,6 @@ markQueryForLocking(Query *qry, bool forUpdate, bool noWait, bool skipOldNew)
Index rti = 0;
ListCell *l;
- if (qry->rowMarks)
- {
- if (forUpdate != qry->forUpdate)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot use both FOR UPDATE and FOR SHARE in one query")));
- if (noWait != qry->rowNoWait)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot use both wait and NOWAIT in one query")));
- }
- qry->forUpdate = forUpdate;
- qry->rowNoWait = noWait;
-
foreach(l, qry->rtable)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
@@ -1014,7 +1001,7 @@ markQueryForLocking(Query *qry, bool forUpdate, bool noWait, bool skipOldNew)
if (rte->rtekind == RTE_RELATION)
{
- qry->rowMarks = list_append_unique_int(qry->rowMarks, rti);
+ applyLockingClause(qry, rti, forUpdate, noWait);
rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
}
else if (rte->rtekind == RTE_SUBQUERY)
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 175df7a695..906e7c4a04 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.98 2006/04/05 22:11:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.99 2006/04/30 18:30:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -240,7 +240,11 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up)
if (qry->resultRelation)
qry->resultRelation += offset;
foreach(l, qry->rowMarks)
- lfirst_int(l) += offset;
+ {
+ RowMarkClause *rc = (RowMarkClause *) lfirst(l);
+
+ rc->rti += offset;
+ }
}
query_tree_walker(qry, OffsetVarNodes_walker,
(void *) &context, 0);
@@ -395,8 +399,10 @@ ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
qry->resultRelation = new_index;
foreach(l, qry->rowMarks)
{
- if (lfirst_int(l) == rt_index)
- lfirst_int(l) = new_index;
+ RowMarkClause *rc = (RowMarkClause *) lfirst(l);
+
+ if (rc->rti == rt_index)
+ rc->rti = new_index;
}
}
query_tree_walker(qry, ChangeVarNodes_walker,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index b54d50702b..5729b58450 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.256 2006/04/15 17:45:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.257 2006/04/30 18:30:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1796,7 +1796,7 @@ CreateQueryTag(Query *parsetree)
tag = "SELECT INTO";
else if (parsetree->rowMarks != NIL)
{
- if (parsetree->forUpdate)
+ if (((RowMarkClause *) linitial(parsetree->rowMarks))->forUpdate)
tag = "SELECT FOR UPDATE";
else
tag = "SELECT FOR SHARE";
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index c0db64bf89..67897a2938 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -2,7 +2,7 @@
* ruleutils.c - Functions to convert stored expressions/querytrees
* back to source text
*
- * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.220 2006/04/22 01:26:00 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.221 2006/04/30 18:30:40 tgl Exp $
**********************************************************************/
#include "postgres.h"
@@ -1858,27 +1858,21 @@ get_select_query_def(Query *query, deparse_context *context,
get_rule_expr(query->limitCount, context, false);
}
- /* Add the FOR UPDATE/SHARE clause if present */
- if (query->rowMarks != NIL)
+ /* Add FOR UPDATE/SHARE clauses if present */
+ foreach(l, query->rowMarks)
{
- if (query->forUpdate)
- appendContextKeyword(context, " FOR UPDATE OF ",
+ RowMarkClause *rc = (RowMarkClause *) lfirst(l);
+ RangeTblEntry *rte = rt_fetch(rc->rti, query->rtable);
+
+ if (rc->forUpdate)
+ appendContextKeyword(context, " FOR UPDATE",
-PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
else
- appendContextKeyword(context, " FOR SHARE OF ",
+ appendContextKeyword(context, " FOR SHARE",
-PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
- sep = "";
- foreach(l, query->rowMarks)
- {
- int rtindex = lfirst_int(l);
- RangeTblEntry *rte = rt_fetch(rtindex, query->rtable);
-
- appendStringInfo(buf, "%s%s",
- sep,
- quote_identifier(rte->eref->aliasname));
- sep = ", ";
- }
- if (query->rowNoWait)
+ appendStringInfo(buf, " OF %s",
+ quote_identifier(rte->eref->aliasname));
+ if (rc->noWait)
appendStringInfo(buf, " NOWAIT");
}
}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index f3c037f97c..4c76aa70fa 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.327 2006/04/30 02:09:07 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.328 2006/04/30 18:30:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200604291
+#define CATALOG_VERSION_NO 200604301
#endif
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 2d346df359..7a9a651e1e 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.149 2006/03/05 15:58:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.150 2006/04/30 18:30:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -321,8 +321,6 @@ typedef struct EState
uint32 es_processed; /* # of tuples processed */
Oid es_lastoid; /* last oid processed (by INSERT) */
List *es_rowMarks; /* not good place, but there is no other */
- bool es_forUpdate; /* true = FOR UPDATE, false = FOR SHARE */
- bool es_rowNoWait; /* FOR UPDATE/SHARE NOWAIT option */
bool es_instrument; /* true requests runtime instrumentation */
bool es_select_into; /* true if doing SELECT INTO */
@@ -351,6 +349,8 @@ typedef struct ExecRowMark
{
Relation relation; /* opened and RowShareLock'd relation */
Index rti; /* its range table index */
+ bool forUpdate; /* true = FOR UPDATE, false = FOR SHARE */
+ bool noWait; /* NOWAIT option */
char resname[32]; /* name for its ctid junk attribute */
} ExecRowMark;
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index fe5ab018f5..53f3ee1d61 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.185 2006/04/15 17:45:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.186 2006/04/30 18:30:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -320,6 +320,7 @@ typedef enum NodeTag
T_InhRelation,
T_FunctionParameter,
T_LockingClause,
+ T_RowMarkClause,
/*
* TAGS FOR RANDOM OTHER STUFF
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index a0a31be51b..896b426370 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.309 2006/04/30 02:09:07 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.310 2006/04/30 18:30:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -57,7 +57,7 @@ typedef uint32 AclMode; /* a bitmask of privilege bits */
#define ACL_USAGE (1<<8) /* for languages and namespaces */
#define ACL_CREATE (1<<9) /* for namespaces and databases */
#define ACL_CREATE_TEMP (1<<10) /* for databases */
-#define ACL_CONNECT (1<<11) /* for database connection privilege */
+#define ACL_CONNECT (1<<11) /* for databases */
#define N_ACL_RIGHTS 12 /* 1 plus the last 1<