aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/analyze.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/analyze.c')
-rw-r--r--src/backend/parser/analyze.c113
1 files changed, 62 insertions, 51 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index d69c0f17864..566c9a0488d 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.333 2006/04/22 01:25:59 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.334 2006/04/30 18:30:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1805,6 +1805,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
{
Query *qry = makeNode(Query);
Node *qual;
+ ListCell *l;
qry->commandType = CMD_SELECT;
@@ -1870,8 +1871,10 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
parseCheckAggregates(pstate, qry);
- if (stmt->lockingClause)
- transformLockingClause(qry, stmt->lockingClause);
+ foreach(l, stmt->lockingClause)
+ {
+ transformLockingClause(qry, (LockingClause *) lfirst(l));
+ }
return qry;
}
@@ -1899,10 +1902,11 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
List *sortClause;
Node *limitOffset;
Node *limitCount;
- LockingClause *lockingClause;
+ List *lockingClause;
Node *node;
ListCell *left_tlist,
- *dtlist;
+ *dtlist,
+ *l;
List *targetvars,
*targetnames,
*sv_relnamespace,
@@ -1942,7 +1946,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
stmt->sortClause = NIL;
stmt->limitOffset = NULL;
stmt->limitCount = NULL;
- stmt->lockingClause = NULL;
+ stmt->lockingClause = NIL;
/* We don't support FOR UPDATE/SHARE with set ops at the moment. */
if (lockingClause)
@@ -2084,8 +2088,10 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
parseCheckAggregates(pstate, qry);
- if (lockingClause)
- transformLockingClause(qry, lockingClause);
+ foreach(l, lockingClause)
+ {
+ transformLockingClause(qry, (LockingClause *) lfirst(l));
+ }
return qry;
}
@@ -2743,44 +2749,34 @@ transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt)
/* exported so planner can check again after rewriting, query pullup, etc */
void
-CheckSelectLocking(Query *qry, bool forUpdate)
+CheckSelectLocking(Query *qry)
{
- const char *operation;
-
- if (forUpdate)
- operation = "SELECT FOR UPDATE";
- else
- operation = "SELECT FOR SHARE";
-
if (qry->setOperations)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- /* translator: %s is a SQL command, like SELECT FOR UPDATE */
- errmsg("%s is not allowed with UNION/INTERSECT/EXCEPT", operation)));
+ errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT")));
if (qry->distinctClause != NIL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- /* translator: %s is a SQL command, like SELECT FOR UPDATE */
- errmsg("%s is not allowed with DISTINCT clause", operation)));
+ errmsg("SELECT FOR UPDATE/SHARE is not allowed with DISTINCT clause")));
if (qry->groupClause != NIL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- /* translator: %s is a SQL command, like SELECT FOR UPDATE */
- errmsg("%s is not allowed with GROUP BY clause", operation)));
+ errmsg("SELECT FOR UPDATE/SHARE is not allowed with GROUP BY clause")));
if (qry->havingQual != NULL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- /* translator: %s is a SQL command, like SELECT FOR UPDATE */
- errmsg("%s is not allowed with HAVING clause", operation)));
+ errmsg("SELECT FOR UPDATE/SHARE is not allowed with HAVING clause")));
if (qry->hasAggs)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- /* translator: %s is a SQL command, like SELECT FOR UPDATE */
- errmsg("%s is not allowed with aggregate functions", operation)));
+ errmsg("SELECT FOR UPDATE/SHARE is not allowed with aggregate functions")));
}
/*
- * Convert FOR UPDATE/SHARE name list into rowMarks list of integer relids
+ * Transform a FOR UPDATE/SHARE clause
+ *
+ * This basically involves replacing names by integer relids.
*
* NB: if you need to change this, see also markQueryForLocking()
* in rewriteHandler.c.
@@ -2789,35 +2785,18 @@ static void
transformLockingClause(Query *qry, LockingClause *lc)
{
List *lockedRels = lc->lockedRels;
- List *rowMarks;
ListCell *l;
ListCell *rt;
Index i;
LockingClause *allrels;
- if (qry->rowMarks)
- {
- if (lc->forUpdate != qry->forUpdate)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot use both FOR UPDATE and FOR SHARE in one query")));
- if (lc->nowait != qry->rowNoWait)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot use both wait and NOWAIT in one query")));
- }
- qry->forUpdate = lc->forUpdate;
- qry->rowNoWait = lc->nowait;
-
- CheckSelectLocking(qry, lc->forUpdate);
+ CheckSelectLocking(qry);
/* make a clause we can pass down to subqueries to select all rels */
allrels = makeNode(LockingClause);
allrels->lockedRels = NIL; /* indicates all rels */
allrels->forUpdate = lc->forUpdate;
- allrels->nowait = lc->nowait;
-
- rowMarks = qry->rowMarks;
+ allrels->noWait = lc->noWait;
if (lockedRels == NIL)
{
@@ -2831,8 +2810,7 @@ transformLockingClause(Query *qry, LockingClause *lc)
switch (rte->rtekind)
{
case RTE_RELATION:
- /* use list_append_unique to avoid duplicates */
- rowMarks = list_append_unique_int(rowMarks, i);
+ applyLockingClause(qry, i, lc->forUpdate, lc->noWait);
rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
break;
case RTE_SUBQUERY:
@@ -2867,8 +2845,8 @@ transformLockingClause(Query *qry, LockingClause *lc)
switch (rte->rtekind)
{
case RTE_RELATION:
- /* use list_append_unique to avoid duplicates */
- rowMarks = list_append_unique_int(rowMarks, i);
+ applyLockingClause(qry, i,
+ lc->forUpdate, lc->noWait);
rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
break;
case RTE_SUBQUERY:
@@ -2909,8 +2887,41 @@ transformLockingClause(Query *qry, LockingClause *lc)
relname)));
}
}
+}
+
+/*
+ * Record locking info for a single rangetable item
+ */
+void
+applyLockingClause(Query *qry, Index rtindex, bool forUpdate, bool noWait)
+{
+ RowMarkClause *rc;
+
+ /* Check for pre-existing entry for same rtindex */
+ if ((rc = get_rowmark(qry, rtindex)) != NULL)
+ {
+ /*
+ * If the same RTE is specified both FOR UPDATE and FOR SHARE,
+ * treat it as FOR UPDATE. (Reasonable, since you can't take
+ * both a shared and exclusive lock at the same time; it'll
+ * end up being exclusive anyway.)
+ *
+ * We also consider that NOWAIT wins if it's specified both ways.
+ * This 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.)
+ */
+ rc->forUpdate |= forUpdate;
+ rc->noWait |= noWait;
+ return;
+ }
- qry->rowMarks = rowMarks;
+ /* Make a new RowMarkClause */
+ rc = makeNode(RowMarkClause);
+ rc->rti = rtindex;
+ rc->forUpdate = forUpdate;
+ rc->noWait = noWait;
+ qry->rowMarks = lappend(qry->rowMarks, rc);
}