When FOR UPDATE/SHARE is used with LIMIT, put the LockRows plan node
underneath the Limit node, not atop it. This fixes the old problem that such a query might unexpectedly return fewer rows than the LIMIT says, due to LockRows discarding updated rows. There is a related problem that LockRows might destroy the sort ordering produced by earlier steps; but fixing that by pushing LockRows below Sort would create serious performance problems that are unjustified in many real-world applications, as well as potential deadlock problems from locking many more rows than expected. Instead, keep the present semantics of applying FOR UPDATE after ORDER BY within a single query level; but allow the user to specify the other way by writing FOR UPDATE in a sub-select. To make that work, track whether FOR UPDATE appeared explicitly in sub-selects or got pushed down from the parent, and don't flatten a sub-select that contained an explicit FOR UPDATE.
This commit is contained in:
parent
44956c52c5
commit
46e3a16b05
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$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
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -1092,22 +1092,12 @@ FOR SHARE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ]
|
|||||||
has already locked a selected row or rows, <command>SELECT FOR
|
has already locked a selected row or rows, <command>SELECT FOR
|
||||||
UPDATE</command> will wait for the other transaction to complete,
|
UPDATE</command> will wait for the other transaction to complete,
|
||||||
and will then lock and return the updated row (or no row, if the
|
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">.
|
linkend="mvcc">.
|
||||||
</para>
|
</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>
|
<para>
|
||||||
<literal>FOR SHARE</literal> behaves similarly, except that it
|
<literal>FOR SHARE</literal> behaves similarly, except that it
|
||||||
acquires a shared rather than exclusive lock on each retrieved
|
acquires a shared rather than exclusive lock on each retrieved
|
||||||
@ -1117,13 +1107,26 @@ FOR SHARE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ]
|
|||||||
from performing <command>SELECT FOR SHARE</command>.
|
from performing <command>SELECT FOR SHARE</command>.
|
||||||
</para>
|
</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>
|
<para>
|
||||||
If specific tables are named in <literal>FOR UPDATE</literal>
|
If specific tables are named in <literal>FOR UPDATE</literal>
|
||||||
or <literal>FOR SHARE</literal>,
|
or <literal>FOR SHARE</literal>,
|
||||||
then only rows coming from those tables are locked; any other
|
then only rows coming from those tables are locked; any other
|
||||||
tables used in the <command>SELECT</command> are simply read as
|
tables used in the <command>SELECT</command> are simply read as
|
||||||
usual. A <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal>
|
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
|
If <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal> is
|
||||||
applied to a view or sub-query, it affects all tables used in
|
applied to a view or sub-query, it affects all tables used in
|
||||||
the view or sub-query.
|
the view or sub-query.
|
||||||
@ -1151,6 +1154,36 @@ FOR SHARE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ]
|
|||||||
individual table rows; for example they cannot be used with aggregation.
|
individual table rows; for example they cannot be used with aggregation.
|
||||||
</para>
|
</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>
|
<caution>
|
||||||
<para>
|
<para>
|
||||||
Avoid locking a row and then modifying it within a later savepoint or
|
Avoid locking a row and then modifying it within a later savepoint or
|
||||||
@ -1177,30 +1210,26 @@ ROLLBACK TO s;
|
|||||||
|
|
||||||
<caution>
|
<caution>
|
||||||
<para>
|
<para>
|
||||||
It is possible for a <command>SELECT</> command using both
|
It is possible for a <command>SELECT</> command using <literal>ORDER
|
||||||
<literal>LIMIT</literal> and <literal>FOR UPDATE/SHARE</literal>
|
BY</literal> and <literal>FOR UPDATE/SHARE</literal> to return rows out of
|
||||||
clauses to return fewer rows than specified by <literal>LIMIT</literal>.
|
order. This is because <literal>ORDER BY</> is applied first.
|
||||||
This is because <literal>LIMIT</> is applied first. The command
|
The command sorts the result, but might then block trying to obtain a lock
|
||||||
selects the specified number of rows,
|
on one or more of the rows. Once the <literal>SELECT</> unblocks, some
|
||||||
but might then block trying to obtain a lock on one or more of them.
|
of the ordering column values might have been modified, leading to those
|
||||||
Once the <literal>SELECT</> unblocks, the row might have been deleted
|
rows appearing to be out of order (though they are in order in terms
|
||||||
or updated so that it does not meet the query <literal>WHERE</> condition
|
of the original column values). This can be worked around at need by
|
||||||
anymore, in which case it will not be returned.
|
placing the <literal>FOR UPDATE/SHARE</literal> clause in a sub-query,
|
||||||
</para>
|
for example
|
||||||
</caution>
|
<programlisting>
|
||||||
|
SELECT * FROM (SELECT * FROM mytable FOR UPDATE) ss ORDER BY column1;
|
||||||
<caution>
|
</programlisting>
|
||||||
<para>
|
Note that this will result in locking all rows of <structname>mytable</>,
|
||||||
Similarly, it is possible for a <command>SELECT</> command
|
whereas <literal>FOR UPDATE</> at the top level would lock only the
|
||||||
using <literal>ORDER BY</literal> and <literal>FOR
|
actually returned rows. This can make for a significant performance
|
||||||
UPDATE/SHARE</literal> to return rows out of order. This is
|
difference, particularly if the <literal>ORDER BY</> is combined with
|
||||||
because <literal>ORDER BY</> is applied first. The command
|
<literal>LIMIT</> or other restrictions. So this technique is recommended
|
||||||
orders the result, but might then block trying to obtain a lock
|
only if concurrent updates of the ordering columns are expected and a
|
||||||
on one or more of the rows. Once the <literal>SELECT</>
|
strictly sorted result is required.
|
||||||
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</>.
|
|
||||||
</para>
|
</para>
|
||||||
</caution>
|
</caution>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
@ -1541,15 +1570,28 @@ SELECT distributors.* WHERE distributors.name = 'Westward';
|
|||||||
used by <productname>MySQL</productname>. The SQL:2008 standard
|
used by <productname>MySQL</productname>. The SQL:2008 standard
|
||||||
has introduced the clauses <literal>OFFSET ... FETCH {FIRST|NEXT}
|
has introduced the clauses <literal>OFFSET ... FETCH {FIRST|NEXT}
|
||||||
...</literal> for the same functionality, as shown above
|
...</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>.
|
syntax is also used by <productname>IBM DB2</productname>.
|
||||||
(Applications written for <productname>Oracle</productname>
|
(Applications written for <productname>Oracle</productname>
|
||||||
frequently use a workaround involving the automatically
|
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.)
|
PostgreSQL, to implement the effects of these clauses.)
|
||||||
</para>
|
</para>
|
||||||
</refsect2>
|
</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>
|
<refsect2>
|
||||||
<title>Nonstandard Clauses</title>
|
<title>Nonstandard Clauses</title>
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1859,6 +1859,7 @@ _copyRowMarkClause(RowMarkClause *from)
|
|||||||
COPY_SCALAR_FIELD(rti);
|
COPY_SCALAR_FIELD(rti);
|
||||||
COPY_SCALAR_FIELD(forUpdate);
|
COPY_SCALAR_FIELD(forUpdate);
|
||||||
COPY_SCALAR_FIELD(noWait);
|
COPY_SCALAR_FIELD(noWait);
|
||||||
|
COPY_SCALAR_FIELD(pushedDown);
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
@ -2223,6 +2224,7 @@ _copyQuery(Query *from)
|
|||||||
COPY_SCALAR_FIELD(hasSubLinks);
|
COPY_SCALAR_FIELD(hasSubLinks);
|
||||||
COPY_SCALAR_FIELD(hasDistinctOn);
|
COPY_SCALAR_FIELD(hasDistinctOn);
|
||||||
COPY_SCALAR_FIELD(hasRecursive);
|
COPY_SCALAR_FIELD(hasRecursive);
|
||||||
|
COPY_SCALAR_FIELD(hasForUpdate);
|
||||||
COPY_NODE_FIELD(cteList);
|
COPY_NODE_FIELD(cteList);
|
||||||
COPY_NODE_FIELD(rtable);
|
COPY_NODE_FIELD(rtable);
|
||||||
COPY_NODE_FIELD(jointree);
|
COPY_NODE_FIELD(jointree);
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -860,6 +860,7 @@ _equalQuery(Query *a, Query *b)
|
|||||||
COMPARE_SCALAR_FIELD(hasSubLinks);
|
COMPARE_SCALAR_FIELD(hasSubLinks);
|
||||||
COMPARE_SCALAR_FIELD(hasDistinctOn);
|
COMPARE_SCALAR_FIELD(hasDistinctOn);
|
||||||
COMPARE_SCALAR_FIELD(hasRecursive);
|
COMPARE_SCALAR_FIELD(hasRecursive);
|
||||||
|
COMPARE_SCALAR_FIELD(hasForUpdate);
|
||||||
COMPARE_NODE_FIELD(cteList);
|
COMPARE_NODE_FIELD(cteList);
|
||||||
COMPARE_NODE_FIELD(rtable);
|
COMPARE_NODE_FIELD(rtable);
|
||||||
COMPARE_NODE_FIELD(jointree);
|
COMPARE_NODE_FIELD(jointree);
|
||||||
@ -2198,6 +2199,7 @@ _equalRowMarkClause(RowMarkClause *a, RowMarkClause *b)
|
|||||||
COMPARE_SCALAR_FIELD(rti);
|
COMPARE_SCALAR_FIELD(rti);
|
||||||
COMPARE_SCALAR_FIELD(forUpdate);
|
COMPARE_SCALAR_FIELD(forUpdate);
|
||||||
COMPARE_SCALAR_FIELD(noWait);
|
COMPARE_SCALAR_FIELD(noWait);
|
||||||
|
COMPARE_SCALAR_FIELD(pushedDown);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
* Every node type that can appear in stored rules' parsetrees *must*
|
* Every node type that can appear in stored rules' parsetrees *must*
|
||||||
@ -1987,6 +1987,7 @@ _outQuery(StringInfo str, Query *node)
|
|||||||
WRITE_BOOL_FIELD(hasSubLinks);
|
WRITE_BOOL_FIELD(hasSubLinks);
|
||||||
WRITE_BOOL_FIELD(hasDistinctOn);
|
WRITE_BOOL_FIELD(hasDistinctOn);
|
||||||
WRITE_BOOL_FIELD(hasRecursive);
|
WRITE_BOOL_FIELD(hasRecursive);
|
||||||
|
WRITE_BOOL_FIELD(hasForUpdate);
|
||||||
WRITE_NODE_FIELD(cteList);
|
WRITE_NODE_FIELD(cteList);
|
||||||
WRITE_NODE_FIELD(rtable);
|
WRITE_NODE_FIELD(rtable);
|
||||||
WRITE_NODE_FIELD(jointree);
|
WRITE_NODE_FIELD(jointree);
|
||||||
@ -2036,6 +2037,7 @@ _outRowMarkClause(StringInfo str, RowMarkClause *node)
|
|||||||
WRITE_UINT_FIELD(rti);
|
WRITE_UINT_FIELD(rti);
|
||||||
WRITE_BOOL_FIELD(forUpdate);
|
WRITE_BOOL_FIELD(forUpdate);
|
||||||
WRITE_BOOL_FIELD(noWait);
|
WRITE_BOOL_FIELD(noWait);
|
||||||
|
WRITE_BOOL_FIELD(pushedDown);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
* Path and Plan nodes do not have any readfuncs support, because we
|
* Path and Plan nodes do not have any readfuncs support, because we
|
||||||
@ -203,6 +203,7 @@ _readQuery(void)
|
|||||||
READ_BOOL_FIELD(hasSubLinks);
|
READ_BOOL_FIELD(hasSubLinks);
|
||||||
READ_BOOL_FIELD(hasDistinctOn);
|
READ_BOOL_FIELD(hasDistinctOn);
|
||||||
READ_BOOL_FIELD(hasRecursive);
|
READ_BOOL_FIELD(hasRecursive);
|
||||||
|
READ_BOOL_FIELD(hasForUpdate);
|
||||||
READ_NODE_FIELD(cteList);
|
READ_NODE_FIELD(cteList);
|
||||||
READ_NODE_FIELD(rtable);
|
READ_NODE_FIELD(rtable);
|
||||||
READ_NODE_FIELD(jointree);
|
READ_NODE_FIELD(jointree);
|
||||||
@ -295,6 +296,7 @@ _readRowMarkClause(void)
|
|||||||
READ_UINT_FIELD(rti);
|
READ_UINT_FIELD(rti);
|
||||||
READ_BOOL_FIELD(forUpdate);
|
READ_BOOL_FIELD(forUpdate);
|
||||||
READ_BOOL_FIELD(noWait);
|
READ_BOOL_FIELD(noWait);
|
||||||
|
READ_BOOL_FIELD(pushedDown);
|
||||||
|
|
||||||
READ_DONE();
|
READ_DONE();
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1638,19 +1638,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If there is a LIMIT/OFFSET clause, add the LIMIT node.
|
* If there is a FOR UPDATE/SHARE clause, add the LockRows 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.
|
|
||||||
* (Note: we intentionally test parse->rowMarks not root->rowMarks here.
|
* (Note: we intentionally test parse->rowMarks not root->rowMarks here.
|
||||||
* If there are only non-locking rowmarks, they should be handled by
|
* If there are only non-locking rowmarks, they should be handled by
|
||||||
* the ModifyTable node instead.)
|
* the ModifyTable node instead.)
|
||||||
@ -1660,6 +1648,23 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
|
|||||||
result_plan = (Plan *) make_lockrows(result_plan,
|
result_plan = (Plan *) make_lockrows(result_plan,
|
||||||
root->rowMarks,
|
root->rowMarks,
|
||||||
SS_assign_special_param(root));
|
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 */
|
/* Compute result-relations list if needed */
|
||||||
@ -1795,20 +1800,33 @@ preprocess_rowmarks(PlannerInfo *root)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert RowMarkClauses to PlanRowMark representation.
|
* 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;
|
prowmarks = NIL;
|
||||||
foreach(l, parse->rowMarks)
|
foreach(l, parse->rowMarks)
|
||||||
{
|
{
|
||||||
RowMarkClause *rc = (RowMarkClause *) lfirst(l);
|
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);
|
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);
|
rels = bms_del_member(rels, rc->rti);
|
||||||
|
|
||||||
|
newrc = makeNode(PlanRowMark);
|
||||||
newrc->rti = newrc->prti = rc->rti;
|
newrc->rti = newrc->prti = rc->rti;
|
||||||
if (rc->forUpdate)
|
if (rc->forUpdate)
|
||||||
newrc->markType = ROW_MARK_EXCLUSIVE;
|
newrc->markType = ROW_MARK_EXCLUSIVE;
|
||||||
@ -1838,7 +1856,6 @@ preprocess_rowmarks(PlannerInfo *root)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
newrc = makeNode(PlanRowMark);
|
newrc = makeNode(PlanRowMark);
|
||||||
|
|
||||||
newrc->rti = newrc->prti = i;
|
newrc->rti = newrc->prti = i;
|
||||||
/* real tables support REFERENCE, anything else needs COPY */
|
/* real tables support REFERENCE, anything else needs COPY */
|
||||||
if (rte->rtekind == RTE_RELATION)
|
if (rte->rtekind == RTE_RELATION)
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1030,6 +1030,12 @@ is_simple_subquery(Query *subquery)
|
|||||||
/*
|
/*
|
||||||
* Can't pull up a subquery involving grouping, aggregation, sorting,
|
* Can't pull up a subquery involving grouping, aggregation, sorting,
|
||||||
* limiting, or WITH. (XXX WITH could possibly be allowed later)
|
* 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 ||
|
if (subquery->hasAggs ||
|
||||||
subquery->hasWindowFuncs ||
|
subquery->hasWindowFuncs ||
|
||||||
@ -1039,6 +1045,7 @@ is_simple_subquery(Query *subquery)
|
|||||||
subquery->distinctClause ||
|
subquery->distinctClause ||
|
||||||
subquery->limitOffset ||
|
subquery->limitOffset ||
|
||||||
subquery->limitCount ||
|
subquery->limitCount ||
|
||||||
|
subquery->hasForUpdate ||
|
||||||
subquery->cteList)
|
subquery->cteList)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -60,8 +60,8 @@ static Query *transformDeclareCursorStmt(ParseState *pstate,
|
|||||||
DeclareCursorStmt *stmt);
|
DeclareCursorStmt *stmt);
|
||||||
static Query *transformExplainStmt(ParseState *pstate,
|
static Query *transformExplainStmt(ParseState *pstate,
|
||||||
ExplainStmt *stmt);
|
ExplainStmt *stmt);
|
||||||
static void transformLockingClause(ParseState *pstate,
|
static void transformLockingClause(ParseState *pstate, Query *qry,
|
||||||
Query *qry, LockingClause *lc);
|
LockingClause *lc, bool pushedDown);
|
||||||
static bool check_parameter_resolution_walker(Node *node, ParseState *pstate);
|
static bool check_parameter_resolution_walker(Node *node, ParseState *pstate);
|
||||||
|
|
||||||
|
|
||||||
@ -896,7 +896,8 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
|
|||||||
|
|
||||||
foreach(l, stmt->lockingClause)
|
foreach(l, stmt->lockingClause)
|
||||||
{
|
{
|
||||||
transformLockingClause(pstate, qry, (LockingClause *) lfirst(l));
|
transformLockingClause(pstate, qry,
|
||||||
|
(LockingClause *) lfirst(l), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return qry;
|
return qry;
|
||||||
@ -1348,7 +1349,8 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
|
|||||||
|
|
||||||
foreach(l, lockingClause)
|
foreach(l, lockingClause)
|
||||||
{
|
{
|
||||||
transformLockingClause(pstate, qry, (LockingClause *) lfirst(l));
|
transformLockingClause(pstate, qry,
|
||||||
|
(LockingClause *) lfirst(l), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return qry;
|
return qry;
|
||||||
@ -2056,7 +2058,8 @@ CheckSelectLocking(Query *qry)
|
|||||||
* in rewriteHandler.c, and isLockedRefname() in parse_relation.c.
|
* in rewriteHandler.c, and isLockedRefname() in parse_relation.c.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc)
|
transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc,
|
||||||
|
bool pushedDown)
|
||||||
{
|
{
|
||||||
List *lockedRels = lc->lockedRels;
|
List *lockedRels = lc->lockedRels;
|
||||||
ListCell *l;
|
ListCell *l;
|
||||||
@ -2084,16 +2087,22 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc)
|
|||||||
switch (rte->rtekind)
|
switch (rte->rtekind)
|
||||||
{
|
{
|
||||||
case RTE_RELATION:
|
case RTE_RELATION:
|
||||||
applyLockingClause(qry, i, lc->forUpdate, lc->noWait);
|
applyLockingClause(qry, i,
|
||||||
|
lc->forUpdate, lc->noWait, pushedDown);
|
||||||
rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
|
rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
|
||||||
break;
|
break;
|
||||||
case RTE_SUBQUERY:
|
case RTE_SUBQUERY:
|
||||||
|
applyLockingClause(qry, i,
|
||||||
|
lc->forUpdate, lc->noWait, pushedDown);
|
||||||
/*
|
/*
|
||||||
* FOR UPDATE/SHARE of subquery is propagated to all of
|
* 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;
|
break;
|
||||||
default:
|
default:
|
||||||
/* ignore JOIN, SPECIAL, FUNCTION, VALUES, CTE RTEs */
|
/* ignore JOIN, SPECIAL, FUNCTION, VALUES, CTE RTEs */
|
||||||
@ -2127,16 +2136,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc)
|
|||||||
{
|
{
|
||||||
case RTE_RELATION:
|
case RTE_RELATION:
|
||||||
applyLockingClause(qry, i,
|
applyLockingClause(qry, i,
|
||||||
lc->forUpdate, lc->noWait);
|
lc->forUpdate, lc->noWait,
|
||||||
|
pushedDown);
|
||||||
rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
|
rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
|
||||||
break;
|
break;
|
||||||
case RTE_SUBQUERY:
|
case RTE_SUBQUERY:
|
||||||
|
applyLockingClause(qry, i,
|
||||||
/*
|
lc->forUpdate, lc->noWait,
|
||||||
* FOR UPDATE/SHARE of subquery is propagated to
|
pushedDown);
|
||||||
* all of subquery's rels
|
/* see comment above */
|
||||||
*/
|
transformLockingClause(pstate, rte->subquery,
|
||||||
transformLockingClause(pstate, rte->subquery, allrels);
|
allrels, true);
|
||||||
break;
|
break;
|
||||||
case RTE_JOIN:
|
case RTE_JOIN:
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
@ -2190,10 +2200,15 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc)
|
|||||||
* Record locking info for a single rangetable item
|
* Record locking info for a single rangetable item
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
applyLockingClause(Query *qry, Index rtindex, bool forUpdate, bool noWait)
|
applyLockingClause(Query *qry, Index rtindex,
|
||||||
|
bool forUpdate, bool noWait, bool pushedDown)
|
||||||
{
|
{
|
||||||
RowMarkClause *rc;
|
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 */
|
/* Check for pre-existing entry for same rtindex */
|
||||||
if ((rc = get_parse_rowmark(qry, rtindex)) != NULL)
|
if ((rc = get_parse_rowmark(qry, rtindex)) != NULL)
|
||||||
{
|
{
|
||||||
@ -2207,9 +2222,12 @@ applyLockingClause(Query *qry, Index rtindex, bool forUpdate, bool noWait)
|
|||||||
* is a bit more debatable but raising an error doesn't seem helpful.
|
* is a bit more debatable but raising an error doesn't seem helpful.
|
||||||
* (Consider for instance SELECT FOR UPDATE NOWAIT from a view that
|
* (Consider for instance SELECT FOR UPDATE NOWAIT from a view that
|
||||||
* internally contains a plain FOR UPDATE spec.)
|
* internally contains a plain FOR UPDATE spec.)
|
||||||
|
*
|
||||||
|
* And of course pushedDown becomes false if any clause is explicit.
|
||||||
*/
|
*/
|
||||||
rc->forUpdate |= forUpdate;
|
rc->forUpdate |= forUpdate;
|
||||||
rc->noWait |= noWait;
|
rc->noWait |= noWait;
|
||||||
|
rc->pushedDown &= pushedDown;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2218,6 +2236,7 @@ applyLockingClause(Query *qry, Index rtindex, bool forUpdate, bool noWait)
|
|||||||
rc->rti = rtindex;
|
rc->rti = rtindex;
|
||||||
rc->forUpdate = forUpdate;
|
rc->forUpdate = forUpdate;
|
||||||
rc->noWait = noWait;
|
rc->noWait = noWait;
|
||||||
|
rc->pushedDown = pushedDown;
|
||||||
qry->rowMarks = lappend(qry->rowMarks, rc);
|
qry->rowMarks = lappend(qry->rowMarks, rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -52,7 +52,7 @@ static Node *get_assignment_input(Node *node);
|
|||||||
static void rewriteValuesRTE(RangeTblEntry *rte, Relation target_relation,
|
static void rewriteValuesRTE(RangeTblEntry *rte, Relation target_relation,
|
||||||
List *attrnos);
|
List *attrnos);
|
||||||
static void markQueryForLocking(Query *qry, Node *jtnode,
|
static void markQueryForLocking(Query *qry, Node *jtnode,
|
||||||
bool forUpdate, bool noWait);
|
bool forUpdate, bool noWait, bool pushedDown);
|
||||||
static List *matchLocks(CmdType event, RuleLock *rulelocks,
|
static List *matchLocks(CmdType event, RuleLock *rulelocks,
|
||||||
int varno, Query *parsetree);
|
int varno, Query *parsetree);
|
||||||
static Query *fireRIRrules(Query *parsetree, List *activeRIRs);
|
static Query *fireRIRrules(Query *parsetree, List *activeRIRs);
|
||||||
@ -1189,23 +1189,13 @@ ApplyRetrieveRule(Query *parsetree,
|
|||||||
rte->modifiedCols = NULL;
|
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)
|
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,
|
markQueryForLocking(rule_action, (Node *) rule_action->jointree,
|
||||||
rc->forUpdate, rc->noWait);
|
rc->forUpdate, rc->noWait, true);
|
||||||
}
|
|
||||||
|
|
||||||
return parsetree;
|
return parsetree;
|
||||||
}
|
}
|
||||||
@ -1222,7 +1212,8 @@ ApplyRetrieveRule(Query *parsetree,
|
|||||||
* to scan the jointree to determine which rels are used.
|
* to scan the jointree to determine which rels are used.
|
||||||
*/
|
*/
|
||||||
static void
|
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)
|
if (jtnode == NULL)
|
||||||
return;
|
return;
|
||||||
@ -1233,14 +1224,15 @@ markQueryForLocking(Query *qry, Node *jtnode, bool forUpdate, bool noWait)
|
|||||||
|
|
||||||
if (rte->rtekind == RTE_RELATION)
|
if (rte->rtekind == RTE_RELATION)
|
||||||
{
|
{
|
||||||
applyLockingClause(qry, rti, forUpdate, noWait);
|
applyLockingClause(qry, rti, forUpdate, noWait, pushedDown);
|
||||||
rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
|
rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
|
||||||
}
|
}
|
||||||
else if (rte->rtekind == RTE_SUBQUERY)
|
else if (rte->rtekind == RTE_SUBQUERY)
|
||||||
{
|
{
|
||||||
|
applyLockingClause(qry, rti, forUpdate, noWait, pushedDown);
|
||||||
/* FOR UPDATE/SHARE of subquery is propagated to subquery's rels */
|
/* FOR UPDATE/SHARE of subquery is propagated to subquery's rels */
|
||||||
markQueryForLocking(rte->subquery, (Node *) rte->subquery->jointree,
|
markQueryForLocking(rte->subquery, (Node *) rte->subquery->jointree,
|
||||||
forUpdate, noWait);
|
forUpdate, noWait, true);
|
||||||
}
|
}
|
||||||
/* other RTE types are unaffected by FOR UPDATE */
|
/* other RTE types are unaffected by FOR UPDATE */
|
||||||
}
|
}
|
||||||
@ -1250,14 +1242,14 @@ markQueryForLocking(Query *qry, Node *jtnode, bool forUpdate, bool noWait)
|
|||||||
ListCell *l;
|
ListCell *l;
|
||||||
|
|
||||||
foreach(l, f->fromlist)
|
foreach(l, f->fromlist)
|
||||||
markQueryForLocking(qry, lfirst(l), forUpdate, noWait);
|
markQueryForLocking(qry, lfirst(l), forUpdate, noWait, pushedDown);
|
||||||
}
|
}
|
||||||
else if (IsA(jtnode, JoinExpr))
|
else if (IsA(jtnode, JoinExpr))
|
||||||
{
|
{
|
||||||
JoinExpr *j = (JoinExpr *) jtnode;
|
JoinExpr *j = (JoinExpr *) jtnode;
|
||||||
|
|
||||||
markQueryForLocking(qry, j->larg, forUpdate, noWait);
|
markQueryForLocking(qry, j->larg, forUpdate, noWait, pushedDown);
|
||||||
markQueryForLocking(qry, j->rarg, forUpdate, noWait);
|
markQueryForLocking(qry, j->rarg, forUpdate, noWait, pushedDown);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
elog(ERROR, "unrecognized node type: %d",
|
elog(ERROR, "unrecognized node type: %d",
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -2549,21 +2549,28 @@ get_select_query_def(Query *query, deparse_context *context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Add FOR UPDATE/SHARE clauses if present */
|
/* Add FOR UPDATE/SHARE clauses if present */
|
||||||
foreach(l, query->rowMarks)
|
if (query->hasForUpdate)
|
||||||
{
|
{
|
||||||
RowMarkClause *rc = (RowMarkClause *) lfirst(l);
|
foreach(l, query->rowMarks)
|
||||||
RangeTblEntry *rte = rt_fetch(rc->rti, query->rtable);
|
{
|
||||||
|
RowMarkClause *rc = (RowMarkClause *) lfirst(l);
|
||||||
|
RangeTblEntry *rte = rt_fetch(rc->rti, query->rtable);
|
||||||
|
|
||||||
if (rc->forUpdate)
|
/* don't print implicit clauses */
|
||||||
appendContextKeyword(context, " FOR UPDATE",
|
if (rc->pushedDown)
|
||||||
-PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
|
continue;
|
||||||
else
|
|
||||||
appendContextKeyword(context, " FOR SHARE",
|
if (rc->forUpdate)
|
||||||
-PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
|
appendContextKeyword(context, " FOR UPDATE",
|
||||||
appendStringInfo(buf, " OF %s",
|
-PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
|
||||||
quote_identifier(rte->eref->aliasname));
|
else
|
||||||
if (rc->noWait)
|
appendContextKeyword(context, " FOR SHARE",
|
||||||
appendStringInfo(buf, " NOWAIT");
|
-PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
|
||||||
|
appendStringInfo(buf, " OF %s",
|
||||||
|
quote_identifier(rte->eref->aliasname));
|
||||||
|
if (rc->noWait)
|
||||||
|
appendStringInfo(buf, " NOWAIT");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context->windowClause = save_windowclause;
|
context->windowClause = save_windowclause;
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -53,6 +53,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 200910251
|
#define CATALOG_VERSION_NO 200910281
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -118,6 +118,7 @@ typedef struct Query
|
|||||||
bool hasSubLinks; /* has subquery SubLink */
|
bool hasSubLinks; /* has subquery SubLink */
|
||||||
bool hasDistinctOn; /* distinctClause is from DISTINCT ON */
|
bool hasDistinctOn; /* distinctClause is from DISTINCT ON */
|
||||||
bool hasRecursive; /* WITH RECURSIVE was specified */
|
bool hasRecursive; /* WITH RECURSIVE was specified */
|
||||||
|
bool hasForUpdate; /* FOR UPDATE or FOR SHARE was specified */
|
||||||
|
|
||||||
List *cteList; /* WITH list (of CommonTableExpr's) */
|
List *cteList; /* WITH list (of CommonTableExpr's) */
|
||||||
|
|
||||||
@ -803,7 +804,12 @@ typedef struct WindowClause
|
|||||||
* parser output representation of FOR UPDATE/SHARE clauses
|
* parser output representation of FOR UPDATE/SHARE clauses
|
||||||
*
|
*
|
||||||
* Query.rowMarks contains a separate RowMarkClause node for each relation
|
* 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
|
typedef struct RowMarkClause
|
||||||
{
|
{
|
||||||
@ -811,6 +817,7 @@ typedef struct RowMarkClause
|
|||||||
Index rti; /* range table index of target relation */
|
Index rti; /* range table index of target relation */
|
||||||
bool forUpdate; /* true = FOR UPDATE, false = FOR SHARE */
|
bool forUpdate; /* true = FOR UPDATE, false = FOR SHARE */
|
||||||
bool noWait; /* NOWAIT option */
|
bool noWait; /* NOWAIT option */
|
||||||
|
bool pushedDown; /* pushed down from higher query level? */
|
||||||
} RowMarkClause;
|
} RowMarkClause;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -31,6 +31,6 @@ extern bool analyze_requires_snapshot(Node *parseTree);
|
|||||||
|
|
||||||
extern void CheckSelectLocking(Query *qry);
|
extern void CheckSelectLocking(Query *qry);
|
||||||
extern void applyLockingClause(Query *qry, Index rtindex,
|
extern void applyLockingClause(Query *qry, Index rtindex,
|
||||||
bool forUpdate, bool noWait);
|
bool forUpdate, bool noWait, bool pushedDown);
|
||||||
|
|
||||||
#endif /* ANALYZE_H */
|
#endif /* ANALYZE_H */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user