<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/select.sgml,v 1.127 2009/10/27 17:11:18 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/select.sgml,v 1.128 2009/10/28 14:55:37 tgl Exp $
PostgreSQL documentation
-->
has already locked a selected row or rows, <command>SELECT FOR
UPDATE</command> will wait for the other transaction to complete,
and will then lock and return the updated row (or no row, if the
- row was deleted). For further discussion see <xref
+ row was deleted). Within a <literal>SERIALIZABLE</> transaction,
+ however, an error will be thrown if a row to be locked has changed
+ since the transaction started. For further discussion see <xref
linkend="mvcc">.
</para>
- <para>
- To prevent the operation from waiting for other transactions to commit,
- use the <literal>NOWAIT</> option. <command>SELECT FOR UPDATE
- NOWAIT</command> reports an error, rather than waiting, if a selected row
- cannot be locked immediately. Note that <literal>NOWAIT</> applies only
- to the row-level lock(s) — the required <literal>ROW SHARE</literal>
- table-level lock is still taken in the ordinary way (see
- <xref linkend="mvcc">). You can use the <literal>NOWAIT</> option of
- <xref linkend="sql-lock" endterm="sql-lock-title">
- if you need to acquire the table-level lock without waiting.
- </para>
-
<para>
<literal>FOR SHARE</literal> behaves similarly, except that it
acquires a shared rather than exclusive lock on each retrieved
from performing <command>SELECT FOR SHARE</command>.
</para>
+ <para>
+ To prevent the operation from waiting for other transactions to commit,
+ use the <literal>NOWAIT</> option. With <literal>NOWAIT</>, the statement
+ reports an error, rather than waiting, if a selected row
+ cannot be locked immediately. Note that <literal>NOWAIT</> applies only
+ to the row-level lock(s) — the required <literal>ROW SHARE</literal>
+ table-level lock is still taken in the ordinary way (see
+ <xref linkend="mvcc">). You can use
+ <xref linkend="sql-lock" endterm="sql-lock-title">
+ with the <literal>NOWAIT</> option first,
+ if you need to acquire the table-level lock without waiting.
+ </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. A <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal>
- clause without a table list affects all tables used in the command.
+ clause without a table list affects all tables used in the statement.
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.
individual table rows; for example they cannot be used with aggregation.
</para>
+ <para>
+ When <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal>
+ appears at the top level of a <command>SELECT</> query, the rows that
+ are locked are exactly those that are returned by the query; in the
+ case of a join query, the rows locked are those that contribute to
+ returned join rows. In addition, rows that satisfied the query
+ conditions as of the query snapshot will be locked, although they
+ will not be returned if they have since been updated to not satisfy
+ the query conditions. If a <literal>LIMIT</> is used, locking stops
+ once enough rows have been returned to satisfy the limit (but note that
+ rows skipped over by <literal>OFFSET</> will get locked). Similarly,
+ if <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal>
+ is used in a cursor's query, only rows actually fetched or stepped past
+ by the cursor will be locked.
+ </para>
+
+ <para>
+ When <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal>
+ appears in a sub-<command>SELECT</>, the rows locked are those
+ returned to the outer query by the sub-query. This might involve
+ fewer rows than inspection of the sub-query alone would suggest,
+ since conditions from the outer query might be used to optimize
+ execution of the sub-query. For example,
+<programlisting>
+SELECT * FROM (SELECT * FROM mytable FOR UPDATE) ss WHERE col1 = 5;
+</programlisting>
+ will lock only rows having <literal>col1 = 5</>, even though that
+ condition is not textually within the sub-query.
+ </para>
+
<caution>
<para>
Avoid locking a row and then modifying it within a later savepoint or
<caution>
<para>
- It is possible for a <command>SELECT</> command using both
- <literal>LIMIT</literal> and <literal>FOR UPDATE/SHARE</literal>
- clauses to return fewer rows than specified by <literal>LIMIT</literal>.
- This is because <literal>LIMIT</> is applied first. The command
- selects the specified number of rows,
- but might then block trying to obtain a lock on one or more of them.
- Once the <literal>SELECT</> unblocks, the row might have been deleted
- or updated so that it does not meet the query <literal>WHERE</> condition
- anymore, in which case it will not be returned.
- </para>
- </caution>
-
- <caution>
- <para>
- Similarly, it is possible for a <command>SELECT</> command
- using <literal>ORDER BY</literal> and <literal>FOR
- UPDATE/SHARE</literal> to return rows out of order. This is
- because <literal>ORDER BY</> is applied first. The command
- orders the result, but might then block trying to obtain a lock
- on one or more of the rows. Once the <literal>SELECT</>
- unblocks, one of the ordered columns might have been modified
- and be returned out of order. A workaround is to perform
- <command>SELECT ... FOR UPDATE/SHARE</> and then <command>SELECT
- ... ORDER BY</>.
+ It is possible for a <command>SELECT</> command using <literal>ORDER
+ BY</literal> and <literal>FOR UPDATE/SHARE</literal> to return rows out of
+ order. This is because <literal>ORDER BY</> is applied first.
+ The command sorts the result, but might then block trying to obtain a lock
+ on one or more of the rows. Once the <literal>SELECT</> unblocks, some
+ of the ordering column values might have been modified, leading to those
+ rows appearing to be out of order (though they are in order in terms
+ of the original column values). This can be worked around at need by
+ placing the <literal>FOR UPDATE/SHARE</literal> clause in a sub-query,
+ for example
+<programlisting>
+SELECT * FROM (SELECT * FROM mytable FOR UPDATE) ss ORDER BY column1;
+</programlisting>
+ Note that this will result in locking all rows of <structname>mytable</>,
+ whereas <literal>FOR UPDATE</> at the top level would lock only the
+ actually returned rows. This can make for a significant performance
+ difference, particularly if the <literal>ORDER BY</> is combined with
+ <literal>LIMIT</> or other restrictions. So this technique is recommended
+ only if concurrent updates of the ordering columns are expected and a
+ strictly sorted result is required.
</para>
</caution>
</refsect2>
used by <productname>MySQL</productname>. The SQL:2008 standard
has introduced the clauses <literal>OFFSET ... FETCH {FIRST|NEXT}
...</literal> for the same functionality, as shown above
- in <xref linkend="sql-limit" endterm="sql-limit-title">, and this
+ in <xref linkend="sql-limit" endterm="sql-limit-title">. This
syntax is also used by <productname>IBM DB2</productname>.
(Applications written for <productname>Oracle</productname>
frequently use a workaround involving the automatically
- generated <literal>rownum</literal> column, not available in
+ generated <literal>rownum</literal> column, which is not available in
PostgreSQL, to implement the effects of these clauses.)
</para>
</refsect2>
+ <refsect2>
+ <title><literal>FOR UPDATE</> and <literal>FOR SHARE</></title>
+
+ <para>
+ Although <literal>FOR UPDATE</> appears in the SQL standard, the
+ standard allows it only as an option of <command>DECLARE CURSOR</>.
+ <productname>PostgreSQL</productname> allows it in any <command>SELECT</>
+ query as well as in sub-<command>SELECT</>s, but this is an extension.
+ The <literal>FOR SHARE</> variant, and the <literal>NOWAIT</> option,
+ do not appear in the standard.
+ </para>
+ </refsect2>
+
<refsect2>
<title>Nonstandard Clauses</title>
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.449 2009/10/26 02:26:31 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.450 2009/10/28 14:55:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
COPY_SCALAR_FIELD(rti);
COPY_SCALAR_FIELD(forUpdate);
COPY_SCALAR_FIELD(noWait);
+ COPY_SCALAR_FIELD(pushedDown);
return newnode;
}
COPY_SCALAR_FIELD(hasSubLinks);
COPY_SCALAR_FIELD(hasDistinctOn);
COPY_SCALAR_FIELD(hasRecursive);
+ COPY_SCALAR_FIELD(hasForUpdate);
COPY_NODE_FIELD(cteList);
COPY_NODE_FIELD(rtable);
COPY_NODE_FIELD(jointree);
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.371 2009/10/26 02:26:31 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.372 2009/10/28 14:55:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
COMPARE_SCALAR_FIELD(hasSubLinks);
COMPARE_SCALAR_FIELD(hasDistinctOn);
COMPARE_SCALAR_FIELD(hasRecursive);
+ COMPARE_SCALAR_FIELD(hasForUpdate);
COMPARE_NODE_FIELD(cteList);
COMPARE_NODE_FIELD(rtable);
COMPARE_NODE_FIELD(jointree);
COMPARE_SCALAR_FIELD(rti);
COMPARE_SCALAR_FIELD(forUpdate);
COMPARE_SCALAR_FIELD(noWait);
+ COMPARE_SCALAR_FIELD(pushedDown);
return true;
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.370 2009/10/26 02:26:31 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.371 2009/10/28 14:55:38 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
WRITE_BOOL_FIELD(hasSubLinks);
WRITE_BOOL_FIELD(hasDistinctOn);
WRITE_BOOL_FIELD(hasRecursive);
+ WRITE_BOOL_FIELD(hasForUpdate);
WRITE_NODE_FIELD(cteList);
WRITE_NODE_FIELD(rtable);
WRITE_NODE_FIELD(jointree);
WRITE_UINT_FIELD(rti);
WRITE_BOOL_FIELD(forUpdate);
WRITE_BOOL_FIELD(noWait);
+ WRITE_BOOL_FIELD(pushedDown);
}
static void
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.226 2009/10/26 02:26:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.227 2009/10/28 14:55:38 tgl Exp $
*
* NOTES
* Path and Plan nodes do not have any readfuncs support, because we
READ_BOOL_FIELD(hasSubLinks);
READ_BOOL_FIELD(hasDistinctOn);
READ_BOOL_FIELD(hasRecursive);
+ READ_BOOL_FIELD(hasForUpdate);
READ_NODE_FIELD(cteList);
READ_NODE_FIELD(rtable);
READ_NODE_FIELD(jointree);
READ_UINT_FIELD(rti);
READ_BOOL_FIELD(forUpdate);
READ_BOOL_FIELD(noWait);
+ READ_BOOL_FIELD(pushedDown);
READ_DONE();
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.260 2009/10/26 02:26:33 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.261 2009/10/28 14:55:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
}
/*
- * If there is a LIMIT/OFFSET clause, add the LIMIT node.
- */
- if (parse->limitCount || parse->limitOffset)
- {
- result_plan = (Plan *) make_limit(result_plan,
- parse->limitOffset,
- parse->limitCount,
- offset_est,
- count_est);
- }
-
- /*
- * Finally, if there is a FOR UPDATE/SHARE clause, add the LockRows node.
+ * If there is a FOR UPDATE/SHARE clause, add the LockRows node.
* (Note: we intentionally test parse->rowMarks not root->rowMarks here.
* If there are only non-locking rowmarks, they should be handled by
* the ModifyTable node instead.)
result_plan = (Plan *) make_lockrows(result_plan,
root->rowMarks,
SS_assign_special_param(root));
+ /*
+ * The result can no longer be assumed sorted, since locking might
+ * cause the sort key columns to be replaced with new values.
+ */
+ current_pathkeys = NIL;
+ }
+
+ /*
+ * Finally, if there is a LIMIT/OFFSET clause, add the LIMIT node.
+ */
+ if (parse->limitCount || parse->limitOffset)
+ {
+ result_plan = (Plan *) make_limit(result_plan,
+ parse->limitOffset,
+ parse->limitCount,
+ offset_est,
+ count_est);
}
/* Compute result-relations list if needed */
/*
* Convert RowMarkClauses to PlanRowMark representation.
- *
- * Note: currently, it is syntactically impossible to have FOR UPDATE
- * applied to an update/delete target rel. If that ever becomes
- * possible, we should drop the target from the PlanRowMark list.
*/
prowmarks = NIL;
foreach(l, parse->rowMarks)
{
RowMarkClause *rc = (RowMarkClause *) lfirst(l);
- PlanRowMark *newrc = makeNode(PlanRowMark);
+ RangeTblEntry *rte = rt_fetch(rc->rti, parse->rtable);
+ PlanRowMark *newrc;
+ /*
+ * Currently, it is syntactically impossible to have FOR UPDATE
+ * applied to an update/delete target rel. If that ever becomes
+ * possible, we should drop the target from the PlanRowMark list.
+ */
Assert(rc->rti != parse->resultRelation);
+
+ /*
+ * Ignore RowMarkClauses for subqueries; they aren't real tables
+ * and can't support true locking. Subqueries that got flattened
+ * into the main query should be ignored completely. Any that didn't
+ * will get ROW_MARK_COPY items in the next loop.
+ */
+ if (rte->rtekind != RTE_RELATION)
+ continue;
+
rels = bms_del_member(rels, rc->rti);
+ newrc = makeNode(PlanRowMark);
newrc->rti = newrc->prti = rc->rti;
if (rc->forUpdate)
newrc->markType = ROW_MARK_EXCLUSIVE;
continue;
newrc = makeNode(PlanRowMark);
-
newrc->rti = newrc->prti = i;
/* real tables support REFERENCE, anything else needs COPY */
if (rte->rtekind == RTE_RELATION)
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.68 2009/10/26 02:26:35 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.69 2009/10/28 14:55:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* Can't pull up a subquery involving grouping, aggregation, sorting,
* limiting, or WITH. (XXX WITH could possibly be allowed later)
+ *
+ * We also don't pull up a subquery that has explicit FOR UPDATE/SHARE
+ * clauses, because pullup would cause the locking to occur semantically
+ * higher than it should. Implicit FOR UPDATE/SHARE is okay because
+ * in that case the locking was originally declared in the upper query
+ * anyway.
*/
if (subquery->hasAggs ||
subquery->hasWindowFuncs ||
subquery->distinctClause ||
subquery->limitOffset ||
subquery->limitCount ||
+ subquery->hasForUpdate ||
subquery->cteList)
return false;
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.394 2009/10/27 17:11:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.395 2009/10/28 14:55:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
DeclareCursorStmt *stmt);
static Query *transformExplainStmt(ParseState *pstate,
ExplainStmt *stmt);
-static void transformLockingClause(ParseState *pstate,
- Query *qry, LockingClause *lc);
+static void transformLockingClause(ParseState *pstate, Query *qry,
+ LockingClause *lc, bool pushedDown);
static bool check_parameter_resolution_walker(Node *node, ParseState *pstate);
foreach(l, stmt->lockingClause)
{
- transformLockingClause(pstate, qry, (LockingClause *) lfirst(l));
+ transformLockingClause(pstate, qry,
+ (LockingClause *) lfirst(l), false);
}
return qry;
foreach(l, lockingClause)
{
- transformLockingClause(pstate, qry, (LockingClause *) lfirst(l));
+ transformLockingClause(pstate, qry,
+ (LockingClause *) lfirst(l), false);
}
return qry;
* in rewriteHandler.c, and isLockedRefname() in parse_relation.c.
*/
static void
-transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc)
+transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc,
+ bool pushedDown)
{
List *lockedRels = lc->lockedRels;
ListCell *l;
switch (rte->rtekind)
{
case RTE_RELATION:
- applyLockingClause(qry, i, lc->forUpdate, lc->noWait);
+ applyLockingClause(qry, i,
+ lc->forUpdate, lc->noWait, pushedDown);
rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
break;
case RTE_SUBQUERY:
-
+ applyLockingClause(qry, i,
+ lc->forUpdate, lc->noWait, pushedDown);
/*
* FOR UPDATE/SHARE of subquery is propagated to all of
- * subquery's rels
+ * subquery's rels, too. We could do this later (based
+ * on the marking of the subquery RTE) but it is convenient
+ * to have local knowledge in each query level about
+ * which rels need to be opened with RowShareLock.
*/
- transformLockingClause(pstate, rte->subquery, allrels);
+ transformLockingClause(pstate, rte->subquery,
+ allrels, true);
break;
default:
/* ignore JOIN, SPECIAL, FUNCTION, VALUES, CTE RTEs */
{
case RTE_RELATION:
applyLockingClause(qry, i,
- lc->forUpdate, lc->noWait);
+ lc->forUpdate, lc->noWait,
+ pushedDown);
rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
break;
case RTE_SUBQUERY:
-
- /*
- * FOR UPDATE/SHARE of subquery is propagated to
- * all of subquery's rels
- */
- transformLockingClause(pstate, rte->subquery, allrels);
+ applyLockingClause(qry, i,
+ lc->forUpdate, lc->noWait,
+ pushedDown);
+ /* see comment above */
+ transformLockingClause(pstate, rte->subquery,
+ allrels, true);
break;
case RTE_JOIN:
ereport(ERROR,
* Record locking info for a single rangetable item
*/
void
-applyLockingClause(Query *qry, Index rtindex, bool forUpdate, bool noWait)
+applyLockingClause(Query *qry, Index rtindex,
+ bool forUpdate, bool noWait, bool pushedDown)
{
RowMarkClause *rc;
+ /* If it's an explicit clause, make sure hasForUpdate gets set */
+ if (!pushedDown)
+ qry->hasForUpdate = true;
+
/* Check for pre-existing entry for same rtindex */
if ((rc = get_parse_rowmark(qry, rtindex)) != NULL)
{
* 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.)
+ *
+ * And of course pushedDown becomes false if any clause is explicit.
*/
rc->forUpdate |= forUpdate;
rc->noWait |= noWait;
+ rc->pushedDown &= pushedDown;
return;
}
rc->rti = rtindex;
rc->forUpdate = forUpdate;
rc->noWait = noWait;
+ rc->pushedDown = pushedDown;
qry->rowMarks = lappend(qry->rowMarks, rc);
}
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.189 2009/10/27 17:11:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.190 2009/10/28 14:55:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static void rewriteValuesRTE(RangeTblEntry *rte, Relation target_relation,
List *attrnos);
static void markQueryForLocking(Query *qry, Node *jtnode,
- bool forUpdate, bool noWait);
+ bool forUpdate, bool noWait, bool pushedDown);
static List *matchLocks(CmdType event, RuleLock *rulelocks,
int varno, Query *parsetree);
static Query *fireRIRrules(Query *parsetree, List *activeRIRs);
rte->modifiedCols = NULL;
/*
- * FOR UPDATE/SHARE of view?
+ * If FOR UPDATE/SHARE of view, mark all the contained tables as
+ * implicit FOR UPDATE/SHARE, the same as the parser would have done
+ * if the view's subquery had been written out explicitly.
*/
if ((rc = get_parse_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 write access, though.
- */
- parsetree->rowMarks = list_delete_ptr(parsetree->rowMarks, rc);
-
- /*
- * Set up the view's referenced tables as if FOR UPDATE/SHARE.
- */
markQueryForLocking(rule_action, (Node *) rule_action->jointree,
- rc->forUpdate, rc->noWait);
- }
+ rc->forUpdate, rc->noWait, true);
return parsetree;
}
* to scan the jointree to determine which rels are used.
*/
static void
-markQueryForLocking(Query *qry, Node *jtnode, bool forUpdate, bool noWait)
+markQueryForLocking(Query *qry, Node *jtnode,
+ bool forUpdate, bool noWait, bool pushedDown)
{
if (jtnode == NULL)
return;
if (rte->rtekind == RTE_RELATION)
{
- applyLockingClause(qry, rti, forUpdate, noWait);
+ applyLockingClause(qry, rti, forUpdate, noWait, pushedDown);
rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
}
else if (rte->rtekind == RTE_SUBQUERY)
{
+ applyLockingClause(qry, rti, forUpdate, noWait, pushedDown);
/* FOR UPDATE/SHARE of subquery is propagated to subquery's rels */
markQueryForLocking(rte->subquery, (Node *) rte->subquery->jointree,
- forUpdate, noWait);
+ forUpdate, noWait, true);
}
/* other RTE types are unaffected by FOR UPDATE */
}
ListCell *l;
foreach(l, f->fromlist)
- markQueryForLocking(qry, lfirst(l), forUpdate, noWait);
+ markQueryForLocking(qry, lfirst(l), forUpdate, noWait, pushedDown);
}
else if (IsA(jtnode, JoinExpr))
{
JoinExpr *j = (JoinExpr *) jtnode;
- markQueryForLocking(qry, j->larg, forUpdate, noWait);
- markQueryForLocking(qry, j->rarg, forUpdate, noWait);
+ markQueryForLocking(qry, j->larg, forUpdate, noWait, pushedDown);
+ markQueryForLocking(qry, j->rarg, forUpdate, noWait, pushedDown);
}
else
elog(ERROR, "unrecognized node type: %d",
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.310 2009/10/14 22:14:23 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.311 2009/10/28 14:55:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
}
/* Add FOR UPDATE/SHARE clauses if present */
- foreach(l, query->rowMarks)
+ if (query->hasForUpdate)
{
- RowMarkClause *rc = (RowMarkClause *) lfirst(l);
- RangeTblEntry *rte = rt_fetch(rc->rti, query->rtable);
+ foreach(l, query->rowMarks)
+ {
+ 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",
- -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
- appendStringInfo(buf, " OF %s",
- quote_identifier(rte->eref->aliasname));
- if (rc->noWait)
- appendStringInfo(buf, " NOWAIT");
+ /* don't print implicit clauses */
+ if (rc->pushedDown)
+ continue;
+
+ if (rc->forUpdate)
+ appendContextKeyword(context, " FOR UPDATE",
+ -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
+ else
+ appendContextKeyword(context, " FOR SHARE",
+ -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
+ appendStringInfo(buf, " OF %s",
+ quote_identifier(rte->eref->aliasname));
+ if (rc->noWait)
+ appendStringInfo(buf, " NOWAIT");
+ }
}
context->windowClause = save_windowclause;
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.547 2009/10/26 02:26:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.548 2009/10/28 14:55:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200910251
+#define CATALOG_VERSION_NO 200910281
#endif
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.411 2009/10/26 02:26:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.412 2009/10/28 14:55:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
bool hasSubLinks; /* has subquery SubLink */
bool hasDistinctOn; /* distinctClause is from DISTINCT ON */
bool hasRecursive; /* WITH RECURSIVE was specified */
+ bool hasForUpdate; /* FOR UPDATE or FOR SHARE was specified */
List *cteList; /* WITH list (of CommonTableExpr's) */
* parser output representation of FOR UPDATE/SHARE clauses
*
* Query.rowMarks contains a separate RowMarkClause node for each relation
- * identified as a FOR UPDATE/SHARE target.
+ * identified as a FOR UPDATE/SHARE target. If FOR UPDATE/SHARE is applied
+ * to a subquery, we generate RowMarkClauses for all normal and subquery rels
+ * in the subquery, but they are marked pushedDown = true to distinguish them
+ * from clauses that were explicitly written at this query level. Also,
+ * Query.hasForUpdate tells whether there were explicit FOR UPDATE/SHARE
+ * clauses in the current query level.
*/
typedef struct RowMarkClause
{
Index rti; /* range table index of target relation */
bool forUpdate; /* true = FOR UPDATE, false = FOR SHARE */
bool noWait; /* NOWAIT option */
+ bool pushedDown; /* pushed down from higher query level? */
} RowMarkClause;
/*
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/parser/analyze.h,v 1.42 2009/10/27 17:11:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/parser/analyze.h,v 1.43 2009/10/28 14:55:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern void CheckSelectLocking(Query *qry);
extern void applyLockingClause(Query *qry, Index rtindex,
- bool forUpdate, bool noWait);
+ bool forUpdate, bool noWait, bool pushedDown);
#endif /* ANALYZE_H */