mirror of
https://github.com/postgres/postgres.git
synced 2025-07-18 17:42:25 +03:00
Make FOR UPDATE/SHARE in the primary query not propagate into WITH queries;
for example in WITH w AS (SELECT * FROM foo) SELECT * FROM w, bar ... FOR UPDATE the FOR UPDATE will now affect bar but not foo. This is more useful and consistent than the original 8.4 behavior, which tried to propagate FOR UPDATE into the WITH query but always failed due to assorted implementation restrictions. Even though we are in process of removing those restrictions, it seems correct on philosophical grounds to not let the outer query's FOR UPDATE affect the WITH query. In passing, fix isLockedRel which frequently got things wrong in nested-subquery cases: "FOR UPDATE OF foo" applies to an alias foo in the current query level, not subqueries. This has been broken for a long time, but it doesn't seem worth back-patching further than 8.4 because the actual consequences are minimal. At worst the parser would sometimes get RowShareLock on a relation when it should be AccessShareLock or vice versa. That would only make a difference if someone were using ExclusiveLock concurrently, which no standard operation does, and anyway FOR UPDATE doesn't result in visible changes so it's not clear that the someone would notice any problem. Between that and the fact that FOR UPDATE barely works with subqueries at all in existing releases, I'm not excited about worrying about it.
This commit is contained in:
@ -17,7 +17,7 @@
|
||||
* 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.393 2009/10/26 02:26:35 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.394 2009/10/27 17:11:18 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -138,12 +138,14 @@ parse_analyze_varparams(Node *parseTree, const char *sourceText,
|
||||
*/
|
||||
Query *
|
||||
parse_sub_analyze(Node *parseTree, ParseState *parentParseState,
|
||||
CommonTableExpr *parentCTE)
|
||||
CommonTableExpr *parentCTE,
|
||||
bool locked_from_parent)
|
||||
{
|
||||
ParseState *pstate = make_parsestate(parentParseState);
|
||||
Query *query;
|
||||
|
||||
pstate->p_parent_cte = parentCTE;
|
||||
pstate->p_locked_from_parent = locked_from_parent;
|
||||
|
||||
query = transformStmt(pstate, parseTree);
|
||||
|
||||
@ -1424,7 +1426,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
|
||||
* of this sub-query, because they are not in the toplevel pstate's
|
||||
* namespace list.
|
||||
*/
|
||||
selectQuery = parse_sub_analyze((Node *) stmt, pstate, NULL);
|
||||
selectQuery = parse_sub_analyze((Node *) stmt, pstate, NULL, false);
|
||||
|
||||
/*
|
||||
* Check for bogus references to Vars on the current query level (but
|
||||
@ -2051,7 +2053,7 @@ CheckSelectLocking(Query *qry)
|
||||
* This basically involves replacing names by integer relids.
|
||||
*
|
||||
* NB: if you need to change this, see also markQueryForLocking()
|
||||
* in rewriteHandler.c, and isLockedRel() in parse_relation.c.
|
||||
* in rewriteHandler.c, and isLockedRefname() in parse_relation.c.
|
||||
*/
|
||||
static void
|
||||
transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc)
|
||||
@ -2093,32 +2095,8 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc)
|
||||
*/
|
||||
transformLockingClause(pstate, rte->subquery, allrels);
|
||||
break;
|
||||
case RTE_CTE:
|
||||
{
|
||||
/*
|
||||
* We allow FOR UPDATE/SHARE of a WITH query to be
|
||||
* propagated into the WITH, but it doesn't seem very
|
||||
* sane to allow this for a reference to an
|
||||
* outer-level WITH. And it definitely wouldn't work
|
||||
* for a self-reference, since we're not done
|
||||
* analyzing the CTE anyway.
|
||||
*/
|
||||
CommonTableExpr *cte;
|
||||
|
||||
if (rte->ctelevelsup > 0 || rte->self_reference)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR UPDATE/SHARE cannot be applied to an outer-level WITH query")));
|
||||
cte = GetCTEForRTE(pstate, rte, -1);
|
||||
/* should be analyzed by now */
|
||||
Assert(IsA(cte->ctequery, Query));
|
||||
transformLockingClause(pstate,
|
||||
(Query *) cte->ctequery,
|
||||
allrels);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* ignore JOIN, SPECIAL, FUNCTION RTEs */
|
||||
/* ignore JOIN, SPECIAL, FUNCTION, VALUES, CTE RTEs */
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2185,30 +2163,10 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc)
|
||||
parser_errposition(pstate, thisrel->location)));
|
||||
break;
|
||||
case RTE_CTE:
|
||||
{
|
||||
/*
|
||||
* We allow FOR UPDATE/SHARE of a WITH query
|
||||
* to be propagated into the WITH, but it
|
||||
* doesn't seem very sane to allow this for a
|
||||
* reference to an outer-level WITH. And it
|
||||
* definitely wouldn't work for a
|
||||
* self-reference, since we're not done
|
||||
* analyzing the CTE anyway.
|
||||
*/
|
||||
CommonTableExpr *cte;
|
||||
|
||||
if (rte->ctelevelsup > 0 || rte->self_reference)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR UPDATE/SHARE cannot be applied to an outer-level WITH query"),
|
||||
parser_errposition(pstate, thisrel->location)));
|
||||
cte = GetCTEForRTE(pstate, rte, -1);
|
||||
/* should be analyzed by now */
|
||||
Assert(IsA(cte->ctequery, Query));
|
||||
transformLockingClause(pstate,
|
||||
(Query *) cte->ctequery,
|
||||
allrels);
|
||||
}
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a WITH query"),
|
||||
parser_errposition(pstate, thisrel->location)));
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized RTE type: %d",
|
||||
|
Reference in New Issue
Block a user