aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2009-10-27 17:11:30 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2009-10-27 17:11:30 +0000
commitbd02b48ba44c6e0593320a9c3e071ba78efe1c04 (patch)
treee7707af135ae9d537dbd43fe38c8aa5a46b9b25a /src
parent0dfba4e98f7ef724839a6f446d96040645b9b487 (diff)
downloadpostgresql-bd02b48ba44c6e0593320a9c3e071ba78efe1c04.tar.gz
postgresql-bd02b48ba44c6e0593320a9c3e071ba78efe1c04.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/backend/parser/analyze.c64
-rw-r--r--src/backend/parser/parse_clause.c5
-rw-r--r--src/backend/parser/parse_cte.c4
-rw-r--r--src/backend/parser/parse_expr.c4
-rw-r--r--src/backend/parser/parse_relation.c59
-rw-r--r--src/backend/rewrite/rewriteHandler.c32
-rw-r--r--src/include/parser/analyze.h5
-rw-r--r--src/include/parser/parse_node.h3
-rw-r--r--src/include/parser/parse_relation.h3
9 files changed, 59 insertions, 120 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 7c67c3a7119..f0af9a6d661 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -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.389.2.2 2009/09/09 03:33:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.389.2.3 2009/10/27 17:11:30 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
@@ -2043,7 +2045,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)
@@ -2085,32 +2087,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;
}
}
@@ -2177,30 +2155,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",
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 16ff39653c5..fee3829fec4 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.189.2.2 2009/09/09 03:33:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.189.2.3 2009/10/27 17:11:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -479,7 +479,8 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
/*
* Analyze and transform the subquery.
*/
- query = parse_sub_analyze(r->subquery, pstate, NULL);
+ query = parse_sub_analyze(r->subquery, pstate, NULL,
+ isLockedRefname(pstate, r->alias->aliasname));
/*
* Check that we got something reasonable. Many of these conditions are
diff --git a/src/backend/parser/parse_cte.c b/src/backend/parser/parse_cte.c
index 0b3f861b50f..41c913c869a 100644
--- a/src/backend/parser/parse_cte.c
+++ b/src/backend/parser/parse_cte.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_cte.c,v 2.6.2.1 2009/09/09 03:33:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_cte.c,v 2.6.2.2 2009/10/27 17:11:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -229,7 +229,7 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
/* Analysis not done already */
Assert(IsA(cte->ctequery, SelectStmt));
- query = parse_sub_analyze(cte->ctequery, pstate, cte);
+ query = parse_sub_analyze(cte->ctequery, pstate, cte, false);
cte->ctequery = (Node *) query;
/*
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 45c0a0970b1..d0e568525c6 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.241.2.1 2009/09/09 03:33:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.241.2.2 2009/10/27 17:11:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1251,7 +1251,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
return result;
pstate->p_hasSubLinks = true;
- qtree = parse_sub_analyze(sublink->subselect, pstate, NULL);
+ qtree = parse_sub_analyze(sublink->subselect, pstate, NULL, false);
/*
* Check that we got something reasonable. Many of these conditions are
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index b506c042c54..ffa5e8ac68b 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.142 2009/06/11 14:49:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.142.2.1 2009/10/27 17:11:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -41,7 +41,6 @@ static RangeTblEntry *scanNameSpaceForRelid(ParseState *pstate, Oid relid,
int location);
static void markRTEForSelectPriv(ParseState *pstate, RangeTblEntry *rte,
int rtindex, AttrNumber col);
-static bool isLockedRel(ParseState *pstate, char *refname);
static void expandRelation(Oid relid, Alias *eref,
int rtindex, int sublevels_up,
int location, bool include_dropped,
@@ -920,7 +919,7 @@ addRangeTableEntry(ParseState *pstate,
* to a rel in a statement, be careful to get the right access level
* depending on whether we're doing SELECT FOR UPDATE/SHARE.
*/
- lockmode = isLockedRel(pstate, refname) ? RowShareLock : AccessShareLock;
+ lockmode = isLockedRefname(pstate, refname) ? RowShareLock : AccessShareLock;
rel = parserOpenTable(pstate, relation, lockmode);
rte->relid = RelationGetRelid(rel);
@@ -1465,40 +1464,46 @@ addRangeTableEntryForCTE(ParseState *pstate,
/*
* Has the specified refname been selected FOR UPDATE/FOR SHARE?
*
- * Note: we pay no attention to whether it's FOR UPDATE vs FOR SHARE.
+ * This is used when we have not yet done transformLockingClause, but need
+ * to know the correct lock to take during initial opening of relations.
+ *
+ * Note: we pay no attention to whether it's FOR UPDATE vs FOR SHARE,
+ * since the table-level lock is the same either way.
*/
-static bool
-isLockedRel(ParseState *pstate, char *refname)
+bool
+isLockedRefname(ParseState *pstate, const char *refname)
{
- /* Outer loop to check parent query levels as well as this one */
- while (pstate != NULL)
+ ListCell *l;
+
+ /*
+ * If we are in a subquery specified as locked FOR UPDATE/SHARE from
+ * parent level, then act as though there's a generic FOR UPDATE here.
+ */
+ if (pstate->p_locked_from_parent)
+ return true;
+
+ foreach(l, pstate->p_locking_clause)
{
- ListCell *l;
+ LockingClause *lc = (LockingClause *) lfirst(l);
- foreach(l, pstate->p_locking_clause)
+ if (lc->lockedRels == NIL)
{
- LockingClause *lc = (LockingClause *) lfirst(l);
+ /* all tables used in query */
+ return true;
+ }
+ else
+ {
+ /* just the named tables */
+ ListCell *l2;
- if (lc->lockedRels == NIL)
- {
- /* all tables used in query */
- return true;
- }
- else
+ foreach(l2, lc->lockedRels)
{
- /* just the named tables */
- ListCell *l2;
-
- foreach(l2, lc->lockedRels)
- {
- RangeVar *thisrel = (RangeVar *) lfirst(l2);
+ RangeVar *thisrel = (RangeVar *) lfirst(l2);
- if (strcmp(refname, thisrel->relname) == 0)
- return true;
- }
+ if (strcmp(refname, thisrel->relname) == 0)
+ return true;
}
}
- pstate = pstate->parentParseState;
}
return false;
}
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index b3a18134539..89ef9a356d2 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.186.2.1 2009/09/02 17:52:33 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.186.2.2 2009/10/27 17:11:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1242,35 +1242,7 @@ markQueryForLocking(Query *qry, Node *jtnode, bool forUpdate, bool noWait)
markQueryForLocking(rte->subquery, (Node *) rte->subquery->jointree,
forUpdate, noWait);
}
- else if (rte->rtekind == 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 (compare
- * transformLockingClause). Which simplifies life here.
- */
- CommonTableExpr *cte = NULL;
- ListCell *lc;
-
- 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")));
- foreach(lc, qry->cteList)
- {
- cte = (CommonTableExpr *) lfirst(lc);
- if (strcmp(cte->ctename, rte->ctename) == 0)
- break;
- }
- if (lc == NULL) /* shouldn't happen */
- elog(ERROR, "could not find CTE \"%s\"", rte->ctename);
- /* should be analyzed by now */
- Assert(IsA(cte->ctequery, Query));
- markQueryForLocking((Query *) cte->ctequery,
- (Node *) ((Query *) cte->ctequery)->jointree,
- forUpdate, noWait);
- }
+ /* other RTE types are unaffected by FOR UPDATE */
}
else if (IsA(jtnode, FromExpr))
{
diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h
index 63b1cf1d950..41b162d3bc3 100644
--- a/src/include/parser/analyze.h
+++ b/src/include/parser/analyze.h
@@ -7,7 +7,7 @@
* 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.40.2.1 2009/09/09 03:33:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/parser/analyze.h,v 1.40.2.2 2009/10/27 17:11:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -23,7 +23,8 @@ extern Query *parse_analyze_varparams(Node *parseTree, const char *sourceText,
Oid **paramTypes, int *numParams);
extern Query *parse_sub_analyze(Node *parseTree, ParseState *parentParseState,
- CommonTableExpr *parentCTE);
+ CommonTableExpr *parentCTE,
+ bool locked_from_parent);
extern Query *transformStmt(ParseState *pstate, Node *parseTree);
extern bool analyze_requires_snapshot(Node *parseTree);
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 84a32b20835..25c8d7f0cd9 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.62.2.1 2009/09/09 03:33:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.62.2.2 2009/10/27 17:11:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -104,6 +104,7 @@ typedef struct ParseState
bool p_hasSubLinks;
bool p_is_insert;
bool p_is_update;
+ bool p_locked_from_parent;
Relation p_target_relation;
RangeTblEntry *p_target_rangetblentry;
} ParseState;
diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h
index ec0fb09d605..ca1da1cc3e2 100644
--- a/src/include/parser/parse_relation.h
+++ b/src/include/parser/parse_relation.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.64 2009/06/11 14:49:11 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.64.2.1 2009/10/27 17:11:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -84,6 +84,7 @@ extern RangeTblEntry *addRangeTableEntryForCTE(ParseState *pstate,
Index levelsup,
Alias *alias,
bool inFromCl);
+extern bool isLockedRefname(ParseState *pstate, const char *refname);
extern void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
bool addToJoinList,
bool addToRelNameSpace, bool addToVarNameSpace);