From 5712eeb2e7cf8e4236acffd4a403e7f14a48866f Mon Sep 17 00:00:00 2001 From: Alvaro Herrera Date: Tue, 23 Jul 2013 14:03:09 -0400 Subject: [PATCH] Tweak FOR UPDATE/SHARE error message wording (again) In commit 0ac5ad5134 I changed some error messages from "FOR UPDATE/SHARE" to a rather long gobbledygook which nobody liked. Then, in commit cb9b66d31 I changed them again, but the alternative chosen there was deemed suboptimal by Peter Eisentraut, who in message 1373937980.20441.8.camel@vanquo.pezone.net proposed an alternative involving a dynamically-constructed string based on the actual locking strength specified in the SQL command. This patch implements that suggestion. --- src/backend/optimizer/plan/initsplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 +- src/backend/parser/analyze.c | 127 +++++++++++++++++++++---- src/include/parser/analyze.h | 1 + 4 files changed, 118 insertions(+), 22 deletions(-) diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 839ed9dde4..8efb94b44d 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -26,6 +26,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "optimizer/var.h" +#include "parser/analyze.h" #include "rewrite/rewriteManip.h" #include "utils/lsyscache.h" @@ -883,7 +884,10 @@ make_outerjoininfo(PlannerInfo *root, (jointype == JOIN_FULL && bms_is_member(rc->rti, left_rels))) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("row-level locks cannot be applied to the nullable side of an outer join"))); + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s cannot be applied to the nullable side of an outer join", + LCS_asString(rc->strength)))); } sjinfo->syn_lefthand = left_rels; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index d80c26420f..4059c666c6 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -1081,7 +1081,11 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) if (parse->rowMarks) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("row-level locks are not allowed with UNION/INTERSECT/EXCEPT"))); + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s is not allowed with UNION/INTERSECT/EXCEPT", + LCS_asString(((RowMarkClause *) + linitial(parse->rowMarks))->strength)))); /* * Calculate pathkeys that represent result ordering requirements diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 16ff23443c..39036fbc86 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -1221,7 +1221,11 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) if (stmt->lockingClause) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES"))); + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s cannot be applied to VALUES", + LCS_asString(((LockingClause *) + linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); @@ -1312,7 +1316,11 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) if (lockingClause) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT"))); + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s is not allowed with UNION/INTERSECT/EXCEPT", + LCS_asString(((LockingClause *) + linitial(lockingClause))->strength)))); /* Process the WITH clause independently of all else */ if (withClause) @@ -1506,7 +1514,11 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, if (stmt->lockingClause) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT"))); + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s is not allowed with UNION/INTERSECT/EXCEPT", + LCS_asString(((LockingClause *) + linitial(stmt->lockingClause))->strength)))); /* * If an internal node of a set-op tree has ORDER BY, LIMIT, FOR UPDATE, @@ -2063,21 +2075,33 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt) if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_HOLD)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("DECLARE CURSOR WITH HOLD ... FOR UPDATE/SHARE is not supported"), + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("DECLARE CURSOR WITH HOLD ... %s is not supported", + LCS_asString(((RowMarkClause *) + linitial(result->rowMarks))->strength)), errdetail("Holdable cursors must be READ ONLY."))); /* FOR UPDATE and SCROLL are not compatible */ if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_SCROLL)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("DECLARE SCROLL CURSOR ... FOR UPDATE/SHARE is not supported"), + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("DECLARE SCROLL CURSOR ... %s is not supported", + LCS_asString(((RowMarkClause *) + linitial(result->rowMarks))->strength)), errdetail("Scrollable cursors must be READ ONLY."))); /* FOR UPDATE and INSENSITIVE are not compatible */ if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_INSENSITIVE)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("DECLARE INSENSITIVE CURSOR ... FOR UPDATE/SHARE is not supported"), + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("DECLARE INSENSITIVE CURSOR ... %s is not supported", + LCS_asString(((RowMarkClause *) + linitial(result->rowMarks))->strength)), errdetail("Insensitive cursors must be READ ONLY."))); /* We won't need the raw querytree any more */ @@ -2196,6 +2220,23 @@ transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt) } +char * +LCS_asString(LockClauseStrength strength) +{ + switch (strength) + { + case LCS_FORKEYSHARE: + return "FOR KEY SHARE"; + case LCS_FORSHARE: + return "FOR SHARE"; + case LCS_FORNOKEYUPDATE: + return "FOR NO KEY UPDATE"; + case LCS_FORUPDATE: + return "FOR UPDATE"; + } + return "FOR some"; /* shouldn't happen */ +} + /* * Check for features that are not supported with FOR [KEY] UPDATE/SHARE. * @@ -2207,31 +2248,59 @@ CheckSelectLocking(Query *qry) if (qry->setOperations) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("row-level locks are not allowed with UNION/INTERSECT/EXCEPT"))); + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s is not allowed with UNION/INTERSECT/EXCEPT", + LCS_asString(((RowMarkClause *) + linitial(qry->rowMarks))->strength)))); if (qry->distinctClause != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("row-level locks are not allowed with DISTINCT clause"))); + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s is not allowed with DISTINCT clause", + LCS_asString(((RowMarkClause *) + linitial(qry->rowMarks))->strength)))); if (qry->groupClause != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("row-level locks are not allowed with GROUP BY clause"))); + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s is not allowed with GROUP BY clause", + LCS_asString(((RowMarkClause *) + linitial(qry->rowMarks))->strength)))); if (qry->havingQual != NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("row-level locks are not allowed with HAVING clause"))); + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s is not allowed with HAVING clause", + LCS_asString(((RowMarkClause *) + linitial(qry->rowMarks))->strength)))); if (qry->hasAggs) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("row-level locks are not allowed with aggregate functions"))); + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s is not allowed with aggregate functions", + LCS_asString(((RowMarkClause *) + linitial(qry->rowMarks))->strength)))); if (qry->hasWindowFuncs) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("row-level locks are not allowed with window functions"))); + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s is not allowed with window functions", + LCS_asString(((RowMarkClause *) + linitial(qry->rowMarks))->strength)))); if (expression_returns_set((Node *) qry->targetList)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("row-level locks are not allowed with set-returning functions in the target list"))); + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s is not allowed with set-returning functions in the target list", + LCS_asString(((RowMarkClause *) + linitial(qry->rowMarks))->strength)))); } /* @@ -2307,7 +2376,10 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, if (thisrel->catalogname || thisrel->schemaname) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("row-level locks must specify unqualified relation names"), + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s must specify unqualified relation names", + LCS_asString(lc->strength)), parser_errposition(pstate, thisrel->location))); i = 0; @@ -2337,25 +2409,37 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, case RTE_JOIN: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("row-level locks cannot be applied to a join"), + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s cannot be applied to a join", + LCS_asString(lc->strength)), parser_errposition(pstate, thisrel->location))); break; case RTE_FUNCTION: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("row-level locks cannot be applied to a function"), + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s cannot be applied to a function", + LCS_asString(lc->strength)), parser_errposition(pstate, thisrel->location))); break; case RTE_VALUES: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("row-level locks cannot be applied to VALUES"), + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s cannot be applied to VALUES", + LCS_asString(lc->strength)), parser_errposition(pstate, thisrel->location))); break; case RTE_CTE: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("row-level locks cannot be applied to a WITH query"), + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s cannot be applied to a WITH query", + LCS_asString(lc->strength)), parser_errposition(pstate, thisrel->location))); break; default: @@ -2369,8 +2453,11 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, if (rt == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), - errmsg("relation \"%s\" in row-level lock clause not found in FROM clause", - thisrel->relname), + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("relation \"%s\" in %s clause not found in FROM clause", + thisrel->relname, + LCS_asString(lc->strength)), parser_errposition(pstate, thisrel->location))); } } diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h index 2f988d4021..b24b205e45 100644 --- a/src/include/parser/analyze.h +++ b/src/include/parser/analyze.h @@ -36,6 +36,7 @@ extern Query *transformStmt(ParseState *pstate, Node *parseTree); extern bool analyze_requires_snapshot(Node *parseTree); +extern char *LCS_asString(LockClauseStrength strength); extern void CheckSelectLocking(Query *qry); extern void applyLockingClause(Query *qry, Index rtindex, LockClauseStrength strength, bool noWait, bool pushedDown); -- 2.40.0