1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-02 09:02:37 +03:00

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:
Tom Lane
2009-10-28 14:55:47 +00:00
parent 44956c52c5
commit 46e3a16b05
13 changed files with 225 additions and 126 deletions

View File

@ -7,7 +7,7 @@
* 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 $
*
*-------------------------------------------------------------------------
*/
@ -52,7 +52,7 @@ static Node *get_assignment_input(Node *node);
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);
@ -1189,23 +1189,13 @@ ApplyRetrieveRule(Query *parsetree,
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;
}
@ -1222,7 +1212,8 @@ ApplyRetrieveRule(Query *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;
@ -1233,14 +1224,15 @@ markQueryForLocking(Query *qry, Node *jtnode, bool forUpdate, bool noWait)
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 */
}
@ -1250,14 +1242,14 @@ markQueryForLocking(Query *qry, Node *jtnode, bool forUpdate, bool noWait)
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",