diff options
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/analyze.c | 58 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 29 |
2 files changed, 44 insertions, 43 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 5aa6ecce7b3..ede41af6dbc 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -2139,7 +2139,7 @@ transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt) /* - * Check for features that are not supported together with FOR UPDATE/SHARE. + * Check for features that are not supported together with FOR [KEY] UPDATE/SHARE. * * exported so planner can check again after rewriting, query pullup, etc */ @@ -2149,35 +2149,35 @@ CheckSelectLocking(Query *qry) if (qry->setOperations) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT"))); + errmsg("SELECT FOR UPDATE/SHARE/FOR KEY UPDATE/FOR KEY SHARE is not allowed with UNION/INTERSECT/EXCEPT"))); if (qry->distinctClause != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE is not allowed with DISTINCT clause"))); + errmsg("SELECT FOR UPDATE/SHARE/FOR KEY UPDATE/FOR KEY SHARE is not allowed with DISTINCT clause"))); if (qry->groupClause != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE is not allowed with GROUP BY clause"))); + errmsg("SELECT FOR UPDATE/SHARE/FOR KEY UPDATE/FOR KEY SHARE is not allowed with GROUP BY clause"))); if (qry->havingQual != NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE is not allowed with HAVING clause"))); + errmsg("SELECT FOR UPDATE/SHARE/FOR KEY UPDATE/FOR KEY SHARE is not allowed with HAVING clause"))); if (qry->hasAggs) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE is not allowed with aggregate functions"))); + errmsg("SELECT FOR UPDATE/SHARE/FOR KEY UPDATE/FOR KEY SHARE is not allowed with aggregate functions"))); if (qry->hasWindowFuncs) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE is not allowed with window functions"))); + errmsg("SELECT FOR UPDATE/SHARE/FOR KEY UPDATE/FOR KEY SHARE is not allowed with window functions"))); if (expression_returns_set((Node *) qry->targetList)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE is not allowed with set-returning functions in the target list"))); + errmsg("SELECT FOR UPDATE/SHARE/FOR KEY UPDATE/FOR KEY SHARE is not allowed with set-returning functions in the target list"))); } /* - * Transform a FOR UPDATE/SHARE clause + * Transform a FOR [KEY] UPDATE/SHARE clause * * This basically involves replacing names by integer relids. * @@ -2199,7 +2199,7 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, /* 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->strength = lc->strength; allrels->noWait = lc->noWait; if (lockedRels == NIL) @@ -2218,15 +2218,15 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, if (rte->relkind == RELKIND_FOREIGN_TABLE) break; applyLockingClause(qry, i, - lc->forUpdate, lc->noWait, pushedDown); + lc->strength, lc->noWait, pushedDown); rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; break; case RTE_SUBQUERY: applyLockingClause(qry, i, - lc->forUpdate, lc->noWait, pushedDown); + lc->strength, lc->noWait, pushedDown); /* - * FOR UPDATE/SHARE of subquery is propagated to all of + * FOR [KEY] UPDATE/SHARE of subquery is propagated to all of * 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 @@ -2252,7 +2252,7 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, if (thisrel->catalogname || thisrel->schemaname) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("SELECT FOR UPDATE/SHARE must specify unqualified relation names"), + errmsg("SELECT FOR UPDATE/SHARE/KEY UPDATE/KEY SHARE must specify unqualified relation names"), parser_errposition(pstate, thisrel->location))); i = 0; @@ -2269,17 +2269,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, if (rte->relkind == RELKIND_FOREIGN_TABLE) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE cannot be used with foreign table \"%s\"", + errmsg("SELECT FOR UPDATE/SHARE/KEY UPDATE/KEY SHARE cannot be used with foreign table \"%s\"", rte->eref->aliasname), parser_errposition(pstate, thisrel->location))); applyLockingClause(qry, i, - lc->forUpdate, lc->noWait, + lc->strength, lc->noWait, pushedDown); rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; break; case RTE_SUBQUERY: applyLockingClause(qry, i, - lc->forUpdate, lc->noWait, + lc->strength, lc->noWait, pushedDown); /* see comment above */ transformLockingClause(pstate, rte->subquery, @@ -2288,25 +2288,25 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, case RTE_JOIN: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a join"), + errmsg("SELECT FOR UPDATE/SHARE/KEY UPDATE/KEY SHARE cannot be applied to a join"), parser_errposition(pstate, thisrel->location))); break; case RTE_FUNCTION: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a function"), + errmsg("SELECT FOR UPDATE/SHARE/KEY UPDATE/KEY SHARE cannot be applied to a function"), parser_errposition(pstate, thisrel->location))); break; case RTE_VALUES: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES"), + errmsg("SELECT FOR UPDATE/SHARE/KEY UPDATE/KEY SHARE cannot be applied to VALUES"), parser_errposition(pstate, thisrel->location))); break; case RTE_CTE: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a WITH query"), + errmsg("SELECT FOR UPDATE/SHARE/KEY UPDATE/KEY SHARE cannot be applied to a WITH query"), parser_errposition(pstate, thisrel->location))); break; default: @@ -2320,7 +2320,7 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, if (rt == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), - errmsg("relation \"%s\" in FOR UPDATE/SHARE clause not found in FROM clause", + errmsg("relation \"%s\" in FOR UPDATE/SHARE/KEY UPDATE/KEY SHARE clause not found in FROM clause", thisrel->relname), parser_errposition(pstate, thisrel->location))); } @@ -2332,7 +2332,7 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, */ void applyLockingClause(Query *qry, Index rtindex, - bool forUpdate, bool noWait, bool pushedDown) + LockClauseStrength strength, bool noWait, bool pushedDown) { RowMarkClause *rc; @@ -2344,10 +2344,10 @@ applyLockingClause(Query *qry, Index rtindex, if ((rc = get_parse_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.) + * If the same RTE is specified for more than one locking strength, + * treat is as the strongest. (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. @@ -2356,7 +2356,7 @@ applyLockingClause(Query *qry, Index rtindex, * * And of course pushedDown becomes false if any clause is explicit. */ - rc->forUpdate |= forUpdate; + rc->strength = Max(rc->strength, strength); rc->noWait |= noWait; rc->pushedDown &= pushedDown; return; @@ -2365,7 +2365,7 @@ applyLockingClause(Query *qry, Index rtindex, /* Make a new RowMarkClause */ rc = makeNode(RowMarkClause); rc->rti = rtindex; - rc->forUpdate = forUpdate; + rc->strength = strength; rc->noWait = noWait; rc->pushedDown = pushedDown; qry->rowMarks = lappend(qry->rowMarks, rc); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index b19afa88e73..828e11058e9 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -361,6 +361,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType, %type <ival> OptTemp %type <oncommit> OnCommitOption +%type <ival> for_locking_strength %type <node> for_locking_item %type <list> for_locking_clause opt_for_locking_clause for_locking_items %type <list> locked_rels_list @@ -8900,9 +8901,10 @@ select_with_parens: * The duplicative productions are annoying, but hard to get rid of without * creating shift/reduce conflicts. * - * FOR UPDATE/SHARE may be before or after LIMIT/OFFSET. + * The locking clause (FOR UPDATE etc) may be before or after LIMIT/OFFSET. * In <=7.2.X, LIMIT/OFFSET had to be after FOR UPDATE - * We now support both orderings, but prefer LIMIT/OFFSET before FOR UPDATE/SHARE + * We now support both orderings, but prefer LIMIT/OFFSET before the locking + * clause. * 2002-08-28 bjm */ select_no_parens: @@ -9321,24 +9323,23 @@ for_locking_items: ; for_locking_item: - FOR UPDATE locked_rels_list opt_nowait + for_locking_strength locked_rels_list opt_nowait { LockingClause *n = makeNode(LockingClause); - n->lockedRels = $3; - n->forUpdate = TRUE; - n->noWait = $4; - $$ = (Node *) n; - } - | FOR SHARE locked_rels_list opt_nowait - { - LockingClause *n = makeNode(LockingClause); - n->lockedRels = $3; - n->forUpdate = FALSE; - n->noWait = $4; + n->lockedRels = $2; + n->strength = $1; + n->noWait = $3; $$ = (Node *) n; } ; +for_locking_strength: + FOR UPDATE { $$ = LCS_FORUPDATE; } + | FOR NO KEY UPDATE { $$ = LCS_FORNOKEYUPDATE; } + | FOR SHARE { $$ = LCS_FORSHARE; } + | FOR KEY SHARE { $$ = LCS_FORKEYSHARE; } + ; + locked_rels_list: OF qualified_name_list { $$ = $2; } | /* EMPTY */ { $$ = NIL; } |