<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/select.sgml,v 1.90 2005/11/01 21:09:51 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/select.sgml,v 1.91 2006/04/30 18:30:38 tgl Exp $
PostgreSQL documentation
-->
[ ORDER BY <replaceable class="parameter">expression</replaceable> [ ASC | DESC | USING <replaceable class="parameter">operator</replaceable> ] [, ...] ]
[ LIMIT { <replaceable class="parameter">count</replaceable> | ALL } ]
[ OFFSET <replaceable class="parameter">start</replaceable> ]
- [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] ]
+ [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] [...] ]
where <replaceable class="parameter">from_item</replaceable> can be one of:
<listitem>
<para>
- If the <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal>
- clause is specified, the
+ If <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal>
+ is specified, the
<command>SELECT</command> statement locks the selected rows
against concurrent updates. (See <xref linkend="sql-for-update-share"
endterm="sql-for-update-share-title"> below.)
from performing <command>SELECT FOR SHARE</command>.
</para>
- <para>
- It is currently not allowed for a single <command>SELECT</command>
- statement to include both <literal>FOR UPDATE</literal> and
- <literal>FOR SHARE</literal>, nor can different parts of the statement use
- both <literal>NOWAIT</> and normal waiting mode.
- </para>
-
<para>
If specific tables are named in <literal>FOR UPDATE</literal>
or <literal>FOR SHARE</literal>,
then only rows coming from those tables are locked; any other
tables used in the <command>SELECT</command> are simply read as
- usual.
+ usual. A <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal>
+ clause without a table list affects all tables used in the command.
+ If <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal> is
+ applied to a view or sub-query, it affects all tables used in
+ the view or sub-query.
+ </para>
+
+ <para>
+ Multiple <literal>FOR UPDATE</literal> and <literal>FOR SHARE</literal>
+ 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 <literal>FOR UPDATE</literal> and
+ <literal>FOR SHARE</literal> clauses, then it is processed as
+ <literal>FOR UPDATE</literal>. Similarly, a table is processed
+ as <literal>NOWAIT</> if that is specified in any of the clauses
+ affecting it.
</para>
<para>
<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/select_into.sgml,v 1.36 2005/08/01 20:31:04 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/select_into.sgml,v 1.37 2006/04/30 18:30:38 tgl Exp $
PostgreSQL documentation
-->
[ ORDER BY <replaceable class="PARAMETER">expression</replaceable> [ ASC | DESC | USING <replaceable class="PARAMETER">operator</replaceable> ] [, ...] ]
[ LIMIT { <replaceable class="PARAMETER">count</replaceable> | ALL } ]
[ OFFSET <replaceable class="PARAMETER">start</replaceable> ]
- [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] ]
+ [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] [...] ]
</synopsis>
</refsynopsisdiv>
-<!-- $PostgreSQL: pgsql/doc/src/sgml/sql.sgml,v 1.39 2006/03/10 19:10:49 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/sql.sgml,v 1.40 2006/04/30 18:30:38 tgl Exp $ -->
<chapter id="sql-intro">
<title>SQL</title>
[ ORDER BY <replaceable class="PARAMETER">expression</replaceable> [ ASC | DESC | USING <replaceable class="PARAMETER">operator</replaceable> ] [, ...] ]
[ LIMIT { <replaceable class="PARAMETER">count</replaceable> | ALL } ]
[ OFFSET <replaceable class="PARAMETER">start</replaceable> ]
- [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] ]
+ [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] [...] ]
</synopsis>
</para>
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
Relation intoRelationDesc;
bool do_select_into;
TupleDesc tupType;
+ ListCell *l;
/*
* Do permissions checks. It's sufficient to examine the query's top
* parseTree->resultRelations identifies them all
*/
ResultRelInfo *resultRelInfo;
- ListCell *l;
numResultRelations = list_length(resultRelations);
resultRelInfos = (ResultRelInfo *)
* 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;
-
- 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);
- }
+ RowMarkClause *rc = (RowMarkClause *) lfirst(l);
+ Oid relid = getrelid(rc->rti, rangeTable);
+ Relation relation;
+ ExecRowMark *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);
}
/*
tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
- if (estate->es_forUpdate)
+ if (erm->forUpdate)
lockmode = LockTupleExclusive;
else
lockmode = LockTupleShared;
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)
{
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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
* 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 $
*
*-------------------------------------------------------------------------
*/
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)
{
COPY_NODE_FIELD(lockedRels);
COPY_SCALAR_FIELD(forUpdate);
- COPY_SCALAR_FIELD(nowait);
+ COPY_SCALAR_FIELD(noWait);
return newnode;
}
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);
COPY_NODE_FIELD(sortClause);
COPY_NODE_FIELD(limitOffset);
COPY_NODE_FIELD(limitCount);
+ COPY_NODE_FIELD(rowMarks);
COPY_NODE_FIELD(setOperations);
COPY_NODE_FIELD(resultRelations);
case T_GroupClause:
retval = _copyGroupClause(from);
break;
+ case T_RowMarkClause:
+ retval = _copyRowMarkClause(from);
+ break;
case T_FkConstraint:
retval = _copyFkConstraint(from);
break;
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
COMPARE_NODE_FIELD(sortClause);
COMPARE_NODE_FIELD(limitOffset);
COMPARE_NODE_FIELD(limitCount);
+ COMPARE_NODE_FIELD(rowMarks);
COMPARE_NODE_FIELD(setOperations);
COMPARE_NODE_FIELD(resultRelations);
{
COMPARE_NODE_FIELD(lockedRels);
COMPARE_SCALAR_FIELD(forUpdate);
- COMPARE_SCALAR_FIELD(nowait);
+ COMPARE_SCALAR_FIELD(noWait);
return true;
}
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)
{
/* 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;
*
*
* 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*
WRITE_NODE_FIELD(lockedRels);
WRITE_BOOL_FIELD(forUpdate);
- WRITE_BOOL_FIELD(nowait);
+ WRITE_BOOL_FIELD(noWait);
}
static void
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);
WRITE_NODE_FIELD(sortClause);
WRITE_NODE_FIELD(limitOffset);
WRITE_NODE_FIELD(limitCount);
+ WRITE_NODE_FIELD(rowMarks);
WRITE_NODE_FIELD(setOperations);
WRITE_NODE_FIELD(resultRelations);
}
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)
{
case T_GroupClause:
_outGroupClause(str, obj);
break;
+ case T_RowMarkClause:
+ _outRowMarkClause(str, obj);
+ break;
case T_SetOperationStmt:
_outSetOperationStmt(str, obj);
break;
*
*
* 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
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);
READ_NODE_FIELD(sortClause);
READ_NODE_FIELD(limitOffset);
READ_NODE_FIELD(limitCount);
+ READ_NODE_FIELD(rowMarks);
READ_NODE_FIELD(setOperations);
READ_NODE_FIELD(resultRelations);
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
*/
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))
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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")));
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/*
* 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
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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
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,
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
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;
* 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 $
*
*-------------------------------------------------------------------------
*/
{
Query *qry = makeNode(Query);
Node *qual;
+ ListCell *l;
qry->commandType = CMD_SELECT;
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;
}
List *sortClause;
Node *limitOffset;
Node *limitCount;
- LockingClause *lockingClause;
+ List *lockingClause;
Node *node;
ListCell *left_tlist,
- *dtlist;
+ *dtlist,
+ *l;
List *targetvars,
*targetnames,
*sv_relnamespace,
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)
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;
}
/* 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.
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)
{
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:
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:
relname)));
}
}
+}
+
+/*
+ * 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;
+ }
- qry->rowMarks = rowMarks;
+ /* Make a new RowMarkClause */
+ rc = makeNode(RowMarkClause);
+ rc->rti = rtindex;
+ rc->forUpdate = forUpdate;
+ rc->noWait = noWait;
+ qry->rowMarks = lappend(qry->rowMarks, rc);
}
*
*
* 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
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);
%type <oncommit> OnCommitOption
%type <withoids> OptWithOids
-%type <node> for_locking_clause opt_for_locking_clause
+%type <node> for_locking_item
+%type <list> for_locking_clause opt_for_locking_clause for_locking_items
%type <list> locked_rels_list
%type <boolean> opt_all
simple_select { $$ = $1; }
| select_clause sort_clause
{
- insertSelectOptions((SelectStmt *) $1, $2, NULL,
+ insertSelectOptions((SelectStmt *) $1, $2, NIL,
NULL, NULL);
$$ = $1;
}
;
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
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:
*/
static void
insertSelectOptions(SelectStmt *stmt,
- List *sortClause, Node *lockingClause,
+ List *sortClause, List *lockingClause,
Node *limitOffset, Node *limitCount)
{
/*
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)
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/*
* 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)
/* 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;
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;
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
*
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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)
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
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;
Query *rule_action;
RangeTblEntry *rte,
*subrte;
+ RowMarkClause *rc;
if (list_length(rule->actions) != 1)
elog(ERROR, "expected just one rule action");
/*
* 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;
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);
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)
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
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,
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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";
* 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"
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");
}
}
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200604291
+#define CATALOG_VERSION_NO 200604301
#endif
* 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 $
*
*-------------------------------------------------------------------------
*/
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 */
{
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;
* 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 $
*
*-------------------------------------------------------------------------
*/
T_InhRelation,
T_FunctionParameter,
T_LockingClause,
+ T_RowMarkClause,
/*
* TAGS FOR RANDOM OTHER STUFF
* 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 $
*
*-------------------------------------------------------------------------
*/
#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<<x */
#define ACL_NO_RIGHTS 0
/* Currently, SELECT ... FOR UPDATE/FOR SHARE requires UPDATE privileges */
List *rtable; /* list of range table entries */
FromExpr *jointree; /* table join tree (FROM and WHERE clauses) */
- List *rowMarks; /* integer list of RT indexes of relations
- * that are selected FOR UPDATE/SHARE */
-
- bool forUpdate; /* true if rowMarks are FOR UPDATE, false if
- * they are FOR SHARE */
- bool rowNoWait; /* FOR UPDATE/SHARE NOWAIT option */
-
List *targetList; /* target list (of TargetEntry) */
List *groupClause; /* a list of GroupClause's */
Node *limitOffset; /* # of result tuples to skip */
Node *limitCount; /* # of result tuples to return */
+ List *rowMarks; /* a list of RowMarkClause's */
+
Node *setOperations; /* set-operation tree if this is top level of
* a UNION/INTERSECT/EXCEPT query */
NodeTag type;
List *lockedRels; /* FOR UPDATE or FOR SHARE relations */
bool forUpdate; /* true = FOR UPDATE, false = FOR SHARE */
- bool nowait; /* NOWAIT option */
+ bool noWait; /* NOWAIT option */
} LockingClause;
*/
typedef SortClause GroupClause;
+/*
+ * RowMarkClause -
+ * representation of FOR UPDATE/SHARE clauses
+ *
+ * We create a separate RowMarkClause node for each target relation
+ */
+typedef struct RowMarkClause
+{
+ NodeTag type;
+ Index rti; /* range table index of target relation */
+ bool forUpdate; /* true = FOR UPDATE, false = FOR SHARE */
+ bool noWait; /* NOWAIT option */
+} RowMarkClause;
/*****************************************************************************
* Optimizable Statements
List *sortClause; /* sort clause (a list of SortBy's) */
Node *limitOffset; /* # of result tuples to skip */
Node *limitCount; /* # of result tuples to return */
- LockingClause *lockingClause; /* FOR UPDATE/FOR SHARE */
+ List *lockingClause; /* FOR UPDATE (list of LockingClause's) */
/*
* These fields are used only in upper-level SelectStmts.
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/parser/analyze.h,v 1.32 2006/03/14 22:48:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/parser/analyze.h,v 1.33 2006/04/30 18:30:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Oid **paramTypes, int *numParams);
extern List *parse_sub_analyze(Node *parseTree, ParseState *parentParseState);
extern List *analyzeCreateSchemaStmt(CreateSchemaStmt *stmt);
-extern void CheckSelectLocking(Query *qry, bool forUpdate);
+extern void CheckSelectLocking(Query *qry);
+extern void applyLockingClause(Query *qry, Index rtindex,
+ bool forUpdate, bool noWait);
#endif /* ANALYZE_H */
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.48 2006/03/14 22:48:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.49 2006/04/30 18:30:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Oid *p_paramtypes; /* OIDs of types for $n parameter symbols */
int p_numparams; /* allocated size of p_paramtypes[] */
int p_next_resno; /* next targetlist resno to assign */
- LockingClause *p_locking_clause; /* FOR UPDATE/FOR SHARE info */
- Node *p_value_substitute; /* what to replace VALUE with, if any */
+ List *p_locking_clause; /* raw FOR UPDATE/FOR SHARE info */
+ Node *p_value_substitute; /* what to replace VALUE with, if any */
bool p_variableparams;
bool p_hasAggs;
bool p_hasSubLinks;
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/parser/parsetree.h,v 1.32 2006/03/05 15:58:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parsetree.h,v 1.33 2006/04/30 18:30:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern TargetEntry *get_tle_by_resno(List *tlist, AttrNumber resno);
+/* ----------------
+ * FOR UPDATE/SHARE info
+ * ----------------
+ */
+
+extern RowMarkClause *get_rowmark(Query *qry, Index rtindex);
+
#endif /* PARSETREE_H */