Tarantool development patches archive
 help / color / mirror / Atom feed
* [tarantool-patches] [PATCH v4 0/8] sql: use diag_set() for errors in SQL
@ 2019-03-13 17:03 imeevma
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 1/8] sql: rework syntax errors imeevma
                   ` (7 more replies)
  0 siblings, 8 replies; 43+ messages in thread
From: imeevma @ 2019-03-13 17:03 UTC (permalink / raw)
  To: korablev; +Cc: tarantool-patches

The goal of this patch-set is to rework all SQL errors and make
Tarantool ones from them. Currently this patch-set contains pathes
that reworks all parser errors.

https://github.com/tarantool/tarantool/issues/3965
https://github.com/tarantool/tarantool/tree/imeevma/gh-3965-use-diag_set-to-describe-error

Difference from last version:
 - Some patches were removed and new added.
 - A bit changed position of patches in the set.

Mergen Imeev (8):
  sql: rework syntax errors
  sql: set SQL parser errors via diag_set()
  sql: replace rc with is_aborted status in struct Parse
  sql: remove field nErr from struct Parse
  sql: remove field zErrMsg from struct Parse
  sql: rework three errors of "unsupported" type
  sql: rework semantic errors
  sql: remove sqlErrorMsg()

 src/box/errcode.h                                  |  18 +-
 src/box/sql.c                                      |  12 +-
 src/box/sql/alter.c                                |   3 +-
 src/box/sql/analyze.c                              |  13 +-
 src/box/sql/build.c                                | 221 +++++-----
 src/box/sql/callback.c                             |   3 +-
 src/box/sql/delete.c                               |  31 +-
 src/box/sql/expr.c                                 | 175 ++++----
 src/box/sql/insert.c                               |  40 +-
 src/box/sql/malloc.c                               |   9 +-
 src/box/sql/parse.y                                |  74 ++--
 src/box/sql/pragma.c                               |  11 +-
 src/box/sql/prepare.c                              |  22 +-
 src/box/sql/resolve.c                              | 279 ++++++++-----
 src/box/sql/select.c                               | 219 +++++-----
 src/box/sql/sqlInt.h                               |  30 +-
 src/box/sql/tokenize.c                             |  84 ++--
 src/box/sql/trigger.c                              |  35 +-
 src/box/sql/update.c                               |  22 +-
 src/box/sql/util.c                                 |  39 --
 src/box/sql/vdbemem.c                              |   8 +-
 src/box/sql/where.c                                |  13 +-
 src/box/sql/wherecode.c                            |   2 +-
 src/box/sql/whereexpr.c                            |   8 +-
 test/box/alter.result                              |   4 +-
 test/box/misc.result                               |  15 +
 test/sql-tap/alter2.test.lua                       |   4 +-
 test/sql-tap/analyzeD.test.lua                     |   2 +-
 test/sql-tap/autoinc.test.lua                      |   2 +-
 test/sql-tap/blob.test.lua                         |  20 +-
 test/sql-tap/check.test.lua                        |  16 +-
 test/sql-tap/colname.test.lua                      |   4 +-
 test/sql-tap/count.test.lua                        |   2 +-
 test/sql-tap/default.test.lua                      |  14 +-
 test/sql-tap/e_select1.test.lua                    |  36 +-
 test/sql-tap/engine.cfg                            |   3 +
 test/sql-tap/fkey2.test.lua                        |   4 +-
 test/sql-tap/func3.test.lua                        |   6 +-
 test/sql-tap/gh-2549-many-columns.test.lua         |   2 +-
 test/sql-tap/gh2168-temp-tables.test.lua           |   4 +-
 test/sql-tap/gh2548-select-compound-limit.test.lua |   2 +-
 test/sql-tap/hexlit.test.lua                       |   4 +-
 test/sql-tap/identifier_case.test.lua              |   2 +-
 test/sql-tap/index-info.test.lua                   |   2 +-
 test/sql-tap/index1.test.lua                       |   2 +-
 test/sql-tap/index7.test.lua                       |   2 +-
 test/sql-tap/insert1.test.lua                      |   2 +-
 test/sql-tap/join.test.lua                         |  22 +-
 test/sql-tap/join3.test.lua                        |   2 +-
 test/sql-tap/keyword1.test.lua                     |   2 +-
 test/sql-tap/misc1.test.lua                        |  14 +-
 test/sql-tap/misc5.test.lua                        |   6 +-
 test/sql-tap/null.test.lua                         |  12 +-
 test/sql-tap/pragma.test.lua                       |   4 +-
 test/sql-tap/select1.test.lua                      |  26 +-
 test/sql-tap/select3.test.lua                      |   8 +-
 test/sql-tap/select4.test.lua                      |   6 +-
 test/sql-tap/select7.test.lua                      |   2 +-
 test/sql-tap/sql-errors.test.lua                   | 459 +++++++++++++++++++++
 test/sql-tap/start-transaction.test.lua            |  14 +-
 test/sql-tap/table.test.lua                        |  10 +-
 test/sql-tap/tkt2822.test.lua                      |  30 +-
 test/sql-tap/tkt3935.test.lua                      |  14 +-
 test/sql-tap/tokenize.test.lua                     |  26 +-
 test/sql-tap/trigger1.test.lua                     |  14 +-
 test/sql-tap/unique.test.lua                       |   2 +-
 test/sql-tap/view.test.lua                         |  24 +-
 test/sql-tap/where7.test.lua                       |   2 +-
 test/sql-tap/whereG.test.lua                       |   6 +-
 test/sql-tap/with1.test.lua                        |  12 +-
 test/sql-tap/with2.test.lua                        |  18 +-
 test/sql/checks.result                             |  12 +-
 test/sql/collation.result                          |  12 +-
 test/sql/foreign-keys.result                       |  10 +-
 test/sql/gh-2347-max-int-literals.result           |   6 +-
 test/sql/gh-2929-primary-key.result                |   8 +-
 test/sql/gh-3888-values-blob-assert.result         |   8 +-
 test/sql/integer-overflow.result                   |   9 +-
 test/sql/iproto.result                             |  16 +-
 test/sql/misc.result                               |  14 +-
 test/sql/on-conflict.result                        |  16 +-
 test/sql/transition.result                         |   3 +-
 test/sql/triggers.result                           |   4 +-
 test/sql/types.result                              |  13 +-
 test/sql/view.result                               |   2 +-
 85 files changed, 1434 insertions(+), 959 deletions(-)
 create mode 100755 test/sql-tap/sql-errors.test.lua

-- 
2.7.4

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] [PATCH v4 1/8] sql: rework syntax errors
  2019-03-13 17:03 [tarantool-patches] [PATCH v4 0/8] sql: use diag_set() for errors in SQL imeevma
@ 2019-03-13 17:03 ` imeevma
  2019-03-14 18:24   ` [tarantool-patches] " n.pettik
  2019-03-15 14:09   ` Kirill Yukhin
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 2/8] sql: set SQL parser errors via diag_set() imeevma
                   ` (6 subsequent siblings)
  7 siblings, 2 replies; 43+ messages in thread
From: imeevma @ 2019-03-13 17:03 UTC (permalink / raw)
  To: korablev; +Cc: tarantool-patches

This patch reworks SQL syntax errors. After this patch, these
error will be set as Tarantool errors.

Part of #3965
---
 src/box/errcode.h                          |  7 +++++
 src/box/sql/build.c                        |  6 ++---
 src/box/sql/parse.y                        | 41 ++++++++++++++++++------------
 src/box/sql/select.c                       | 11 ++++----
 src/box/sql/tokenize.c                     |  7 ++---
 test/box/misc.result                       |  6 +++++
 test/sql-tap/alter2.test.lua               |  4 +--
 test/sql-tap/blob.test.lua                 | 20 +++++++--------
 test/sql-tap/check.test.lua                | 10 ++++----
 test/sql-tap/colname.test.lua              |  2 +-
 test/sql-tap/count.test.lua                |  2 +-
 test/sql-tap/default.test.lua              |  4 +--
 test/sql-tap/e_select1.test.lua            | 18 ++++++-------
 test/sql-tap/gh2168-temp-tables.test.lua   |  4 +--
 test/sql-tap/identifier_case.test.lua      |  2 +-
 test/sql-tap/index-info.test.lua           |  2 +-
 test/sql-tap/index1.test.lua               |  2 +-
 test/sql-tap/index7.test.lua               |  2 +-
 test/sql-tap/join.test.lua                 |  2 +-
 test/sql-tap/keyword1.test.lua             |  2 +-
 test/sql-tap/misc1.test.lua                |  8 +++---
 test/sql-tap/misc5.test.lua                |  6 ++---
 test/sql-tap/null.test.lua                 |  8 +++---
 test/sql-tap/pragma.test.lua               |  4 +--
 test/sql-tap/select1.test.lua              | 20 +++++++--------
 test/sql-tap/select3.test.lua              |  4 +--
 test/sql-tap/start-transaction.test.lua    | 14 +++++-----
 test/sql-tap/table.test.lua                | 10 ++++----
 test/sql-tap/tkt3935.test.lua              | 14 +++++-----
 test/sql-tap/tokenize.test.lua             | 26 +++++++++----------
 test/sql-tap/trigger1.test.lua             | 14 +++++-----
 test/sql-tap/view.test.lua                 |  4 +--
 test/sql-tap/with1.test.lua                |  6 ++---
 test/sql-tap/with2.test.lua                | 18 ++++++-------
 test/sql/checks.result                     |  2 +-
 test/sql/collation.result                  | 12 ++++-----
 test/sql/foreign-keys.result               | 10 +++++---
 test/sql/gh-3888-values-blob-assert.result |  8 +++---
 test/sql/iproto.result                     | 10 ++++----
 test/sql/misc.result                       | 14 +++++-----
 test/sql/on-conflict.result                | 16 ++++++------
 test/sql/types.result                      | 13 ++++++----
 42 files changed, 213 insertions(+), 182 deletions(-)

diff --git a/src/box/errcode.h b/src/box/errcode.h
index a1fcf01..f2f47c0 100644
--- a/src/box/errcode.h
+++ b/src/box/errcode.h
@@ -231,6 +231,13 @@ struct errcode_record {
 	/*176 */_(ER_SQL_CANT_RESOLVE_FIELD,	"Can’t resolve field '%s'") \
 	/*177 */_(ER_INDEX_EXISTS_IN_SPACE,	"Index '%s' already exists in space '%s'") \
 	/*178 */_(ER_INCONSISTENT_TYPES,	"Inconsistent types: expected %s got %s") \
+	/*179 */_(ER_SQL_SYNTAX,		"Syntax error in %s: %s") \
+	/*180 */_(ER_SQL_STACK_OVERFLOW,	"Failed to parse SQL statement: parser stack limit reached") \
+	/*181 */_(ER_SQL_SELECT_WILDCARD,	"Failed to expand '*' in SELECT statement without FROM clause") \
+	/*182 */_(ER_SQL_STATEMENT_EMPTY,	"Failed to execute an empty SQL statement") \
+	/*183 */_(ER_SQL_KEYWORD_IS_RESERVED,	"Keyword '%.*s' is reserved. Please use double quotes if '%.*s' is an identifier.") \
+	/*184 */_(ER_SQL_UNRECOGNIZED_SYNTAX,	"Syntax error near '%.*s'") \
+	/*185 */_(ER_SQL_UNKNOWN_TOKEN,		"Syntax error: unrecognized token: '%.*s'") \
 
 /*
  * !IMPORTANT! Please follow instructions at start of the file
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index e9851d9..f112c9f 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -2823,9 +2823,9 @@ sqlSrcListAppendFromTerm(Parse * pParse,	/* Parsing context */
 	struct SrcList_item *pItem;
 	sql *db = pParse->db;
 	if (!p && (pOn || pUsing)) {
-		sqlErrorMsg(pParse, "a JOIN clause is required before %s",
-				(pOn ? "ON" : "USING")
-		    );
+		diag_set(ClientError, ER_SQL_SYNTAX, "FROM clause",
+			 "a JOIN clause is required before ON and USING");
+		sql_parser_error(pParse);
 		goto append_from_error;
 	}
 	p = sqlSrcListAppend(db, p, pTable);
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index 996f55d..66fb44b 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -33,13 +33,16 @@
   UNUSED_PARAMETER(yymajor);  /* Silence some compiler warnings */
   assert( TOKEN.z[0] );  /* The tokenizer always gives us a token */
   if (yypParser->is_fallback_failed && TOKEN.isReserved) {
-    sqlErrorMsg(pParse, "keyword \"%T\" is reserved", &TOKEN);
+    diag_set(ClientError, ER_SQL_KEYWORD_IS_RESERVED, TOKEN.n, TOKEN.z,
+             TOKEN.n, TOKEN.z);
   } else {
-    sqlErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN);
+    diag_set(ClientError, ER_SQL_UNRECOGNIZED_SYNTAX, TOKEN.n, TOKEN.z);
   }
+  sql_parser_error(pParse);
 }
 %stack_overflow {
-  sqlErrorMsg(pParse, "parser stack overflow");
+  diag_set(ClientError, ER_SQL_STACK_OVERFLOW);
+  sql_parser_error(pParse);
 }
 
 // The name of the generated procedure that implements the parser
@@ -114,7 +117,8 @@ ecmd ::= explain cmdx SEMI. {
 		sql_finish_coding(pParse);
 }
 ecmd ::= SEMI. {
-  sqlErrorMsg(pParse, "syntax error: empty request");
+  diag_set(ClientError, ER_SQL_STATEMENT_EMPTY);
+  sql_parser_error(pParse);
 }
 explain ::= .
 explain ::= EXPLAIN.              { pParse->explain = 1; }
@@ -227,7 +231,8 @@ columnname(A) ::= nm(A) typedef(Y). {sqlAddColumn(pParse,&A,&Y);}
 %type nm {Token}
 nm(A) ::= id(A). {
   if(A.isReserved) {
-    sqlErrorMsg(pParse, "keyword \"%T\" is reserved", &A);
+    diag_set(ClientError, ER_SQL_KEYWORD_IS_RESERVED, A.n, A.z, A.n, A.z);
+    sql_parser_error(pParse);
   }
 }
 
@@ -897,14 +902,17 @@ expr(A) ::= VARIABLE(X).     {
   } else if (!(X.z[0]=='#' && sqlIsdigit(X.z[1]))) {
     u32 n = X.n;
     spanExpr(&A, pParse, TK_VARIABLE, X);
-    if (A.pExpr->u.zToken[0] == '?' && n > 1)
-        sqlErrorMsg(pParse, "near \"%T\": syntax error", &t);
-    else
-        sqlExprAssignVarNumber(pParse, A.pExpr, n);
+    if (A.pExpr->u.zToken[0] == '?' && n > 1) {
+      diag_set(ClientError, ER_SQL_UNRECOGNIZED_SYNTAX, t.n, t.z);
+      sql_parser_error(pParse);
+    } else {
+      sqlExprAssignVarNumber(pParse, A.pExpr, n);
+    }
   }else{
     assert( t.n>=2 );
     spanSet(&A, &t, &t);
-    sqlErrorMsg(pParse, "near \"%T\": syntax error", &t);
+    diag_set(ClientError, ER_SQL_UNRECOGNIZED_SYNTAX, t.n, t.z);
+    sql_parser_error(pParse);
     A.pExpr = NULL;
   }
 }
@@ -1378,14 +1386,15 @@ trnm(A) ::= nm DOT nm(X). {
 //
 tridxby ::= .
 tridxby ::= INDEXED BY nm. {
-  sqlErrorMsg(pParse,
-        "the INDEXED BY clause is not allowed on UPDATE or DELETE statements "
-        "within triggers");
+  diag_set(ClientError, ER_SQL_SYNTAX, "trigger body", "the INDEXED BY clause "\
+           "is not allowed on UPDATE or DELETE statements within triggers");
+  sql_parser_error(pParse);
 }
 tridxby ::= NOT INDEXED. {
-  sqlErrorMsg(pParse,
-        "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements "
-        "within triggers");
+  diag_set(ClientError, ER_SQL_SYNTAX, "trigger body", "the NOT INDEXED "\
+           "clause is not allowed on UPDATE or DELETE statements within "\
+           "triggers");
+  sql_parser_error(pParse);
 }
 
 
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 782da1f..ef24760 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -2092,8 +2092,9 @@ computeLimitRegisters(Parse * pParse, Select * p, int iBreak)
 		if((p->pLimit->flags & EP_Collate) != 0 ||
 		   (p->pOffset != NULL &&
 		   (p->pOffset->flags & EP_Collate) != 0)) {
-			sqlErrorMsg(pParse, "near \"COLLATE\": "\
-						"syntax error");
+			diag_set(ClientError, ER_SQL_UNRECOGNIZED_SYNTAX,
+				 sizeof("COLLATE"), "COLLATE");
+			sql_parser_error(pParse);
 			return;
 		}
 		p->iLimit = iLimit = ++pParse->nMem;
@@ -5049,11 +5050,11 @@ selectExpander(Walker * pWalker, Select * p)
 						diag_set(ClientError,
 							 ER_NO_SUCH_SPACE,
 							 zTName);
-						sql_parser_error(pParse);
 					} else {
-						sqlErrorMsg(pParse,
-								"no tables specified");
+						diag_set(ClientError,
+							 ER_SQL_SELECT_WILDCARD);
 					}
+					sql_parser_error(pParse);
 				}
 			}
 		}
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index abf837d..58685c4 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -506,9 +506,10 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 				break;
 			}
 			if (tokenType == TK_ILLEGAL) {
-				sqlErrorMsg(pParse,
-						"unrecognized token: \"%T\"",
-						&pParse->sLastToken);
+				diag_set(ClientError, ER_SQL_UNKNOWN_TOKEN,
+					 pParse->sLastToken.n,
+					 pParse->sLastToken.z);
+				sql_parser_error(pParse);
 				break;
 			}
 		} else {
diff --git a/test/box/misc.result b/test/box/misc.result
index d9f5dd5..27579c6 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -508,6 +508,12 @@ t;
   176: box.error.SQL_CANT_RESOLVE_FIELD
   177: box.error.INDEX_EXISTS_IN_SPACE
   178: box.error.INCONSISTENT_TYPES
+  179: box.error.SQL_SYNTAX
+  180: box.error.SQL_STACK_OVERFLOW
+  181: box.error.SQL_SELECT_WILDCARD
+  182: box.error.SQL_STATEMENT_EMPTY
+  184: box.error.SQL_UNRECOGNIZED_SYNTAX
+  185: box.error.SQL_UNKNOWN_TOKEN
 ...
 test_run:cmd("setopt delimiter ''");
 ---
diff --git a/test/sql-tap/alter2.test.lua b/test/sql-tap/alter2.test.lua
index e219a36..beb6f5d 100755
--- a/test/sql-tap/alter2.test.lua
+++ b/test/sql-tap/alter2.test.lua
@@ -223,7 +223,7 @@ test:do_catchsql_test(
         ALTER TABLE child ADD CONSTRAINT fk FOREIGN KEY REFERENCES child(id);
     ]], {
         -- <alter2-4.1>
-        1, "near \"REFERENCES\": syntax error"
+        1, "Syntax error near 'REFERENCES'"
         -- </alter2-4.1>
     })
 
@@ -233,7 +233,7 @@ test:do_catchsql_test(
         ALTER TABLE child ADD CONSTRAINT fk () FOREIGN KEY REFERENCES child(id);
     ]], {
         -- <alter2-4.1>
-        1, "near \"(\": syntax error"
+        1, "Syntax error near '('"
         -- </alter2-4.2>
     })
 
diff --git a/test/sql-tap/blob.test.lua b/test/sql-tap/blob.test.lua
index 376d5b1..2b5c7a9 100755
--- a/test/sql-tap/blob.test.lua
+++ b/test/sql-tap/blob.test.lua
@@ -72,7 +72,7 @@ test:do_catchsql_test(
         SELECT X'01020k304', 100
     ]], {
         -- <blob-1.4>
-        1, [[unrecognized token: "X'01020k304'"]]
+        1, [[Syntax error: unrecognized token: 'X'01020k304'']]
         -- </blob-1.4>
     })
 
@@ -81,7 +81,7 @@ test:do_catchsql_test(
     [[
         SELECT X'01020, 100]], {
         -- <blob-1.5>
-        1, [[unrecognized token: "X'01020, 100"]]
+        1, [[Syntax error: unrecognized token: 'X'01020, 100']]
         -- </blob-1.5>
     })
 
@@ -91,7 +91,7 @@ test:do_catchsql_test(
         SELECT X'01020 100'
     ]], {
         -- <blob-1.6>
-        1, [[unrecognized token: "X'01020 100'"]]
+        1, [[Syntax error: unrecognized token: 'X'01020 100'']]
         -- </blob-1.6>
     })
 
@@ -101,7 +101,7 @@ test:do_catchsql_test(
         SELECT X'01001'
     ]], {
         -- <blob-1.7>
-        1, [[unrecognized token: "X'01001'"]]
+        1, [[Syntax error: unrecognized token: 'X'01001'']]
         -- </blob-1.7>
     })
 
@@ -111,7 +111,7 @@ test:do_catchsql_test(
         SELECT x'012/45'
     ]], {
         -- <blob-1.8>
-        1, [[unrecognized token: "x'012/45'"]]
+        1, [[Syntax error: unrecognized token: 'x'012/45'']]
         -- </blob-1.8>
     })
 
@@ -121,7 +121,7 @@ test:do_catchsql_test(
         SELECT x'012:45'
     ]], {
         -- <blob-1.9>
-        1, [[unrecognized token: "x'012:45'"]]
+        1, [[Syntax error: unrecognized token: 'x'012:45'']]
         -- </blob-1.9>
     })
 
@@ -131,7 +131,7 @@ test:do_catchsql_test(
         SELECT x'012@45'
     ]], {
         -- <blob-1.10>
-        1, [[unrecognized token: "x'012@45'"]]
+        1, [[Syntax error: unrecognized token: 'x'012@45'']]
         -- </blob-1.10>
     })
 
@@ -141,7 +141,7 @@ test:do_catchsql_test(
         SELECT x'012G45'
     ]], {
         -- <blob-1.11>
-        1, [[unrecognized token: "x'012G45'"]]
+        1, [[Syntax error: unrecognized token: 'x'012G45'']]
         -- </blob-1.11>
     })
 
@@ -151,7 +151,7 @@ test:do_catchsql_test(
         SELECT x'012`45'
     ]], {
         -- <blob-1.12>
-        1, [[unrecognized token: "x'012`45'"]]
+        1, [[Syntax error: unrecognized token: 'x'012`45'']]
         -- </blob-1.12>
     })
 
@@ -161,7 +161,7 @@ test:do_catchsql_test(
         SELECT x'012g45'
     ]], {
         -- <blob-1.13>
-        1, [[unrecognized token: "x'012g45'"]]
+        1, [[Syntax error: unrecognized token: 'x'012g45'']]
         -- </blob-1.13>
     })
 
diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
index 2bb1b2e..b6b7fc7 100755
--- a/test/sql-tap/check.test.lua
+++ b/test/sql-tap/check.test.lua
@@ -281,7 +281,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <check-2.10>
-        1,"near \",\": syntax error"
+        1,"Syntax error near ','"
         -- </check-2.10>
     })
 
@@ -295,7 +295,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <check-2.10>
-        1,"near \",\": syntax error"
+        1,"Syntax error near ','"
         -- </check-2.10>
     })
 
@@ -781,7 +781,7 @@ test:do_catchsql_test(
         ON CONFLICT REPLACE)
     ]], {
         -- <9.1>
-        1, "keyword \"ON\" is reserved"
+        1, "Keyword 'ON' is reserved. Please use double quotes if 'ON' is an identifier."
         -- </9.1>
     })
 
@@ -792,7 +792,7 @@ test:do_catchsql_test(
         ON CONFLICT ABORT)
     ]], {
         -- <9.2>
-        1, "keyword \"ON\" is reserved"
+        1, "Keyword 'ON' is reserved. Please use double quotes if 'ON' is an identifier."
         -- </9.2>
     })
 
@@ -803,7 +803,7 @@ test:do_catchsql_test(
         ON CONFLICT ROLLBACK)
     ]], {
         -- <9.3>
-        1, "keyword \"ON\" is reserved"
+        1, "Keyword 'ON' is reserved. Please use double quotes if 'ON' is an identifier."
         -- </9.3>
     })
 
diff --git a/test/sql-tap/colname.test.lua b/test/sql-tap/colname.test.lua
index b7dd2b7..29fdf13 100755
--- a/test/sql-tap/colname.test.lua
+++ b/test/sql-tap/colname.test.lua
@@ -576,7 +576,7 @@ for i, val in ipairs(data) do
 end
 
 local data2 = {
-    {[['a']],{1, "/syntax error/"}}, -- because ' is delimiter for strings
+    {[['a']],{1, "/Syntax error/"}}, -- because ' is delimiter for strings
     {[[`a`]],{1, "/unrecognized token/"}}, -- because ` is undefined symbol
     {"[a]",{1, "/unrecognized token/"}} -- because [ is undefined symbol
 }
diff --git a/test/sql-tap/count.test.lua b/test/sql-tap/count.test.lua
index fbd60da..cf5bfcc 100755
--- a/test/sql-tap/count.test.lua
+++ b/test/sql-tap/count.test.lua
@@ -128,7 +128,7 @@ test:do_catchsql_test(
         SELECT count(DISTINCT *) FROM t2
     ]], {
         -- <count-2.2>
-        1, [[near "*": syntax error]]
+        1, [[Syntax error near '*']]
         -- </count-2.2>
     })
 
diff --git a/test/sql-tap/default.test.lua b/test/sql-tap/default.test.lua
index 267ce88..67151b0 100755
--- a/test/sql-tap/default.test.lua
+++ b/test/sql-tap/default.test.lua
@@ -224,7 +224,7 @@ test:do_catchsql_test(
         CREATE TABLE t6(id INTEGER PRIMARY KEY, b TEXT DEFAULT id);
     ]], {
     -- <default-5.2>
-    1, "near \"id\": syntax error"
+    1, "Syntax error near 'id'"
     -- </default-5.2>
 })
 
@@ -234,7 +234,7 @@ test:do_catchsql_test(
         CREATE TABLE t6(id INTEGER PRIMARY KEY, b TEXT DEFAULT "id");
     ]], {
     -- <default-5.3>
-    1, "near \"\"id\"\": syntax error"
+    1, "Syntax error near '\"id\"'"
     -- </default-5.3>
 })
 
diff --git a/test/sql-tap/e_select1.test.lua b/test/sql-tap/e_select1.test.lua
index 75fe81e..e190ad7 100755
--- a/test/sql-tap/e_select1.test.lua
+++ b/test/sql-tap/e_select1.test.lua
@@ -105,7 +105,7 @@ test:do_catchsql_test(
         SELECT count(*) FROM t1, t2 USING (a) ON (t1.a=t2.a)
     ]], {
         -- <e_select-0.1.5>
-        1, [[keyword "ON" is reserved]]
+        1, [[Keyword 'ON' is reserved. Please use double quotes if 'ON' is an identifier.]]
         -- </e_select-0.1.5>
     })
 
@@ -807,14 +807,14 @@ test:do_select_tests(
 -- FROM clause.
 --
 data = {
-    {"1.1", "SELECT a, b, c FROM z1 WHERE *",  'near \"*\": syntax error'},
-    {"1.2", "SELECT a, b, c FROM z1 GROUP BY *", 'near \"*\": syntax error'},
-    {"1.3", "SELECT 1 + * FROM z1",  'near \"*\": syntax error'},
-    {"1.4", "SELECT * + 1 FROM z1", 'near \"+\": syntax error'},
-    {"2.1", "SELECT *", 'no tables specified'},
-    {"2.2", "SELECT * WHERE 1", 'no tables specified'},
-    {"2.3", "SELECT * WHERE 0", 'no tables specified'},
-    {"2.4", "SELECT count(*), *", 'no tables specified' }
+    {"1.1", "SELECT a, b, c FROM z1 WHERE *",  "Syntax error near '*'"},
+    {"1.2", "SELECT a, b, c FROM z1 GROUP BY *", "Syntax error near '*'"},
+    {"1.3", "SELECT 1 + * FROM z1",  "Syntax error near '*'"},
+    {"1.4", "SELECT * + 1 FROM z1", "Syntax error near '+'"},
+    {"2.1", "SELECT *", "Failed to expand '*' in SELECT statement without FROM clause"},
+    {"2.2", "SELECT * WHERE 1", "Failed to expand '*' in SELECT statement without FROM clause"},
+    {"2.3", "SELECT * WHERE 0", "Failed to expand '*' in SELECT statement without FROM clause"},
+    {"2.4", "SELECT count(*), *", "Failed to expand '*' in SELECT statement without FROM clause"}
 }
 
 for _, val in ipairs(data) do
diff --git a/test/sql-tap/gh2168-temp-tables.test.lua b/test/sql-tap/gh2168-temp-tables.test.lua
index 536e187..3b29c9e 100755
--- a/test/sql-tap/gh2168-temp-tables.test.lua
+++ b/test/sql-tap/gh2168-temp-tables.test.lua
@@ -8,7 +8,7 @@ test:do_catchsql_test(
 		CREATE TEMP TABLE tmp1 (id INTEGER PRIMARY KEY);
 	]], {
 		-- <trigger2-10.1>
-	1, "near \"TEMP\": syntax error"	
+	1, "Syntax error near 'TEMP'"
 		-- <trigger2-10.1>
 });
 
@@ -23,7 +23,7 @@ test:do_catchsql_test(
 		END;
 	]], {
 		-- <trigger2-10.1>
-	1, "near \"TEMP\": syntax error"	
+	1, "Syntax error near 'TEMP'"
 		-- <trigger2-10.1>
 });
 
diff --git a/test/sql-tap/identifier_case.test.lua b/test/sql-tap/identifier_case.test.lua
index 923d5e6..74c7ce2 100755
--- a/test/sql-tap/identifier_case.test.lua
+++ b/test/sql-tap/identifier_case.test.lua
@@ -238,7 +238,7 @@ test:do_catchsql_test(
 data = {
     { 1,  [[ 'a' < 'b' collate binary ]], {1, "Collation 'BINARY' does not exist"}},
     { 2,  [[ 'a' < 'b' collate "binary" ]], {0, {1}}},
-    { 3,  [[ 'a' < 'b' collate 'binary' ]], {1, [[near "'binary'": syntax error]]}},
+    { 3,  [[ 'a' < 'b' collate 'binary' ]], {1, [[Syntax error near ''binary'']]}},
     { 4,  [[ 'a' < 'b' collate "unicode" ]], {0, {1}}},
     { 5,  [[ 5 < 'b' collate "unicode" ]], {0, {1}}},
     { 6,  [[ 5 < 'b' collate unicode ]], {1,"Collation 'UNICODE' does not exist"}},
diff --git a/test/sql-tap/index-info.test.lua b/test/sql-tap/index-info.test.lua
index d052e8c..a5ed9a9 100755
--- a/test/sql-tap/index-info.test.lua
+++ b/test/sql-tap/index-info.test.lua
@@ -26,7 +26,7 @@ test:do_catchsql_test(
     "index-info-1.2",
     "PRAGMA index_info = t1.a;",
     {
-        1, "near \".\": syntax error",
+        1, "Syntax error near '.'",
     })
 
 -- Case: single column index with an integer column.
diff --git a/test/sql-tap/index1.test.lua b/test/sql-tap/index1.test.lua
index fa97018..b23e9b3 100755
--- a/test/sql-tap/index1.test.lua
+++ b/test/sql-tap/index1.test.lua
@@ -994,7 +994,7 @@ test:do_catchsql_test(
         CREATE INDEX temp.i21 ON t6(c);
     ]], {
         -- <index-21.1>
-        1, "near \".\": syntax error"
+        1, "Syntax error near '.'"
         -- </index-21.1>
     })
 
diff --git a/test/sql-tap/index7.test.lua b/test/sql-tap/index7.test.lua
index 0a7fe60..7dd9efa 100755
--- a/test/sql-tap/index7.test.lua
+++ b/test/sql-tap/index7.test.lua
@@ -292,7 +292,7 @@ test:do_catchsql_test(
         CREATE TABLE t1 (a INTEGER PRIMARY KEY, b INTEGER);
         CREATE UNIQUE INDEX i ON t1 (a) WHERE a = 3;
     ]], {
-        1, "keyword \"WHERE\" is reserved"
+        1, "Keyword 'WHERE' is reserved. Please use double quotes if 'WHERE' is an identifier."
     })
 
 -- Currently, when a user tries to create index (or primary key,
diff --git a/test/sql-tap/join.test.lua b/test/sql-tap/join.test.lua
index bda4091..df272a9 100755
--- a/test/sql-tap/join.test.lua
+++ b/test/sql-tap/join.test.lua
@@ -570,7 +570,7 @@ test:do_catchsql_test(
         SELECT * FROM t1 USING(a) 
     ]], {
         -- <join-3.5>
-        1, "a JOIN clause is required before USING"
+        1, "Syntax error in FROM clause: a JOIN clause is required before ON and USING"
         -- </join-3.5>
     })
 
diff --git a/test/sql-tap/keyword1.test.lua b/test/sql-tap/keyword1.test.lua
index 44fa1cf..9c524d6 100755
--- a/test/sql-tap/keyword1.test.lua
+++ b/test/sql-tap/keyword1.test.lua
@@ -238,7 +238,7 @@ for _, kw in ipairs(bannedkws) do
     test:do_catchsql_test(
         "bannedkw1-"..kw..".1",
         query, {
-            1, 'keyword "'..kw..'" is reserved'
+            1, "Keyword '"..kw.."' is reserved. Please use double quotes if '"..kw.."' is an identifier."
         })
 end
 
diff --git a/test/sql-tap/misc1.test.lua b/test/sql-tap/misc1.test.lua
index 4358b58..1b288da 100755
--- a/test/sql-tap/misc1.test.lua
+++ b/test/sql-tap/misc1.test.lua
@@ -271,7 +271,7 @@ test:do_catchsql_test(
         UPDATE t3 SET a=0 WHEREwww b=2;
     ]], {
         -- <misc1-5.1>
-        1, [[near "WHEREwww": syntax error]]
+        1, [[Syntax error near 'WHEREwww']]
         -- </misc1-5.1>
     })
 
@@ -413,7 +413,7 @@ test:do_catchsql_test(
         SELECT *;
     ]], {
         -- <misc1-8.1>
-        1, "no tables specified"
+        1, "Failed to expand '*' in SELECT statement without FROM clause"
         -- </misc1-8.1>
     })
 
@@ -1037,7 +1037,7 @@ test:do_catchsql_test(
         select''like''like''like#0;
     ]], {
         -- <misc1-21.1>
-        1, [[near "#0": syntax error]]
+        1, [[Syntax error near '#0']]
         -- </misc1-21.1>
     })
 
@@ -1047,7 +1047,7 @@ test:do_catchsql_test(
         VALUES(0,0x0MATCH#0;
     ]], {
         -- <misc1-21.2>
-        1, [[near ";": syntax error]]
+        1, [[Syntax error near ';']]
         -- </misc1-21.2>
     })
 
diff --git a/test/sql-tap/misc5.test.lua b/test/sql-tap/misc5.test.lua
index 0852741..5f98f21 100755
--- a/test/sql-tap/misc5.test.lua
+++ b/test/sql-tap/misc5.test.lua
@@ -299,7 +299,7 @@ test:do_test(
         return test:catchsql(sql)
     end, {
         -- <misc5-7.1>
-        1, "parser stack overflow"
+        1, "Failed to parse SQL statement: parser stack limit reached"
         -- </misc5-7.1>
     })
 
@@ -347,7 +347,7 @@ test:do_catchsql_test(
         SELECT 123abc
     ]], {
         -- <misc5-10.1>
-        1, [[unrecognized token: "123abc"]]
+        1, [[Syntax error: unrecognized token: '123abc']]
         -- </misc5-10.1>
     })
 
@@ -357,7 +357,7 @@ test:do_catchsql_test(
         SELECT 1*123.4e5ghi;
     ]], {
         -- <misc5-10.2>
-        1, [[unrecognized token: "123.4e5ghi"]]
+        1, [[Syntax error: unrecognized token: '123.4e5ghi']]
         -- </misc5-10.2>
     })
 
diff --git a/test/sql-tap/null.test.lua b/test/sql-tap/null.test.lua
index 66135d3..50a2cfb 100755
--- a/test/sql-tap/null.test.lua
+++ b/test/sql-tap/null.test.lua
@@ -517,7 +517,7 @@ test:do_catchsql_test(
     ]],
     {
     -- <index-1.3>
-    1, "near \"1\": syntax error"
+    1, "Syntax error near '1'"
     -- <index-1.3>
     })
 
@@ -528,7 +528,7 @@ test:do_catchsql_test(
     ]],
     {
     -- <index-1.3>
-    1, "near \"1\": syntax error"
+    1, "Syntax error near '1'"
     -- <index-1.3>
     })
 
@@ -539,7 +539,7 @@ test:do_catchsql_test(
     ]],
     {
     -- <index-1.3>
-    1, "near \"1\": syntax error"
+    1, "Syntax error near '1'"
     -- <index-1.3>
     })
 
@@ -550,7 +550,7 @@ test:do_catchsql_test(
     ]],
     {
     -- <index-1.3>
-    1, "near \"1\": syntax error"
+    1, "Syntax error near '1'"
     -- <index-1.3>
     })
 
diff --git a/test/sql-tap/pragma.test.lua b/test/sql-tap/pragma.test.lua
index 975a0e9..14d622f 100755
--- a/test/sql-tap/pragma.test.lua
+++ b/test/sql-tap/pragma.test.lua
@@ -43,7 +43,7 @@ test:do_catchsql_test(
 	[[
 		pragma sql_default_engine 'memtx';
 	]], {
-	1, 'near \"\'memtx\'\": syntax error'
+	1, "Syntax error near ''memtx''"
 })
 
 test:do_catchsql_test(
@@ -51,7 +51,7 @@ test:do_catchsql_test(
 	[[
 		pragma sql_default_engine 1;
 	]], {
-	1, 'near \"1\": syntax error'
+	1, "Syntax error near '1'"
 })
 
 --
diff --git a/test/sql-tap/select1.test.lua b/test/sql-tap/select1.test.lua
index 6c35b6f..d73429a 100755
--- a/test/sql-tap/select1.test.lua
+++ b/test/sql-tap/select1.test.lua
@@ -1406,7 +1406,7 @@ test:do_catchsql_test(
         SELECT f1 FROM test1 WHERE f2=;
     ]], {
         -- <select1-7.1>
-        1, [[near ";": syntax error]]
+        1, [[Syntax error near ';']]
         -- </select1-7.1>
     })
 
@@ -1416,7 +1416,7 @@ test:do_catchsql_test(
             SELECT f1 FROM test1 UNION SELECT WHERE;
         ]], {
             -- <select1-7.2>
-            1, [[keyword "WHERE" is reserved]]
+            1, [[Keyword 'WHERE' is reserved. Please use double quotes if 'WHERE' is an identifier.]]
             -- </select1-7.2>
         })
 
@@ -1428,7 +1428,7 @@ test:do_catchsql_test(
     [[
         SELECT f1 FROM test1 as "hi", test2 as]], {
         -- <select1-7.3>
-        1, [[keyword "as" is reserved]]
+        1, [[Keyword 'as' is reserved. Please use double quotes if 'as' is an identifier.]]
         -- </select1-7.3>
     })
 
@@ -1438,7 +1438,7 @@ test:do_catchsql_test(
         SELECT f1 FROM test1 ORDER BY;
     ]], {
         -- <select1-7.4>
-        1, [[near ";": syntax error]]
+        1, [[Syntax error near ';']]
         -- </select1-7.4>
     })
 
@@ -1448,7 +1448,7 @@ test:do_catchsql_test(
         SELECT f1 FROM test1 ORDER BY f1 desc, f2 where;
     ]], {
         -- <select1-7.5>
-        1, [[keyword "where" is reserved]]
+        1, [[Keyword 'where' is reserved. Please use double quotes if 'where' is an identifier.]]
         -- </select1-7.5>
     })
 
@@ -1458,7 +1458,7 @@ test:do_catchsql_test(
         SELECT count(f1,f2 FROM test1;
     ]], {
         -- <select1-7.6>
-        1, [[keyword "FROM" is reserved]]
+        1, [[Keyword 'FROM' is reserved. Please use double quotes if 'FROM' is an identifier.]]
         -- </select1-7.6>
     })
 
@@ -1468,7 +1468,7 @@ test:do_catchsql_test(
         SELECT count(f1,f2+) FROM test1;
     ]], {
         -- <select1-7.7>
-        1, [[near ")": syntax error]]
+        1, [[Syntax error near ')']]
         -- </select1-7.7>
     })
 
@@ -1478,7 +1478,7 @@ test:do_catchsql_test(
         SELECT f1 FROM test1 ORDER BY f2, f1+;
     ]], {
         -- <select1-7.8>
-        1, [[near ";": syntax error]]
+        1, [[Syntax error near ';']]
         -- </select1-7.8>
     })
 
@@ -1488,7 +1488,7 @@ test:do_catchsql_test(
         SELECT f1 FROM test1 LIMIT 5+3 OFFSET 11 ORDER BY f2;
     ]], {
         -- <select1-7.9>
-        1, [[keyword "ORDER" is reserved]]
+        1, [[Keyword 'ORDER' is reserved. Please use double quotes if 'ORDER' is an identifier.]]
         -- </select1-7.9>
     })
 
@@ -2075,7 +2075,7 @@ test:do_catchsql_test(
         SELECT 1 FROM (SELECT *)
     ]], {
         -- <select1-16.1>
-        1, "no tables specified"
+        1, "Failed to expand '*' in SELECT statement without FROM clause"
         -- </select1-16.1>
     })
 
diff --git a/test/sql-tap/select3.test.lua b/test/sql-tap/select3.test.lua
index e7e306e..9fb825f 100755
--- a/test/sql-tap/select3.test.lua
+++ b/test/sql-tap/select3.test.lua
@@ -182,7 +182,7 @@ test:do_catchsql_test("select3-2.13", [[
   SELECT log, count(*) FROM t1 GROUP BY ORDER BY log;
 ]], {
   -- <select3-2.13>
-  1, [[keyword "ORDER" is reserved]]
+  1, [[Keyword 'ORDER' is reserved. Please use double quotes if 'ORDER' is an identifier.]]
   -- </select3-2.13>
 })
 
@@ -190,7 +190,7 @@ test:do_catchsql_test("select3-2.14", [[
   SELECT log, count(*) FROM t1 GROUP BY;
 ]], {
   -- <select3-2.14>
-  1, [[near ";": syntax error]]
+  1, [[Syntax error near ';']]
   -- </select3-2.14>
 })
 
diff --git a/test/sql-tap/start-transaction.test.lua b/test/sql-tap/start-transaction.test.lua
index 82356ae..c5703d4 100755
--- a/test/sql-tap/start-transaction.test.lua
+++ b/test/sql-tap/start-transaction.test.lua
@@ -21,7 +21,7 @@ test:do_catchsql_test(
 		COMMIT;
 	]], {
 		-- <start-transaction-1.0>
-		1, "near \"BEGIN\": syntax error"
+		1, "Syntax error near 'BEGIN'"
 		-- <start-transaction-1.0>
 	})
 
@@ -46,7 +46,7 @@ test:do_catchsql_test(
 		COMMIT;
 	]], {
 		-- <start-transaction-1.1>
-		1, "near \"BEGIN\": syntax error"
+		1, "Syntax error near 'BEGIN'"
 		-- <start-transaction-1.1>
 	})
 
@@ -94,7 +94,7 @@ test:do_catchsql_test(
 		COMMIT TRANSACTION;
 	]], {
 		-- <start-transaction-1.6>
-		1, "keyword \"TRANSACTION\" is reserved"
+		1, "Keyword 'TRANSACTION' is reserved. Please use double quotes if 'TRANSACTION' is an identifier."
 		-- <start-transaction-1.6>
 	})
 
@@ -119,7 +119,7 @@ test:do_catchsql_test(
 		END;
 	]], {
 		-- <start-transaction-1.8>
-		1, "keyword \"END\" is reserved"
+		1, "Keyword 'END' is reserved. Please use double quotes if 'END' is an identifier."
 		-- <start-transaction-1.8>
 	})
 
@@ -144,7 +144,7 @@ test:do_catchsql_test(
 		END TRANSACTION;
 	]], {
 		-- <start-transaction-1.10>
-		1, "keyword \"END\" is reserved"
+		1, "Keyword 'END' is reserved. Please use double quotes if 'END' is an identifier."
 		-- <start-transaction-1.10>
 	})
 
@@ -193,7 +193,7 @@ test:do_catchsql_test(
 		COMMIT;
 	]], {
 		-- <start-transaction-1.14>
-		1, "keyword \"TRANSACTION\" is reserved"
+		1, "Keyword 'TRANSACTION' is reserved. Please use double quotes if 'TRANSACTION' is an identifier."
 		-- <start-transaction-1.14>
 	})
 
@@ -246,7 +246,7 @@ test:do_catchsql_test(
 		COMMIT;
 	]], {
 		-- <start-transaction-1.18>
-		1, "keyword \"TRANSACTION\" is reserved"
+		1, "Keyword 'TRANSACTION' is reserved. Please use double quotes if 'TRANSACTION' is an identifier."
 		-- <start-transaction-1.18>
 	})
 
diff --git a/test/sql-tap/table.test.lua b/test/sql-tap/table.test.lua
index aa3eea8..c89d890 100755
--- a/test/sql-tap/table.test.lua
+++ b/test/sql-tap/table.test.lua
@@ -620,7 +620,7 @@ test:do_catchsql_test(
 		CREATE TEMP TABLE t1(a INTEGER PRIMARY KEY, b VARCHAR(10));
 	]], {
 	-- <temp>
-	1, "near \"TEMP\": syntax error"
+	1, "Syntax error near 'TEMP'"
 	-- <temp>
 	})
 
@@ -630,7 +630,7 @@ test:do_catchsql_test(
 		CREATE TEMPORARY TABLE t1(a INTEGER PRIMARY KEY, b VARCHAR(10));
 	]], {
 	-- <temporary>
-	1, "near \"TEMPORARY\": syntax error"
+	1, "Syntax error near 'TEMPORARY'"
 	-- <temporary>
 	})
 
@@ -1257,7 +1257,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <table-22.1>
-        1,"keyword \"CONSTRAINT\" is reserved"
+        1,"Keyword 'CONSTRAINT' is reserved. Please use double quotes if 'CONSTRAINT' is an identifier."
         -- </table-22.1>
     })
 
@@ -1322,7 +1322,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <table-22.6>
-        1,"keyword \"CONSTRAINT\" is reserved"
+        1,"Keyword 'CONSTRAINT' is reserved. Please use double quotes if 'CONSTRAINT' is an identifier."
         -- </table-22.6>
     })
 
@@ -1336,7 +1336,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <table-22.7>
-        1,"keyword \"CONSTRAINT\" is reserved"
+        1,"Keyword 'CONSTRAINT' is reserved. Please use double quotes if 'CONSTRAINT' is an identifier."
         -- </table-22.7>
     })
 
diff --git a/test/sql-tap/tkt3935.test.lua b/test/sql-tap/tkt3935.test.lua
index e13391c..cc8fea7 100755
--- a/test/sql-tap/tkt3935.test.lua
+++ b/test/sql-tap/tkt3935.test.lua
@@ -57,7 +57,7 @@ test:do_catchsql_test(
         SELECT a FROM (t1) AS t ON b USING(a) 
     ]], {
         -- <tkt3935.4>
-        1, "a JOIN clause is required before ON"
+        1, "Syntax error in FROM clause: a JOIN clause is required before ON and USING"
         -- </tkt3935.4>
     })
 
@@ -67,7 +67,7 @@ test:do_catchsql_test(
         SELECT a FROM (t1) AS t ON b 
     ]], {
         -- <tkt3935.5>
-        1, "a JOIN clause is required before ON"
+        1, "Syntax error in FROM clause: a JOIN clause is required before ON and USING"
         -- </tkt3935.5>
     })
 
@@ -77,7 +77,7 @@ test:do_catchsql_test(
         SELECT a FROM (SELECT * FROM t1) AS t ON b USING(a) 
     ]], {
         -- <tkt3935.6>
-        1, "a JOIN clause is required before ON"
+        1, "Syntax error in FROM clause: a JOIN clause is required before ON and USING"
         -- </tkt3935.6>
     })
 
@@ -87,7 +87,7 @@ test:do_catchsql_test(
         SELECT a FROM (SELECT * FROM t1) AS t ON b 
     ]], {
         -- <tkt3935.7>
-        1, "a JOIN clause is required before ON"
+        1, "Syntax error in FROM clause: a JOIN clause is required before ON and USING"
         -- </tkt3935.7>
     })
 
@@ -97,7 +97,7 @@ test:do_catchsql_test(
         SELECT a FROM t1 AS t ON b 
     ]], {
         -- <tkt3935.8>
-        1, "a JOIN clause is required before ON"
+        1, "Syntax error in FROM clause: a JOIN clause is required before ON and USING"
         -- </tkt3935.8>
     })
 
@@ -107,7 +107,7 @@ test:do_catchsql_test(
         SELECT a FROM t1 AS t ON b USING(a) 
     ]], {
         -- <tkt3935.9>
-        1, "a JOIN clause is required before ON"
+        1, "Syntax error in FROM clause: a JOIN clause is required before ON and USING"
         -- </tkt3935.9>
     })
 
@@ -117,7 +117,7 @@ test:do_catchsql_test(
         SELECT a FROM t1 AS t USING(a) 
     ]], {
         -- <tkt3935.10>
-        1, "a JOIN clause is required before USING"
+        1, "Syntax error in FROM clause: a JOIN clause is required before ON and USING"
         -- </tkt3935.10>
     })
 
diff --git a/test/sql-tap/tokenize.test.lua b/test/sql-tap/tokenize.test.lua
index 9ef4fd0..b1a097e 100755
--- a/test/sql-tap/tokenize.test.lua
+++ b/test/sql-tap/tokenize.test.lua
@@ -26,7 +26,7 @@ test:do_catchsql_test(
         SELECT 1.0e+
     ]], {
         -- <tokenize-1.1>
-        1, [[unrecognized token: "1.0e"]]
+        1, [[Syntax error: unrecognized token: '1.0e']]
         -- </tokenize-1.1>
     })
 
@@ -36,7 +36,7 @@ test:do_catchsql_test(
         SELECT 1.0E+
     ]], {
         -- <tokenize-1.2>
-        1, [[unrecognized token: "1.0E"]]
+        1, [[Syntax error: unrecognized token: '1.0E']]
         -- </tokenize-1.2>
     })
 
@@ -46,7 +46,7 @@ test:do_catchsql_test(
         SELECT 1.0e-
     ]], {
         -- <tokenize-1.3>
-        1, [[unrecognized token: "1.0e"]]
+        1, [[Syntax error: unrecognized token: '1.0e']]
         -- </tokenize-1.3>
     })
 
@@ -56,7 +56,7 @@ test:do_catchsql_test(
         SELECT 1.0E-
     ]], {
         -- <tokenize-1.4>
-        1, [[unrecognized token: "1.0E"]]
+        1, [[Syntax error: unrecognized token: '1.0E']]
         -- </tokenize-1.4>
     })
 
@@ -66,7 +66,7 @@ test:do_catchsql_test(
         SELECT 1.0e+/
     ]], {
         -- <tokenize-1.5>
-        1, [[unrecognized token: "1.0e"]]
+        1, [[Syntax error: unrecognized token: '1.0e']]
         -- </tokenize-1.5>
     })
 
@@ -76,7 +76,7 @@ test:do_catchsql_test(
         SELECT 1.0E+:
     ]], {
         -- <tokenize-1.6>
-        1, [[unrecognized token: "1.0E"]]
+        1, [[Syntax error: unrecognized token: '1.0E']]
         -- </tokenize-1.6>
     })
 
@@ -86,7 +86,7 @@ test:do_catchsql_test(
         SELECT 1.0e-:
     ]], {
         -- <tokenize-1.7>
-        1, [[unrecognized token: "1.0e"]]
+        1, [[Syntax error: unrecognized token: '1.0e']]
         -- </tokenize-1.7>
     })
 
@@ -96,7 +96,7 @@ test:do_catchsql_test(
         SELECT 1.0E-/
     ]], {
         -- <tokenize-1.8>
-        1, [[unrecognized token: "1.0E"]]
+        1, [[Syntax error: unrecognized token: '1.0E']]
         -- </tokenize-1.8>
     })
 
@@ -106,7 +106,7 @@ test:do_catchsql_test(
         SELECT 1.0F+5
     ]], {
         -- <tokenize-1.9>
-        1, [[unrecognized token: "1.0F"]]
+        1, [[Syntax error: unrecognized token: '1.0F']]
         -- </tokenize-1.9>
     })
 
@@ -116,7 +116,7 @@ test:do_catchsql_test(
         SELECT 1.0d-10
     ]], {
         -- <tokenize-1.10>
-        1, [[unrecognized token: "1.0d"]]
+        1, [[Syntax error: unrecognized token: '1.0d']]
         -- </tokenize-1.10>
     })
 
@@ -126,7 +126,7 @@ test:do_catchsql_test(
         SELECT 1.0e,5
     ]], {
         -- <tokenize-1.11>
-        1, [[unrecognized token: "1.0e"]]
+        1, [[Syntax error: unrecognized token: '1.0e']]
         -- </tokenize-1.11>
     })
 
@@ -136,7 +136,7 @@ test:do_catchsql_test(
         SELECT 1.0E.10
     ]], {
         -- <tokenize-1.12>
-        1, [[unrecognized token: "1.0E"]]
+        1, [[Syntax error: unrecognized token: '1.0E']]
         -- </tokenize-1.12>
     })
 
@@ -145,7 +145,7 @@ test:do_catchsql_test(
     [[
         SELECT 1, 2 /*]], {
         -- <tokenize-2.1>
-        1, [[near "*": syntax error]]
+        1, [[Syntax error near '*']]
         -- </tokenize-2.1>
     })
 
diff --git a/test/sql-tap/trigger1.test.lua b/test/sql-tap/trigger1.test.lua
index 871a144..2984d4c 100755
--- a/test/sql-tap/trigger1.test.lua
+++ b/test/sql-tap/trigger1.test.lua
@@ -72,7 +72,7 @@ test:do_catchsql_test(
         END;
     ]], {
         -- <trigger1-1.1.3>
-        1, [[near "STATEMENT": syntax error]]
+        1, [[Syntax error near 'STATEMENT']]
         -- </trigger1-1.1.3>
     })
 
@@ -297,7 +297,7 @@ test:do_catchsql_test(
         END;
     ]], {
         -- <trigger1-2.1>
-        1, [[near ";": syntax error]]
+        1, [[Syntax error near ';']]
         -- </trigger1-2.1>
     })
 
@@ -310,7 +310,7 @@ test:do_catchsql_test(
         END;
     ]], {
         -- <trigger1-2.2>
-        1, [[near ";": syntax error]]
+        1, [[Syntax error near ';']]
         -- </trigger1-2.2>
     })
 
@@ -809,7 +809,7 @@ test:do_catchsql_test(
         END;
     ]], {
         -- <trigger1-16.4>
-        1, "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements within triggers"
+        1, "Syntax error in trigger body: the NOT INDEXED clause is not allowed on UPDATE or DELETE statements within triggers"
         -- </trigger1-16.4>
     })
 
@@ -821,7 +821,7 @@ test:do_catchsql_test(
         END;
     ]], {
         -- <trigger1-16.5>
-        1, "the INDEXED BY clause is not allowed on UPDATE or DELETE statements within triggers"
+        1, "Syntax error in trigger body: the INDEXED BY clause is not allowed on UPDATE or DELETE statements within triggers"
         -- </trigger1-16.5>
     })
 
@@ -833,7 +833,7 @@ test:do_catchsql_test(
         END;
     ]], {
         -- <trigger1-16.6>
-        1, "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements within triggers"
+        1, "Syntax error in trigger body: the NOT INDEXED clause is not allowed on UPDATE or DELETE statements within triggers"
         -- </trigger1-16.6>
     })
 
@@ -845,7 +845,7 @@ test:do_catchsql_test(
         END;
     ]], {
         -- <trigger1-16.7>
-        1, "the INDEXED BY clause is not allowed on UPDATE or DELETE statements within triggers"
+        1, "Syntax error in trigger body: the INDEXED BY clause is not allowed on UPDATE or DELETE statements within triggers"
         -- </trigger1-16.7>
     })
 
diff --git a/test/sql-tap/view.test.lua b/test/sql-tap/view.test.lua
index c99a0a5..72fdab8 100755
--- a/test/sql-tap/view.test.lua
+++ b/test/sql-tap/view.test.lua
@@ -293,7 +293,7 @@ test:do_catchsql_test(
         CREATE VIEW v1err(x,y DESC,z) AS SELECT a, b+c, c-b FROM t1;
     ]], {
         -- <view-3.3.4>
-        1, [[keyword "DESC" is reserved]]
+        1, [[Keyword 'DESC' is reserved. Please use double quotes if 'DESC' is an identifier.]]
         -- </view-3.3.4>
     })
 
@@ -961,7 +961,7 @@ test:do_catchsql_test(
         DROP VIEW main.nosuchview
     ]], {
         -- <view-17.2>
-        1, "near \".\": syntax error"
+        1, "Syntax error near '.'"
         -- </view-17.2>
     })
 
diff --git a/test/sql-tap/with1.test.lua b/test/sql-tap/with1.test.lua
index 7896483..add2345 100755
--- a/test/sql-tap/with1.test.lua
+++ b/test/sql-tap/with1.test.lua
@@ -178,7 +178,7 @@ test:do_catchsql_test(3.6, [[
   SELECT * FROM tmp;
 ]], {
   -- <3.6>
-  1, [[keyword "SELECT" is reserved]]
+  1, [[Keyword 'SELECT' is reserved. Please use double quotes if 'SELECT' is an identifier.]]
   -- </3.6>
 })
 
@@ -1018,7 +1018,7 @@ test:do_catchsql_test(13.1, [[
   SELECT i FROM c;
 ]], {
   -- <13.1>
-  1, "no tables specified"
+  1, "Failed to expand '*' in SELECT statement without FROM clause"
   -- </13.1>
 })
 
@@ -1027,7 +1027,7 @@ test:do_catchsql_test(13.2, [[
   SELECT i FROM c;
 ]], {
   -- <13.2>
-  1, "no tables specified"
+  1, "Failed to expand '*' in SELECT statement without FROM clause"
   -- </13.2>
 })
 
diff --git a/test/sql-tap/with2.test.lua b/test/sql-tap/with2.test.lua
index c27a9d1..ca3f00e 100755
--- a/test/sql-tap/with2.test.lua
+++ b/test/sql-tap/with2.test.lua
@@ -317,7 +317,7 @@ test:do_catchsql_test(4.1, [[
     SELECT * FROM x;
 ]], {
     -- <4.1>
-    1, [[near ")": syntax error]]
+    1, [[Syntax error near ')']]
     -- </4.1>
 })
 
@@ -519,7 +519,7 @@ test:do_catchsql_test(6.2, [[
     INSERT INTO t2 VALUES(1, 2,);
 ]], {
     -- <6.2>
-    1, [[near ")": syntax error]]
+    1, [[Syntax error near ')']]
     -- </6.2>
 })
 
@@ -528,7 +528,7 @@ test:do_catchsql_test("6.3.1", [[
     INSERT INTO t2 SELECT a, b, FROM t1;
 ]], {
     -- <6.3>
-    1, [[keyword "FROM" is reserved]]
+    1, [[Keyword 'FROM' is reserved. Please use double quotes if 'FROM' is an identifier.]]
     -- </6.3>
 })
 
@@ -546,7 +546,7 @@ test:do_catchsql_test(6.4, [[
     INSERT INTO t2 SELECT a, b, FROM t1 a a a;
 ]], {
     -- <6.4>
-    1, [[keyword "FROM" is reserved]]
+    1, [[Keyword 'FROM' is reserved. Please use double quotes if 'FROM' is an identifier.]]
     -- </6.4>
 })
 
@@ -555,7 +555,7 @@ test:do_catchsql_test(6.5, [[
     DELETE FROM t2 WHERE;
 ]], {
     -- <6.5>
-    1, [[near ";": syntax error]]
+    1, [[Syntax error near ';']]
     -- </6.5>
 })
 
@@ -563,7 +563,7 @@ test:do_catchsql_test(6.6, [[
     WITH x AS (SELECT * FROM t1) DELETE FROM t2 WHERE
 ]], {
     -- <6.6>
-    1, '/near .* syntax error/'
+    1, "Syntax error near '\n'"
     -- </6.6>
 })
 
@@ -571,7 +571,7 @@ test:do_catchsql_test(6.7, [[
     WITH x AS (SELECT * FROM t1) DELETE FROM t2 WHRE 1;
 ]], {
     -- <6.7>
-    1, '/near .* syntax error/'
+    1, "Syntax error near 'WHRE'"
     -- </6.7>
 })
 
@@ -579,7 +579,7 @@ test:do_catchsql_test(6.8, [[
     WITH x AS (SELECT * FROM t1) UPDATE t2 SET a = 10, b = ;
 ]], {
     -- <6.8>
-    1, '/near .* syntax error/'
+    1, "Syntax error near ';'"
     -- </6.8>
 })
 
@@ -587,7 +587,7 @@ test:do_catchsql_test(6.9, [[
     WITH x AS (SELECT * FROM t1) UPDATE t2 SET a = 10, b = 1 WHERE a===b;
 ]], {
     -- <6.9>
-    1, '/near .* syntax error/'
+    1, "Syntax error near '='"
     -- </6.9>
 })
 
diff --git a/test/sql/checks.result b/test/sql/checks.result
index 2eafae8..e31964c 100644
--- a/test/sql/checks.result
+++ b/test/sql/checks.result
@@ -30,7 +30,7 @@ t = {513, 1, 'test', 'memtx', 0, opts, format}
 s = box.space._space:insert(t)
 ---
 - error: 'Wrong space options (field 5): invalid expression specified (SQL error:
-    near "<": syntax error)'
+    Syntax error near ''<'')'
 ...
 opts = {checks = {{expr = 'X>5'}}}
 ---
diff --git a/test/sql/collation.result b/test/sql/collation.result
index faa9f81..3794990 100644
--- a/test/sql/collation.result
+++ b/test/sql/collation.result
@@ -14,23 +14,23 @@ box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
 -- All of these tests should throw error "near "COLLATE": syntax error"
 box.sql.execute("SELECT 1 LIMIT 1 COLLATE BINARY;")
 ---
-- error: 'near "COLLATE": syntax error'
+- error: Syntax error near 'COLLATE'
 ...
 box.sql.execute("SELECT 1 LIMIT 1 COLLATE BINARY OFFSET 1;")
 ---
-- error: 'near "COLLATE": syntax error'
+- error: Syntax error near 'COLLATE'
 ...
 box.sql.execute("SELECT 1 LIMIT 1 OFFSET 1 COLLATE BINARY;")
 ---
-- error: 'near "COLLATE": syntax error'
+- error: Syntax error near 'COLLATE'
 ...
 box.sql.execute("SELECT 1 LIMIT 1, 1 COLLATE BINARY;")
 ---
-- error: 'near "COLLATE": syntax error'
+- error: Syntax error near 'COLLATE'
 ...
 box.sql.execute("SELECT 1 LIMIT 1 COLLATE BINARY, 1;")
 ---
-- error: 'near "COLLATE": syntax error'
+- error: Syntax error near 'COLLATE'
 ...
 -- gh-3052: upper/lower support only default locale
 -- For tr-TR result depends on collation
@@ -102,7 +102,7 @@ cn = remote.connect(box.cfg.listen)
 ...
 cn:execute('select 1 limit ? collate not_exist', {1})
 ---
-- error: 'Failed to execute SQL statement: near "COLLATE": syntax error'
+- error: 'Failed to execute SQL statement: Syntax error near ''COLLATE'''
 ...
 cn:close()
 ---
diff --git a/test/sql/foreign-keys.result b/test/sql/foreign-keys.result
index becb4c2..3c6464e 100644
--- a/test/sql/foreign-keys.result
+++ b/test/sql/foreign-keys.result
@@ -358,19 +358,21 @@ box.sql.execute('CREATE TABLE t1 (id INT PRIMARY KEY);')
 ...
 box.sql.execute('CREATE TABLE t2 (id INT PRIMARY KEY REFERENCES t2 ON DELETE CASCADE ON DELETE RESTRICT);')
 ---
-- error: keyword "DELETE" is reserved
+- error: Keyword 'DELETE' is reserved. Please use double quotes if 'DELETE' is an
+    identifier.
 ...
 box.sql.execute('CREATE TABLE t2 (id INT PRIMARY KEY REFERENCES t2 ON DELETE CASCADE ON DELETE CASCADE);')
 ---
-- error: keyword "DELETE" is reserved
+- error: Keyword 'DELETE' is reserved. Please use double quotes if 'DELETE' is an
+    identifier.
 ...
 box.sql.execute('CREATE TABLE t2 (id INT PRIMARY KEY REFERENCES t2 ON DELETE CASCADE ON UPDATE RESTRICT ON DELETE RESTRICT);')
 ---
-- error: keyword "ON" is reserved
+- error: Keyword 'ON' is reserved. Please use double quotes if 'ON' is an identifier.
 ...
 box.sql.execute('CREATE TABLE t2 (id INT PRIMARY KEY REFERENCES t2 ON DELETE CASCADE MATCH FULL);')
 ---
-- error: keyword "MATCH" is reserved
+- error: Keyword 'MATCH' is reserved. Please use double quotes if 'MATCH' is an identifier.
 ...
 box.space.T1:drop()
 ---
diff --git a/test/sql/gh-3888-values-blob-assert.result b/test/sql/gh-3888-values-blob-assert.result
index 8deded7..611c10d 100644
--- a/test/sql/gh-3888-values-blob-assert.result
+++ b/test/sql/gh-3888-values-blob-assert.result
@@ -16,20 +16,20 @@ box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
 -- check 'VALUES' against typedef keywords (should fail)
 box.sql.execute('VALUES(scalar)')
 ---
-- error: 'near "scalar": syntax error'
+- error: Syntax error near 'scalar'
 ...
 box.sql.execute('VALUES(float)')
 ---
-- error: 'near "float": syntax error'
+- error: Syntax error near 'float'
 ...
 -- check 'SELECT' against typedef keywords (should fail)
 box.sql.execute('SELECT scalar')
 ---
-- error: 'near "scalar": syntax error'
+- error: Syntax error near 'scalar'
 ...
 box.sql.execute('SELECT float')
 ---
-- error: 'near "float": syntax error'
+- error: Syntax error near 'float'
 ...
 -- check 'VALUES' against ID (should fail)
 box.sql.execute('VALUES(TheColumnName)')
diff --git a/test/sql/iproto.result b/test/sql/iproto.result
index da7b40f..938aea9 100644
--- a/test/sql/iproto.result
+++ b/test/sql/iproto.result
@@ -96,7 +96,7 @@ cn:execute('insert into not_existing_table values ("kek")')
 ...
 cn:execute('insert qwerty gjsdjq  q  qwd qmq;; q;qwd;')
 ---
-- error: 'Failed to execute SQL statement: near "qwerty": syntax error'
+- error: 'Failed to execute SQL statement: Syntax error near ''qwerty'''
 ...
 -- Empty result.
 cn:execute('select id as identifier from test where a = 5;')
@@ -109,7 +109,7 @@ cn:execute('select id as identifier from test where a = 5;')
 -- netbox API errors.
 cn:execute(100)
 ---
-- error: 'Failed to execute SQL statement: near "100": syntax error'
+- error: 'Failed to execute SQL statement: Syntax error near ''100'''
 ...
 cn:execute('select 1', nil, {dry_run = true})
 ---
@@ -118,11 +118,11 @@ cn:execute('select 1', nil, {dry_run = true})
 -- Empty request.
 cn:execute('')
 ---
-- error: 'Failed to execute SQL statement: syntax error: empty request'
+- error: 'Failed to execute SQL statement: Failed to execute an empty SQL statement'
 ...
 cn:execute('   ;')
 ---
-- error: 'Failed to execute SQL statement: syntax error: empty request'
+- error: 'Failed to execute SQL statement: Failed to execute an empty SQL statement'
 ...
 --
 -- gh-3467: allow only positive integers under limit clause.
@@ -567,7 +567,7 @@ cn:execute('drop table if exists test3')
 --
 cn:execute('select ?1, ?2, ?3', {1, 2, 3})
 ---
-- error: 'Failed to execute SQL statement: near "?1": syntax error'
+- error: 'Failed to execute SQL statement: Syntax error near ''?1'''
 ...
 cn:execute('select $name, $name2', {1, 2})
 ---
diff --git a/test/sql/misc.result b/test/sql/misc.result
index ef104c1..9a8aa8d 100644
--- a/test/sql/misc.result
+++ b/test/sql/misc.result
@@ -14,11 +14,13 @@ box.sql.execute('select 1;')
 ...
 box.sql.execute('select 1; select 2;')
 ---
-- error: keyword "select" is reserved
+- error: Keyword 'select' is reserved. Please use double quotes if 'select' is an
+    identifier.
 ...
 box.sql.execute('create table t1 (id INT primary key); select 100;')
 ---
-- error: keyword "select" is reserved
+- error: Keyword 'select' is reserved. Please use double quotes if 'select' is an
+    identifier.
 ...
 box.space.t1 == nil
 ---
@@ -26,17 +28,17 @@ box.space.t1 == nil
 ...
 box.sql.execute(';')
 ---
-- error: 'syntax error: empty request'
+- error: Failed to execute an empty SQL statement
 ...
 box.sql.execute('')
 ---
-- error: 'syntax error: empty request'
+- error: Failed to execute an empty SQL statement
 ...
 box.sql.execute('     ;')
 ---
-- error: 'syntax error: empty request'
+- error: Failed to execute an empty SQL statement
 ...
 box.sql.execute('\n\n\n\t\t\t   ')
 ---
-- error: 'syntax error: empty request'
+- error: Failed to execute an empty SQL statement
 ...
diff --git a/test/sql/on-conflict.result b/test/sql/on-conflict.result
index 07bb403..6d37e69 100644
--- a/test/sql/on-conflict.result
+++ b/test/sql/on-conflict.result
@@ -13,37 +13,37 @@ box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
 --
 box.sql.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, v INTEGER UNIQUE ON CONFLICT ABORT)")
 ---
-- error: keyword "ON" is reserved
+- error: Keyword 'ON' is reserved. Please use double quotes if 'ON' is an identifier.
 ...
 box.sql.execute("CREATE TABLE q (id INTEGER PRIMARY KEY, v INTEGER UNIQUE ON CONFLICT FAIL)")
 ---
-- error: keyword "ON" is reserved
+- error: Keyword 'ON' is reserved. Please use double quotes if 'ON' is an identifier.
 ...
 box.sql.execute("CREATE TABLE p (id INTEGER PRIMARY KEY, v INTEGER UNIQUE ON CONFLICT IGNORE)")
 ---
-- error: keyword "ON" is reserved
+- error: Keyword 'ON' is reserved. Please use double quotes if 'ON' is an identifier.
 ...
 box.sql.execute("CREATE TABLE g (id INTEGER PRIMARY KEY, v INTEGER UNIQUE ON CONFLICT REPLACE)")
 ---
-- error: keyword "ON" is reserved
+- error: Keyword 'ON' is reserved. Please use double quotes if 'ON' is an identifier.
 ...
 box.sql.execute("CREATE TABLE e (id INTEGER PRIMARY KEY ON CONFLICT REPLACE, v INTEGER)")
 ---
-- error: keyword "ON" is reserved
+- error: Keyword 'ON' is reserved. Please use double quotes if 'ON' is an identifier.
 ...
 box.sql.execute("CREATE TABLE t1(a INT PRIMARY KEY ON CONFLICT REPLACE)")
 ---
-- error: keyword "ON" is reserved
+- error: Keyword 'ON' is reserved. Please use double quotes if 'ON' is an identifier.
 ...
 box.sql.execute("CREATE TABLE t2(a INT PRIMARY KEY ON CONFLICT IGNORE)")
 ---
-- error: keyword "ON" is reserved
+- error: Keyword 'ON' is reserved. Please use double quotes if 'ON' is an identifier.
 ...
 -- CHECK constraint is illegal with REPLACE option.
 --
 box.sql.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, a INTEGER CHECK (a > 5) ON CONFLICT REPLACE);")
 ---
-- error: keyword "ON" is reserved
+- error: Keyword 'ON' is reserved. Please use double quotes if 'ON' is an identifier.
 ...
 --
 -- gh-3473: Primary key can't be declared with NULL.
diff --git a/test/sql/types.result b/test/sql/types.result
index 11b045c..1220cc0 100644
--- a/test/sql/types.result
+++ b/test/sql/types.result
@@ -8,23 +8,26 @@ test_run = env.new()
 --
 box.sql.execute("CREATE TABLE t1 (id PRIMARY KEY);")
 ---
-- error: keyword "PRIMARY" is reserved
+- error: Keyword 'PRIMARY' is reserved. Please use double quotes if 'PRIMARY' is an
+    identifier.
 ...
 box.sql.execute("CREATE TABLE t1 (a, id INT PRIMARY KEY);")
 ---
-- error: 'near ",": syntax error'
+- error: Syntax error near ','
 ...
 box.sql.execute("CREATE TABLE t1 (id PRIMARY KEY, a INT);")
 ---
-- error: keyword "PRIMARY" is reserved
+- error: Keyword 'PRIMARY' is reserved. Please use double quotes if 'PRIMARY' is an
+    identifier.
 ...
 box.sql.execute("CREATE TABLE t1 (id INT PRIMARY KEY, a);")
 ---
-- error: 'near ")": syntax error'
+- error: Syntax error near ')'
 ...
 box.sql.execute("CREATE TABLE t1 (id INT PRIMARY KEY, a INT, b UNIQUE);")
 ---
-- error: keyword "UNIQUE" is reserved
+- error: Keyword 'UNIQUE' is reserved. Please use double quotes if 'UNIQUE' is an
+    identifier.
 ...
 -- gh-3104: real type is stored in space format.
 --
-- 
2.7.4

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] [PATCH v4 2/8] sql: set SQL parser errors via diag_set()
  2019-03-13 17:03 [tarantool-patches] [PATCH v4 0/8] sql: use diag_set() for errors in SQL imeevma
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 1/8] sql: rework syntax errors imeevma
@ 2019-03-13 17:03 ` imeevma
  2019-03-14 19:26   ` [tarantool-patches] " n.pettik
  2019-03-19 11:24   ` Kirill Yukhin
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 3/8] sql: replace rc with is_aborted status in struct Parse imeevma
                   ` (5 subsequent siblings)
  7 siblings, 2 replies; 43+ messages in thread
From: imeevma @ 2019-03-13 17:03 UTC (permalink / raw)
  To: korablev; +Cc: tarantool-patches

Hi! Thank you for review! Diff between versions and new version of
patch below.

Diff between patches:

commit 61bc67e61298129d66a436d58957bb411b6c9b81
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Wed Mar 6 21:27:51 2019 +0300

    Temporary: Review fix

diff --git a/src/box/sql/malloc.c b/src/box/sql/malloc.c
index e0d2ec8..8812298 100644
--- a/src/box/sql/malloc.c
+++ b/src/box/sql/malloc.c
@@ -55,9 +55,7 @@ sql_sized_malloc(int nByte)
    p[0] = nByte;
    p++;
  } else {
-   testcase(sqlGlobalConfig.xLog != 0);
-   sql_log(SQL_NOMEM,
-         "failed to allocate %u bytes of memory", nByte);
+   diag_set(OutOfMemory, nByte, "realloc", "p");
  }
  return (void *)p;
 }
@@ -115,10 +113,7 @@ sql_sized_realloc(void *pPrior, int nByte)
    p[0] = nByte;
    p++;
  } else {
-   testcase(sqlGlobalConfig.xLog != 0);
-   sql_log(SQL_NOMEM,
-         "failed memory resize %u to %u bytes",
-         sql_sized_sizeof(pPrior), nByte);
+   diag_set(OutOfMemory, nByte, "malloc", "p");
  }
  return (void *)p;
 }
diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
index 0c6296d..828a1ae 100644
--- a/src/box/sql/prepare.c
+++ b/src/box/sql/prepare.c
@@ -102,9 +102,8 @@ sqlPrepare(sql * db,  /* Database handle. */
 
  if (sParse.rc == SQL_DONE)
    sParse.rc = SQL_OK;
- if (db->mallocFailed) {
-   sParse.rc = SQL_NOMEM;
- }
+ if (db->mallocFailed)
+   sParse.rc = SQL_TARANTOOL_ERROR;
  if (pzTail) {
    *pzTail = sParse.zTail;
  }
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index 58685c4..834c165 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -483,7 +483,9 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
              &pParse->sLastToken.isReserved);
      i += pParse->sLastToken.n;
      if (i > mxSqlLen) {
-       pParse->rc = SQL_TOOBIG;
+       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+          "string or blob too big");
+       pParse->rc = SQL_TARANTOOL_ERROR;
        break;
      }
    } else {
@@ -502,7 +504,9 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
      assert(tokenType == TK_SPACE
             || tokenType == TK_ILLEGAL);
      if (db->u1.isInterrupted) {
-       pParse->rc = SQL_INTERRUPT;
+       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+          "interrupted");
+       pParse->rc = SQL_TARANTOOL_ERROR;
        break;
      }
      if (tokenType == TK_ILLEGAL) {
@@ -529,7 +533,7 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 #endif       /* YYDEBUG */
  sqlParserFree(pEngine, sql_free);
  if (db->mallocFailed) {
-   pParse->rc = SQL_NOMEM;
+   pParse->rc = SQL_TARANTOOL_ERROR;
  }
  if (pParse->rc != SQL_OK && pParse->rc != SQL_DONE
      && pParse->zErrMsg == 0) {
diff --git a/src/box/sql/util.c b/src/box/sql/util.c
index e4c93cb..a6d1f5c 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -211,7 +211,8 @@ sqlErrorWithMsg(sql * db, int err_code, const char *zFormat, ...)
 }
 
 /*
- * Add an error message to pParse->zErrMsg and increment pParse->nErr.
+ * Add an error to the diagnostics area, increment pParse->nErr
+ * and set pParse->rc.
  * The following formatting characters are allowed:
  *
  *      %s      Insert a string


New version:

commit 3be89ad14633e9b03c01200ee3d1b3c6776fccc5
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Sun Feb 24 12:02:49 2019 +0300

    sql: set SQL parser errors via diag_set()
    
    After this patch all SQL parser errors will be set via diag_set().
    They were saved in field zErrMsg of struct Parse before this
    patch.
    
    Part of #3965

diff --git a/src/box/errcode.h b/src/box/errcode.h
index f2f47c0..d234d26 100644
--- a/src/box/errcode.h
+++ b/src/box/errcode.h
@@ -238,6 +238,7 @@ struct errcode_record {
  /*183 */_(ER_SQL_KEYWORD_IS_RESERVED, "Keyword '%.*s' is reserved. Please use double quotes if '%.*s' is an identifier.") \
  /*184 */_(ER_SQL_UNRECOGNIZED_SYNTAX, "Syntax error near '%.*s'") \
  /*185 */_(ER_SQL_UNKNOWN_TOKEN,   "Syntax error: unrecognized token: '%.*s'") \
+ /*186 */_(ER_SQL_PARSER_GENERIC,  "%s") \
 
 /*
  * !IMPORTANT! Please follow instructions at start of the file
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index f112c9f..deb5b89 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -194,7 +194,8 @@ sql_finish_coding(struct Parse *parse_context)
    sqlVdbeMakeReady(v, parse_context);
    parse_context->rc = SQL_DONE;
  } else {
-   parse_context->rc = SQL_ERROR;
+   if (parse_context->rc != SQL_TARANTOOL_ERROR)
+     parse_context->rc = SQL_ERROR;
  }
 }
 /**
diff --git a/src/box/sql/malloc.c b/src/box/sql/malloc.c
index e0d2ec8..8812298 100644
--- a/src/box/sql/malloc.c
+++ b/src/box/sql/malloc.c
@@ -55,9 +55,7 @@ sql_sized_malloc(int nByte)
    p[0] = nByte;
    p++;
  } else {
-   testcase(sqlGlobalConfig.xLog != 0);
-   sql_log(SQL_NOMEM,
-         "failed to allocate %u bytes of memory", nByte);
+   diag_set(OutOfMemory, nByte, "realloc", "p");
  }
  return (void *)p;
 }
@@ -115,10 +113,7 @@ sql_sized_realloc(void *pPrior, int nByte)
    p[0] = nByte;
    p++;
  } else {
-   testcase(sqlGlobalConfig.xLog != 0);
-   sql_log(SQL_NOMEM,
-         "failed memory resize %u to %u bytes",
-         sql_sized_sizeof(pPrior), nByte);
+   diag_set(OutOfMemory, nByte, "malloc", "p");
  }
  return (void *)p;
 }
diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
index 0c6296d..828a1ae 100644
--- a/src/box/sql/prepare.c
+++ b/src/box/sql/prepare.c
@@ -102,9 +102,8 @@ sqlPrepare(sql * db,  /* Database handle. */
 
  if (sParse.rc == SQL_DONE)
    sParse.rc = SQL_OK;
- if (db->mallocFailed) {
-   sParse.rc = SQL_NOMEM;
- }
+ if (db->mallocFailed)
+   sParse.rc = SQL_TARANTOOL_ERROR;
  if (pzTail) {
    *pzTail = sParse.zTail;
  }
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index 58685c4..834c165 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -483,7 +483,9 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
              &pParse->sLastToken.isReserved);
      i += pParse->sLastToken.n;
      if (i > mxSqlLen) {
-       pParse->rc = SQL_TOOBIG;
+       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+          "string or blob too big");
+       pParse->rc = SQL_TARANTOOL_ERROR;
        break;
      }
    } else {
@@ -502,7 +504,9 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
      assert(tokenType == TK_SPACE
             || tokenType == TK_ILLEGAL);
      if (db->u1.isInterrupted) {
-       pParse->rc = SQL_INTERRUPT;
+       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+          "interrupted");
+       pParse->rc = SQL_TARANTOOL_ERROR;
        break;
      }
      if (tokenType == TK_ILLEGAL) {
@@ -529,7 +533,7 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 #endif       /* YYDEBUG */
  sqlParserFree(pEngine, sql_free);
  if (db->mallocFailed) {
-   pParse->rc = SQL_NOMEM;
+   pParse->rc = SQL_TARANTOOL_ERROR;
  }
  if (pParse->rc != SQL_OK && pParse->rc != SQL_DONE
      && pParse->zErrMsg == 0) {
diff --git a/src/box/sql/util.c b/src/box/sql/util.c
index c89e2e8..a6d1f5c 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -211,7 +211,8 @@ sqlErrorWithMsg(sql * db, int err_code, const char *zFormat, ...)
 }
 
 /*
- * Add an error message to pParse->zErrMsg and increment pParse->nErr.
+ * Add an error to the diagnostics area, increment pParse->nErr
+ * and set pParse->rc.
  * The following formatting characters are allowed:
  *
  *      %s      Insert a string
@@ -236,10 +237,10 @@ sqlErrorMsg(Parse * pParse, const char *zFormat, ...)
  va_start(ap, zFormat);
  zMsg = sqlVMPrintf(db, zFormat, ap);
  va_end(ap);
+ diag_set(ClientError, ER_SQL_PARSER_GENERIC, zMsg);
+ sqlDbFree(db, zMsg);
  pParse->nErr++;
- sqlDbFree(db, pParse->zErrMsg);
- pParse->zErrMsg = zMsg;
- pParse->rc = SQL_ERROR;
+ pParse->rc = SQL_TARANTOOL_ERROR;
 }
 
 void
diff --git a/test/box/misc.result b/test/box/misc.result
index 27579c6..9f0b2c7 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -514,6 +514,7 @@ t;
   182: box.error.SQL_STATEMENT_EMPTY
   184: box.error.SQL_UNRECOGNIZED_SYNTAX
   185: box.error.SQL_UNKNOWN_TOKEN
+  186: box.error.SQL_PARSER_GENERIC
 ...
 test_run:cmd("setopt delimiter ''");
 ---
diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
index b6b7fc7..bab6493 100755
--- a/test/sql-tap/check.test.lua
+++ b/test/sql-tap/check.test.lua
@@ -319,7 +319,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <check-3.1>
-        1, "Failed to create space 'T3': SQL error: subqueries prohibited in CHECK constraints"
+        1, "Failed to create space 'T3': subqueries prohibited in CHECK constraints"
         -- </check-3.1>
     })
 
diff --git a/test/sql/triggers.result b/test/sql/triggers.result
index 2f5b148..826e998 100644
--- a/test/sql/triggers.result
+++ b/test/sql/triggers.result
@@ -398,14 +398,14 @@ space_id = box.space.T1.id
 ...
 box.sql.execute("CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN new.a = ? BEGIN SELECT 1; END;")
 ---
-- error: 'SQL error: bindings are not allowed in DDL'
+- error: bindings are not allowed in DDL
 ...
 tuple = {"TR1", space_id, {sql = [[CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN new.a = ? BEGIN SELECT 1; END;]]}}
 ---
 ...
 box.space._trigger:insert(tuple)
 ---
-- error: 'SQL error: bindings are not allowed in DDL'
+- error: bindings are not allowed in DDL
 ...
 box.sql.execute("DROP TABLE t1;")
 ---
-- 
2.7.4

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] [PATCH v4 3/8] sql: replace rc with is_aborted status in struct Parse
  2019-03-13 17:03 [tarantool-patches] [PATCH v4 0/8] sql: use diag_set() for errors in SQL imeevma
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 1/8] sql: rework syntax errors imeevma
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 2/8] sql: set SQL parser errors via diag_set() imeevma
@ 2019-03-13 17:03 ` imeevma
  2019-03-14 19:53   ` [tarantool-patches] " n.pettik
  2019-03-19 13:17   ` Kirill Yukhin
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 4/8] sql: remove field nErr from " imeevma
                   ` (4 subsequent siblings)
  7 siblings, 2 replies; 43+ messages in thread
From: imeevma @ 2019-03-13 17:03 UTC (permalink / raw)
  To: korablev; +Cc: tarantool-patches

Hi! Thank you for review! My answers and new version of patch
below. Here won't be diff between patches as I rewritten this
patch to change its positions with one that removes nErr.

On 3/5/19 12:06 PM, n.pettik wrote:
> Nit: remove from.
Fixed.

>> Currently, the field rc of the struct Parse can have only two
>> values: SQL_OK or SQL_TARANTOOL_ERROR. Therefore, it is logical to
>> replace it with a new boolean field. This patche replaces field rc
>> by new field is_aborted.
>
> Fixed commit message (original one contained several mistakes):
>
>     sql: replace rc with is_abort status in stuct Parse
>     
>     Currently, field representing return code in struct Parse can take only
>     two values: SQL_OK (successfully finished parsing) and
>     SQL_TARANTOOL_ERROR (in case of any errors occurred). Therefore, it can
>     be replaced with a boolean field. Patch provides straightforward
>     refactoring.
>     
>     Part of #3965
Thanks, fixed.

>> diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
>> index 0c6296d..42737ff 100644
>> --- a/src/box/sql/prepare.c
>> +++ b/src/box/sql/prepare.c
>> @@ -100,15 +100,15 @@ sqlPrepare(sql * db,	/* Database handle. */
>> 	}
>> 	assert(0 == sParse.nQueryLoop);
>>
>> -	if (sParse.rc == SQL_DONE)
>> -		sParse.rc = SQL_OK;
>> 	if (db->mallocFailed) {
>> -		sParse.rc = SQL_NOMEM;
>> +		diag_set(OutOfMemory, 0, "SQL", "db");
>> +		sParse.is_aborted = true;
>
> See comments for previous patches.
Fixed in patch "sql: set SQL parser errors via diag_set()".

>> diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
>> index 719b85e..42ff4b8 100644
>> --- a/src/box/sql/sqlInt.h
>> +++ b/src/box/sql/sqlInt.h
>> @@ -2643,7 +2643,6 @@ struct Parse {
>> 	sql *db;		/* The main database structure */
>> 	char *zErrMsg;		/* An error message */
>> 	Vdbe *pVdbe;		/* An engine for executing database bytecode */
>> -	int rc;			/* Return code from execution */
>> 	u8 colNamesSet;		/* TRUE after OP_ColumnName has been issued to pVdbe */
>> 	u8 nTempReg;		/* Number of temporary registers in aTempReg[] */
>> 	u8 isMultiWrite;	/* True if statement may modify/insert multiple rows */
>> @@ -2677,6 +2676,8 @@ struct Parse {
>> 	u8 eOrconf;		/* Default ON CONFLICT policy for trigger steps */
>> 	/** Region to make SQL temp allocations. */
>> 	struct region region;
>> +	/** Flag to show that parsing should be aborted. */
>
> Comment is misleading: now we don’t abort parsing process,
> but instead allow it to be finished and raise an error at the end.
> Fix comment pls.
Fixed.

>> +	bool is_aborted;
>>
>>   /**************************************************************************
>>   * Fields above must be initialized to zero.  The fields that follow,
>> @@ -534,25 +533,17 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
>> 	sqlParserFree(pEngine, sql_free);
>> 	if (db->mallocFailed) {
>> 		diag_set(OutOfMemory, 0, "SQL", "db");
>> -		pParse->rc = SQL_TARANTOOL_ERROR;
>> -	}
>> -	if (pParse->rc != SQL_OK && pParse->rc != SQL_DONE
>> -	    && pParse->zErrMsg == 0) {
>> -		const char *error;
>> -		if (is_tarantool_error(pParse->rc) &&
>> -		    tarantoolErrorMessage() != NULL)
>> -			error = tarantoolErrorMessage();
>> -		else
>> -			error = sqlErrStr(pParse->rc);
>> -		pParse->zErrMsg = sqlMPrintf(db, "%s", error);
>> +		pParse->is_aborted = true;
>> 	}
>> +	if (pParse->is_aborted && pParse->zErrMsg == 0)
>> +		pParse->zErrMsg = sqlMPrintf(db, "%s", tarantoolErrorMessage());
>> 	assert(pzErrMsg != 0);
>> 	if (pParse->zErrMsg) {
>> 		*pzErrMsg = pParse->zErrMsg;
>> -		sql_log(pParse->rc, "%s", *pzErrMsg);
>> +		sql_log(SQL_TARANTOOL_ERROR, "%s", *pzErrMsg);
>
> Do we need this call at all?
Fixed, removed.

>> diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
>> index bab6493..0d8bf15 100755
>> --- a/test/sql-tap/check.test.lua
>> +++ b/test/sql-tap/check.test.lua
>> @@ -516,7 +516,7 @@ test:do_catchsql_test(
>>         );
>>     ]], {
>>         -- <check-5.1>
>> -        1, "Wrong space options (field 5): invalid expression specified (SQL error: bindings are not allowed in DDL)"
>> +        1, "Wrong space options (field 5): invalid expression specified (bindings are not allowed in DDL)"
>>         -- </check-5.1>
>>     })
>
> Why test results have changed if you provided
> non-functional refactoring?
It become this way because now the error in diag instead of being
only in zErrMsg of struct Parse.


New version:

commit ad9f22e790f24598fae717ff1de8992b43ef48c9
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Wed Mar 6 22:09:26 2019 +0300

    sql: replace rc with is_aborted status in struct Parse
    
    Currently, field representing return code in struct Parse can take
    only two values: SQL_OK (successfully finished parsing) and
    SQL_TARANTOOL_ERROR (in case of any errors occurred). Therefore,
    it can be replaced with a boolean field. Patch provides
    straightforward refactoring.
    
    Part of #3965

diff --git a/src/box/sql.c b/src/box/sql.c
index a2937a0..c2e5d6b 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -1275,7 +1275,7 @@ sql_ephemeral_space_def_new(struct Parse *parser, const char *name)
 	if (def == NULL) {
 		diag_set(OutOfMemory, size, "region_alloc",
 			 "sql_ephemeral_space_def_new");
-		parser->rc = SQL_TARANTOOL_ERROR;
+		parser->is_aborted = true;
 		parser->nErr++;
 		return NULL;
 	}
@@ -1294,7 +1294,7 @@ sql_ephemeral_space_new(Parse *parser, const char *name)
 	struct space *space = (struct space *) region_alloc(&parser->region, sz);
 	if (space == NULL) {
 		diag_set(OutOfMemory, sz, "region", "space");
-		parser->rc = SQL_TARANTOOL_ERROR;
+		parser->is_aborted = true;
 		parser->nErr++;
 		return NULL;
 	}
@@ -1363,12 +1363,8 @@ sql_checks_resolve_space_def_reference(ExprList *expr_list,
 
 	sql_resolve_self_reference(&parser, def, NC_IsCheck, NULL, expr_list);
 	int rc = 0;
-	if (parser.rc != SQL_OK) {
-		/* Tarantool error may be already set with diag. */
-		if (parser.rc != SQL_TARANTOOL_ERROR)
-			diag_set(ClientError, ER_SQL, parser.zErrMsg);
+	if (parser.is_aborted)
 		rc = -1;
-	}
 	sql_parser_destroy(&parser);
 	return rc;
 }
diff --git a/src/box/sql/alter.c b/src/box/sql/alter.c
index d49ebb8..fe4754f 100644
--- a/src/box/sql/alter.c
+++ b/src/box/sql/alter.c
@@ -72,7 +72,7 @@ exit_rename_table:
 	return;
 tnt_error:
 	sqlDbFree(db, new_name);
-	parse->rc = SQL_TARANTOOL_ERROR;
+	parse->is_aborted = true;
 	parse->nErr++;
 	goto exit_rename_table;
 }
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index ea5cbc3..96b7099 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -907,7 +907,7 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space)
 		if (jump_addrs == NULL) {
 			diag_set(OutOfMemory, sizeof(int) * part_count,
 				 "region", "jump_addrs");
-			parse->rc = SQL_TARANTOOL_ERROR;
+			parse->is_aborted = true;
 			parse->nErr++;
 			return;
 		}
@@ -1130,7 +1130,7 @@ sqlAnalyze(Parse * pParse, Token * pName)
 				}
 			} else {
 				diag_set(ClientError, ER_NO_SUCH_SPACE, z);
-				pParse->rc = SQL_TARANTOOL_ERROR;
+				pParse->is_aborted = true;
 				pParse->nErr++;
 			}
 			sqlDbFree(db, z);
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index deb5b89..0179a45 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -91,7 +91,7 @@ save_record(struct Parse *parser, uint32_t space_id, int reg_key,
 	if (record == NULL) {
 		diag_set(OutOfMemory, sizeof(*record), "region_alloc",
 			 "record");
-		parser->rc = SQL_TARANTOOL_ERROR;
+		parser->is_aborted = true;
 		parser->nErr++;
 		return;
 	}
@@ -146,8 +146,7 @@ sql_finish_coding(struct Parse *parse_context)
 	}
 
 	if (db->mallocFailed || parse_context->nErr != 0) {
-		if (parse_context->rc == SQL_OK)
-			parse_context->rc = SQL_ERROR;
+		parse_context->is_aborted = true;
 		return;
 	}
 	/*
@@ -192,10 +191,8 @@ sql_finish_coding(struct Parse *parse_context)
 	if (parse_context->nErr == 0 && !db->mallocFailed) {
 		assert(parse_context->iCacheLevel == 0);
 		sqlVdbeMakeReady(v, parse_context);
-		parse_context->rc = SQL_DONE;
 	} else {
-		if (parse_context->rc != SQL_TARANTOOL_ERROR)
-			parse_context->rc = SQL_ERROR;
+		parse_context->is_aborted = true;
 	}
 }
 /**
@@ -397,7 +394,7 @@ sql_field_retrieve(Parse *parser, struct space_def *space_def, uint32_t id)
 			diag_set(OutOfMemory, columns_new *
 				sizeof(space_def->fields[0]),
 				"region_alloc", "sql_field_retrieve");
-			parser->rc = SQL_TARANTOOL_ERROR;
+			parser->is_aborted = true;
 			parser->nErr++;
 			return NULL;
 		}
@@ -453,7 +450,7 @@ sqlAddColumn(Parse * pParse, Token * pName, struct type_def *type_def)
 	if (z == NULL) {
 		diag_set(OutOfMemory, pName->n + 1,
 			 "region_alloc", "z");
-		pParse->rc = SQL_TARANTOOL_ERROR;
+		pParse->is_aborted = true;
 		pParse->nErr++;
 		return;
 	}
@@ -501,7 +498,7 @@ sql_column_add_nullable_action(struct Parse *parser,
 				   on_conflict_action_strs[field->
 							   nullable_action]);
 		diag_set(ClientError, ER_SQL, err_msg);
-		parser->rc = SQL_TARANTOOL_ERROR;
+		parser->is_aborted = true;
 		parser->nErr++;
 		return;
 	}
@@ -543,7 +540,7 @@ sqlAddDefaultValue(Parse * pParse, ExprSpan * pSpan)
 				diag_set(OutOfMemory, default_length + 1,
 					 "region_alloc",
 					 "field->default_value");
-				pParse->rc = SQL_TARANTOOL_ERROR;
+				pParse->is_aborted = true;
 				pParse->nErr++;
 				return;
 			}
@@ -562,7 +559,7 @@ field_def_create_for_pk(struct Parse *parser, struct field_def *field,
 	if (field->nullable_action != ON_CONFLICT_ACTION_ABORT &&
 	    field->nullable_action != ON_CONFLICT_ACTION_DEFAULT) {
 		diag_set(ClientError, ER_NULLABLE_PRIMARY, space_name);
-		parser->rc = SQL_TARANTOOL_ERROR;
+		parser->is_aborted = true;
 		parser->nErr++;
 		return -1;
 	} else if (field->nullable_action == ON_CONFLICT_ACTION_DEFAULT) {
@@ -851,7 +848,7 @@ vdbe_emit_create_index(struct Parse *parse, struct space_def *def,
 	save_record(parse, BOX_INDEX_ID, entry_reg, 2, v->nOp - 1);
 	return;
 error:
-	parse->rc = SQL_TARANTOOL_ERROR;
+	parse->is_aborted = true;
 	parse->nErr++;
 
 }
@@ -912,7 +909,7 @@ createSpace(Parse * pParse, int iSpaceId, char *zStmt)
 	return;
 error:
 	pParse->nErr++;
-	pParse->rc = SQL_TARANTOOL_ERROR;
+	pParse->is_aborted = true;
 }
 
 int
@@ -1093,7 +1090,7 @@ vdbe_emit_fk_constraint_create(struct Parse *parse_context,
 	return;
 error:
 	parse_context->nErr++;
-	parse_context->rc = SQL_TARANTOOL_ERROR;
+	parse_context->is_aborted = true;
 }
 
 /**
@@ -1121,7 +1118,7 @@ resolve_link(struct Parse *parse_context, const struct space_def *def,
 	diag_set(ClientError, ER_CREATE_FK_CONSTRAINT, fk_name,
 		 tt_sprintf("unknown column %s in foreign key definition",
 			    field_name));
-	parse_context->rc = SQL_TARANTOOL_ERROR;
+	parse_context->is_aborted = true;
 	parse_context->nErr++;
 	return -1;
 }
@@ -1253,7 +1250,7 @@ sqlEndTable(Parse * pParse,	/* Parse context */
 					 "foreign key does not match the "\
 					 "number of columns in the primary "\
 					 "index of referenced table");
-				pParse->rc = SQL_TARANTOOL_ERROR;
+				pParse->is_aborted = true;
 				pParse->nErr++;
 				return;
 			}
@@ -1329,7 +1326,7 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
 	space->def->opts.sql = strndup(begin->z, n);
 	if (space->def->opts.sql == NULL) {
 		diag_set(OutOfMemory, n, "strndup", "opts.sql");
-		parse_context->rc = SQL_TARANTOOL_ERROR;
+		parse_context->is_aborted = true;
 		parse_context->nErr++;
 		goto create_view_fail;
 	}
@@ -1649,7 +1646,7 @@ sql_drop_table(struct Parse *parse_context, struct SrcList *table_name_list,
 		if (! fk_constraint_is_self_referenced(fk->def)) {
 			diag_set(ClientError, ER_DROP_SPACE, space_name,
 				 "other objects depend on it");
-			parse_context->rc = SQL_TARANTOOL_ERROR;
+			parse_context->is_aborted = true;
 			parse_context->nErr++;
 			goto exit_drop_table;
 		}
@@ -1685,7 +1682,7 @@ columnno_by_name(struct Parse *parse_context, const struct space *space,
 		diag_set(ClientError, ER_CREATE_FK_CONSTRAINT, fk_name,
 			 tt_sprintf("foreign key refers to nonexistent field %s",
 				    column_name));
-		parse_context->rc = SQL_TARANTOOL_ERROR;
+		parse_context->is_aborted = true;
 		parse_context->nErr++;
 		return -1;
 	}
@@ -1895,7 +1892,7 @@ exit_create_fk:
 	sqlDbFree(db, constraint_name);
 	return;
 tnt_error:
-	parse_context->rc = SQL_TARANTOOL_ERROR;
+	parse_context->is_aborted = true;
 	parse_context->nErr++;
 	goto exit_create_fk;
 }
@@ -1920,7 +1917,7 @@ sql_drop_foreign_key(struct Parse *parse_context, struct SrcList *table,
 	struct space *child = space_by_name(table_name);
 	if (child == NULL) {
 		diag_set(ClientError, ER_NO_SUCH_SPACE, table_name);
-		parse_context->rc = SQL_TARANTOOL_ERROR;
+		parse_context->is_aborted = true;
 		parse_context->nErr++;
 		return;
 	}
@@ -2099,7 +2096,7 @@ cleanup:
 		key_def_delete(key_def);
 	return rc;
 tnt_error:
-	parse->rc = SQL_TARANTOOL_ERROR;
+	parse->is_aborted = true;
 	++parse->nErr;
 	goto cleanup;
 }
@@ -2150,7 +2147,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
 		if (space == NULL) {
 			if (! if_not_exist) {
 				diag_set(ClientError, ER_NO_SUCH_SPACE, name);
-				parse->rc = SQL_TARANTOOL_ERROR;
+				parse->is_aborted = true;
 				parse->nErr++;
 			}
 			goto exit_create_index;
@@ -2245,7 +2242,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
 		diag_set(ClientError, ER_MODIFY_INDEX, name, def->name,
 			 "can't create index on system space");
 		parse->nErr++;
-		parse->rc = SQL_TARANTOOL_ERROR;
+		parse->is_aborted = true;
 		goto exit_create_index;
 	}
 
@@ -2273,7 +2270,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
 	index = (struct index *) region_alloc(&parse->region, sizeof(*index));
 	if (index == NULL) {
 		diag_set(OutOfMemory, sizeof(*index), "region", "index");
-		parse->rc = SQL_TARANTOOL_ERROR;
+		parse->is_aborted = true;
 		parse->nErr++;
 		goto exit_create_index;
 	}
diff --git a/src/box/sql/callback.c b/src/box/sql/callback.c
index 4594cac..5c9cf98 100644
--- a/src/box/sql/callback.c
+++ b/src/box/sql/callback.c
@@ -49,7 +49,7 @@ sql_get_coll_seq(Parse *parser, const char *name, uint32_t *coll_id)
 	struct coll_id *p = coll_by_name(name, strlen(name));
 	if (p == NULL) {
 		diag_set(ClientError, ER_NO_SUCH_COLLATION, name);
-		parser->rc = SQL_TARANTOOL_ERROR;
+		parser->is_aborted = true;
 		parser->nErr++;
 		return NULL;
 	} else {
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index 5170c7f..3123681 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -50,7 +50,7 @@ sql_lookup_space(struct Parse *parse, struct SrcList_item *space_name)
 	if (space->def->field_count == 0) {
 		diag_set(ClientError, ER_UNSUPPORTED, "SQL",
 			 "space without format");
-		parse->rc = SQL_TARANTOOL_ERROR;
+		parse->is_aborted = true;
 		parse->nErr++;
 		return NULL;
 	}
@@ -116,7 +116,7 @@ cleanup:
 	return;
 
 tarantool_error:
-	parse->rc = SQL_TARANTOOL_ERROR;
+	parse->is_aborted = true;
 	parse->nErr++;
 	goto cleanup;
 }
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 82688df..39b747d 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -258,7 +258,7 @@ sql_expr_coll(Parse *parse, Expr *p, bool *is_explicit_coll, uint32_t *coll_id,
 						diag_set(ClientError,
 							 ER_ILLEGAL_COLLATION_MIX);
 						parse->nErr++;
-						parse->rc = SQL_TARANTOOL_ERROR;
+						parse->is_aborted = true;
 					}
 					return -1;
 				}
@@ -433,7 +433,7 @@ sql_binary_compare_coll_seq(Parse *parser, Expr *left, Expr *right,
 		return -1;
 	if (collations_check_compatibility(lhs_coll_id, is_lhs_forced,
 					   rhs_coll_id, is_rhs_forced, id) != 0) {
-		parser->rc = SQL_TARANTOOL_ERROR;
+		parser->is_aborted = true;
 		parser->nErr++;
 		return -1;
 	}
diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
index 8b909f2..d8a0bda 100644
--- a/src/box/sql/pragma.c
+++ b/src/box/sql/pragma.c
@@ -506,7 +506,7 @@ sqlPragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 		box_iterator_t* iter;
 		iter = box_index_iterator(space->def->id, 0,ITER_ALL, key_buf, key_end);
 		if (iter == NULL) {
-			pParse->rc = SQL_TARANTOOL_ERROR;
+			pParse->is_aborted = true;
 			pParse->nErr++;
 			goto pragma_out;
 		}
@@ -565,7 +565,7 @@ sqlPragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 		if (!token_is_string(pValue)) {
 			diag_set(ClientError, ER_ILLEGAL_PARAMS,
 				 "string value is expected");
-			pParse->rc = SQL_TARANTOOL_ERROR;
+			pParse->is_aborted = true;
 			pParse->nErr++;
 			goto pragma_out;
 		}
@@ -577,7 +577,7 @@ sqlPragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 			sqlVdbeAddOp2(v, OP_ResultRow, 1, 1);
 		} else {
 			if (sql_default_engine_set(zRight) != 0) {
-				pParse->rc = SQL_TARANTOOL_ERROR;
+				pParse->is_aborted = true;
 				pParse->nErr++;
 				goto pragma_out;
 			}
diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
index 828a1ae..85385ee 100644
--- a/src/box/sql/prepare.c
+++ b/src/box/sql/prepare.c
@@ -52,7 +52,6 @@ sqlPrepare(sql * db,	/* Database handle. */
 	       const char **pzTail	/* OUT: End of parsed string */
     )
 {
-	char *zErrMsg = 0;	/* Error message */
 	int rc = SQL_OK;	/* Result code */
 	Parse sParse;		/* Parsing context */
 	sql_parser_create(&sParse, db);
@@ -89,25 +88,24 @@ sqlPrepare(sql * db,	/* Database handle. */
 		}
 		zSqlCopy = sqlDbStrNDup(db, zSql, nBytes);
 		if (zSqlCopy) {
-			sqlRunParser(&sParse, zSqlCopy, &zErrMsg);
+			sqlRunParser(&sParse, zSqlCopy);
 			sParse.zTail = &zSql[sParse.zTail - zSqlCopy];
 			sqlDbFree(db, zSqlCopy);
 		} else {
 			sParse.zTail = &zSql[nBytes];
 		}
 	} else {
-		sqlRunParser(&sParse, zSql, &zErrMsg);
+		sqlRunParser(&sParse, zSql);
 	}
 	assert(0 == sParse.nQueryLoop);
 
-	if (sParse.rc == SQL_DONE)
-		sParse.rc = SQL_OK;
 	if (db->mallocFailed)
-		sParse.rc = SQL_TARANTOOL_ERROR;
+		sParse.is_aborted = true;
 	if (pzTail) {
 		*pzTail = sParse.zTail;
 	}
-	rc = sParse.rc;
+	if (sParse.is_aborted)
+		rc = SQL_TARANTOOL_ERROR;
 
 	if (rc == SQL_OK && sParse.pVdbe && sParse.explain) {
 		static const char *const azColName[] = {
@@ -168,11 +166,7 @@ sqlPrepare(sql * db,	/* Database handle. */
 		*ppStmt = (sql_stmt *) sParse.pVdbe;
 	}
 
-	if (zErrMsg) {
-		sqlErrorWithMsg(db, rc, "%s", zErrMsg);
-	} else {
-		sqlError(db, rc);
-	}
+	db->errCode = rc;
 
 	/* Delete any TriggerPrg structures allocated while parsing this statement. */
 	while (sParse.pTriggerPrg) {
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 49b0052..5b9d216 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -1341,7 +1341,7 @@ resolveSelectStep(Walker * pWalker, Select * p)
 					 "clause or be used in an aggregate "
 					 "function");
 				pParse->nErr++;
-				pParse->rc = SQL_TARANTOOL_ERROR;
+				pParse->is_aborted = true;
 				return WRC_Abort;
 			}
 			/*
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index ef24760..7185a10 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -2235,7 +2235,7 @@ multi_select_coll_seq_r(struct Parse *parser, struct Select *p, int n,
 	if (collations_check_compatibility(prior_coll_id, is_prior_forced,
 					   current_coll_id, is_current_forced,
 					   &res_coll_id) != 0) {
-		parser->rc = SQL_TARANTOOL_ERROR;
+		parser->is_aborted = true;
 		parser->nErr++;
 		return 0;
 	}
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index eb14885..dd21091 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -2655,7 +2655,6 @@ struct Parse {
 	sql *db;		/* The main database structure */
 	char *zErrMsg;		/* An error message */
 	Vdbe *pVdbe;		/* An engine for executing database bytecode */
-	int rc;			/* Return code from execution */
 	u8 colNamesSet;		/* TRUE after OP_ColumnName has been issued to pVdbe */
 	u8 nTempReg;		/* Number of temporary registers in aTempReg[] */
 	u8 isMultiWrite;	/* True if statement may modify/insert multiple rows */
@@ -2690,6 +2689,8 @@ struct Parse {
 	u8 eOrconf;		/* Default ON CONFLICT policy for trigger steps */
 	/** Region to make SQL temp allocations. */
 	struct region region;
+	/** True, if error should be raised after parsing. */
+	bool is_aborted;
 
   /**************************************************************************
   * Fields above must be initialized to zero.  The fields that follow,
@@ -3200,7 +3201,7 @@ void sqlDequote(char *);
 void sqlNormalizeName(char *z);
 void sqlTokenInit(Token *, char *);
 int sqlKeywordCode(const unsigned char *, int);
-int sqlRunParser(Parse *, const char *, char **);
+int sqlRunParser(Parse *, const char *);
 
 /**
  * Increment error counter.
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index 834c165..5c23ec0 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -437,17 +437,17 @@ parser_space_delete(struct sql *db, struct space *space)
 	sql_expr_list_delete(db, space->def->opts.checks);
 }
 
-/*
- * Run the parser on the given SQL string.  The parser structure is
- * passed in.  An SQL_ status code is returned.  If an error occurs
- * then an and attempt is made to write an error message into
- * memory obtained from sql_malloc() and to make *pzErrMsg point to that
- * error message.
+/**
+ * Run the parser on the given SQL string.
+ *
+ * @param pParse Parser context.
+ * @param zSql SQL string.
+ * @retval 0 on success.
+ * @retval -1 on error.
  */
 int
-sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
+sqlRunParser(Parse * pParse, const char *zSql)
 {
-	int nErr = 0;		/* Number of errors encountered */
 	int i;			/* Loop counter */
 	void *pEngine;		/* The LEMON-generated LALR(1) parser */
 	int tokenType;		/* type of the next token */
@@ -460,15 +460,13 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 	if (db->nVdbeActive == 0) {
 		db->u1.isInterrupted = 0;
 	}
-	pParse->rc = SQL_OK;
 	pParse->zTail = zSql;
 	i = 0;
-	assert(pzErrMsg != 0);
 	/* sqlParserTrace(stdout, "parser: "); */
 	pEngine = sqlParserAlloc(sqlMalloc);
 	if (pEngine == 0) {
 		sqlOomFault(db);
-		return SQL_NOMEM;
+		return -1;
 	}
 	assert(pParse->new_space == NULL);
 	assert(pParse->parsed_ast.trigger == NULL);
@@ -485,7 +483,7 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 			if (i > mxSqlLen) {
 				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
 					 "string or blob too big");
-				pParse->rc = SQL_TARANTOOL_ERROR;
+				pParse->is_aborted = true;
 				break;
 			}
 		} else {
@@ -506,7 +504,7 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 			if (db->u1.isInterrupted) {
 				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
 					 "interrupted");
-				pParse->rc = SQL_TARANTOOL_ERROR;
+				pParse->is_aborted = true;
 				break;
 			}
 			if (tokenType == TK_ILLEGAL) {
@@ -520,11 +518,10 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 			sqlParser(pEngine, tokenType, pParse->sLastToken,
 				      pParse);
 			lastTokenParsed = tokenType;
-			if (pParse->rc != SQL_OK || db->mallocFailed)
+			if (pParse->is_aborted || db->mallocFailed)
 				break;
 		}
 	}
-	assert(nErr == 0);
 	pParse->zTail = &zSql[i];
 #ifdef YYTRACKMAXSTACKDEPTH
 	sqlStatusHighwater(SQL_STATUS_PARSER_STACK,
@@ -532,25 +529,10 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 	    );
 #endif				/* YYDEBUG */
 	sqlParserFree(pEngine, sql_free);
-	if (db->mallocFailed) {
-		pParse->rc = SQL_TARANTOOL_ERROR;
-	}
-	if (pParse->rc != SQL_OK && pParse->rc != SQL_DONE
-	    && pParse->zErrMsg == 0) {
-		const char *error;
-		if (is_tarantool_error(pParse->rc) &&
-		    tarantoolErrorMessage() != NULL)
-			error = tarantoolErrorMessage();
-		else
-			error = sqlErrStr(pParse->rc);
-		pParse->zErrMsg = sqlMPrintf(db, "%s", error);
-	}
-	assert(pzErrMsg != 0);
-	if (pParse->zErrMsg) {
-		*pzErrMsg = pParse->zErrMsg;
-		sql_log(pParse->rc, "%s", *pzErrMsg);
-		nErr++;
-	}
+	if (db->mallocFailed)
+		pParse->is_aborted = true;
+	if (pParse->is_aborted && pParse->zErrMsg == 0)
+		pParse->zErrMsg = sqlMPrintf(db, "%s", tarantoolErrorMessage());
 	if (pParse->pVdbe != NULL && pParse->nErr > 0) {
 		sqlVdbeDelete(pParse->pVdbe);
 		pParse->pVdbe = 0;
@@ -559,8 +541,9 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 	if (pParse->pWithToFree)
 		sqlWithDelete(db, pParse->pWithToFree);
 	sqlDbFree(db, pParse->pVList);
-	assert(nErr == 0 || pParse->rc != SQL_OK);
-	return nErr;
+	if (pParse->is_aborted)
+		return -1;
+	return 0;
 }
 
 struct Expr *
@@ -581,11 +564,8 @@ sql_expr_compile(sql *db, const char *expr, int expr_len)
 	}
 	sprintf(stmt, "%s%.*s", outer, expr_len, expr);
 
-	char *sql_error = NULL;
-	if (sqlRunParser(&parser, stmt, &sql_error) != SQL_OK ||
-	    parser.parsed_ast_type != AST_TYPE_EXPR) {
-		diag_set(ClientError, ER_SQL, sql_error);
-	} else {
+	if (sqlRunParser(&parser, stmt) == 0 &&
+	    parser.parsed_ast_type == AST_TYPE_EXPR) {
 		expression = parser.parsed_ast.expr;
 		parser.parsed_ast.expr = NULL;
 	}
@@ -603,8 +583,7 @@ sql_view_compile(struct sql *db, const char *view_stmt)
 
 	struct Select *select = NULL;
 
-	char *unused;
-	if (sqlRunParser(&parser, view_stmt, &unused) != SQL_OK ||
+	if (sqlRunParser(&parser, view_stmt) != 0 ||
 	    parser.parsed_ast_type != AST_TYPE_SELECT) {
 		diag_set(ClientError, ER_SQL_EXECUTE, view_stmt);
 	} else {
@@ -622,13 +601,9 @@ sql_trigger_compile(struct sql *db, const char *sql)
 	struct Parse parser;
 	sql_parser_create(&parser, db);
 	parser.parse_only = true;
-	char *sql_error = NULL;
 	struct sql_trigger *trigger = NULL;
-	if (sqlRunParser(&parser, sql, &sql_error) != SQL_OK ||
-	    parser.parsed_ast_type != AST_TYPE_TRIGGER) {
-	    if (parser.rc != SQL_TARANTOOL_ERROR)
-		diag_set(ClientError, ER_SQL, sql_error);
-	} else {
+	if (sqlRunParser(&parser, sql) == 0 &&
+	    parser.parsed_ast_type == AST_TYPE_TRIGGER) {
 		trigger = parser.parsed_ast.trigger;
 		parser.parsed_ast.trigger = NULL;
 	}
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index f7e6189..5ee0d96 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -154,7 +154,7 @@ sql_trigger_begin(struct Parse *parse, struct Token *name, int tr_tm,
 	return;
 
 set_tarantool_error_and_cleanup:
-	parse->rc = SQL_TARANTOOL_ERROR;
+	parse->is_aborted = true;
 	parse->nErr++;
 	goto trigger_cleanup;
 }
@@ -735,7 +735,7 @@ transferParseError(Parse * pTo, Parse * pFrom)
 	if (pTo->nErr == 0) {
 		pTo->zErrMsg = pFrom->zErrMsg;
 		pTo->nErr = pFrom->nErr;
-		pTo->rc = pFrom->rc;
+		pTo->is_aborted = pFrom->is_aborted;
 	} else {
 		sqlDbFree(pFrom->db, pFrom->zErrMsg);
 	}
diff --git a/src/box/sql/util.c b/src/box/sql/util.c
index a6d1f5c..5aa4fda 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -212,7 +212,7 @@ sqlErrorWithMsg(sql * db, int err_code, const char *zFormat, ...)
 
 /*
  * Add an error to the diagnostics area, increment pParse->nErr
- * and set pParse->rc.
+ * and set pParse->is_aborted.
  * The following formatting characters are allowed:
  *
  *      %s      Insert a string
@@ -240,14 +240,14 @@ sqlErrorMsg(Parse * pParse, const char *zFormat, ...)
 	diag_set(ClientError, ER_SQL_PARSER_GENERIC, zMsg);
 	sqlDbFree(db, zMsg);
 	pParse->nErr++;
-	pParse->rc = SQL_TARANTOOL_ERROR;
+	pParse->is_aborted = true;
 }
 
 void
 sql_parser_error(struct Parse *parse_context)
 {
 	parse_context->nErr++;
-	parse_context->rc = SQL_TARANTOOL_ERROR;
+	parse_context->is_aborted = true;
 }
 
 /*
diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
index 074ff8c..f417c49 100644
--- a/src/box/sql/vdbemem.c
+++ b/src/box/sql/vdbemem.c
@@ -1203,7 +1203,7 @@ valueFromFunction(sql * db,	/* The database connection */
 		goto value_from_function_out;
 	}
 
-	assert(pCtx->pParse->rc == SQL_OK);
+	assert(!pCtx->pParse->is_aborted);
 	memset(&ctx, 0, sizeof(ctx));
 	ctx.pOut = pVal;
 	ctx.pFunc = pFunc;
@@ -1215,7 +1215,8 @@ valueFromFunction(sql * db,	/* The database connection */
 		sql_value_apply_type(pVal, type);
 		assert(rc == SQL_OK);
 	}
-	pCtx->pParse->rc = rc;
+	if (rc != SQL_OK)
+		pCtx->pParse->is_aborted = true;
 
  value_from_function_out:
 	if (rc != SQL_OK) {
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index 5a3c9be..6c5c61e 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -2800,7 +2800,7 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder,	/* WHERE clause information */
 		if (key_def == NULL) {
 tnt_error:
 			pWInfo->pParse->nErr++;
-			pWInfo->pParse->rc = SQL_TARANTOOL_ERROR;
+			pWInfo->pParse->is_aborted = true;
 			return SQL_TARANTOOL_ERROR;
 		}
 
diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
index bab6493..0d8bf15 100755
--- a/test/sql-tap/check.test.lua
+++ b/test/sql-tap/check.test.lua
@@ -516,7 +516,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <check-5.1>
-        1, "Wrong space options (field 5): invalid expression specified (SQL error: bindings are not allowed in DDL)"
+        1, "Wrong space options (field 5): invalid expression specified (bindings are not allowed in DDL)"
         -- </check-5.1>
     })
 
@@ -528,7 +528,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <check-5.2>
-        1, "Wrong space options (field 5): invalid expression specified (SQL error: bindings are not allowed in DDL)"
+        1, "Wrong space options (field 5): invalid expression specified (bindings are not allowed in DDL)"
         -- </check-5.2>
     })
 
diff --git a/test/sql/checks.result b/test/sql/checks.result
index e31964c..42df657 100644
--- a/test/sql/checks.result
+++ b/test/sql/checks.result
@@ -29,8 +29,8 @@ t = {513, 1, 'test', 'memtx', 0, opts, format}
 ...
 s = box.space._space:insert(t)
 ---
-- error: 'Wrong space options (field 5): invalid expression specified (SQL error:
-    Syntax error near ''<'')'
+- error: 'Wrong space options (field 5): invalid expression specified (Syntax error
+    near ''<'')'
 ...
 opts = {checks = {{expr = 'X>5'}}}
 ---
@@ -122,8 +122,8 @@ box.sql.execute("DROP TABLE w2;")
 --
 box.sql.execute("CREATE TABLE t5(x INT PRIMARY KEY, y INT, CHECK( x*y < ? ));")
 ---
-- error: 'Wrong space options (field 5): invalid expression specified (SQL error:
-    bindings are not allowed in DDL)'
+- error: 'Wrong space options (field 5): invalid expression specified (bindings are
+    not allowed in DDL)'
 ...
 opts = {checks = {{expr = '?>5', name = 'ONE'}}}
 ---
@@ -136,8 +136,8 @@ t = {513, 1, 'test', 'memtx', 0, opts, format}
 ...
 s = box.space._space:insert(t)
 ---
-- error: 'Wrong space options (field 5): invalid expression specified (SQL error:
-    bindings are not allowed in DDL)'
+- error: 'Wrong space options (field 5): invalid expression specified (bindings are
+    not allowed in DDL)'
 ...
 test_run:cmd("clear filter")
 ---
-- 
2.7.4

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] [PATCH v4 4/8] sql: remove field nErr from struct Parse
  2019-03-13 17:03 [tarantool-patches] [PATCH v4 0/8] sql: use diag_set() for errors in SQL imeevma
                   ` (2 preceding siblings ...)
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 3/8] sql: replace rc with is_aborted status in struct Parse imeevma
@ 2019-03-13 17:03 ` imeevma
  2019-03-14 19:58   ` [tarantool-patches] " n.pettik
  2019-03-19 13:27   ` Kirill Yukhin
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 5/8] sql: remove field zErrMsg " imeevma
                   ` (3 subsequent siblings)
  7 siblings, 2 replies; 43+ messages in thread
From: imeevma @ 2019-03-13 17:03 UTC (permalink / raw)
  To: korablev; +Cc: tarantool-patches

Hi! Thank you for review! My answers and new version of patch
below. Here won't be diff between patches as I rewritten this
patch to change its positions with one that removes rc.

On 3/5/19 12:06 PM, n.pettik wrote:
>
>> New version:
>>
>> commit 0448cc416f3401eeecbad1883fea7193399a72aa
>> Author: Mergen Imeev <imeevma@gmail.com>
>> Date:   Tue Feb 26 22:22:26 2019 +0300
>>
>>    sql: remove field nErr of struct Parse
>
> Nit: remove from (not of).
Fixed.

>>
>>    At the moment, the only purpose of the field nErr of struct Parse
>>    is to show whether the field rc of the same struct is SQL_OK or
>>    SQL_TARANTOOL_ERROR. Let's remove it.
>>
>>    Part of #3965
>
> The same problem with ordering here: you replaced nErr > 0 check
> with rc == SQL_TARANTOOL_ERROR and then replaced it with
> is_aborted. If you firstly replaced rc with is_aborted and then
> nErr > 0 -> is_aborted - diff would be slightly smaller.
Fixed, now removing of rc placed before removing of nErr.

>> @@ -145,9 +144,11 @@ sql_finish_coding(struct Parse *parse_context)
>> 			     "Exit with an error if CREATE statement fails"));
>> 	}
>>
>> -	if (db->mallocFailed || parse_context->nErr != 0) {
>> -		if (parse_context->rc == SQL_OK)
>> -			parse_context->rc = SQL_ERROR;
>> +	if (parse_context->rc == SQL_TARANTOOL_ERROR)
>> +		return;
>> +	if (db->mallocFailed) {
>> +		diag_set(OutOfMemory, 0, "SQL", "db”);
>
> It is not a place to set OOM. Firstly, there are a lot of other
> places where mallocFailed is set, but diag is not.
> You should set this error in sqlOomFault(). Check if any
> other function also can set this flag.
Fixed in patch "sql: set SQL parser errors via diag_set()".

>> +		parse_context->rc = SQL_TARANTOOL_ERROR;
>> 		return;
>> 	}
>> 	/*
>> @@ -189,13 +190,12 @@ sql_finish_coding(struct Parse *parse_context)
>> 		sqlVdbeGoto(v, 1);
>> 	}
>> 	/* Get the VDBE program ready for execution. */
>> -	if (parse_context->nErr == 0 && !db->mallocFailed) {
>> +	if (parse_context->rc == SQL_OK && !db->mallocFailed) {
>> 		assert(parse_context->iCacheLevel == 0);
>> 		sqlVdbeMakeReady(v, parse_context);
>> -		parse_context->rc = SQL_DONE;
>> -	} else {
>> -		if (parse_context->rc != SQL_TARANTOOL_ERROR)
>> -			parse_context->rc = SQL_ERROR;
>> +	} else if (parse_context->rc != SQL_TARANTOOL_ERROR){
>> +		diag_set(OutOfMemory, 0, "SQL", "db");
>> +		parse_context->rc = SQL_TARANTOOL_ERROR;
>
> The same here.
Fixed in patch "sql: set SQL parser errors via diag_set()".

>> @@ -1284,7 +1273,7 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
>> 	}
>> 	sqlStartTable(parse_context, name, if_exists);
>> 	struct space *space = parse_context->new_space;
>> -	if (space == NULL || parse_context->nErr != 0)
>> +	if (space == NULL || parse_context->rc == SQL_TARANTOOL_ERROR)
>
> Instead of checking that rc == SQL_TARANTOOL_ERROR, I’d rather
> test that rc != 0. It doesn’t matter now, since rc is anyway removed.
Fixed due to this patch goes after one that removes rc.

>> diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
>> index 58685c4..fac2781 100644
>> --- a/src/box/sql/tokenize.c
>> +++ b/src/box/sql/tokenize.c
>> @@ -483,7 +483,9 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
>> 				      &pParse->sLastToken.isReserved);
>> 			i += pParse->sLastToken.n;
>> 			if (i > mxSqlLen) {
>> -				pParse->rc = SQL_TOOBIG;
>> +				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
>> +					 "string or blob too big");
>> +				pParse->rc = SQL_TARANTOOL_ERROR;
>> 				break;
>
> Move this change to previous patch.
Fixed.

>> @@ -502,7 +504,9 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
>> 			assert(tokenType == TK_SPACE
>> 			       || tokenType == TK_ILLEGAL);
>> 			if (db->u1.isInterrupted) {
>> -				pParse->rc = SQL_INTERRUPT;
>> +				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
>> +					 "interrupted");
>> +				pParse->rc = SQL_TARANTOOL_ERROR;
>
> The same.
Fixed.

>> @@ -529,7 +533,8 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
>> #endif				/* YYDEBUG */
>> 	sqlParserFree(pEngine, sql_free);
>> 	if (db->mallocFailed) {
>> -		pParse->rc = SQL_NOMEM;
>> +		diag_set(OutOfMemory, 0, "SQL", "db");
>> +		pParse->rc = SQL_TARANTOOL_ERROR;
>> 	}
>
> The same.
Fixed.

>> 	if (pParse->rc != SQL_OK && pParse->rc != SQL_DONE
>> 	    && pParse->zErrMsg == 0) {
>> diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
>> index f67c32e..a42e872 100644
>> --- a/src/box/sql/vdbemem.c
>> +++ b/src/box/sql/vdbemem.c
>> @@ -1227,7 +1227,8 @@ valueFromFunction(sql * db,	/* The database connection */
>> 		sql_value_apply_type(pVal, type);
>> 		assert(rc == SQL_OK);
>> 	}
>> -	pCtx->pParse->rc = rc;
>> +	if (rc != SQL_OK)
>> +		pCtx->pParse->rc = SQL_TARANTOOL_ERROR;
>>
>
> Why did you change this code?
Fixed due to this patch goes after one that removes rc.


New version:

commit 0f737d0b5d137b924914f0ef48cb7e32624e3f76
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Wed Mar 6 22:33:10 2019 +0300

    sql: remove field nErr from struct Parse
    
    At the moment, the only purpose of the field nErr of struct Parse
    is to show whether the field is_aborted of the same struct is true
    or false. Let's remove it.
    
    Part of #3965

diff --git a/src/box/sql.c b/src/box/sql.c
index c2e5d6b..1004bb7 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -1276,7 +1276,6 @@ sql_ephemeral_space_def_new(struct Parse *parser, const char *name)
 		diag_set(OutOfMemory, size, "region_alloc",
 			 "sql_ephemeral_space_def_new");
 		parser->is_aborted = true;
-		parser->nErr++;
 		return NULL;
 	}
 
@@ -1295,7 +1294,6 @@ sql_ephemeral_space_new(Parse *parser, const char *name)
 	if (space == NULL) {
 		diag_set(OutOfMemory, sz, "region", "space");
 		parser->is_aborted = true;
-		parser->nErr++;
 		return NULL;
 	}
 
diff --git a/src/box/sql/alter.c b/src/box/sql/alter.c
index fe4754f..bd9b034 100644
--- a/src/box/sql/alter.c
+++ b/src/box/sql/alter.c
@@ -73,7 +73,6 @@ exit_rename_table:
 tnt_error:
 	sqlDbFree(db, new_name);
 	parse->is_aborted = true;
-	parse->nErr++;
 	goto exit_rename_table;
 }
 
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index 96b7099..f95b34b 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -908,7 +908,6 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space)
 			diag_set(OutOfMemory, sizeof(int) * part_count,
 				 "region", "jump_addrs");
 			parse->is_aborted = true;
-			parse->nErr++;
 			return;
 		}
 		/*
@@ -1131,7 +1130,6 @@ sqlAnalyze(Parse * pParse, Token * pName)
 			} else {
 				diag_set(ClientError, ER_NO_SUCH_SPACE, z);
 				pParse->is_aborted = true;
-				pParse->nErr++;
 			}
 			sqlDbFree(db, z);
 		}
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 0179a45..0c06555 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -92,7 +92,6 @@ save_record(struct Parse *parser, uint32_t space_id, int reg_key,
 		diag_set(OutOfMemory, sizeof(*record), "region_alloc",
 			 "record");
 		parser->is_aborted = true;
-		parser->nErr++;
 		return;
 	}
 	record->space_id = space_id;
@@ -145,10 +144,10 @@ sql_finish_coding(struct Parse *parse_context)
 			     "Exit with an error if CREATE statement fails"));
 	}
 
-	if (db->mallocFailed || parse_context->nErr != 0) {
+	if (db->mallocFailed)
 		parse_context->is_aborted = true;
+	if (parse_context->is_aborted)
 		return;
-	}
 	/*
 	 * Begin by generating some termination code at the end
 	 * of the vdbe program
@@ -188,7 +187,7 @@ sql_finish_coding(struct Parse *parse_context)
 		sqlVdbeGoto(v, 1);
 	}
 	/* Get the VDBE program ready for execution. */
-	if (parse_context->nErr == 0 && !db->mallocFailed) {
+	if (!parse_context->is_aborted && !db->mallocFailed) {
 		assert(parse_context->iCacheLevel == 0);
 		sqlVdbeMakeReady(v, parse_context);
 	} else {
@@ -342,7 +341,7 @@ sqlStartTable(Parse *pParse, Token *pName, int noErr)
 	if (space != NULL) {
 		if (!noErr) {
 			diag_set(ClientError, ER_SPACE_EXISTS, zName);
-			sql_parser_error(pParse);
+			pParse->is_aborted = true;
 		} else {
 			assert(!db->init.busy || CORRUPT_DB);
 		}
@@ -395,7 +394,6 @@ sql_field_retrieve(Parse *parser, struct space_def *space_def, uint32_t id)
 				sizeof(space_def->fields[0]),
 				"region_alloc", "sql_field_retrieve");
 			parser->is_aborted = true;
-			parser->nErr++;
 			return NULL;
 		}
 
@@ -451,7 +449,6 @@ sqlAddColumn(Parse * pParse, Token * pName, struct type_def *type_def)
 		diag_set(OutOfMemory, pName->n + 1,
 			 "region_alloc", "z");
 		pParse->is_aborted = true;
-		pParse->nErr++;
 		return;
 	}
 	memcpy(z, pName->z, pName->n);
@@ -460,7 +457,7 @@ sqlAddColumn(Parse * pParse, Token * pName, struct type_def *type_def)
 	for (uint32_t i = 0; i < def->field_count; i++) {
 		if (strcmp(z, def->fields[i].name) == 0) {
 			diag_set(ClientError, ER_SPACE_FIELD_IS_DUPLICATE, z);
-			sql_parser_error(pParse);
+			pParse->is_aborted = true;
 			return;
 		}
 	}
@@ -499,7 +496,6 @@ sql_column_add_nullable_action(struct Parse *parser,
 							   nullable_action]);
 		diag_set(ClientError, ER_SQL, err_msg);
 		parser->is_aborted = true;
-		parser->nErr++;
 		return;
 	}
 	field->nullable_action = nullable_action;
@@ -541,7 +537,6 @@ sqlAddDefaultValue(Parse * pParse, ExprSpan * pSpan)
 					 "region_alloc",
 					 "field->default_value");
 				pParse->is_aborted = true;
-				pParse->nErr++;
 				return;
 			}
 			strncpy(field->default_value, pSpan->zStart,
@@ -560,7 +555,6 @@ field_def_create_for_pk(struct Parse *parser, struct field_def *field,
 	    field->nullable_action != ON_CONFLICT_ACTION_DEFAULT) {
 		diag_set(ClientError, ER_NULLABLE_PRIMARY, space_name);
 		parser->is_aborted = true;
-		parser->nErr++;
 		return -1;
 	} else if (field->nullable_action == ON_CONFLICT_ACTION_DEFAULT) {
 		field->nullable_action = ON_CONFLICT_ACTION_ABORT;
@@ -649,7 +643,7 @@ sqlAddPrimaryKey(Parse * pParse,	/* Parsing context */
 		sql_create_index(pParse, 0, 0, pList, 0, sortOrder, false,
 				 SQL_INDEX_TYPE_CONSTRAINT_PK);
 		pList = 0;
-		if (pParse->nErr > 0)
+		if (pParse->is_aborted)
 			goto primary_key_exit;
 	}
 
@@ -849,7 +843,6 @@ vdbe_emit_create_index(struct Parse *parse, struct space_def *def,
 	return;
 error:
 	parse->is_aborted = true;
-	parse->nErr++;
 
 }
 
@@ -908,7 +901,6 @@ createSpace(Parse * pParse, int iSpaceId, char *zStmt)
 	save_record(pParse, BOX_SPACE_ID, iFirstCol, 1, v->nOp - 1);
 	return;
 error:
-	pParse->nErr++;
 	pParse->is_aborted = true;
 }
 
@@ -1089,7 +1081,6 @@ vdbe_emit_fk_constraint_create(struct Parse *parse_context,
 	sqlReleaseTempRange(parse_context, constr_tuple_reg, 10);
 	return;
 error:
-	parse_context->nErr++;
 	parse_context->is_aborted = true;
 }
 
@@ -1119,7 +1110,6 @@ resolve_link(struct Parse *parse_context, const struct space_def *def,
 		 tt_sprintf("unknown column %s in foreign key definition",
 			    field_name));
 	parse_context->is_aborted = true;
-	parse_context->nErr++;
 	return -1;
 }
 
@@ -1251,7 +1241,6 @@ sqlEndTable(Parse * pParse,	/* Parse context */
 					 "number of columns in the primary "\
 					 "index of referenced table");
 				pParse->is_aborted = true;
-				pParse->nErr++;
 				return;
 			}
 			for (uint32_t i = 0; i < fk_def->field_count; ++i) {
@@ -1281,7 +1270,7 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
 	}
 	sqlStartTable(parse_context, name, if_exists);
 	struct space *space = parse_context->new_space;
-	if (space == NULL || parse_context->nErr != 0)
+	if (space == NULL || parse_context->is_aborted)
 		goto create_view_fail;
 
 	struct space *select_res_space =
@@ -1327,7 +1316,6 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
 	if (space->def->opts.sql == NULL) {
 		diag_set(OutOfMemory, n, "strndup", "opts.sql");
 		parse_context->is_aborted = true;
-		parse_context->nErr++;
 		goto create_view_fail;
 	}
 
@@ -1600,14 +1588,14 @@ sql_drop_table(struct Parse *parse_context, struct SrcList *table_name_list,
 		goto exit_drop_table;
 	}
 	sqlVdbeCountChanges(v);
-	assert(parse_context->nErr == 0);
+	assert(!parse_context->is_aborted);
 	assert(table_name_list->nSrc == 1);
 	const char *space_name = table_name_list->a[0].zName;
 	struct space *space = space_by_name(space_name);
 	if (space == NULL) {
 		if (!if_exists) {
 			diag_set(ClientError, ER_NO_SUCH_SPACE, space_name);
-			sql_parser_error(parse_context);
+			parse_context->is_aborted = true;
 		}
 		goto exit_drop_table;
 	}
@@ -1647,7 +1635,6 @@ sql_drop_table(struct Parse *parse_context, struct SrcList *table_name_list,
 			diag_set(ClientError, ER_DROP_SPACE, space_name,
 				 "other objects depend on it");
 			parse_context->is_aborted = true;
-			parse_context->nErr++;
 			goto exit_drop_table;
 		}
 	}
@@ -1683,7 +1670,6 @@ columnno_by_name(struct Parse *parse_context, const struct space *space,
 			 tt_sprintf("foreign key refers to nonexistent field %s",
 				    column_name));
 		parse_context->is_aborted = true;
-		parse_context->nErr++;
 		return -1;
 	}
 	return 0;
@@ -1893,7 +1879,6 @@ exit_create_fk:
 	return;
 tnt_error:
 	parse_context->is_aborted = true;
-	parse_context->nErr++;
 	goto exit_create_fk;
 }
 
@@ -1918,7 +1903,6 @@ sql_drop_foreign_key(struct Parse *parse_context, struct SrcList *table,
 	if (child == NULL) {
 		diag_set(ClientError, ER_NO_SUCH_SPACE, table_name);
 		parse_context->is_aborted = true;
-		parse_context->nErr++;
 		return;
 	}
 	char *constraint_name = sqlNameFromToken(parse_context->db,
@@ -2045,7 +2029,7 @@ index_fill_def(struct Parse *parse, struct index *index,
 		struct Expr *expr = expr_list->a[i].pExpr;
 		sql_resolve_self_reference(parse, space_def, NC_IdxExpr,
 					   expr, 0);
-		if (parse->nErr > 0)
+		if (parse->is_aborted)
 			goto cleanup;
 
 		struct Expr *column_expr = sqlExprSkipCollate(expr);
@@ -2097,7 +2081,6 @@ cleanup:
 	return rc;
 tnt_error:
 	parse->is_aborted = true;
-	++parse->nErr;
 	goto cleanup;
 }
 
@@ -2125,7 +2108,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
 	struct sql *db = parse->db;
 	assert(!db->init.busy);
 
-	if (db->mallocFailed || parse->nErr > 0)
+	if (db->mallocFailed || parse->is_aborted)
 		goto exit_create_index;
 	if (idx_type == SQL_INDEX_TYPE_UNIQUE ||
 	    idx_type == SQL_INDEX_TYPE_NON_UNIQUE) {
@@ -2148,7 +2131,6 @@ sql_create_index(struct Parse *parse, struct Token *token,
 			if (! if_not_exist) {
 				diag_set(ClientError, ER_NO_SUCH_SPACE, name);
 				parse->is_aborted = true;
-				parse->nErr++;
 			}
 			goto exit_create_index;
 		}
@@ -2194,7 +2176,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
 			if (!if_not_exist) {
 				diag_set(ClientError, ER_INDEX_EXISTS_IN_SPACE,
 					 name, def->name);
-				sql_parser_error(parse);
+				parse->is_aborted = true;
 			}
 			goto exit_create_index;
 		}
@@ -2241,7 +2223,6 @@ sql_create_index(struct Parse *parse, struct Token *token,
 	if (tbl_name != NULL && space_is_system(space)) {
 		diag_set(ClientError, ER_MODIFY_INDEX, name, def->name,
 			 "can't create index on system space");
-		parse->nErr++;
 		parse->is_aborted = true;
 		goto exit_create_index;
 	}
@@ -2271,7 +2252,6 @@ sql_create_index(struct Parse *parse, struct Token *token,
 	if (index == NULL) {
 		diag_set(OutOfMemory, sizeof(*index), "region", "index");
 		parse->is_aborted = true;
-		parse->nErr++;
 		goto exit_create_index;
 	}
 	memset(index, 0, sizeof(*index));
@@ -2441,7 +2421,7 @@ sql_drop_index(struct Parse *parse_context, struct SrcList *index_name_list,
 	assert(v != NULL);
 	struct sql *db = parse_context->db;
 	/* Never called with prior errors. */
-	assert(parse_context->nErr == 0);
+	assert(!parse_context->is_aborted);
 	assert(table_token != NULL);
 	const char *table_name = sqlNameFromToken(db, table_token);
 	if (db->mallocFailed) {
@@ -2454,7 +2434,7 @@ sql_drop_index(struct Parse *parse_context, struct SrcList *index_name_list,
 	if (space == NULL) {
 		if (!if_exists) {
 			diag_set(ClientError, ER_NO_SUCH_SPACE, table_name);
-			sql_parser_error(parse_context);
+			parse_context->is_aborted = true;
 		}
 		goto exit_drop_index;
 	}
@@ -2465,7 +2445,7 @@ sql_drop_index(struct Parse *parse_context, struct SrcList *index_name_list,
 		if (!if_exists) {
 			diag_set(ClientError, ER_NO_SUCH_INDEX_NAME,
 				 index_name, table_name);
-			sql_parser_error(parse_context);
+			parse_context->is_aborted = true;
 		}
 		goto exit_drop_index;
 	}
@@ -2823,7 +2803,7 @@ sqlSrcListAppendFromTerm(Parse * pParse,	/* Parsing context */
 	if (!p && (pOn || pUsing)) {
 		diag_set(ClientError, ER_SQL_SYNTAX, "FROM clause",
 			 "a JOIN clause is required before ON and USING");
-		sql_parser_error(pParse);
+		pParse->is_aborted = true;
 		goto append_from_error;
 	}
 	p = sqlSrcListAppend(db, p, pTable);
diff --git a/src/box/sql/callback.c b/src/box/sql/callback.c
index 5c9cf98..4919753 100644
--- a/src/box/sql/callback.c
+++ b/src/box/sql/callback.c
@@ -50,7 +50,6 @@ sql_get_coll_seq(Parse *parser, const char *name, uint32_t *coll_id)
 	if (p == NULL) {
 		diag_set(ClientError, ER_NO_SUCH_COLLATION, name);
 		parser->is_aborted = true;
-		parser->nErr++;
 		return NULL;
 	} else {
 		*coll_id = p->id;
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index 3123681..87d4ed4 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -43,7 +43,7 @@ sql_lookup_space(struct Parse *parse, struct SrcList_item *space_name)
 	struct space *space = space_by_name(space_name->zName);
 	if (space == NULL) {
 		diag_set(ClientError, ER_NO_SUCH_SPACE, space_name->zName);
-		sql_parser_error(parse);
+		parse->is_aborted = true;
 		return NULL;
 	}
 	assert(space != NULL);
@@ -51,7 +51,6 @@ sql_lookup_space(struct Parse *parse, struct SrcList_item *space_name)
 		diag_set(ClientError, ER_UNSUPPORTED, "SQL",
 			 "space without format");
 		parse->is_aborted = true;
-		parse->nErr++;
 		return NULL;
 	}
 	space_name->space = space;
@@ -117,7 +116,6 @@ cleanup:
 
 tarantool_error:
 	parse->is_aborted = true;
-	parse->nErr++;
 	goto cleanup;
 }
 
@@ -126,7 +124,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 		      struct Expr *where)
 {
 	struct sql *db = parse->db;
-	if (parse->nErr || db->mallocFailed)
+	if (parse->is_aborted || db->mallocFailed)
 		goto delete_from_cleanup;
 
 	assert(tab_list->nSrc == 1);
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 39b747d..4193596 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -254,10 +254,9 @@ sql_expr_coll(Parse *parse, Expr *p, bool *is_explicit_coll, uint32_t *coll_id,
 					 * several times: this
 					 * function is recursive.
 					 */
-					if (parse->nErr == 0) {
+					if (!parse->is_aborted) {
 						diag_set(ClientError,
 							 ER_ILLEGAL_COLLATION_MIX);
-						parse->nErr++;
 						parse->is_aborted = true;
 					}
 					return -1;
@@ -434,7 +433,6 @@ sql_binary_compare_coll_seq(Parse *parser, Expr *left, Expr *right,
 	if (collations_check_compatibility(lhs_coll_id, is_lhs_forced,
 					   rhs_coll_id, is_rhs_forced, id) != 0) {
 		parser->is_aborted = true;
-		parser->nErr++;
 		return -1;
 	}
 	return 0;
@@ -844,7 +842,7 @@ exprSetHeight(Expr * p)
 void
 sqlExprSetHeightAndFlags(Parse * pParse, Expr * p)
 {
-	if (pParse->nErr)
+	if (pParse->is_aborted)
 		return;
 	exprSetHeight(p);
 	sqlExprCheckHeight(pParse, p->nHeight);
@@ -1028,7 +1026,7 @@ sqlPExpr(Parse * pParse,	/* Parsing context */
     )
 {
 	Expr *p;
-	if (op == TK_AND && pParse->nErr == 0) {
+	if (op == TK_AND && !pParse->is_aborted) {
 		/* Take advantage of short-circuit false optimization for AND */
 		p = sqlExprAnd(pParse->db, pLeft, pRight);
 	} else {
@@ -2430,7 +2428,7 @@ sqlFindInIndex(Parse * pParse,	/* Parsing context */
 	 * satisfy the query.  This is preferable to generating a new
 	 * ephemeral table.
 	 */
-	if (pParse->nErr == 0 && (p = isCandidateForInOpt(pX)) != 0) {
+	if (!pParse->is_aborted && (p = isCandidateForInOpt(pX)) != 0) {
 		sql *db = pParse->db;	/* Database connection */
 		ExprList *pEList = p->pEList;
 		int nExpr = pEList->nExpr;
@@ -3064,7 +3062,7 @@ sqlExprCodeIN(Parse * pParse,	/* Parsing and code generating context */
 				   destIfFalse == destIfNull ? 0 : &rRhsHasNull,
 				   aiMap, 0);
 
-	assert(pParse->nErr || nVector == 1 || eType == IN_INDEX_EPH
+	assert(pParse->is_aborted || nVector == 1 || eType == IN_INDEX_EPH
 	       || eType == IN_INDEX_INDEX_ASC || eType == IN_INDEX_INDEX_DESC);
 #ifdef SQL_DEBUG
 	/* Confirm that aiMap[] contains nVector integer values between 0 and
@@ -3387,7 +3385,7 @@ sqlExprCacheStore(Parse * pParse, int iTab, int iCol, int iReg)
 	struct yColCache *p;
 
 	/* Unless an error has occurred, register numbers are always positive. */
-	assert(iReg > 0 || pParse->nErr || pParse->db->mallocFailed);
+	assert(iReg > 0 || pParse->is_aborted || pParse->db->mallocFailed);
 	assert(iCol >= -1 && iCol < 32768);	/* Finite column numbers */
 
 	/* The SQL_ColumnCache flag disables the column cache.  This is used
@@ -4020,7 +4018,7 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 			if (pDef == 0 || pDef->xFinalize != 0) {
 				diag_set(ClientError, ER_NO_SUCH_FUNCTION,
 					 zId);
-				sql_parser_error(pParse);
+				pParse->is_aborted = true;
 				break;
 			}
 
@@ -4364,7 +4362,7 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 			} else {
 				sqlVdbeAddOp2(v, OP_Null, 0, target);
 			}
-			assert(pParse->db->mallocFailed || pParse->nErr > 0
+			assert(pParse->db->mallocFailed || pParse->is_aborted
 			       || pParse->iCacheLevel == iCacheLevel);
 			sqlVdbeResolveLabel(v, endLabel);
 			break;
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 17fbdec..6f7f020 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -277,7 +277,7 @@ sqlInsert(Parse * pParse,	/* Parser context */
 
 	db = pParse->db;
 	memset(&dest, 0, sizeof(dest));
-	if (pParse->nErr || db->mallocFailed) {
+	if (pParse->is_aborted || db->mallocFailed) {
 		goto insert_cleanup;
 	}
 
@@ -426,7 +426,7 @@ sqlInsert(Parse * pParse,	/* Parser context */
 		dest.nSdst = space_def->field_count;
 		rc = sqlSelect(pParse, pSelect, &dest);
 		regFromSelect = dest.iSdst;
-		if (rc || db->mallocFailed || pParse->nErr)
+		if (rc || db->mallocFailed || pParse->is_aborted)
 			goto insert_cleanup;
 		sqlVdbeEndCoroutine(v, regYield);
 		sqlVdbeJumpHere(v, addrTop - 1);	/* label B: */
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index 66fb44b..3e86a7f 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -38,11 +38,11 @@
   } else {
     diag_set(ClientError, ER_SQL_UNRECOGNIZED_SYNTAX, TOKEN.n, TOKEN.z);
   }
-  sql_parser_error(pParse);
+  pParse->is_aborted = true;
 }
 %stack_overflow {
   diag_set(ClientError, ER_SQL_STACK_OVERFLOW);
-  sql_parser_error(pParse);
+  pParse->is_aborted = true;
 }
 
 // The name of the generated procedure that implements the parser
@@ -118,7 +118,7 @@ ecmd ::= explain cmdx SEMI. {
 }
 ecmd ::= SEMI. {
   diag_set(ClientError, ER_SQL_STATEMENT_EMPTY);
-  sql_parser_error(pParse);
+  pParse->is_aborted = true;
 }
 explain ::= .
 explain ::= EXPLAIN.              { pParse->explain = 1; }
@@ -232,7 +232,7 @@ columnname(A) ::= nm(A) typedef(Y). {sqlAddColumn(pParse,&A,&Y);}
 nm(A) ::= id(A). {
   if(A.isReserved) {
     diag_set(ClientError, ER_SQL_KEYWORD_IS_RESERVED, A.n, A.z, A.n, A.z);
-    sql_parser_error(pParse);
+    pParse->is_aborted = true;
   }
 }
 
@@ -904,7 +904,7 @@ expr(A) ::= VARIABLE(X).     {
     spanExpr(&A, pParse, TK_VARIABLE, X);
     if (A.pExpr->u.zToken[0] == '?' && n > 1) {
       diag_set(ClientError, ER_SQL_UNRECOGNIZED_SYNTAX, t.n, t.z);
-      sql_parser_error(pParse);
+      pParse->is_aborted = true;
     } else {
       sqlExprAssignVarNumber(pParse, A.pExpr, n);
     }
@@ -912,7 +912,7 @@ expr(A) ::= VARIABLE(X).     {
     assert( t.n>=2 );
     spanSet(&A, &t, &t);
     diag_set(ClientError, ER_SQL_UNRECOGNIZED_SYNTAX, t.n, t.z);
-    sql_parser_error(pParse);
+    pParse->is_aborted = true;
     A.pExpr = NULL;
   }
 }
@@ -1388,13 +1388,13 @@ tridxby ::= .
 tridxby ::= INDEXED BY nm. {
   diag_set(ClientError, ER_SQL_SYNTAX, "trigger body", "the INDEXED BY clause "\
            "is not allowed on UPDATE or DELETE statements within triggers");
-  sql_parser_error(pParse);
+  pParse->is_aborted = true;
 }
 tridxby ::= NOT INDEXED. {
   diag_set(ClientError, ER_SQL_SYNTAX, "trigger body", "the NOT INDEXED "\
            "clause is not allowed on UPDATE or DELETE statements within "\
            "triggers");
-  sql_parser_error(pParse);
+  pParse->is_aborted = true;
 }
 
 
diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
index d8a0bda..2b9c9b4 100644
--- a/src/box/sql/pragma.c
+++ b/src/box/sql/pragma.c
@@ -440,7 +440,7 @@ sqlPragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 	pPragma = pragmaLocate(zLeft);
 	if (pPragma == 0) {
 		diag_set(ClientError, ER_SQL_NO_SUCH_PRAGMA, zLeft);
-		sql_parser_error(pParse);
+		pParse->is_aborted = true;
 		goto pragma_out;
 	}
 	/* Register the result column names for pragmas that return results */
@@ -507,7 +507,6 @@ sqlPragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 		iter = box_index_iterator(space->def->id, 0,ITER_ALL, key_buf, key_end);
 		if (iter == NULL) {
 			pParse->is_aborted = true;
-			pParse->nErr++;
 			goto pragma_out;
 		}
 		int rc = box_iterator_next(iter, &tuple);
@@ -566,7 +565,6 @@ sqlPragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 			diag_set(ClientError, ER_ILLEGAL_PARAMS,
 				 "string value is expected");
 			pParse->is_aborted = true;
-			pParse->nErr++;
 			goto pragma_out;
 		}
 		if (zRight == NULL) {
@@ -578,7 +576,6 @@ sqlPragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 		} else {
 			if (sql_default_engine_set(zRight) != 0) {
 				pParse->is_aborted = true;
-				pParse->nErr++;
 				goto pragma_out;
 			}
 			sqlVdbeAddOp0(v, OP_Expire);
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 5b9d216..94bb0af 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -441,7 +441,7 @@ lookupName(Parse * pParse,	/* The parsing context */
 			diag_set(ClientError, ER_NO_SUCH_FIELD_NAME, zCol,
 				 zTab);
 		}
-		sql_parser_error(pParse);
+		pParse->is_aborted = true;
 		pTopNC->nErr++;
 	}
 
@@ -707,7 +707,7 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 #endif
 			    ) {
 				diag_set(ClientError, ER_NO_SUCH_FUNCTION, zId);
-				sql_parser_error(pParse);
+				pParse->is_aborted = true;
 				pNC->nErr++;
 			} else if (wrong_num_args) {
 				sqlErrorMsg(pParse,
@@ -809,7 +809,7 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 			break;
 		}
 	}
-	return (pParse->nErr
+	return (pParse->is_aborted
 		|| pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue;
 }
 
@@ -1195,7 +1195,7 @@ resolveSelectStep(Walker * pWalker, Select * p)
 	 */
 	if ((p->selFlags & SF_Expanded) == 0) {
 		sqlSelectPrep(pParse, p, pOuterNC);
-		return (pParse->nErr
+		return (pParse->is_aborted
 			|| db->mallocFailed) ? WRC_Abort : WRC_Prune;
 	}
 
@@ -1252,7 +1252,7 @@ resolveSelectStep(Walker * pWalker, Select * p)
 				sqlResolveSelectNames(pParse,
 							  pItem->pSelect,
 							  pOuterNC);
-				if (pParse->nErr || db->mallocFailed)
+				if (pParse->is_aborted || db->mallocFailed)
 					return WRC_Abort;
 
 				for (pNC = pOuterNC; pNC; pNC = pNC->pNext)
@@ -1340,7 +1340,6 @@ resolveSelectStep(Walker * pWalker, Select * p)
 					 "argument must appear in the GROUP BY "
 					 "clause or be used in an aggregate "
 					 "function");
-				pParse->nErr++;
 				pParse->is_aborted = true;
 				return WRC_Abort;
 			}
@@ -1536,7 +1535,7 @@ sqlResolveExprNames(NameContext * pNC,	/* Namespace to resolve expressions in. *
 #if SQL_MAX_EXPR_DEPTH>0
 	pNC->pParse->nHeight -= pExpr->nHeight;
 #endif
-	if (pNC->nErr > 0 || w.pParse->nErr > 0) {
+	if (pNC->nErr > 0 || w.pParse->is_aborted) {
 		ExprSetProperty(pExpr, EP_Error);
 	}
 	if (pNC->ncFlags & NC_HasAgg) {
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 7185a10..e83b898 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -196,13 +196,13 @@ sqlSelectNew(Parse * pParse,	/* Parsing context */
 	pNew->pLimit = pLimit;
 	pNew->pOffset = pOffset;
 	pNew->pWith = 0;
-	assert(pOffset == 0 || pLimit != 0 || pParse->nErr > 0
+	assert(pOffset == 0 || pLimit != 0 || pParse->is_aborted
 	       || db->mallocFailed != 0);
 	if (db->mallocFailed) {
 		clearSelect(db, pNew, pNew != &standin);
 		pNew = 0;
 	} else {
-		assert(pNew->pSrc != 0 || pParse->nErr > 0);
+		assert(pNew->pSrc != 0 || pParse->is_aborted);
 	}
 	assert(pNew != &standin);
 	return pNew;
@@ -2003,7 +2003,7 @@ sqlResultSetOfSelect(Parse * pParse, Select * pSelect)
 	user_session->sql_flags |= ~SQL_FullColNames;
 	user_session->sql_flags &= SQL_ShortColNames;
 	sqlSelectPrep(pParse, pSelect, 0);
-	if (pParse->nErr)
+	if (pParse->is_aborted)
 		return NULL;
 	while (pSelect->pPrior)
 		pSelect = pSelect->pPrior;
@@ -2094,7 +2094,7 @@ computeLimitRegisters(Parse * pParse, Select * p, int iBreak)
 		   (p->pOffset->flags & EP_Collate) != 0)) {
 			diag_set(ClientError, ER_SQL_UNRECOGNIZED_SYNTAX,
 				 sizeof("COLLATE"), "COLLATE");
-			sql_parser_error(pParse);
+			pParse->is_aborted = true;
 			return;
 		}
 		p->iLimit = iLimit = ++pParse->nMem;
@@ -2236,7 +2236,6 @@ multi_select_coll_seq_r(struct Parse *parser, struct Select *p, int n,
 					   current_coll_id, is_current_forced,
 					   &res_coll_id) != 0) {
 		parser->is_aborted = true;
-		parser->nErr++;
 		return 0;
 	}
 	*is_forced_coll = (is_prior_forced || is_current_forced);
@@ -3115,7 +3114,7 @@ generateOutputSubroutine(struct Parse *parse, struct Select *p,
 		 * of the scan loop.
 		 */
 	case SRT_Mem:{
-			assert(in->nSdst == 1 || parse->nErr > 0);
+			assert(in->nSdst == 1 || parse->is_aborted);
 			testcase(in->nSdst != 1);
 			sqlExprCodeMove(parse, in->iSdst, dest->iSDParm,
 					    1);
@@ -3585,7 +3584,7 @@ multiSelectOrderBy(Parse * pParse,	/* Parsing context */
   *** subqueries ***
   */
 	explainComposite(pParse, p->op, iSub1, iSub2, 0);
-	return pParse->nErr != 0;
+	return pParse->is_aborted;
 }
 #endif
 
@@ -4428,7 +4427,7 @@ sqlIndexedByLookup(Parse * pParse, struct SrcList_item *pFrom)
 		if (idx == NULL) {
 			diag_set(ClientError, ER_NO_SUCH_INDEX_NAME,
 				 zIndexedBy, space->def->name);
-			sql_parser_error(pParse);
+			pParse->is_aborted = true;
 			return SQL_ERROR;
 		}
 		pFrom->pIBIndex = idx->def;
@@ -5054,7 +5053,7 @@ selectExpander(Walker * pWalker, Select * p)
 						diag_set(ClientError,
 							 ER_SQL_SELECT_WILDCARD);
 					}
-					sql_parser_error(pParse);
+					pParse->is_aborted = true;
 				}
 			}
 		}
@@ -5096,7 +5095,7 @@ sqlExprWalkNoop(Walker * NotUsed, Expr * NotUsed2)
  * name resolution is performed.
  *
  * If anything goes wrong, an error message is written into pParse.
- * The calling function can detect the problem by looking at pParse->nErr
+ * The calling function can detect the problem by looking at pParse->is_aborted
  * and/or pParse->db->mallocFailed.
  */
 static void
@@ -5205,10 +5204,10 @@ sqlSelectPrep(Parse * pParse,	/* The parser context */
 	if (p->selFlags & SF_HasTypeInfo)
 		return;
 	sqlSelectExpand(pParse, p);
-	if (pParse->nErr || db->mallocFailed)
+	if (pParse->is_aborted || db->mallocFailed)
 		return;
 	sqlResolveSelectNames(pParse, p, pOuterNC);
-	if (pParse->nErr || db->mallocFailed)
+	if (pParse->is_aborted || db->mallocFailed)
 		return;
 	sqlSelectAddTypeInfo(pParse, p);
 }
@@ -5463,7 +5462,7 @@ sqlSelect(Parse * pParse,		/* The parser context */
 	pParse->iSelectId = pParse->iNextSelectId++;
 
 	db = pParse->db;
-	if (p == 0 || db->mallocFailed || pParse->nErr) {
+	if (p == 0 || db->mallocFailed || pParse->is_aborted) {
 		return 1;
 	}
 	memset(&sAggInfo, 0, sizeof(sAggInfo));
@@ -5498,7 +5497,7 @@ sqlSelect(Parse * pParse,		/* The parser context */
 	memset(&sSort, 0, sizeof(sSort));
 	sSort.pOrderBy = p->pOrderBy;
 	pTabList = p->pSrc;
-	if (pParse->nErr || db->mallocFailed) {
+	if (pParse->is_aborted || db->mallocFailed) {
 		goto select_end;
 	}
 	assert(p->pEList != 0);
@@ -6392,7 +6391,7 @@ sqlSelect(Parse * pParse,		/* The parser context */
 	/* The SELECT has been coded. If there is an error in the Parse structure,
 	 * set the return code to 1. Otherwise 0.
 	 */
-	rc = (pParse->nErr > 0);
+	rc = (pParse->is_aborted);
 
 	/* Control jumps to here if an error is encountered above, or upon
 	 * successful coding of the SELECT.
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index dd21091..b05e67b 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -2665,7 +2665,6 @@ struct Parse {
 	u8 nColCache;		/* Number of entries in aColCache[] */
 	int nRangeReg;		/* Size of the temporary register block */
 	int iRangeReg;		/* First register in temporary register block */
-	int nErr;		/* Number of errors seen */
 	int nTab;		/* Number of previously allocated VDBE cursors */
 	int nMem;		/* Number of memory cells used so far */
 	int nOpAlloc;		/* Number of slots allocated for Vdbe.aOp[] */
@@ -3204,14 +3203,6 @@ int sqlKeywordCode(const unsigned char *, int);
 int sqlRunParser(Parse *, const char *);
 
 /**
- * Increment error counter.
- *
- * @param parse_context Current parsing context.
- */
-void
-sql_parser_error(struct Parse *parse_context);
-
-/**
  * This routine is called after a single SQL statement has been
  * parsed and a VDBE program to execute that statement has been
  * prepared.  This routine puts the finishing touches on the
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index 5c23ec0..be04c17 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -511,7 +511,7 @@ sqlRunParser(Parse * pParse, const char *zSql)
 				diag_set(ClientError, ER_SQL_UNKNOWN_TOKEN,
 					 pParse->sLastToken.n,
 					 pParse->sLastToken.z);
-				sql_parser_error(pParse);
+				pParse->is_aborted = true;
 				break;
 			}
 		} else {
@@ -533,7 +533,7 @@ sqlRunParser(Parse * pParse, const char *zSql)
 		pParse->is_aborted = true;
 	if (pParse->is_aborted && pParse->zErrMsg == 0)
 		pParse->zErrMsg = sqlMPrintf(db, "%s", tarantoolErrorMessage());
-	if (pParse->pVdbe != NULL && pParse->nErr > 0) {
+	if (pParse->pVdbe != NULL && pParse->is_aborted) {
 		sqlVdbeDelete(pParse->pVdbe);
 		pParse->pVdbe = 0;
 	}
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index 5ee0d96..7eacd33 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -155,7 +155,6 @@ sql_trigger_begin(struct Parse *parse, struct Token *name, int tr_tm,
 
 set_tarantool_error_and_cleanup:
 	parse->is_aborted = true;
-	parse->nErr++;
 	goto trigger_cleanup;
 }
 
@@ -169,7 +168,7 @@ sql_trigger_finish(struct Parse *parse, struct TriggerStep *step_list,
 	struct sql *db = parse->db;
 
 	parse->parsed_ast.trigger = NULL;
-	if (NEVER(parse->nErr) || trigger == NULL)
+	if (NEVER(parse->is_aborted) || trigger == NULL)
 		goto cleanup;
 	char *trigger_name = trigger->zName;
 	trigger->step_list = step_list;
@@ -730,11 +729,10 @@ onErrorText(int onError)
 static void
 transferParseError(Parse * pTo, Parse * pFrom)
 {
-	assert(pFrom->zErrMsg == 0 || pFrom->nErr);
-	assert(pTo->zErrMsg == 0 || pTo->nErr);
-	if (pTo->nErr == 0) {
+	assert(pFrom->zErrMsg == 0 || pFrom->is_aborted);
+	assert(pTo->zErrMsg == 0 || pTo->is_aborted);
+	if (!pTo->is_aborted) {
 		pTo->zErrMsg = pFrom->zErrMsg;
-		pTo->nErr = pFrom->nErr;
 		pTo->is_aborted = pFrom->is_aborted;
 	} else {
 		sqlDbFree(pFrom->db, pFrom->zErrMsg);
@@ -924,7 +922,7 @@ vdbe_code_row_trigger_direct(struct Parse *parser, struct sql_trigger *trigger,
 	struct Vdbe *v = sqlGetVdbe(parser);
 
 	TriggerPrg *pPrg = sql_row_trigger(parser, trigger, space, orconf);
-	assert(pPrg != NULL || parser->nErr != 0 ||
+	assert(pPrg != NULL || parser->is_aborted ||
 	       parser->db->mallocFailed != 0);
 
 	/*
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 670e547..05ceeb4 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -116,7 +116,7 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 	int upd_cols_cnt = 0;
 
 	db = pParse->db;
-	if (pParse->nErr || db->mallocFailed) {
+	if (pParse->is_aborted || db->mallocFailed) {
 		goto update_cleanup;
 	}
 	assert(pTabList->nSrc == 1);
@@ -192,7 +192,7 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 		if (j >= (int)def->field_count) {
 			diag_set(ClientError, ER_NO_SUCH_FIELD_NAME,
 				 pChanges->a[i].zName, def->name);
-			sql_parser_error(pParse);
+			pParse->is_aborted = true;
 			goto update_cleanup;
 		}
 	}
diff --git a/src/box/sql/util.c b/src/box/sql/util.c
index 5aa4fda..d9bb2af 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -211,8 +211,8 @@ sqlErrorWithMsg(sql * db, int err_code, const char *zFormat, ...)
 }
 
 /*
- * Add an error to the diagnostics area, increment pParse->nErr
- * and set pParse->is_aborted.
+ * Add an error to the diagnostics area and set
+ * pParse->is_aborted.
  * The following formatting characters are allowed:
  *
  *      %s      Insert a string
@@ -239,17 +239,9 @@ sqlErrorMsg(Parse * pParse, const char *zFormat, ...)
 	va_end(ap);
 	diag_set(ClientError, ER_SQL_PARSER_GENERIC, zMsg);
 	sqlDbFree(db, zMsg);
-	pParse->nErr++;
 	pParse->is_aborted = true;
 }
 
-void
-sql_parser_error(struct Parse *parse_context)
-{
-	parse_context->nErr++;
-	parse_context->is_aborted = true;
-}
-
 /*
  * Convert an SQL-style quoted string into a normal string by removing
  * the quote characters.  The conversion is done in-place.  If the
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index 6c5c61e..cf70e06 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -2799,7 +2799,6 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder,	/* WHERE clause information */
 		struct key_def *key_def = key_def_new(&part, 1);
 		if (key_def == NULL) {
 tnt_error:
-			pWInfo->pParse->nErr++;
 			pWInfo->pParse->is_aborted = true;
 			return SQL_TARANTOOL_ERROR;
 		}
@@ -4458,7 +4457,7 @@ sqlWhereBegin(Parse * pParse,	/* The parser context */
 	    (user_session->sql_flags & SQL_ReverseOrder) != 0) {
 		pWInfo->revMask = ALLBITS;
 	}
-	if (pParse->nErr || NEVER(db->mallocFailed)) {
+	if (pParse->is_aborted || NEVER(db->mallocFailed)) {
 		goto whereBeginError;
 	}
 #ifdef SQL_DEBUG
diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
index 018fd8a..e1c3366 100644
--- a/src/box/sql/wherecode.c
+++ b/src/box/sql/wherecode.c
@@ -1472,7 +1472,7 @@ sqlWhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about the W
 				    sqlWhereBegin(pParse, pOrTab, pOrExpr,
 						      0, 0, wctrlFlags,
 						      iCovCur);
-				assert(pSubWInfo || pParse->nErr
+				assert(pSubWInfo || pParse->is_aborted
 				       || db->mallocFailed);
 				if (pSubWInfo) {
 					WhereLoop *pSubLoop;
-- 
2.7.4

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] [PATCH v4 5/8] sql: remove field zErrMsg from struct Parse
  2019-03-13 17:03 [tarantool-patches] [PATCH v4 0/8] sql: use diag_set() for errors in SQL imeevma
                   ` (3 preceding siblings ...)
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 4/8] sql: remove field nErr from " imeevma
@ 2019-03-13 17:03 ` imeevma
  2019-03-14 22:15   ` [tarantool-patches] " n.pettik
  2019-03-19 13:20   ` Kirill Yukhin
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 6/8] sql: rework three errors of "unsupported" type imeevma
                   ` (2 subsequent siblings)
  7 siblings, 2 replies; 43+ messages in thread
From: imeevma @ 2019-03-13 17:03 UTC (permalink / raw)
  To: korablev; +Cc: tarantool-patches

Hi! Thank you for review! My answers and new version of patch
below. Here won't be diff between patches due to some of its code
were moved to previous patches.

On 3/5/19 12:06 PM, n.pettik wrote:
>
>>>> @@ -146,11 +145,7 @@ sqlPrepare(sql * db,  /* Database handle. */
>>>>   *ppStmt = (sql_stmt *) sParse.pVdbe;
>>>> }
>>>>
>>>> -  if (zErrMsg) {
>>>> -    sqlErrorWithMsg(db, rc, "%s", zErrMsg);
>>>> -  } else {
>>>> -    sqlError(db, rc);
>>>> -  }
>>>> +  sqlError(db, rc);
>>>
>>> sqlError seems to be useless/dead. Please, make a note somewhere
>>> to remove it as follow-up to error-refactoring patch-set.
>>>
>> Ok, made a note.
>
> And where is it? Please, paste a link to it.
I will compile all of notices to this issue and send them a bit later.

>> New version:
>>
>> commit 29c77ea3f8463994b98bcb23653f901cf46e472a
>> Author: Mergen Imeev <imeevma@gmail.com>
>> Date:   Wed Feb 27 09:40:17 2019 +0300
>>
>>    sql: remove field zErrMsg of struct Parse
>>
>>    This field become unused and should be removed.
>>
>>    Part of #3965
>>
>> diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
>> index 42737ff..4359fa7 100644
>> --- a/src/box/sql/prepare.c
>> +++ b/src/box/sql/prepare.c
>> @@ -52,7 +52,6 @@ sqlPrepare(sql * db,  /* Database handle. */
>>         const char **pzTail  /* OUT: End of parsed string */
>>     )
>> {
>> - char *zErrMsg = 0;  /* Error message */
>>  int rc = SQL_OK;  /* Result code */
>>  Parse sParse;   /* Parsing context */
>>  sql_parser_create(&sParse, db);
>> @@ -89,14 +88,14 @@ sqlPrepare(sql * db,  /* Database handle. */
>>    }
>>    zSqlCopy = sqlDbStrNDup(db, zSql, nBytes);
>>    if (zSqlCopy) {
>> -     sqlRunParser(&sParse, zSqlCopy, &zErrMsg);
>> +     sqlRunParser(&sParse, zSqlCopy);
>>      sParse.zTail = &zSql[sParse.zTail - zSqlCopy];
>>      sqlDbFree(db, zSqlCopy);
>>    } else {
>>      sParse.zTail = &zSql[nBytes];
>>    }
>>  } else {
>> -   sqlRunParser(&sParse, zSql, &zErrMsg);
>> +   sqlRunParser(&sParse, zSql);
>>  }
>>  assert(0 == sParse.nQueryLoop);
>>
>> @@ -169,11 +168,7 @@ sqlPrepare(sql * db, /* Database handle. */
>>    *ppStmt = (sql_stmt *) sParse.pVdbe;
>>  }
>>
>> - if (zErrMsg) {
>> -   sqlErrorWithMsg(db, rc, "%s", zErrMsg);
>> - } else {
>> -   sqlError(db, rc);
>> - }
>> + sqlError(db, rc);
>
> As we pointed out, you can remove this call.
Removed.

>>
>>  /* Delete any TriggerPrg structures allocated while parsing this statement. */
>>  while (sParse.pTriggerPrg) {
>> @@ -318,7 +313,6 @@ sql_parser_destroy(Parse *parser)
>>    db->lookaside.bDisable -= parser->disableLookaside;
>>  }
>>  parser->disableLookaside = 0;
>> - sqlDbFree(db, parser->zErrMsg);
>>  switch (parser->parsed_ast_type) {
>>  case AST_TYPE_SELECT:
>>    sql_select_delete(db, parser->parsed_ast.select);
>> diff --git a/src/box/sql/select.c b/src/box/sql/select.c
>> index cfac553..66cbc73 100644
>> --- a/src/box/sql/select.c
>> +++ b/src/box/sql/select.c
>> @@ -5431,9 +5431,7 @@ vdbe_code_raise_on_multiple_rows(struct Parse *parser, int limit_reg, int end_ma
>>  * The results are returned according to the SelectDest structure.
>>  * See comments in sqlInt.h for further information.
>>  *
>> - * This routine returns the number of errors.  If any errors are
>> - * encountered, then an appropriate error message is left in
>> - * pParse->zErrMsg.
>> + * This routine returns the number of errors.
>
> That’s not true:
>
>         rc = pParse->is_aborted;
>
>        /* Control jumps to here if an error is encountered above, or upon
>         * successful coding of the SELECT.
>         */
>  select_end:
>        pParse->iSelectId = iRestoreSelectId;
>
>        /* Identify column names if results of the SELECT are to be output.
>         */
>        if (rc == SQL_OK && pDest->eDest == SRT_Output) {
>               generateColumnNames(pParse, pTabList, pEList);
>        }
>
>        sqlDbFree(db, sAggInfo.aCol);
>        sqlDbFree(db, sAggInfo.aFunc);
> #ifdef SQL_DEBUG
>        SELECTTRACE(1, pParse, p, ("end processing\n"));
>        pParse->nSelectIndent--;
> #endif
> return rc;
>
> So it doesn’t return count of occurred errors.
>
>> diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
>> index 4ca3c2e..1a7248e 100644
>> --- a/src/box/sql/tokenize.c
>> +++ b/src/box/sql/tokenize.c
>> @@ -439,13 +439,10 @@ parser_space_delete(struct sql *db, struct space *space)
>>
>> /*
>>  * Run the parser on the given SQL string.  The parser structure is
>> - * passed in.  An SQL_ status code is returned.  If an error occurs
>> - * then an and attempt is made to write an error message into
>> - * memory obtained from sql_malloc() and to make *pzErrMsg point to that
>> - * error message.
>> + * passed in.  An SQL_ status code is returned.
>
> On the other hand:
>
> return nErr;
>
> So what is the truth?
>
Fixed, rewritten part of the comment.


New version:

commit d0d78b7eb71413f1e5b0cc7bf0c3c83e4aafe55b
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Wed Feb 27 09:40:17 2019 +0300

    sql: remove field zErrMsg from struct Parse
    
    This field become unused and should be removed.
    
    Part of #3965

diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
index 85385ee..512a3dd 100644
--- a/src/box/sql/prepare.c
+++ b/src/box/sql/prepare.c
@@ -311,7 +311,6 @@ sql_parser_destroy(Parse *parser)
 		db->lookaside.bDisable -= parser->disableLookaside;
 	}
 	parser->disableLookaside = 0;
-	sqlDbFree(db, parser->zErrMsg);
 	switch (parser->parsed_ast_type) {
 	case AST_TYPE_SELECT:
 		sql_select_delete(db, parser->parsed_ast.select);
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index e83b898..9a8ea9d 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -5431,12 +5431,11 @@ vdbe_code_raise_on_multiple_rows(struct Parse *parser, int limit_reg, int end_ma
  * The results are returned according to the SelectDest structure.
  * See comments in sqlInt.h for further information.
  *
- * This routine returns the number of errors.  If any errors are
- * encountered, then an appropriate error message is left in
- * pParse->zErrMsg.
- *
  * This routine does NOT free the Select structure passed in.  The
  * calling function needs to do that.
+ *
+ * @retval 0 on success.
+ * @retval != 0 on error.
  */
 int
 sqlSelect(Parse * pParse,		/* The parser context */
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index b05e67b..5ddaca2 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -2653,7 +2653,6 @@ struct fk_constraint_parse {
  */
 struct Parse {
 	sql *db;		/* The main database structure */
-	char *zErrMsg;		/* An error message */
 	Vdbe *pVdbe;		/* An engine for executing database bytecode */
 	u8 colNamesSet;		/* TRUE after OP_ColumnName has been issued to pVdbe */
 	u8 nTempReg;		/* Number of temporary registers in aTempReg[] */
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index be04c17..db06c39 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -531,8 +531,6 @@ sqlRunParser(Parse * pParse, const char *zSql)
 	sqlParserFree(pEngine, sql_free);
 	if (db->mallocFailed)
 		pParse->is_aborted = true;
-	if (pParse->is_aborted && pParse->zErrMsg == 0)
-		pParse->zErrMsg = sqlMPrintf(db, "%s", tarantoolErrorMessage());
 	if (pParse->pVdbe != NULL && pParse->is_aborted) {
 		sqlVdbeDelete(pParse->pVdbe);
 		pParse->pVdbe = 0;
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index 7eacd33..2f1268a 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -721,25 +721,6 @@ onErrorText(int onError)
 }
 #endif
 
-/*
- * Parse context structure pFrom has just been used to create a sub-vdbe
- * (trigger program). If an error has occurred, transfer error information
- * from pFrom to pTo.
- */
-static void
-transferParseError(Parse * pTo, Parse * pFrom)
-{
-	assert(pFrom->zErrMsg == 0 || pFrom->is_aborted);
-	assert(pTo->zErrMsg == 0 || pTo->is_aborted);
-	if (!pTo->is_aborted) {
-		pTo->zErrMsg = pFrom->zErrMsg;
-		pTo->is_aborted = pFrom->is_aborted;
-	} else {
-		sqlDbFree(pFrom->db, pFrom->zErrMsg);
-	}
-	pFrom->zErrMsg = NULL;
-}
-
 /**
  * Create and populate a new TriggerPrg object with a sub-program
  * implementing trigger pTrigger with ON CONFLICT policy orconf.
@@ -848,7 +829,8 @@ sql_row_trigger_program(struct Parse *parser, struct sql_trigger *trigger,
 		VdbeComment((v, "End: %s.%s", trigger->zName,
 			     onErrorText(orconf)));
 
-		transferParseError(parser, pSubParse);
+		if (!parser->is_aborted)
+			parser->is_aborted = pSubParse->is_aborted;
 		if (db->mallocFailed == 0) {
 			pProgram->aOp =
 			    sqlVdbeTakeOpArray(v, &pProgram->nOp,
-- 
2.7.4

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] [PATCH v4 6/8] sql: rework three errors of "unsupported" type
  2019-03-13 17:03 [tarantool-patches] [PATCH v4 0/8] sql: use diag_set() for errors in SQL imeevma
                   ` (4 preceding siblings ...)
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 5/8] sql: remove field zErrMsg " imeevma
@ 2019-03-13 17:03 ` imeevma
  2019-03-14 22:15   ` [tarantool-patches] " n.pettik
  2019-03-19 13:30   ` Kirill Yukhin
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 7/8] sql: rework semantic errors imeevma
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 8/8] sql: remove sqlErrorMsg() imeevma
  7 siblings, 2 replies; 43+ messages in thread
From: imeevma @ 2019-03-13 17:03 UTC (permalink / raw)
  To: korablev; +Cc: tarantool-patches

Three errors of "unsupported" type were reworked in this patch.

Part of #3965
---
 src/box/errcode.h              |  1 +
 src/box/sql/analyze.c          |  7 ++++---
 src/box/sql/select.c           | 10 ++++++----
 test/box/misc.result           |  1 +
 test/sql-tap/analyzeD.test.lua |  2 +-
 test/sql-tap/join.test.lua     |  2 +-
 test/sql-tap/with1.test.lua    |  2 +-
 7 files changed, 15 insertions(+), 10 deletions(-)

diff --git a/src/box/errcode.h b/src/box/errcode.h
index d234d26..7764aa3 100644
--- a/src/box/errcode.h
+++ b/src/box/errcode.h
@@ -239,6 +239,7 @@ struct errcode_record {
 	/*184 */_(ER_SQL_UNRECOGNIZED_SYNTAX,	"Syntax error near '%.*s'") \
 	/*185 */_(ER_SQL_UNKNOWN_TOKEN,		"Syntax error: unrecognized token: '%.*s'") \
 	/*186 */_(ER_SQL_PARSER_GENERIC,	"%s") \
+	/*187 */_(ER_SQL_ANALYZE_ARGUMENT,	"ANALYZE statement argument %s is not a base table") \
 
 /*
  * !IMPORTANT! Please follow instructions at start of the file
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index f95b34b..6ea598c 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -1121,9 +1121,10 @@ sqlAnalyze(Parse * pParse, Token * pName)
 			struct space *sp = space_by_name(z);
 			if (sp != NULL) {
 				if (sp->def->opts.is_view) {
-					sqlErrorMsg(pParse, "VIEW isn't "\
-							"allowed to be "\
-							"analyzed");
+					diag_set(ClientError,
+						 ER_SQL_ANALYZE_ARGUMENT,
+						 sp->def->name);
+					pParse->is_aborted = true;
 				} else {
 					vdbe_emit_analyze_table(pParse, sp);
 				}
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 9a8ea9d..e26b08b 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -407,8 +407,9 @@ sqlJoinType(Parse * pParse, Token * pA, Token * pB, Token * pC)
 		jointype = JT_INNER;
 	} else if ((jointype & JT_OUTER) != 0
 		   && (jointype & (JT_LEFT | JT_RIGHT)) != JT_LEFT) {
-		sqlErrorMsg(pParse,
-				"RIGHT and FULL OUTER JOINs are not currently supported");
+		diag_set(ClientError, ER_UNSUPPORTED, "Tarantool",
+			 "RIGHT and FULL OUTER JOINs");
+		pParse->is_aborted = true;
 		jointype = JT_INNER;
 	}
 	return jointype;
@@ -2465,8 +2466,9 @@ generateWithRecursiveQuery(Parse * pParse,	/* Parsing context */
 	 * the value for the recursive-table. Store the results in the Queue.
 	 */
 	if (p->selFlags & SF_Aggregate) {
-		sqlErrorMsg(pParse,
-				"recursive aggregate queries not supported");
+		diag_set(ClientError, ER_UNSUPPORTED, "Tarantool",
+			 "recursive aggregate queries");
+		pParse->is_aborted = true;
 	} else {
 		p->pPrior = 0;
 		sqlSelect(pParse, p, &destQueue);
diff --git a/test/box/misc.result b/test/box/misc.result
index 9f0b2c7..c350bbd 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -515,6 +515,7 @@ t;
   184: box.error.SQL_UNRECOGNIZED_SYNTAX
   185: box.error.SQL_UNKNOWN_TOKEN
   186: box.error.SQL_PARSER_GENERIC
+  187: box.error.SQL_ANALYZE_ARGUMENT
 ...
 test_run:cmd("setopt delimiter ''");
 ---
diff --git a/test/sql-tap/analyzeD.test.lua b/test/sql-tap/analyzeD.test.lua
index 82ad7ff..1f00e80 100755
--- a/test/sql-tap/analyzeD.test.lua
+++ b/test/sql-tap/analyzeD.test.lua
@@ -177,7 +177,7 @@ test:do_catchsql_test(
 		ANALYZE v;
 	]], {
 		-- <analyzeD-1.12>
-		1, "VIEW isn't allowed to be analyzed"
+		1, "ANALYZE statement argument V is not a base table"
 		-- <analyzeD-1.12>
 	})
 
diff --git a/test/sql-tap/join.test.lua b/test/sql-tap/join.test.lua
index df272a9..da29f77 100755
--- a/test/sql-tap/join.test.lua
+++ b/test/sql-tap/join.test.lua
@@ -480,7 +480,7 @@ test:do_catchsql_test(
         SELECT * FROM t1 NATURAL RIGHT OUTER JOIN t2;
     ]], {
         -- <join-2.3>
-        1, "RIGHT and FULL OUTER JOINs are not currently supported"
+        1, "Tarantool does not support RIGHT and FULL OUTER JOINs"
         -- </join-2.3>
     })
 
diff --git a/test/sql-tap/with1.test.lua b/test/sql-tap/with1.test.lua
index add2345..16c9b12 100755
--- a/test/sql-tap/with1.test.lua
+++ b/test/sql-tap/with1.test.lua
@@ -1065,7 +1065,7 @@ test:do_catchsql_test(16.1, [[
   SELECT * FROM i;
 ]], {
   -- <16.1>
-  1, "recursive aggregate queries not supported"
+  1, "Tarantool does not support recursive aggregate queries"
   -- </16.1>
 })
 
-- 
2.7.4

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] [PATCH v4 7/8] sql: rework semantic errors
  2019-03-13 17:03 [tarantool-patches] [PATCH v4 0/8] sql: use diag_set() for errors in SQL imeevma
                   ` (5 preceding siblings ...)
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 6/8] sql: rework three errors of "unsupported" type imeevma
@ 2019-03-13 17:03 ` imeevma
  2019-03-15 15:49   ` [tarantool-patches] " n.pettik
  2019-03-27  6:48   ` Kirill Yukhin
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 8/8] sql: remove sqlErrorMsg() imeevma
  7 siblings, 2 replies; 43+ messages in thread
From: imeevma @ 2019-03-13 17:03 UTC (permalink / raw)
  To: korablev; +Cc: tarantool-patches

This patch reworks some of SQL semantic errors.

Part of #3965
---
 src/box/errcode.h                                  |   9 +-
 src/box/sql/build.c                                |  99 ++---
 src/box/sql/delete.c                               |   5 +-
 src/box/sql/expr.c                                 |  49 +--
 src/box/sql/insert.c                               |  12 +-
 src/box/sql/parse.y                                |  23 +-
 src/box/sql/resolve.c                              | 122 +++---
 src/box/sql/select.c                               |  21 +-
 src/box/sql/sqlInt.h                               |   3 +-
 src/box/sql/update.c                               |   5 +-
 src/box/sql/where.c                                |   4 +-
 test/box/alter.result                              |   4 +-
 test/box/misc.result                               |   7 +
 test/sql-tap/autoinc.test.lua                      |   2 +-
 test/sql-tap/check.test.lua                        |   2 +-
 test/sql-tap/colname.test.lua                      |   2 +-
 test/sql-tap/default.test.lua                      |  10 +-
 test/sql-tap/engine.cfg                            |   3 +
 test/sql-tap/fkey2.test.lua                        |   4 +-
 test/sql-tap/func3.test.lua                        |   6 +-
 test/sql-tap/gh-2549-many-columns.test.lua         |   2 +-
 test/sql-tap/gh2548-select-compound-limit.test.lua |   2 +-
 test/sql-tap/hexlit.test.lua                       |   4 +-
 test/sql-tap/insert1.test.lua                      |   2 +-
 test/sql-tap/join.test.lua                         |  18 +-
 test/sql-tap/join3.test.lua                        |   2 +-
 test/sql-tap/misc1.test.lua                        |   6 +-
 test/sql-tap/select7.test.lua                      |   2 +-
 test/sql-tap/sql-errors.test.lua                   | 419 +++++++++++++++++++++
 test/sql-tap/unique.test.lua                       |   2 +-
 test/sql-tap/view.test.lua                         |  20 +-
 test/sql-tap/where7.test.lua                       |   2 +-
 test/sql-tap/whereG.test.lua                       |   6 +-
 test/sql/gh-2347-max-int-literals.result           |   6 +-
 test/sql/gh-2929-primary-key.result                |   8 +-
 test/sql/integer-overflow.result                   |   9 +-
 test/sql/iproto.result                             |   5 +-
 test/sql/transition.result                         |   3 +-
 test/sql/view.result                               |   2 +-
 39 files changed, 689 insertions(+), 223 deletions(-)
 create mode 100755 test/sql-tap/sql-errors.test.lua

diff --git a/src/box/errcode.h b/src/box/errcode.h
index 7764aa3..a0d8f4ae 100644
--- a/src/box/errcode.h
+++ b/src/box/errcode.h
@@ -122,7 +122,7 @@ struct errcode_record {
 	/* 67 */_(ER_REPLICA_ID_IS_RESERVED,	"Can't initialize replica id with a reserved value %u") \
 	/* 68 */_(ER_INVALID_ORDER,		"Invalid LSN order for instance %u: previous LSN = %llu, new lsn = %llu") \
 	/* 69 */_(ER_MISSING_REQUEST_FIELD,	"Missing mandatory field '%s' in request") \
-	/* 70 */_(ER_IDENTIFIER,		"Invalid identifier '%s' (expected printable symbols only)") \
+	/* 70 */_(ER_IDENTIFIER,		"Invalid identifier '%s' (expected printable symbols only or it is too long)") \
 	/* 71 */_(ER_DROP_FUNCTION,		"Can't drop function %u: %s") \
 	/* 72 */_(ER_ITERATOR_TYPE,		"Unknown iterator type '%s'") \
 	/* 73 */_(ER_REPLICA_MAX,		"Replica count limit reached: %u") \
@@ -240,6 +240,13 @@ struct errcode_record {
 	/*185 */_(ER_SQL_UNKNOWN_TOKEN,		"Syntax error: unrecognized token: '%.*s'") \
 	/*186 */_(ER_SQL_PARSER_GENERIC,	"%s") \
 	/*187 */_(ER_SQL_ANALYZE_ARGUMENT,	"ANALYZE statement argument %s is not a base table") \
+	/*188 */_(ER_SQL_COLUMN_COUNT_MAX,	"Failed to create space '%s': space column count %d exceeds the limit (%d)") \
+	/*189 */_(ER_HEX_LITERAL_MAX,		"Hex literal %s%s length %d exceeds the supported limit (%d)") \
+	/*190 */_(ER_INT_LITERAL_MAX,		"Integer literal %s%s exceeds the supported range %lld - %lld") \
+	/*191 */_(ER_SQL_PARSER_LIMIT,		"%s%.*s %d exceeds the limit (%d)") \
+	/*192 */_(ER_INDEX_DEF,			"%s prohibited in an index definition") \
+	/*193 */_(ER_CHECK_CONSTRAINT_DEF,	"%s prohibited in a CHECK constraint definition") \
+	/*194 */_(ER_PRIMARY_KEY_DEF,		"Expressions are prohibited in a primary key definition") \
 
 /*
  * !IMPORTANT! Please follow instructions at start of the file
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 0c06555..26434b1 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -267,14 +267,14 @@ int
 sqlCheckIdentifierName(Parse *pParse, char *zName)
 {
 	ssize_t len = strlen(zName);
-
-	if (len > BOX_NAME_MAX || identifier_check(zName, len) != 0) {
-		sqlErrorMsg(pParse,
-				"identifier name is invalid: %s",
-				zName);
-		return SQL_ERROR;
+	if (len <= BOX_NAME_MAX && identifier_check(zName, len) == 0)
+		return SQL_OK;
+	if (len > BOX_NAME_MAX) {
+		diag_set(ClientError, ER_IDENTIFIER,
+			 tt_cstr(zName, BOX_INVALID_NAME_MAX));
 	}
-	return SQL_OK;
+	pParse->is_aborted = true;
+	return SQL_ERROR;
 }
 
 /**
@@ -432,7 +432,9 @@ sqlAddColumn(Parse * pParse, Token * pName, struct type_def *type_def)
 
 #if SQL_MAX_COLUMN
 	if ((int)def->field_count + 1 > db->aLimit[SQL_LIMIT_COLUMN]) {
-		sqlErrorMsg(pParse, "too many columns on %s", def->name);
+		diag_set(ClientError, ER_SQL_COLUMN_COUNT_MAX, def->name,
+			 def->field_count + 1, db->aLimit[SQL_LIMIT_COLUMN]);
+		pParse->is_aborted = true;
 		return;
 	}
 #endif
@@ -521,9 +523,9 @@ sqlAddDefaultValue(Parse * pParse, ExprSpan * pSpan)
 		struct space_def *def = pParse->new_space->def;
 		if (!sqlExprIsConstantOrFunction
 		    (pSpan->pExpr, db->init.busy)) {
-			sqlErrorMsg(pParse,
-					"default value of column [%s] is not constant",
-					def->fields[def->field_count - 1].name);
+			diag_set(ClientError, ER_CREATE_SPACE, def->name,
+				 "default value of column is not constant");
+			pParse->is_aborted = true;
 		} else {
 			assert(def != NULL);
 			struct field_def *field =
@@ -588,9 +590,9 @@ sqlAddPrimaryKey(Parse * pParse,	/* Parsing context */
 	if (space == NULL)
 		goto primary_key_exit;
 	if (sql_space_primary_key(space) != NULL) {
-		sqlErrorMsg(pParse,
-				"table \"%s\" has more than one primary key",
-				space->def->name);
+		diag_set(ClientError, ER_CREATE_SPACE, space->def->name,
+			 "too many primary keys");
+		pParse->is_aborted = true;
 		goto primary_key_exit;
 	}
 	if (pList == NULL) {
@@ -603,8 +605,8 @@ sqlAddPrimaryKey(Parse * pParse,	/* Parsing context */
 			    sqlExprSkipCollate(pList->a[i].pExpr);
 			assert(pCExpr != 0);
 			if (pCExpr->op != TK_ID) {
-				sqlErrorMsg(pParse, "expressions prohibited"
-							" in PRIMARY KEY");
+				diag_set(ClientError, ER_PRIMARY_KEY_DEF);
+				pParse->is_aborted = true;
 				goto primary_key_exit;
 			}
 			const char *name = pCExpr->u.zToken;
@@ -636,8 +638,10 @@ sqlAddPrimaryKey(Parse * pParse,	/* Parsing context */
 		if (db->mallocFailed)
 			goto primary_key_exit;
 	} else if (autoInc) {
-		sqlErrorMsg(pParse, "AUTOINCREMENT is only allowed on an "
-				"INTEGER PRIMARY KEY or INT PRIMARY KEY");
+		diag_set(ClientError, ER_CREATE_SPACE, space->def->name,
+			 "AUTOINCREMENT is only allowed on an INTEGER PRIMARY "\
+			 "KEY or INT PRIMARY KEY");
+		pParse->is_aborted = true;
 		goto primary_key_exit;
 	} else {
 		sql_create_index(pParse, 0, 0, pList, 0, sortOrder, false,
@@ -1144,9 +1148,10 @@ sqlEndTable(Parse * pParse,	/* Parse context */
 
 	if (!new_space->def->opts.is_view) {
 		if (sql_space_primary_key(new_space) == NULL) {
-			sqlErrorMsg(pParse,
-					"PRIMARY KEY missing on table %s",
-					new_space->def->name);
+			diag_set(ClientError, ER_CREATE_SPACE,
+				 new_space->def->name,
+				 "PRIMARY KEY missing");
+			pParse->is_aborted = true;
 			goto cleanup;
 		}
 	}
@@ -1264,8 +1269,10 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
 {
 	struct sql *db = parse_context->db;
 	if (parse_context->nVar > 0) {
-		sqlErrorMsg(parse_context,
-				"parameters are not allowed in views");
+		diag_set(ClientError, ER_CREATE_SPACE,
+			 sqlNameFromToken(db, name),
+			 "parameters are not allowed in views");
+		parse_context->is_aborted = true;
 		goto create_view_fail;
 	}
 	sqlStartTable(parse_context, name, if_exists);
@@ -1279,10 +1286,10 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
 		goto create_view_fail;
 	if (aliases != NULL) {
 		if ((int)select_res_space->def->field_count != aliases->nExpr) {
-			sqlErrorMsg(parse_context, "expected %d columns "\
-					"for '%s' but got %d", aliases->nExpr,
-					space->def->name,
-					select_res_space->def->field_count);
+			diag_set(ClientError, ER_CREATE_SPACE, space->def->name,
+				 "number of aliases doesn't match provided "\
+				 "columns");
+			parse_context->is_aborted = true;
 			goto create_view_fail;
 		}
 		sqlColumnsFromExprList(parse_context, aliases, space->def);
@@ -1604,13 +1611,15 @@ sql_drop_table(struct Parse *parse_context, struct SrcList *table_name_list,
 	 * and DROP VIEW is not used on a table.
 	 */
 	if (is_view && !space->def->opts.is_view) {
-		sqlErrorMsg(parse_context, "use DROP TABLE to delete table %s",
-				space_name);
+		diag_set(ClientError, ER_DROP_SPACE, space_name,
+			 "use DROP TABLE");
+		parse_context->is_aborted = true;
 		goto exit_drop_table;
 	}
 	if (!is_view && space->def->opts.is_view) {
-		sqlErrorMsg(parse_context, "use DROP VIEW to delete view %s",
-				space_name);
+		diag_set(ClientError, ER_DROP_SPACE, space_name,
+			 "use DROP VIEW");
+		parse_context->is_aborted = true;
 		goto exit_drop_table;
 	}
 	/*
@@ -1753,12 +1762,6 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child,
 			diag_set(ClientError, ER_NO_SUCH_SPACE, parent_name);;
 			goto tnt_error;
 		}
-	} else {
-		if (parent_space->def->opts.is_view) {
-			sqlErrorMsg(parse_context,
-					"referenced table can't be view");
-			goto exit_create_fk;
-		}
 	}
 	if (constraint == NULL && !is_alter) {
 		if (parse_context->constraintName.n == 0) {
@@ -1775,6 +1778,11 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child,
 	}
 	if (constraint_name == NULL)
 		goto exit_create_fk;
+	if (!is_self_referenced && parent_space->def->opts.is_view) {
+		diag_set(ClientError, ER_CREATE_FK_CONSTRAINT, constraint_name,
+			"referenced space can't be VIEW");
+		goto tnt_error;
+	}
 	const char *error_msg = "number of columns in foreign key does not "
 				"match the number of columns in the primary "
 				"index of referenced table";
@@ -2144,7 +2152,10 @@ sql_create_index(struct Parse *parse, struct Token *token,
 	struct space_def *def = space->def;
 
 	if (def->opts.is_view) {
-		sqlErrorMsg(parse, "views can not be indexed");
+		diag_set(ClientError, ER_MODIFY_INDEX,
+			 sqlNameFromToken(db, token), def->name,
+			 "views can not be indexed");
+		parse->is_aborted = true;
 		goto exit_create_index;
 	}
 	/*
@@ -2245,7 +2256,12 @@ sql_create_index(struct Parse *parse, struct Token *token,
 		assert(col_list->nExpr == 1);
 		sqlExprListSetSortOrder(col_list, sort_order);
 	} else {
-		sqlExprListCheckLength(parse, col_list, "index");
+		if (col_list->nExpr > db->aLimit[SQL_LIMIT_COLUMN]) {
+			diag_set(ClientError, ER_SQL_PARSER_LIMIT,
+				 "The number of columns in index", 0, "",
+				 col_list->nExpr, db->aLimit[SQL_LIMIT_COLUMN]);
+			parse->is_aborted = true;
+		}
 	}
 
 	index = (struct index *) region_alloc(&parse->region, sizeof(*index));
@@ -2942,11 +2958,8 @@ sqlSavepoint(Parse * pParse, int op, Token * pName)
 			return;
 		}
 		if (op == SAVEPOINT_BEGIN &&
-			sqlCheckIdentifierName(pParse, zName)
-				!= SQL_OK) {
-			sqlErrorMsg(pParse, "bad savepoint name");
+		    sqlCheckIdentifierName(pParse, zName) != SQL_OK)
 			return;
-		}
 		sqlVdbeAddOp4(v, OP_Savepoint, op, 0, 0, zName, P4_DYNAMIC);
 	}
 }
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index 87d4ed4..e8a5f0d 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -157,8 +157,9 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 			goto delete_from_cleanup;
 
 		if (trigger_list == NULL) {
-			sqlErrorMsg(parse, "cannot modify %s because it is a"
-					" view", space->def->name);
+			diag_set(ClientError, ER_ALTER_SPACE, space->def->name,
+				 "it is a view");
+			parse->is_aborted = true;
 			goto delete_from_cleanup;
 		}
 	}
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 4193596..de06ee0 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -750,15 +750,14 @@ codeVectorCompare(Parse * pParse,	/* Code generator context */
 int
 sqlExprCheckHeight(Parse * pParse, int nHeight)
 {
-	int rc = SQL_OK;
 	int mxHeight = pParse->db->aLimit[SQL_LIMIT_EXPR_DEPTH];
 	if (nHeight > mxHeight) {
-		sqlErrorMsg(pParse,
-				"Expression tree is too large (maximum depth %d)",
-				mxHeight);
-		rc = SQL_ERROR;
+		diag_set(ClientError, ER_SQL_PARSER_LIMIT, "Number of nodes "\
+			 "in expression tree", 0, "", nHeight, mxHeight);
+		pParse->is_aborted = true;
+		return SQL_ERROR;
 	}
-	return rc;
+	return SQL_OK;
 }
 
 /* The following three functions, heightOfExpr(), heightOfExprList()
@@ -1197,9 +1196,9 @@ sqlExprAssignVarNumber(Parse * pParse, Expr * pExpr, u32 n)
 			testcase(i == SQL_BIND_PARAMETER_MAX - 1);
 			testcase(i == SQL_BIND_PARAMETER_MAX);
 			if (!is_ok || i < 1 || i > SQL_BIND_PARAMETER_MAX) {
-				sqlErrorMsg(pParse,
-						"variable number must be between $1 and $%d",
-						SQL_BIND_PARAMETER_MAX);
+				diag_set(ClientError, ER_SQL_BIND_PARAMETER_MAX,
+					 SQL_BIND_PARAMETER_MAX);
+				pParse->is_aborted = true;
 				return;
 			}
 			if (x > pParse->nVar) {
@@ -1227,7 +1226,9 @@ sqlExprAssignVarNumber(Parse * pParse, Expr * pExpr, u32 n)
 	}
 	pExpr->iColumn = x;
 	if (x > SQL_BIND_PARAMETER_MAX) {
-		sqlErrorMsg(pParse, "too many SQL variables");
+		diag_set(ClientError, ER_SQL_BIND_PARAMETER_MAX,
+			 SQL_BIND_PARAMETER_MAX);
+		pParse->is_aborted = true;
 	}
 }
 
@@ -1888,22 +1889,6 @@ sqlExprListSetSpan(Parse * pParse,	/* Parsing context */
 }
 
 /*
- * If the expression list pEList contains more than iLimit elements,
- * leave an error message in pParse.
- */
-void
-sqlExprListCheckLength(Parse * pParse,
-			   ExprList * pEList, const char *zObject)
-{
-	int mx = pParse->db->aLimit[SQL_LIMIT_COLUMN];
-	testcase(pEList && pEList->nExpr == mx);
-	testcase(pEList && pEList->nExpr == mx + 1);
-	if (pEList && pEList->nExpr > mx) {
-		sqlErrorMsg(pParse, "too many columns in %s", zObject);
-	}
-}
-
-/*
  * Delete an entire expression list.
  */
 static SQL_NOINLINE void
@@ -3336,15 +3321,15 @@ expr_code_int(struct Parse *parse, struct Expr *expr, bool is_neg,
 		int c = sql_dec_or_hex_to_i64(z, &value);
 		if (c == 1 || (c == 2 && !is_neg) ||
 		    (is_neg && value == SMALLEST_INT64)) {
+			const char *sign = is_neg ? "-" : "";
 			if (sql_strnicmp(z, "0x", 2) == 0) {
-				sqlErrorMsg(parse,
-						"hex literal too big: %s%s",
-						is_neg ? "-" : "", z);
+				diag_set(ClientError, ER_HEX_LITERAL_MAX, sign,
+					 z, strlen(z) - 2, 16);
 			} else {
-				sqlErrorMsg(parse,
-						"oversized integer: %s%s",
-						is_neg ? "-" : "", z);
+				diag_set(ClientError, ER_INT_LITERAL_MAX, sign,
+					 z, INT64_MIN, INT64_MAX);
 			}
+			parse->is_aborted = true;
 		} else {
 			if (is_neg)
 				value = c == 2 ? SMALLEST_INT64 : -value;
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 6f7f020..263df4d 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -322,8 +322,9 @@ sqlInsert(Parse * pParse,	/* Parser context */
 
 	/* Cannot insert into a read-only table. */
 	if (is_view && tmask == 0) {
-		sqlErrorMsg(pParse, "cannot modify %s because it is a view",
-				space_def->name);
+		diag_set(ClientError, ER_ALTER_SPACE, space->def->name,
+			 "it is a view");
+		pParse->is_aborted = true;
 		goto insert_cleanup;
 	}
 
@@ -388,9 +389,10 @@ sqlInsert(Parse * pParse,	/* Parser context */
 				}
 			}
 			if (j >= (int) space_def->field_count) {
-				sqlErrorMsg(pParse,
-						"table %S has no column named %s",
-						pTabList, 0, pColumn->a[i].zName);
+				diag_set(ClientError, ER_NO_SUCH_FIELD_NAME,
+					 pColumn->a[i].zName,
+					 pTabList->a[0].zName);
+				pParse->is_aborted = true;
 				goto insert_cleanup;
 			}
 			if (bit_test(used_columns, j)) {
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index 3e86a7f..acf947b 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -410,9 +410,10 @@ cmd ::= select(X).  {
         (mxSelect = pParse->db->aLimit[SQL_LIMIT_COMPOUND_SELECT])>0 &&
         cnt>mxSelect
       ){
-        sqlErrorMsg(pParse, "Too many UNION or EXCEPT or INTERSECT "
-                        "operations (limit %d is set)",
-                        pParse->db->aLimit[SQL_LIMIT_COMPOUND_SELECT]);
+         diag_set(ClientError, ER_SQL_PARSER_LIMIT, "The number of UNION or "\
+                  "EXCEPT or INTERSECT operations", 0, "", cnt,
+                  pParse->db->aLimit[SQL_LIMIT_COMPOUND_SELECT]);
+         pParse->is_aborted = true;
       }
     }
   }
@@ -742,7 +743,11 @@ cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
         where_opt(W).  {
   sqlWithPush(pParse, C, 1);
   sqlSrcListIndexedBy(pParse, X, &I);
-  sqlExprListCheckLength(pParse,Y,"set list");
+  if (Y != NULL && Y->nExpr > pParse->db->aLimit[SQL_LIMIT_COLUMN]) {
+    diag_set(ClientError, ER_SQL_PARSER_LIMIT, "The number of columns in set "\
+             "list", 0, "", Y->nExpr, pParse->db->aLimit[SQL_LIMIT_COLUMN]);
+    pParse->is_aborted = true;
+  }
   sqlSubProgramsRemaining = SQL_MAX_COMPILING_TRIGGERS;
   /* Instruct SQL to initate Tarantool's transaction.  */
   pParse->initiateTTrans = true;
@@ -930,7 +935,10 @@ expr(A) ::= CAST(X) LP expr(E) AS typedef(T) RP(Y). {
 %endif  SQL_OMIT_CAST
 expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP(E). {
   if( Y && Y->nExpr>pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG] ){
-    sqlErrorMsg(pParse, "too many arguments on function %T", &X);
+    diag_set(ClientError, ER_SQL_PARSER_LIMIT, "Number of "\
+             "arguments to function ", X.n, X.z, Y->nExpr,
+             pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]);
+    pParse->is_aborted = true;
   }
   A.pExpr = sqlExprFunction(pParse, Y, &X);
   spanSet(&A,&X,&E);
@@ -946,7 +954,10 @@ expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP(E). {
 type_func(A) ::= CHAR(A) .
 expr(A) ::= type_func(X) LP distinct(D) exprlist(Y) RP(E). {
   if( Y && Y->nExpr>pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG] ){
-    sqlErrorMsg(pParse, "too many arguments on function %T", &X);
+    diag_set(ClientError, ER_SQL_PARSER_LIMIT, "Number of "\
+             "arguments to function ", X.n, X.z, Y->nExpr,
+             pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]);
+    pParse->is_aborted = true;
   }
   A.pExpr = sqlExprFunction(pParse, Y, &X);
   spanSet(&A,&X,&E);
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 94bb0af..9171d05 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -510,30 +510,6 @@ sqlCreateColumnExpr(sql * db, SrcList * pSrc, int iSrc, int iCol)
 }
 
 /*
- * Report an error that an expression is not valid for some set of
- * pNC->ncFlags values determined by validMask.
- */
-static void
-notValid(Parse * pParse,	/* Leave error message here */
-	 NameContext * pNC,	/* The name context */
-	 const char *zMsg,	/* Type of error */
-	 int validMask		/* Set of contexts for which prohibited */
-    )
-{
-	assert((validMask & ~(NC_IsCheck | NC_IdxExpr)) == 0);
-	if ((pNC->ncFlags & validMask) != 0) {
-		const char *zIn;
-		if (pNC->ncFlags & NC_IdxExpr)
-			zIn = "index expressions";
-		else if (pNC->ncFlags & NC_IsCheck)
-			zIn = "CHECK constraints";
-		else
-			unreachable();
-		sqlErrorMsg(pParse, "%s prohibited in %s", zMsg, zIn);
-	}
-}
-
-/*
  * Expression p should encode a floating point value between 1.0 and 0.0.
  * Return 1024 times this value.  Or return -1 if p is not a floating point
  * value between 1.0 and 0.0.
@@ -605,7 +581,10 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 			Expr *pRight;
 
 			/* if( pSrcList==0 ) break; */
-			notValid(pParse, pNC, "the \".\" operator", NC_IdxExpr);
+			if (pNC->ncFlags & NC_IdxExpr) {
+				diag_set(ClientError, ER_INDEX_DEF, "'.' operator is");
+				pParse->is_aborted = true;
+			}
 			pRight = pExpr->pRight;
 			if (pRight->op == TK_ID) {
 				zTable = pExpr->pLeft->u.zToken;
@@ -646,6 +625,9 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 			} else {
 				is_agg = pDef->xFinalize != 0;
 				pExpr->type = pDef->ret_type;
+				const char *err_msg =
+					"second argument to likelihood() must "\
+					"be a constant between 0.0 and 1.0";
 				if (pDef->funcFlags & SQL_FUNC_UNLIKELY) {
 					ExprSetProperty(pExpr,
 							EP_Unlikely | EP_Skip);
@@ -654,9 +636,11 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 						    exprProbability(pList->a[1].
 								    pExpr);
 						if (pExpr->iTable < 0) {
-							sqlErrorMsg(pParse,
-									"second argument to likelihood() must be a "
-									"constant between 0.0 and 1.0");
+							diag_set(ClientError,
+								 ER_ILLEGAL_PARAMS,
+								 err_msg);
+							pParse->is_aborted =
+								true;
 							pNC->nErr++;
 						}
 					} else {
@@ -690,9 +674,12 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 					 * that might change over time cannot be used
 					 * in an index.
 					 */
-					notValid(pParse, pNC,
-						 "non-deterministic functions",
-						 NC_IdxExpr);
+					if (pNC->ncFlags & NC_IdxExpr) {
+						diag_set(ClientError, ER_INDEX_DEF,
+							 "Non-deterministic functions "\
+							 "are");
+						pParse->is_aborted = true;
+					}
 				}
 			}
 			if (is_agg && (pNC->ncFlags & NC_AllowAgg) == 0) {
@@ -754,8 +741,16 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 			testcase(pExpr->op == TK_IN);
 			if (ExprHasProperty(pExpr, EP_xIsSelect)) {
 				int nRef = pNC->nRef;
-				notValid(pParse, pNC, "subqueries",
-					 NC_IsCheck | NC_IdxExpr);
+				if (pNC->ncFlags & NC_IdxExpr) {
+					diag_set(ClientError, ER_INDEX_DEF,
+						 "Subqueries are");
+					pParse->is_aborted = true;
+				} else if (pNC->ncFlags & NC_IsCheck) {
+					diag_set(ClientError,
+						 ER_CHECK_CONSTRAINT_DEF,
+						 "Subqueries are");
+					pParse->is_aborted = true;
+				}
 				sqlWalkSelect(pWalker, pExpr->x.pSelect);
 				assert(pNC->nRef >= nRef);
 				if (nRef != pNC->nRef) {
@@ -766,8 +761,12 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 			break;
 		}
 	case TK_VARIABLE:{
-			notValid(pParse, pNC, "parameters",
-				 NC_IsCheck | NC_IdxExpr);
+			assert((pNC->ncFlags & NC_IsCheck) == 0);
+			if (pNC->ncFlags & NC_IdxExpr) {
+				diag_set(ClientError, ER_INDEX_DEF,
+					 "Parameter markers are");
+				pParse->is_aborted = true;
+			}
 			break;
 		}
 	case TK_BETWEEN:
@@ -952,7 +951,10 @@ resolveCompoundOrderBy(Parse * pParse,	/* Parsing context.  Leave error messages
 	db = pParse->db;
 #if SQL_MAX_COLUMN
 	if (pOrderBy->nExpr > db->aLimit[SQL_LIMIT_COLUMN]) {
-		sqlErrorMsg(pParse, "too many terms in ORDER BY clause");
+		diag_set(ClientError, ER_SQL_PARSER_LIMIT,
+			 "The number of terms in ORDER BY clause", 0, "",
+			 pOrderBy->nExpr, db->aLimit[SQL_LIMIT_COLUMN]);
+		pParse->is_aborted = true;
 		return 1;
 	}
 #endif
@@ -1042,27 +1044,34 @@ resolveCompoundOrderBy(Parse * pParse,	/* Parsing context.  Leave error messages
  * field) then convert that term into a copy of the corresponding result set
  * column.
  *
- * If any errors are detected, add an error message to pParse and
- * return non-zero.  Return zero if no errors are seen.
+ * @param pParse Parsing context.
+ * @param pSelect The SELECT statement containing the clause.
+ * @param pOrderBy The ORDER BY or GROUP BY clause to be
+ *                 processed.
+ * @param is_order_by True if pOrderBy is ORDER BY, false if
+ *                    pOrderBy is GROUP BY
+ * @retval 0 On success, not 0 elsewhere.
  */
 int
-sqlResolveOrderGroupBy(Parse * pParse,	/* Parsing context.  Leave error messages here */
-			   Select * pSelect,	/* The SELECT statement containing the clause */
-			   ExprList * pOrderBy,	/* The ORDER BY or GROUP BY clause to be processed */
-			   const char *zType	/* "ORDER" or "GROUP" */
-    )
+sqlResolveOrderGroupBy(Parse *pParse, Select *pSelect, ExprList *pOrderBy,
+		       bool is_order_by)
 {
 	int i;
 	sql *db = pParse->db;
 	ExprList *pEList;
 	struct ExprList_item *pItem;
+	const char *zType = is_order_by ? "ORDER" : "GROUP";
 
 	if (pOrderBy == 0 || pParse->db->mallocFailed)
 		return 0;
 #if SQL_MAX_COLUMN
 	if (pOrderBy->nExpr > db->aLimit[SQL_LIMIT_COLUMN]) {
-		sqlErrorMsg(pParse, "too many terms in %s BY clause",
-				zType);
+		const char *err_msg =
+			is_order_by ? "The number of terms in ORDER BY clause" :
+				      "The number of terms in GROUP BY clause";
+		diag_set(ClientError, ER_SQL_PARSER_LIMIT, err_msg, 0, "",
+			 pOrderBy->nExpr, db->aLimit[SQL_LIMIT_COLUMN]);
+		pParse->is_aborted = true;
 		return 1;
 	}
 #endif
@@ -1096,22 +1105,23 @@ sqlResolveOrderGroupBy(Parse * pParse,	/* Parsing context.  Leave error messages
  * result-set expression.  Otherwise, the expression is resolved in
  * the usual way - using sqlResolveExprNames().
  *
- * This routine returns the number of errors.  If errors occur, then
- * an appropriate error message might be left in pParse.  (OOM errors
- * excepted.)
+ * @param pNC The name context of the SELECT statement.
+ * @param pSelect The SELECT statement containing the clause.
+ * @param pOrderBy An ORDER BY or GROUP BY clause to resolve.
+ * @param is_order_by True if pOrderBy is ORDER BY, false if
+ *                    pOrderBy is GROUP BY
+ * @retval 0 On success, not 0 elsewhere.
  */
 static int
-resolveOrderGroupBy(NameContext * pNC,	/* The name context of the SELECT statement */
-		    Select * pSelect,	/* The SELECT statement holding pOrderBy */
-		    ExprList * pOrderBy,	/* An ORDER BY or GROUP BY clause to resolve */
-		    const char *zType	/* Either "ORDER" or "GROUP", as appropriate */
-    )
+resolveOrderGroupBy(NameContext *pNC, Select *pSelect, ExprList *pOrderBy,
+		    bool is_order_by)
 {
 	int i, j;		/* Loop counters */
 	int iCol;		/* Column number */
 	struct ExprList_item *pItem;	/* A term of the ORDER BY clause */
 	Parse *pParse;		/* Parsing context */
 	int nResult;		/* Number of terms in the result set */
+	const char *zType = is_order_by ? "ORDER" : "GROUP";
 
 	if (pOrderBy == 0)
 		return 0;
@@ -1158,7 +1168,7 @@ resolveOrderGroupBy(NameContext * pNC,	/* The name context of the SELECT stateme
 			}
 		}
 	}
-	return sqlResolveOrderGroupBy(pParse, pSelect, pOrderBy, zType);
+	return sqlResolveOrderGroupBy(pParse, pSelect, pOrderBy, is_order_by);
 }
 
 /*
@@ -1401,7 +1411,7 @@ resolveSelectStep(Walker * pWalker, Select * p)
 		 * resolve those symbols on the incorrect ORDER BY for consistency.
 		 */
 		if (isCompound <= nCompound	/* Defer right-most ORDER BY of a compound */
-		    && resolveOrderGroupBy(&sNC, p, p->pOrderBy, "ORDER")
+		    && resolveOrderGroupBy(&sNC, p, p->pOrderBy, true)
 		    ) {
 			return WRC_Abort;
 		}
@@ -1415,7 +1425,7 @@ resolveSelectStep(Walker * pWalker, Select * p)
 		if (pGroupBy) {
 			struct ExprList_item *pItem;
 
-			if (resolveOrderGroupBy(&sNC, p, pGroupBy, "GROUP")
+			if (resolveOrderGroupBy(&sNC, p, pGroupBy, false)
 			    || db->mallocFailed) {
 				return WRC_Abort;
 			}
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index e26b08b..f2cad56 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -3396,11 +3396,9 @@ multiSelectOrderBy(Parse * pParse,	/* Parsing context */
 	 */
 	p->pPrior = 0;
 	pPrior->pNext = 0;
-	sqlResolveOrderGroupBy(pParse, p, p->pOrderBy, "ORDER");
-	if (pPrior->pPrior == 0) {
-		sqlResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy,
-					   "ORDER");
-	}
+	sqlResolveOrderGroupBy(pParse, p, p->pOrderBy, true);
+	if (pPrior->pPrior == 0)
+		sqlResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, true);
 
 	/* Compute the limit registers */
 	computeLimitRegisters(pParse, p, labelEnd);
@@ -5064,7 +5062,10 @@ selectExpander(Walker * pWalker, Select * p)
 	}
 #if SQL_MAX_COLUMN
 	if (p->pEList && p->pEList->nExpr > db->aLimit[SQL_LIMIT_COLUMN]) {
-		sqlErrorMsg(pParse, "too many columns in result set");
+		diag_set(ClientError, ER_SQL_PARSER_LIMIT, "The number of "\
+			 "columns in result set", 0, "", p->pEList->nExpr,
+			 db->aLimit[SQL_LIMIT_COLUMN]);
+		pParse->is_aborted = true;
 		return WRC_Abort;
 	}
 #endif
@@ -5524,10 +5525,10 @@ sqlSelect(Parse * pParse,		/* The parser context */
 		 * columns in the SELECT on the RHS
 		 */
 		if ((int)space->def->field_count != pSub->pEList->nExpr) {
-			sqlErrorMsg(pParse,
-					"expected %d columns for '%s' but got %d",
-					space->def->field_count, space->def->name,
-					pSub->pEList->nExpr);
+			diag_set(ClientError, ER_CREATE_SPACE, space->def->name,
+				 "number of aliases doesn't match provided "\
+				 "columns");
+			pParse->is_aborted = true;
 			goto select_end;
 		}
 
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 5ddaca2..1e49d5b 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -4424,7 +4424,7 @@ int sqlMatchSpanName(const char *, const char *, const char *);
 int sqlResolveExprNames(NameContext *, Expr *);
 int sqlResolveExprListNames(NameContext *, ExprList *);
 void sqlResolveSelectNames(Parse *, Select *, NameContext *);
-int sqlResolveOrderGroupBy(Parse *, Select *, ExprList *, const char *);
+int sqlResolveOrderGroupBy(Parse *, Select *, ExprList *, bool);
 
 /**
  * Generate code for default value.
@@ -4632,7 +4632,6 @@ sql_int64 sqlStmtCurrentTime(sql_context *);
 int sqlVdbeParameterIndex(Vdbe *, const char *, int);
 int sqlTransferBindings(sql_stmt *, sql_stmt *);
 int sqlReprepare(Vdbe *);
-void sqlExprListCheckLength(Parse *, ExprList *, const char *);
 
 /**
  * This function verifies that two collations (to be more precise
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 05ceeb4..bc0ab66 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -139,8 +139,9 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 		goto update_cleanup;
 	}
 	if (is_view && tmask == 0) {
-		sqlErrorMsg(pParse, "cannot modify %s because it is a view",
-				space->def->name);
+		diag_set(ClientError, ER_ALTER_SPACE, space->def->name,
+			 "it is a view");
+		pParse->is_aborted = true;
 		goto update_cleanup;
 	}
 
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index cf70e06..33885d0 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -4286,7 +4286,9 @@ sqlWhereBegin(Parse * pParse,	/* The parser context */
 	 */
 	testcase(pTabList->nSrc == BMS);
 	if (pTabList->nSrc > BMS) {
-		sqlErrorMsg(pParse, "at most %d tables in a join", BMS);
+		diag_set(ClientError, ER_SQL_PARSER_LIMIT, "The number of "\
+			 "tables in a join", 0, "", pTabList->nSrc, BMS);
+		pParse->is_aborted = true;
 		return 0;
 	}
 
diff --git a/test/box/alter.result b/test/box/alter.result
index 9a1086e..37bc51c 100644
--- a/test/box/alter.result
+++ b/test/box/alter.result
@@ -367,7 +367,7 @@ s:drop()
 ...
 box.schema.space.create('')
 ---
-- error: Invalid identifier '' (expected printable symbols only)
+- error: Invalid identifier '' (expected printable symbols only or it is too long)
 ...
 -- valid identifiers
 box.schema.space.create('_Abcde'):drop()
@@ -438,7 +438,7 @@ i:drop()
 ...
 space:create_index('')
 ---
-- error: Invalid identifier '' (expected printable symbols only)
+- error: Invalid identifier '' (expected printable symbols only or it is too long)
 ...
 space:drop()
 ---
diff --git a/test/box/misc.result b/test/box/misc.result
index c350bbd..871bfa1 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -516,6 +516,13 @@ t;
   185: box.error.SQL_UNKNOWN_TOKEN
   186: box.error.SQL_PARSER_GENERIC
   187: box.error.SQL_ANALYZE_ARGUMENT
+  188: box.error.SQL_COLUMN_COUNT_MAX
+  189: box.error.HEX_LITERAL_MAX
+  190: box.error.INT_LITERAL_MAX
+  191: box.error.SQL_PARSER_LIMIT
+  192: box.error.INDEX_DEF
+  193: box.error.CHECK_CONSTRAINT_DEF
+  194: box.error.PRIMARY_KEY_DEF
 ...
 test_run:cmd("setopt delimiter ''");
 ---
diff --git a/test/sql-tap/autoinc.test.lua b/test/sql-tap/autoinc.test.lua
index dc2f60e..7abb679 100755
--- a/test/sql-tap/autoinc.test.lua
+++ b/test/sql-tap/autoinc.test.lua
@@ -561,7 +561,7 @@ test:do_catchsql_test(
         CREATE TABLE t8(x TEXT PRIMARY KEY AUTOINCREMENT);
     ]], {
         -- <autoinc-7.2>
-        1, "AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY or INT PRIMARY KEY"
+        1, "Failed to create space 'T8': AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY or INT PRIMARY KEY"
         -- </autoinc-7.2>
     })
 
diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
index 0d8bf15..dd2de92 100755
--- a/test/sql-tap/check.test.lua
+++ b/test/sql-tap/check.test.lua
@@ -319,7 +319,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <check-3.1>
-        1, "Failed to create space 'T3': subqueries prohibited in CHECK constraints"
+        1, "Failed to create space 'T3': Subqueries are prohibited in a CHECK constraint definition"
         -- </check-3.1>
     })
 
diff --git a/test/sql-tap/colname.test.lua b/test/sql-tap/colname.test.lua
index 29fdf13..77c4280 100755
--- a/test/sql-tap/colname.test.lua
+++ b/test/sql-tap/colname.test.lua
@@ -637,7 +637,7 @@ test:do_test(
 test:do_catchsql_test(
     "colname-11.1",
     [[ create table t1(a INT, b INT, c INT, primary key('A'))]],
-    {1, "expressions prohibited in PRIMARY KEY"})
+    {1, "Expressions are prohibited in a primary key definition"})
 
 test:do_catchsql_test(
     "colname-11.2",
diff --git a/test/sql-tap/default.test.lua b/test/sql-tap/default.test.lua
index 67151b0..fbe285b 100755
--- a/test/sql-tap/default.test.lua
+++ b/test/sql-tap/default.test.lua
@@ -66,7 +66,7 @@ test:do_catchsql_test(
 	);
 	]], {
 	-- <default-1.3>
-	1, "default value of column [Y] is not constant"
+	1, "Failed to create space 'T3': default value of column is not constant"
 	-- </default-1.3>
 })
 
@@ -173,7 +173,7 @@ test:do_catchsql_test(
 	);
 	]], {
 	-- <default-4.2>
-	1, "default value of column [B] is not constant"
+	1, "Failed to create space 'T2': default value of column is not constant"
 	-- </default-4.2>
 })
 
@@ -187,7 +187,7 @@ test:do_catchsql_test(
 	);
 	]], {
 	-- <default-4.3>
-	1, "default value of column [B] is not constant"
+	1, "Failed to create space 'T2': default value of column is not constant"
 	-- </default-4.3>
 })
 
@@ -201,7 +201,7 @@ test:do_catchsql_test(
 	);
 	]], {
 	-- <default-4.4>
-	1, "default value of column [B] is not constant"
+	1, "Failed to create space 'T2': default value of column is not constant"
 	-- </default-4.4>
 })
 
@@ -214,7 +214,7 @@ test:do_catchsql_test(
         CREATE TABLE t6(id INTEGER PRIMARY KEY, b TEXT DEFAULT(id));
     ]], {
     -- <default-5.1>
-    1, "default value of column [B] is not constant"
+    1, "Failed to create space 'T6': default value of column is not constant"
     -- </default-5.1>
 })
 
diff --git a/test/sql-tap/engine.cfg b/test/sql-tap/engine.cfg
index 413dd3f..cc56592 100644
--- a/test/sql-tap/engine.cfg
+++ b/test/sql-tap/engine.cfg
@@ -11,6 +11,9 @@
     "sort.test.lua": {
         "memtx": {"engine": "memtx"}
     },
+    "sql-errors.test.lua": {
+        "memtx": {"engine": "memtx"}
+    },
     "*": {
         "memtx": {"engine": "memtx"},
         "vinyl": {"engine": "vinyl"}
diff --git a/test/sql-tap/fkey2.test.lua b/test/sql-tap/fkey2.test.lua
index d347e5a..678b857 100755
--- a/test/sql-tap/fkey2.test.lua
+++ b/test/sql-tap/fkey2.test.lua
@@ -729,7 +729,7 @@ test:do_catchsql_test(
         CREATE TABLE c(x  INT PRIMARY KEY REFERENCES v(y));
     ]], {
         -- <fkey2-7.2>
-        1, "referenced table can't be view"
+        1, "Failed to create foreign key constraint 'FK_CONSTRAINT_1_C': referenced space can't be VIEW"
         -- </fkey2-7.2>
     })
 
@@ -1157,7 +1157,7 @@ test:do_catchsql_test(
         CREATE TABLE t1(x INT PRIMARY KEY REFERENCES v);
     ]], {
         -- <fkey2-10.20>
-        1, "referenced table can't be view"
+        1, "Failed to create foreign key constraint 'FK_CONSTRAINT_1_T1': referenced space can't be VIEW"
         -- </fkey2-10.20>
     })
 
diff --git a/test/sql-tap/func3.test.lua b/test/sql-tap/func3.test.lua
index 4ea9a5c..6d6411c 100755
--- a/test/sql-tap/func3.test.lua
+++ b/test/sql-tap/func3.test.lua
@@ -104,7 +104,7 @@ test:do_catchsql_test(
         SELECT likelihood(123, 1.000001);
     ]], {
         -- <func3-5.8>
-        1, "second argument to likelihood() must be a constant between 0.0 and 1.0"
+        1, "Illegal parameters, second argument to likelihood() must be a constant between 0.0 and 1.0"
         -- </func3-5.8>
     })
 
@@ -114,7 +114,7 @@ test:do_catchsql_test(
         SELECT likelihood(123, -0.000001);
     ]], {
         -- <func3-5.9>
-        1, "second argument to likelihood() must be a constant between 0.0 and 1.0"
+        1, "Illegal parameters, second argument to likelihood() must be a constant between 0.0 and 1.0"
         -- </func3-5.9>
     })
 
@@ -124,7 +124,7 @@ test:do_catchsql_test(
         SELECT likelihood(123, 0.5+0.3);
     ]], {
         -- <func3-5.10>
-        1, "second argument to likelihood() must be a constant between 0.0 and 1.0"
+        1, "Illegal parameters, second argument to likelihood() must be a constant between 0.0 and 1.0"
         -- </func3-5.10>
     })
 
diff --git a/test/sql-tap/gh-2549-many-columns.test.lua b/test/sql-tap/gh-2549-many-columns.test.lua
index 3de4d67..ed8c9d9 100755
--- a/test/sql-tap/gh-2549-many-columns.test.lua
+++ b/test/sql-tap/gh-2549-many-columns.test.lua
@@ -35,7 +35,7 @@ test:do_catchsql_test(
 	"columns-1.2",
 	fail_statement, {
 		-- <columns-1.2>
-		1, "too many columns on T2"
+		1, "Failed to create space 'T2': space column count 2001 exceeds the limit (2000)"
 		-- <columns-1.2>
 	})
 
diff --git a/test/sql-tap/gh2548-select-compound-limit.test.lua b/test/sql-tap/gh2548-select-compound-limit.test.lua
index 5494a66..e8c8d95 100755
--- a/test/sql-tap/gh2548-select-compound-limit.test.lua
+++ b/test/sql-tap/gh2548-select-compound-limit.test.lua
@@ -58,7 +58,7 @@ test:do_catchsql_test(
     "gh2548-select-compound-limit-2",
     select_string_last, {
         -- <gh2548-select-compound-limit-2>
-        1, "Too many UNION or EXCEPT or INTERSECT operations (limit 30 is set)"
+        1, "The number of UNION or EXCEPT or INTERSECT operations 31 exceeds the limit (30)"
         -- </gh2548-select-compound-limit-2>
     })
 
diff --git a/test/sql-tap/hexlit.test.lua b/test/sql-tap/hexlit.test.lua
index 158eda7..288d823 100755
--- a/test/sql-tap/hexlit.test.lua
+++ b/test/sql-tap/hexlit.test.lua
@@ -107,7 +107,7 @@ test:do_catchsql_test(
         SELECT 0x10000000000000000;
     ]], {
         -- <hexlist-400>
-        1, "hex literal too big: 0x10000000000000000"
+        1, "Hex literal 0x10000000000000000 length 17 exceeds the supported limit (16)"
         -- </hexlist-400>
     })
 
@@ -119,7 +119,7 @@ test:do_catchsql_test(
         INSERT INTO t1 VALUES(1+0x10000000000000000);
     ]], {
         -- <hexlist-410>
-        1, "hex literal too big: 0x10000000000000000"
+        1, "Hex literal 0x10000000000000000 length 17 exceeds the supported limit (16)"
         -- </hexlist-410>
     })
 
diff --git a/test/sql-tap/insert1.test.lua b/test/sql-tap/insert1.test.lua
index 363fa8a..031fd22 100755
--- a/test/sql-tap/insert1.test.lua
+++ b/test/sql-tap/insert1.test.lua
@@ -77,7 +77,7 @@ test:do_catchsql_test("insert-1.4", [[
   INSERT INTO test1(one,four) VALUES(1,2)
 ]], {
   -- <insert-1.4>
-  1, "table TEST1 has no column named FOUR"
+  1, "Field 'FOUR' was not found in the space 'TEST1' format"
   -- </insert-1.4>
 })
 
diff --git a/test/sql-tap/join.test.lua b/test/sql-tap/join.test.lua
index da29f77..ef60609 100755
--- a/test/sql-tap/join.test.lua
+++ b/test/sql-tap/join.test.lua
@@ -1067,11 +1067,11 @@ end
 jointest("join-12.2", 30, {0, {1}})
 jointest("join-12.3", 63, {0, {1}})
 jointest("join-12.4", 64, {0, {1}})
-jointest("join-12.5", 65, {1, 'at most 64 tables in a join'})
-jointest("join-12.6", 66, {1, 'at most 64 tables in a join'})
-jointest("join-12.7", 127, {1, 'at most 64 tables in a join'})
-jointest("join-12.8", 128, {1, 'at most 64 tables in a join'})
-jointest("join-12.9", 1000, {1, 'at most 64 tables in a join'})
+jointest("join-12.5", 65, {1, 'The number of tables in a join 65 exceeds the limit (64)'})
+jointest("join-12.6", 66, {1, 'The number of tables in a join 66 exceeds the limit (64)'})
+jointest("join-12.7", 127, {1, 'The number of tables in a join 127 exceeds the limit (64)'})
+jointest("join-12.8", 128, {1, 'The number of tables in a join 128 exceeds the limit (64)'})
+jointest("join-12.9", 1000, {1, 'The number of tables in a join 1000 exceeds the limit (64)'})
 -- If sql is built with sql_MEMDEBUG, then the huge number of realloc()
 -- calls made by the following test cases are too time consuming to run.
 -- Without sql_MEMDEBUG, realloc() is fast enough that these are not
@@ -1079,10 +1079,10 @@ jointest("join-12.9", 1000, {1, 'at most 64 tables in a join'})
 --if X(0, "X!capable", [["pragma&&compileoption_diags"]]) then
 --    if X(703, "X!cmd", [=[["expr","[lsearch [db eval {PRAGMA compile_options}] MEMDEBUG]<0"]]=])
 -- then
-jointest("join-12.10", 65534, {1, 'at most 64 tables in a join'})
-jointest("join-12.11", 65535, {1, 'at most 64 tables in a join'})
-jointest("join-12.12", 65536, {1, 'at most 64 tables in a join'})
-jointest("join-12.13", 65537, {1, 'at most 64 tables in a join'})
+jointest("join-12.10", 65534, {1, 'The number of tables in a join 65534 exceeds the limit (64)'})
+jointest("join-12.11", 65535, {1, 'The number of tables in a join 65535 exceeds the limit (64)'})
+jointest("join-12.12", 65536, {1, 'The number of tables in a join 65536 exceeds the limit (64)'})
+jointest("join-12.13", 65537, {1, 'The number of tables in a join 65537 exceeds the limit (64)'})
 --    end
 --end
 
diff --git a/test/sql-tap/join3.test.lua b/test/sql-tap/join3.test.lua
index 6b822de..876b312 100755
--- a/test/sql-tap/join3.test.lua
+++ b/test/sql-tap/join3.test.lua
@@ -85,7 +85,7 @@ test:do_test(
         return test:catchsql(sql)
     end, {
         -- <join3-3.1>
-        1, "at most "..bitmask_size.." tables in a join"
+        1, "The number of tables in a join " .. bitmask_size + 1 .. " exceeds the limit (".. bitmask_size ..")"
         -- </join3-3.1>
     })
 
diff --git a/test/sql-tap/misc1.test.lua b/test/sql-tap/misc1.test.lua
index 1b288da..ffef2d4 100755
--- a/test/sql-tap/misc1.test.lua
+++ b/test/sql-tap/misc1.test.lua
@@ -348,7 +348,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <misc1-7.1>
-        1, [[table "ERROR1" has more than one primary key]]
+        1, [[Failed to create space 'ERROR1': too many primary keys]]
         -- </misc1-7.1>
     })
 
@@ -361,7 +361,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <misc1-7.2>
-        1, [[table "ERROR1" has more than one primary key]]
+        1, [[Failed to create space 'ERROR1': too many primary keys]]
         -- </misc1-7.2>
     })
 
@@ -897,7 +897,7 @@ test:do_catchsql_test(
         CREATE TABLE test2(a text primary key, b text, primary key(a,b));
     ]], {
         -- <misc1-16.3>
-        1, [[table "TEST2" has more than one primary key]]
+        1, [[Failed to create space 'TEST2': too many primary keys]]
         -- </misc1-16.3>
     })
 
diff --git a/test/sql-tap/select7.test.lua b/test/sql-tap/select7.test.lua
index 4029c20..7037d20 100755
--- a/test/sql-tap/select7.test.lua
+++ b/test/sql-tap/select7.test.lua
@@ -179,7 +179,7 @@ test:do_catchsql_test(
     "select7-6.2",
     sql, {
         -- <select7-6.2>
-        1, "Too many UNION or EXCEPT or INTERSECT operations (limit 30 is set)"
+        1, "The number of UNION or EXCEPT or INTERSECT operations 33 exceeds the limit (30)"
         -- </select7-6.2>
     })
 
diff --git a/test/sql-tap/sql-errors.test.lua b/test/sql-tap/sql-errors.test.lua
new file mode 100755
index 0000000..4a92af6
--- /dev/null
+++ b/test/sql-tap/sql-errors.test.lua
@@ -0,0 +1,419 @@
+#!/usr/bin/env tarantool
+test = require("sqltester")
+test:plan(36)
+
+test:execsql([[
+	CREATE TABLE t0 (i INT PRIMARY KEY);
+	CREATE VIEW v0 AS SELECT * FROM t0;
+]])
+format = {}
+for i = 1, 2001 do format[i] = {name = 'A' .. i, type = 'unsigned'} end
+s0 = box.schema.space.create('S0', {format = format})
+i0 = s0:create_index('I0')
+
+test:do_catchsql_test(
+	"sql-errors-1.1",
+	[[
+		ANALYZE v0;
+	]], {
+		-- <sql-errors-1.1>
+		1,"ANALYZE statement argument V0 is not a base table"
+		-- </sql-errors-1.1>
+	})
+
+create_statement = 'CREATE TABLE t2 (i INT PRIMARY KEY'
+for i = 1, 2001 do
+	create_statement = create_statement .. ', s' .. i .. ' INT'
+end
+create_statement = create_statement .. ');'
+
+test:do_catchsql_test(
+	"sql-errors-1.2",
+	create_statement,
+	{
+		-- <sql-errors-1.2>
+		1,"Failed to create space 'T2': space column count 2001 exceeds the limit (2000)"
+		-- </sql-errors-1.2>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.3",
+	[[
+		CREATE TABLE t3 (i INT PRIMARY KEY, a INT DEFAULT(MAX(i, 1)));
+	]], {
+		-- <sql-errors-1.3>
+		1,"Failed to create space 'T3': default value of column is not constant"
+		-- </sql-errors-1.3>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.4",
+	[[
+		CREATE TABLE t4 (i INT PRIMARY KEY, a INT PRIMARY KEY);
+	]], {
+		-- <sql-errors-1.4>
+		1,"Failed to create space 'T4': too many primary keys"
+		-- </sql-errors-1.4>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.5",
+	[[
+		CREATE TABLE t5 (i TEXT PRIMARY KEY AUTOINCREMENT);
+	]], {
+		-- <sql-errors-1.5>
+		1,"Failed to create space 'T5': AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY or INT PRIMARY KEY"
+		-- </sql-errors-1.5>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.6",
+	[[
+		CREATE TABLE t6 (i INT);
+	]], {
+		-- <sql-errors-1.6>
+		1,"Failed to create space 'T6': PRIMARY KEY missing"
+		-- </sql-errors-1.6>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.7",
+	[[
+		CREATE VIEW v7(a,b) AS SELECT * FROM t0;
+	]], {
+		-- <sql-errors-1.7>
+		1,"Failed to create space 'V7': number of aliases doesn't match provided columns"
+		-- </sql-errors-1.7>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.8",
+	[[
+		DROP VIEW t0;
+	]], {
+		-- <sql-errors-1.8>
+		1,"Can't drop space 'T0': use DROP TABLE"
+		-- </sql-errors-1.8>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.9",
+	[[
+		DROP TABLE v0;
+	]], {
+		-- <sql-errors-1.9>
+		1,"Can't drop space 'V0': use DROP VIEW"
+		-- </sql-errors-1.9>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.10",
+	[[
+		CREATE TABLE t10(i INT PRIMARY KEY REFERENCES v0);
+	]], {
+		-- <sql-errors-1.10>
+		1,"Failed to create foreign key constraint 'FK_CONSTRAINT_1_T10': referenced space can't be VIEW"
+		-- </sql-errors-1.10>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.11",
+	[[
+		CREATE VIEW v11 AS SELECT * FROM t0 WHERE i = ?;
+	]], {
+		-- <sql-errors-1.11>
+		1,"Failed to create space 'V11': parameters are not allowed in views"
+		-- </sql-errors-1.11>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.12",
+	[[
+		CREATE INDEX i12 ON v0(i);
+	]], {
+		-- <sql-errors-1.12>
+		1,"Can't create or modify index 'I12' in space 'V0': views can not be indexed"
+		-- </sql-errors-1.12>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.13",
+	[[
+		SELECT 9223372036854775808;
+	]], {
+		-- <sql-errors-1.13>
+		1,"Integer literal 9223372036854775808 exceeds the supported range -9223372036854775808 - 9223372036854775807"
+		-- </sql-errors-1.13>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.14",
+	[[
+		SELECT 0x10000000000000000;
+	]], {
+		-- <sql-errors-1.14>
+		1,"Hex literal 0x10000000000000000 length 17 exceeds the supported limit (16)"
+		-- </sql-errors-1.14>
+	})
+
+select_statement = 'SELECT i FROM t0 WHERE i = 0'
+for i = 1, 200 do
+	select_statement = select_statement .. ' OR i = ' .. i
+end
+select_statement = select_statement .. ';'
+
+test:do_catchsql_test(
+	"sql-errors-1.15",
+	select_statement,
+	{
+		-- <sql-errors-1.15>
+		1,"Number of nodes in expression tree 201 exceeds the limit (200)"
+		-- </sql-errors-1.15>
+	})
+
+select_statement = 'SELECT CHAR(1'
+for i = 1, 127 do
+	select_statement = select_statement .. ', ' .. i
+end
+select_statement = select_statement .. ');'
+
+test:do_catchsql_test(
+	"sql-errors-1.16",
+	select_statement,
+	{
+		-- <sql-errors-1.16>
+		1,"Number of arguments to function CHAR 128 exceeds the limit (127)"
+		-- </sql-errors-1.16>
+	})
+
+select_statement = 'SELECT MAX(1'
+for i = 1, 127 do
+	select_statement = select_statement .. ', ' .. i
+end
+select_statement = select_statement .. ');'
+
+test:do_catchsql_test(
+	"sql-errors-1.17",
+	select_statement,
+	{
+		-- <sql-errors-1.17>
+		1,"Number of arguments to function MAX 128 exceeds the limit (127)"
+		-- </sql-errors-1.17>
+	})
+
+select_statement = 'SELECT 0'
+for i = 1, 30 do
+	select_statement = select_statement .. ' UNION ALL SELECT ' .. i
+end
+select_statement = select_statement .. ';'
+
+test:do_catchsql_test(
+	"sql-errors-1.18",
+	select_statement,
+	{
+		-- <sql-errors-1.18>
+		1,"The number of UNION or EXCEPT or INTERSECT operations 31 exceeds the limit (30)"
+		-- </sql-errors-1.18>
+	})
+
+select_statement = 'SELECT 0'
+for i = 1, 2000 do
+	select_statement = select_statement .. ', ' .. i
+end
+select_statement = select_statement .. ';'
+
+test:do_catchsql_test(
+	"sql-errors-1.19",
+	select_statement,
+	{
+		-- <sql-errors-1.19>
+		1,"The number of columns in result set 2001 exceeds the limit (2000)"
+		-- </sql-errors-1.19>
+	})
+
+select_statement = 'SELECT * FROM t0'
+for i = 1, 64 do
+	select_statement = select_statement .. ', t0 as t' .. i
+end
+select_statement = select_statement .. ';'
+
+test:do_catchsql_test(
+	"sql-errors-1.20",
+	select_statement,
+	{
+		-- <sql-errors-1.20>
+		1,"The number of tables in a join 65 exceeds the limit (64)"
+		-- </sql-errors-1.20>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.21",
+	[[
+		SELECT $65001;
+	]], {
+		-- <sql-errors-1.21>
+		1,"SQL bind parameter limit reached: 65000"
+		-- </sql-errors-1.21>
+	})
+
+select_statement = 'SELECT '..string.rep('?, ', box.schema.SQL_BIND_PARAMETER_MAX)..'?;'
+
+test:do_catchsql_test(
+	"sql-errors-1.22",
+	select_statement,
+	{
+		-- <sql-errors-1.22>
+		1,"SQL bind parameter limit reached: 65000"
+		-- </sql-errors-1.22>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.23",
+	[[
+		INSERT INTO v0 VALUES (2);
+	]], {
+		-- <sql-errors-1.23>
+		1,"Can't modify space 'V0': it is a view"
+		-- </sql-errors-1.23>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.24",
+	[[
+		UPDATE v0 SET i = 2 WHERE i = 1;
+	]], {
+		-- <sql-errors-1.24>
+		1,"Can't modify space 'V0': it is a view"
+		-- </sql-errors-1.24>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.25",
+	[[
+		DELETE FROM v0;
+	]], {
+		-- <sql-errors-1.25>
+		1,"Can't modify space 'V0': it is a view"
+		-- </sql-errors-1.25>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.26",
+	[[
+		CREATE TABLE t26 (i INT, PRIMARY KEY('i'));
+	]], {
+		-- <sql-errors-1.26>
+		1,"Expressions are prohibited in a primary key definition"
+		-- </sql-errors-1.26>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.27",
+	[[
+		CREATE TABLE t27 (i INT PRIMARY KEY, CHECK(i < (SELECT * FROM t0)));
+	]], {
+		-- <sql-errors-1.27>
+		1,"Failed to create space 'T27': Subqueries are prohibited in a CHECK constraint definition"
+		-- </sql-errors-1.27>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.28",
+	[[
+		CREATE INDEX i28 ON t0(t0.i);
+	]], {
+		-- <sql-errors-1.28>
+		1,"'.' operator is prohibited in an index definition"
+		-- </sql-errors-1.28>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.29",
+	[[
+		CREATE INDEX i29 ON t0($1);
+	]], {
+		-- <sql-errors-1.29>
+		1,"Parameter markers are prohibited in an index definition"
+		-- </sql-errors-1.29>
+	})
+
+create_index_statement = 'CREATE INDEX i30 on t0(i'..string.rep(', i', 2000)..');'
+
+test:do_catchsql_test(
+	"sql-errors-1.30",
+	create_index_statement,
+	{
+		-- <sql-errors-1.30>
+		1,"The number of columns in index 2001 exceeds the limit (2000)"
+		-- </sql-errors-1.30>
+	})
+
+update_statement = 'UPDATE s0 SET a1 = a1 + 1'
+for i = 2, 2001 do
+	update_statement = update_statement .. ', a' .. i .. ' = a' .. i .. ' + 1'
+end
+update_statement = update_statement .. ';'
+
+test:do_catchsql_test(
+	"sql-errors-1.31",
+	update_statement,
+	{
+		-- <sql-errors-1.31>
+		1,"The number of columns in set list 2001 exceeds the limit (2000)"
+		-- </sql-errors-1.31>
+	})
+
+select_statement = 'SELECT * FROM (SELECT 1 UNION ALL SELECT 1 ORDER BY 1'..string.rep(', 1', 2000)..')'
+
+test:do_catchsql_test(
+	"sql-errors-1.32",
+	select_statement,
+	{
+		-- <sql-errors-1.32>
+		1,"The number of terms in ORDER BY clause 2001 exceeds the limit (2000)"
+		-- </sql-errors-1.32>
+	})
+
+select_statement = 'SELECT 1 ORDER BY 1'..string.rep(', 1', 2000)
+
+test:do_catchsql_test(
+	"sql-errors-1.33",
+	select_statement,
+	{
+		-- <sql-errors-1.33>
+		1,"The number of terms in ORDER BY clause 2001 exceeds the limit (2000)"
+		-- </sql-errors-1.33>
+	})
+
+select_statement = 'SELECT 1 as '..string.rep('x', 65001)
+
+test:do_catchsql_test(
+	"sql-errors-1.34",
+	select_statement,
+	{
+		-- <sql-errors-1.34>
+		1,"Invalid identifier 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' (expected printable symbols only or it is too long)"
+		-- </sql-errors-1.34>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.35",
+	[[
+		SELECT 1 as "";
+	]], {
+		-- <sql-errors-1.35>
+		1,"Invalid identifier '' (expected printable symbols only or it is too long)"
+		-- </sql-errors-1.35>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.36",
+	[[
+		SELECT likelihood(1, 2);
+	]], {
+		-- <sql-errors-1.36>
+		1,"Illegal parameters, second argument to likelihood() must be a constant between 0.0 and 1.0"
+		-- </sql-errors-1.36>
+	})
+
+test:finish_test()
diff --git a/test/sql-tap/unique.test.lua b/test/sql-tap/unique.test.lua
index 56ac74a..79bede8 100755
--- a/test/sql-tap/unique.test.lua
+++ b/test/sql-tap/unique.test.lua
@@ -36,7 +36,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <unique-1.1>
-        1, [[table "T1" has more than one primary key]]
+        1, [[Failed to create space 'T1': too many primary keys]]
         -- </unique-1.1>
     })
 
diff --git a/test/sql-tap/view.test.lua b/test/sql-tap/view.test.lua
index 72fdab8..e1affb6 100755
--- a/test/sql-tap/view.test.lua
+++ b/test/sql-tap/view.test.lua
@@ -189,7 +189,7 @@ test:do_catchsql_test(
         INSERT INTO v2 VALUES(1,2,3,4);
     ]], {
         -- <view-2.2>
-        1, "cannot modify V2 because it is a view"
+        1, "Can't modify space 'V2': it is a view"
         -- </view-2.2>
     })
 
@@ -199,7 +199,7 @@ test:do_catchsql_test(
         UPDATE v2 SET a=10 WHERE a=5;
     ]], {
         -- <view-2.3>
-        1, "cannot modify V2 because it is a view"
+        1, "Can't modify space 'V2': it is a view"
         -- </view-2.3>
     })
 
@@ -209,7 +209,7 @@ test:do_catchsql_test(
         DELETE FROM v2;
     ]], {
         -- <view-2.4>
-        1, "cannot modify V2 because it is a view"
+        1, "Can't modify space 'V2': it is a view"
         -- </view-2.4>
     })
 
@@ -302,7 +302,7 @@ test:do_catchsql_test(
     [[
         CREATE VIEW v1err(x,y) AS SELECT a, b+c, c-b FROM t1;
         SELECT * FROM v1err;
-    ]], {1, "expected 2 columns for 'V1ERR' but got 3"})
+    ]], {1, "Failed to create space 'V1ERR': number of aliases doesn't match provided columns"})
 
 test:do_catchsql_test(
     "view-3.3.5.2",
@@ -310,7 +310,7 @@ test:do_catchsql_test(
         DROP VIEW IF EXISTS v1err;
         CREATE VIEW v1err(w,x,y,z) AS SELECT a, b+c, c-b FROM t1;
         SELECT * FROM v1err;
-    ]], {1, "expected 4 columns for 'V1ERR' but got 3"})
+    ]], {1, "Failed to create space 'V1ERR': number of aliases doesn't match provided columns"})
 
 -- #MUST_WORK_TEST no query solution
 -- # ifcapable compound {
@@ -332,7 +332,7 @@ test:do_catchsql_test(
         DROP VIEW t1;
     ]], {
         -- <view-4.1>
-        1, "use DROP TABLE to delete table T1"
+        1, "Can't drop space 'T1': use DROP TABLE"
         -- </view-4.1>
     })
 
@@ -352,7 +352,7 @@ test:do_catchsql_test(
         DROP TABLE v1;
     ]], {
         -- <view-4.3>
-        1, "use DROP VIEW to delete view V1"
+        1, "Can't drop space 'V1': use DROP VIEW"
         -- </view-4.3>
     })
 
@@ -372,7 +372,7 @@ test:do_catchsql_test(
         CREATE INDEX i1v1 ON v1(xyz);
     ]], {
         -- <view-4.5>
-        1, "views can not be indexed"
+        1, "Can't create or modify index 'I1V1' in space 'V1': views can not be indexed"
         -- </view-4.5>
     })
 
@@ -842,7 +842,7 @@ test:do_catchsql_test(
         CREATE VIEW v12 AS SELECT a FROM t1 WHERE b=?
     ]], {
         -- <view-12.1>
-        1, "parameters are not allowed in views"
+        1, "Failed to create space 'V12': parameters are not allowed in views"
         -- </view-12.1>
     })
 
@@ -852,7 +852,7 @@ test:do_catchsql_test(
         CREATE VIEW v12(x) AS SELECT a FROM t1 WHERE b=?
     ]], {
         -- <view-12.2>
-        1, "parameters are not allowed in views"
+        1, "Failed to create space 'V12': parameters are not allowed in views"
         -- </view-12.2>
     })
 
diff --git a/test/sql-tap/where7.test.lua b/test/sql-tap/where7.test.lua
index 2e6f116..ecd0d24 100755
--- a/test/sql-tap/where7.test.lua
+++ b/test/sql-tap/where7.test.lua
@@ -325,7 +325,7 @@ test:do_test(
         end
         return test:catchsql(sql)
     end, {
-        1, "Expression tree is too large (maximum depth 200)"
+        1, "Number of nodes in expression tree 201 exceeds the limit (200)"
     })
 
 test:do_test(
diff --git a/test/sql-tap/whereG.test.lua b/test/sql-tap/whereG.test.lua
index 155c906..39424d4 100755
--- a/test/sql-tap/whereG.test.lua
+++ b/test/sql-tap/whereG.test.lua
@@ -193,7 +193,7 @@ test:do_catchsql_test(
            AND album.aid=track.aid;
     ]], {
         -- <whereG-2.1>
-        1, "second argument to likelihood() must be a constant between 0.0 and 1.0"
+        1, "Illegal parameters, second argument to likelihood() must be a constant between 0.0 and 1.0"
         -- </whereG-2.1>
     })
 
@@ -207,7 +207,7 @@ test:do_catchsql_test(
            AND album.aid=track.aid;
     ]], {
         -- <whereG-2.2>
-        1, "second argument to likelihood() must be a constant between 0.0 and 1.0"
+        1, "Illegal parameters, second argument to likelihood() must be a constant between 0.0 and 1.0"
         -- </whereG-2.2>
     })
 
@@ -221,7 +221,7 @@ test:do_catchsql_test(
            AND album.aid=track.aid;
     ]], {
         -- <whereG-2.3>
-        1, "second argument to likelihood() must be a constant between 0.0 and 1.0"
+        1, "Illegal parameters, second argument to likelihood() must be a constant between 0.0 and 1.0"
         -- </whereG-2.3>
     })
 
diff --git a/test/sql/gh-2347-max-int-literals.result b/test/sql/gh-2347-max-int-literals.result
index c289a80..b511440 100644
--- a/test/sql/gh-2347-max-int-literals.result
+++ b/test/sql/gh-2347-max-int-literals.result
@@ -20,9 +20,11 @@ box.sql.execute("select (-9223372036854775808)")
 ...
 box.sql.execute("select (9223372036854775808)")
 ---
-- error: 'oversized integer: 9223372036854775808'
+- error: Integer literal 9223372036854775808 exceeds the supported range -9223372036854775808
+    - 9223372036854775807
 ...
 box.sql.execute("select (-9223372036854775809)")
 ---
-- error: 'oversized integer: -9223372036854775809'
+- error: Integer literal -9223372036854775809 exceeds the supported range -9223372036854775808
+    - 9223372036854775807
 ...
diff --git a/test/sql/gh-2929-primary-key.result b/test/sql/gh-2929-primary-key.result
index 4052665..e36f4e4 100644
--- a/test/sql/gh-2929-primary-key.result
+++ b/test/sql/gh-2929-primary-key.result
@@ -18,19 +18,19 @@ box.sql.execute("CREATE TABLE t1(a INT PRIMARY KEY, b INT UNIQUE)")
 ...
 box.sql.execute("CREATE TABLE t2(a INT UNIQUE, b INT)")
 ---
-- error: PRIMARY KEY missing on table T2
+- error: 'Failed to create space ''T2'': PRIMARY KEY missing'
 ...
 box.sql.execute("CREATE TABLE t3(a FLOAT)")
 ---
-- error: PRIMARY KEY missing on table T3
+- error: 'Failed to create space ''T3'': PRIMARY KEY missing'
 ...
 box.sql.execute("CREATE TABLE t4(a FLOAT, b TEXT)")
 ---
-- error: PRIMARY KEY missing on table T4
+- error: 'Failed to create space ''T4'': PRIMARY KEY missing'
 ...
 box.sql.execute("CREATE TABLE t5(a FLOAT, b FLOAT UNIQUE)")
 ---
-- error: PRIMARY KEY missing on table T5
+- error: 'Failed to create space ''T5'': PRIMARY KEY missing'
 ...
 box.sql.execute("DROP TABLE t1")
 ---
diff --git a/test/sql/integer-overflow.result b/test/sql/integer-overflow.result
index 4754c04..09e864e 100644
--- a/test/sql/integer-overflow.result
+++ b/test/sql/integer-overflow.result
@@ -30,15 +30,18 @@ box.sql.execute('SELECT (9223372036854775807 + 1);')
 --
 box.sql.execute('SELECT 9223372036854775808;')
 ---
-- error: 'oversized integer: 9223372036854775808'
+- error: Integer literal 9223372036854775808 exceeds the supported range -9223372036854775808
+    - 9223372036854775807
 ...
 box.sql.execute('SELECT -9223372036854775809;')
 ---
-- error: 'oversized integer: -9223372036854775809'
+- error: Integer literal -9223372036854775809 exceeds the supported range -9223372036854775808
+    - 9223372036854775807
 ...
 box.sql.execute('SELECT 9223372036854775808 - 1;')
 ---
-- error: 'oversized integer: 9223372036854775808'
+- error: Integer literal 9223372036854775808 exceeds the supported range -9223372036854775808
+    - 9223372036854775807
 ...
 -- Test that CAST may also leads to overflow.
 --
diff --git a/test/sql/iproto.result b/test/sql/iproto.result
index 938aea9..56099fa 100644
--- a/test/sql/iproto.result
+++ b/test/sql/iproto.result
@@ -363,7 +363,7 @@ sql = 'select '..string.rep('?, ', box.schema.SQL_BIND_PARAMETER_MAX)..'?'
 ...
 cn:execute(sql)
 ---
-- error: 'Failed to execute SQL statement: too many SQL variables'
+- error: 'Failed to execute SQL statement: SQL bind parameter limit reached: 65000'
 ...
 -- Try too many parameter values.
 sql = 'select ?'
@@ -571,8 +571,7 @@ cn:execute('select ?1, ?2, ?3', {1, 2, 3})
 ...
 cn:execute('select $name, $name2', {1, 2})
 ---
-- error: 'Failed to execute SQL statement: variable number must be between $1 and
-    $65000'
+- error: 'Failed to execute SQL statement: SQL bind parameter limit reached: 65000'
 ...
 parameters = {}
 ---
diff --git a/test/sql/transition.result b/test/sql/transition.result
index ea110c0..89035e5 100644
--- a/test/sql/transition.result
+++ b/test/sql/transition.result
@@ -180,7 +180,8 @@ box.sql.execute("DROP TABLE barfoo")
 -- attempt to create a table lacking PRIMARY KEY
 box.sql.execute("CREATE TABLE without_rowid_lacking_primary_key(x SCALAR)")
 ---
-- error: PRIMARY KEY missing on table WITHOUT_ROWID_LACKING_PRIMARY_KEY
+- error: 'Failed to create space ''WITHOUT_ROWID_LACKING_PRIMARY_KEY'': PRIMARY KEY
+    missing'
 ...
 -- create a table with implicit indices (used to SEGFAULT)
 box.sql.execute("CREATE TABLE implicit_indices(a INT PRIMARY KEY,b INT,c INT,d TEXT UNIQUE)")
diff --git a/test/sql/view.result b/test/sql/view.result
index fd8fe00..d330adc 100644
--- a/test/sql/view.result
+++ b/test/sql/view.result
@@ -19,7 +19,7 @@ box.sql.execute("CREATE VIEW v1 AS SELECT a+b FROM t1;");
 -- View can't have any indexes.
 box.sql.execute("CREATE INDEX i1 on v1(a);");
 ---
-- error: views can not be indexed
+- error: 'Can''t create or modify index ''I1'' in space ''V1'': views can not be indexed'
 ...
 v1 = box.space.V1;
 ---
-- 
2.7.4

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] [PATCH v4 8/8] sql: remove sqlErrorMsg()
  2019-03-13 17:03 [tarantool-patches] [PATCH v4 0/8] sql: use diag_set() for errors in SQL imeevma
                   ` (6 preceding siblings ...)
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 7/8] sql: rework semantic errors imeevma
@ 2019-03-13 17:03 ` imeevma
  2019-03-15 13:36   ` [tarantool-patches] " n.pettik
  2019-03-27  6:49   ` Kirill Yukhin
  7 siblings, 2 replies; 43+ messages in thread
From: imeevma @ 2019-03-13 17:03 UTC (permalink / raw)
  To: korablev; +Cc: tarantool-patches

Hi! Thank you for review! My answers and new version of patch
below. Here won't be diff between patches due to a ot of changes
in previous patches. Some of functionality of previous version of
this patch were moved in new patch "sql: rework semantic errors"

On 3/5/19 3:16 PM, n.pettik wrote:
>
>> @@ -1284,10 +1302,13 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
>>    goto create_view_fail;
>>  if (aliases != NULL) {
>>    if ((int)select_res_space->def->field_count != aliases->nExpr) {
>> -      sqlErrorMsg(parse_context, "expected %d columns "\
>> -          "for '%s' but got %d", aliases->nExpr,
>> -          space->def->name,
>> -          select_res_space->def->field_count);
>> +      const char *err_msg =
>> +        tt_sprintf("expected %d columns for '%s' but "\
>> +             "got %d", aliases->nExpr,
>> +             space->def->name,
>> +             select_res_space->def->field_count);
>> +      diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
>> +      parse_context->is_aborted = true;
>>      goto create_view_fail;
>>    }
>>    sqlColumnsFromExprList(parse_context, aliases, space->def);
>> @@ -1609,13 +1630,17 @@ sql_drop_table(struct Parse *parse_context, struct SrcList *table_name_list,
>>   * and DROP VIEW is not used on a table.
>>   */
>>  if (is_view && !space->def->opts.is_view) {
>> -    sqlErrorMsg(parse_context, "use DROP TABLE to delete table %s",
>> -        space_name);
>> +    const char *err_msg = tt_sprintf("use DROP TABLE to delete "\
>> +             "table %s", space_name);
>> +    diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
>
> Why not ER_DROP_SPACE?
Fixed in "sql: rework semantic errors".

>> +    parse_context->is_aborted = true;
>>    goto exit_drop_table;
>>  }
>>  if (!is_view && space->def->opts.is_view) {
>> -    sqlErrorMsg(parse_context, "use DROP VIEW to delete view %s",
>> -        space_name);
>> +    const char *err_msg = tt_sprintf("use DROP VIEW to delete "\
>> +             "view %s", space_name);
>> +    diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
>
> The same question.
Fixed in "sql: rework semantic errors".

>> +    parse_context->is_aborted = true;
>>    goto exit_drop_table;
>>  }
>>  /*
>> @@ -1760,8 +1785,10 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child,
>>    }
>>  } else {
>>    if (parent_space->def->opts.is_view) {
>> -      sqlErrorMsg(parse_context,
>> -          "referenced table can't be view");
>> +      const char *err_msg = tt_sprintf("referenced table "\
>> +               "can't be view");
>> +      diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
>
> ER_CREATE_FK_CONSTRAINT
Fixed in "sql: rework semantic errors".

>> +      parse_context->is_aborted = true;
>>      goto exit_create_fk;
>>    }
>>  }
>> @@ -2149,7 +2176,9 @@ sql_create_index(struct Parse *parse, struct Token *token,
>>  struct space_def *def = space->def;
>>
>>  if (def->opts.is_view) {
>> -    sqlErrorMsg(parse, "views can not be indexed");
>> +    const char *err_msg = tt_sprintf("views can not be indexed");
>> +    diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
>
> ER_CREATE_INDEX
Fixed in "sql: rework semantic errors".

>> +    parse->is_aborted = true;
>>    goto exit_create_index;
>>  }
>>  /*
>> @@ -2947,11 +2976,8 @@ sqlSavepoint(Parse * pParse, int op, Token * pName)
>>      return;
>>    }
>>    if (op == SAVEPOINT_BEGIN &&
>> -      sqlCheckIdentifierName(pParse, zName)
>> -        != SQL_OK) {
>> -      sqlErrorMsg(pParse, "bad savepoint name");
>> +      sqlCheckIdentifierName(pParse, zName) != SQL_OK)
>
> != 0
Function sqlCheckIdentifierName() still works in terms of
{SQL_OK, SQL_ERROR}, so I left this part as it was.

>>      return;
>> -    }
>>    sqlVdbeAddOp4(v, OP_Savepoint, op, 0, 0, zName, P4_DYNAMIC);
>>  }
>> }
>> @@ -1757,8 +1759,10 @@ sqlExprListAppendVector(Parse * pParse, /* Parsing context */
>>   */
>>  if (pExpr->op != TK_SELECT
>>      && pColumns->nId != (n = sqlExprVectorSize(pExpr))) {
>> -    sqlErrorMsg(pParse, "%d columns assigned %d values",
>> -        pColumns->nId, n);
>> +    const char *err_msg = tt_sprintf("%d columns assigned %d "\
>> +             "values", pColumns->nId, n);
>> +    diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
>> +    pParse->is_aborted = true;
>>    goto vector_append_error;
>>  }
>>
>> @@ -1878,7 +1882,10 @@ sqlExprListCheckLength(Parse * pParse,
>>  testcase(pEList && pEList->nExpr == mx);
>>  testcase(pEList && pEList->nExpr == mx + 1);
>>  if (pEList && pEList->nExpr > mx) {
>> -    sqlErrorMsg(pParse, "too many columns in %s", zObject);
>> +    const char *err_msg = tt_sprintf("too many columns in %s",
>> +             zObject);
>
> You introduced LIMIT and “too many columns” errors in previous patch.
> Why you can’t use them here?
Fixed in "sql: rework semantic errors".

>> +    diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
>> +    pParse->is_aborted = true;
>>  }
>> }
>>
>> diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
>> index b69f059..3653824 100644
>> --- a/src/box/sql/parse.y
>> +++ b/src/box/sql/parse.y
>> @@ -897,7 +897,9 @@ expr(A) ::= VARIABLE(X).     {
>>   Token t = X;
>>   if (pParse->parse_only) {
>>     spanSet(&A, &t, &t);
>> -    sqlErrorMsg(pParse, "bindings are not allowed in DDL");
>> +    const char *err_msg = tt_sprintf("bindings are not allowed in DDL”);
>
> Em, why can’t you inline this var?
>
> diag_set(ClientError, ER_SQL_PARSER_GENERIC, "bindings are not allowed in DDL");
Fixed.

>> +    diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
>> +    pParse->is_aborted = true;
>>     A.pExpr = NULL;
>>   } else if (!(X.z[0]=='#' && sqlIsdigit(X.z[1]))) {
>>     u32 n = X.n;
>>
>> diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
>> index 02eca37..63a1b96 100644
>> --- a/src/box/sql/resolve.c
>> +++ b/src/box/sql/resolve.c
>> @@ -392,14 +392,25 @@ lookupName(Parse * pParse, /* The parsing context */
>>          pOrig = pEList->a[j].pExpr;
>>          if ((pNC->ncFlags & NC_AllowAgg) == 0
>>              && ExprHasProperty(pOrig, EP_Agg)) {
>> -            sqlErrorMsg(pParse,
>> -                "misuse of aliased aggregate %s",
>> -                zAs);
>> +            const char *err_msg =
>> +              tt_sprintf("misuse of "\
>> +                   "aliased "\
>> +                   "aggregate "\
>> +                   "%s", zAs);
>
> Such formatting looks terrible. Lets break 80 border or move
> declaration of this error msg at few blocks above.
Fixed, I think.

>> @@ -633,9 +647,13 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
>>                exprProbability(pList->a[1].
>>                    pExpr);
>>            if (pExpr->iTable < 0) {
>> -              sqlErrorMsg(pParse,
>> -                  "second argument to likelihood() must be a "
>> -                  "constant between 0.0 and 1.0");
>> +              const char *err_msg =
>> +                tt_sprintf("second argument to likelihood() must be a "\
>> +                     "constant between 0.0 and 1.0”);
>
> You don’t need sprintf here.
Fixed.

>> +              diag_set(ClientError,
>> +                 ER_SQL_PARSER_GENERIC,
>> +                 err_msg);
>> +              pParse->is_aborted = true;
>>              pNC->nErr++;
>>            }
>>          } else {
>> @@ -802,7 +827,11 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
>>        testcase(pExpr->op == TK_GT);
>>        testcase(pExpr->op == TK_GE);
>>        testcase(pExpr->op == TK_BETWEEN);
>> -        sqlErrorMsg(pParse, "row value misused");
>> +        const char *err_msg = tt_sprintf("row value "\
>> +                 "misused”);
>
> The same.
Fixed.

>> +        diag_set(ClientError, ER_SQL_PARSER_GENERIC,
>> +           err_msg);
>> +        pParse->is_aborted = true;
>>      }
>>      break;
>>    }
>> @@ -950,7 +964,10 @@ resolveCompoundOrderBy(Parse * pParse,  /* Parsing context.  Leave error messages
>>  db = pParse->db;
>> #if SQL_MAX_COLUMN
>>  if (pOrderBy->nExpr > db->aLimit[SQL_LIMIT_COLUMN]) {
>> -    sqlErrorMsg(pParse, "too many terms in ORDER BY clause");
>> +    const char *err_msg = tt_sprintf("too many terms in ORDER BY "\
>> +             "clause”);
>
> The same.
Fixed.

>> +    diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
>> +    pParse->is_aborted = true;
>>    return 1;
>>  }
>> #endif
>> @@ -1024,9 +1045,11 @@ resolveCompoundOrderBy(Parse * pParse,  /* Parsing context.  Leave error messages
>>  }
>>  for (i = 0; i < pOrderBy->nExpr; i++) {
>>    if (pOrderBy->a[i].done == 0) {
>> -      sqlErrorMsg(pParse,
>> -          "%r ORDER BY term does not match any "
>> -          "column in the result set", i + 1);
>> +      const char *err_msg =
>> +        tt_sprintf("ORDER BY term does not match any "\
>> +             "column in the result set”);
>
> The same.
Fixed.

>> +      diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
>> +      pParse->is_aborted = true;
>>      return 1;
>>    }
>>  }
>> @@ -1417,9 +1450,15 @@ resolveSelectStep(Walker * pWalker, Select * p)
>>      for (i = 0, pItem = pGroupBy->a; i < pGroupBy->nExpr;
>>           i++, pItem++) {
>>        if (ExprHasProperty(pItem->pExpr, EP_Agg)) {
>> -          sqlErrorMsg(pParse,
>> -              "aggregate functions are not allowed in "
>> -              "the GROUP BY clause");
>> +          const char *err_msg =
>> +            tt_sprintf("aggregate "\
>> +                 "functions are not "\
>> +                 "allowed in the "\
>> +                 "GROUP BY clause”);
>
> Ok, verify all usages of sprintf and make sure they are really required.
> I see several more in code.
Fixed.

>> @@ -613,8 +622,11 @@ sqlProcessJoin(Parse * pParse, Select * p)
>>    /* Disallow both ON and USING clauses in the same join
>>     */
>>    if (pRight->pOn && pRight->pUsing) {
>> -      sqlErrorMsg(pParse, "cannot have both ON and USING "
>> -          "clauses in the same join");
>> +      const char *err_msg =
>> +        tt_sprintf("cannot have both ON and USING "
>> +             "clauses in the same join");
>> +      diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
>> +      pParse->is_aborted = true;
>>      return 1;
>>    }
>>
>> @@ -650,10 +662,16 @@ sqlProcessJoin(Parse * pParse, Select * p)
>>            || !tableAndColumnIndex(pSrc, i + 1, zName,
>>                  &iLeft, &iLeftCol)
>>            ) {
>> -          sqlErrorMsg(pParse,
>> -              "cannot join using column %s - column "
>> -              "not present in both tables",
>> -              zName);
>> +          const char *err_msg =
>> +            tt_sprintf("cannot join using "\
>> +                 "column %s - "\
>> +                 "column not "\
>> +                 "present in both "\
>> +                 "tables", zName);
>
> Horrible formatting.
Fixed, I think.

>> diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
>> index 85718e1..5a74c65 100644
>> --- a/src/box/sql/sqlInt.h
>> +++ b/src/box/sql/sqlInt.h
>> @@ -3182,7 +3182,6 @@ void sqlTreeViewWith(TreeView *, const With *);
>> #endif
>>
>> void sqlSetString(char **, sql *, const char *);
>> -void sqlErrorMsg(Parse *, const char *, ...);
>> void sqlDequote(char *);
>> void sqlNormalizeName(char *z);
>> void sqlTokenInit(Token *, char *);
>> diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
>> index 2f1268a..297ba01 100644
>> --- a/src/box/sql/trigger.c
>> +++ b/src/box/sql/trigger.c
>> @@ -621,8 +621,11 @@ codeTriggerProgram(Parse * pParse,  /* The parser context */
>>  sqlSubProgramsRemaining--;
>>
>>  if (sqlSubProgramsRemaining == 0) {
>> -    sqlErrorMsg(pParse,
>> -        "Maximum number of chained trigger activations exceeded.");
>> +    const char *err_msg = tt_sprintf("Maximum number of chained "\
>> +             "trigger activations "\
>> +             "exceeded.”);
>
> Please, make sure that it is valid error. And the rest of errors
> I pointed in google doc file. If this is unreachable code, then
> replace error with assert.
This error is valid. As for the other ones - I couldn't reach some
of them, but didn't remove them for now.

>> -/*
>>  * Convert an SQL-style quoted string into a normal string by removing
>>  * the quote characters.  The conversion is done in-place.  If the
>>  * input does not begin with a quote character, then this routine
>> diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
>> index 9957f3a..d4fbf17 100644
>> --- a/src/box/sql/vdbemem.c
>> +++ b/src/box/sql/vdbemem.c
>> @@ -1222,7 +1222,9 @@ valueFromFunction(sql * db,  /* The database connection */
>>  pFunc->xSFunc(&ctx, nVal, apVal);
>>  if (ctx.isError) {
>>    rc = ctx.isError;
>> -    sqlErrorMsg(pCtx->pParse, "%s", sql_value_text(pVal));
>> +    diag_set(ClientError, ER_SQL_PARSER_GENERIC,
>> +       sql_value_text(pVal));
>
> What kind of error is it? Please, add reasonable error message,
> not only text representation of value.
I did, but not sure if this error is reachable. I wasn't able to
reproduce it.

>> +    pCtx->pParse->is_aborted = true;
>>  } else {
>>    sql_value_apply_type(pVal, type);
>>    assert(rc == SQL_OK);
>> diff --git a/src/box/sql/where.c b/src/box/sql/where.c
>> index 33885d0..e2c91e0 100644
>> --- a/src/box/sql/where.c
>> +++ b/src/box/sql/where.c
>> @@ -3914,7 +3914,9 @@ wherePathSolver(WhereInfo * pWInfo, LogEst nRowEst)
>>  }
>>
>>  if (nFrom == 0) {
>> -    sqlErrorMsg(pParse, "no query solution");
>> +    const char *err_msg = tt_sprintf("no query solution”);
>
> Same: this path seems to be unreachable.
I think so too, but left as it is for now.

>> diff --git a/test/sql-tap/e_select1.test.lua b/test/sql-tap/e_select1.test.lua
>> index e190ad7..16a31a8 100755
>> --- a/test/sql-tap/e_select1.test.lua
>> +++ b/test/sql-tap/e_select1.test.lua
>> @@ -1855,13 +1855,13 @@ test:do_catchsql_test(
>>     "e_select-8.7.1.1",
>>     "SELECT x FROM d1 UNION ALL SELECT a FROM d2 ORDER BY x*z",
>>     {
>> -        1, "1st ORDER BY term does not match any column in the result set"})
>> +        1, "ORDER BY term does not match any column in the result set”}
>
> Why did you change error message?
> I see quite a lot affected tests.
I did this due to non-standart format %r that is used here.
Changed text of the error.


New version:

commit 99f4adc61352899a83a7097b965845bafaa968ef
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Sat Mar 9 18:54:19 2019 +0300

    sql: remove sqlErrorMsg()
    
    This patch completely replaces sqlErrorMsg() with diag_set() and
    removes sqlErrorMsg().
    
    Closes #3965
    Closes #3036

diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 26434b1..565bc17 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -490,13 +490,12 @@ sql_column_add_nullable_action(struct Parse *parser,
  if (field->nullable_action != ON_CONFLICT_ACTION_DEFAULT &&
      nullable_action != field->nullable_action) {
    /* Prevent defining nullable_action many times. */
-   const char *err_msg =
-     tt_sprintf("NULL declaration for column '%s' of table "
-          "'%s' has been already set to '%s'",
-          field->name, def->name,
-          on_conflict_action_strs[field->
-                nullable_action]);
-   diag_set(ClientError, ER_SQL, err_msg);
+   const char *err = "NULL declaration for column '%s' of table "
+         "'%s' has been already set to '%s'";
+   const char *action =
+     on_conflict_action_strs[field->nullable_action];
+   err = tt_sprintf(err, field->name, def->name, action);
+   diag_set(ClientError, ER_SQL, err);
    parser->is_aborted = true;
    return;
  }
@@ -3044,11 +3043,12 @@ sqlWithAdd(Parse * pParse,  /* Parsing context */
  zName = sqlNameFromToken(pParse->db, pName);
  if (zName && pWith) {
    int i;
+   const char *err = "Ambiguous table name in WITH query: %s";
    for (i = 0; i < pWith->nCte; i++) {
      if (strcmp(zName, pWith->a[i].zName) == 0) {
-       sqlErrorMsg(pParse,
-           "duplicate WITH table name: %s",
-           zName);
+       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+          tt_sprintf(err, zName));
+       pParse->is_aborted = true;
      }
    }
  }
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index e8a5f0d..fcd320d 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -96,17 +96,17 @@ sql_table_truncate(struct Parse *parse, struct SrcList *tab_list)
    goto tarantool_error;
  }
  if (! rlist_empty(&space->parent_fk_constraint)) {
-   const char *err_msg =
-     tt_sprintf("can not truncate space '%s' because other "
-          "objects depend on it", space->def->name);
-   diag_set(ClientError, ER_SQL, err_msg);
+   const char *err = "can not truncate space '%s' because other "
+         "objects depend on it";
+   diag_set(ClientError, ER_SQL,
+      tt_sprintf(err, space->def->name));
    goto tarantool_error;
  }
  if (space->def->opts.is_view) {
-   const char *err_msg =
-     tt_sprintf("can not truncate space '%s' because it is "
-          "a view", space->def->name);
-   diag_set(ClientError, ER_SQL, err_msg);
+   const char *err = "can not truncate space '%s' because it is "\
+         "a view";
+   diag_set(ClientError, ER_SQL,
+      tt_sprintf(err, space->def->name));
    goto tarantool_error;
  }
  sqlVdbeAddOp2(v, OP_Clear, space->def->id, true);
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index de06ee0..7205cb0 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -668,7 +668,9 @@ codeVectorCompare(Parse * pParse, /* Code generator context */
  int addrDone = sqlVdbeMakeLabel(v);
 
  if (nLeft != sqlExprVectorSize(pRight)) {
-   sqlErrorMsg(pParse, "row value misused");
+   diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+      "row value misused");
+   pParse->is_aborted = true;
    return;
  }
  assert(pExpr->op == TK_EQ || pExpr->op == TK_NE
@@ -1195,7 +1197,14 @@ sqlExprAssignVarNumber(Parse * pParse, Expr * pExpr, u32 n)
      testcase(i == 1);
      testcase(i == SQL_BIND_PARAMETER_MAX - 1);
      testcase(i == SQL_BIND_PARAMETER_MAX);
-     if (!is_ok || i < 1 || i > SQL_BIND_PARAMETER_MAX) {
+     if (i < 1) {
+       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+          "Index of binding slots must start "\
+          "from 1");
+       pParse->is_aborted = true;
+       return;
+     }
+     if (!is_ok || i > SQL_BIND_PARAMETER_MAX) {
        diag_set(ClientError, ER_SQL_BIND_PARAMETER_MAX,
           SQL_BIND_PARAMETER_MAX);
        pParse->is_aborted = true;
@@ -1779,8 +1788,10 @@ sqlExprListAppendVector(Parse * pParse,  /* Parsing context */
   */
  if (pExpr->op != TK_SELECT
      && pColumns->nId != (n = sqlExprVectorSize(pExpr))) {
-   sqlErrorMsg(pParse, "%d columns assigned %d values",
-       pColumns->nId, n);
+   const char *err = tt_sprintf("%d columns assigned %d values",
+              pColumns->nId, n);
+   diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+   pParse->is_aborted = true;
    goto vector_append_error;
  }
 
@@ -2644,41 +2655,6 @@ expr_in_type(Parse *pParse, Expr *pExpr)
 }
 
 /*
- * Load the Parse object passed as the first argument with an error
- * message of the form:
- *
- *   "sub-select returns N columns - expected M"
- */
-void
-sqlSubselectError(Parse * pParse, int nActual, int nExpect)
-{
- const char *zFmt = "sub-select returns %d columns - expected %d";
- sqlErrorMsg(pParse, zFmt, nActual, nExpect);
-}
-
-/*
- * Expression pExpr is a vector that has been used in a context where
- * it is not permitted. If pExpr is a sub-select vector, this routine
- * loads the Parse object with a message of the form:
- *
- *   "sub-select returns N columns - expected 1"
- *
- * Or, if it is a regular scalar vector:
- *
- *   "row value misused"
- */
-void
-sqlVectorErrorMsg(Parse * pParse, Expr * pExpr)
-{
- if (pExpr->flags & EP_xIsSelect) {
-   sqlSubselectError(pParse, pExpr->x.pSelect->pEList->nExpr,
-             1);
- } else {
-   sqlErrorMsg(pParse, "row value misused");
- }
-}
-
-/*
  * Generate code for scalar subqueries used as a subquery expression, EXISTS,
  * or IN operators.  Examples:
  *
@@ -2959,15 +2935,26 @@ int
 sqlExprCheckIN(Parse * pParse, Expr * pIn)
 {
  int nVector = sqlExprVectorSize(pIn->pLeft);
+ const char *err;
  if ((pIn->flags & EP_xIsSelect)) {
    if (nVector != pIn->x.pSelect->pEList->nExpr) {
-     sqlSubselectError(pParse,
-               pIn->x.pSelect->pEList->nExpr,
-               nVector);
+     err = "sub-select returns %d columns - expected %d";
+     int expr_count = pIn->x.pSelect->pEList->nExpr;
+     err = tt_sprintf(err, expr_count, nVector);
+     diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+     pParse->is_aborted = true;
      return 1;
    }
  } else if (nVector != 1) {
-   sqlVectorErrorMsg(pParse, pIn->pLeft);
+   if (pIn->pLeft->flags & EP_xIsSelect) {
+     err = "sub-select returns %d columns - expected 1";
+     int expr_count = pIn->pLeft->x.pSelect->pEList->nExpr;
+     err = tt_sprintf(err, expr_count);
+   } else {
+     err = "row value misused";
+   }
+   diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+   pParse->is_aborted = true;
    return 1;
  }
  return 0;
@@ -3965,9 +3952,10 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
      AggInfo *pInfo = pExpr->pAggInfo;
      if (pInfo == 0) {
        assert(!ExprHasProperty(pExpr, EP_IntValue));
-       sqlErrorMsg(pParse,
-           "misuse of aggregate: %s()",
-           pExpr->u.zToken);
+       const char *err = "misuse of aggregate: %s()";
+       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+          tt_sprintf(err, pExpr->u.zToken));
+       pParse->is_aborted = true;
      } else {
        pExpr->type = pInfo->aFunc->pFunc->ret_type;
        return pInfo->aFunc[pExpr->iAgg].iMem;
@@ -4134,7 +4122,12 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
      testcase(op == TK_SELECT);
      if (op == TK_SELECT
          && (nCol = pExpr->x.pSelect->pEList->nExpr) != 1) {
-       sqlSubselectError(pParse, nCol, 1);
+       const char *err = "sub-select returns %d "\
+             "columns - expected 1";
+       err = tt_sprintf(err, nCol);
+       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+          err);
+       pParse->is_aborted = true;
      } else {
        return sqlCodeSubselect(pParse, pExpr, 0);
      }
@@ -4153,9 +4146,11 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
             sqlExprVectorSize(pExpr->
                       pLeft))
          ) {
-       sqlErrorMsg(pParse,
-           "%d columns assigned %d values",
-           pExpr->iTable, n);
+       const char *err =
+         "%d columns assigned %d values";
+       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+          tt_sprintf(err, pExpr->iTable, n));
+       pParse->is_aborted = true;
      }
      return pExpr->pLeft->iTable + pExpr->iColumn;
    }
@@ -4254,7 +4249,9 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
    }
 
  case TK_VECTOR:{
-     sqlErrorMsg(pParse, "row value misused");
+     diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+        "row value misused");
+     pParse->is_aborted = true;
      break;
    }
 
@@ -4354,8 +4351,9 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
    }
  case TK_RAISE:
    if (pParse->triggered_space == NULL) {
-     sqlErrorMsg(pParse, "RAISE() may only be used "
-         "within a trigger-program");
+     diag_set(ClientError, ER_SQL_PARSER_GENERIC, "RAISE() "\
+        "may only be used within a trigger-program");
+     pParse->is_aborted = true;
      return 0;
    }
    if (pExpr->on_conflict_action == ON_CONFLICT_ACTION_ABORT)
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 263df4d..7635f7b 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -396,10 +396,11 @@ sqlInsert(Parse * pParse, /* Parser context */
        goto insert_cleanup;
      }
      if (bit_test(used_columns, j)) {
-       const char *err;
-       err = "table id list: duplicate column name %s";
-       sqlErrorMsg(pParse,
-           err, pColumn->a[i].zName);
+       const char *err = "table id list: duplicate "\
+             "column name %s";
+       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+          tt_sprintf(err, pColumn->a[i].zName));
+       pParse->is_aborted = true;
        goto insert_cleanup;
      }
      bit_set(used_columns, j);
@@ -510,14 +511,19 @@ sqlInsert(Parse * pParse, /* Parser context */
 
  if (pColumn == NULL && nColumn != 0 &&
      nColumn != (int)space_def->field_count) {
-   sqlErrorMsg(pParse,
-       "table %S has %d columns but %d values were supplied",
-       pTabList, 0, space_def->field_count, nColumn);
+   const char *err =
+     "table %s has %d columns but %d values were supplied";
+   err = tt_sprintf(err, pTabList->a[0].zName,
+        space_def->field_count, nColumn);
+   diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+   pParse->is_aborted = true;
    goto insert_cleanup;
  }
  if (pColumn != 0 && nColumn != pColumn->nId) {
-   sqlErrorMsg(pParse, "%d values for %d columns", nColumn,
-       pColumn->nId);
+   const char *err = "%d values for %d columns";
+   err = tt_sprintf(err, nColumn, pColumn->nId);
+   diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+   pParse->is_aborted = true;
    goto insert_cleanup;
  }
 
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index acf947b..80a9265 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -902,7 +902,9 @@ expr(A) ::= VARIABLE(X).     {
   Token t = X;
   if (pParse->parse_only) {
     spanSet(&A, &t, &t);
-    sqlErrorMsg(pParse, "bindings are not allowed in DDL");
+    diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+             "bindings are not allowed in DDL");
+    pParse->is_aborted = true;
     A.pExpr = NULL;
   } else if (!(X.z[0]=='#' && sqlIsdigit(X.z[1]))) {
     u32 n = X.n;
@@ -1386,9 +1388,9 @@ trigger_cmd_list(A) ::= trigger_cmd(A) SEMI. {
 trnm(A) ::= nm(A).
 trnm(A) ::= nm DOT nm(X). {
   A = X;
-  sqlErrorMsg(pParse,
-        "qualified table names are not allowed on INSERT, UPDATE, and DELETE "
-        "statements within triggers");
+  diag_set(ClientError, ER_SQL_PARSER_GENERIC, "qualified table names are not "\
+           "allowed on INSERT, UPDATE, and DELETE statements within triggers");
+  pParse->is_aborted = true;
 }
 
 // Disallow the INDEX BY and NOT INDEXED clauses on UPDATE and DELETE
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 9171d05..6720914 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -390,16 +390,22 @@ lookupName(Parse * pParse,  /* The parsing context */
          assert(pExpr->x.pList == 0);
          assert(pExpr->x.pSelect == 0);
          pOrig = pEList->a[j].pExpr;
+         const char *err = "misuse of aliased "\
+               "aggregate %s";
          if ((pNC->ncFlags & NC_AllowAgg) == 0
              && ExprHasProperty(pOrig, EP_Agg)) {
-           sqlErrorMsg(pParse,
-               "misuse of aliased aggregate %s",
-               zAs);
+           err = tt_sprintf(err, zAs);
+           diag_set(ClientError,
+              ER_SQL_PARSER_GENERIC,
+              err);
+           pParse->is_aborted = true;
            return WRC_Abort;
          }
          if (sqlExprVectorSize(pOrig) != 1) {
-           sqlErrorMsg(pParse,
-               "row value misused");
+           diag_set(ClientError,
+              ER_SQL_PARSER_GENERIC,
+              "row value misused");
+           pParse->is_aborted = true;
            return WRC_Abort;
          }
          resolveAlias(pParse, pEList, j, pExpr,
@@ -426,12 +432,15 @@ lookupName(Parse * pParse,  /* The parsing context */
   * more matches.  Either way, we have an error.
   */
  if (cnt > 1) {
+   const char *err;
    if (zTab) {
-     sqlErrorMsg(pParse, "ambiguous column name: %s.%s",
-           zTab, zCol);
+     err = tt_sprintf("ambiguous column name: %s.%s", zTab,
+          zCol);
    } else {
-     sqlErrorMsg(pParse, "ambiguous column name: %s", zCol);
+     err = tt_sprintf("ambiguous column name: %s", zCol);
    }
+   diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+   pParse->is_aborted = true;
    pTopNC->nErr++;
  }
  if (cnt == 0) {
@@ -625,7 +634,7 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
      } else {
        is_agg = pDef->xFinalize != 0;
        pExpr->type = pDef->ret_type;
-       const char *err_msg =
+       const char *err =
          "second argument to likelihood() must "\
          "be a constant between 0.0 and 1.0";
        if (pDef->funcFlags & SQL_FUNC_UNLIKELY) {
@@ -638,7 +647,7 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
            if (pExpr->iTable < 0) {
              diag_set(ClientError,
                 ER_ILLEGAL_PARAMS,
-                err_msg);
+                err);
              pParse->is_aborted =
                true;
              pNC->nErr++;
@@ -683,9 +692,11 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
        }
      }
      if (is_agg && (pNC->ncFlags & NC_AllowAgg) == 0) {
-       sqlErrorMsg(pParse,
-           "misuse of aggregate function %.*s()",
-           nId, zId);
+       const char *err =
+         tt_sprintf("misuse of aggregate "\
+              "function %.*s()", nId, zId);
+       diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+       pParse->is_aborted = true;
        pNC->nErr++;
        is_agg = 0;
      } else if (no_such_func && pParse->db->init.busy == 0
@@ -697,9 +708,12 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
        pParse->is_aborted = true;
        pNC->nErr++;
      } else if (wrong_num_args) {
-       sqlErrorMsg(pParse,
-           "wrong number of arguments to function %.*s()",
-           nId, zId);
+       const char *err = "wrong number of arguments "\
+             "to function %.*s()";
+       err = tt_sprintf(err, nId, zId);
+       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+          err);
+       pParse->is_aborted = true;
        pNC->nErr++;
      }
      if (is_agg)
@@ -803,7 +817,9 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
        testcase(pExpr->op == TK_GT);
        testcase(pExpr->op == TK_GE);
        testcase(pExpr->op == TK_BETWEEN);
-       sqlErrorMsg(pParse, "row value misused");
+       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+          "row value misused");
+       pParse->is_aborted = true;
      }
      break;
    }
@@ -905,21 +921,6 @@ resolveOrderByTermToExprList(Parse * pParse, /* Parsing context for error messag
 }
 
 /*
- * Generate an ORDER BY or GROUP BY term out-of-range error.
- */
-static void
-resolveOutOfRangeError(Parse * pParse, /* The error context into which to write the error */
-          const char *zType, /* "ORDER" or "GROUP" */
-          int i, /* The index (1-based) of the term out of range */
-          int mx /* Largest permissible value of i */
-    )
-{
- sqlErrorMsg(pParse,
-     "%r %s BY term out of range - should be "
-     "between 1 and %d", i, zType, mx);
-}
-
-/*
  * Analyze the ORDER BY clause in a compound SELECT statement.   Modify
  * each term of the ORDER BY clause is a constant integer between 1
  * and N where N is the number of columns in the compound SELECT.
@@ -980,9 +981,16 @@ resolveCompoundOrderBy(Parse * pParse, /* Parsing context.  Leave error messages
      pE = sqlExprSkipCollate(pItem->pExpr);
      if (sqlExprIsInteger(pE, &iCol)) {
        if (iCol <= 0 || iCol > pEList->nExpr) {
-         resolveOutOfRangeError(pParse, "ORDER",
-                    i + 1,
-                    pEList->nExpr);
+         const char *err =
+           "Error at ORDER BY in place "\
+           "%d: term out of range - "\
+           "should be between 1 and %d";
+         err = tt_sprintf(err, i + 1,
+              pEList->nExpr);
+         diag_set(ClientError,
+            ER_SQL_PARSER_GENERIC,
+            err);
+         pParse->is_aborted = true;
          return 1;
        }
      } else {
@@ -1028,9 +1036,12 @@ resolveCompoundOrderBy(Parse * pParse, /* Parsing context.  Leave error messages
  }
  for (i = 0; i < pOrderBy->nExpr; i++) {
    if (pOrderBy->a[i].done == 0) {
-     sqlErrorMsg(pParse,
-         "%r ORDER BY term does not match any "
-         "column in the result set", i + 1);
+     const char *err = "Error at ORDER BY in place %d: "\
+           "term does not match any column in "\
+           "the result set";
+     err = tt_sprintf(err, i + 1);
+     diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+     pParse->is_aborted = true;
      return 1;
    }
  }
@@ -1080,8 +1091,14 @@ sqlResolveOrderGroupBy(Parse *pParse, Select *pSelect, ExprList *pOrderBy,
  for (i = 0, pItem = pOrderBy->a; i < pOrderBy->nExpr; i++, pItem++) {
    if (pItem->u.x.iOrderByCol) {
      if (pItem->u.x.iOrderByCol > pEList->nExpr) {
-       resolveOutOfRangeError(pParse, zType, i + 1,
-                  pEList->nExpr);
+       const char *err = "Error at %s BY in place "\
+             "%d: term out of range - "\
+             "should be between 1 and %d";
+       err = tt_sprintf(err, zType, i + 1,
+            pEList->nExpr);
+       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+          err);
+       pParse->is_aborted = true;
        return 1;
      }
      resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol - 1,
@@ -1148,8 +1165,13 @@ resolveOrderGroupBy(NameContext *pNC, Select *pSelect, ExprList *pOrderBy,
       * order-by term to a copy of the result-set expression
       */
      if (iCol < 1 || iCol > 0xffff) {
-       resolveOutOfRangeError(pParse, zType, i + 1,
-                  nResult);
+       const char *err = "Error at %s BY in place "\
+             "%d: term out of range - "\
+             "should be between 1 and %d";
+       err = tt_sprintf(err, zType, i + 1, nResult);
+       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+          err);
+       pParse->is_aborted = true;
        return 1;
      }
      pItem->u.x.iOrderByCol = (u16) iCol;
@@ -1429,12 +1451,15 @@ resolveSelectStep(Walker * pWalker, Select * p)
          || db->mallocFailed) {
        return WRC_Abort;
      }
+     const char *err_msg = "aggregate functions are not "\
+               "allowed in the GROUP BY clause";
      for (i = 0, pItem = pGroupBy->a; i < pGroupBy->nExpr;
           i++, pItem++) {
        if (ExprHasProperty(pItem->pExpr, EP_Agg)) {
-         sqlErrorMsg(pParse,
-             "aggregate functions are not allowed in "
-             "the GROUP BY clause");
+         diag_set(ClientError,
+            ER_SQL_PARSER_GENERIC,
+            err_msg);
+         pParse->is_aborted = true;
          return WRC_Abort;
        }
      }
@@ -1444,10 +1469,37 @@ resolveSelectStep(Walker * pWalker, Select * p)
     * number of expressions in the select list.
     */
    if (p->pNext && p->pEList->nExpr != p->pNext->pEList->nExpr) {
-     sqlSelectWrongNumTermsError(pParse, p->pNext);
+     if (p->pNext->selFlags & SF_Values) {
+       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+          "all VALUES must have the same "\
+          "number of terms");
+     } else {
+       const char *err_msg =
+         "SELECTs to the left and right of %s "\
+         "do not have the same number of "\
+         "result columns";
+       switch (p->pNext->op) {
+       case TK_ALL:
+         err_msg = tt_sprintf(err_msg,
+                  "UNION ALL");
+         break;
+       case TK_INTERSECT:
+         err_msg = tt_sprintf(err_msg,
+                  "INTERSECT");
+         break;
+       case TK_EXCEPT:
+         err_msg = tt_sprintf(err_msg, "EXCEPT");
+         break;
+       default:
+         err_msg = tt_sprintf(err_msg, "UNION");
+         break;
+       }
+       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+          err_msg);
+     }
+     pParse->is_aborted = true;
      return WRC_Abort;
    }
-
    /* Advance to the next term of the compound
     */
    p = p->pPrior;
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index f2cad56..ca8e5a8 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -397,13 +397,19 @@ sqlJoinType(Parse * pParse, Token * pA, Token * pB, Token * pC)
  }
  if ((jointype & (JT_INNER | JT_OUTER)) == (JT_INNER | JT_OUTER) ||
      (jointype & JT_ERROR) != 0) {
-   const char *zSp = " ";
    assert(pB != 0);
-   if (pC == 0) {
-     zSp++;
+   const char *err;
+   if (pC == NULL) {
+     err = tt_sprintf("unknown or unsupported join type: "\
+          "%.*s %.*s", pA->n, pA->z, pB->n,
+          pB->z);
+   } else {
+     err = tt_sprintf("unknown or unsupported join type: "\
+          "%.*s %.*s %.*s", pA->n, pA->z, pB->n,
+          pB->z, pC->n, pC->z);
    }
-   sqlErrorMsg(pParse, "unknown or unsupported join type: "
-       "%T %T%s%T", pA, pB, zSp, pC);
+   diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+   pParse->is_aborted = true;
    jointype = JT_INNER;
  } else if ((jointype & JT_OUTER) != 0
       && (jointype & (JT_LEFT | JT_RIGHT)) != JT_LEFT) {
@@ -590,9 +596,10 @@ sqlProcessJoin(Parse * pParse, Select * p)
     */
    if (pRight->fg.jointype & JT_NATURAL) {
      if (pRight->pOn || pRight->pUsing) {
-       sqlErrorMsg(pParse,
-           "a NATURAL join may not have "
-           "an ON or USING clause", 0);
+       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+          "a NATURAL join may not have "
+          "an ON or USING clause");
+       pParse->is_aborted = true;
        return 1;
      }
      for (j = 0; j < (int)right_space->def->field_count; j++) {
@@ -613,8 +620,10 @@ sqlProcessJoin(Parse * pParse, Select * p)
    /* Disallow both ON and USING clauses in the same join
     */
    if (pRight->pOn && pRight->pUsing) {
-     sqlErrorMsg(pParse, "cannot have both ON and USING "
-         "clauses in the same join");
+     diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+        "cannot have both ON and USING clauses in "\
+        "the same join");
+     pParse->is_aborted = true;
      return 1;
    }
 
@@ -637,6 +646,8 @@ sqlProcessJoin(Parse * pParse, Select * p)
     * not contained in both tables to be joined.
     */
    if (pRight->pUsing) {
+     const char *err = "cannot join using column %s - "\
+           "column not present in both tables";
      IdList *pList = pRight->pUsing;
      for (j = 0; j < pList->nId; j++) {
        char *zName;  /* Name of the term in the USING clause */
@@ -650,10 +661,10 @@ sqlProcessJoin(Parse * pParse, Select * p)
            || !tableAndColumnIndex(pSrc, i + 1, zName,
                  &iLeft, &iLeftCol)
            ) {
-         sqlErrorMsg(pParse,
-             "cannot join using column %s - column "
-             "not present in both tables",
-             zName);
+         err = tt_sprintf(err, zName);
+         diag_set(ClientError,
+            ER_SQL_PARSER_GENERIC, err);
+         pParse->is_aborted = true;
          return 1;
        }
        addWhereTerm(pParse, pSrc, iLeft, iLeftCol,
@@ -2601,16 +2612,20 @@ multiSelect(Parse * pParse, /* Parsing context */
  pPrior = p->pPrior;
  dest = *pDest;
  if (pPrior->pOrderBy) {
-   sqlErrorMsg(pParse,
-       "ORDER BY clause should come after %s not before",
-       selectOpName(p->op));
+   const char *err_msg =
+     tt_sprintf("ORDER BY clause should come after %s not "\
+          "before", selectOpName(p->op));
+   diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+   pParse->is_aborted = true;
    rc = 1;
    goto multi_select_end;
  }
  if (pPrior->pLimit) {
-   sqlErrorMsg(pParse,
-       "LIMIT clause should come after %s not before",
-       selectOpName(p->op));
+   const char *err_msg =
+     tt_sprintf("LIMIT clause should come after %s not "\
+          "before", selectOpName(p->op));
+   diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+   pParse->is_aborted = true;
    rc = 1;
    goto multi_select_end;
  }
@@ -2988,19 +3003,6 @@ multiSelect(Parse * pParse,  /* Parsing context */
 }
 #endif       /* SQL_OMIT_COMPOUND_SELECT */
 
-void
-sqlSelectWrongNumTermsError(struct Parse *parse, struct Select * p)
-{
- if (p->selFlags & SF_Values) {
-   sqlErrorMsg(parse, "all VALUES must have the same number "\
-       "of terms");
- } else {
-   sqlErrorMsg(parse, "SELECTs to the left and right of %s "
-       "do not have the same number of result columns",
-       selectOpName(p->op));
- }
-}
-
 /**
  * Code an output subroutine for a coroutine implementation of a
  * SELECT statment.
@@ -3622,7 +3624,19 @@ substExpr(Parse * pParse,  /* Report errors here */
      assert(pEList != 0 && pExpr->iColumn < pEList->nExpr);
      assert(pExpr->pLeft == 0 && pExpr->pRight == 0);
      if (sqlExprIsVector(pCopy)) {
-       sqlVectorErrorMsg(pParse, pCopy);
+       const char *err;
+       if (pCopy->flags & EP_xIsSelect) {
+         err = "sub-select returns %d columns "\
+               "- expected 1";
+         int expr_count =
+           pCopy->x.pSelect->pEList->nExpr;
+         err = tt_sprintf(err, expr_count);
+       } else {
+         err = "row value misused";
+       }
+       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+          err);
+       pParse->is_aborted = true;
      } else {
        pNew = sqlExprDup(db, pCopy, 0);
        if (pNew && (pExpr->flags & EP_FromJoin)) {
@@ -4518,21 +4532,6 @@ convertCompoundSelectToSubquery(Walker * pWalker, Select * p)
  return WRC_Continue;
 }
 
-/*
- * Check to see if the FROM clause term pFrom has table-valued function
- * arguments.  If it does, leave an error message in pParse and return
- * non-zero, since pFrom is not allowed to be a table-valued function.
- */
-static int
-cannotBeFunction(Parse * pParse, struct SrcList_item *pFrom)
-{
- if (pFrom->fg.isTabFunc) {
-   sqlErrorMsg(pParse, "'%s' is not a function", pFrom->zName);
-   return 1;
- }
- return 0;
-}
-
 #ifndef SQL_OMIT_CTE
 /*
  * Argument pWith (which may be NULL) points to a linked list of nested
@@ -4627,11 +4626,18 @@ withExpand(Walker * pWalker, struct SrcList_item *pFrom)
     * In this case, proceed.
     */
    if (pCte->zCteErr) {
-     sqlErrorMsg(pParse, pCte->zCteErr, pCte->zName);
+     diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+        tt_sprintf(pCte->zCteErr, pCte->zName));
+     pParse->is_aborted = true;
      return SQL_ERROR;
    }
-   if (cannotBeFunction(pParse, pFrom))
+   if (pFrom->fg.isTabFunc) {
+     const char *err = "'%s' is not a function";
+     diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+        tt_sprintf(err, pFrom->zName));
+     pParse->is_aborted = true;
      return SQL_ERROR;
+   }
 
    assert(pFrom->space == NULL);
    pFrom->space = sql_ephemeral_space_new(pParse, pCte->zName);
@@ -4663,9 +4669,11 @@ withExpand(Walker * pWalker, struct SrcList_item *pFrom)
      }
    }
    if (ref_counter > 1) {
-     sqlErrorMsg(pParse,
-         "multiple references to recursive table: %s",
-         pCte->zName);
+     const char *err_msg =
+       tt_sprintf("multiple references to recursive "\
+            "table: %s", pCte->zName);
+     diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+     pParse->is_aborted = true;
      return SQL_ERROR;
    }
    assert(ref_counter == 0 ||
@@ -4681,10 +4689,13 @@ withExpand(Walker * pWalker, struct SrcList_item *pFrom)
    pEList = pLeft->pEList;
    if (pCte->pCols) {
      if (pEList && pEList->nExpr != pCte->pCols->nExpr) {
-       sqlErrorMsg(pParse,
-           "table %s has %d values for %d columns",
-           pCte->zName, pEList->nExpr,
-           pCte->pCols->nExpr);
+       const char *err_msg =
+         tt_sprintf("table %s has %d values "\
+              "for %d columns",
+              pCte->zName, pEList->nExpr,
+              pCte->pCols->nExpr);
+       diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+       pParse->is_aborted = true;
        pParse->pWith = pSavedWith;
        return SQL_ERROR;
      }
@@ -4840,8 +4851,15 @@ selectExpander(Walker * pWalker, Select * p)
      struct space *space = sql_lookup_space(pParse, pFrom);
      if (space == NULL)
        return WRC_Abort;
-     if (cannotBeFunction(pParse, pFrom))
+     if (pFrom->fg.isTabFunc) {
+       const char *err =
+         tt_sprintf("'%s' is not a function",
+              pFrom->zName);
+       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+          err);
+       pParse->is_aborted = true;
        return WRC_Abort;
+     }
      if (space->def->opts.is_view) {
        struct Select *select =
          sql_view_compile(db, space->def->opts.sql);
@@ -5252,9 +5270,10 @@ resetAccumulator(Parse * pParse, AggInfo * pAggInfo)
      Expr *pE = pFunc->pExpr;
      assert(!ExprHasProperty(pE, EP_xIsSelect));
      if (pE->x.pList == 0 || pE->x.pList->nExpr != 1) {
-       sqlErrorMsg(pParse,
-           "DISTINCT aggregates must have exactly one "
-           "argument");
+       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+          "DISTINCT aggregates must have "\
+          "exactly one argument");
+       pParse->is_aborted = true;
        pFunc->iDistinct = -1;
      } else {
        struct sql_key_info *key_info =
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 1e49d5b..67a0fb0 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -3194,7 +3194,6 @@ void sqlTreeViewWith(TreeView *, const With *);
 #endif
 
 void sqlSetString(char **, sql *, const char *);
-void sqlErrorMsg(Parse *, const char *, ...);
 void sqlDequote(char *);
 void sqlNormalizeName(char *z);
 void sqlTokenInit(Token *, char *);
@@ -4410,16 +4409,6 @@ void sqlExpirePreparedStatements(sql *);
 int sqlCodeSubselect(Parse *, Expr *, int);
 void sqlSelectPrep(Parse *, Select *, NameContext *);
 
-/**
- * Error message for when two or more terms of a compound select
- * have different size result sets.
- *
- * @param parse Parsing context.
- * @param p Select struct to analyze.
- */
-void
-sqlSelectWrongNumTermsError(struct Parse *parse, struct Select *p);
-
 int sqlMatchSpanName(const char *, const char *, const char *);
 int sqlResolveExprNames(NameContext *, Expr *);
 int sqlResolveExprListNames(NameContext *, ExprList *);
@@ -4868,7 +4857,6 @@ int sqlExprVectorSize(Expr * pExpr);
 int sqlExprIsVector(Expr * pExpr);
 Expr *sqlVectorFieldSubexpr(Expr *, int);
 Expr *sqlExprForVectorField(Parse *, Expr *, int);
-void sqlVectorErrorMsg(Parse *, Expr *);
 
 /* Tarantool: right now query compilation is invoked on top of
  * fiber's stack. Need to limit number of nested programs under
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index 2f1268a..9f06e53 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -621,8 +621,9 @@ codeTriggerProgram(Parse * pParse,  /* The parser context */
  sqlSubProgramsRemaining--;
 
  if (sqlSubProgramsRemaining == 0) {
-   sqlErrorMsg(pParse,
-       "Maximum number of chained trigger activations exceeded.");
+   diag_set(ClientError, ER_SQL_PARSER_GENERIC, "Maximum number "\
+      "of chained trigger activations exceeded.");
+   pParse->is_aborted = true;
  }
 
  for (pStep = pStepList; pStep; pStep = pStep->pNext) {
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index bc0ab66..ee3b3b5 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -179,10 +179,15 @@ sqlUpdate(Parse * pParse,   /* The parser context */
            sql_space_column_is_in_pk(space, j))
          is_pk_modified = true;
        if (aXRef[j] != -1) {
-         sqlErrorMsg(pParse,
-             "set id list: duplicate"
-             " column name %s",
-             pChanges->a[i].zName);
+         const char *err =
+           "set id list: duplicate "\
+           "column name %s";
+         err = tt_sprintf(err,
+              pChanges->a[i].zName);
+         diag_set(ClientError,
+            ER_SQL_PARSER_GENERIC,
+            err);
+         pParse->is_aborted = true;
          goto update_cleanup;
        }
        aXRef[j] = i;
diff --git a/src/box/sql/util.c b/src/box/sql/util.c
index d9bb2af..cac404f 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -211,38 +211,6 @@ sqlErrorWithMsg(sql * db, int err_code, const char *zFormat, ...)
 }
 
 /*
- * Add an error to the diagnostics area and set
- * pParse->is_aborted.
- * The following formatting characters are allowed:
- *
- *      %s      Insert a string
- *      %z      A string that should be freed after use
- *      %d      Insert an integer
- *      %T      Insert a token
- *      %S      Insert the first element of a SrcList
- *
- * This function should be used to report any error that occurs while
- * compiling an SQL statement (i.e. within sql_prepare()). The
- * last thing the sql_prepare() function does is copy the error
- * stored by this function into the database handle using sqlError().
- * Functions sqlError() or sqlErrorWithMsg() should be used
- * during statement execution (sql_step() etc.).
- */
-void
-sqlErrorMsg(Parse * pParse, const char *zFormat, ...)
-{
- char *zMsg;
- va_list ap;
- sql *db = pParse->db;
- va_start(ap, zFormat);
- zMsg = sqlVMPrintf(db, zFormat, ap);
- va_end(ap);
- diag_set(ClientError, ER_SQL_PARSER_GENERIC, zMsg);
- sqlDbFree(db, zMsg);
- pParse->is_aborted = true;
-}
-
-/*
  * Convert an SQL-style quoted string into a normal string by removing
  * the quote characters.  The conversion is done in-place.  If the
  * input does not begin with a quote character, then this routine
diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
index f417c49..9f0dee4 100644
--- a/src/box/sql/vdbemem.c
+++ b/src/box/sql/vdbemem.c
@@ -1210,13 +1210,14 @@ valueFromFunction(sql * db, /* The database connection */
  pFunc->xSFunc(&ctx, nVal, apVal);
  if (ctx.isError) {
    rc = ctx.isError;
-   sqlErrorMsg(pCtx->pParse, "%s", sql_value_text(pVal));
+   const char *err = "Error in function '%s': %s";
+   err = tt_sprintf(err, pFunc->zName, sql_value_text(pVal));
+   diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+   pCtx->pParse->is_aborted = true;
  } else {
    sql_value_apply_type(pVal, type);
    assert(rc == SQL_OK);
  }
- if (rc != SQL_OK)
-   pCtx->pParse->is_aborted = true;
 
  value_from_function_out:
  if (rc != SQL_OK) {
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index 33885d0..1e8c2e0 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -3914,7 +3914,9 @@ wherePathSolver(WhereInfo * pWInfo, LogEst nRowEst)
  }
 
  if (nFrom == 0) {
-   sqlErrorMsg(pParse, "no query solution");
+   diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+      "no query solution");
+   pParse->is_aborted = true;
    sqlDbFree(db, pSpace);
    return SQL_ERROR;
  }
diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c
index 6df28ad..bc0964c 100644
--- a/src/box/sql/whereexpr.c
+++ b/src/box/sql/whereexpr.c
@@ -1497,9 +1497,11 @@ sqlWhereTabFuncArgs(Parse * pParse,  /* Parsing context */
    while (k < (int)space_def->field_count)
      k++;
    if (k >= (int)space_def->field_count) {
-     sqlErrorMsg(pParse,
-         "too many arguments on %s() - max %d",
-         space_def->name, j);
+     const char *err =
+       tt_sprintf("too many arguments on %s() - max "\
+            "%d", space_def->name, j);
+     diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+     pParse->is_aborted = true;
      return;
    }
    pColRef = sqlExprAlloc(pParse->db, TK_COLUMN, 0, 0);
diff --git a/test/sql-tap/e_select1.test.lua b/test/sql-tap/e_select1.test.lua
index e190ad7..8e9a2bb 100755
--- a/test/sql-tap/e_select1.test.lua
+++ b/test/sql-tap/e_select1.test.lua
@@ -1855,13 +1855,13 @@ test:do_catchsql_test(
     "e_select-8.7.1.1",
     "SELECT x FROM d1 UNION ALL SELECT a FROM d2 ORDER BY x*z",
     {
-        1, "1st ORDER BY term does not match any column in the result set"})
+        1, "Error at ORDER BY in place 1: term does not match any column in the result set"})
 
 test:do_catchsql_test(
     "e_select-8.7.1.2",
     "SELECT x,z FROM d1 UNION ALL SELECT a,b FROM d2 ORDER BY x, x/z",
     {
-        1, "2nd ORDER BY term does not match any column in the result set"})
+        1, "Error at ORDER BY in place 2: term does not match any column in the result set"})
 
 test:do_select_tests(
     "e_select-8.7.2",
@@ -2077,12 +2077,12 @@ test:do_select_tests(
 -- EVIDENCE-OF: R-39265-04070 If no matching expression can be found in
 -- the result columns of any constituent SELECT, it is an error.
 --
-for _, val in ipairs({{1, "SELECT a FROM d5 UNION SELECT c FROM d6 ORDER BY a+1", "1st"},
-    {2, "SELECT a FROM d5 UNION SELECT c FROM d6 ORDER BY a, a+1", "2nd"},
-    {3, "SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY \"hello\"", "1st"},
-    {4, "SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY blah", "1st"},
-    {5, "SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY c,d,c+d", "3rd"},
-    {6, "SELECT * FROM d5 EXCEPT SELECT * FROM d7 ORDER BY 1,2,b,a/b", "4th"}}) do
+for _, val in ipairs({{1, "SELECT a FROM d5 UNION SELECT c FROM d6 ORDER BY a+1", "1"},
+    {2, "SELECT a FROM d5 UNION SELECT c FROM d6 ORDER BY a, a+1", "2"},
+    {3, "SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY \"hello\"", "1"},
+    {4, "SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY blah", "1"},
+    {5, "SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY c,d,c+d", "3"},
+    {6, "SELECT * FROM d5 EXCEPT SELECT * FROM d7 ORDER BY 1,2,b,a/b", "4"}}) do
     local tn = val[1]
     local select = val[2]
     local err_param = val[3]
@@ -2090,7 +2090,7 @@ for _, val in ipairs({{1, "SELECT a FROM d5 UNION SELECT c FROM d6 ORDER BY a+1"
         "e_select-8.14."..tn,
         select,
         {
-            1, string.format("%s ORDER BY term does not match any column in the result set", err_param)})
+            1, string.format("Error at ORDER BY in place %s: term does not match any column in the result set", err_param)})
 end
 -- EVIDENCE-OF: R-03407-11483 Each term of the ORDER BY clause is
 -- processed separately and may be matched against result columns from
diff --git a/test/sql-tap/null.test.lua b/test/sql-tap/null.test.lua
index 50a2cfb..de4d503 100755
--- a/test/sql-tap/null.test.lua
+++ b/test/sql-tap/null.test.lua
@@ -295,7 +295,7 @@ test:do_catchsql_test(
         select b from t1 union select c from t1 order by t1.a;
     ]], {
         -- <null-6.5>
-        1, "1st ORDER BY term does not match any column in the result set"
+        1, "Error at ORDER BY in place 1: term does not match any column in the result set"
         -- </null-6.5>
     })
 
@@ -305,7 +305,7 @@ test:do_catchsql_test(
         select b from t1 union select c from t1 order by t1.a;
     ]], {
         -- <null-6.6>
-        1, "1st ORDER BY term does not match any column in the result set"
+        1, "Error at ORDER BY in place 1: term does not match any column in the result set"
         -- </null-6.6>
     })
 
diff --git a/test/sql-tap/select1.test.lua b/test/sql-tap/select1.test.lua
index d73429a..545ae9d 100755
--- a/test/sql-tap/select1.test.lua
+++ b/test/sql-tap/select1.test.lua
@@ -840,7 +840,7 @@ test:do_catchsql_test(
         SELECT * FROM t5 ORDER BY 3;
     ]], {
         -- <select1-4.10.1>
-        1, "1st ORDER BY term out of range - should be between 1 and 2"
+        1, "Error at ORDER BY in place 1: term out of range - should be between 1 and 2"
         -- </select1-4.10.1>
     })
 
@@ -850,7 +850,7 @@ test:do_catchsql_test(
         SELECT * FROM t5 ORDER BY -1;
     ]], {
         -- <select1-4.10.2>
-        1, "1st ORDER BY term out of range - should be between 1 and 2"
+        1, "Error at ORDER BY in place 1: term out of range - should be between 1 and 2"
         -- </select1-4.10.2>
     })
 
@@ -1334,7 +1334,7 @@ test:do_catchsql2_test(
             ORDER BY f2+101;
         ]], {
             -- <select1-6.11>
-            1, "1st ORDER BY term does not match any column in the result set"
+            1, "Error at ORDER BY in place 1: term does not match any column in the result set"
             -- </select1-6.11>
         })
 
diff --git a/test/sql-tap/select3.test.lua b/test/sql-tap/select3.test.lua
index 9fb825f..3c88f77 100755
--- a/test/sql-tap/select3.test.lua
+++ b/test/sql-tap/select3.test.lua
@@ -157,7 +157,7 @@ test:do_catchsql_test("select3-2.10", [[
   SELECT log, count(*) FROM t1 GROUP BY 0 ORDER BY log;
 ]], {
   -- <select3-2.10>
-  1, "1st GROUP BY term out of range - should be between 1 and 2"
+  1, "Error at GROUP BY in place 1: term out of range - should be between 1 and 2"
   -- </select3-2.10>
 })
 
@@ -165,7 +165,7 @@ test:do_catchsql_test("select3-2.11", [[
   SELECT log, count(*) FROM t1 GROUP BY 3 ORDER BY log;
 ]], {
   -- <select3-2.11>
-  1, "1st GROUP BY term out of range - should be between 1 and 2"
+  1, "Error at GROUP BY in place 1: term out of range - should be between 1 and 2"
   -- </select3-2.11>
 })
 
diff --git a/test/sql-tap/select4.test.lua b/test/sql-tap/select4.test.lua
index bd2ada9..3aafedb 100755
--- a/test/sql-tap/select4.test.lua
+++ b/test/sql-tap/select4.test.lua
@@ -450,7 +450,7 @@ test:do_catchsql_test(
         ORDER BY "xyzzy";
     ]], {
         -- <select4-5.2c>
-        1, "1st ORDER BY term does not match any column in the result set"
+        1, "Error at ORDER BY in place 1: term does not match any column in the result set"
         -- </select4-5.2c>
     })
 
@@ -463,7 +463,7 @@ test:do_catchsql_test(
         ORDER BY "xyzzy";
     ]], {
         -- <select4-5.2d>
-        1, "1st ORDER BY term does not match any column in the result set"
+        1, "Error at ORDER BY in place 1: term does not match any column in the result set"
         -- </select4-5.2d>
     })
 
@@ -515,7 +515,7 @@ test:do_catchsql_test(
         ORDER BY 2;
     ]], {
         -- <select4-5.2h>
-        1, "1st ORDER BY term out of range - should be between 1 and 1"
+        1, "Error at ORDER BY in place 1: term out of range - should be between 1 and 1"
         -- </select4-5.2h>
     })
 
diff --git a/test/sql-tap/sql-errors.test.lua b/test/sql-tap/sql-errors.test.lua
index 4a92af6..4caa44f 100755
--- a/test/sql-tap/sql-errors.test.lua
+++ b/test/sql-tap/sql-errors.test.lua
@@ -1,9 +1,9 @@
 #!/usr/bin/env tarantool
 test = require("sqltester")
-test:plan(36)
+test:plan(40)
 
 test:execsql([[
- CREATE TABLE t0 (i INT PRIMARY KEY);
+ CREATE TABLE t0 (i INT PRIMARY KEY, a INT);
  CREATE VIEW v0 AS SELECT * FROM t0;
 ]])
 format = {}
@@ -79,7 +79,7 @@ test:do_catchsql_test(
 test:do_catchsql_test(
  "sql-errors-1.7",
  [[
-   CREATE VIEW v7(a,b) AS SELECT * FROM t0;
+   CREATE VIEW v7(a,b,c) AS SELECT * FROM t0;
  ]], {
    -- <sql-errors-1.7>
    1,"Failed to create space 'V7': number of aliases doesn't match provided columns"
@@ -416,4 +416,44 @@ test:do_catchsql_test(
    -- </sql-errors-1.36>
  })
 
+test:do_catchsql_test(
+ "sql-errors-1.37",
+ [[
+   CREATE TRIGGER r0 AFTER INSERT ON t0 BEGIN INSERT INTO t0.i VALUES (2); END;
+ ]], {
+   -- <sql-errors-1.37>
+   1,"qualified table names are not allowed on INSERT, UPDATE, and DELETE statements within triggers"
+   -- </sql-errors-1.37>
+ })
+
+test:do_catchsql_test(
+ "sql-errors-1.38",
+ [[
+   UPDATE t0 SET (i, a) = (100,1,1);
+ ]], {
+   -- <sql-errors-1.38>
+   1,"2 columns assigned 3 values"
+   -- </sql-errors-1.38>
+ })
+
+test:do_catchsql_test(
+ "sql-errors-1.39",
+ [[
+   SELECT * FROM t0();
+ ]], {
+   -- <sql-errors-1.39>
+   1,"'T0' is not a function"
+   -- </sql-errors-1.39>
+ })
+
+test:do_catchsql_test(
+ "sql-errors-1.40",
+ [[
+   SELECT $0;
+ ]], {
+   -- <sql-errors-1.40>
+   1,"Index of binding slots must start from 1"
+   -- </sql-errors-1.40>
+ })
+
 test:finish_test()
diff --git a/test/sql-tap/tkt2822.test.lua b/test/sql-tap/tkt2822.test.lua
index 4212cbd..86674ae 100755
--- a/test/sql-tap/tkt2822.test.lua
+++ b/test/sql-tap/tkt2822.test.lua
@@ -200,7 +200,7 @@ test:do_catchsql_test(
         SELECT a, b, c FROM t1 UNION ALL SELECT a, b, c FROM t2 ORDER BY x
     ]], {
         -- <tkt2822-4.1>
-        1, "1st ORDER BY term does not match any column in the result set"
+        1, "Error at ORDER BY in place 1: term does not match any column in the result set"
         -- </tkt2822-4.1>
     })
 
@@ -298,7 +298,7 @@ test:do_test(
         ]]
     end, {
         -- <tkt2822-7.1>
-        1, "1st ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 1: term out of range - should be between 1 and 25"
         -- </tkt2822-7.1>
     })
 
@@ -308,7 +308,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 0;
     ]], {
         -- <tkt2822-7.2.1>
-        1, "2nd ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 2: term out of range - should be between 1 and 25"
         -- </tkt2822-7.2.1>
     })
 
@@ -318,7 +318,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 26;
     ]], {
         -- <tkt2822-7.2.2>
-        1, "2nd ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 2: term out of range - should be between 1 and 25"
         -- </tkt2822-7.2.2>
     })
 
@@ -328,7 +328,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 65536;
     ]], {
         -- <tkt2822-7.2.3>
-        1, "2nd ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 2: term out of range - should be between 1 and 25"
         -- </tkt2822-7.2.3>
     })
 
@@ -338,7 +338,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 2, 0;
     ]], {
         -- <tkt2822-7.3>
-        1, "3rd ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 3: term out of range - should be between 1 and 25"
         -- </tkt2822-7.3>
     })
 
@@ -348,7 +348,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 2, 3, 0;
     ]], {
         -- <tkt2822-7.4>
-        1, "4th ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 4: term out of range - should be between 1 and 25"
         -- </tkt2822-7.4>
     })
 
@@ -358,7 +358,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 0;
     ]], {
         -- <tkt2822-7.9>
-        1, "9th ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 9: term out of range - should be between 1 and 25"
         -- </tkt2822-7.9>
     })
 
@@ -368,7 +368,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 0;
     ]], {
         -- <tkt2822-7.10>
-        1, "10th ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 10: term out of range - should be between 1 and 25"
         -- </tkt2822-7.10>
     })
 
@@ -378,7 +378,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0;
     ]], {
         -- <tkt2822-7.11>
-        1, "11th ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 11: term out of range - should be between 1 and 25"
         -- </tkt2822-7.11>
     })
 
@@ -388,7 +388,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 0;
     ]], {
         -- <tkt2822-7.12>
-        1, "12th ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 12: term out of range - should be between 1 and 25"
         -- </tkt2822-7.12>
     })
 
@@ -398,7 +398,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 0;
     ]], {
         -- <tkt2822-7.13>
-        1, "13th ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 13: term out of range - should be between 1 and 25"
         -- </tkt2822-7.13>
     })
 
@@ -409,7 +409,7 @@ test:do_catchsql_test(
                                  11,12,13,14,15,16,17,18,19, 0
     ]], {
         -- <tkt2822-7.20>
-        1, "20th ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 20: term out of range - should be between 1 and 25"
         -- </tkt2822-7.20>
     })
 
@@ -420,7 +420,7 @@ test:do_catchsql_test(
                                  11,12,13,14,15,16,17,18,19, 20, 0
     ]], {
         -- <tkt2822-7.21>
-        1, "21st ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 21: term out of range - should be between 1 and 25"
         -- </tkt2822-7.21>
     })
 
@@ -431,7 +431,7 @@ test:do_catchsql_test(
                                  11,12,13,14,15,16,17,18,19, 20, 21, 0
     ]], {
         -- <tkt2822-7.22>
-        1, "22nd ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 22: term out of range - should be between 1 and 25"
         -- </tkt2822-7.22>
     })
 
diff --git a/test/sql-tap/with1.test.lua b/test/sql-tap/with1.test.lua
index 16c9b12..f1a1699 100755
--- a/test/sql-tap/with1.test.lua
+++ b/test/sql-tap/with1.test.lua
@@ -134,7 +134,7 @@ test:do_catchsql_test(3.2, [[
   SELECT * FROM tmp;
 ]], {
   -- <3.2>
-  1, "duplicate WITH table name: TMP"
+  1, "Ambiguous table name in WITH query: TMP"
   -- </3.2>
 })
 
@@ -782,7 +782,7 @@ test:do_catchsql_test("10.7.1", [[
   SELECT * FROM t
 ]], {
   -- <10.7.1>
-  1, "1st ORDER BY term does not match any column in the result set"
+  1, "Error at ORDER BY in place 1: term does not match any column in the result set"
   -- </10.7.1>
 })
 
diff --git a/test/sql/iproto.result b/test/sql/iproto.result
index 56099fa..48376c8 100644
--- a/test/sql/iproto.result
+++ b/test/sql/iproto.result
@@ -571,7 +571,8 @@ cn:execute('select ?1, ?2, ?3', {1, 2, 3})
 ...
 cn:execute('select $name, $name2', {1, 2})
 ---
-- error: 'Failed to execute SQL statement: SQL bind parameter limit reached: 65000'
+- error: 'Failed to execute SQL statement: Index of binding slots must start from
+    1'
 ...
 parameters = {}
 ---
-- 
2.7.4

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 1/8] sql: rework syntax errors
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 1/8] sql: rework syntax errors imeevma
@ 2019-03-14 18:24   ` n.pettik
  2019-03-14 18:28     ` Imeev Mergen
  2019-03-15 14:09   ` Kirill Yukhin
  1 sibling, 1 reply; 43+ messages in thread
From: n.pettik @ 2019-03-14 18:24 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen



> On 13 Mar 2019, at 20:03, imeevma@tarantool.org wrote:
> 
> This patch reworks SQL syntax errors. After this patch, these
> error will be set as Tarantool errors.
> 
> Part of #3965

Please, don’t send again patches which are already OK.
Both me and Konstantin looked at it, ping smb to push it.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 1/8] sql: rework syntax errors
  2019-03-14 18:24   ` [tarantool-patches] " n.pettik
@ 2019-03-14 18:28     ` Imeev Mergen
  0 siblings, 0 replies; 43+ messages in thread
From: Imeev Mergen @ 2019-03-14 18:28 UTC (permalink / raw)
  To: Kirill Yukhin; +Cc: tarantool-patches, n.pettik

[-- Attachment #1: Type: text/plain, Size: 420 bytes --]

Hi! Could you look at this patch and push it if everything is
fine?

On 3/14/19 9:24 PM, n.pettik wrote:
>
>> On 13 Mar 2019, at 20:03, imeevma@tarantool.org wrote:
>>
>> This patch reworks SQL syntax errors. After this patch, these
>> error will be set as Tarantool errors.
>>
>> Part of #3965
> Please, don’t send again patches which are already OK.
> Both me and Konstantin looked at it, ping smb to push it.
>
>
>

[-- Attachment #2: Type: text/html, Size: 1113 bytes --]

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 2/8] sql: set SQL parser errors via diag_set()
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 2/8] sql: set SQL parser errors via diag_set() imeevma
@ 2019-03-14 19:26   ` n.pettik
  2019-03-14 19:36     ` n.pettik
  2019-03-18 15:06     ` Mergen Imeev
  2019-03-19 11:24   ` Kirill Yukhin
  1 sibling, 2 replies; 43+ messages in thread
From: n.pettik @ 2019-03-14 19:26 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen



> On 13 Mar 2019, at 20:03, imeevma@tarantool.org wrote:
> 
> Hi! Thank you for review! Diff between versions and new version of
> patch below.
> 
> Diff between patches:
> 
> commit 61bc67e61298129d66a436d58957bb411b6c9b81
> Author: Mergen Imeev <imeevma@gmail.com>
> Date:   Wed Mar 6 21:27:51 2019 +0300
> 
>    Temporary: Review fix
> 
> diff --git a/src/box/sql/malloc.c b/src/box/sql/malloc.c
> index e0d2ec8..8812298 100644
> --- a/src/box/sql/malloc.c
> +++ b/src/box/sql/malloc.c
> @@ -55,9 +55,7 @@ sql_sized_malloc(int nByte)
>    p[0] = nByte;
>    p++;
>  } else {
> -   testcase(sqlGlobalConfig.xLog != 0);
> -   sql_log(SQL_NOMEM,
> -         "failed to allocate %u bytes of memory", nByte);
> +   diag_set(OutOfMemory, nByte, "realloc", "p”);

This function doesn’t set mallocFailed flag. A lot of callers
of this function don’d check its return value. So I guess this
could result in installed diag error, but it would be ignored.
Can we set here at least mallocFailed?

>  }
>  return (void *)p;
> }
> @@ -115,10 +113,7 @@ sql_sized_realloc(void *pPrior, int nByte)
>    p[0] = nByte;
>    p++;
>  } else {
> -   testcase(sqlGlobalConfig.xLog != 0);
> -   sql_log(SQL_NOMEM,
> -         "failed memory resize %u to %u bytes",
> -         sql_sized_sizeof(pPrior), nByte);
> +   diag_set(OutOfMemory, nByte, "malloc", "p”);

The same is here.

>  }
>  return (void *)p;
> }
> diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
> index 0c6296d..828a1ae 100644
> --- a/src/box/sql/prepare.c
> +++ b/src/box/sql/prepare.c
> @@ -102,9 +102,8 @@ sqlPrepare(sql * db,  /* Database handle. */
> 
>  if (sParse.rc == SQL_DONE)
>    sParse.rc = SQL_OK;
> - if (db->mallocFailed) {
> -   sParse.rc = SQL_NOMEM;
> - }
> + if (db->mallocFailed)
> +   sParse.rc = SQL_TARANTOOL_ERROR;
>  if (pzTail) {
>    *pzTail = sParse.zTail;
>  }
> diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
> index 58685c4..834c165 100644
> --- a/src/box/sql/tokenize.c
> +++ b/src/box/sql/tokenize.c
> @@ -483,7 +483,9 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
>              &pParse->sLastToken.isReserved);
>      i += pParse->sLastToken.n;
>      if (i > mxSqlLen) {
> -       pParse->rc = SQL_TOOBIG;
> +       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> +          "string or blob too big”);

I would add to error message max possible length.

> +       pParse->rc = SQL_TARANTOOL_ERROR;
>        break;
>      }
>    } else {
> @@ -502,7 +504,9 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
>      assert(tokenType == TK_SPACE
>             || tokenType == TK_ILLEGAL);
>      if (db->u1.isInterrupted) {
> -       pParse->rc = SQL_INTERRUPT;
> +       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> +          "interrupted”);

What does it mean? AFAIR it is dead code (i.e. everything
connected with “interrupt”).

> +       pParse->rc = SQL_TARANTOOL_ERROR;
>        break;
>      }
>      if (tokenType == TK_ILLEGAL) {

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 2/8] sql: set SQL parser errors via diag_set()
  2019-03-14 19:26   ` [tarantool-patches] " n.pettik
@ 2019-03-14 19:36     ` n.pettik
  2019-03-18 15:06     ` Mergen Imeev
  1 sibling, 0 replies; 43+ messages in thread
From: n.pettik @ 2019-03-14 19:36 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen

Almost forgot. Apply this small diff please.
You confused malloc with realloc in diag_set().
And it improves readability of code.

diff --git a/src/box/sql/malloc.c b/src/box/sql/malloc.c
index 8812298e0..83ab23589 100644
--- a/src/box/sql/malloc.c
+++ b/src/box/sql/malloc.c
@@ -51,12 +51,12 @@ sql_sized_malloc(int nByte)
        assert(nByte > 0);
        nByte = ROUND8(nByte);
        p = malloc(nByte + 8);
-       if (p) {
-               p[0] = nByte;
-               p++;
-       } else {
-               diag_set(OutOfMemory, nByte, "realloc", "p");
+       if (p == NULL) {
+               diag_set(OutOfMemory, nByte, "malloc", "p");
+               return NULL;
        }
+       p[0] = nByte;
+       p++;
        return (void *)p;
 }
 
@@ -109,12 +109,12 @@ sql_sized_realloc(void *pPrior, int nByte)
        assert(nByte == ROUND8(nByte)); /* EV: R-46199-30249 */
        p--;
        p = realloc(p, nByte + 8);
-       if (p) {
-               p[0] = nByte;
-               p++;
-       } else {
-               diag_set(OutOfMemory, nByte, "malloc", "p");
+       if (p == NULL) {
+               diag_set(OutOfMemory, nByte, "realloc", "p");
+               return NULL;
        }
+       p[0] = nByte;
+       p++;
        return (void *)p;
 }

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 3/8] sql: replace rc with is_aborted status in struct Parse
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 3/8] sql: replace rc with is_aborted status in struct Parse imeevma
@ 2019-03-14 19:53   ` n.pettik
  2019-03-18 15:28     ` Mergen Imeev
  2019-03-19 13:17   ` Kirill Yukhin
  1 sibling, 1 reply; 43+ messages in thread
From: n.pettik @ 2019-03-14 19:53 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen


>>> diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
>>> index bab6493..0d8bf15 100755
>>> --- a/test/sql-tap/check.test.lua
>>> +++ b/test/sql-tap/check.test.lua
>>> @@ -516,7 +516,7 @@ test:do_catchsql_test(
>>>        );
>>>    ]], {
>>>        -- <check-5.1>
>>> -        1, "Wrong space options (field 5): invalid expression specified (SQL error: bindings are not allowed in DDL)"
>>> +        1, "Wrong space options (field 5): invalid expression specified (bindings are not allowed in DDL)"
>>>        -- </check-5.1>
>>>    })
>> 
>> Why test results have changed if you provided
>> non-functional refactoring?
> It become this way because now the error in diag instead of being
> only in zErrMsg of struct Parse.

So, then it should be related to the previous patch, I guess.
Otherwise, still don’t understand. Or fix commit message,
since now it implies that only refactoring was provided.
Or, what is better - move functional changes to separate patch.

> index a2937a0..c2e5d6b 100644
> --- a/src/box/sql.c
> +++ b/src/box/sql.c
> @@ -1363,12 +1363,8 @@ sql_checks_resolve_space_def_reference(ExprList *expr_list,
> 
> 	sql_resolve_self_reference(&parser, def, NC_IsCheck, NULL, expr_list);
> 	int rc = 0;
> -	if (parser.rc != SQL_OK) {
> -		/* Tarantool error may be already set with diag. */
> -		if (parser.rc != SQL_TARANTOOL_ERROR)
> -			diag_set(ClientError, ER_SQL, parser.zErrMsg);
> +	if (parser.is_aborted)
> 		rc = -1;
> -	}
> 	sql_parser_destroy(&parser);
> 	return rc;

diff --git a/src/box/sql.c b/src/box/sql.c
index c2e5d6bd1..ea71dd101 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -1362,9 +1362,6 @@ sql_checks_resolve_space_def_reference(ExprList *expr_list,
        parser.parse_only = true;
 
        sql_resolve_self_reference(&parser, def, NC_IsCheck, NULL, expr_list);
-       int rc = 0;
-       if (parser.is_aborted)
-               rc = -1;
        sql_parser_destroy(&parser);
-       return rc;
+       return parser.is_aborted ? -1 : 0;
 }

> }
> 
> diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
> index 828a1ae..85385ee 100644
> --- a/src/box/sql/prepare.c
> +++ b/src/box/sql/prepare.c
> @@ -52,7 +52,6 @@ sqlPrepare(sql * db,	/* Database handle. */
> 	       const char **pzTail	/* OUT: End of parsed string */
>     )
> {
> -	char *zErrMsg = 0;	/* Error message */
> 	int rc = SQL_OK;	/* Result code */
> 	Parse sParse;		/* Parsing context */
> 	sql_parser_create(&sParse, db);
> @@ -89,25 +88,24 @@ sqlPrepare(sql * db,	/* Database handle. */
> 		}
> 		zSqlCopy = sqlDbStrNDup(db, zSql, nBytes);
> 		if (zSqlCopy) {
> -			sqlRunParser(&sParse, zSqlCopy, &zErrMsg);
> +			sqlRunParser(&sParse, zSqlCopy);
> 			sParse.zTail = &zSql[sParse.zTail - zSqlCopy];
> 			sqlDbFree(db, zSqlCopy);
> 		} else {
> 			sParse.zTail = &zSql[nBytes];
> 		}
> 	} else {
> -		sqlRunParser(&sParse, zSql, &zErrMsg);
> +		sqlRunParser(&sParse, zSql);
> 	}
> 	assert(0 == sParse.nQueryLoop);
> 
> -	if (sParse.rc == SQL_DONE)
> -		sParse.rc = SQL_OK;
> 	if (db->mallocFailed)
> -		sParse.rc = SQL_TARANTOOL_ERROR;
> +		sParse.is_aborted = true;
> 	if (pzTail) {
> 		*pzTail = sParse.zTail;
> 	}
> -	rc = sParse.rc;
> +	if (sParse.is_aborted)
> +		rc = SQL_TARANTOOL_ERROR;
> 
> 	if (rc == SQL_OK && sParse.pVdbe && sParse.explain) {
> 		static const char *const azColName[] = {
> @@ -168,11 +166,7 @@ sqlPrepare(sql * db,	/* Database handle. */
> 		*ppStmt = (sql_stmt *) sParse.pVdbe;
> 	}
> 
> -	if (zErrMsg) {
> -		sqlErrorWithMsg(db, rc, "%s", zErrMsg);
> -	} else {
> -		sqlError(db, rc);
> -	}
> +	db->errCode = rc;

Is this thing used anywhere?

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 4/8] sql: remove field nErr from struct Parse
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 4/8] sql: remove field nErr from " imeevma
@ 2019-03-14 19:58   ` n.pettik
  2019-03-19 13:27   ` Kirill Yukhin
  1 sibling, 0 replies; 43+ messages in thread
From: n.pettik @ 2019-03-14 19:58 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen

This one is OK. As soon as minor comments in previous
patches are fixed, it can be pushed.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 6/8] sql: rework three errors of "unsupported" type
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 6/8] sql: rework three errors of "unsupported" type imeevma
@ 2019-03-14 22:15   ` n.pettik
  2019-03-19 13:30   ` Kirill Yukhin
  1 sibling, 0 replies; 43+ messages in thread
From: n.pettik @ 2019-03-14 22:15 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen

LGTM.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 5/8] sql: remove field zErrMsg from struct Parse
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 5/8] sql: remove field zErrMsg " imeevma
@ 2019-03-14 22:15   ` n.pettik
  2019-03-19 13:20   ` Kirill Yukhin
  1 sibling, 0 replies; 43+ messages in thread
From: n.pettik @ 2019-03-14 22:15 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen

LGTM.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 8/8] sql: remove sqlErrorMsg()
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 8/8] sql: remove sqlErrorMsg() imeevma
@ 2019-03-15 13:36   ` n.pettik
  2019-03-25 18:47     ` Mergen Imeev
  2019-03-27  6:49   ` Kirill Yukhin
  1 sibling, 1 reply; 43+ messages in thread
From: n.pettik @ 2019-03-15 13:36 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen


>>> +    parse->is_aborted = true;
>>>   goto exit_create_index;
>>> }
>>> /*
>>> @@ -2947,11 +2976,8 @@ sqlSavepoint(Parse * pParse, int op, Token * pName)
>>>     return;
>>>   }
>>>   if (op == SAVEPOINT_BEGIN &&
>>> -      sqlCheckIdentifierName(pParse, zName)
>>> -        != SQL_OK) {
>>> -      sqlErrorMsg(pParse, "bad savepoint name");
>>> +      sqlCheckIdentifierName(pParse, zName) != SQL_OK)
>> 
>> != 0
> Function sqlCheckIdentifierName() still works in terms of
> {SQL_OK, SQL_ERROR}, so I left this part as it was.

Under the hood SQL_OK is 0. We are going to remove
this status codes, so let's fix it now.

>>> diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
>>> index 85718e1..5a74c65 100644
>>> --- a/src/box/sql/sqlInt.h
>>> +++ b/src/box/sql/sqlInt.h
>>> @@ -3182,7 +3182,6 @@ void sqlTreeViewWith(TreeView *, const With *);
>>> #endif
>>> 
>>> void sqlSetString(char **, sql *, const char *);
>>> -void sqlErrorMsg(Parse *, const char *, ...);
>>> void sqlDequote(char *);
>>> void sqlNormalizeName(char *z);
>>> void sqlTokenInit(Token *, char *);
>>> diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
>>> index 2f1268a..297ba01 100644
>>> --- a/src/box/sql/trigger.c
>>> +++ b/src/box/sql/trigger.c
>>> @@ -621,8 +621,11 @@ codeTriggerProgram(Parse * pParse,  /* The parser context */
>>> sqlSubProgramsRemaining--;
>>> 
>>> if (sqlSubProgramsRemaining == 0) {
>>> -    sqlErrorMsg(pParse,
>>> -        "Maximum number of chained trigger activations exceeded.");
>>> +    const char *err_msg = tt_sprintf("Maximum number of chained "\
>>> +             "trigger activations "\
>>> +             "exceeded.”);
>> 
>> Please, make sure that it is valid error. And the rest of errors
>> I pointed in google doc file. If this is unreachable code, then
>> replace error with assert.
> This error is valid. As for the other ones - I couldn't reach some
> of them, but didn't remove them for now.

Why? Comment code and place assertion instead.
Otherwise, provide solid argument to avoid doing that.

>>> -/*
>>> * Convert an SQL-style quoted string into a normal string by removing
>>> * the quote characters.  The conversion is done in-place.  If the
>>> * input does not begin with a quote character, then this routine
>>> diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
>>> index 9957f3a..d4fbf17 100644
>>> --- a/src/box/sql/vdbemem.c
>>> +++ b/src/box/sql/vdbemem.c
>>> @@ -1222,7 +1222,9 @@ valueFromFunction(sql * db,  /* The database connection */
>>> pFunc->xSFunc(&ctx, nVal, apVal);
>>> if (ctx.isError) {
>>>   rc = ctx.isError;
>>> -    sqlErrorMsg(pCtx->pParse, "%s", sql_value_text(pVal));
>>> +    diag_set(ClientError, ER_SQL_PARSER_GENERIC,
>>> +       sql_value_text(pVal));
>> 
>> What kind of error is it? Please, add reasonable error message,
>> not only text representation of value.
> I did, but not sure if this error is reachable. I wasn't able to
> reproduce it.

See comment above.

>>> +    pCtx->pParse->is_aborted = true;
>>> } else {
>>>   sql_value_apply_type(pVal, type);
>>>   assert(rc == SQL_OK);
>>> diff --git a/src/box/sql/where.c b/src/box/sql/where.c
>>> index 33885d0..e2c91e0 100644
>>> --- a/src/box/sql/where.c
>>> +++ b/src/box/sql/where.c
>>> @@ -3914,7 +3914,9 @@ wherePathSolver(WhereInfo * pWInfo, LogEst nRowEst)
>>> }
>>> 
>>> if (nFrom == 0) {
>>> -    sqlErrorMsg(pParse, "no query solution");
>>> +    const char *err_msg = tt_sprintf("no query solution”);
>> 
>> Same: this path seems to be unreachable.
> I think so too, but left as it is for now.

The same.

> New version:
> 
> commit 99f4adc61352899a83a7097b965845bafaa968ef
> Author: Mergen Imeev <imeevma@gmail.com>
> Date:   Sat Mar 9 18:54:19 2019 +0300
> 
>    sql: remove sqlErrorMsg()
> 
>    This patch completely replaces sqlErrorMsg() with diag_set() and
>    removes sqlErrorMsg().
> 
>    Closes #3965
>    Closes #3036

I wouldn’t close both issues since there’s still place to work on:
split generic error into specific ones, use diag for vdbe.

> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index de06ee0..7205cb0 100644
> --- a/src/box/sql/expr.c
> +++ b/src/box/sql/expr.c
> @@ -668,7 +668,9 @@ codeVectorCompare(Parse * pParse, /* Code generator context */
>  int addrDone = sqlVdbeMakeLabel(v);
> 
>  if (nLeft != sqlExprVectorSize(pRight)) {
> -   sqlErrorMsg(pParse, "row value misused");
> +   diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> +      "row value misused”);

I see a lot of “row value misused” errors in source code,
but only one test case on this error.

> @@ -2959,15 +2935,26 @@ int
> sqlExprCheckIN(Parse * pParse, Expr * pIn)
> {
>  int nVector = sqlExprVectorSize(pIn->pLeft);
> + const char *err;
>  if ((pIn->flags & EP_xIsSelect)) {
>    if (nVector != pIn->x.pSelect->pEList->nExpr) {
> -     sqlSubselectError(pParse,
> -               pIn->x.pSelect->pEList->nExpr,
> -               nVector);
> +     err = "sub-select returns %d columns - expected %d";
> +     int expr_count = pIn->x.pSelect->pEList->nExpr;
> +     err = tt_sprintf(err, expr_count, nVector);
> +     diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
> +     pParse->is_aborted = true;
>      return 1;
>    }
>  } else if (nVector != 1) {
> -   sqlVectorErrorMsg(pParse, pIn->pLeft);
> +   if (pIn->pLeft->flags & EP_xIsSelect) {
> +     err = "sub-select returns %d columns - expected 1";
> +     int expr_count = pIn->pLeft->x.pSelect->pEList->nExpr;
> +     err = tt_sprintf(err, expr_count);
> +   } else {
> +     err = "row value misused”;

A bit shorter version. Please apply this and other diffs:

diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 7205cb0a8..344e1ab9a 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -2940,16 +2940,16 @@ sqlExprCheckIN(Parse * pParse, Expr * pIn)
                if (nVector != pIn->x.pSelect->pEList->nExpr) {
                        err = "sub-select returns %d columns - expected %d";
                        int expr_count = pIn->x.pSelect->pEList->nExpr;
-                       err = tt_sprintf(err, expr_count, nVector);
-                       diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+                       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+                                tt_sprintf(err, expr_count, nVector););
                        pParse->is_aborted = true;
                        return 1;
                }
        } else if (nVector != 1) {
                if (pIn->pLeft->flags & EP_xIsSelect) {
-                       err = "sub-select returns %d columns - expected 1";
                        int expr_count = pIn->pLeft->x.pSelect->pEList->nExpr;
-                       err = tt_sprintf(err, expr_count);
+                       err = tt_sprintf("sub-select returns %d columns - "\
+                                        "expected 1", expr_count);

> +   }
> +   diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
> +   pParse->is_aborted = true;
>    return 1;
>  }
>  return 0;
> @@ -4134,7 +4122,12 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
>      testcase(op == TK_SELECT);
>      if (op == TK_SELECT
>          && (nCol = pExpr->x.pSelect->pEList->nExpr) != 1) {
> -       sqlSubselectError(pParse, nCol, 1);
> +       const char *err = "sub-select returns %d "\
> +             "columns - expected 1";
> +       err = tt_sprintf(err, nCol);

@@ -4124,9 +4124,8 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
                            && (nCol = pExpr->x.pSelect->pEList->nExpr) != 1) {
                                const char *err = "sub-select returns %d "\
                                                  "columns - expected 1";
-                               err = tt_sprintf(err, nCol);
                                diag_set(ClientError, ER_SQL_PARSER_GENERIC,
-                                        err);
+                                        tt_sprintf(err, nCol););

> @@ -510,14 +511,19 @@ sqlInsert(Parse * pParse, /* Parser context */
> 
>  if (pColumn != 0 && nColumn != pColumn->nId) {
> -   sqlErrorMsg(pParse, "%d values for %d columns", nColumn,
> -       pColumn->nId);
> +   const char *err = "%d values for %d columns";
> +   err = tt_sprintf(err, nColumn, pColumn->nId);
> +   diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);

diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 7635f7b69..6f30ab37e 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -521,8 +521,8 @@ sqlInsert(Parse * pParse,   /* Parser context */
        }
        if (pColumn != 0 && nColumn != pColumn->nId) {
                const char *err = "%d values for %d columns";
-               err = tt_sprintf(err, nColumn, pColumn->nId);
-               diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+               diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+                        tt_sprintf(err, nColumn, pColumn->nId));
                pParse->is_aborted = true;
                goto insert_cleanup;

> +   pParse->is_aborted = true;
>    goto insert_cleanup;
>  }
> 
> 
> // Disallow the INDEX BY and NOT INDEXED clauses on UPDATE and DELETE
> diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
> index 9171d05..6720914 100644
> --- a/src/box/sql/resolve.c
> +++ b/src/box/sql/resolve.c
> @@ -390,16 +390,22 @@ lookupName(Parse * pParse,  /* The parsing context */
>          assert(pExpr->x.pList == 0);
>          assert(pExpr->x.pSelect == 0);
>          pOrig = pEList->a[j].pExpr;
> +         const char *err = "misuse of aliased "\
> +               "aggregate %s";
>          if ((pNC->ncFlags & NC_AllowAgg) == 0
>              && ExprHasProperty(pOrig, EP_Agg)) {
> -           sqlErrorMsg(pParse,
> -               "misuse of aliased aggregate %s",
> -               zAs);
> +           err = tt_sprintf(err, zAs);
> +           diag_set(ClientError,
> +              ER_SQL_PARSER_GENERIC,
> +              err);

diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 672091465..30f2b1f5a 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -394,10 +394,9 @@ lookupName(Parse * pParse, /* The parsing context */
                                                          "aggregate %s";
                                        if ((pNC->ncFlags & NC_AllowAgg) == 0
                                            && ExprHasProperty(pOrig, EP_Agg)) {
-                                               err = tt_sprintf(err, zAs);
                                                diag_set(ClientError,
                                                         ER_SQL_PARSER_GENERIC,
-                                                        err);
+                                                        tt_sprintf(err, zAs));
                                                pParse->is_aborted = true;

> @@ -697,9 +708,12 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
>        pParse->is_aborted = true;
>        pNC->nErr++;
>      } else if (wrong_num_args) {
> -       sqlErrorMsg(pParse,
> -           "wrong number of arguments to function %.*s()",
> -           nId, zId);
> +       const char *err = "wrong number of arguments "\
> +             "to function %.*s()";
> +       err = tt_sprintf(err, nId, zId);
> +       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> +          err);

@@ -710,9 +709,8 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
                        } else if (wrong_num_args) {
                                const char *err = "wrong number of arguments "\
                                                  "to function %.*s()";
-                               err = tt_sprintf(err, nId, zId);
                                diag_set(ClientError, ER_SQL_PARSER_GENERIC,
-                                        err);
+                                        tt_sprintf(err, nId, zId));
                                pParse->is_aborted = true;
                                pNC->nErr++;

> @@ -980,9 +981,16 @@ resolveCompoundOrderBy(Parse * pParse, /* Parsing context.  Leave error messages
>      pE = sqlExprSkipCollate(pItem->pExpr);
>      if (sqlExprIsInteger(pE, &iCol)) {
>        if (iCol <= 0 || iCol > pEList->nExpr) {
> -         resolveOutOfRangeError(pParse, "ORDER",
> -                    i + 1,
> -                    pEList->nExpr);
> +         const char *err =
> +           "Error at ORDER BY in place "\
> +           "%d: term out of range - "\
> +           "should be between 1 and %d";
> +         err = tt_sprintf(err, i + 1,
> +              pEList->nExpr);
> +         diag_set(ClientError,
> +            ER_SQL_PARSER_GENERIC,
> +            err);

@@ -988,8 +986,7 @@ resolveCompoundOrderBy(Parse * pParse,      /* Parsing context.  Leave error messages
                                        err = tt_sprintf(err, i + 1,
                                                         pEList->nExpr);
                                        diag_set(ClientError,
-                                                ER_SQL_PARSER_GENERIC,
-                                                err);
+                                                ER_SQL_PARSER_GENERIC, err);
                                        pParse->is_aborted = true;
                                        return 1;

> @@ -1028,9 +1036,12 @@ resolveCompoundOrderBy(Parse * pParse, /* Parsing context.  Leave error messages
>  }
>  for (i = 0; i < pOrderBy->nExpr; i++) {
>    if (pOrderBy->a[i].done == 0) {
> -     sqlErrorMsg(pParse,
> -         "%r ORDER BY term does not match any "
> -         "column in the result set", i + 1);
> +     const char *err = "Error at ORDER BY in place %d: "\
> +           "term does not match any column in "\
> +           "the result set";
> +     err = tt_sprintf(err, i + 1);
> +     diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);

@@ -1039,8 +1036,8 @@ resolveCompoundOrderBy(Parse * pParse,    /* Parsing context.  Leave error messages
                        const char *err = "Error at ORDER BY in place %d: "\
                                          "term does not match any column in "\
                                          "the result set";
-                       err = tt_sprintf(err, i + 1);
-                       diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+                       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+                                tt_sprintf(err, i + 1));

> @@ -1444,10 +1469,37 @@ resolveSelectStep(Walker * pWalker, Select * p)
>     * number of expressions in the select list.
>     */
>    if (p->pNext && p->pEList->nExpr != p->pNext->pEList->nExpr) {
> -     sqlSelectWrongNumTermsError(pParse, p->pNext);
> +     if (p->pNext->selFlags & SF_Values) {
> +       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> +          "all VALUES must have the same "\
> +          "number of terms");
> +     } else {
> +       const char *err_msg =
> +         "SELECTs to the left and right of %s "\
> +         "do not have the same number of "\
> +         "result columns";
> +       switch (p->pNext->op) {
> +       case TK_ALL:
> +         err_msg = tt_sprintf(err_msg,
> +                  "UNION ALL");
> +         break;

Why don’t use selectOpName?
Like it was in  sqlSelectWrongNumTermsError().

> +       case TK_INTERSECT:
> +         err_msg = tt_sprintf(err_msg,
> +                  "INTERSECT");
> +         break;
> +       case TK_EXCEPT:
> +         err_msg = tt_sprintf(err_msg, "EXCEPT");
> +         break;
> +       default:
> +         err_msg = tt_sprintf(err_msg, "UNION");
> +         break;
> +       }
> +       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> +          err_msg);
> +     }
> +     pParse->is_aborted = true;
>      return WRC_Abort;
> 
> diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
> index f417c49..9f0dee4 100644
> --- a/src/box/sql/vdbemem.c
> +++ b/src/box/sql/vdbemem.c
> @@ -1210,13 +1210,14 @@ valueFromFunction(sql * db, /* The database connection */
>  pFunc->xSFunc(&ctx, nVal, apVal);
>  if (ctx.isError) {
>    rc = ctx.isError;
> -   sqlErrorMsg(pCtx->pParse, "%s", sql_value_text(pVal));
> +   const char *err = "Error in function '%s': %s”;

It doesn’t look better than previous variant. If you don’t
know what kind of error this is and how it can appear -
just replace it with assertion.

> +   err = tt_sprintf(err, pFunc->zName, sql_value_text(pVal));
> +   diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
> +   pCtx->pParse->is_aborted = true;
>  } else {
>    sql_value_apply_type(pVal, type);
>    assert(rc == SQL_OK);
>  }
> - if (rc != SQL_OK)
> -   pCtx->pParse->is_aborted = true;
> 
>  value_from_function_out:
>  if (rc != SQL_OK) {
> diff --git a/src/box/sql/where.c b/src/box/sql/where.c
> index 33885d0..1e8c2e0 100644
> --- a/src/box/sql/where.c
> +++ b/src/box/sql/where.c
> @@ -3914,7 +3914,9 @@ wherePathSolver(WhereInfo * pWInfo, LogEst nRowEst)
>  }
> 
>  if (nFrom == 0) {
> -   sqlErrorMsg(pParse, "no query solution");
> +   diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> +      "no query solution”);

As I already said in previous review - replace with assertion
and comment.

> +   pParse->is_aborted = true;
>    sqlDbFree(db, pSpace);
>    return SQL_ERROR;
>  }

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 1/8] sql: rework syntax errors
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 1/8] sql: rework syntax errors imeevma
  2019-03-14 18:24   ` [tarantool-patches] " n.pettik
@ 2019-03-15 14:09   ` Kirill Yukhin
  1 sibling, 0 replies; 43+ messages in thread
From: Kirill Yukhin @ 2019-03-15 14:09 UTC (permalink / raw)
  To: tarantool-patches; +Cc: korablev

Hello,

On 13 Mar 20:03, imeevma@tarantool.org wrote:
> This patch reworks SQL syntax errors. After this patch, these
> error will be set as Tarantool errors.
> 
> Part of #3965

I've checked your patch into 2.1 branch.

--
Regards, Kirill Yukhin

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 7/8] sql: rework semantic errors
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 7/8] sql: rework semantic errors imeevma
@ 2019-03-15 15:49   ` n.pettik
  2019-03-22 12:48     ` Mergen Imeev
  2019-03-27  6:48   ` Kirill Yukhin
  1 sibling, 1 reply; 43+ messages in thread
From: n.pettik @ 2019-03-15 15:49 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen


> diff --git a/src/box/errcode.h b/src/box/errcode.h
> index 7764aa3..a0d8f4ae 100644
> --- a/src/box/errcode.h
> +++ b/src/box/errcode.h
> @@ -122,7 +122,7 @@ struct errcode_record {
> 	/* 67 */_(ER_REPLICA_ID_IS_RESERVED,	"Can't initialize replica id with a reserved value %u") \
> 	/* 68 */_(ER_INVALID_ORDER,		"Invalid LSN order for instance %u: previous LSN = %llu, new lsn = %llu") \
> 	/* 69 */_(ER_MISSING_REQUEST_FIELD,	"Missing mandatory field '%s' in request") \
> -	/* 70 */_(ER_IDENTIFIER,		"Invalid identifier '%s' (expected printable symbols only)") \
> +	/* 70 */_(ER_IDENTIFIER,		"Invalid identifier '%s' (expected printable symbols only or it is too long)") \
> 	/* 71 */_(ER_DROP_FUNCTION,		"Can't drop function %u: %s") \
> 	/* 72 */_(ER_ITERATOR_TYPE,		"Unknown iterator type '%s'") \
> 	/* 73 */_(ER_REPLICA_MAX,		"Replica count limit reached: %u") \
> @@ -240,6 +240,13 @@ struct errcode_record {
> 	/*185 */_(ER_SQL_UNKNOWN_TOKEN,		"Syntax error: unrecognized token: '%.*s'") \
> 	/*186 */_(ER_SQL_PARSER_GENERIC,	"%s") \
> 	/*187 */_(ER_SQL_ANALYZE_ARGUMENT,	"ANALYZE statement argument %s is not a base table") \
> +	/*188 */_(ER_SQL_COLUMN_COUNT_MAX,	"Failed to create space '%s': space column count %d exceeds the limit (%d)") \
> +	/*189 */_(ER_HEX_LITERAL_MAX,		"Hex literal %s%s length %d exceeds the supported limit (%d)") \
> +	/*190 */_(ER_INT_LITERAL_MAX,		"Integer literal %s%s exceeds the supported range %lld - %lld") \
> +	/*191 */_(ER_SQL_PARSER_LIMIT,		"%s%.*s %d exceeds the limit (%d)") \

You use auxiliary params only in one place. I guess it is worth
removing them.

> +	/*192 */_(ER_INDEX_DEF,			"%s prohibited in an index definition") \

Why not ER_UNSUPPORTED?
ER_INDEX_DEF -> ER_INDEX_DEF_UNSUPPORTED

Also, quite unnatural message pattern: it is hard to
understand diags like:

diag_set(ClientError, ER_INDEX_DEF,
        "Parameter markers are");

> +	/*193 */_(ER_CHECK_CONSTRAINT_DEF,	"%s prohibited in a CHECK constraint definition") \

The same question.
ER_CHECK_CONSTRAINT_DEF -> ER_CK_DEF_UNSUPPORTED

> +	/*194 */_(ER_PRIMARY_KEY_DEF,		"Expressions are prohibited in a primary key definition") \

Just substitute it with ER_INDEX_DEF_UNSUPPORTED:
expressions are prohibited in secondary index definition as well.

> 
> /*
>  * !IMPORTANT! Please follow instructions at start of the file
> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
> index 0c06555..26434b1 100644
> --- a/src/box/sql/build.c
> +++ b/src/box/sql/build.c
> @@ -267,14 +267,14 @@ int
> sqlCheckIdentifierName(Parse *pParse, char *zName)
> {
> 	ssize_t len = strlen(zName);
> -
> -	if (len > BOX_NAME_MAX || identifier_check(zName, len) != 0) {
> -		sqlErrorMsg(pParse,
> -				"identifier name is invalid: %s",
> -				zName);
> -		return SQL_ERROR;
> +	if (len <= BOX_NAME_MAX && identifier_check(zName, len) == 0)
> +		return SQL_OK;

return 0;

> +	if (len > BOX_NAME_MAX) {
> +		diag_set(ClientError, ER_IDENTIFIER,
> +			 tt_cstr(zName, BOX_INVALID_NAME_MAX));
> 	}
> -	return SQL_OK;
> +	pParse->is_aborted = true;
> +	return SQL_ERROR;

return -1;

        ssize_t len = strlen(zName);
-       if (len <= BOX_NAME_MAX && identifier_check(zName, len) == 0)
-               return SQL_OK;
        if (len > BOX_NAME_MAX) {
                diag_set(ClientError, ER_IDENTIFIER,
                         tt_cstr(zName, BOX_INVALID_NAME_MAX));
+               pParse->is_aborted = true;
+               return -1;
        }
-       pParse->is_aborted = true;
-       return SQL_ERROR;
+       if (identifier_check(zName, len) != 0) {
+               pParse->is_aborted = true;
+               return -1;
+       }
+       return 0;

> @@ -588,9 +590,9 @@ sqlAddPrimaryKey(Parse * pParse,	/* Parsing context */
> 	if (space == NULL)
> 		goto primary_key_exit;
> 	if (sql_space_primary_key(space) != NULL) {
> -		sqlErrorMsg(pParse,
> -				"table \"%s\" has more than one primary key",
> -				space->def->name);
> +		diag_set(ClientError, ER_CREATE_SPACE, space->def->name,
> +			 "too many primary keys”);

too many primary keys -> primary key already exists

> +		pParse->is_aborted = true;
> 		goto primary_key_exit;
> 	}
> 	if (pList == NULL) {
> 
> @@ -1775,6 +1778,11 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child,
> 	}
> 	if (constraint_name == NULL)
> 		goto exit_create_fk;
> +	if (!is_self_referenced && parent_space->def->opts.is_view) {
> +		diag_set(ClientError, ER_CREATE_FK_CONSTRAINT, constraint_name,
> +			"referenced space can't be VIEW");
> +		goto tnt_error;
> +	}

This check is already processed in on_replace_dd_fk_constraint()
Could you remove this check to avoid code duplication?

> @@ -2942,11 +2958,8 @@ sqlSavepoint(Parse * pParse, int op, Token * pName)
> 			return;
> 		}
> 		if (op == SAVEPOINT_BEGIN &&
> -			sqlCheckIdentifierName(pParse, zName)
> -				!= SQL_OK) {
> -			sqlErrorMsg(pParse, "bad savepoint name");
> +		    sqlCheckIdentifierName(pParse, zName) != SQL_OK)

!= 0

> diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
> index 87d4ed4..e8a5f0d 100644
> --- a/src/box/sql/delete.c
> +++ b/src/box/sql/delete.c
> @@ -157,8 +157,9 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
> 			goto delete_from_cleanup;
> 
> 		if (trigger_list == NULL) {
> -			sqlErrorMsg(parse, "cannot modify %s because it is a"
> -					" view", space->def->name);
> +			diag_set(ClientError, ER_ALTER_SPACE, space->def->name,
> +				 "it is a view”);

Nit: it is a view -> space is a view (sounds better IMHO).

> +			parse->is_aborted = true;
> 			goto delete_from_cleanup;
> 		}
> 	}
> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index 4193596..de06ee0 100644
> --- a/src/box/sql/expr.c
> +++ b/src/box/sql/expr.c
> @@ -750,15 +750,14 @@ codeVectorCompare(Parse * pParse,	/* Code generator context */
> int
> sqlExprCheckHeight(Parse * pParse, int nHeight)
> {
> -	int rc = SQL_OK;
> 	int mxHeight = pParse->db->aLimit[SQL_LIMIT_EXPR_DEPTH];
> 	if (nHeight > mxHeight) {
> -		sqlErrorMsg(pParse,
> -				"Expression tree is too large (maximum depth %d)",
> -				mxHeight);
> -		rc = SQL_ERROR;
> +		diag_set(ClientError, ER_SQL_PARSER_LIMIT, "Number of nodes "\
> +			 "in expression tree", 0, "", nHeight, mxHeight);
> +		pParse->is_aborted = true;
> +		return SQL_ERROR;

return -1;

> 	}
> -	return rc;
> +	return SQL_OK;

return 0;

> 
> @@ -690,9 +674,12 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
> 					 * that might change over time cannot be used
> 					 * in an index.
> 					 */
> -					notValid(pParse, pNC,
> -						 "non-deterministic functions",
> -						 NC_IdxExpr);
> +					if (pNC->ncFlags & NC_IdxExpr) {
> +						diag_set(ClientError, ER_INDEX_DEF,
> +							 "Non-deterministic functions "\
> +							 "are”);

This code seems to be unreachable: functional indexes
are filtered a way earlier.

> @@ -754,8 +741,16 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
> 			testcase(pExpr->op == TK_IN);
> 			if (ExprHasProperty(pExpr, EP_xIsSelect)) {
> 				int nRef = pNC->nRef;
> -				notValid(pParse, pNC, "subqueries",
> -					 NC_IsCheck | NC_IdxExpr);
> +				if (pNC->ncFlags & NC_IdxExpr) {
> +					diag_set(ClientError, ER_INDEX_DEF,
> +						 "Subqueries are”);

The same: managed to grep this error only for ck constraint.

> @@ -1042,27 +1044,34 @@ resolveCompoundOrderBy(Parse * pParse,	/* Parsing context.  Leave error messages
>  * field) then convert that term into a copy of the corresponding result set
>  * column.
>  *
> - * If any errors are detected, add an error message to pParse and
> - * return non-zero.  Return zero if no errors are seen.
> + * @param pParse Parsing context.
> + * @param pSelect The SELECT statement containing the clause.
> + * @param pOrderBy The ORDER BY or GROUP BY clause to be
> + *                 processed.
> + * @param is_order_by True if pOrderBy is ORDER BY, false if
> + *                    pOrderBy is GROUP BY
> + * @retval 0 On success, not 0 elsewhere.
>  */
> int
> -sqlResolveOrderGroupBy(Parse * pParse,	/* Parsing context.  Leave error messages here */
> -			   Select * pSelect,	/* The SELECT statement containing the clause */
> -			   ExprList * pOrderBy,	/* The ORDER BY or GROUP BY clause to be processed */
> -			   const char *zType	/* "ORDER" or "GROUP" */
> -    )
> +sqlResolveOrderGroupBy(Parse *pParse, Select *pSelect, ExprList *pOrderBy,
> +		       bool is_order_by)

Why did you decide to fix code style here? Anyway, you didn't finished it
(struct prefixes, param naming and so on and so forth)

> {
> 	int i;
> 	sql *db = pParse->db;
> 	ExprList *pEList;
> 	struct ExprList_item *pItem;
> +	const char *zType = is_order_by ? "ORDER" : "GROUP";
> 
> 	if (pOrderBy == 0 || pParse->db->mallocFailed)
> 		return 0;
> 
> @@ -1096,22 +1105,23 @@ sqlResolveOrderGroupBy(Parse * pParse,	/* Parsing context.  Leave error messages
>  * result-set expression.  Otherwise, the expression is resolved in
>  * the usual way - using sqlResolveExprNames().
>  *
> - * This routine returns the number of errors.  If errors occur, then
> - * an appropriate error message might be left in pParse.  (OOM errors
> - * excepted.)
> + * @param pNC The name context of the SELECT statement.
> + * @param pSelect The SELECT statement containing the clause.
> + * @param pOrderBy An ORDER BY or GROUP BY clause to resolve.
> + * @param is_order_by True if pOrderBy is ORDER BY, false if
> + *                    pOrderBy is GROUP BY
> + * @retval 0 On success, not 0 elsewhere.
>  */
> static int
> -resolveOrderGroupBy(NameContext * pNC,	/* The name context of the SELECT statement */
> -		    Select * pSelect,	/* The SELECT statement holding pOrderBy */
> -		    ExprList * pOrderBy,	/* An ORDER BY or GROUP BY clause to resolve */
> -		    const char *zType	/* Either "ORDER" or "GROUP", as appropriate */
> -    )

The same question.

> +resolveOrderGroupBy(NameContext *pNC, Select *pSelect, ExprList *pOrderBy,
> +		    bool is_order_by)
> {
> 	int i, j;		/* Loop counters */
> 	int iCol;		/* Column number */
> 	struct ExprList_item *pItem;	/* A term of the ORDER BY clause */
> 	Parse *pParse;		/* Parsing context */
> 	int nResult;		/* Number of terms in the result set */
> +	const char *zType = is_order_by ? "ORDER" : "GROUP";
> 
> 	if (pOrderBy == 0)
> 		return 0;
> 
> 	/* Compute the limit registers */
> 	computeLimitRegisters(pParse, p, labelEnd);
> diff --git a/src/box/sql/update.c b/src/box/sql/update.c
> index 05ceeb4..bc0ab66 100644
> --- a/src/box/sql/update.c
> +++ b/src/box/sql/update.c
> @@ -139,8 +139,9 @@ sqlUpdate(Parse * pParse,		/* The parser context */
> 		goto update_cleanup;
> 	}
> 	if (is_view && tmask == 0) {
> -		sqlErrorMsg(pParse, "cannot modify %s because it is a view",
> -				space->def->name);
> +		diag_set(ClientError, ER_ALTER_SPACE, space->def->name,
> +			 "it is a view”);

-> space is a view

> +		pParse->is_aborted = true;
> 		goto update_cleanup;
> 	}
> 
> 
> diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
> index 0d8bf15..dd2de92 100755
> --- a/test/sql-tap/check.test.lua
> +++ b/test/sql-tap/check.test.lua
> @@ -319,7 +319,7 @@ test:do_catchsql_test(
>         );
>     ]], {
>         -- <check-3.1>
> -        1, "Failed to create space 'T3': subqueries prohibited in CHECK constraints"
> +        1, "Failed to create space 'T3': Subqueries are prohibited in a CHECK constraint definition”

Don’t use capital letter after ‘:’, it would allow you to avoid fixing tests.
The same is applied to the fixes below.

>         -- </check-3.1>
>     })
> 
> diff --git a/test/sql-tap/colname.test.lua b/test/sql-tap/colname.test.lua
> index 29fdf13..77c4280 100755
> --- a/test/sql-tap/colname.test.lua
> +++ b/test/sql-tap/colname.test.lua
> @@ -637,7 +637,7 @@ test:do_test(
> test:do_catchsql_test(
>     "colname-11.1",
>     [[ create table t1(a INT, b INT, c INT, primary key('A'))]],
> -    {1, "expressions prohibited in PRIMARY KEY"})
> +    {1, "Expressions are prohibited in a primary key definition"})
> 
> test:do_catchsql_test(
>     "colname-11.2",
> diff --git a/test/sql-tap/default.test.lua b/test/sql-tap/default.test.lua
> index 67151b0..fbe285b 100755
> --- a/test/sql-tap/default.test.lua
> +++ b/test/sql-tap/default.test.lua
> @@ -66,7 +66,7 @@ test:do_catchsql_test(
> 	);
> 	]], {
> 	-- <default-1.3>
> -	1, "default value of column [Y] is not constant"
> +	1, "Failed to create space 'T3': default value of column is not constant”

Is it possible to print also name of column (like in original version)?

> diff --git a/test/sql-tap/engine.cfg b/test/sql-tap/engine.cfg
> index 413dd3f..cc56592 100644
> --- a/test/sql-tap/engine.cfg
> +++ b/test/sql-tap/engine.cfg
> @@ -11,6 +11,9 @@
>     "sort.test.lua": {
>         "memtx": {"engine": "memtx"}
>     },
> +    "sql-errors.test.lua": {
> +        "memtx": {"engine": "memtx"}
> +    },

I would run this test both for vinyl and memtx.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 2/8] sql: set SQL parser errors via diag_set()
  2019-03-14 19:26   ` [tarantool-patches] " n.pettik
  2019-03-14 19:36     ` n.pettik
@ 2019-03-18 15:06     ` Mergen Imeev
  2019-03-19  9:41       ` n.pettik
  1 sibling, 1 reply; 43+ messages in thread
From: Mergen Imeev @ 2019-03-18 15:06 UTC (permalink / raw)
  To: n.pettik; +Cc: tarantool-patches

On Thu, Mar 14, 2019 at 10:26:16PM +0300, n.pettik wrote:
> 
> 
> > On 13 Mar 2019, at 20:03, imeevma@tarantool.org wrote:
> > 
> > Hi! Thank you for review! Diff between versions and new version of
> > patch below.
> > 
> > Diff between patches:
> > 
> > commit 61bc67e61298129d66a436d58957bb411b6c9b81
> > Author: Mergen Imeev <imeevma@gmail.com>
> > Date:   Wed Mar 6 21:27:51 2019 +0300
> > 
> >    Temporary: Review fix
> > 
> > diff --git a/src/box/sql/malloc.c b/src/box/sql/malloc.c
> > index e0d2ec8..8812298 100644
> > --- a/src/box/sql/malloc.c
> > +++ b/src/box/sql/malloc.c
> > @@ -55,9 +55,7 @@ sql_sized_malloc(int nByte)
> >    p[0] = nByte;
> >    p++;
> >  } else {
> > -   testcase(sqlGlobalConfig.xLog != 0);
> > -   sql_log(SQL_NOMEM,
> > -         "failed to allocate %u bytes of memory", nByte);
> > +   diag_set(OutOfMemory, nByte, "realloc", "p”);
> 
> This function doesn’t set mallocFailed flag. A lot of callers
> of this function don’d check its return value. So I guess this
> could result in installed diag error, but it would be ignored.
> Can we set here at least mallocFailed?
> 
Fixed.

> >  }
> >  return (void *)p;
> > }
> > @@ -115,10 +113,7 @@ sql_sized_realloc(void *pPrior, int nByte)
> >    p[0] = nByte;
> >    p++;
> >  } else {
> > -   testcase(sqlGlobalConfig.xLog != 0);
> > -   sql_log(SQL_NOMEM,
> > -         "failed memory resize %u to %u bytes",
> > -         sql_sized_sizeof(pPrior), nByte);
> > +   diag_set(OutOfMemory, nByte, "malloc", "p”);
> 
> The same is here.
> 
Fixed.

> >  }
> >  return (void *)p;
> > }
> > diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
> > index 0c6296d..828a1ae 100644
> > --- a/src/box/sql/prepare.c
> > +++ b/src/box/sql/prepare.c
> > @@ -102,9 +102,8 @@ sqlPrepare(sql * db,  /* Database handle. */
> > 
> >  if (sParse.rc == SQL_DONE)
> >    sParse.rc = SQL_OK;
> > - if (db->mallocFailed) {
> > -   sParse.rc = SQL_NOMEM;
> > - }
> > + if (db->mallocFailed)
> > +   sParse.rc = SQL_TARANTOOL_ERROR;
> >  if (pzTail) {
> >    *pzTail = sParse.zTail;
> >  }
> > diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
> > index 58685c4..834c165 100644
> > --- a/src/box/sql/tokenize.c
> > +++ b/src/box/sql/tokenize.c
> > @@ -483,7 +483,9 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
> >              &pParse->sLastToken.isReserved);
> >      i += pParse->sLastToken.n;
> >      if (i > mxSqlLen) {
> > -       pParse->rc = SQL_TOOBIG;
> > +       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> > +          "string or blob too big”);
> 
> I would add to error message max possible length.
> 
I will add this change in review fix of patch
"sql: rework semantic errors". Btw I wasn't able to reproduce this
error due to limit being too big. So I got an error:
"error: not enough memory". Due to this I didn't create test for
this error.

> > +       pParse->rc = SQL_TARANTOOL_ERROR;
> >        break;
> >      }
> >    } else {
> > @@ -502,7 +504,9 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
> >      assert(tokenType == TK_SPACE
> >             || tokenType == TK_ILLEGAL);
> >      if (db->u1.isInterrupted) {
> > -       pParse->rc = SQL_INTERRUPT;
> > +       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> > +          "interrupted”);
> 
> What does it mean? AFAIR it is dead code (i.e. everything
> connected with “interrupt”).
> 
Removed.

> > +       pParse->rc = SQL_TARANTOOL_ERROR;
> >        break;
> >      }
> >      if (tokenType == TK_ILLEGAL) {
> 

Diff between patches:

commit c5d5c541000868b1aba78d07d0b004f0022a3735
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Fri Mar 15 20:58:48 2019 +0300

    Temporary: Review fix

diff --git a/src/box/sql/malloc.c b/src/box/sql/malloc.c
index 8812298..dc018fc 100644
--- a/src/box/sql/malloc.c
+++ b/src/box/sql/malloc.c
@@ -51,12 +51,13 @@ sql_sized_malloc(int nByte)
 	assert(nByte > 0);
 	nByte = ROUND8(nByte);
 	p = malloc(nByte + 8);
-	if (p) {
-		p[0] = nByte;
-		p++;
-	} else {
-		diag_set(OutOfMemory, nByte, "realloc", "p");
+	if (p == NULL) {
+		sql_get()->mallocFailed = 1;
+		diag_set(OutOfMemory, nByte, "malloc", "p");
+		return NULL;
 	}
+	p[0] = nByte;
+	p++;
 	return (void *)p;
 }
 
@@ -109,12 +110,13 @@ sql_sized_realloc(void *pPrior, int nByte)
 	assert(nByte == ROUND8(nByte));	/* EV: R-46199-30249 */
 	p--;
 	p = realloc(p, nByte + 8);
-	if (p) {
-		p[0] = nByte;
-		p++;
-	} else {
-		diag_set(OutOfMemory, nByte, "malloc", "p");
+	if (p == NULL) {
+		sql_get()->mallocFailed = 1;
+		diag_set(OutOfMemory, nByte, "realloc", "p");
+		return NULL;
 	}
+	p[0] = nByte;
+	p++;
 	return (void *)p;
 }
 
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index 834c165..f0a2f16 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -503,12 +503,6 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 		if (tokenType >= TK_SPACE) {
 			assert(tokenType == TK_SPACE
 			       || tokenType == TK_ILLEGAL);
-			if (db->u1.isInterrupted) {
-				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
-					 "interrupted");
-				pParse->rc = SQL_TARANTOOL_ERROR;
-				break;
-			}
 			if (tokenType == TK_ILLEGAL) {
 				diag_set(ClientError, ER_SQL_UNKNOWN_TOKEN,
 					 pParse->sLastToken.n,


New patch:

commit ded3bd139fa1fbaecf32e546d12a1ea03d69b04f
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Sun Feb 24 12:02:49 2019 +0300

    sql: set SQL parser errors via diag_set()
    
    After this patch all SQL parser errors will be set via diag_set().
    They were saved in field zErrMsg of struct Parse before this
    patch.
    
    Part of ...3965

diff --git a/src/box/errcode.h b/src/box/errcode.h
index f2f47c0..d234d26 100644
--- a/src/box/errcode.h
+++ b/src/box/errcode.h
@@ -238,6 +238,7 @@ struct errcode_record {
 	/*183 */_(ER_SQL_KEYWORD_IS_RESERVED,	"Keyword '%.*s' is reserved. Please use double quotes if '%.*s' is an identifier.") \
 	/*184 */_(ER_SQL_UNRECOGNIZED_SYNTAX,	"Syntax error near '%.*s'") \
 	/*185 */_(ER_SQL_UNKNOWN_TOKEN,		"Syntax error: unrecognized token: '%.*s'") \
+	/*186 */_(ER_SQL_PARSER_GENERIC,	"%s") \
 
 /*
  * !IMPORTANT! Please follow instructions at start of the file
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index f112c9f..deb5b89 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -194,7 +194,8 @@ sql_finish_coding(struct Parse *parse_context)
 		sqlVdbeMakeReady(v, parse_context);
 		parse_context->rc = SQL_DONE;
 	} else {
-		parse_context->rc = SQL_ERROR;
+		if (parse_context->rc != SQL_TARANTOOL_ERROR)
+			parse_context->rc = SQL_ERROR;
 	}
 }
 /**
diff --git a/src/box/sql/malloc.c b/src/box/sql/malloc.c
index e0d2ec8..dc018fc 100644
--- a/src/box/sql/malloc.c
+++ b/src/box/sql/malloc.c
@@ -51,14 +51,13 @@ sql_sized_malloc(int nByte)
 	assert(nByte > 0);
 	nByte = ROUND8(nByte);
 	p = malloc(nByte + 8);
-	if (p) {
-		p[0] = nByte;
-		p++;
-	} else {
-		testcase(sqlGlobalConfig.xLog != 0);
-		sql_log(SQL_NOMEM,
-			    "failed to allocate %u bytes of memory", nByte);
+	if (p == NULL) {
+		sql_get()->mallocFailed = 1;
+		diag_set(OutOfMemory, nByte, "malloc", "p");
+		return NULL;
 	}
+	p[0] = nByte;
+	p++;
 	return (void *)p;
 }
 
@@ -111,15 +110,13 @@ sql_sized_realloc(void *pPrior, int nByte)
 	assert(nByte == ROUND8(nByte));	/* EV: R-46199-30249 */
 	p--;
 	p = realloc(p, nByte + 8);
-	if (p) {
-		p[0] = nByte;
-		p++;
-	} else {
-		testcase(sqlGlobalConfig.xLog != 0);
-		sql_log(SQL_NOMEM,
-			    "failed memory resize %u to %u bytes",
-			    sql_sized_sizeof(pPrior), nByte);
+	if (p == NULL) {
+		sql_get()->mallocFailed = 1;
+		diag_set(OutOfMemory, nByte, "realloc", "p");
+		return NULL;
 	}
+	p[0] = nByte;
+	p++;
 	return (void *)p;
 }
 
diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
index 0c6296d..828a1ae 100644
--- a/src/box/sql/prepare.c
+++ b/src/box/sql/prepare.c
@@ -102,9 +102,8 @@ sqlPrepare(sql * db,	/* Database handle. */
 
 	if (sParse.rc == SQL_DONE)
 		sParse.rc = SQL_OK;
-	if (db->mallocFailed) {
-		sParse.rc = SQL_NOMEM;
-	}
+	if (db->mallocFailed)
+		sParse.rc = SQL_TARANTOOL_ERROR;
 	if (pzTail) {
 		*pzTail = sParse.zTail;
 	}
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index 58685c4..f0a2f16 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -483,7 +483,9 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 				      &pParse->sLastToken.isReserved);
 			i += pParse->sLastToken.n;
 			if (i > mxSqlLen) {
-				pParse->rc = SQL_TOOBIG;
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 "string or blob too big");
+				pParse->rc = SQL_TARANTOOL_ERROR;
 				break;
 			}
 		} else {
@@ -501,10 +503,6 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 		if (tokenType >= TK_SPACE) {
 			assert(tokenType == TK_SPACE
 			       || tokenType == TK_ILLEGAL);
-			if (db->u1.isInterrupted) {
-				pParse->rc = SQL_INTERRUPT;
-				break;
-			}
 			if (tokenType == TK_ILLEGAL) {
 				diag_set(ClientError, ER_SQL_UNKNOWN_TOKEN,
 					 pParse->sLastToken.n,
@@ -529,7 +527,7 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 #endif				/* YYDEBUG */
 	sqlParserFree(pEngine, sql_free);
 	if (db->mallocFailed) {
-		pParse->rc = SQL_NOMEM;
+		pParse->rc = SQL_TARANTOOL_ERROR;
 	}
 	if (pParse->rc != SQL_OK && pParse->rc != SQL_DONE
 	    && pParse->zErrMsg == 0) {
diff --git a/src/box/sql/util.c b/src/box/sql/util.c
index c89e2e8..a6d1f5c 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -211,7 +211,8 @@ sqlErrorWithMsg(sql * db, int err_code, const char *zFormat, ...)
 }
 
 /*
- * Add an error message to pParse->zErrMsg and increment pParse->nErr.
+ * Add an error to the diagnostics area, increment pParse->nErr
+ * and set pParse->rc.
  * The following formatting characters are allowed:
  *
  *      %s      Insert a string
@@ -236,10 +237,10 @@ sqlErrorMsg(Parse * pParse, const char *zFormat, ...)
 	va_start(ap, zFormat);
 	zMsg = sqlVMPrintf(db, zFormat, ap);
 	va_end(ap);
+	diag_set(ClientError, ER_SQL_PARSER_GENERIC, zMsg);
+	sqlDbFree(db, zMsg);
 	pParse->nErr++;
-	sqlDbFree(db, pParse->zErrMsg);
-	pParse->zErrMsg = zMsg;
-	pParse->rc = SQL_ERROR;
+	pParse->rc = SQL_TARANTOOL_ERROR;
 }
 
 void
diff --git a/test/box/misc.result b/test/box/misc.result
index 27579c6..9f0b2c7 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -514,6 +514,7 @@ t;
   182: box.error.SQL_STATEMENT_EMPTY
   184: box.error.SQL_UNRECOGNIZED_SYNTAX
   185: box.error.SQL_UNKNOWN_TOKEN
+  186: box.error.SQL_PARSER_GENERIC
 ...
 test_run:cmd("setopt delimiter ''");
 ---
diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
index b6b7fc7..bab6493 100755
--- a/test/sql-tap/check.test.lua
+++ b/test/sql-tap/check.test.lua
@@ -319,7 +319,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <check-3.1>
-        1, "Failed to create space 'T3': SQL error: subqueries prohibited in CHECK constraints"
+        1, "Failed to create space 'T3': subqueries prohibited in CHECK constraints"
         -- </check-3.1>
     })
 
diff --git a/test/sql/triggers.result b/test/sql/triggers.result
index 2f5b148..826e998 100644
--- a/test/sql/triggers.result
+++ b/test/sql/triggers.result
@@ -398,14 +398,14 @@ space_id = box.space.T1.id
 ...
 box.sql.execute("CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN new.a = ? BEGIN SELECT 1; END;")
 ---
-- error: 'SQL error: bindings are not allowed in DDL'
+- error: bindings are not allowed in DDL
 ...
 tuple = {"TR1", space_id, {sql = [[CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN new.a = ? BEGIN SELECT 1; END;]]}}
 ---
 ...
 box.space._trigger:insert(tuple)
 ---
-- error: 'SQL error: bindings are not allowed in DDL'
+- error: bindings are not allowed in DDL
 ...
 box.sql.execute("DROP TABLE t1;")
 ---

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 3/8] sql: replace rc with is_aborted status in struct Parse
  2019-03-14 19:53   ` [tarantool-patches] " n.pettik
@ 2019-03-18 15:28     ` Mergen Imeev
  2019-03-19  9:54       ` n.pettik
  0 siblings, 1 reply; 43+ messages in thread
From: Mergen Imeev @ 2019-03-18 15:28 UTC (permalink / raw)
  To: n.pettik; +Cc: tarantool-patches

Hi! Thank you for review. My answers and two new patches below.


On Thu, Mar 14, 2019 at 10:53:00PM +0300, n.pettik wrote:
> 
> >>> diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
> >>> index bab6493..0d8bf15 100755
> >>> --- a/test/sql-tap/check.test.lua
> >>> +++ b/test/sql-tap/check.test.lua
> >>> @@ -516,7 +516,7 @@ test:do_catchsql_test(
> >>>        );
> >>>    ]], {
> >>>        -- <check-5.1>
> >>> -        1, "Wrong space options (field 5): invalid expression specified (SQL error: bindings are not allowed in DDL)"
> >>> +        1, "Wrong space options (field 5): invalid expression specified (bindings are not allowed in DDL)"
> >>>        -- </check-5.1>
> >>>    })
> >> 
> >> Why test results have changed if you provided
> >> non-functional refactoring?
> > It become this way because now the error in diag instead of being
> > only in zErrMsg of struct Parse.
> 
> So, then it should be related to the previous patch, I guess.
> Otherwise, still don’t understand. Or fix commit message,
> since now it implies that only refactoring was provided.
> Or, what is better - move functional changes to separate patch.
>
I divided this ptch into two:
"sql: remove argument pzErrMsg from sqlRunParser()"
"sql: replace rc with is_aborted status in struct Parse"

> > index a2937a0..c2e5d6b 100644
> > --- a/src/box/sql.c
> > +++ b/src/box/sql.c
> > @@ -1363,12 +1363,8 @@ sql_checks_resolve_space_def_reference(ExprList *expr_list,
> > 
> > 	sql_resolve_self_reference(&parser, def, NC_IsCheck, NULL, expr_list);
> > 	int rc = 0;
> > -	if (parser.rc != SQL_OK) {
> > -		/* Tarantool error may be already set with diag. */
> > -		if (parser.rc != SQL_TARANTOOL_ERROR)
> > -			diag_set(ClientError, ER_SQL, parser.zErrMsg);
> > +	if (parser.is_aborted)
> > 		rc = -1;
> > -	}
> > 	sql_parser_destroy(&parser);
> > 	return rc;
> 
> diff --git a/src/box/sql.c b/src/box/sql.c
> index c2e5d6bd1..ea71dd101 100644
> --- a/src/box/sql.c
> +++ b/src/box/sql.c
> @@ -1362,9 +1362,6 @@ sql_checks_resolve_space_def_reference(ExprList *expr_list,
>         parser.parse_only = true;
>  
>         sql_resolve_self_reference(&parser, def, NC_IsCheck, NULL, expr_list);
> -       int rc = 0;
> -       if (parser.is_aborted)
> -               rc = -1;
>         sql_parser_destroy(&parser);
> -       return rc;
> +       return parser.is_aborted ? -1 : 0;
>  }
> 
I am not sure that this should be applied. I think it isn't right
to look at field is_aborted of parser after parser destruction.
> > }
> > 
> > diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
> > index 828a1ae..85385ee 100644
> > --- a/src/box/sql/prepare.c
> > +++ b/src/box/sql/prepare.c
> > @@ -52,7 +52,6 @@ sqlPrepare(sql * db,	/* Database handle. */
> > 	       const char **pzTail	/* OUT: End of parsed string */
> >     )
> > {
> > -	char *zErrMsg = 0;	/* Error message */
> > 	int rc = SQL_OK;	/* Result code */
> > 	Parse sParse;		/* Parsing context */
> > 	sql_parser_create(&sParse, db);
> > @@ -89,25 +88,24 @@ sqlPrepare(sql * db,	/* Database handle. */
> > 		}
> > 		zSqlCopy = sqlDbStrNDup(db, zSql, nBytes);
> > 		if (zSqlCopy) {
> > -			sqlRunParser(&sParse, zSqlCopy, &zErrMsg);
> > +			sqlRunParser(&sParse, zSqlCopy);
> > 			sParse.zTail = &zSql[sParse.zTail - zSqlCopy];
> > 			sqlDbFree(db, zSqlCopy);
> > 		} else {
> > 			sParse.zTail = &zSql[nBytes];
> > 		}
> > 	} else {
> > -		sqlRunParser(&sParse, zSql, &zErrMsg);
> > +		sqlRunParser(&sParse, zSql);
> > 	}
> > 	assert(0 == sParse.nQueryLoop);
> > 
> > -	if (sParse.rc == SQL_DONE)
> > -		sParse.rc = SQL_OK;
> > 	if (db->mallocFailed)
> > -		sParse.rc = SQL_TARANTOOL_ERROR;
> > +		sParse.is_aborted = true;
> > 	if (pzTail) {
> > 		*pzTail = sParse.zTail;
> > 	}
> > -	rc = sParse.rc;
> > +	if (sParse.is_aborted)
> > +		rc = SQL_TARANTOOL_ERROR;
> > 
> > 	if (rc == SQL_OK && sParse.pVdbe && sParse.explain) {
> > 		static const char *const azColName[] = {
> > @@ -168,11 +166,7 @@ sqlPrepare(sql * db,	/* Database handle. */
> > 		*ppStmt = (sql_stmt *) sParse.pVdbe;
> > 	}
> > 
> > -	if (zErrMsg) {
> > -		sqlErrorWithMsg(db, rc, "%s", zErrMsg);
> > -	} else {
> > -		sqlError(db, rc);
> > -	}
> > +	db->errCode = rc;
> 
> Is this thing used anywhere?
Yes, it is used in sql_errmsg(). It contains VDBE errors too, so I
think it would be better to leave it for now.
> 
>

First new patch:

commit 50af26719f6d195417f546ea2cfd4e70e3b09421
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Fri Mar 15 21:37:58 2019 +0300

    sql: remove argument pzErrMsg from sqlRunParser()
    
    This argument has practically no functionality, but deleting it
    allows us to replace the rc field of the Parse structure with a
    new bool field.
    
    Part of #3965

diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
index 828a1ae..4eaa9a4 100644
--- a/src/box/sql/prepare.c
+++ b/src/box/sql/prepare.c
@@ -52,7 +52,6 @@ sqlPrepare(sql * db,	/* Database handle. */
 	       const char **pzTail	/* OUT: End of parsed string */
     )
 {
-	char *zErrMsg = 0;	/* Error message */
 	int rc = SQL_OK;	/* Result code */
 	Parse sParse;		/* Parsing context */
 	sql_parser_create(&sParse, db);
@@ -89,14 +88,14 @@ sqlPrepare(sql * db,	/* Database handle. */
 		}
 		zSqlCopy = sqlDbStrNDup(db, zSql, nBytes);
 		if (zSqlCopy) {
-			sqlRunParser(&sParse, zSqlCopy, &zErrMsg);
+			sqlRunParser(&sParse, zSqlCopy);
 			sParse.zTail = &zSql[sParse.zTail - zSqlCopy];
 			sqlDbFree(db, zSqlCopy);
 		} else {
 			sParse.zTail = &zSql[nBytes];
 		}
 	} else {
-		sqlRunParser(&sParse, zSql, &zErrMsg);
+		sqlRunParser(&sParse, zSql);
 	}
 	assert(0 == sParse.nQueryLoop);
 
@@ -168,11 +167,7 @@ sqlPrepare(sql * db,	/* Database handle. */
 		*ppStmt = (sql_stmt *) sParse.pVdbe;
 	}
 
-	if (zErrMsg) {
-		sqlErrorWithMsg(db, rc, "%s", zErrMsg);
-	} else {
-		sqlError(db, rc);
-	}
+	db->errCode = rc;
 
 	/* Delete any TriggerPrg structures allocated while parsing this statement. */
 	while (sParse.pTriggerPrg) {
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index c63ff1c..b9b45b5 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -3198,7 +3198,7 @@ void sqlDequote(char *);
 void sqlNormalizeName(char *z);
 void sqlTokenInit(Token *, char *);
 int sqlKeywordCode(const unsigned char *, int);
-int sqlRunParser(Parse *, const char *, char **);
+int sqlRunParser(Parse *, const char *);
 
 /**
  * Increment error counter.
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index f0a2f16..8ee996e 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -437,17 +437,17 @@ parser_space_delete(struct sql *db, struct space *space)
 	sql_expr_list_delete(db, space->def->opts.checks);
 }
 
-/*
- * Run the parser on the given SQL string.  The parser structure is
- * passed in.  An SQL_ status code is returned.  If an error occurs
- * then an and attempt is made to write an error message into
- * memory obtained from sql_malloc() and to make *pzErrMsg point to that
- * error message.
+/**
+ * Run the parser on the given SQL string.
+ *
+ * @param pParse Parser context.
+ * @param zSql SQL string.
+ * @retval 0 on success.
+ * @retval -1 on error.
  */
 int
-sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
+sqlRunParser(Parse * pParse, const char *zSql)
 {
-	int nErr = 0;		/* Number of errors encountered */
 	int i;			/* Loop counter */
 	void *pEngine;		/* The LEMON-generated LALR(1) parser */
 	int tokenType;		/* type of the next token */
@@ -463,12 +463,11 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 	pParse->rc = SQL_OK;
 	pParse->zTail = zSql;
 	i = 0;
-	assert(pzErrMsg != 0);
 	/* sqlParserTrace(stdout, "parser: "); */
 	pEngine = sqlParserAlloc(sqlMalloc);
 	if (pEngine == 0) {
 		sqlOomFault(db);
-		return SQL_NOMEM;
+		return -1;
 	}
 	assert(pParse->new_space == NULL);
 	assert(pParse->parsed_ast.trigger == NULL);
@@ -518,7 +517,6 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 				break;
 		}
 	}
-	assert(nErr == 0);
 	pParse->zTail = &zSql[i];
 #ifdef YYTRACKMAXSTACKDEPTH
 	sqlStatusHighwater(SQL_STATUS_PARSER_STACK,
@@ -530,21 +528,8 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 		pParse->rc = SQL_TARANTOOL_ERROR;
 	}
 	if (pParse->rc != SQL_OK && pParse->rc != SQL_DONE
-	    && pParse->zErrMsg == 0) {
-		const char *error;
-		if (is_tarantool_error(pParse->rc) &&
-		    tarantoolErrorMessage() != NULL)
-			error = tarantoolErrorMessage();
-		else
-			error = sqlErrStr(pParse->rc);
-		pParse->zErrMsg = sqlMPrintf(db, "%s", error);
-	}
-	assert(pzErrMsg != 0);
-	if (pParse->zErrMsg) {
-		*pzErrMsg = pParse->zErrMsg;
-		sql_log(pParse->rc, "%s", *pzErrMsg);
-		nErr++;
-	}
+	    && pParse->zErrMsg == 0)
+		pParse->zErrMsg = sqlMPrintf(db, "%s", tarantoolErrorMessage());
 	if (pParse->pVdbe != NULL && pParse->nErr > 0) {
 		sqlVdbeDelete(pParse->pVdbe);
 		pParse->pVdbe = 0;
@@ -553,8 +538,7 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 	if (pParse->pWithToFree)
 		sqlWithDelete(db, pParse->pWithToFree);
 	sqlDbFree(db, pParse->pVList);
-	assert(nErr == 0 || pParse->rc != SQL_OK);
-	return nErr;
+	return pParse->nErr > 0 || pParse->rc == SQL_TARANTOOL_ERROR ? -1 : 0;
 }
 
 struct Expr *
@@ -575,11 +559,8 @@ sql_expr_compile(sql *db, const char *expr, int expr_len)
 	}
 	sprintf(stmt, "%s%.*s", outer, expr_len, expr);
 
-	char *sql_error = NULL;
-	if (sqlRunParser(&parser, stmt, &sql_error) != SQL_OK ||
-	    parser.parsed_ast_type != AST_TYPE_EXPR) {
-		diag_set(ClientError, ER_SQL, sql_error);
-	} else {
+	if (sqlRunParser(&parser, stmt) == 0 &&
+	    parser.parsed_ast_type == AST_TYPE_EXPR) {
 		expression = parser.parsed_ast.expr;
 		parser.parsed_ast.expr = NULL;
 	}
@@ -597,8 +578,7 @@ sql_view_compile(struct sql *db, const char *view_stmt)
 
 	struct Select *select = NULL;
 
-	char *unused;
-	if (sqlRunParser(&parser, view_stmt, &unused) != SQL_OK ||
+	if (sqlRunParser(&parser, view_stmt) != 0 ||
 	    parser.parsed_ast_type != AST_TYPE_SELECT) {
 		diag_set(ClientError, ER_SQL_EXECUTE, view_stmt);
 	} else {
@@ -616,13 +596,9 @@ sql_trigger_compile(struct sql *db, const char *sql)
 	struct Parse parser;
 	sql_parser_create(&parser, db);
 	parser.parse_only = true;
-	char *sql_error = NULL;
 	struct sql_trigger *trigger = NULL;
-	if (sqlRunParser(&parser, sql, &sql_error) != SQL_OK ||
-	    parser.parsed_ast_type != AST_TYPE_TRIGGER) {
-	    if (parser.rc != SQL_TARANTOOL_ERROR)
-		diag_set(ClientError, ER_SQL, sql_error);
-	} else {
+	if (sqlRunParser(&parser, sql) == 0 &&
+	    parser.parsed_ast_type == AST_TYPE_TRIGGER) {
 		trigger = parser.parsed_ast.trigger;
 		parser.parsed_ast.trigger = NULL;
 	}
diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
index bab6493..0d8bf15 100755
--- a/test/sql-tap/check.test.lua
+++ b/test/sql-tap/check.test.lua
@@ -516,7 +516,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <check-5.1>
-        1, "Wrong space options (field 5): invalid expression specified (SQL error: bindings are not allowed in DDL)"
+        1, "Wrong space options (field 5): invalid expression specified (bindings are not allowed in DDL)"
         -- </check-5.1>
     })
 
@@ -528,7 +528,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <check-5.2>
-        1, "Wrong space options (field 5): invalid expression specified (SQL error: bindings are not allowed in DDL)"
+        1, "Wrong space options (field 5): invalid expression specified (bindings are not allowed in DDL)"
         -- </check-5.2>
     })
 
diff --git a/test/sql/checks.result b/test/sql/checks.result
index e31964c..42df657 100644
--- a/test/sql/checks.result
+++ b/test/sql/checks.result
@@ -29,8 +29,8 @@ t = {513, 1, 'test', 'memtx', 0, opts, format}
 ...
 s = box.space._space:insert(t)
 ---
-- error: 'Wrong space options (field 5): invalid expression specified (SQL error:
-    Syntax error near ''<'')'
+- error: 'Wrong space options (field 5): invalid expression specified (Syntax error
+    near ''<'')'
 ...
 opts = {checks = {{expr = 'X>5'}}}
 ---
@@ -122,8 +122,8 @@ box.sql.execute("DROP TABLE w2;")
 --
 box.sql.execute("CREATE TABLE t5(x INT PRIMARY KEY, y INT, CHECK( x*y < ? ));")
 ---
-- error: 'Wrong space options (field 5): invalid expression specified (SQL error:
-    bindings are not allowed in DDL)'
+- error: 'Wrong space options (field 5): invalid expression specified (bindings are
+    not allowed in DDL)'
 ...
 opts = {checks = {{expr = '?>5', name = 'ONE'}}}
 ---
@@ -136,8 +136,8 @@ t = {513, 1, 'test', 'memtx', 0, opts, format}
 ...
 s = box.space._space:insert(t)
 ---
-- error: 'Wrong space options (field 5): invalid expression specified (SQL error:
-    bindings are not allowed in DDL)'
+- error: 'Wrong space options (field 5): invalid expression specified (bindings are
+    not allowed in DDL)'
 ...
 test_run:cmd("clear filter")
 ---


Second new patch:

commit c8316b7f20ffad80857568fab2ad422c85218d05
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Wed Mar 6 22:09:26 2019 +0300

    sql: replace rc with is_aborted status in struct Parse
    
    Currently, field representing return code in struct Parse can take
    only two values: SQL_OK (successfully finished parsing) and
    SQL_TARANTOOL_ERROR (in case of any errors occurred). Therefore,
    it can be replaced with a boolean field. Patch provides
    straightforward refactoring.
    
    Part of #3965

diff --git a/src/box/sql.c b/src/box/sql.c
index a2937a0..c2e5d6b 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -1275,7 +1275,7 @@ sql_ephemeral_space_def_new(struct Parse *parser, const char *name)
 	if (def == NULL) {
 		diag_set(OutOfMemory, size, "region_alloc",
 			 "sql_ephemeral_space_def_new");
-		parser->rc = SQL_TARANTOOL_ERROR;
+		parser->is_aborted = true;
 		parser->nErr++;
 		return NULL;
 	}
@@ -1294,7 +1294,7 @@ sql_ephemeral_space_new(Parse *parser, const char *name)
 	struct space *space = (struct space *) region_alloc(&parser->region, sz);
 	if (space == NULL) {
 		diag_set(OutOfMemory, sz, "region", "space");
-		parser->rc = SQL_TARANTOOL_ERROR;
+		parser->is_aborted = true;
 		parser->nErr++;
 		return NULL;
 	}
@@ -1363,12 +1363,8 @@ sql_checks_resolve_space_def_reference(ExprList *expr_list,
 
 	sql_resolve_self_reference(&parser, def, NC_IsCheck, NULL, expr_list);
 	int rc = 0;
-	if (parser.rc != SQL_OK) {
-		/* Tarantool error may be already set with diag. */
-		if (parser.rc != SQL_TARANTOOL_ERROR)
-			diag_set(ClientError, ER_SQL, parser.zErrMsg);
+	if (parser.is_aborted)
 		rc = -1;
-	}
 	sql_parser_destroy(&parser);
 	return rc;
 }
diff --git a/src/box/sql/alter.c b/src/box/sql/alter.c
index d49ebb8..fe4754f 100644
--- a/src/box/sql/alter.c
+++ b/src/box/sql/alter.c
@@ -72,7 +72,7 @@ exit_rename_table:
 	return;
 tnt_error:
 	sqlDbFree(db, new_name);
-	parse->rc = SQL_TARANTOOL_ERROR;
+	parse->is_aborted = true;
 	parse->nErr++;
 	goto exit_rename_table;
 }
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index ea5cbc3..96b7099 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -907,7 +907,7 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space)
 		if (jump_addrs == NULL) {
 			diag_set(OutOfMemory, sizeof(int) * part_count,
 				 "region", "jump_addrs");
-			parse->rc = SQL_TARANTOOL_ERROR;
+			parse->is_aborted = true;
 			parse->nErr++;
 			return;
 		}
@@ -1130,7 +1130,7 @@ sqlAnalyze(Parse * pParse, Token * pName)
 				}
 			} else {
 				diag_set(ClientError, ER_NO_SUCH_SPACE, z);
-				pParse->rc = SQL_TARANTOOL_ERROR;
+				pParse->is_aborted = true;
 				pParse->nErr++;
 			}
 			sqlDbFree(db, z);
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index deb5b89..0179a45 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -91,7 +91,7 @@ save_record(struct Parse *parser, uint32_t space_id, int reg_key,
 	if (record == NULL) {
 		diag_set(OutOfMemory, sizeof(*record), "region_alloc",
 			 "record");
-		parser->rc = SQL_TARANTOOL_ERROR;
+		parser->is_aborted = true;
 		parser->nErr++;
 		return;
 	}
@@ -146,8 +146,7 @@ sql_finish_coding(struct Parse *parse_context)
 	}
 
 	if (db->mallocFailed || parse_context->nErr != 0) {
-		if (parse_context->rc == SQL_OK)
-			parse_context->rc = SQL_ERROR;
+		parse_context->is_aborted = true;
 		return;
 	}
 	/*
@@ -192,10 +191,8 @@ sql_finish_coding(struct Parse *parse_context)
 	if (parse_context->nErr == 0 && !db->mallocFailed) {
 		assert(parse_context->iCacheLevel == 0);
 		sqlVdbeMakeReady(v, parse_context);
-		parse_context->rc = SQL_DONE;
 	} else {
-		if (parse_context->rc != SQL_TARANTOOL_ERROR)
-			parse_context->rc = SQL_ERROR;
+		parse_context->is_aborted = true;
 	}
 }
 /**
@@ -397,7 +394,7 @@ sql_field_retrieve(Parse *parser, struct space_def *space_def, uint32_t id)
 			diag_set(OutOfMemory, columns_new *
 				sizeof(space_def->fields[0]),
 				"region_alloc", "sql_field_retrieve");
-			parser->rc = SQL_TARANTOOL_ERROR;
+			parser->is_aborted = true;
 			parser->nErr++;
 			return NULL;
 		}
@@ -453,7 +450,7 @@ sqlAddColumn(Parse * pParse, Token * pName, struct type_def *type_def)
 	if (z == NULL) {
 		diag_set(OutOfMemory, pName->n + 1,
 			 "region_alloc", "z");
-		pParse->rc = SQL_TARANTOOL_ERROR;
+		pParse->is_aborted = true;
 		pParse->nErr++;
 		return;
 	}
@@ -501,7 +498,7 @@ sql_column_add_nullable_action(struct Parse *parser,
 				   on_conflict_action_strs[field->
 							   nullable_action]);
 		diag_set(ClientError, ER_SQL, err_msg);
-		parser->rc = SQL_TARANTOOL_ERROR;
+		parser->is_aborted = true;
 		parser->nErr++;
 		return;
 	}
@@ -543,7 +540,7 @@ sqlAddDefaultValue(Parse * pParse, ExprSpan * pSpan)
 				diag_set(OutOfMemory, default_length + 1,
 					 "region_alloc",
 					 "field->default_value");
-				pParse->rc = SQL_TARANTOOL_ERROR;
+				pParse->is_aborted = true;
 				pParse->nErr++;
 				return;
 			}
@@ -562,7 +559,7 @@ field_def_create_for_pk(struct Parse *parser, struct field_def *field,
 	if (field->nullable_action != ON_CONFLICT_ACTION_ABORT &&
 	    field->nullable_action != ON_CONFLICT_ACTION_DEFAULT) {
 		diag_set(ClientError, ER_NULLABLE_PRIMARY, space_name);
-		parser->rc = SQL_TARANTOOL_ERROR;
+		parser->is_aborted = true;
 		parser->nErr++;
 		return -1;
 	} else if (field->nullable_action == ON_CONFLICT_ACTION_DEFAULT) {
@@ -851,7 +848,7 @@ vdbe_emit_create_index(struct Parse *parse, struct space_def *def,
 	save_record(parse, BOX_INDEX_ID, entry_reg, 2, v->nOp - 1);
 	return;
 error:
-	parse->rc = SQL_TARANTOOL_ERROR;
+	parse->is_aborted = true;
 	parse->nErr++;
 
 }
@@ -912,7 +909,7 @@ createSpace(Parse * pParse, int iSpaceId, char *zStmt)
 	return;
 error:
 	pParse->nErr++;
-	pParse->rc = SQL_TARANTOOL_ERROR;
+	pParse->is_aborted = true;
 }
 
 int
@@ -1093,7 +1090,7 @@ vdbe_emit_fk_constraint_create(struct Parse *parse_context,
 	return;
 error:
 	parse_context->nErr++;
-	parse_context->rc = SQL_TARANTOOL_ERROR;
+	parse_context->is_aborted = true;
 }
 
 /**
@@ -1121,7 +1118,7 @@ resolve_link(struct Parse *parse_context, const struct space_def *def,
 	diag_set(ClientError, ER_CREATE_FK_CONSTRAINT, fk_name,
 		 tt_sprintf("unknown column %s in foreign key definition",
 			    field_name));
-	parse_context->rc = SQL_TARANTOOL_ERROR;
+	parse_context->is_aborted = true;
 	parse_context->nErr++;
 	return -1;
 }
@@ -1253,7 +1250,7 @@ sqlEndTable(Parse * pParse,	/* Parse context */
 					 "foreign key does not match the "\
 					 "number of columns in the primary "\
 					 "index of referenced table");
-				pParse->rc = SQL_TARANTOOL_ERROR;
+				pParse->is_aborted = true;
 				pParse->nErr++;
 				return;
 			}
@@ -1329,7 +1326,7 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
 	space->def->opts.sql = strndup(begin->z, n);
 	if (space->def->opts.sql == NULL) {
 		diag_set(OutOfMemory, n, "strndup", "opts.sql");
-		parse_context->rc = SQL_TARANTOOL_ERROR;
+		parse_context->is_aborted = true;
 		parse_context->nErr++;
 		goto create_view_fail;
 	}
@@ -1649,7 +1646,7 @@ sql_drop_table(struct Parse *parse_context, struct SrcList *table_name_list,
 		if (! fk_constraint_is_self_referenced(fk->def)) {
 			diag_set(ClientError, ER_DROP_SPACE, space_name,
 				 "other objects depend on it");
-			parse_context->rc = SQL_TARANTOOL_ERROR;
+			parse_context->is_aborted = true;
 			parse_context->nErr++;
 			goto exit_drop_table;
 		}
@@ -1685,7 +1682,7 @@ columnno_by_name(struct Parse *parse_context, const struct space *space,
 		diag_set(ClientError, ER_CREATE_FK_CONSTRAINT, fk_name,
 			 tt_sprintf("foreign key refers to nonexistent field %s",
 				    column_name));
-		parse_context->rc = SQL_TARANTOOL_ERROR;
+		parse_context->is_aborted = true;
 		parse_context->nErr++;
 		return -1;
 	}
@@ -1895,7 +1892,7 @@ exit_create_fk:
 	sqlDbFree(db, constraint_name);
 	return;
 tnt_error:
-	parse_context->rc = SQL_TARANTOOL_ERROR;
+	parse_context->is_aborted = true;
 	parse_context->nErr++;
 	goto exit_create_fk;
 }
@@ -1920,7 +1917,7 @@ sql_drop_foreign_key(struct Parse *parse_context, struct SrcList *table,
 	struct space *child = space_by_name(table_name);
 	if (child == NULL) {
 		diag_set(ClientError, ER_NO_SUCH_SPACE, table_name);
-		parse_context->rc = SQL_TARANTOOL_ERROR;
+		parse_context->is_aborted = true;
 		parse_context->nErr++;
 		return;
 	}
@@ -2099,7 +2096,7 @@ cleanup:
 		key_def_delete(key_def);
 	return rc;
 tnt_error:
-	parse->rc = SQL_TARANTOOL_ERROR;
+	parse->is_aborted = true;
 	++parse->nErr;
 	goto cleanup;
 }
@@ -2150,7 +2147,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
 		if (space == NULL) {
 			if (! if_not_exist) {
 				diag_set(ClientError, ER_NO_SUCH_SPACE, name);
-				parse->rc = SQL_TARANTOOL_ERROR;
+				parse->is_aborted = true;
 				parse->nErr++;
 			}
 			goto exit_create_index;
@@ -2245,7 +2242,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
 		diag_set(ClientError, ER_MODIFY_INDEX, name, def->name,
 			 "can't create index on system space");
 		parse->nErr++;
-		parse->rc = SQL_TARANTOOL_ERROR;
+		parse->is_aborted = true;
 		goto exit_create_index;
 	}
 
@@ -2273,7 +2270,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
 	index = (struct index *) region_alloc(&parse->region, sizeof(*index));
 	if (index == NULL) {
 		diag_set(OutOfMemory, sizeof(*index), "region", "index");
-		parse->rc = SQL_TARANTOOL_ERROR;
+		parse->is_aborted = true;
 		parse->nErr++;
 		goto exit_create_index;
 	}
diff --git a/src/box/sql/callback.c b/src/box/sql/callback.c
index 4594cac..5c9cf98 100644
--- a/src/box/sql/callback.c
+++ b/src/box/sql/callback.c
@@ -49,7 +49,7 @@ sql_get_coll_seq(Parse *parser, const char *name, uint32_t *coll_id)
 	struct coll_id *p = coll_by_name(name, strlen(name));
 	if (p == NULL) {
 		diag_set(ClientError, ER_NO_SUCH_COLLATION, name);
-		parser->rc = SQL_TARANTOOL_ERROR;
+		parser->is_aborted = true;
 		parser->nErr++;
 		return NULL;
 	} else {
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index 5170c7f..3123681 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -50,7 +50,7 @@ sql_lookup_space(struct Parse *parse, struct SrcList_item *space_name)
 	if (space->def->field_count == 0) {
 		diag_set(ClientError, ER_UNSUPPORTED, "SQL",
 			 "space without format");
-		parse->rc = SQL_TARANTOOL_ERROR;
+		parse->is_aborted = true;
 		parse->nErr++;
 		return NULL;
 	}
@@ -116,7 +116,7 @@ cleanup:
 	return;
 
 tarantool_error:
-	parse->rc = SQL_TARANTOOL_ERROR;
+	parse->is_aborted = true;
 	parse->nErr++;
 	goto cleanup;
 }
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 82688df..39b747d 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -258,7 +258,7 @@ sql_expr_coll(Parse *parse, Expr *p, bool *is_explicit_coll, uint32_t *coll_id,
 						diag_set(ClientError,
 							 ER_ILLEGAL_COLLATION_MIX);
 						parse->nErr++;
-						parse->rc = SQL_TARANTOOL_ERROR;
+						parse->is_aborted = true;
 					}
 					return -1;
 				}
@@ -433,7 +433,7 @@ sql_binary_compare_coll_seq(Parse *parser, Expr *left, Expr *right,
 		return -1;
 	if (collations_check_compatibility(lhs_coll_id, is_lhs_forced,
 					   rhs_coll_id, is_rhs_forced, id) != 0) {
-		parser->rc = SQL_TARANTOOL_ERROR;
+		parser->is_aborted = true;
 		parser->nErr++;
 		return -1;
 	}
diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
index 8b909f2..d8a0bda 100644
--- a/src/box/sql/pragma.c
+++ b/src/box/sql/pragma.c
@@ -506,7 +506,7 @@ sqlPragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 		box_iterator_t* iter;
 		iter = box_index_iterator(space->def->id, 0,ITER_ALL, key_buf, key_end);
 		if (iter == NULL) {
-			pParse->rc = SQL_TARANTOOL_ERROR;
+			pParse->is_aborted = true;
 			pParse->nErr++;
 			goto pragma_out;
 		}
@@ -565,7 +565,7 @@ sqlPragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 		if (!token_is_string(pValue)) {
 			diag_set(ClientError, ER_ILLEGAL_PARAMS,
 				 "string value is expected");
-			pParse->rc = SQL_TARANTOOL_ERROR;
+			pParse->is_aborted = true;
 			pParse->nErr++;
 			goto pragma_out;
 		}
@@ -577,7 +577,7 @@ sqlPragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 			sqlVdbeAddOp2(v, OP_ResultRow, 1, 1);
 		} else {
 			if (sql_default_engine_set(zRight) != 0) {
-				pParse->rc = SQL_TARANTOOL_ERROR;
+				pParse->is_aborted = true;
 				pParse->nErr++;
 				goto pragma_out;
 			}
diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
index 4eaa9a4..85385ee 100644
--- a/src/box/sql/prepare.c
+++ b/src/box/sql/prepare.c
@@ -99,14 +99,13 @@ sqlPrepare(sql * db,	/* Database handle. */
 	}
 	assert(0 == sParse.nQueryLoop);
 
-	if (sParse.rc == SQL_DONE)
-		sParse.rc = SQL_OK;
 	if (db->mallocFailed)
-		sParse.rc = SQL_TARANTOOL_ERROR;
+		sParse.is_aborted = true;
 	if (pzTail) {
 		*pzTail = sParse.zTail;
 	}
-	rc = sParse.rc;
+	if (sParse.is_aborted)
+		rc = SQL_TARANTOOL_ERROR;
 
 	if (rc == SQL_OK && sParse.pVdbe && sParse.explain) {
 		static const char *const azColName[] = {
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 49b0052..5b9d216 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -1341,7 +1341,7 @@ resolveSelectStep(Walker * pWalker, Select * p)
 					 "clause or be used in an aggregate "
 					 "function");
 				pParse->nErr++;
-				pParse->rc = SQL_TARANTOOL_ERROR;
+				pParse->is_aborted = true;
 				return WRC_Abort;
 			}
 			/*
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index ef24760..7185a10 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -2235,7 +2235,7 @@ multi_select_coll_seq_r(struct Parse *parser, struct Select *p, int n,
 	if (collations_check_compatibility(prior_coll_id, is_prior_forced,
 					   current_coll_id, is_current_forced,
 					   &res_coll_id) != 0) {
-		parser->rc = SQL_TARANTOOL_ERROR;
+		parser->is_aborted = true;
 		parser->nErr++;
 		return 0;
 	}
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index b9b45b5..22fab27 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -2653,7 +2653,6 @@ struct Parse {
 	sql *db;		/* The main database structure */
 	char *zErrMsg;		/* An error message */
 	Vdbe *pVdbe;		/* An engine for executing database bytecode */
-	int rc;			/* Return code from execution */
 	u8 colNamesSet;		/* TRUE after OP_ColumnName has been issued to pVdbe */
 	u8 nTempReg;		/* Number of temporary registers in aTempReg[] */
 	u8 isMultiWrite;	/* True if statement may modify/insert multiple rows */
@@ -2688,6 +2687,8 @@ struct Parse {
 	u8 eOrconf;		/* Default ON CONFLICT policy for trigger steps */
 	/** Region to make SQL temp allocations. */
 	struct region region;
+	/** True, if error should be raised after parsing. */
+	bool is_aborted;
 
   /**************************************************************************
   * Fields above must be initialized to zero.  The fields that follow,
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index 8ee996e..00a84ab 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -460,7 +460,6 @@ sqlRunParser(Parse * pParse, const char *zSql)
 	if (db->nVdbeActive == 0) {
 		db->u1.isInterrupted = 0;
 	}
-	pParse->rc = SQL_OK;
 	pParse->zTail = zSql;
 	i = 0;
 	/* sqlParserTrace(stdout, "parser: "); */
@@ -484,7 +483,7 @@ sqlRunParser(Parse * pParse, const char *zSql)
 			if (i > mxSqlLen) {
 				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
 					 "string or blob too big");
-				pParse->rc = SQL_TARANTOOL_ERROR;
+				pParse->is_aborted = true;
 				break;
 			}
 		} else {
@@ -513,7 +512,7 @@ sqlRunParser(Parse * pParse, const char *zSql)
 			sqlParser(pEngine, tokenType, pParse->sLastToken,
 				      pParse);
 			lastTokenParsed = tokenType;
-			if (pParse->rc != SQL_OK || db->mallocFailed)
+			if (pParse->is_aborted || db->mallocFailed)
 				break;
 		}
 	}
@@ -524,11 +523,9 @@ sqlRunParser(Parse * pParse, const char *zSql)
 	    );
 #endif				/* YYDEBUG */
 	sqlParserFree(pEngine, sql_free);
-	if (db->mallocFailed) {
-		pParse->rc = SQL_TARANTOOL_ERROR;
-	}
-	if (pParse->rc != SQL_OK && pParse->rc != SQL_DONE
-	    && pParse->zErrMsg == 0)
+	if (db->mallocFailed)
+		pParse->is_aborted = true;
+	if (pParse->is_aborted && pParse->zErrMsg == 0)
 		pParse->zErrMsg = sqlMPrintf(db, "%s", tarantoolErrorMessage());
 	if (pParse->pVdbe != NULL && pParse->nErr > 0) {
 		sqlVdbeDelete(pParse->pVdbe);
@@ -538,7 +535,7 @@ sqlRunParser(Parse * pParse, const char *zSql)
 	if (pParse->pWithToFree)
 		sqlWithDelete(db, pParse->pWithToFree);
 	sqlDbFree(db, pParse->pVList);
-	return pParse->nErr > 0 || pParse->rc == SQL_TARANTOOL_ERROR ? -1 : 0;
+	return pParse->is_aborted ? -1 : 0;
 }
 
 struct Expr *
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index f7e6189..5ee0d96 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -154,7 +154,7 @@ sql_trigger_begin(struct Parse *parse, struct Token *name, int tr_tm,
 	return;
 
 set_tarantool_error_and_cleanup:
-	parse->rc = SQL_TARANTOOL_ERROR;
+	parse->is_aborted = true;
 	parse->nErr++;
 	goto trigger_cleanup;
 }
@@ -735,7 +735,7 @@ transferParseError(Parse * pTo, Parse * pFrom)
 	if (pTo->nErr == 0) {
 		pTo->zErrMsg = pFrom->zErrMsg;
 		pTo->nErr = pFrom->nErr;
-		pTo->rc = pFrom->rc;
+		pTo->is_aborted = pFrom->is_aborted;
 	} else {
 		sqlDbFree(pFrom->db, pFrom->zErrMsg);
 	}
diff --git a/src/box/sql/util.c b/src/box/sql/util.c
index a6d1f5c..5aa4fda 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -212,7 +212,7 @@ sqlErrorWithMsg(sql * db, int err_code, const char *zFormat, ...)
 
 /*
  * Add an error to the diagnostics area, increment pParse->nErr
- * and set pParse->rc.
+ * and set pParse->is_aborted.
  * The following formatting characters are allowed:
  *
  *      %s      Insert a string
@@ -240,14 +240,14 @@ sqlErrorMsg(Parse * pParse, const char *zFormat, ...)
 	diag_set(ClientError, ER_SQL_PARSER_GENERIC, zMsg);
 	sqlDbFree(db, zMsg);
 	pParse->nErr++;
-	pParse->rc = SQL_TARANTOOL_ERROR;
+	pParse->is_aborted = true;
 }
 
 void
 sql_parser_error(struct Parse *parse_context)
 {
 	parse_context->nErr++;
-	parse_context->rc = SQL_TARANTOOL_ERROR;
+	parse_context->is_aborted = true;
 }
 
 /*
diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
index 074ff8c..f417c49 100644
--- a/src/box/sql/vdbemem.c
+++ b/src/box/sql/vdbemem.c
@@ -1203,7 +1203,7 @@ valueFromFunction(sql * db,	/* The database connection */
 		goto value_from_function_out;
 	}
 
-	assert(pCtx->pParse->rc == SQL_OK);
+	assert(!pCtx->pParse->is_aborted);
 	memset(&ctx, 0, sizeof(ctx));
 	ctx.pOut = pVal;
 	ctx.pFunc = pFunc;
@@ -1215,7 +1215,8 @@ valueFromFunction(sql * db,	/* The database connection */
 		sql_value_apply_type(pVal, type);
 		assert(rc == SQL_OK);
 	}
-	pCtx->pParse->rc = rc;
+	if (rc != SQL_OK)
+		pCtx->pParse->is_aborted = true;
 
  value_from_function_out:
 	if (rc != SQL_OK) {
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index b46b7c3..3e1c783 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -2800,7 +2800,7 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder,	/* WHERE clause information */
 		if (key_def == NULL) {
 tnt_error:
 			pWInfo->pParse->nErr++;
-			pWInfo->pParse->rc = SQL_TARANTOOL_ERROR;
+			pWInfo->pParse->is_aborted = true;
 			return SQL_TARANTOOL_ERROR;
 		}
 
 

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 2/8] sql: set SQL parser errors via diag_set()
  2019-03-18 15:06     ` Mergen Imeev
@ 2019-03-19  9:41       ` n.pettik
  0 siblings, 0 replies; 43+ messages in thread
From: n.pettik @ 2019-03-19  9:41 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen

[-- Attachment #1: Type: text/plain, Size: 976 bytes --]


>>> diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
>>> index 58685c4..834c165 100644
>>> --- a/src/box/sql/tokenize.c
>>> +++ b/src/box/sql/tokenize.c
>>> @@ -483,7 +483,9 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
>>>             &pParse->sLastToken.isReserved);
>>>     i += pParse->sLastToken.n;
>>>     if (i > mxSqlLen) {
>>> -       pParse->rc = SQL_TOOBIG;
>>> +       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
>>> +          "string or blob too big”);
>> 
>> I would add to error message max possible length.
>> 
> I will add this change in review fix of patch
> "sql: rework semantic errors". Btw I wasn't able to reproduce this
> error due to limit being too big. So I got an error:
> "error: not enough memory". Due to this I didn't create test for
> this error.

This error is an indication that ssmth went really wrong.
Please, investigate this bug or open an issue.

This patch itself LGTM.


[-- Attachment #2: Type: text/html, Size: 5688 bytes --]

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 3/8] sql: replace rc with is_aborted status in struct Parse
  2019-03-18 15:28     ` Mergen Imeev
@ 2019-03-19  9:54       ` n.pettik
  0 siblings, 0 replies; 43+ messages in thread
From: n.pettik @ 2019-03-19  9:54 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen

[-- Attachment #1: Type: text/plain, Size: 2748 bytes --]


>>>>> diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
>>>>> index bab6493..0d8bf15 100755
>>>>> --- a/test/sql-tap/check.test.lua
>>>>> +++ b/test/sql-tap/check.test.lua
>>>>> @@ -516,7 +516,7 @@ test:do_catchsql_test(
>>>>>       );
>>>>>   ]], {
>>>>>       -- <check-5.1>
>>>>> -        1, "Wrong space options (field 5): invalid expression specified (SQL error: bindings are not allowed in DDL)"
>>>>> +        1, "Wrong space options (field 5): invalid expression specified (bindings are not allowed in DDL)"
>>>>>       -- </check-5.1>
>>>>>   })
>>>> 
>>>> Why test results have changed if you provided
>>>> non-functional refactoring?
>>> It become this way because now the error in diag instead of being
>>> only in zErrMsg of struct Parse.
>> 
>> So, then it should be related to the previous patch, I guess.
>> Otherwise, still don’t understand. Or fix commit message,
>> since now it implies that only refactoring was provided.
>> Or, what is better - move functional changes to separate patch.
>> 
> I divided this ptch into two:
> "sql: remove argument pzErrMsg from sqlRunParser()"
> "sql: replace rc with is_aborted status in struct Parse”

Both patches are OK.

>>> index a2937a0..c2e5d6b 100644
>>> --- a/src/box/sql.c
>>> +++ b/src/box/sql.c
>>> @@ -1363,12 +1363,8 @@ sql_checks_resolve_space_def_reference(ExprList *expr_list,
>>> 
>>> 	sql_resolve_self_reference(&parser, def, NC_IsCheck, NULL, expr_list);
>>> 	int rc = 0;
>>> -	if (parser.rc != SQL_OK) {
>>> -		/* Tarantool error may be already set with diag. */
>>> -		if (parser.rc != SQL_TARANTOOL_ERROR)
>>> -			diag_set(ClientError, ER_SQL, parser.zErrMsg);
>>> +	if (parser.is_aborted)
>>> 		rc = -1;
>>> -	}
>>> 	sql_parser_destroy(&parser);
>>> 	return rc;
>> 
>> diff --git a/src/box/sql.c b/src/box/sql.c
>> index c2e5d6bd1..ea71dd101 100644
>> --- a/src/box/sql.c
>> +++ b/src/box/sql.c
>> @@ -1362,9 +1362,6 @@ sql_checks_resolve_space_def_reference(ExprList *expr_list,
>>        parser.parse_only = true;
>> 
>>        sql_resolve_self_reference(&parser, def, NC_IsCheck, NULL, expr_list);
>> -       int rc = 0;
>> -       if (parser.is_aborted)
>> -               rc = -1;
>>        sql_parser_destroy(&parser);
>> -       return rc;
>> +       return parser.is_aborted ? -1 : 0;
>> }
>> 
> I am not sure that this should be applied. I think it isn't right
> to look at field is_aborted of parser after parser destruction.

sql_parser_destroy() doesn’t affect is_abort field. On the other
hand, mb you are right, and using object after destructor is called
may seem strange. Anyway, replace pls ‘if' with ternary operator:
3 lines of code fit into one.


[-- Attachment #2: Type: text/html, Size: 9343 bytes --]

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 2/8] sql: set SQL parser errors via diag_set()
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 2/8] sql: set SQL parser errors via diag_set() imeevma
  2019-03-14 19:26   ` [tarantool-patches] " n.pettik
@ 2019-03-19 11:24   ` Kirill Yukhin
  1 sibling, 0 replies; 43+ messages in thread
From: Kirill Yukhin @ 2019-03-19 11:24 UTC (permalink / raw)
  To: tarantool-patches; +Cc: korablev

Hello,

On 13 Mar 20:03, imeevma@tarantool.org wrote:
> Hi! Thank you for review! Diff between versions and new version of
> patch below.

I've cherry-picked your patch on to 2.1 branch.

--
Regards, Kirill Yukhin

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 3/8] sql: replace rc with is_aborted status in struct Parse
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 3/8] sql: replace rc with is_aborted status in struct Parse imeevma
  2019-03-14 19:53   ` [tarantool-patches] " n.pettik
@ 2019-03-19 13:17   ` Kirill Yukhin
  1 sibling, 0 replies; 43+ messages in thread
From: Kirill Yukhin @ 2019-03-19 13:17 UTC (permalink / raw)
  To: tarantool-patches; +Cc: korablev

Hello,

On 13 Mar 20:03, imeevma@tarantool.org wrote:
> Hi! Thank you for review! My answers and new version of patch
> below. Here won't be diff between patches as I rewritten this
> patch to change its positions with one that removes nErr.

I've checked your patch into 2.1 branch (added Nikita's nit
> 
> On 3/5/19 12:06 PM, n.pettik wrote:
> > Nit: remove from.
> Fixed.
> 
> >> Currently, the field rc of the struct Parse can have only two
> >> values: SQL_OK or SQL_TARANTOOL_ERROR. Therefore, it is logical to
> >> replace it with a new boolean field. This patche replaces field rc
> >> by new field is_aborted.
> >
> > Fixed commit message (original one contained several mistakes):
> >
> >     sql: replace rc with is_abort status in stuct Parse
> >     
> >     Currently, field representing return code in struct Parse can take only
> >     two values: SQL_OK (successfully finished parsing) and
> >     SQL_TARANTOOL_ERROR (in case of any errors occurred). Therefore, it can
> >     be replaced with a boolean field. Patch provides straightforward
> >     refactoring.
> >     
> >     Part of #3965
> Thanks, fixed.
> 
> >> diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
> >> index 0c6296d..42737ff 100644
> >> --- a/src/box/sql/prepare.c
> >> +++ b/src/box/sql/prepare.c
> >> @@ -100,15 +100,15 @@ sqlPrepare(sql * db,	/* Database handle. */
> >> 	}
> >> 	assert(0 == sParse.nQueryLoop);
> >>
> >> -	if (sParse.rc == SQL_DONE)
> >> -		sParse.rc = SQL_OK;
> >> 	if (db->mallocFailed) {
> >> -		sParse.rc = SQL_NOMEM;
> >> +		diag_set(OutOfMemory, 0, "SQL", "db");
> >> +		sParse.is_aborted = true;
> >
> > See comments for previous patches.
> Fixed in patch "sql: set SQL parser errors via diag_set()".
> 
> >> diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
> >> index 719b85e..42ff4b8 100644
> >> --- a/src/box/sql/sqlInt.h
> >> +++ b/src/box/sql/sqlInt.h
> >> @@ -2643,7 +2643,6 @@ struct Parse {
> >> 	sql *db;		/* The main database structure */
> >> 	char *zErrMsg;		/* An error message */
> >> 	Vdbe *pVdbe;		/* An engine for executing database bytecode */
> >> -	int rc;			/* Return code from execution */
> >> 	u8 colNamesSet;		/* TRUE after OP_ColumnName has been issued to pVdbe */
> >> 	u8 nTempReg;		/* Number of temporary registers in aTempReg[] */
> >> 	u8 isMultiWrite;	/* True if statement may modify/insert multiple rows */
> >> @@ -2677,6 +2676,8 @@ struct Parse {
> >> 	u8 eOrconf;		/* Default ON CONFLICT policy for trigger steps */
> >> 	/** Region to make SQL temp allocations. */
> >> 	struct region region;
> >> +	/** Flag to show that parsing should be aborted. */
> >
> > Comment is misleading: now we don’t abort parsing process,
> > but instead allow it to be finished and raise an error at the end.
> > Fix comment pls.
> Fixed.
> 
> >> +	bool is_aborted;
> >>
> >>   /**************************************************************************
> >>   * Fields above must be initialized to zero.  The fields that follow,
> >> @@ -534,25 +533,17 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
> >> 	sqlParserFree(pEngine, sql_free);
> >> 	if (db->mallocFailed) {
> >> 		diag_set(OutOfMemory, 0, "SQL", "db");
> >> -		pParse->rc = SQL_TARANTOOL_ERROR;
> >> -	}
> >> -	if (pParse->rc != SQL_OK && pParse->rc != SQL_DONE
> >> -	    && pParse->zErrMsg == 0) {
> >> -		const char *error;
> >> -		if (is_tarantool_error(pParse->rc) &&
> >> -		    tarantoolErrorMessage() != NULL)
> >> -			error = tarantoolErrorMessage();
> >> -		else
> >> -			error = sqlErrStr(pParse->rc);
> >> -		pParse->zErrMsg = sqlMPrintf(db, "%s", error);
> >> +		pParse->is_aborted = true;
> >> 	}
> >> +	if (pParse->is_aborted && pParse->zErrMsg == 0)
> >> +		pParse->zErrMsg = sqlMPrintf(db, "%s", tarantoolErrorMessage());
> >> 	assert(pzErrMsg != 0);
> >> 	if (pParse->zErrMsg) {
> >> 		*pzErrMsg = pParse->zErrMsg;
> >> -		sql_log(pParse->rc, "%s", *pzErrMsg);
> >> +		sql_log(SQL_TARANTOOL_ERROR, "%s", *pzErrMsg);
> >
> > Do we need this call at all?
> Fixed, removed.
> 
> >> diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
> >> index bab6493..0d8bf15 100755
> >> --- a/test/sql-tap/check.test.lua
> >> +++ b/test/sql-tap/check.test.lua
> >> @@ -516,7 +516,7 @@ test:do_catchsql_test(
> >>         );
> >>     ]], {
> >>         -- <check-5.1>
> >> -        1, "Wrong space options (field 5): invalid expression specified (SQL error: bindings are not allowed in DDL)"
> >> +        1, "Wrong space options (field 5): invalid expression specified (bindings are not allowed in DDL)"
> >>         -- </check-5.1>
> >>     })
> >
> > Why test results have changed if you provided
> > non-functional refactoring?
> It become this way because now the error in diag instead of being
> only in zErrMsg of struct Parse.
> 
> 
> New version:
> 
> commit ad9f22e790f24598fae717ff1de8992b43ef48c9
> Author: Mergen Imeev <imeevma@gmail.com>
> Date:   Wed Mar 6 22:09:26 2019 +0300
> 
>     sql: replace rc with is_aborted status in struct Parse
>     
>     Currently, field representing return code in struct Parse can take
>     only two values: SQL_OK (successfully finished parsing) and
>     SQL_TARANTOOL_ERROR (in case of any errors occurred). Therefore,
>     it can be replaced with a boolean field. Patch provides
>     straightforward refactoring.
>     
>     Part of #3965
> 
> diff --git a/src/box/sql.c b/src/box/sql.c
> index a2937a0..c2e5d6b 100644
> --- a/src/box/sql.c
> +++ b/src/box/sql.c
> @@ -1275,7 +1275,7 @@ sql_ephemeral_space_def_new(struct Parse *parser, const char *name)
>  	if (def == NULL) {
>  		diag_set(OutOfMemory, size, "region_alloc",
>  			 "sql_ephemeral_space_def_new");
> -		parser->rc = SQL_TARANTOOL_ERROR;
> +		parser->is_aborted = true;
>  		parser->nErr++;
>  		return NULL;
>  	}
> @@ -1294,7 +1294,7 @@ sql_ephemeral_space_new(Parse *parser, const char *name)
>  	struct space *space = (struct space *) region_alloc(&parser->region, sz);
>  	if (space == NULL) {
>  		diag_set(OutOfMemory, sz, "region", "space");
> -		parser->rc = SQL_TARANTOOL_ERROR;
> +		parser->is_aborted = true;
>  		parser->nErr++;
>  		return NULL;
>  	}
> @@ -1363,12 +1363,8 @@ sql_checks_resolve_space_def_reference(ExprList *expr_list,
>  
>  	sql_resolve_self_reference(&parser, def, NC_IsCheck, NULL, expr_list);
>  	int rc = 0;
> -	if (parser.rc != SQL_OK) {
> -		/* Tarantool error may be already set with diag. */
> -		if (parser.rc != SQL_TARANTOOL_ERROR)
> -			diag_set(ClientError, ER_SQL, parser.zErrMsg);
> +	if (parser.is_aborted)
>  		rc = -1;
> -	}
>  	sql_parser_destroy(&parser);
>  	return rc;
>  }
> diff --git a/src/box/sql/alter.c b/src/box/sql/alter.c
> index d49ebb8..fe4754f 100644
> --- a/src/box/sql/alter.c
> +++ b/src/box/sql/alter.c
> @@ -72,7 +72,7 @@ exit_rename_table:
>  	return;
>  tnt_error:
>  	sqlDbFree(db, new_name);
> -	parse->rc = SQL_TARANTOOL_ERROR;
> +	parse->is_aborted = true;
>  	parse->nErr++;
>  	goto exit_rename_table;
>  }
> diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
> index ea5cbc3..96b7099 100644
> --- a/src/box/sql/analyze.c
> +++ b/src/box/sql/analyze.c
> @@ -907,7 +907,7 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space)
>  		if (jump_addrs == NULL) {
>  			diag_set(OutOfMemory, sizeof(int) * part_count,
>  				 "region", "jump_addrs");
> -			parse->rc = SQL_TARANTOOL_ERROR;
> +			parse->is_aborted = true;
>  			parse->nErr++;
>  			return;
>  		}
> @@ -1130,7 +1130,7 @@ sqlAnalyze(Parse * pParse, Token * pName)
>  				}
>  			} else {
>  				diag_set(ClientError, ER_NO_SUCH_SPACE, z);
> -				pParse->rc = SQL_TARANTOOL_ERROR;
> +				pParse->is_aborted = true;
>  				pParse->nErr++;
>  			}
>  			sqlDbFree(db, z);
> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
> index deb5b89..0179a45 100644
> --- a/src/box/sql/build.c
> +++ b/src/box/sql/build.c
> @@ -91,7 +91,7 @@ save_record(struct Parse *parser, uint32_t space_id, int reg_key,
>  	if (record == NULL) {
>  		diag_set(OutOfMemory, sizeof(*record), "region_alloc",
>  			 "record");
> -		parser->rc = SQL_TARANTOOL_ERROR;
> +		parser->is_aborted = true;
>  		parser->nErr++;
>  		return;
>  	}
> @@ -146,8 +146,7 @@ sql_finish_coding(struct Parse *parse_context)
>  	}
>  
>  	if (db->mallocFailed || parse_context->nErr != 0) {
> -		if (parse_context->rc == SQL_OK)
> -			parse_context->rc = SQL_ERROR;
> +		parse_context->is_aborted = true;
>  		return;
>  	}
>  	/*
> @@ -192,10 +191,8 @@ sql_finish_coding(struct Parse *parse_context)
>  	if (parse_context->nErr == 0 && !db->mallocFailed) {
>  		assert(parse_context->iCacheLevel == 0);
>  		sqlVdbeMakeReady(v, parse_context);
> -		parse_context->rc = SQL_DONE;
>  	} else {
> -		if (parse_context->rc != SQL_TARANTOOL_ERROR)
> -			parse_context->rc = SQL_ERROR;
> +		parse_context->is_aborted = true;
>  	}
>  }
>  /**
> @@ -397,7 +394,7 @@ sql_field_retrieve(Parse *parser, struct space_def *space_def, uint32_t id)
>  			diag_set(OutOfMemory, columns_new *
>  				sizeof(space_def->fields[0]),
>  				"region_alloc", "sql_field_retrieve");
> -			parser->rc = SQL_TARANTOOL_ERROR;
> +			parser->is_aborted = true;
>  			parser->nErr++;
>  			return NULL;
>  		}
> @@ -453,7 +450,7 @@ sqlAddColumn(Parse * pParse, Token * pName, struct type_def *type_def)
>  	if (z == NULL) {
>  		diag_set(OutOfMemory, pName->n + 1,
>  			 "region_alloc", "z");
> -		pParse->rc = SQL_TARANTOOL_ERROR;
> +		pParse->is_aborted = true;
>  		pParse->nErr++;
>  		return;
>  	}
> @@ -501,7 +498,7 @@ sql_column_add_nullable_action(struct Parse *parser,
>  				   on_conflict_action_strs[field->
>  							   nullable_action]);
>  		diag_set(ClientError, ER_SQL, err_msg);
> -		parser->rc = SQL_TARANTOOL_ERROR;
> +		parser->is_aborted = true;
>  		parser->nErr++;
>  		return;
>  	}
> @@ -543,7 +540,7 @@ sqlAddDefaultValue(Parse * pParse, ExprSpan * pSpan)
>  				diag_set(OutOfMemory, default_length + 1,
>  					 "region_alloc",
>  					 "field->default_value");
> -				pParse->rc = SQL_TARANTOOL_ERROR;
> +				pParse->is_aborted = true;
>  				pParse->nErr++;
>  				return;
>  			}
> @@ -562,7 +559,7 @@ field_def_create_for_pk(struct Parse *parser, struct field_def *field,
>  	if (field->nullable_action != ON_CONFLICT_ACTION_ABORT &&
>  	    field->nullable_action != ON_CONFLICT_ACTION_DEFAULT) {
>  		diag_set(ClientError, ER_NULLABLE_PRIMARY, space_name);
> -		parser->rc = SQL_TARANTOOL_ERROR;
> +		parser->is_aborted = true;
>  		parser->nErr++;
>  		return -1;
>  	} else if (field->nullable_action == ON_CONFLICT_ACTION_DEFAULT) {
> @@ -851,7 +848,7 @@ vdbe_emit_create_index(struct Parse *parse, struct space_def *def,
>  	save_record(parse, BOX_INDEX_ID, entry_reg, 2, v->nOp - 1);
>  	return;
>  error:
> -	parse->rc = SQL_TARANTOOL_ERROR;
> +	parse->is_aborted = true;
>  	parse->nErr++;
>  
>  }
> @@ -912,7 +909,7 @@ createSpace(Parse * pParse, int iSpaceId, char *zStmt)
>  	return;
>  error:
>  	pParse->nErr++;
> -	pParse->rc = SQL_TARANTOOL_ERROR;
> +	pParse->is_aborted = true;
>  }
>  
>  int
> @@ -1093,7 +1090,7 @@ vdbe_emit_fk_constraint_create(struct Parse *parse_context,
>  	return;
>  error:
>  	parse_context->nErr++;
> -	parse_context->rc = SQL_TARANTOOL_ERROR;
> +	parse_context->is_aborted = true;
>  }
>  
>  /**
> @@ -1121,7 +1118,7 @@ resolve_link(struct Parse *parse_context, const struct space_def *def,
>  	diag_set(ClientError, ER_CREATE_FK_CONSTRAINT, fk_name,
>  		 tt_sprintf("unknown column %s in foreign key definition",
>  			    field_name));
> -	parse_context->rc = SQL_TARANTOOL_ERROR;
> +	parse_context->is_aborted = true;
>  	parse_context->nErr++;
>  	return -1;
>  }
> @@ -1253,7 +1250,7 @@ sqlEndTable(Parse * pParse,	/* Parse context */
>  					 "foreign key does not match the "\
>  					 "number of columns in the primary "\
>  					 "index of referenced table");
> -				pParse->rc = SQL_TARANTOOL_ERROR;
> +				pParse->is_aborted = true;
>  				pParse->nErr++;
>  				return;
>  			}
> @@ -1329,7 +1326,7 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
>  	space->def->opts.sql = strndup(begin->z, n);
>  	if (space->def->opts.sql == NULL) {
>  		diag_set(OutOfMemory, n, "strndup", "opts.sql");
> -		parse_context->rc = SQL_TARANTOOL_ERROR;
> +		parse_context->is_aborted = true;
>  		parse_context->nErr++;
>  		goto create_view_fail;
>  	}
> @@ -1649,7 +1646,7 @@ sql_drop_table(struct Parse *parse_context, struct SrcList *table_name_list,
>  		if (! fk_constraint_is_self_referenced(fk->def)) {
>  			diag_set(ClientError, ER_DROP_SPACE, space_name,
>  				 "other objects depend on it");
> -			parse_context->rc = SQL_TARANTOOL_ERROR;
> +			parse_context->is_aborted = true;
>  			parse_context->nErr++;
>  			goto exit_drop_table;
>  		}
> @@ -1685,7 +1682,7 @@ columnno_by_name(struct Parse *parse_context, const struct space *space,
>  		diag_set(ClientError, ER_CREATE_FK_CONSTRAINT, fk_name,
>  			 tt_sprintf("foreign key refers to nonexistent field %s",
>  				    column_name));
> -		parse_context->rc = SQL_TARANTOOL_ERROR;
> +		parse_context->is_aborted = true;
>  		parse_context->nErr++;
>  		return -1;
>  	}
> @@ -1895,7 +1892,7 @@ exit_create_fk:
>  	sqlDbFree(db, constraint_name);
>  	return;
>  tnt_error:
> -	parse_context->rc = SQL_TARANTOOL_ERROR;
> +	parse_context->is_aborted = true;
>  	parse_context->nErr++;
>  	goto exit_create_fk;
>  }
> @@ -1920,7 +1917,7 @@ sql_drop_foreign_key(struct Parse *parse_context, struct SrcList *table,
>  	struct space *child = space_by_name(table_name);
>  	if (child == NULL) {
>  		diag_set(ClientError, ER_NO_SUCH_SPACE, table_name);
> -		parse_context->rc = SQL_TARANTOOL_ERROR;
> +		parse_context->is_aborted = true;
>  		parse_context->nErr++;
>  		return;
>  	}
> @@ -2099,7 +2096,7 @@ cleanup:
>  		key_def_delete(key_def);
>  	return rc;
>  tnt_error:
> -	parse->rc = SQL_TARANTOOL_ERROR;
> +	parse->is_aborted = true;
>  	++parse->nErr;
>  	goto cleanup;
>  }
> @@ -2150,7 +2147,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
>  		if (space == NULL) {
>  			if (! if_not_exist) {
>  				diag_set(ClientError, ER_NO_SUCH_SPACE, name);
> -				parse->rc = SQL_TARANTOOL_ERROR;
> +				parse->is_aborted = true;
>  				parse->nErr++;
>  			}
>  			goto exit_create_index;
> @@ -2245,7 +2242,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
>  		diag_set(ClientError, ER_MODIFY_INDEX, name, def->name,
>  			 "can't create index on system space");
>  		parse->nErr++;
> -		parse->rc = SQL_TARANTOOL_ERROR;
> +		parse->is_aborted = true;
>  		goto exit_create_index;
>  	}
>  
> @@ -2273,7 +2270,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
>  	index = (struct index *) region_alloc(&parse->region, sizeof(*index));
>  	if (index == NULL) {
>  		diag_set(OutOfMemory, sizeof(*index), "region", "index");
> -		parse->rc = SQL_TARANTOOL_ERROR;
> +		parse->is_aborted = true;
>  		parse->nErr++;
>  		goto exit_create_index;
>  	}
> diff --git a/src/box/sql/callback.c b/src/box/sql/callback.c
> index 4594cac..5c9cf98 100644
> --- a/src/box/sql/callback.c
> +++ b/src/box/sql/callback.c
> @@ -49,7 +49,7 @@ sql_get_coll_seq(Parse *parser, const char *name, uint32_t *coll_id)
>  	struct coll_id *p = coll_by_name(name, strlen(name));
>  	if (p == NULL) {
>  		diag_set(ClientError, ER_NO_SUCH_COLLATION, name);
> -		parser->rc = SQL_TARANTOOL_ERROR;
> +		parser->is_aborted = true;
>  		parser->nErr++;
>  		return NULL;
>  	} else {
> diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
> index 5170c7f..3123681 100644
> --- a/src/box/sql/delete.c
> +++ b/src/box/sql/delete.c
> @@ -50,7 +50,7 @@ sql_lookup_space(struct Parse *parse, struct SrcList_item *space_name)
>  	if (space->def->field_count == 0) {
>  		diag_set(ClientError, ER_UNSUPPORTED, "SQL",
>  			 "space without format");
> -		parse->rc = SQL_TARANTOOL_ERROR;
> +		parse->is_aborted = true;
>  		parse->nErr++;
>  		return NULL;
>  	}
> @@ -116,7 +116,7 @@ cleanup:
>  	return;
>  
>  tarantool_error:
> -	parse->rc = SQL_TARANTOOL_ERROR;
> +	parse->is_aborted = true;
>  	parse->nErr++;
>  	goto cleanup;
>  }
> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index 82688df..39b747d 100644
> --- a/src/box/sql/expr.c
> +++ b/src/box/sql/expr.c
> @@ -258,7 +258,7 @@ sql_expr_coll(Parse *parse, Expr *p, bool *is_explicit_coll, uint32_t *coll_id,
>  						diag_set(ClientError,
>  							 ER_ILLEGAL_COLLATION_MIX);
>  						parse->nErr++;
> -						parse->rc = SQL_TARANTOOL_ERROR;
> +						parse->is_aborted = true;
>  					}
>  					return -1;
>  				}
> @@ -433,7 +433,7 @@ sql_binary_compare_coll_seq(Parse *parser, Expr *left, Expr *right,
>  		return -1;
>  	if (collations_check_compatibility(lhs_coll_id, is_lhs_forced,
>  					   rhs_coll_id, is_rhs_forced, id) != 0) {
> -		parser->rc = SQL_TARANTOOL_ERROR;
> +		parser->is_aborted = true;
>  		parser->nErr++;
>  		return -1;
>  	}
> diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
> index 8b909f2..d8a0bda 100644
> --- a/src/box/sql/pragma.c
> +++ b/src/box/sql/pragma.c
> @@ -506,7 +506,7 @@ sqlPragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
>  		box_iterator_t* iter;
>  		iter = box_index_iterator(space->def->id, 0,ITER_ALL, key_buf, key_end);
>  		if (iter == NULL) {
> -			pParse->rc = SQL_TARANTOOL_ERROR;
> +			pParse->is_aborted = true;
>  			pParse->nErr++;
>  			goto pragma_out;
>  		}
> @@ -565,7 +565,7 @@ sqlPragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
>  		if (!token_is_string(pValue)) {
>  			diag_set(ClientError, ER_ILLEGAL_PARAMS,
>  				 "string value is expected");
> -			pParse->rc = SQL_TARANTOOL_ERROR;
> +			pParse->is_aborted = true;
>  			pParse->nErr++;
>  			goto pragma_out;
>  		}
> @@ -577,7 +577,7 @@ sqlPragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
>  			sqlVdbeAddOp2(v, OP_ResultRow, 1, 1);
>  		} else {
>  			if (sql_default_engine_set(zRight) != 0) {
> -				pParse->rc = SQL_TARANTOOL_ERROR;
> +				pParse->is_aborted = true;
>  				pParse->nErr++;
>  				goto pragma_out;
>  			}
> diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
> index 828a1ae..85385ee 100644
> --- a/src/box/sql/prepare.c
> +++ b/src/box/sql/prepare.c
> @@ -52,7 +52,6 @@ sqlPrepare(sql * db,	/* Database handle. */
>  	       const char **pzTail	/* OUT: End of parsed string */
>      )
>  {
> -	char *zErrMsg = 0;	/* Error message */
>  	int rc = SQL_OK;	/* Result code */
>  	Parse sParse;		/* Parsing context */
>  	sql_parser_create(&sParse, db);
> @@ -89,25 +88,24 @@ sqlPrepare(sql * db,	/* Database handle. */
>  		}
>  		zSqlCopy = sqlDbStrNDup(db, zSql, nBytes);
>  		if (zSqlCopy) {
> -			sqlRunParser(&sParse, zSqlCopy, &zErrMsg);
> +			sqlRunParser(&sParse, zSqlCopy);
>  			sParse.zTail = &zSql[sParse.zTail - zSqlCopy];
>  			sqlDbFree(db, zSqlCopy);
>  		} else {
>  			sParse.zTail = &zSql[nBytes];
>  		}
>  	} else {
> -		sqlRunParser(&sParse, zSql, &zErrMsg);
> +		sqlRunParser(&sParse, zSql);
>  	}
>  	assert(0 == sParse.nQueryLoop);
>  
> -	if (sParse.rc == SQL_DONE)
> -		sParse.rc = SQL_OK;
>  	if (db->mallocFailed)
> -		sParse.rc = SQL_TARANTOOL_ERROR;
> +		sParse.is_aborted = true;
>  	if (pzTail) {
>  		*pzTail = sParse.zTail;
>  	}
> -	rc = sParse.rc;
> +	if (sParse.is_aborted)
> +		rc = SQL_TARANTOOL_ERROR;
>  
>  	if (rc == SQL_OK && sParse.pVdbe && sParse.explain) {
>  		static const char *const azColName[] = {
> @@ -168,11 +166,7 @@ sqlPrepare(sql * db,	/* Database handle. */
>  		*ppStmt = (sql_stmt *) sParse.pVdbe;
>  	}
>  
> -	if (zErrMsg) {
> -		sqlErrorWithMsg(db, rc, "%s", zErrMsg);
> -	} else {
> -		sqlError(db, rc);
> -	}
> +	db->errCode = rc;
>  
>  	/* Delete any TriggerPrg structures allocated while parsing this statement. */
>  	while (sParse.pTriggerPrg) {
> diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
> index 49b0052..5b9d216 100644
> --- a/src/box/sql/resolve.c
> +++ b/src/box/sql/resolve.c
> @@ -1341,7 +1341,7 @@ resolveSelectStep(Walker * pWalker, Select * p)
>  					 "clause or be used in an aggregate "
>  					 "function");
>  				pParse->nErr++;
> -				pParse->rc = SQL_TARANTOOL_ERROR;
> +				pParse->is_aborted = true;
>  				return WRC_Abort;
>  			}
>  			/*
> diff --git a/src/box/sql/select.c b/src/box/sql/select.c
> index ef24760..7185a10 100644
> --- a/src/box/sql/select.c
> +++ b/src/box/sql/select.c
> @@ -2235,7 +2235,7 @@ multi_select_coll_seq_r(struct Parse *parser, struct Select *p, int n,
>  	if (collations_check_compatibility(prior_coll_id, is_prior_forced,
>  					   current_coll_id, is_current_forced,
>  					   &res_coll_id) != 0) {
> -		parser->rc = SQL_TARANTOOL_ERROR;
> +		parser->is_aborted = true;
>  		parser->nErr++;
>  		return 0;
>  	}
> diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
> index eb14885..dd21091 100644
> --- a/src/box/sql/sqlInt.h
> +++ b/src/box/sql/sqlInt.h
> @@ -2655,7 +2655,6 @@ struct Parse {
>  	sql *db;		/* The main database structure */
>  	char *zErrMsg;		/* An error message */
>  	Vdbe *pVdbe;		/* An engine for executing database bytecode */
> -	int rc;			/* Return code from execution */
>  	u8 colNamesSet;		/* TRUE after OP_ColumnName has been issued to pVdbe */
>  	u8 nTempReg;		/* Number of temporary registers in aTempReg[] */
>  	u8 isMultiWrite;	/* True if statement may modify/insert multiple rows */
> @@ -2690,6 +2689,8 @@ struct Parse {
>  	u8 eOrconf;		/* Default ON CONFLICT policy for trigger steps */
>  	/** Region to make SQL temp allocations. */
>  	struct region region;
> +	/** True, if error should be raised after parsing. */
> +	bool is_aborted;
>  
>    /**************************************************************************
>    * Fields above must be initialized to zero.  The fields that follow,
> @@ -3200,7 +3201,7 @@ void sqlDequote(char *);
>  void sqlNormalizeName(char *z);
>  void sqlTokenInit(Token *, char *);
>  int sqlKeywordCode(const unsigned char *, int);
> -int sqlRunParser(Parse *, const char *, char **);
> +int sqlRunParser(Parse *, const char *);
>  
>  /**
>   * Increment error counter.
> diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
> index 834c165..5c23ec0 100644
> --- a/src/box/sql/tokenize.c
> +++ b/src/box/sql/tokenize.c
> @@ -437,17 +437,17 @@ parser_space_delete(struct sql *db, struct space *space)
>  	sql_expr_list_delete(db, space->def->opts.checks);
>  }
>  
> -/*
> - * Run the parser on the given SQL string.  The parser structure is
> - * passed in.  An SQL_ status code is returned.  If an error occurs
> - * then an and attempt is made to write an error message into
> - * memory obtained from sql_malloc() and to make *pzErrMsg point to that
> - * error message.
> +/**
> + * Run the parser on the given SQL string.
> + *
> + * @param pParse Parser context.
> + * @param zSql SQL string.
> + * @retval 0 on success.
> + * @retval -1 on error.
>   */
>  int
> -sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
> +sqlRunParser(Parse * pParse, const char *zSql)
>  {
> -	int nErr = 0;		/* Number of errors encountered */
>  	int i;			/* Loop counter */
>  	void *pEngine;		/* The LEMON-generated LALR(1) parser */
>  	int tokenType;		/* type of the next token */
> @@ -460,15 +460,13 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
>  	if (db->nVdbeActive == 0) {
>  		db->u1.isInterrupted = 0;
>  	}
> -	pParse->rc = SQL_OK;
>  	pParse->zTail = zSql;
>  	i = 0;
> -	assert(pzErrMsg != 0);
>  	/* sqlParserTrace(stdout, "parser: "); */
>  	pEngine = sqlParserAlloc(sqlMalloc);
>  	if (pEngine == 0) {
>  		sqlOomFault(db);
> -		return SQL_NOMEM;
> +		return -1;
>  	}
>  	assert(pParse->new_space == NULL);
>  	assert(pParse->parsed_ast.trigger == NULL);
> @@ -485,7 +483,7 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
>  			if (i > mxSqlLen) {
>  				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
>  					 "string or blob too big");
> -				pParse->rc = SQL_TARANTOOL_ERROR;
> +				pParse->is_aborted = true;
>  				break;
>  			}
>  		} else {
> @@ -506,7 +504,7 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
>  			if (db->u1.isInterrupted) {
>  				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
>  					 "interrupted");
> -				pParse->rc = SQL_TARANTOOL_ERROR;
> +				pParse->is_aborted = true;
>  				break;
>  			}
>  			if (tokenType == TK_ILLEGAL) {
> @@ -520,11 +518,10 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
>  			sqlParser(pEngine, tokenType, pParse->sLastToken,
>  				      pParse);
>  			lastTokenParsed = tokenType;
> -			if (pParse->rc != SQL_OK || db->mallocFailed)
> +			if (pParse->is_aborted || db->mallocFailed)
>  				break;
>  		}
>  	}
> -	assert(nErr == 0);
>  	pParse->zTail = &zSql[i];
>  #ifdef YYTRACKMAXSTACKDEPTH
>  	sqlStatusHighwater(SQL_STATUS_PARSER_STACK,
> @@ -532,25 +529,10 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
>  	    );
>  #endif				/* YYDEBUG */
>  	sqlParserFree(pEngine, sql_free);
> -	if (db->mallocFailed) {
> -		pParse->rc = SQL_TARANTOOL_ERROR;
> -	}
> -	if (pParse->rc != SQL_OK && pParse->rc != SQL_DONE
> -	    && pParse->zErrMsg == 0) {
> -		const char *error;
> -		if (is_tarantool_error(pParse->rc) &&
> -		    tarantoolErrorMessage() != NULL)
> -			error = tarantoolErrorMessage();
> -		else
> -			error = sqlErrStr(pParse->rc);
> -		pParse->zErrMsg = sqlMPrintf(db, "%s", error);
> -	}
> -	assert(pzErrMsg != 0);
> -	if (pParse->zErrMsg) {
> -		*pzErrMsg = pParse->zErrMsg;
> -		sql_log(pParse->rc, "%s", *pzErrMsg);
> -		nErr++;
> -	}
> +	if (db->mallocFailed)
> +		pParse->is_aborted = true;
> +	if (pParse->is_aborted && pParse->zErrMsg == 0)
> +		pParse->zErrMsg = sqlMPrintf(db, "%s", tarantoolErrorMessage());
>  	if (pParse->pVdbe != NULL && pParse->nErr > 0) {
>  		sqlVdbeDelete(pParse->pVdbe);
>  		pParse->pVdbe = 0;
> @@ -559,8 +541,9 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
>  	if (pParse->pWithToFree)
>  		sqlWithDelete(db, pParse->pWithToFree);
>  	sqlDbFree(db, pParse->pVList);
> -	assert(nErr == 0 || pParse->rc != SQL_OK);
> -	return nErr;
> +	if (pParse->is_aborted)
> +		return -1;
> +	return 0;
>  }
>  
>  struct Expr *
> @@ -581,11 +564,8 @@ sql_expr_compile(sql *db, const char *expr, int expr_len)
>  	}
>  	sprintf(stmt, "%s%.*s", outer, expr_len, expr);
>  
> -	char *sql_error = NULL;
> -	if (sqlRunParser(&parser, stmt, &sql_error) != SQL_OK ||
> -	    parser.parsed_ast_type != AST_TYPE_EXPR) {
> -		diag_set(ClientError, ER_SQL, sql_error);
> -	} else {
> +	if (sqlRunParser(&parser, stmt) == 0 &&
> +	    parser.parsed_ast_type == AST_TYPE_EXPR) {
>  		expression = parser.parsed_ast.expr;
>  		parser.parsed_ast.expr = NULL;
>  	}
> @@ -603,8 +583,7 @@ sql_view_compile(struct sql *db, const char *view_stmt)
>  
>  	struct Select *select = NULL;
>  
> -	char *unused;
> -	if (sqlRunParser(&parser, view_stmt, &unused) != SQL_OK ||
> +	if (sqlRunParser(&parser, view_stmt) != 0 ||
>  	    parser.parsed_ast_type != AST_TYPE_SELECT) {
>  		diag_set(ClientError, ER_SQL_EXECUTE, view_stmt);
>  	} else {
> @@ -622,13 +601,9 @@ sql_trigger_compile(struct sql *db, const char *sql)
>  	struct Parse parser;
>  	sql_parser_create(&parser, db);
>  	parser.parse_only = true;
> -	char *sql_error = NULL;
>  	struct sql_trigger *trigger = NULL;
> -	if (sqlRunParser(&parser, sql, &sql_error) != SQL_OK ||
> -	    parser.parsed_ast_type != AST_TYPE_TRIGGER) {
> -	    if (parser.rc != SQL_TARANTOOL_ERROR)
> -		diag_set(ClientError, ER_SQL, sql_error);
> -	} else {
> +	if (sqlRunParser(&parser, sql) == 0 &&
> +	    parser.parsed_ast_type == AST_TYPE_TRIGGER) {
>  		trigger = parser.parsed_ast.trigger;
>  		parser.parsed_ast.trigger = NULL;
>  	}
> diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
> index f7e6189..5ee0d96 100644
> --- a/src/box/sql/trigger.c
> +++ b/src/box/sql/trigger.c
> @@ -154,7 +154,7 @@ sql_trigger_begin(struct Parse *parse, struct Token *name, int tr_tm,
>  	return;
>  
>  set_tarantool_error_and_cleanup:
> -	parse->rc = SQL_TARANTOOL_ERROR;
> +	parse->is_aborted = true;
>  	parse->nErr++;
>  	goto trigger_cleanup;
>  }
> @@ -735,7 +735,7 @@ transferParseError(Parse * pTo, Parse * pFrom)
>  	if (pTo->nErr == 0) {
>  		pTo->zErrMsg = pFrom->zErrMsg;
>  		pTo->nErr = pFrom->nErr;
> -		pTo->rc = pFrom->rc;
> +		pTo->is_aborted = pFrom->is_aborted;
>  	} else {
>  		sqlDbFree(pFrom->db, pFrom->zErrMsg);
>  	}
> diff --git a/src/box/sql/util.c b/src/box/sql/util.c
> index a6d1f5c..5aa4fda 100644
> --- a/src/box/sql/util.c
> +++ b/src/box/sql/util.c
> @@ -212,7 +212,7 @@ sqlErrorWithMsg(sql * db, int err_code, const char *zFormat, ...)
>  
>  /*
>   * Add an error to the diagnostics area, increment pParse->nErr
> - * and set pParse->rc.
> + * and set pParse->is_aborted.
>   * The following formatting characters are allowed:
>   *
>   *      %s      Insert a string
> @@ -240,14 +240,14 @@ sqlErrorMsg(Parse * pParse, const char *zFormat, ...)
>  	diag_set(ClientError, ER_SQL_PARSER_GENERIC, zMsg);
>  	sqlDbFree(db, zMsg);
>  	pParse->nErr++;
> -	pParse->rc = SQL_TARANTOOL_ERROR;
> +	pParse->is_aborted = true;
>  }
>  
>  void
>  sql_parser_error(struct Parse *parse_context)
>  {
>  	parse_context->nErr++;
> -	parse_context->rc = SQL_TARANTOOL_ERROR;
> +	parse_context->is_aborted = true;
>  }
>  
>  /*
> diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
> index 074ff8c..f417c49 100644
> --- a/src/box/sql/vdbemem.c
> +++ b/src/box/sql/vdbemem.c
> @@ -1203,7 +1203,7 @@ valueFromFunction(sql * db,	/* The database connection */
>  		goto value_from_function_out;
>  	}
>  
> -	assert(pCtx->pParse->rc == SQL_OK);
> +	assert(!pCtx->pParse->is_aborted);
>  	memset(&ctx, 0, sizeof(ctx));
>  	ctx.pOut = pVal;
>  	ctx.pFunc = pFunc;
> @@ -1215,7 +1215,8 @@ valueFromFunction(sql * db,	/* The database connection */
>  		sql_value_apply_type(pVal, type);
>  		assert(rc == SQL_OK);
>  	}
> -	pCtx->pParse->rc = rc;
> +	if (rc != SQL_OK)
> +		pCtx->pParse->is_aborted = true;
>  
>   value_from_function_out:
>  	if (rc != SQL_OK) {
> diff --git a/src/box/sql/where.c b/src/box/sql/where.c
> index 5a3c9be..6c5c61e 100644
> --- a/src/box/sql/where.c
> +++ b/src/box/sql/where.c
> @@ -2800,7 +2800,7 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder,	/* WHERE clause information */
>  		if (key_def == NULL) {
>  tnt_error:
>  			pWInfo->pParse->nErr++;
> -			pWInfo->pParse->rc = SQL_TARANTOOL_ERROR;
> +			pWInfo->pParse->is_aborted = true;
>  			return SQL_TARANTOOL_ERROR;
>  		}
>  
> diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
> index bab6493..0d8bf15 100755
> --- a/test/sql-tap/check.test.lua
> +++ b/test/sql-tap/check.test.lua
> @@ -516,7 +516,7 @@ test:do_catchsql_test(
>          );
>      ]], {
>          -- <check-5.1>
> -        1, "Wrong space options (field 5): invalid expression specified (SQL error: bindings are not allowed in DDL)"
> +        1, "Wrong space options (field 5): invalid expression specified (bindings are not allowed in DDL)"
>          -- </check-5.1>
>      })
>  
> @@ -528,7 +528,7 @@ test:do_catchsql_test(
>          );
>      ]], {
>          -- <check-5.2>
> -        1, "Wrong space options (field 5): invalid expression specified (SQL error: bindings are not allowed in DDL)"
> +        1, "Wrong space options (field 5): invalid expression specified (bindings are not allowed in DDL)"
>          -- </check-5.2>
>      })
>  
> diff --git a/test/sql/checks.result b/test/sql/checks.result
> index e31964c..42df657 100644
> --- a/test/sql/checks.result
> +++ b/test/sql/checks.result
> @@ -29,8 +29,8 @@ t = {513, 1, 'test', 'memtx', 0, opts, format}
>  ...
>  s = box.space._space:insert(t)
>  ---
> -- error: 'Wrong space options (field 5): invalid expression specified (SQL error:
> -    Syntax error near ''<'')'
> +- error: 'Wrong space options (field 5): invalid expression specified (Syntax error
> +    near ''<'')'
>  ...
>  opts = {checks = {{expr = 'X>5'}}}
>  ---
> @@ -122,8 +122,8 @@ box.sql.execute("DROP TABLE w2;")
>  --
>  box.sql.execute("CREATE TABLE t5(x INT PRIMARY KEY, y INT, CHECK( x*y < ? ));")
>  ---
> -- error: 'Wrong space options (field 5): invalid expression specified (SQL error:
> -    bindings are not allowed in DDL)'
> +- error: 'Wrong space options (field 5): invalid expression specified (bindings are
> +    not allowed in DDL)'
>  ...
>  opts = {checks = {{expr = '?>5', name = 'ONE'}}}
>  ---
> @@ -136,8 +136,8 @@ t = {513, 1, 'test', 'memtx', 0, opts, format}
>  ...
>  s = box.space._space:insert(t)
>  ---
> -- error: 'Wrong space options (field 5): invalid expression specified (SQL error:
> -    bindings are not allowed in DDL)'
> +- error: 'Wrong space options (field 5): invalid expression specified (bindings are
> +    not allowed in DDL)'
>  ...
>  test_run:cmd("clear filter")
>  ---
> -- 
> 2.7.4
> 
> 

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 5/8] sql: remove field zErrMsg from struct Parse
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 5/8] sql: remove field zErrMsg " imeevma
  2019-03-14 22:15   ` [tarantool-patches] " n.pettik
@ 2019-03-19 13:20   ` Kirill Yukhin
  1 sibling, 0 replies; 43+ messages in thread
From: Kirill Yukhin @ 2019-03-19 13:20 UTC (permalink / raw)
  To: tarantool-patches; +Cc: korablev

Hello,

On 13 Mar 20:03, imeevma@tarantool.org wrote:
> Hi! Thank you for review! My answers and new version of patch
> below. Here won't be diff between patches due to some of its code
> were moved to previous patches.

I've checked your patch into 2.1 branch.

--
Regards, Kirill Yukhin

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 4/8] sql: remove field nErr from struct Parse
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 4/8] sql: remove field nErr from " imeevma
  2019-03-14 19:58   ` [tarantool-patches] " n.pettik
@ 2019-03-19 13:27   ` Kirill Yukhin
  1 sibling, 0 replies; 43+ messages in thread
From: Kirill Yukhin @ 2019-03-19 13:27 UTC (permalink / raw)
  To: tarantool-patches; +Cc: korablev

Hello,

On 13 Mar 20:03, imeevma@tarantool.org wrote:
> Hi! Thank you for review! My answers and new version of patch
> below. Here won't be diff between patches as I rewritten this
> patch to change its positions with one that removes rc.

I've rebased & cherry-picked your patch onto 2.1 branch.

--
Regards, Kirill Yukhin

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 6/8] sql: rework three errors of "unsupported" type
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 6/8] sql: rework three errors of "unsupported" type imeevma
  2019-03-14 22:15   ` [tarantool-patches] " n.pettik
@ 2019-03-19 13:30   ` Kirill Yukhin
  1 sibling, 0 replies; 43+ messages in thread
From: Kirill Yukhin @ 2019-03-19 13:30 UTC (permalink / raw)
  To: tarantool-patches; +Cc: korablev

Hello,

On 13 Mar 20:03, imeevma@tarantool.org wrote:
> Three errors of "unsupported" type were reworked in this patch.
> 
> Part of #3965
> ---
>  src/box/errcode.h              |  1 +
>  src/box/sql/analyze.c          |  7 ++++---
>  src/box/sql/select.c           | 10 ++++++----
>  test/box/misc.result           |  1 +
>  test/sql-tap/analyzeD.test.lua |  2 +-
>  test/sql-tap/join.test.lua     |  2 +-
>  test/sql-tap/with1.test.lua    |  2 +-
>  7 files changed, 15 insertions(+), 10 deletions(-)

I've cherry-picked your patch onto 2.1 branch.

--
Regards, Kirill Yukhin

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 7/8] sql: rework semantic errors
  2019-03-15 15:49   ` [tarantool-patches] " n.pettik
@ 2019-03-22 12:48     ` Mergen Imeev
  2019-03-26 14:14       ` n.pettik
  0 siblings, 1 reply; 43+ messages in thread
From: Mergen Imeev @ 2019-03-22 12:48 UTC (permalink / raw)
  To: n.pettik; +Cc: tarantool-patches

Hi! Thank you for review! My answers with fixes and new version
below.

On Fri, Mar 15, 2019 at 06:49:20PM +0300, n.pettik wrote:
> 
> > diff --git a/src/box/errcode.h b/src/box/errcode.h
> > index 7764aa3..a0d8f4ae 100644
> > --- a/src/box/errcode.h
> > +++ b/src/box/errcode.h
> > @@ -122,7 +122,7 @@ struct errcode_record {
> > 	/* 67 */_(ER_REPLICA_ID_IS_RESERVED,	"Can't initialize replica id with a reserved value %u") \
> > 	/* 68 */_(ER_INVALID_ORDER,		"Invalid LSN order for instance %u: previous LSN = %llu, new lsn = %llu") \
> > 	/* 69 */_(ER_MISSING_REQUEST_FIELD,	"Missing mandatory field '%s' in request") \
> > -	/* 70 */_(ER_IDENTIFIER,		"Invalid identifier '%s' (expected printable symbols only)") \
> > +	/* 70 */_(ER_IDENTIFIER,		"Invalid identifier '%s' (expected printable symbols only or it is too long)") \
> > 	/* 71 */_(ER_DROP_FUNCTION,		"Can't drop function %u: %s") \
> > 	/* 72 */_(ER_ITERATOR_TYPE,		"Unknown iterator type '%s'") \
> > 	/* 73 */_(ER_REPLICA_MAX,		"Replica count limit reached: %u") \
> > @@ -240,6 +240,13 @@ struct errcode_record {
> > 	/*185 */_(ER_SQL_UNKNOWN_TOKEN,		"Syntax error: unrecognized token: '%.*s'") \
> > 	/*186 */_(ER_SQL_PARSER_GENERIC,	"%s") \
> > 	/*187 */_(ER_SQL_ANALYZE_ARGUMENT,	"ANALYZE statement argument %s is not a base table") \
> > +	/*188 */_(ER_SQL_COLUMN_COUNT_MAX,	"Failed to create space '%s': space column count %d exceeds the limit (%d)") \
> > +	/*189 */_(ER_HEX_LITERAL_MAX,		"Hex literal %s%s length %d exceeds the supported limit (%d)") \
> > +	/*190 */_(ER_INT_LITERAL_MAX,		"Integer literal %s%s exceeds the supported range %lld - %lld") \
> > +	/*191 */_(ER_SQL_PARSER_LIMIT,		"%s%.*s %d exceeds the limit (%d)") \
> 
> You use auxiliary params only in one place. I guess it is worth
> removing them.
> 
Fixed:

diff --git a/src/box/errcode.h b/src/box/errcode.h
index a0d8f4ae..b38bcc3 100644
--- a/src/box/errcode.h
+++ b/src/box/errcode.h
@@ -243,7 +243,7 @@ struct errcode_record {
 	/*188 */_(ER_SQL_COLUMN_COUNT_MAX,	"Failed to create space '%s': space column count %d exceeds the limit (%d)") \
 	/*189 */_(ER_HEX_LITERAL_MAX,		"Hex literal %s%s length %d exceeds the supported limit (%d)") \
 	/*190 */_(ER_INT_LITERAL_MAX,		"Integer literal %s%s exceeds the supported range %lld - %lld") \
-	/*191 */_(ER_SQL_PARSER_LIMIT,		"%s%.*s %d exceeds the limit (%d)") \
+	/*191 */_(ER_SQL_PARSER_LIMIT,		"%s %d exceeds the limit (%d)") \
 	/*192 */_(ER_INDEX_DEF,			"%s prohibited in an index definition") \
 	/*193 */_(ER_CHECK_CONSTRAINT_DEF,	"%s prohibited in a CHECK constraint definition") \
 	/*194 */_(ER_PRIMARY_KEY_DEF,		"Expressions are prohibited in a primary key definition") \
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 26434b1..c714a06 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -2258,7 +2258,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
 	} else {
 		if (col_list->nExpr > db->aLimit[SQL_LIMIT_COLUMN]) {
 			diag_set(ClientError, ER_SQL_PARSER_LIMIT,
-				 "The number of columns in index", 0, "",
+				 "The number of columns in index",
 				 col_list->nExpr, db->aLimit[SQL_LIMIT_COLUMN]);
 			parse->is_aborted = true;
 		}
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 434b13f..f38c3c8 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -753,7 +753,7 @@ sqlExprCheckHeight(Parse * pParse, int nHeight)
 	int mxHeight = pParse->db->aLimit[SQL_LIMIT_EXPR_DEPTH];
 	if (nHeight > mxHeight) {
 		diag_set(ClientError, ER_SQL_PARSER_LIMIT, "Number of nodes "\
-			 "in expression tree", 0, "", nHeight, mxHeight);
+			 "in expression tree", nHeight, mxHeight);
 		pParse->is_aborted = true;
 		return SQL_ERROR;
 	}
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index ee8da3e..ff668e0 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -411,7 +411,7 @@ cmd ::= select(X).  {
         cnt>mxSelect
       ){
          diag_set(ClientError, ER_SQL_PARSER_LIMIT, "The number of UNION or "\
-                  "EXCEPT or INTERSECT operations", 0, "", cnt,
+                  "EXCEPT or INTERSECT operations", cnt,
                   pParse->db->aLimit[SQL_LIMIT_COMPOUND_SELECT]);
          pParse->is_aborted = true;
       }
@@ -747,7 +747,7 @@ cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
   sqlSrcListIndexedBy(pParse, X, &I);
   if (Y != NULL && Y->nExpr > pParse->db->aLimit[SQL_LIMIT_COLUMN]) {
     diag_set(ClientError, ER_SQL_PARSER_LIMIT, "The number of columns in set "\
-             "list", 0, "", Y->nExpr, pParse->db->aLimit[SQL_LIMIT_COLUMN]);
+             "list", Y->nExpr, pParse->db->aLimit[SQL_LIMIT_COLUMN]);
     pParse->is_aborted = true;
   }
   sqlSubProgramsRemaining = SQL_MAX_COMPILING_TRIGGERS;
@@ -937,8 +937,9 @@ expr(A) ::= CAST(X) LP expr(E) AS typedef(T) RP(Y). {
 %endif  SQL_OMIT_CAST
 expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP(E). {
   if( Y && Y->nExpr>pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG] ){
-    diag_set(ClientError, ER_SQL_PARSER_LIMIT, "Number of "\
-             "arguments to function ", X.n, X.z, Y->nExpr,
+    const char *err =
+      tt_sprintf("Number of arguments to function %.*s", X.n, X.z);
+    diag_set(ClientError, ER_SQL_PARSER_LIMIT, err, Y->nExpr,
              pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]);
     pParse->is_aborted = true;
   }
@@ -956,8 +957,9 @@ expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP(E). {
 type_func(A) ::= CHAR(A) .
 expr(A) ::= type_func(X) LP distinct(D) exprlist(Y) RP(E). {
   if( Y && Y->nExpr>pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG] ){
-    diag_set(ClientError, ER_SQL_PARSER_LIMIT, "Number of "\
-             "arguments to function ", X.n, X.z, Y->nExpr,
+    const char *err =
+      tt_sprintf("Number of arguments to function %.*s", X.n, X.z);
+    diag_set(ClientError, ER_SQL_PARSER_LIMIT, err, Y->nExpr,
              pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]);
     pParse->is_aborted = true;
   }
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 9171d05..c1af4e6 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -952,7 +952,7 @@ resolveCompoundOrderBy(Parse * pParse,	/* Parsing context.  Leave error messages
 #if SQL_MAX_COLUMN
 	if (pOrderBy->nExpr > db->aLimit[SQL_LIMIT_COLUMN]) {
 		diag_set(ClientError, ER_SQL_PARSER_LIMIT,
-			 "The number of terms in ORDER BY clause", 0, "",
+			 "The number of terms in ORDER BY clause",
 			 pOrderBy->nExpr, db->aLimit[SQL_LIMIT_COLUMN]);
 		pParse->is_aborted = true;
 		return 1;
@@ -1069,7 +1069,7 @@ sqlResolveOrderGroupBy(Parse *pParse, Select *pSelect, ExprList *pOrderBy,
 		const char *err_msg =
 			is_order_by ? "The number of terms in ORDER BY clause" :
 				      "The number of terms in GROUP BY clause";
-		diag_set(ClientError, ER_SQL_PARSER_LIMIT, err_msg, 0, "",
+		diag_set(ClientError, ER_SQL_PARSER_LIMIT, err_msg,
 			 pOrderBy->nExpr, db->aLimit[SQL_LIMIT_COLUMN]);
 		pParse->is_aborted = true;
 		return 1;
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 27a17a4..3f58f30 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -5063,7 +5063,7 @@ selectExpander(Walker * pWalker, Select * p)
 #if SQL_MAX_COLUMN
 	if (p->pEList && p->pEList->nExpr > db->aLimit[SQL_LIMIT_COLUMN]) {
 		diag_set(ClientError, ER_SQL_PARSER_LIMIT, "The number of "\
-			 "columns in result set", 0, "", p->pEList->nExpr,
+			 "columns in result set", p->pEList->nExpr,
 			 db->aLimit[SQL_LIMIT_COLUMN]);
 		pParse->is_aborted = true;
 		return WRC_Abort;
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index 0de3cf0..2f83e81 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -4287,7 +4287,7 @@ sqlWhereBegin(Parse * pParse,	/* The parser context */
 	testcase(pTabList->nSrc == BMS);
 	if (pTabList->nSrc > BMS) {
 		diag_set(ClientError, ER_SQL_PARSER_LIMIT, "The number of "\
-			 "tables in a join", 0, "", pTabList->nSrc, BMS);
+			 "tables in a join", pTabList->nSrc, BMS);
 		pParse->is_aborted = true;
 		return 0;
 	}


> > +	/*192 */_(ER_INDEX_DEF,			"%s prohibited in an index definition") \
> 
> Why not ER_UNSUPPORTED?
> ER_INDEX_DEF -> ER_INDEX_DEF_UNSUPPORTED
> 
> Also, quite unnatural message pattern: it is hard to
> understand diags like:
> 
> diag_set(ClientError, ER_INDEX_DEF,
>         "Parameter markers are");
>
Fixed, diff below.

> > +	/*193 */_(ER_CHECK_CONSTRAINT_DEF,	"%s prohibited in a CHECK constraint definition") \
> 
> The same question.
> ER_CHECK_CONSTRAINT_DEF -> ER_CK_DEF_UNSUPPORTED
> 
> > +	/*194 */_(ER_PRIMARY_KEY_DEF,		"Expressions are prohibited in a primary key definition") \
> 
> Just substitute it with ER_INDEX_DEF_UNSUPPORTED:
> expressions are prohibited in secondary index definition as well.
> 
Fixed:

diff --git a/src/box/errcode.h b/src/box/errcode.h
index b38bcc3..3f8cb8e 100644
--- a/src/box/errcode.h
+++ b/src/box/errcode.h
@@ -244,9 +244,8 @@ struct errcode_record {
 	/*189 */_(ER_HEX_LITERAL_MAX,		"Hex literal %s%s length %d exceeds the supported limit (%d)") \
 	/*190 */_(ER_INT_LITERAL_MAX,		"Integer literal %s%s exceeds the supported range %lld - %lld") \
 	/*191 */_(ER_SQL_PARSER_LIMIT,		"%s %d exceeds the limit (%d)") \
-	/*192 */_(ER_INDEX_DEF,			"%s prohibited in an index definition") \
-	/*193 */_(ER_CHECK_CONSTRAINT_DEF,	"%s prohibited in a CHECK constraint definition") \
-	/*194 */_(ER_PRIMARY_KEY_DEF,		"Expressions are prohibited in a primary key definition") \
+	/*192 */_(ER_INDEX_DEF_UNSUPPORTED,	"%s are prohibited in an index definition") \
+	/*193 */_(ER_CK_DEF_UNSUPPORTED,	"%s are prohibited in a CHECK constraint definition") \
 
 /*
  * !IMPORTANT! Please follow instructions at start of the file
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index c714a06..7d5f3ed 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -605,7 +605,8 @@ sqlAddPrimaryKey(Parse * pParse,	/* Parsing context */
 			    sqlExprSkipCollate(pList->a[i].pExpr);
 			assert(pCExpr != 0);
 			if (pCExpr->op != TK_ID) {
-				diag_set(ClientError, ER_PRIMARY_KEY_DEF);
+				diag_set(ClientError, ER_INDEX_DEF_UNSUPPORTED,
+					 "Expressions");
 				pParse->is_aborted = true;
 				goto primary_key_exit;
 			}
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index c1af4e6..9d5c22b 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -582,7 +582,8 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 
 			/* if( pSrcList==0 ) break; */
 			if (pNC->ncFlags & NC_IdxExpr) {
-				diag_set(ClientError, ER_INDEX_DEF, "'.' operator is");
+				diag_set(ClientError, ER_INDEX_DEF_UNSUPPORTED,
+					 "Expressions");
 				pParse->is_aborted = true;
 			}
 			pRight = pExpr->pRight;
@@ -675,9 +676,10 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 					 * in an index.
 					 */
 					if (pNC->ncFlags & NC_IdxExpr) {
-						diag_set(ClientError, ER_INDEX_DEF,
-							 "Non-deterministic functions "\
-							 "are");
+						diag_set(ClientError,
+							 ER_INDEX_DEF_UNSUPPORTED,
+							 "Non-deterministic "\
+							 "functions");
 						pParse->is_aborted = true;
 					}
 				}
@@ -742,13 +744,14 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 			if (ExprHasProperty(pExpr, EP_xIsSelect)) {
 				int nRef = pNC->nRef;
 				if (pNC->ncFlags & NC_IdxExpr) {
-					diag_set(ClientError, ER_INDEX_DEF,
-						 "Subqueries are");
+					diag_set(ClientError,
+						 ER_INDEX_DEF_UNSUPPORTED,
+						 "Subqueries");
 					pParse->is_aborted = true;
 				} else if (pNC->ncFlags & NC_IsCheck) {
 					diag_set(ClientError,
-						 ER_CHECK_CONSTRAINT_DEF,
-						 "Subqueries are");
+						 ER_CK_DEF_UNSUPPORTED,
+						 "Subqueries");
 					pParse->is_aborted = true;
 				}
 				sqlWalkSelect(pWalker, pExpr->x.pSelect);
@@ -763,8 +766,8 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 	case TK_VARIABLE:{
 			assert((pNC->ncFlags & NC_IsCheck) == 0);
 			if (pNC->ncFlags & NC_IdxExpr) {
-				diag_set(ClientError, ER_INDEX_DEF,
-					 "Parameter markers are");
+				diag_set(ClientError, ER_INDEX_DEF_UNSUPPORTED,
+					 "Parameter markers");
 				pParse->is_aborted = true;
 			}
 			break;
diff --git a/test/box/misc.result b/test/box/misc.result
index 871bfa1..5dda752 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -520,9 +520,8 @@ t;
   189: box.error.HEX_LITERAL_MAX
   190: box.error.INT_LITERAL_MAX
   191: box.error.SQL_PARSER_LIMIT
-  192: box.error.INDEX_DEF
-  193: box.error.CHECK_CONSTRAINT_DEF
-  194: box.error.PRIMARY_KEY_DEF
+  192: box.error.INDEX_DEF_UNSUPPORTED
+  193: box.error.CK_DEF_UNSUPPORTED
 ...
 test_run:cmd("setopt delimiter ''");
 ---
diff --git a/test/sql-tap/colname.test.lua b/test/sql-tap/colname.test.lua
index 77c4280..253497c 100755
--- a/test/sql-tap/colname.test.lua
+++ b/test/sql-tap/colname.test.lua
@@ -637,7 +637,7 @@ test:do_test(
 test:do_catchsql_test(
     "colname-11.1",
     [[ create table t1(a INT, b INT, c INT, primary key('A'))]],
-    {1, "Expressions are prohibited in a primary key definition"})
+    {1, "Expressions are prohibited in an index definition"})
 
 test:do_catchsql_test(
     "colname-11.2",
diff --git a/test/sql-tap/sql-errors.test.lua b/test/sql-tap/sql-errors.test.lua
index 4a92af6..43f14cc 100755
--- a/test/sql-tap/sql-errors.test.lua
+++ b/test/sql-tap/sql-errors.test.lua
@@ -303,7 +303,7 @@ test:do_catchsql_test(
 		CREATE TABLE t26 (i INT, PRIMARY KEY('i'));
 	]], {
 		-- <sql-errors-1.26>
-		1,"Expressions are prohibited in a primary key definition"
+		1,"Expressions are prohibited in an index definition"
 		-- </sql-errors-1.26>
 	})
 
@@ -323,7 +323,7 @@ test:do_catchsql_test(
 		CREATE INDEX i28 ON t0(t0.i);
 	]], {
 		-- <sql-errors-1.28>
-		1,"'.' operator is prohibited in an index definition"
+		1,"Expressions are prohibited in an index definition"
 		-- </sql-errors-1.28>
 	})
 

> > 
> > /*
> >  * !IMPORTANT! Please follow instructions at start of the file
> > diff --git a/src/box/sql/build.c b/src/box/sql/build.c
> > index 0c06555..26434b1 100644
> > --- a/src/box/sql/build.c
> > +++ b/src/box/sql/build.c
> > @@ -267,14 +267,14 @@ int
> > sqlCheckIdentifierName(Parse *pParse, char *zName)
> > {
> > 	ssize_t len = strlen(zName);
> > -
> > -	if (len > BOX_NAME_MAX || identifier_check(zName, len) != 0) {
> > -		sqlErrorMsg(pParse,
> > -				"identifier name is invalid: %s",
> > -				zName);
> > -		return SQL_ERROR;
> > +	if (len <= BOX_NAME_MAX && identifier_check(zName, len) == 0)
> > +		return SQL_OK;
> 
> return 0;
> 
Fixed, diff below.

> > +	if (len > BOX_NAME_MAX) {
> > +		diag_set(ClientError, ER_IDENTIFIER,
> > +			 tt_cstr(zName, BOX_INVALID_NAME_MAX));
> > 	}
> > -	return SQL_OK;
> > +	pParse->is_aborted = true;
> > +	return SQL_ERROR;
> 
> return -1;
> 
>         ssize_t len = strlen(zName);
> -       if (len <= BOX_NAME_MAX && identifier_check(zName, len) == 0)
> -               return SQL_OK;
>         if (len > BOX_NAME_MAX) {
>                 diag_set(ClientError, ER_IDENTIFIER,
>                          tt_cstr(zName, BOX_INVALID_NAME_MAX));
> +               pParse->is_aborted = true;
> +               return -1;
>         }
> -       pParse->is_aborted = true;
> -       return SQL_ERROR;
> +       if (identifier_check(zName, len) != 0) {
> +               pParse->is_aborted = true;
> +               return -1;
> +       }
> +       return 0;
> 
Fixed:

diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 05d39af..0022254 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -262,19 +262,28 @@ sqlNameFromToken(sql * db, Token * pName)
  * (e.g. table, index, column name of a real table)
  * All names are legal except those that cantain non-printable
  * characters or have length greater than BOX_NAME_MAX.
+ *
+ * @param pParse Parser context.
+ * @param zName Identifier to check.
+ *
+ * @retval 0 on success.
+ * @retval -1 on error.
  */
 int
 sqlCheckIdentifierName(Parse *pParse, char *zName)
 {
 	ssize_t len = strlen(zName);
-	if (len <= BOX_NAME_MAX && identifier_check(zName, len) == 0)
-		return SQL_OK;
 	if (len > BOX_NAME_MAX) {
 		diag_set(ClientError, ER_IDENTIFIER,
 			 tt_cstr(zName, BOX_INVALID_NAME_MAX));
+		pParse->is_aborted = true;
+		return -1;
 	}
-	pParse->is_aborted = true;
-	return SQL_ERROR;
+	if (identifier_check(zName, len) != 0) {
+		pParse->is_aborted = true;
+		return -1;
+	}
+	return 0;
 }
 
 /**
@@ -334,7 +343,7 @@ sqlStartTable(Parse *pParse, Token *pName, int noErr)
 	pParse->sNameToken = *pName;
 	if (zName == 0)
 		return;
-	if (sqlCheckIdentifierName(pParse, zName) != SQL_OK)
+	if (sqlCheckIdentifierName(pParse, zName) != 0)
 		goto cleanup;
 
 	struct space *space = space_by_name(zName);
@@ -2962,7 +2971,7 @@ sqlSavepoint(Parse * pParse, int op, Token * pName)
 			return;
 		}
 		if (op == SAVEPOINT_BEGIN &&
-		    sqlCheckIdentifierName(pParse, zName) != SQL_OK)
+		    sqlCheckIdentifierName(pParse, zName) != 0)
 			return;
 		sqlVdbeAddOp4(v, OP_Savepoint, op, 0, 0, zName, P4_DYNAMIC);
 	}
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index 7eacd33..b23d60a 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -86,7 +86,7 @@ sql_trigger_begin(struct Parse *parse, struct Token *name, int tr_tm,
 	if (trigger_name == NULL)
 		goto trigger_cleanup;
 
-	if (sqlCheckIdentifierName(parse, trigger_name) != SQL_OK)
+	if (sqlCheckIdentifierName(parse, trigger_name) != 0)
 		goto trigger_cleanup;
 
 	const char *table_name = table->a[0].zName;



> > @@ -588,9 +590,9 @@ sqlAddPrimaryKey(Parse * pParse,	/* Parsing context */
> > 	if (space == NULL)
> > 		goto primary_key_exit;
> > 	if (sql_space_primary_key(space) != NULL) {
> > -		sqlErrorMsg(pParse,
> > -				"table \"%s\" has more than one primary key",
> > -				space->def->name);
> > +		diag_set(ClientError, ER_CREATE_SPACE, space->def->name,
> > +			 "too many primary keys”);
> 
> too many primary keys -> primary key already exists
> 
Fixed, diff below (the same diff which replaces "it is a view" by
"space is a view").

> > +		pParse->is_aborted = true;
> > 		goto primary_key_exit;
> > 	}
> > 	if (pList == NULL) {
> > 
> > @@ -1775,6 +1778,11 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child,
> > 	}
> > 	if (constraint_name == NULL)
> > 		goto exit_create_fk;
> > +	if (!is_self_referenced && parent_space->def->opts.is_view) {
> > +		diag_set(ClientError, ER_CREATE_FK_CONSTRAINT, constraint_name,
> > +			"referenced space can't be VIEW");
> > +		goto tnt_error;
> > +	}
> 
> This check is already processed in on_replace_dd_fk_constraint()
> Could you remove this check to avoid code duplication?
>
It can't be done here because this function wotk with parent PK
and a view do not have PK. I think it is possible to remove it in
alter.cc, but not sure that it would be right.
 
> > @@ -2942,11 +2958,8 @@ sqlSavepoint(Parse * pParse, int op, Token * pName)
> > 			return;
> > 		}
> > 		if (op == SAVEPOINT_BEGIN &&
> > -			sqlCheckIdentifierName(pParse, zName)
> > -				!= SQL_OK) {
> > -			sqlErrorMsg(pParse, "bad savepoint name");
> > +		    sqlCheckIdentifierName(pParse, zName) != SQL_OK)
> 
> != 0
> 
Fixed, diff above.

> > diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
> > index 87d4ed4..e8a5f0d 100644
> > --- a/src/box/sql/delete.c
> > +++ b/src/box/sql/delete.c
> > @@ -157,8 +157,9 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
> > 			goto delete_from_cleanup;
> > 
> > 		if (trigger_list == NULL) {
> > -			sqlErrorMsg(parse, "cannot modify %s because it is a"
> > -					" view", space->def->name);
> > +			diag_set(ClientError, ER_ALTER_SPACE, space->def->name,
> > +				 "it is a view”);
> 
> Nit: it is a view -> space is a view (sounds better IMHO).
> 
Fixed, diff below.

> > +			parse->is_aborted = true;
> > 			goto delete_from_cleanup;
> > 		}
> > 	}
> > diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> > index 4193596..de06ee0 100644
> > --- a/src/box/sql/expr.c
> > +++ b/src/box/sql/expr.c
> > @@ -750,15 +750,14 @@ codeVectorCompare(Parse * pParse,	/* Code generator context */
> > int
> > sqlExprCheckHeight(Parse * pParse, int nHeight)
> > {
> > -	int rc = SQL_OK;
> > 	int mxHeight = pParse->db->aLimit[SQL_LIMIT_EXPR_DEPTH];
> > 	if (nHeight > mxHeight) {
> > -		sqlErrorMsg(pParse,
> > -				"Expression tree is too large (maximum depth %d)",
> > -				mxHeight);
> > -		rc = SQL_ERROR;
> > +		diag_set(ClientError, ER_SQL_PARSER_LIMIT, "Number of nodes "\
> > +			 "in expression tree", 0, "", nHeight, mxHeight);
> > +		pParse->is_aborted = true;
> > +		return SQL_ERROR;
> 
> return -1;
> 
> > 	}
Fixed, diff below.

> > -	return rc;
> > +	return SQL_OK;
> 
> return 0;
> 
Fixed:

diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index f38c3c8..de993c1 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -746,6 +746,12 @@ codeVectorCompare(Parse * pParse,	/* Code generator context */
  * Check that argument nHeight is less than or equal to the maximum
  * expression depth allowed. If it is not, leave an error message in
  * pParse.
+ *
+ * @param pParse Parser context.
+ * @param zName Depth to check.
+ *
+ * @retval 0 on success.
+ * @retval -1 on error.
  */
 int
 sqlExprCheckHeight(Parse * pParse, int nHeight)
@@ -755,9 +761,9 @@ sqlExprCheckHeight(Parse * pParse, int nHeight)
 		diag_set(ClientError, ER_SQL_PARSER_LIMIT, "Number of nodes "\
 			 "in expression tree", nHeight, mxHeight);
 		pParse->is_aborted = true;
-		return SQL_ERROR;
+		return -1;
 	}
-	return SQL_OK;
+	return 0;
 }
 
 /* The following three functions, heightOfExpr(), heightOfExprList()


> > 
> > @@ -690,9 +674,12 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
> > 					 * that might change over time cannot be used
> > 					 * in an index.
> > 					 */
> > -					notValid(pParse, pNC,
> > -						 "non-deterministic functions",
> > -						 NC_IdxExpr);
> > +					if (pNC->ncFlags & NC_IdxExpr) {
> > +						diag_set(ClientError, ER_INDEX_DEF,
> > +							 "Non-deterministic functions "\
> > +							 "are”);
> 
> This code seems to be unreachable: functional indexes
> are filtered a way earlier.
> 
Fixed, diff below.

> > @@ -754,8 +741,16 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
> > 			testcase(pExpr->op == TK_IN);
> > 			if (ExprHasProperty(pExpr, EP_xIsSelect)) {
> > 				int nRef = pNC->nRef;
> > -				notValid(pParse, pNC, "subqueries",
> > -					 NC_IsCheck | NC_IdxExpr);
> > +				if (pNC->ncFlags & NC_IdxExpr) {
> > +					diag_set(ClientError, ER_INDEX_DEF,
> > +						 "Subqueries are”);
> 
> The same: managed to grep this error only for ck constraint.
> 
Fixed:



> > @@ -1042,27 +1044,34 @@ resolveCompoundOrderBy(Parse * pParse,	/* Parsing context.  Leave error messages
> >  * field) then convert that term into a copy of the corresponding result set
> >  * column.
> >  *
> > - * If any errors are detected, add an error message to pParse and
> > - * return non-zero.  Return zero if no errors are seen.
> > + * @param pParse Parsing context.
> > + * @param pSelect The SELECT statement containing the clause.
> > + * @param pOrderBy The ORDER BY or GROUP BY clause to be
> > + *                 processed.
> > + * @param is_order_by True if pOrderBy is ORDER BY, false if
> > + *                    pOrderBy is GROUP BY
> > + * @retval 0 On success, not 0 elsewhere.
> >  */
> > int
> > -sqlResolveOrderGroupBy(Parse * pParse,	/* Parsing context.  Leave error messages here */
> > -			   Select * pSelect,	/* The SELECT statement containing the clause */
> > -			   ExprList * pOrderBy,	/* The ORDER BY or GROUP BY clause to be processed */
> > -			   const char *zType	/* "ORDER" or "GROUP" */
> > -    )
> > +sqlResolveOrderGroupBy(Parse *pParse, Select *pSelect, ExprList *pOrderBy,
> > +		       bool is_order_by)
> 
> Why did you decide to fix code style here? Anyway, you didn't finished it
> (struct prefixes, param naming and so on and so forth)
> 
I fixed this because I thought that it is quite unreliable to
differentiate names of term using first letter of its name. Should
I remove these changes?
> > {
> > 	int i;
> > 	sql *db = pParse->db;
> > 	ExprList *pEList;
> > 	struct ExprList_item *pItem;
> > +	const char *zType = is_order_by ? "ORDER" : "GROUP";
> > 
> > 	if (pOrderBy == 0 || pParse->db->mallocFailed)
> > 		return 0;
> > 
> > @@ -1096,22 +1105,23 @@ sqlResolveOrderGroupBy(Parse * pParse,	/* Parsing context.  Leave error messages
> >  * result-set expression.  Otherwise, the expression is resolved in
> >  * the usual way - using sqlResolveExprNames().
> >  *
> > - * This routine returns the number of errors.  If errors occur, then
> > - * an appropriate error message might be left in pParse.  (OOM errors
> > - * excepted.)
> > + * @param pNC The name context of the SELECT statement.
> > + * @param pSelect The SELECT statement containing the clause.
> > + * @param pOrderBy An ORDER BY or GROUP BY clause to resolve.
> > + * @param is_order_by True if pOrderBy is ORDER BY, false if
> > + *                    pOrderBy is GROUP BY
> > + * @retval 0 On success, not 0 elsewhere.
> >  */
> > static int
> > -resolveOrderGroupBy(NameContext * pNC,	/* The name context of the SELECT statement */
> > -		    Select * pSelect,	/* The SELECT statement holding pOrderBy */
> > -		    ExprList * pOrderBy,	/* An ORDER BY or GROUP BY clause to resolve */
> > -		    const char *zType	/* Either "ORDER" or "GROUP", as appropriate */
> > -    )
> 
> The same question.
> 
Answer above.

> > +resolveOrderGroupBy(NameContext *pNC, Select *pSelect, ExprList *pOrderBy,
> > +		    bool is_order_by)
> > {
> > 	int i, j;		/* Loop counters */
> > 	int iCol;		/* Column number */
> > 	struct ExprList_item *pItem;	/* A term of the ORDER BY clause */
> > 	Parse *pParse;		/* Parsing context */
> > 	int nResult;		/* Number of terms in the result set */
> > +	const char *zType = is_order_by ? "ORDER" : "GROUP";
> > 
> > 	if (pOrderBy == 0)
> > 		return 0;
> > 
> > 	/* Compute the limit registers */
> > 	computeLimitRegisters(pParse, p, labelEnd);
> > diff --git a/src/box/sql/update.c b/src/box/sql/update.c
> > index 05ceeb4..bc0ab66 100644
> > --- a/src/box/sql/update.c
> > +++ b/src/box/sql/update.c
> > @@ -139,8 +139,9 @@ sqlUpdate(Parse * pParse,		/* The parser context */
> > 		goto update_cleanup;
> > 	}
> > 	if (is_view && tmask == 0) {
> > -		sqlErrorMsg(pParse, "cannot modify %s because it is a view",
> > -				space->def->name);
> > +		diag_set(ClientError, ER_ALTER_SPACE, space->def->name,
> > +			 "it is a view”);
> 
> -> space is a view
> 
Fixed:

diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 0022254..b6b6c24 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -603,7 +603,7 @@ sqlAddPrimaryKey(Parse * pParse,	/* Parsing context */
 		goto primary_key_exit;
 	if (sql_space_primary_key(space) != NULL) {
 		diag_set(ClientError, ER_CREATE_SPACE, space->def->name,
-			 "too many primary keys");
+			 "primary key already exists");
 		pParse->is_aborted = true;
 		goto primary_key_exit;
 	}
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index dbaf2c1..0eb28d7 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -110,8 +110,8 @@ sql_table_truncate(struct Parse *parse, struct SrcList *tab_list)
 	}
 	if (space->def->opts.is_view) {
 		const char *err_msg =
-			tt_sprintf("can not truncate space '%s' because it is "
-				   "a view", space->def->name);
+			tt_sprintf("can not truncate space '%s' because space "\
+				   "is a view", space->def->name);
 		diag_set(ClientError, ER_SQL, err_msg);
 		goto tarantool_error;
 	}
@@ -164,7 +164,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 
 		if (trigger_list == NULL) {
 			diag_set(ClientError, ER_ALTER_SPACE, space->def->name,
-				 "it is a view");
+				 "space is a view");
 			parse->is_aborted = true;
 			goto delete_from_cleanup;
 		}
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 263df4d..0ca38ae 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -323,7 +323,7 @@ sqlInsert(Parse * pParse,	/* Parser context */
 	/* Cannot insert into a read-only table. */
 	if (is_view && tmask == 0) {
 		diag_set(ClientError, ER_ALTER_SPACE, space->def->name,
-			 "it is a view");
+			 "space is a view");
 		pParse->is_aborted = true;
 		goto insert_cleanup;
 	}
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index bc0ab66..71a1e00 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -140,7 +140,7 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 	}
 	if (is_view && tmask == 0) {
 		diag_set(ClientError, ER_ALTER_SPACE, space->def->name,
-			 "it is a view");
+			 "space is a view");
 		pParse->is_aborted = true;
 		goto update_cleanup;
 	}
diff --git a/test/sql-tap/misc1.test.lua b/test/sql-tap/misc1.test.lua
index ffef2d4..6b9f5fa 100755
--- a/test/sql-tap/misc1.test.lua
+++ b/test/sql-tap/misc1.test.lua
@@ -348,7 +348,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <misc1-7.1>
-        1, [[Failed to create space 'ERROR1': too many primary keys]]
+        1, [[Failed to create space 'ERROR1': primary key already exists]]
         -- </misc1-7.1>
     })
 
@@ -361,7 +361,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <misc1-7.2>
-        1, [[Failed to create space 'ERROR1': too many primary keys]]
+        1, [[Failed to create space 'ERROR1': primary key already exists]]
         -- </misc1-7.2>
     })
 
@@ -897,7 +897,7 @@ test:do_catchsql_test(
         CREATE TABLE test2(a text primary key, b text, primary key(a,b));
     ]], {
         -- <misc1-16.3>
-        1, [[Failed to create space 'TEST2': too many primary keys]]
+        1, [[Failed to create space 'TEST2': primary key already exists]]
         -- </misc1-16.3>
     })
 
diff --git a/test/sql-tap/sql-errors.test.lua b/test/sql-tap/sql-errors.test.lua
index cd20310..ac242c4 100755
--- a/test/sql-tap/sql-errors.test.lua
+++ b/test/sql-tap/sql-errors.test.lua
@@ -52,7 +52,7 @@ test:do_catchsql_test(
 		CREATE TABLE t4 (i INT PRIMARY KEY, a INT PRIMARY KEY);
 	]], {
 		-- <sql-errors-1.4>
-		1,"Failed to create space 'T4': too many primary keys"
+		1,"Failed to create space 'T4': primary key already exists"
 		-- </sql-errors-1.4>
 	})
 
@@ -273,7 +273,7 @@ test:do_catchsql_test(
 		INSERT INTO v0 VALUES (2);
 	]], {
 		-- <sql-errors-1.23>
-		1,"Can't modify space 'V0': it is a view"
+		1,"Can't modify space 'V0': space is a view"
 		-- </sql-errors-1.23>
 	})
 
@@ -283,7 +283,7 @@ test:do_catchsql_test(
 		UPDATE v0 SET i = 2 WHERE i = 1;
 	]], {
 		-- <sql-errors-1.24>
-		1,"Can't modify space 'V0': it is a view"
+		1,"Can't modify space 'V0': space is a view"
 		-- </sql-errors-1.24>
 	})
 
@@ -293,7 +293,7 @@ test:do_catchsql_test(
 		DELETE FROM v0;
 	]], {
 		-- <sql-errors-1.25>
-		1,"Can't modify space 'V0': it is a view"
+		1,"Can't modify space 'V0': space is a view"
 		-- </sql-errors-1.25>
 	})
 
diff --git a/test/sql-tap/unique.test.lua b/test/sql-tap/unique.test.lua
index 79bede8..a3fa1c9 100755
--- a/test/sql-tap/unique.test.lua
+++ b/test/sql-tap/unique.test.lua
@@ -36,7 +36,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <unique-1.1>
-        1, [[Failed to create space 'T1': too many primary keys]]
+        1, [[Failed to create space 'T1': primary key already exists]]
         -- </unique-1.1>
     })
 
diff --git a/test/sql-tap/view.test.lua b/test/sql-tap/view.test.lua
index e1affb6..0032e1b 100755
--- a/test/sql-tap/view.test.lua
+++ b/test/sql-tap/view.test.lua
@@ -189,7 +189,7 @@ test:do_catchsql_test(
         INSERT INTO v2 VALUES(1,2,3,4);
     ]], {
         -- <view-2.2>
-        1, "Can't modify space 'V2': it is a view"
+        1, "Can't modify space 'V2': space is a view"
         -- </view-2.2>
     })
 
@@ -199,7 +199,7 @@ test:do_catchsql_test(
         UPDATE v2 SET a=10 WHERE a=5;
     ]], {
         -- <view-2.3>
-        1, "Can't modify space 'V2': it is a view"
+        1, "Can't modify space 'V2': space is a view"
         -- </view-2.3>
     })
 
@@ -209,7 +209,7 @@ test:do_catchsql_test(
         DELETE FROM v2;
     ]], {
         -- <view-2.4>
-        1, "Can't modify space 'V2': it is a view"
+        1, "Can't modify space 'V2': space is a view"
         -- </view-2.4>
     })
 
diff --git a/test/sql/delete.result b/test/sql/delete.result
index e024dd6..29459cc 100644
--- a/test/sql/delete.result
+++ b/test/sql/delete.result
@@ -93,7 +93,7 @@ box.sql.execute("CREATE VIEW v1 AS SELECT * FROM t1;")
 ...
 box.sql.execute("TRUNCATE TABLE v1;")
 ---
-- error: 'SQL error: can not truncate space ''V1'' because it is a view'
+- error: 'SQL error: can not truncate space ''V1'' because space is a view'
 ...
 -- Can't truncate table with FK.
 box.sql.execute("CREATE TABLE t2(x INT PRIMARY KEY REFERENCES t1(id));")


> > +		pParse->is_aborted = true;
> > 		goto update_cleanup;
> > 	}
> > 
> > 
> > diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
> > index 0d8bf15..dd2de92 100755
> > --- a/test/sql-tap/check.test.lua
> > +++ b/test/sql-tap/check.test.lua
> > @@ -319,7 +319,7 @@ test:do_catchsql_test(
> >         );
> >     ]], {
> >         -- <check-3.1>
> > -        1, "Failed to create space 'T3': subqueries prohibited in CHECK constraints"
> > +        1, "Failed to create space 'T3': Subqueries are prohibited in a CHECK constraint definition”
> 
> Don’t use capital letter after ‘:’, it would allow you to avoid fixing tests.
> The same is applied to the fixes below.
> 
It can't be done because this error consist of two different
errors: ER_CREATE_SPACE and ER_CK_DEF_UNSUPPORTED. The first one
uses message of the second error. It is done this way in alter.cc.

> >         -- </check-3.1>
> >     })
> > 
> > diff --git a/test/sql-tap/colname.test.lua b/test/sql-tap/colname.test.lua
> > index 29fdf13..77c4280 100755
> > --- a/test/sql-tap/colname.test.lua
> > +++ b/test/sql-tap/colname.test.lua
> > @@ -637,7 +637,7 @@ test:do_test(
> > test:do_catchsql_test(
> >     "colname-11.1",
> >     [[ create table t1(a INT, b INT, c INT, primary key('A'))]],
> > -    {1, "expressions prohibited in PRIMARY KEY"})
> > +    {1, "Expressions are prohibited in a primary key definition"})
> > 
> > test:do_catchsql_test(
> >     "colname-11.2",
> > diff --git a/test/sql-tap/default.test.lua b/test/sql-tap/default.test.lua
> > index 67151b0..fbe285b 100755
> > --- a/test/sql-tap/default.test.lua
> > +++ b/test/sql-tap/default.test.lua
> > @@ -66,7 +66,7 @@ test:do_catchsql_test(
> > 	);
> > 	]], {
> > 	-- <default-1.3>
> > -	1, "default value of column [Y] is not constant"
> > +	1, "Failed to create space 'T3': default value of column is not constant”
> 
> Is it possible to print also name of column (like in original version)?
> 
Fixed:

diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 7d5f3ed..05d39af 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -523,8 +523,11 @@ sqlAddDefaultValue(Parse * pParse, ExprSpan * pSpan)
 		struct space_def *def = pParse->new_space->def;
 		if (!sqlExprIsConstantOrFunction
 		    (pSpan->pExpr, db->init.busy)) {
+			const char *column_name =
+				def->fields[def->field_count - 1].name;
 			diag_set(ClientError, ER_CREATE_SPACE, def->name,
-				 "default value of column is not constant");
+				 tt_sprintf("default value of column '%s' is "\
+					    "not constant", column_name));
 			pParse->is_aborted = true;
 		} else {
 			assert(def != NULL);
diff --git a/test/sql-tap/default.test.lua b/test/sql-tap/default.test.lua
index fbe285b..564d373 100755
--- a/test/sql-tap/default.test.lua
+++ b/test/sql-tap/default.test.lua
@@ -66,7 +66,7 @@ test:do_catchsql_test(
 	);
 	]], {
 	-- <default-1.3>
-	1, "Failed to create space 'T3': default value of column is not constant"
+	1, "Failed to create space 'T3': default value of column 'Y' is not constant"
 	-- </default-1.3>
 })
 
@@ -173,7 +173,7 @@ test:do_catchsql_test(
 	);
 	]], {
 	-- <default-4.2>
-	1, "Failed to create space 'T2': default value of column is not constant"
+	1, "Failed to create space 'T2': default value of column 'B' is not constant"
 	-- </default-4.2>
 })
 
@@ -187,7 +187,7 @@ test:do_catchsql_test(
 	);
 	]], {
 	-- <default-4.3>
-	1, "Failed to create space 'T2': default value of column is not constant"
+	1, "Failed to create space 'T2': default value of column 'B' is not constant"
 	-- </default-4.3>
 })
 
@@ -201,7 +201,7 @@ test:do_catchsql_test(
 	);
 	]], {
 	-- <default-4.4>
-	1, "Failed to create space 'T2': default value of column is not constant"
+	1, "Failed to create space 'T2': default value of column 'B' is not constant"
 	-- </default-4.4>
 })
 
@@ -214,7 +214,7 @@ test:do_catchsql_test(
         CREATE TABLE t6(id INTEGER PRIMARY KEY, b TEXT DEFAULT(id));
     ]], {
     -- <default-5.1>
-    1, "Failed to create space 'T6': default value of column is not constant"
+    1, "Failed to create space 'T6': default value of column 'B' is not constant"
     -- </default-5.1>
 })
 
diff --git a/test/sql-tap/sql-errors.test.lua b/test/sql-tap/sql-errors.test.lua
index 43f14cc..cd20310 100755
--- a/test/sql-tap/sql-errors.test.lua
+++ b/test/sql-tap/sql-errors.test.lua
@@ -42,7 +42,7 @@ test:do_catchsql_test(
 		CREATE TABLE t3 (i INT PRIMARY KEY, a INT DEFAULT(MAX(i, 1)));
 	]], {
 		-- <sql-errors-1.3>
-		1,"Failed to create space 'T3': default value of column is not constant"
+		1,"Failed to create space 'T3': default value of column 'A' is not constant"
 		-- </sql-errors-1.3>
 	})
 


> > diff --git a/test/sql-tap/engine.cfg b/test/sql-tap/engine.cfg
> > index 413dd3f..cc56592 100644
> > --- a/test/sql-tap/engine.cfg
> > +++ b/test/sql-tap/engine.cfg
> > @@ -11,6 +11,9 @@
> >     "sort.test.lua": {
> >         "memtx": {"engine": "memtx"}
> >     },
> > +    "sql-errors.test.lua": {
> > +        "memtx": {"engine": "memtx"}
> > +    },
> 
> I would run this test both for vinyl and memtx.
> 
Fixed:

diff --git a/test/sql-tap/engine.cfg b/test/sql-tap/engine.cfg
index cc56592..413dd3f 100644
--- a/test/sql-tap/engine.cfg
+++ b/test/sql-tap/engine.cfg
@@ -11,9 +11,6 @@
     "sort.test.lua": {
         "memtx": {"engine": "memtx"}
     },
-    "sql-errors.test.lua": {
-        "memtx": {"engine": "memtx"}
-    },
     "*": {
         "memtx": {"engine": "memtx"},
         "vinyl": {"engine": "vinyl"}

Also, I changed one more error:
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index de0f282..efb895f 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -481,8 +481,8 @@ sqlRunParser(Parse * pParse, const char *zSql)
 				      &pParse->sLastToken.isReserved);
 			i += pParse->sLastToken.n;
 			if (i > mxSqlLen) {
-				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
-					 "string or blob too big");
+				diag_set(ClientError, ER_SQL_PARSER_LIMIT,
+					 "SQL command length", i, mxSqlLen);
 				pParse->is_aborted = true;
 				break;
 			}

I will investigate this error a bit later.


New version:

commit 39c071b39c0ad1d79c3d117fb69303ffcc7f5cd8
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Thu Mar 7 21:28:06 2019 +0300

    sql: rework semantic errors
    
    This patch reworks some of SQL semantic errors.
    
    Part of #3965

diff --git a/src/box/errcode.h b/src/box/errcode.h
index 7764aa3..3f8cb8e 100644
--- a/src/box/errcode.h
+++ b/src/box/errcode.h
@@ -122,7 +122,7 @@ struct errcode_record {
 	/* 67 */_(ER_REPLICA_ID_IS_RESERVED,	"Can't initialize replica id with a reserved value %u") \
 	/* 68 */_(ER_INVALID_ORDER,		"Invalid LSN order for instance %u: previous LSN = %llu, new lsn = %llu") \
 	/* 69 */_(ER_MISSING_REQUEST_FIELD,	"Missing mandatory field '%s' in request") \
-	/* 70 */_(ER_IDENTIFIER,		"Invalid identifier '%s' (expected printable symbols only)") \
+	/* 70 */_(ER_IDENTIFIER,		"Invalid identifier '%s' (expected printable symbols only or it is too long)") \
 	/* 71 */_(ER_DROP_FUNCTION,		"Can't drop function %u: %s") \
 	/* 72 */_(ER_ITERATOR_TYPE,		"Unknown iterator type '%s'") \
 	/* 73 */_(ER_REPLICA_MAX,		"Replica count limit reached: %u") \
@@ -240,6 +240,12 @@ struct errcode_record {
 	/*185 */_(ER_SQL_UNKNOWN_TOKEN,		"Syntax error: unrecognized token: '%.*s'") \
 	/*186 */_(ER_SQL_PARSER_GENERIC,	"%s") \
 	/*187 */_(ER_SQL_ANALYZE_ARGUMENT,	"ANALYZE statement argument %s is not a base table") \
+	/*188 */_(ER_SQL_COLUMN_COUNT_MAX,	"Failed to create space '%s': space column count %d exceeds the limit (%d)") \
+	/*189 */_(ER_HEX_LITERAL_MAX,		"Hex literal %s%s length %d exceeds the supported limit (%d)") \
+	/*190 */_(ER_INT_LITERAL_MAX,		"Integer literal %s%s exceeds the supported range %lld - %lld") \
+	/*191 */_(ER_SQL_PARSER_LIMIT,		"%s %d exceeds the limit (%d)") \
+	/*192 */_(ER_INDEX_DEF_UNSUPPORTED,	"%s are prohibited in an index definition") \
+	/*193 */_(ER_CK_DEF_UNSUPPORTED,	"%s are prohibited in a CHECK constraint definition") \
 
 /*
  * !IMPORTANT! Please follow instructions at start of the file
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 0c06555..b6b6c24 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -262,19 +262,28 @@ sqlNameFromToken(sql * db, Token * pName)
  * (e.g. table, index, column name of a real table)
  * All names are legal except those that cantain non-printable
  * characters or have length greater than BOX_NAME_MAX.
+ *
+ * @param pParse Parser context.
+ * @param zName Identifier to check.
+ *
+ * @retval 0 on success.
+ * @retval -1 on error.
  */
 int
 sqlCheckIdentifierName(Parse *pParse, char *zName)
 {
 	ssize_t len = strlen(zName);
-
-	if (len > BOX_NAME_MAX || identifier_check(zName, len) != 0) {
-		sqlErrorMsg(pParse,
-				"identifier name is invalid: %s",
-				zName);
-		return SQL_ERROR;
+	if (len > BOX_NAME_MAX) {
+		diag_set(ClientError, ER_IDENTIFIER,
+			 tt_cstr(zName, BOX_INVALID_NAME_MAX));
+		pParse->is_aborted = true;
+		return -1;
+	}
+	if (identifier_check(zName, len) != 0) {
+		pParse->is_aborted = true;
+		return -1;
 	}
-	return SQL_OK;
+	return 0;
 }
 
 /**
@@ -334,7 +343,7 @@ sqlStartTable(Parse *pParse, Token *pName, int noErr)
 	pParse->sNameToken = *pName;
 	if (zName == 0)
 		return;
-	if (sqlCheckIdentifierName(pParse, zName) != SQL_OK)
+	if (sqlCheckIdentifierName(pParse, zName) != 0)
 		goto cleanup;
 
 	struct space *space = space_by_name(zName);
@@ -432,7 +441,9 @@ sqlAddColumn(Parse * pParse, Token * pName, struct type_def *type_def)
 
 #if SQL_MAX_COLUMN
 	if ((int)def->field_count + 1 > db->aLimit[SQL_LIMIT_COLUMN]) {
-		sqlErrorMsg(pParse, "too many columns on %s", def->name);
+		diag_set(ClientError, ER_SQL_COLUMN_COUNT_MAX, def->name,
+			 def->field_count + 1, db->aLimit[SQL_LIMIT_COLUMN]);
+		pParse->is_aborted = true;
 		return;
 	}
 #endif
@@ -521,9 +532,12 @@ sqlAddDefaultValue(Parse * pParse, ExprSpan * pSpan)
 		struct space_def *def = pParse->new_space->def;
 		if (!sqlExprIsConstantOrFunction
 		    (pSpan->pExpr, db->init.busy)) {
-			sqlErrorMsg(pParse,
-					"default value of column [%s] is not constant",
-					def->fields[def->field_count - 1].name);
+			const char *column_name =
+				def->fields[def->field_count - 1].name;
+			diag_set(ClientError, ER_CREATE_SPACE, def->name,
+				 tt_sprintf("default value of column '%s' is "\
+					    "not constant", column_name));
+			pParse->is_aborted = true;
 		} else {
 			assert(def != NULL);
 			struct field_def *field =
@@ -588,9 +602,9 @@ sqlAddPrimaryKey(Parse * pParse,	/* Parsing context */
 	if (space == NULL)
 		goto primary_key_exit;
 	if (sql_space_primary_key(space) != NULL) {
-		sqlErrorMsg(pParse,
-				"table \"%s\" has more than one primary key",
-				space->def->name);
+		diag_set(ClientError, ER_CREATE_SPACE, space->def->name,
+			 "primary key already exists");
+		pParse->is_aborted = true;
 		goto primary_key_exit;
 	}
 	if (pList == NULL) {
@@ -603,8 +617,9 @@ sqlAddPrimaryKey(Parse * pParse,	/* Parsing context */
 			    sqlExprSkipCollate(pList->a[i].pExpr);
 			assert(pCExpr != 0);
 			if (pCExpr->op != TK_ID) {
-				sqlErrorMsg(pParse, "expressions prohibited"
-							" in PRIMARY KEY");
+				diag_set(ClientError, ER_INDEX_DEF_UNSUPPORTED,
+					 "Expressions");
+				pParse->is_aborted = true;
 				goto primary_key_exit;
 			}
 			const char *name = pCExpr->u.zToken;
@@ -636,8 +651,10 @@ sqlAddPrimaryKey(Parse * pParse,	/* Parsing context */
 		if (db->mallocFailed)
 			goto primary_key_exit;
 	} else if (autoInc) {
-		sqlErrorMsg(pParse, "AUTOINCREMENT is only allowed on an "
-				"INTEGER PRIMARY KEY or INT PRIMARY KEY");
+		diag_set(ClientError, ER_CREATE_SPACE, space->def->name,
+			 "AUTOINCREMENT is only allowed on an INTEGER PRIMARY "\
+			 "KEY or INT PRIMARY KEY");
+		pParse->is_aborted = true;
 		goto primary_key_exit;
 	} else {
 		sql_create_index(pParse, 0, 0, pList, 0, sortOrder, false,
@@ -1144,9 +1161,10 @@ sqlEndTable(Parse * pParse,	/* Parse context */
 
 	if (!new_space->def->opts.is_view) {
 		if (sql_space_primary_key(new_space) == NULL) {
-			sqlErrorMsg(pParse,
-					"PRIMARY KEY missing on table %s",
-					new_space->def->name);
+			diag_set(ClientError, ER_CREATE_SPACE,
+				 new_space->def->name,
+				 "PRIMARY KEY missing");
+			pParse->is_aborted = true;
 			goto cleanup;
 		}
 	}
@@ -1264,8 +1282,10 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
 {
 	struct sql *db = parse_context->db;
 	if (parse_context->nVar > 0) {
-		sqlErrorMsg(parse_context,
-				"parameters are not allowed in views");
+		diag_set(ClientError, ER_CREATE_SPACE,
+			 sqlNameFromToken(db, name),
+			 "parameters are not allowed in views");
+		parse_context->is_aborted = true;
 		goto create_view_fail;
 	}
 	sqlStartTable(parse_context, name, if_exists);
@@ -1279,10 +1299,10 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
 		goto create_view_fail;
 	if (aliases != NULL) {
 		if ((int)select_res_space->def->field_count != aliases->nExpr) {
-			sqlErrorMsg(parse_context, "expected %d columns "\
-					"for '%s' but got %d", aliases->nExpr,
-					space->def->name,
-					select_res_space->def->field_count);
+			diag_set(ClientError, ER_CREATE_SPACE, space->def->name,
+				 "number of aliases doesn't match provided "\
+				 "columns");
+			parse_context->is_aborted = true;
 			goto create_view_fail;
 		}
 		sqlColumnsFromExprList(parse_context, aliases, space->def);
@@ -1604,13 +1624,15 @@ sql_drop_table(struct Parse *parse_context, struct SrcList *table_name_list,
 	 * and DROP VIEW is not used on a table.
 	 */
 	if (is_view && !space->def->opts.is_view) {
-		sqlErrorMsg(parse_context, "use DROP TABLE to delete table %s",
-				space_name);
+		diag_set(ClientError, ER_DROP_SPACE, space_name,
+			 "use DROP TABLE");
+		parse_context->is_aborted = true;
 		goto exit_drop_table;
 	}
 	if (!is_view && space->def->opts.is_view) {
-		sqlErrorMsg(parse_context, "use DROP VIEW to delete view %s",
-				space_name);
+		diag_set(ClientError, ER_DROP_SPACE, space_name,
+			 "use DROP VIEW");
+		parse_context->is_aborted = true;
 		goto exit_drop_table;
 	}
 	/*
@@ -1753,12 +1775,6 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child,
 			diag_set(ClientError, ER_NO_SUCH_SPACE, parent_name);;
 			goto tnt_error;
 		}
-	} else {
-		if (parent_space->def->opts.is_view) {
-			sqlErrorMsg(parse_context,
-					"referenced table can't be view");
-			goto exit_create_fk;
-		}
 	}
 	if (constraint == NULL && !is_alter) {
 		if (parse_context->constraintName.n == 0) {
@@ -1775,6 +1791,11 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child,
 	}
 	if (constraint_name == NULL)
 		goto exit_create_fk;
+	if (!is_self_referenced && parent_space->def->opts.is_view) {
+		diag_set(ClientError, ER_CREATE_FK_CONSTRAINT, constraint_name,
+			"referenced space can't be VIEW");
+		goto tnt_error;
+	}
 	const char *error_msg = "number of columns in foreign key does not "
 				"match the number of columns in the primary "
 				"index of referenced table";
@@ -2144,7 +2165,10 @@ sql_create_index(struct Parse *parse, struct Token *token,
 	struct space_def *def = space->def;
 
 	if (def->opts.is_view) {
-		sqlErrorMsg(parse, "views can not be indexed");
+		diag_set(ClientError, ER_MODIFY_INDEX,
+			 sqlNameFromToken(db, token), def->name,
+			 "views can not be indexed");
+		parse->is_aborted = true;
 		goto exit_create_index;
 	}
 	/*
@@ -2245,7 +2269,12 @@ sql_create_index(struct Parse *parse, struct Token *token,
 		assert(col_list->nExpr == 1);
 		sqlExprListSetSortOrder(col_list, sort_order);
 	} else {
-		sqlExprListCheckLength(parse, col_list, "index");
+		if (col_list->nExpr > db->aLimit[SQL_LIMIT_COLUMN]) {
+			diag_set(ClientError, ER_SQL_PARSER_LIMIT,
+				 "The number of columns in index",
+				 col_list->nExpr, db->aLimit[SQL_LIMIT_COLUMN]);
+			parse->is_aborted = true;
+		}
 	}
 
 	index = (struct index *) region_alloc(&parse->region, sizeof(*index));
@@ -2942,11 +2971,8 @@ sqlSavepoint(Parse * pParse, int op, Token * pName)
 			return;
 		}
 		if (op == SAVEPOINT_BEGIN &&
-			sqlCheckIdentifierName(pParse, zName)
-				!= SQL_OK) {
-			sqlErrorMsg(pParse, "bad savepoint name");
+		    sqlCheckIdentifierName(pParse, zName) != 0)
 			return;
-		}
 		sqlVdbeAddOp4(v, OP_Savepoint, op, 0, 0, zName, P4_DYNAMIC);
 	}
 }
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index f4d0334..0eb28d7 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -110,8 +110,8 @@ sql_table_truncate(struct Parse *parse, struct SrcList *tab_list)
 	}
 	if (space->def->opts.is_view) {
 		const char *err_msg =
-			tt_sprintf("can not truncate space '%s' because it is "
-				   "a view", space->def->name);
+			tt_sprintf("can not truncate space '%s' because space "\
+				   "is a view", space->def->name);
 		diag_set(ClientError, ER_SQL, err_msg);
 		goto tarantool_error;
 	}
@@ -163,8 +163,9 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 			goto delete_from_cleanup;
 
 		if (trigger_list == NULL) {
-			sqlErrorMsg(parse, "cannot modify %s because it is a"
-					" view", space->def->name);
+			diag_set(ClientError, ER_ALTER_SPACE, space->def->name,
+				 "space is a view");
+			parse->is_aborted = true;
 			goto delete_from_cleanup;
 		}
 	}
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index a2c7093..de993c1 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -746,19 +746,24 @@ codeVectorCompare(Parse * pParse,	/* Code generator context */
  * Check that argument nHeight is less than or equal to the maximum
  * expression depth allowed. If it is not, leave an error message in
  * pParse.
+ *
+ * @param pParse Parser context.
+ * @param zName Depth to check.
+ *
+ * @retval 0 on success.
+ * @retval -1 on error.
  */
 int
 sqlExprCheckHeight(Parse * pParse, int nHeight)
 {
-	int rc = SQL_OK;
 	int mxHeight = pParse->db->aLimit[SQL_LIMIT_EXPR_DEPTH];
 	if (nHeight > mxHeight) {
-		sqlErrorMsg(pParse,
-				"Expression tree is too large (maximum depth %d)",
-				mxHeight);
-		rc = SQL_ERROR;
+		diag_set(ClientError, ER_SQL_PARSER_LIMIT, "Number of nodes "\
+			 "in expression tree", nHeight, mxHeight);
+		pParse->is_aborted = true;
+		return -1;
 	}
-	return rc;
+	return 0;
 }
 
 /* The following three functions, heightOfExpr(), heightOfExprList()
@@ -1197,9 +1202,9 @@ sqlExprAssignVarNumber(Parse * pParse, Expr * pExpr, u32 n)
 			testcase(i == SQL_BIND_PARAMETER_MAX - 1);
 			testcase(i == SQL_BIND_PARAMETER_MAX);
 			if (!is_ok || i < 1 || i > SQL_BIND_PARAMETER_MAX) {
-				sqlErrorMsg(pParse,
-						"variable number must be between $1 and $%d",
-						SQL_BIND_PARAMETER_MAX);
+				diag_set(ClientError, ER_SQL_BIND_PARAMETER_MAX,
+					 SQL_BIND_PARAMETER_MAX);
+				pParse->is_aborted = true;
 				return;
 			}
 			if (x > pParse->nVar) {
@@ -1227,7 +1232,9 @@ sqlExprAssignVarNumber(Parse * pParse, Expr * pExpr, u32 n)
 	}
 	pExpr->iColumn = x;
 	if (x > SQL_BIND_PARAMETER_MAX) {
-		sqlErrorMsg(pParse, "too many SQL variables");
+		diag_set(ClientError, ER_SQL_BIND_PARAMETER_MAX,
+			 SQL_BIND_PARAMETER_MAX);
+		pParse->is_aborted = true;
 	}
 }
 
@@ -1907,22 +1914,6 @@ sqlExprListSetSpan(Parse * pParse,	/* Parsing context */
 }
 
 /*
- * If the expression list pEList contains more than iLimit elements,
- * leave an error message in pParse.
- */
-void
-sqlExprListCheckLength(Parse * pParse,
-			   ExprList * pEList, const char *zObject)
-{
-	int mx = pParse->db->aLimit[SQL_LIMIT_COLUMN];
-	testcase(pEList && pEList->nExpr == mx);
-	testcase(pEList && pEList->nExpr == mx + 1);
-	if (pEList && pEList->nExpr > mx) {
-		sqlErrorMsg(pParse, "too many columns in %s", zObject);
-	}
-}
-
-/*
  * Delete an entire expression list.
  */
 static SQL_NOINLINE void
@@ -3355,15 +3346,15 @@ expr_code_int(struct Parse *parse, struct Expr *expr, bool is_neg,
 		int c = sql_dec_or_hex_to_i64(z, &value);
 		if (c == 1 || (c == 2 && !is_neg) ||
 		    (is_neg && value == SMALLEST_INT64)) {
+			const char *sign = is_neg ? "-" : "";
 			if (sql_strnicmp(z, "0x", 2) == 0) {
-				sqlErrorMsg(parse,
-						"hex literal too big: %s%s",
-						is_neg ? "-" : "", z);
+				diag_set(ClientError, ER_HEX_LITERAL_MAX, sign,
+					 z, strlen(z) - 2, 16);
 			} else {
-				sqlErrorMsg(parse,
-						"oversized integer: %s%s",
-						is_neg ? "-" : "", z);
+				diag_set(ClientError, ER_INT_LITERAL_MAX, sign,
+					 z, INT64_MIN, INT64_MAX);
 			}
+			parse->is_aborted = true;
 		} else {
 			if (is_neg)
 				value = c == 2 ? SMALLEST_INT64 : -value;
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 6f7f020..0ca38ae 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -322,8 +322,9 @@ sqlInsert(Parse * pParse,	/* Parser context */
 
 	/* Cannot insert into a read-only table. */
 	if (is_view && tmask == 0) {
-		sqlErrorMsg(pParse, "cannot modify %s because it is a view",
-				space_def->name);
+		diag_set(ClientError, ER_ALTER_SPACE, space->def->name,
+			 "space is a view");
+		pParse->is_aborted = true;
 		goto insert_cleanup;
 	}
 
@@ -388,9 +389,10 @@ sqlInsert(Parse * pParse,	/* Parser context */
 				}
 			}
 			if (j >= (int) space_def->field_count) {
-				sqlErrorMsg(pParse,
-						"table %S has no column named %s",
-						pTabList, 0, pColumn->a[i].zName);
+				diag_set(ClientError, ER_NO_SUCH_FIELD_NAME,
+					 pColumn->a[i].zName,
+					 pTabList->a[0].zName);
+				pParse->is_aborted = true;
 				goto insert_cleanup;
 			}
 			if (bit_test(used_columns, j)) {
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index b27651c..ff668e0 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -410,9 +410,10 @@ cmd ::= select(X).  {
         (mxSelect = pParse->db->aLimit[SQL_LIMIT_COMPOUND_SELECT])>0 &&
         cnt>mxSelect
       ){
-        sqlErrorMsg(pParse, "Too many UNION or EXCEPT or INTERSECT "
-                        "operations (limit %d is set)",
-                        pParse->db->aLimit[SQL_LIMIT_COMPOUND_SELECT]);
+         diag_set(ClientError, ER_SQL_PARSER_LIMIT, "The number of UNION or "\
+                  "EXCEPT or INTERSECT operations", cnt,
+                  pParse->db->aLimit[SQL_LIMIT_COMPOUND_SELECT]);
+         pParse->is_aborted = true;
       }
     }
   }
@@ -744,7 +745,11 @@ cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
         where_opt(W).  {
   sqlWithPush(pParse, C, 1);
   sqlSrcListIndexedBy(pParse, X, &I);
-  sqlExprListCheckLength(pParse,Y,"set list");
+  if (Y != NULL && Y->nExpr > pParse->db->aLimit[SQL_LIMIT_COLUMN]) {
+    diag_set(ClientError, ER_SQL_PARSER_LIMIT, "The number of columns in set "\
+             "list", Y->nExpr, pParse->db->aLimit[SQL_LIMIT_COLUMN]);
+    pParse->is_aborted = true;
+  }
   sqlSubProgramsRemaining = SQL_MAX_COMPILING_TRIGGERS;
   /* Instruct SQL to initate Tarantool's transaction.  */
   pParse->initiateTTrans = true;
@@ -932,7 +937,11 @@ expr(A) ::= CAST(X) LP expr(E) AS typedef(T) RP(Y). {
 %endif  SQL_OMIT_CAST
 expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP(E). {
   if( Y && Y->nExpr>pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG] ){
-    sqlErrorMsg(pParse, "too many arguments on function %T", &X);
+    const char *err =
+      tt_sprintf("Number of arguments to function %.*s", X.n, X.z);
+    diag_set(ClientError, ER_SQL_PARSER_LIMIT, err, Y->nExpr,
+             pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]);
+    pParse->is_aborted = true;
   }
   A.pExpr = sqlExprFunction(pParse, Y, &X);
   spanSet(&A,&X,&E);
@@ -948,7 +957,11 @@ expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP(E). {
 type_func(A) ::= CHAR(A) .
 expr(A) ::= type_func(X) LP distinct(D) exprlist(Y) RP(E). {
   if( Y && Y->nExpr>pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG] ){
-    sqlErrorMsg(pParse, "too many arguments on function %T", &X);
+    const char *err =
+      tt_sprintf("Number of arguments to function %.*s", X.n, X.z);
+    diag_set(ClientError, ER_SQL_PARSER_LIMIT, err, Y->nExpr,
+             pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]);
+    pParse->is_aborted = true;
   }
   A.pExpr = sqlExprFunction(pParse, Y, &X);
   spanSet(&A,&X,&E);
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 94bb0af..8cdcfd0 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -510,30 +510,6 @@ sqlCreateColumnExpr(sql * db, SrcList * pSrc, int iSrc, int iCol)
 }
 
 /*
- * Report an error that an expression is not valid for some set of
- * pNC->ncFlags values determined by validMask.
- */
-static void
-notValid(Parse * pParse,	/* Leave error message here */
-	 NameContext * pNC,	/* The name context */
-	 const char *zMsg,	/* Type of error */
-	 int validMask		/* Set of contexts for which prohibited */
-    )
-{
-	assert((validMask & ~(NC_IsCheck | NC_IdxExpr)) == 0);
-	if ((pNC->ncFlags & validMask) != 0) {
-		const char *zIn;
-		if (pNC->ncFlags & NC_IdxExpr)
-			zIn = "index expressions";
-		else if (pNC->ncFlags & NC_IsCheck)
-			zIn = "CHECK constraints";
-		else
-			unreachable();
-		sqlErrorMsg(pParse, "%s prohibited in %s", zMsg, zIn);
-	}
-}
-
-/*
  * Expression p should encode a floating point value between 1.0 and 0.0.
  * Return 1024 times this value.  Or return -1 if p is not a floating point
  * value between 1.0 and 0.0.
@@ -605,7 +581,11 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 			Expr *pRight;
 
 			/* if( pSrcList==0 ) break; */
-			notValid(pParse, pNC, "the \".\" operator", NC_IdxExpr);
+			if (pNC->ncFlags & NC_IdxExpr) {
+				diag_set(ClientError, ER_INDEX_DEF_UNSUPPORTED,
+					 "Expressions");
+				pParse->is_aborted = true;
+			}
 			pRight = pExpr->pRight;
 			if (pRight->op == TK_ID) {
 				zTable = pExpr->pLeft->u.zToken;
@@ -646,6 +626,9 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 			} else {
 				is_agg = pDef->xFinalize != 0;
 				pExpr->type = pDef->ret_type;
+				const char *err_msg =
+					"second argument to likelihood() must "\
+					"be a constant between 0.0 and 1.0";
 				if (pDef->funcFlags & SQL_FUNC_UNLIKELY) {
 					ExprSetProperty(pExpr,
 							EP_Unlikely | EP_Skip);
@@ -654,9 +637,11 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 						    exprProbability(pList->a[1].
 								    pExpr);
 						if (pExpr->iTable < 0) {
-							sqlErrorMsg(pParse,
-									"second argument to likelihood() must be a "
-									"constant between 0.0 and 1.0");
+							diag_set(ClientError,
+								 ER_ILLEGAL_PARAMS,
+								 err_msg);
+							pParse->is_aborted =
+								true;
 							pNC->nErr++;
 						}
 					} else {
@@ -690,9 +675,7 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 					 * that might change over time cannot be used
 					 * in an index.
 					 */
-					notValid(pParse, pNC,
-						 "non-deterministic functions",
-						 NC_IdxExpr);
+					assert((pNC->ncFlags & NC_IdxExpr) == 0);
 				}
 			}
 			if (is_agg && (pNC->ncFlags & NC_AllowAgg) == 0) {
@@ -754,8 +737,13 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 			testcase(pExpr->op == TK_IN);
 			if (ExprHasProperty(pExpr, EP_xIsSelect)) {
 				int nRef = pNC->nRef;
-				notValid(pParse, pNC, "subqueries",
-					 NC_IsCheck | NC_IdxExpr);
+				assert((pNC->ncFlags & NC_IdxExpr) == 0);
+				if (pNC->ncFlags & NC_IsCheck) {
+					diag_set(ClientError,
+						 ER_CK_DEF_UNSUPPORTED,
+						 "Subqueries");
+					pParse->is_aborted = true;
+				}
 				sqlWalkSelect(pWalker, pExpr->x.pSelect);
 				assert(pNC->nRef >= nRef);
 				if (nRef != pNC->nRef) {
@@ -766,8 +754,12 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 			break;
 		}
 	case TK_VARIABLE:{
-			notValid(pParse, pNC, "parameters",
-				 NC_IsCheck | NC_IdxExpr);
+			assert((pNC->ncFlags & NC_IsCheck) == 0);
+			if (pNC->ncFlags & NC_IdxExpr) {
+				diag_set(ClientError, ER_INDEX_DEF_UNSUPPORTED,
+					 "Parameter markers");
+				pParse->is_aborted = true;
+			}
 			break;
 		}
 	case TK_BETWEEN:
@@ -952,7 +944,10 @@ resolveCompoundOrderBy(Parse * pParse,	/* Parsing context.  Leave error messages
 	db = pParse->db;
 #if SQL_MAX_COLUMN
 	if (pOrderBy->nExpr > db->aLimit[SQL_LIMIT_COLUMN]) {
-		sqlErrorMsg(pParse, "too many terms in ORDER BY clause");
+		diag_set(ClientError, ER_SQL_PARSER_LIMIT,
+			 "The number of terms in ORDER BY clause",
+			 pOrderBy->nExpr, db->aLimit[SQL_LIMIT_COLUMN]);
+		pParse->is_aborted = true;
 		return 1;
 	}
 #endif
@@ -1042,27 +1037,34 @@ resolveCompoundOrderBy(Parse * pParse,	/* Parsing context.  Leave error messages
  * field) then convert that term into a copy of the corresponding result set
  * column.
  *
- * If any errors are detected, add an error message to pParse and
- * return non-zero.  Return zero if no errors are seen.
+ * @param pParse Parsing context.
+ * @param pSelect The SELECT statement containing the clause.
+ * @param pOrderBy The ORDER BY or GROUP BY clause to be
+ *                 processed.
+ * @param is_order_by True if pOrderBy is ORDER BY, false if
+ *                    pOrderBy is GROUP BY
+ * @retval 0 On success, not 0 elsewhere.
  */
 int
-sqlResolveOrderGroupBy(Parse * pParse,	/* Parsing context.  Leave error messages here */
-			   Select * pSelect,	/* The SELECT statement containing the clause */
-			   ExprList * pOrderBy,	/* The ORDER BY or GROUP BY clause to be processed */
-			   const char *zType	/* "ORDER" or "GROUP" */
-    )
+sqlResolveOrderGroupBy(Parse *pParse, Select *pSelect, ExprList *pOrderBy,
+		       bool is_order_by)
 {
 	int i;
 	sql *db = pParse->db;
 	ExprList *pEList;
 	struct ExprList_item *pItem;
+	const char *zType = is_order_by ? "ORDER" : "GROUP";
 
 	if (pOrderBy == 0 || pParse->db->mallocFailed)
 		return 0;
 #if SQL_MAX_COLUMN
 	if (pOrderBy->nExpr > db->aLimit[SQL_LIMIT_COLUMN]) {
-		sqlErrorMsg(pParse, "too many terms in %s BY clause",
-				zType);
+		const char *err_msg =
+			is_order_by ? "The number of terms in ORDER BY clause" :
+				      "The number of terms in GROUP BY clause";
+		diag_set(ClientError, ER_SQL_PARSER_LIMIT, err_msg,
+			 pOrderBy->nExpr, db->aLimit[SQL_LIMIT_COLUMN]);
+		pParse->is_aborted = true;
 		return 1;
 	}
 #endif
@@ -1096,22 +1098,23 @@ sqlResolveOrderGroupBy(Parse * pParse,	/* Parsing context.  Leave error messages
  * result-set expression.  Otherwise, the expression is resolved in
  * the usual way - using sqlResolveExprNames().
  *
- * This routine returns the number of errors.  If errors occur, then
- * an appropriate error message might be left in pParse.  (OOM errors
- * excepted.)
+ * @param pNC The name context of the SELECT statement.
+ * @param pSelect The SELECT statement containing the clause.
+ * @param pOrderBy An ORDER BY or GROUP BY clause to resolve.
+ * @param is_order_by True if pOrderBy is ORDER BY, false if
+ *                    pOrderBy is GROUP BY
+ * @retval 0 On success, not 0 elsewhere.
  */
 static int
-resolveOrderGroupBy(NameContext * pNC,	/* The name context of the SELECT statement */
-		    Select * pSelect,	/* The SELECT statement holding pOrderBy */
-		    ExprList * pOrderBy,	/* An ORDER BY or GROUP BY clause to resolve */
-		    const char *zType	/* Either "ORDER" or "GROUP", as appropriate */
-    )
+resolveOrderGroupBy(NameContext *pNC, Select *pSelect, ExprList *pOrderBy,
+		    bool is_order_by)
 {
 	int i, j;		/* Loop counters */
 	int iCol;		/* Column number */
 	struct ExprList_item *pItem;	/* A term of the ORDER BY clause */
 	Parse *pParse;		/* Parsing context */
 	int nResult;		/* Number of terms in the result set */
+	const char *zType = is_order_by ? "ORDER" : "GROUP";
 
 	if (pOrderBy == 0)
 		return 0;
@@ -1158,7 +1161,7 @@ resolveOrderGroupBy(NameContext * pNC,	/* The name context of the SELECT stateme
 			}
 		}
 	}
-	return sqlResolveOrderGroupBy(pParse, pSelect, pOrderBy, zType);
+	return sqlResolveOrderGroupBy(pParse, pSelect, pOrderBy, is_order_by);
 }
 
 /*
@@ -1401,7 +1404,7 @@ resolveSelectStep(Walker * pWalker, Select * p)
 		 * resolve those symbols on the incorrect ORDER BY for consistency.
 		 */
 		if (isCompound <= nCompound	/* Defer right-most ORDER BY of a compound */
-		    && resolveOrderGroupBy(&sNC, p, p->pOrderBy, "ORDER")
+		    && resolveOrderGroupBy(&sNC, p, p->pOrderBy, true)
 		    ) {
 			return WRC_Abort;
 		}
@@ -1415,7 +1418,7 @@ resolveSelectStep(Walker * pWalker, Select * p)
 		if (pGroupBy) {
 			struct ExprList_item *pItem;
 
-			if (resolveOrderGroupBy(&sNC, p, pGroupBy, "GROUP")
+			if (resolveOrderGroupBy(&sNC, p, pGroupBy, false)
 			    || db->mallocFailed) {
 				return WRC_Abort;
 			}
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 5195656..3f58f30 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -3396,11 +3396,9 @@ multiSelectOrderBy(Parse * pParse,	/* Parsing context */
 	 */
 	p->pPrior = 0;
 	pPrior->pNext = 0;
-	sqlResolveOrderGroupBy(pParse, p, p->pOrderBy, "ORDER");
-	if (pPrior->pPrior == 0) {
-		sqlResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy,
-					   "ORDER");
-	}
+	sqlResolveOrderGroupBy(pParse, p, p->pOrderBy, true);
+	if (pPrior->pPrior == 0)
+		sqlResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, true);
 
 	/* Compute the limit registers */
 	computeLimitRegisters(pParse, p, labelEnd);
@@ -5064,7 +5062,10 @@ selectExpander(Walker * pWalker, Select * p)
 	}
 #if SQL_MAX_COLUMN
 	if (p->pEList && p->pEList->nExpr > db->aLimit[SQL_LIMIT_COLUMN]) {
-		sqlErrorMsg(pParse, "too many columns in result set");
+		diag_set(ClientError, ER_SQL_PARSER_LIMIT, "The number of "\
+			 "columns in result set", p->pEList->nExpr,
+			 db->aLimit[SQL_LIMIT_COLUMN]);
+		pParse->is_aborted = true;
 		return WRC_Abort;
 	}
 #endif
@@ -5525,10 +5526,10 @@ sqlSelect(Parse * pParse,		/* The parser context */
 		 * columns in the SELECT on the RHS
 		 */
 		if ((int)space->def->field_count != pSub->pEList->nExpr) {
-			sqlErrorMsg(pParse,
-					"expected %d columns for '%s' but got %d",
-					space->def->field_count, space->def->name,
-					pSub->pEList->nExpr);
+			diag_set(ClientError, ER_CREATE_SPACE, space->def->name,
+				 "number of aliases doesn't match provided "\
+				 "columns");
+			pParse->is_aborted = true;
 			goto select_end;
 		}
 
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 8967ea3..363ee8d 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -4441,7 +4441,7 @@ int sqlMatchSpanName(const char *, const char *, const char *);
 int sqlResolveExprNames(NameContext *, Expr *);
 int sqlResolveExprListNames(NameContext *, ExprList *);
 void sqlResolveSelectNames(Parse *, Select *, NameContext *);
-int sqlResolveOrderGroupBy(Parse *, Select *, ExprList *, const char *);
+int sqlResolveOrderGroupBy(Parse *, Select *, ExprList *, bool);
 
 /**
  * Generate code for default value.
@@ -4649,7 +4649,6 @@ sql_int64 sqlStmtCurrentTime(sql_context *);
 int sqlVdbeParameterIndex(Vdbe *, const char *, int);
 int sqlTransferBindings(sql_stmt *, sql_stmt *);
 int sqlReprepare(Vdbe *);
-void sqlExprListCheckLength(Parse *, ExprList *, const char *);
 
 /**
  * This function verifies that two collations (to be more precise
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index de0f282..efb895f 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -481,8 +481,8 @@ sqlRunParser(Parse * pParse, const char *zSql)
 				      &pParse->sLastToken.isReserved);
 			i += pParse->sLastToken.n;
 			if (i > mxSqlLen) {
-				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
-					 "string or blob too big");
+				diag_set(ClientError, ER_SQL_PARSER_LIMIT,
+					 "SQL command length", i, mxSqlLen);
 				pParse->is_aborted = true;
 				break;
 			}
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index 7eacd33..b23d60a 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -86,7 +86,7 @@ sql_trigger_begin(struct Parse *parse, struct Token *name, int tr_tm,
 	if (trigger_name == NULL)
 		goto trigger_cleanup;
 
-	if (sqlCheckIdentifierName(parse, trigger_name) != SQL_OK)
+	if (sqlCheckIdentifierName(parse, trigger_name) != 0)
 		goto trigger_cleanup;
 
 	const char *table_name = table->a[0].zName;
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 05ceeb4..71a1e00 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -139,8 +139,9 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 		goto update_cleanup;
 	}
 	if (is_view && tmask == 0) {
-		sqlErrorMsg(pParse, "cannot modify %s because it is a view",
-				space->def->name);
+		diag_set(ClientError, ER_ALTER_SPACE, space->def->name,
+			 "space is a view");
+		pParse->is_aborted = true;
 		goto update_cleanup;
 	}
 
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index ebc2624..2f83e81 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -4286,7 +4286,9 @@ sqlWhereBegin(Parse * pParse,	/* The parser context */
 	 */
 	testcase(pTabList->nSrc == BMS);
 	if (pTabList->nSrc > BMS) {
-		sqlErrorMsg(pParse, "at most %d tables in a join", BMS);
+		diag_set(ClientError, ER_SQL_PARSER_LIMIT, "The number of "\
+			 "tables in a join", pTabList->nSrc, BMS);
+		pParse->is_aborted = true;
 		return 0;
 	}
 
diff --git a/test/box/alter.result b/test/box/alter.result
index 9a1086e..37bc51c 100644
--- a/test/box/alter.result
+++ b/test/box/alter.result
@@ -367,7 +367,7 @@ s:drop()
 ...
 box.schema.space.create('')
 ---
-- error: Invalid identifier '' (expected printable symbols only)
+- error: Invalid identifier '' (expected printable symbols only or it is too long)
 ...
 -- valid identifiers
 box.schema.space.create('_Abcde'):drop()
@@ -438,7 +438,7 @@ i:drop()
 ...
 space:create_index('')
 ---
-- error: Invalid identifier '' (expected printable symbols only)
+- error: Invalid identifier '' (expected printable symbols only or it is too long)
 ...
 space:drop()
 ---
diff --git a/test/box/misc.result b/test/box/misc.result
index c350bbd..5dda752 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -516,6 +516,12 @@ t;
   185: box.error.SQL_UNKNOWN_TOKEN
   186: box.error.SQL_PARSER_GENERIC
   187: box.error.SQL_ANALYZE_ARGUMENT
+  188: box.error.SQL_COLUMN_COUNT_MAX
+  189: box.error.HEX_LITERAL_MAX
+  190: box.error.INT_LITERAL_MAX
+  191: box.error.SQL_PARSER_LIMIT
+  192: box.error.INDEX_DEF_UNSUPPORTED
+  193: box.error.CK_DEF_UNSUPPORTED
 ...
 test_run:cmd("setopt delimiter ''");
 ---
diff --git a/test/sql-tap/autoinc.test.lua b/test/sql-tap/autoinc.test.lua
index dc2f60e..7abb679 100755
--- a/test/sql-tap/autoinc.test.lua
+++ b/test/sql-tap/autoinc.test.lua
@@ -561,7 +561,7 @@ test:do_catchsql_test(
         CREATE TABLE t8(x TEXT PRIMARY KEY AUTOINCREMENT);
     ]], {
         -- <autoinc-7.2>
-        1, "AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY or INT PRIMARY KEY"
+        1, "Failed to create space 'T8': AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY or INT PRIMARY KEY"
         -- </autoinc-7.2>
     })
 
diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
index 0d8bf15..dd2de92 100755
--- a/test/sql-tap/check.test.lua
+++ b/test/sql-tap/check.test.lua
@@ -319,7 +319,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <check-3.1>
-        1, "Failed to create space 'T3': subqueries prohibited in CHECK constraints"
+        1, "Failed to create space 'T3': Subqueries are prohibited in a CHECK constraint definition"
         -- </check-3.1>
     })
 
diff --git a/test/sql-tap/colname.test.lua b/test/sql-tap/colname.test.lua
index 29fdf13..253497c 100755
--- a/test/sql-tap/colname.test.lua
+++ b/test/sql-tap/colname.test.lua
@@ -637,7 +637,7 @@ test:do_test(
 test:do_catchsql_test(
     "colname-11.1",
     [[ create table t1(a INT, b INT, c INT, primary key('A'))]],
-    {1, "expressions prohibited in PRIMARY KEY"})
+    {1, "Expressions are prohibited in an index definition"})
 
 test:do_catchsql_test(
     "colname-11.2",
diff --git a/test/sql-tap/default.test.lua b/test/sql-tap/default.test.lua
index 67151b0..564d373 100755
--- a/test/sql-tap/default.test.lua
+++ b/test/sql-tap/default.test.lua
@@ -66,7 +66,7 @@ test:do_catchsql_test(
 	);
 	]], {
 	-- <default-1.3>
-	1, "default value of column [Y] is not constant"
+	1, "Failed to create space 'T3': default value of column 'Y' is not constant"
 	-- </default-1.3>
 })
 
@@ -173,7 +173,7 @@ test:do_catchsql_test(
 	);
 	]], {
 	-- <default-4.2>
-	1, "default value of column [B] is not constant"
+	1, "Failed to create space 'T2': default value of column 'B' is not constant"
 	-- </default-4.2>
 })
 
@@ -187,7 +187,7 @@ test:do_catchsql_test(
 	);
 	]], {
 	-- <default-4.3>
-	1, "default value of column [B] is not constant"
+	1, "Failed to create space 'T2': default value of column 'B' is not constant"
 	-- </default-4.3>
 })
 
@@ -201,7 +201,7 @@ test:do_catchsql_test(
 	);
 	]], {
 	-- <default-4.4>
-	1, "default value of column [B] is not constant"
+	1, "Failed to create space 'T2': default value of column 'B' is not constant"
 	-- </default-4.4>
 })
 
@@ -214,7 +214,7 @@ test:do_catchsql_test(
         CREATE TABLE t6(id INTEGER PRIMARY KEY, b TEXT DEFAULT(id));
     ]], {
     -- <default-5.1>
-    1, "default value of column [B] is not constant"
+    1, "Failed to create space 'T6': default value of column 'B' is not constant"
     -- </default-5.1>
 })
 
diff --git a/test/sql-tap/fkey2.test.lua b/test/sql-tap/fkey2.test.lua
index d347e5a..678b857 100755
--- a/test/sql-tap/fkey2.test.lua
+++ b/test/sql-tap/fkey2.test.lua
@@ -729,7 +729,7 @@ test:do_catchsql_test(
         CREATE TABLE c(x  INT PRIMARY KEY REFERENCES v(y));
     ]], {
         -- <fkey2-7.2>
-        1, "referenced table can't be view"
+        1, "Failed to create foreign key constraint 'FK_CONSTRAINT_1_C': referenced space can't be VIEW"
         -- </fkey2-7.2>
     })
 
@@ -1157,7 +1157,7 @@ test:do_catchsql_test(
         CREATE TABLE t1(x INT PRIMARY KEY REFERENCES v);
     ]], {
         -- <fkey2-10.20>
-        1, "referenced table can't be view"
+        1, "Failed to create foreign key constraint 'FK_CONSTRAINT_1_T1': referenced space can't be VIEW"
         -- </fkey2-10.20>
     })
 
diff --git a/test/sql-tap/func3.test.lua b/test/sql-tap/func3.test.lua
index 4ea9a5c..6d6411c 100755
--- a/test/sql-tap/func3.test.lua
+++ b/test/sql-tap/func3.test.lua
@@ -104,7 +104,7 @@ test:do_catchsql_test(
         SELECT likelihood(123, 1.000001);
     ]], {
         -- <func3-5.8>
-        1, "second argument to likelihood() must be a constant between 0.0 and 1.0"
+        1, "Illegal parameters, second argument to likelihood() must be a constant between 0.0 and 1.0"
         -- </func3-5.8>
     })
 
@@ -114,7 +114,7 @@ test:do_catchsql_test(
         SELECT likelihood(123, -0.000001);
     ]], {
         -- <func3-5.9>
-        1, "second argument to likelihood() must be a constant between 0.0 and 1.0"
+        1, "Illegal parameters, second argument to likelihood() must be a constant between 0.0 and 1.0"
         -- </func3-5.9>
     })
 
@@ -124,7 +124,7 @@ test:do_catchsql_test(
         SELECT likelihood(123, 0.5+0.3);
     ]], {
         -- <func3-5.10>
-        1, "second argument to likelihood() must be a constant between 0.0 and 1.0"
+        1, "Illegal parameters, second argument to likelihood() must be a constant between 0.0 and 1.0"
         -- </func3-5.10>
     })
 
diff --git a/test/sql-tap/gh-2549-many-columns.test.lua b/test/sql-tap/gh-2549-many-columns.test.lua
index 3de4d67..ed8c9d9 100755
--- a/test/sql-tap/gh-2549-many-columns.test.lua
+++ b/test/sql-tap/gh-2549-many-columns.test.lua
@@ -35,7 +35,7 @@ test:do_catchsql_test(
 	"columns-1.2",
 	fail_statement, {
 		-- <columns-1.2>
-		1, "too many columns on T2"
+		1, "Failed to create space 'T2': space column count 2001 exceeds the limit (2000)"
 		-- <columns-1.2>
 	})
 
diff --git a/test/sql-tap/gh2548-select-compound-limit.test.lua b/test/sql-tap/gh2548-select-compound-limit.test.lua
index 5494a66..e8c8d95 100755
--- a/test/sql-tap/gh2548-select-compound-limit.test.lua
+++ b/test/sql-tap/gh2548-select-compound-limit.test.lua
@@ -58,7 +58,7 @@ test:do_catchsql_test(
     "gh2548-select-compound-limit-2",
     select_string_last, {
         -- <gh2548-select-compound-limit-2>
-        1, "Too many UNION or EXCEPT or INTERSECT operations (limit 30 is set)"
+        1, "The number of UNION or EXCEPT or INTERSECT operations 31 exceeds the limit (30)"
         -- </gh2548-select-compound-limit-2>
     })
 
diff --git a/test/sql-tap/hexlit.test.lua b/test/sql-tap/hexlit.test.lua
index 158eda7..288d823 100755
--- a/test/sql-tap/hexlit.test.lua
+++ b/test/sql-tap/hexlit.test.lua
@@ -107,7 +107,7 @@ test:do_catchsql_test(
         SELECT 0x10000000000000000;
     ]], {
         -- <hexlist-400>
-        1, "hex literal too big: 0x10000000000000000"
+        1, "Hex literal 0x10000000000000000 length 17 exceeds the supported limit (16)"
         -- </hexlist-400>
     })
 
@@ -119,7 +119,7 @@ test:do_catchsql_test(
         INSERT INTO t1 VALUES(1+0x10000000000000000);
     ]], {
         -- <hexlist-410>
-        1, "hex literal too big: 0x10000000000000000"
+        1, "Hex literal 0x10000000000000000 length 17 exceeds the supported limit (16)"
         -- </hexlist-410>
     })
 
diff --git a/test/sql-tap/insert1.test.lua b/test/sql-tap/insert1.test.lua
index 363fa8a..031fd22 100755
--- a/test/sql-tap/insert1.test.lua
+++ b/test/sql-tap/insert1.test.lua
@@ -77,7 +77,7 @@ test:do_catchsql_test("insert-1.4", [[
   INSERT INTO test1(one,four) VALUES(1,2)
 ]], {
   -- <insert-1.4>
-  1, "table TEST1 has no column named FOUR"
+  1, "Field 'FOUR' was not found in the space 'TEST1' format"
   -- </insert-1.4>
 })
 
diff --git a/test/sql-tap/join.test.lua b/test/sql-tap/join.test.lua
index da29f77..ef60609 100755
--- a/test/sql-tap/join.test.lua
+++ b/test/sql-tap/join.test.lua
@@ -1067,11 +1067,11 @@ end
 jointest("join-12.2", 30, {0, {1}})
 jointest("join-12.3", 63, {0, {1}})
 jointest("join-12.4", 64, {0, {1}})
-jointest("join-12.5", 65, {1, 'at most 64 tables in a join'})
-jointest("join-12.6", 66, {1, 'at most 64 tables in a join'})
-jointest("join-12.7", 127, {1, 'at most 64 tables in a join'})
-jointest("join-12.8", 128, {1, 'at most 64 tables in a join'})
-jointest("join-12.9", 1000, {1, 'at most 64 tables in a join'})
+jointest("join-12.5", 65, {1, 'The number of tables in a join 65 exceeds the limit (64)'})
+jointest("join-12.6", 66, {1, 'The number of tables in a join 66 exceeds the limit (64)'})
+jointest("join-12.7", 127, {1, 'The number of tables in a join 127 exceeds the limit (64)'})
+jointest("join-12.8", 128, {1, 'The number of tables in a join 128 exceeds the limit (64)'})
+jointest("join-12.9", 1000, {1, 'The number of tables in a join 1000 exceeds the limit (64)'})
 -- If sql is built with sql_MEMDEBUG, then the huge number of realloc()
 -- calls made by the following test cases are too time consuming to run.
 -- Without sql_MEMDEBUG, realloc() is fast enough that these are not
@@ -1079,10 +1079,10 @@ jointest("join-12.9", 1000, {1, 'at most 64 tables in a join'})
 --if X(0, "X!capable", [["pragma&&compileoption_diags"]]) then
 --    if X(703, "X!cmd", [=[["expr","[lsearch [db eval {PRAGMA compile_options}] MEMDEBUG]<0"]]=])
 -- then
-jointest("join-12.10", 65534, {1, 'at most 64 tables in a join'})
-jointest("join-12.11", 65535, {1, 'at most 64 tables in a join'})
-jointest("join-12.12", 65536, {1, 'at most 64 tables in a join'})
-jointest("join-12.13", 65537, {1, 'at most 64 tables in a join'})
+jointest("join-12.10", 65534, {1, 'The number of tables in a join 65534 exceeds the limit (64)'})
+jointest("join-12.11", 65535, {1, 'The number of tables in a join 65535 exceeds the limit (64)'})
+jointest("join-12.12", 65536, {1, 'The number of tables in a join 65536 exceeds the limit (64)'})
+jointest("join-12.13", 65537, {1, 'The number of tables in a join 65537 exceeds the limit (64)'})
 --    end
 --end
 
diff --git a/test/sql-tap/join3.test.lua b/test/sql-tap/join3.test.lua
index 6b822de..876b312 100755
--- a/test/sql-tap/join3.test.lua
+++ b/test/sql-tap/join3.test.lua
@@ -85,7 +85,7 @@ test:do_test(
         return test:catchsql(sql)
     end, {
         -- <join3-3.1>
-        1, "at most "..bitmask_size.." tables in a join"
+        1, "The number of tables in a join " .. bitmask_size + 1 .. " exceeds the limit (".. bitmask_size ..")"
         -- </join3-3.1>
     })
 
diff --git a/test/sql-tap/misc1.test.lua b/test/sql-tap/misc1.test.lua
index 1b288da..6b9f5fa 100755
--- a/test/sql-tap/misc1.test.lua
+++ b/test/sql-tap/misc1.test.lua
@@ -348,7 +348,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <misc1-7.1>
-        1, [[table "ERROR1" has more than one primary key]]
+        1, [[Failed to create space 'ERROR1': primary key already exists]]
         -- </misc1-7.1>
     })
 
@@ -361,7 +361,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <misc1-7.2>
-        1, [[table "ERROR1" has more than one primary key]]
+        1, [[Failed to create space 'ERROR1': primary key already exists]]
         -- </misc1-7.2>
     })
 
@@ -897,7 +897,7 @@ test:do_catchsql_test(
         CREATE TABLE test2(a text primary key, b text, primary key(a,b));
     ]], {
         -- <misc1-16.3>
-        1, [[table "TEST2" has more than one primary key]]
+        1, [[Failed to create space 'TEST2': primary key already exists]]
         -- </misc1-16.3>
     })
 
diff --git a/test/sql-tap/select7.test.lua b/test/sql-tap/select7.test.lua
index 4029c20..7037d20 100755
--- a/test/sql-tap/select7.test.lua
+++ b/test/sql-tap/select7.test.lua
@@ -179,7 +179,7 @@ test:do_catchsql_test(
     "select7-6.2",
     sql, {
         -- <select7-6.2>
-        1, "Too many UNION or EXCEPT or INTERSECT operations (limit 30 is set)"
+        1, "The number of UNION or EXCEPT or INTERSECT operations 33 exceeds the limit (30)"
         -- </select7-6.2>
     })
 
diff --git a/test/sql-tap/sql-errors.test.lua b/test/sql-tap/sql-errors.test.lua
new file mode 100755
index 0000000..ac242c4
--- /dev/null
+++ b/test/sql-tap/sql-errors.test.lua
@@ -0,0 +1,419 @@
+#!/usr/bin/env tarantool
+test = require("sqltester")
+test:plan(36)
+
+test:execsql([[
+	CREATE TABLE t0 (i INT PRIMARY KEY);
+	CREATE VIEW v0 AS SELECT * FROM t0;
+]])
+format = {}
+for i = 1, 2001 do format[i] = {name = 'A' .. i, type = 'unsigned'} end
+s0 = box.schema.space.create('S0', {format = format})
+i0 = s0:create_index('I0')
+
+test:do_catchsql_test(
+	"sql-errors-1.1",
+	[[
+		ANALYZE v0;
+	]], {
+		-- <sql-errors-1.1>
+		1,"ANALYZE statement argument V0 is not a base table"
+		-- </sql-errors-1.1>
+	})
+
+create_statement = 'CREATE TABLE t2 (i INT PRIMARY KEY'
+for i = 1, 2001 do
+	create_statement = create_statement .. ', s' .. i .. ' INT'
+end
+create_statement = create_statement .. ');'
+
+test:do_catchsql_test(
+	"sql-errors-1.2",
+	create_statement,
+	{
+		-- <sql-errors-1.2>
+		1,"Failed to create space 'T2': space column count 2001 exceeds the limit (2000)"
+		-- </sql-errors-1.2>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.3",
+	[[
+		CREATE TABLE t3 (i INT PRIMARY KEY, a INT DEFAULT(MAX(i, 1)));
+	]], {
+		-- <sql-errors-1.3>
+		1,"Failed to create space 'T3': default value of column 'A' is not constant"
+		-- </sql-errors-1.3>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.4",
+	[[
+		CREATE TABLE t4 (i INT PRIMARY KEY, a INT PRIMARY KEY);
+	]], {
+		-- <sql-errors-1.4>
+		1,"Failed to create space 'T4': primary key already exists"
+		-- </sql-errors-1.4>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.5",
+	[[
+		CREATE TABLE t5 (i TEXT PRIMARY KEY AUTOINCREMENT);
+	]], {
+		-- <sql-errors-1.5>
+		1,"Failed to create space 'T5': AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY or INT PRIMARY KEY"
+		-- </sql-errors-1.5>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.6",
+	[[
+		CREATE TABLE t6 (i INT);
+	]], {
+		-- <sql-errors-1.6>
+		1,"Failed to create space 'T6': PRIMARY KEY missing"
+		-- </sql-errors-1.6>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.7",
+	[[
+		CREATE VIEW v7(a,b) AS SELECT * FROM t0;
+	]], {
+		-- <sql-errors-1.7>
+		1,"Failed to create space 'V7': number of aliases doesn't match provided columns"
+		-- </sql-errors-1.7>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.8",
+	[[
+		DROP VIEW t0;
+	]], {
+		-- <sql-errors-1.8>
+		1,"Can't drop space 'T0': use DROP TABLE"
+		-- </sql-errors-1.8>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.9",
+	[[
+		DROP TABLE v0;
+	]], {
+		-- <sql-errors-1.9>
+		1,"Can't drop space 'V0': use DROP VIEW"
+		-- </sql-errors-1.9>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.10",
+	[[
+		CREATE TABLE t10(i INT PRIMARY KEY REFERENCES v0);
+	]], {
+		-- <sql-errors-1.10>
+		1,"Failed to create foreign key constraint 'FK_CONSTRAINT_1_T10': referenced space can't be VIEW"
+		-- </sql-errors-1.10>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.11",
+	[[
+		CREATE VIEW v11 AS SELECT * FROM t0 WHERE i = ?;
+	]], {
+		-- <sql-errors-1.11>
+		1,"Failed to create space 'V11': parameters are not allowed in views"
+		-- </sql-errors-1.11>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.12",
+	[[
+		CREATE INDEX i12 ON v0(i);
+	]], {
+		-- <sql-errors-1.12>
+		1,"Can't create or modify index 'I12' in space 'V0': views can not be indexed"
+		-- </sql-errors-1.12>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.13",
+	[[
+		SELECT 9223372036854775808;
+	]], {
+		-- <sql-errors-1.13>
+		1,"Integer literal 9223372036854775808 exceeds the supported range -9223372036854775808 - 9223372036854775807"
+		-- </sql-errors-1.13>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.14",
+	[[
+		SELECT 0x10000000000000000;
+	]], {
+		-- <sql-errors-1.14>
+		1,"Hex literal 0x10000000000000000 length 17 exceeds the supported limit (16)"
+		-- </sql-errors-1.14>
+	})
+
+select_statement = 'SELECT i FROM t0 WHERE i = 0'
+for i = 1, 200 do
+	select_statement = select_statement .. ' OR i = ' .. i
+end
+select_statement = select_statement .. ';'
+
+test:do_catchsql_test(
+	"sql-errors-1.15",
+	select_statement,
+	{
+		-- <sql-errors-1.15>
+		1,"Number of nodes in expression tree 201 exceeds the limit (200)"
+		-- </sql-errors-1.15>
+	})
+
+select_statement = 'SELECT CHAR(1'
+for i = 1, 127 do
+	select_statement = select_statement .. ', ' .. i
+end
+select_statement = select_statement .. ');'
+
+test:do_catchsql_test(
+	"sql-errors-1.16",
+	select_statement,
+	{
+		-- <sql-errors-1.16>
+		1,"Number of arguments to function CHAR 128 exceeds the limit (127)"
+		-- </sql-errors-1.16>
+	})
+
+select_statement = 'SELECT MAX(1'
+for i = 1, 127 do
+	select_statement = select_statement .. ', ' .. i
+end
+select_statement = select_statement .. ');'
+
+test:do_catchsql_test(
+	"sql-errors-1.17",
+	select_statement,
+	{
+		-- <sql-errors-1.17>
+		1,"Number of arguments to function MAX 128 exceeds the limit (127)"
+		-- </sql-errors-1.17>
+	})
+
+select_statement = 'SELECT 0'
+for i = 1, 30 do
+	select_statement = select_statement .. ' UNION ALL SELECT ' .. i
+end
+select_statement = select_statement .. ';'
+
+test:do_catchsql_test(
+	"sql-errors-1.18",
+	select_statement,
+	{
+		-- <sql-errors-1.18>
+		1,"The number of UNION or EXCEPT or INTERSECT operations 31 exceeds the limit (30)"
+		-- </sql-errors-1.18>
+	})
+
+select_statement = 'SELECT 0'
+for i = 1, 2000 do
+	select_statement = select_statement .. ', ' .. i
+end
+select_statement = select_statement .. ';'
+
+test:do_catchsql_test(
+	"sql-errors-1.19",
+	select_statement,
+	{
+		-- <sql-errors-1.19>
+		1,"The number of columns in result set 2001 exceeds the limit (2000)"
+		-- </sql-errors-1.19>
+	})
+
+select_statement = 'SELECT * FROM t0'
+for i = 1, 64 do
+	select_statement = select_statement .. ', t0 as t' .. i
+end
+select_statement = select_statement .. ';'
+
+test:do_catchsql_test(
+	"sql-errors-1.20",
+	select_statement,
+	{
+		-- <sql-errors-1.20>
+		1,"The number of tables in a join 65 exceeds the limit (64)"
+		-- </sql-errors-1.20>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.21",
+	[[
+		SELECT $65001;
+	]], {
+		-- <sql-errors-1.21>
+		1,"SQL bind parameter limit reached: 65000"
+		-- </sql-errors-1.21>
+	})
+
+select_statement = 'SELECT '..string.rep('?, ', box.schema.SQL_BIND_PARAMETER_MAX)..'?;'
+
+test:do_catchsql_test(
+	"sql-errors-1.22",
+	select_statement,
+	{
+		-- <sql-errors-1.22>
+		1,"SQL bind parameter limit reached: 65000"
+		-- </sql-errors-1.22>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.23",
+	[[
+		INSERT INTO v0 VALUES (2);
+	]], {
+		-- <sql-errors-1.23>
+		1,"Can't modify space 'V0': space is a view"
+		-- </sql-errors-1.23>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.24",
+	[[
+		UPDATE v0 SET i = 2 WHERE i = 1;
+	]], {
+		-- <sql-errors-1.24>
+		1,"Can't modify space 'V0': space is a view"
+		-- </sql-errors-1.24>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.25",
+	[[
+		DELETE FROM v0;
+	]], {
+		-- <sql-errors-1.25>
+		1,"Can't modify space 'V0': space is a view"
+		-- </sql-errors-1.25>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.26",
+	[[
+		CREATE TABLE t26 (i INT, PRIMARY KEY('i'));
+	]], {
+		-- <sql-errors-1.26>
+		1,"Expressions are prohibited in an index definition"
+		-- </sql-errors-1.26>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.27",
+	[[
+		CREATE TABLE t27 (i INT PRIMARY KEY, CHECK(i < (SELECT * FROM t0)));
+	]], {
+		-- <sql-errors-1.27>
+		1,"Failed to create space 'T27': Subqueries are prohibited in a CHECK constraint definition"
+		-- </sql-errors-1.27>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.28",
+	[[
+		CREATE INDEX i28 ON t0(t0.i);
+	]], {
+		-- <sql-errors-1.28>
+		1,"Expressions are prohibited in an index definition"
+		-- </sql-errors-1.28>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.29",
+	[[
+		CREATE INDEX i29 ON t0($1);
+	]], {
+		-- <sql-errors-1.29>
+		1,"Parameter markers are prohibited in an index definition"
+		-- </sql-errors-1.29>
+	})
+
+create_index_statement = 'CREATE INDEX i30 on t0(i'..string.rep(', i', 2000)..');'
+
+test:do_catchsql_test(
+	"sql-errors-1.30",
+	create_index_statement,
+	{
+		-- <sql-errors-1.30>
+		1,"The number of columns in index 2001 exceeds the limit (2000)"
+		-- </sql-errors-1.30>
+	})
+
+update_statement = 'UPDATE s0 SET a1 = a1 + 1'
+for i = 2, 2001 do
+	update_statement = update_statement .. ', a' .. i .. ' = a' .. i .. ' + 1'
+end
+update_statement = update_statement .. ';'
+
+test:do_catchsql_test(
+	"sql-errors-1.31",
+	update_statement,
+	{
+		-- <sql-errors-1.31>
+		1,"The number of columns in set list 2001 exceeds the limit (2000)"
+		-- </sql-errors-1.31>
+	})
+
+select_statement = 'SELECT * FROM (SELECT 1 UNION ALL SELECT 1 ORDER BY 1'..string.rep(', 1', 2000)..')'
+
+test:do_catchsql_test(
+	"sql-errors-1.32",
+	select_statement,
+	{
+		-- <sql-errors-1.32>
+		1,"The number of terms in ORDER BY clause 2001 exceeds the limit (2000)"
+		-- </sql-errors-1.32>
+	})
+
+select_statement = 'SELECT 1 ORDER BY 1'..string.rep(', 1', 2000)
+
+test:do_catchsql_test(
+	"sql-errors-1.33",
+	select_statement,
+	{
+		-- <sql-errors-1.33>
+		1,"The number of terms in ORDER BY clause 2001 exceeds the limit (2000)"
+		-- </sql-errors-1.33>
+	})
+
+select_statement = 'SELECT 1 as '..string.rep('x', 65001)
+
+test:do_catchsql_test(
+	"sql-errors-1.34",
+	select_statement,
+	{
+		-- <sql-errors-1.34>
+		1,"Invalid identifier 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' (expected printable symbols only or it is too long)"
+		-- </sql-errors-1.34>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.35",
+	[[
+		SELECT 1 as "";
+	]], {
+		-- <sql-errors-1.35>
+		1,"Invalid identifier '' (expected printable symbols only or it is too long)"
+		-- </sql-errors-1.35>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.36",
+	[[
+		SELECT likelihood(1, 2);
+	]], {
+		-- <sql-errors-1.36>
+		1,"Illegal parameters, second argument to likelihood() must be a constant between 0.0 and 1.0"
+		-- </sql-errors-1.36>
+	})
+
+test:finish_test()
diff --git a/test/sql-tap/unique.test.lua b/test/sql-tap/unique.test.lua
index 56ac74a..a3fa1c9 100755
--- a/test/sql-tap/unique.test.lua
+++ b/test/sql-tap/unique.test.lua
@@ -36,7 +36,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <unique-1.1>
-        1, [[table "T1" has more than one primary key]]
+        1, [[Failed to create space 'T1': primary key already exists]]
         -- </unique-1.1>
     })
 
diff --git a/test/sql-tap/view.test.lua b/test/sql-tap/view.test.lua
index 72fdab8..0032e1b 100755
--- a/test/sql-tap/view.test.lua
+++ b/test/sql-tap/view.test.lua
@@ -189,7 +189,7 @@ test:do_catchsql_test(
         INSERT INTO v2 VALUES(1,2,3,4);
     ]], {
         -- <view-2.2>
-        1, "cannot modify V2 because it is a view"
+        1, "Can't modify space 'V2': space is a view"
         -- </view-2.2>
     })
 
@@ -199,7 +199,7 @@ test:do_catchsql_test(
         UPDATE v2 SET a=10 WHERE a=5;
     ]], {
         -- <view-2.3>
-        1, "cannot modify V2 because it is a view"
+        1, "Can't modify space 'V2': space is a view"
         -- </view-2.3>
     })
 
@@ -209,7 +209,7 @@ test:do_catchsql_test(
         DELETE FROM v2;
     ]], {
         -- <view-2.4>
-        1, "cannot modify V2 because it is a view"
+        1, "Can't modify space 'V2': space is a view"
         -- </view-2.4>
     })
 
@@ -302,7 +302,7 @@ test:do_catchsql_test(
     [[
         CREATE VIEW v1err(x,y) AS SELECT a, b+c, c-b FROM t1;
         SELECT * FROM v1err;
-    ]], {1, "expected 2 columns for 'V1ERR' but got 3"})
+    ]], {1, "Failed to create space 'V1ERR': number of aliases doesn't match provided columns"})
 
 test:do_catchsql_test(
     "view-3.3.5.2",
@@ -310,7 +310,7 @@ test:do_catchsql_test(
         DROP VIEW IF EXISTS v1err;
         CREATE VIEW v1err(w,x,y,z) AS SELECT a, b+c, c-b FROM t1;
         SELECT * FROM v1err;
-    ]], {1, "expected 4 columns for 'V1ERR' but got 3"})
+    ]], {1, "Failed to create space 'V1ERR': number of aliases doesn't match provided columns"})
 
 -- #MUST_WORK_TEST no query solution
 -- # ifcapable compound {
@@ -332,7 +332,7 @@ test:do_catchsql_test(
         DROP VIEW t1;
     ]], {
         -- <view-4.1>
-        1, "use DROP TABLE to delete table T1"
+        1, "Can't drop space 'T1': use DROP TABLE"
         -- </view-4.1>
     })
 
@@ -352,7 +352,7 @@ test:do_catchsql_test(
         DROP TABLE v1;
     ]], {
         -- <view-4.3>
-        1, "use DROP VIEW to delete view V1"
+        1, "Can't drop space 'V1': use DROP VIEW"
         -- </view-4.3>
     })
 
@@ -372,7 +372,7 @@ test:do_catchsql_test(
         CREATE INDEX i1v1 ON v1(xyz);
     ]], {
         -- <view-4.5>
-        1, "views can not be indexed"
+        1, "Can't create or modify index 'I1V1' in space 'V1': views can not be indexed"
         -- </view-4.5>
     })
 
@@ -842,7 +842,7 @@ test:do_catchsql_test(
         CREATE VIEW v12 AS SELECT a FROM t1 WHERE b=?
     ]], {
         -- <view-12.1>
-        1, "parameters are not allowed in views"
+        1, "Failed to create space 'V12': parameters are not allowed in views"
         -- </view-12.1>
     })
 
@@ -852,7 +852,7 @@ test:do_catchsql_test(
         CREATE VIEW v12(x) AS SELECT a FROM t1 WHERE b=?
     ]], {
         -- <view-12.2>
-        1, "parameters are not allowed in views"
+        1, "Failed to create space 'V12': parameters are not allowed in views"
         -- </view-12.2>
     })
 
diff --git a/test/sql-tap/where7.test.lua b/test/sql-tap/where7.test.lua
index 2e6f116..ecd0d24 100755
--- a/test/sql-tap/where7.test.lua
+++ b/test/sql-tap/where7.test.lua
@@ -325,7 +325,7 @@ test:do_test(
         end
         return test:catchsql(sql)
     end, {
-        1, "Expression tree is too large (maximum depth 200)"
+        1, "Number of nodes in expression tree 201 exceeds the limit (200)"
     })
 
 test:do_test(
diff --git a/test/sql-tap/whereG.test.lua b/test/sql-tap/whereG.test.lua
index 155c906..39424d4 100755
--- a/test/sql-tap/whereG.test.lua
+++ b/test/sql-tap/whereG.test.lua
@@ -193,7 +193,7 @@ test:do_catchsql_test(
            AND album.aid=track.aid;
     ]], {
         -- <whereG-2.1>
-        1, "second argument to likelihood() must be a constant between 0.0 and 1.0"
+        1, "Illegal parameters, second argument to likelihood() must be a constant between 0.0 and 1.0"
         -- </whereG-2.1>
     })
 
@@ -207,7 +207,7 @@ test:do_catchsql_test(
            AND album.aid=track.aid;
     ]], {
         -- <whereG-2.2>
-        1, "second argument to likelihood() must be a constant between 0.0 and 1.0"
+        1, "Illegal parameters, second argument to likelihood() must be a constant between 0.0 and 1.0"
         -- </whereG-2.2>
     })
 
@@ -221,7 +221,7 @@ test:do_catchsql_test(
            AND album.aid=track.aid;
     ]], {
         -- <whereG-2.3>
-        1, "second argument to likelihood() must be a constant between 0.0 and 1.0"
+        1, "Illegal parameters, second argument to likelihood() must be a constant between 0.0 and 1.0"
         -- </whereG-2.3>
     })
 
diff --git a/test/sql/delete.result b/test/sql/delete.result
index e024dd6..29459cc 100644
--- a/test/sql/delete.result
+++ b/test/sql/delete.result
@@ -93,7 +93,7 @@ box.sql.execute("CREATE VIEW v1 AS SELECT * FROM t1;")
 ...
 box.sql.execute("TRUNCATE TABLE v1;")
 ---
-- error: 'SQL error: can not truncate space ''V1'' because it is a view'
+- error: 'SQL error: can not truncate space ''V1'' because space is a view'
 ...
 -- Can't truncate table with FK.
 box.sql.execute("CREATE TABLE t2(x INT PRIMARY KEY REFERENCES t1(id));")
diff --git a/test/sql/gh-2347-max-int-literals.result b/test/sql/gh-2347-max-int-literals.result
index c289a80..b511440 100644
--- a/test/sql/gh-2347-max-int-literals.result
+++ b/test/sql/gh-2347-max-int-literals.result
@@ -20,9 +20,11 @@ box.sql.execute("select (-9223372036854775808)")
 ...
 box.sql.execute("select (9223372036854775808)")
 ---
-- error: 'oversized integer: 9223372036854775808'
+- error: Integer literal 9223372036854775808 exceeds the supported range -9223372036854775808
+    - 9223372036854775807
 ...
 box.sql.execute("select (-9223372036854775809)")
 ---
-- error: 'oversized integer: -9223372036854775809'
+- error: Integer literal -9223372036854775809 exceeds the supported range -9223372036854775808
+    - 9223372036854775807
 ...
diff --git a/test/sql/gh-2929-primary-key.result b/test/sql/gh-2929-primary-key.result
index 4052665..e36f4e4 100644
--- a/test/sql/gh-2929-primary-key.result
+++ b/test/sql/gh-2929-primary-key.result
@@ -18,19 +18,19 @@ box.sql.execute("CREATE TABLE t1(a INT PRIMARY KEY, b INT UNIQUE)")
 ...
 box.sql.execute("CREATE TABLE t2(a INT UNIQUE, b INT)")
 ---
-- error: PRIMARY KEY missing on table T2
+- error: 'Failed to create space ''T2'': PRIMARY KEY missing'
 ...
 box.sql.execute("CREATE TABLE t3(a FLOAT)")
 ---
-- error: PRIMARY KEY missing on table T3
+- error: 'Failed to create space ''T3'': PRIMARY KEY missing'
 ...
 box.sql.execute("CREATE TABLE t4(a FLOAT, b TEXT)")
 ---
-- error: PRIMARY KEY missing on table T4
+- error: 'Failed to create space ''T4'': PRIMARY KEY missing'
 ...
 box.sql.execute("CREATE TABLE t5(a FLOAT, b FLOAT UNIQUE)")
 ---
-- error: PRIMARY KEY missing on table T5
+- error: 'Failed to create space ''T5'': PRIMARY KEY missing'
 ...
 box.sql.execute("DROP TABLE t1")
 ---
diff --git a/test/sql/integer-overflow.result b/test/sql/integer-overflow.result
index 4754c04..09e864e 100644
--- a/test/sql/integer-overflow.result
+++ b/test/sql/integer-overflow.result
@@ -30,15 +30,18 @@ box.sql.execute('SELECT (9223372036854775807 + 1);')
 --
 box.sql.execute('SELECT 9223372036854775808;')
 ---
-- error: 'oversized integer: 9223372036854775808'
+- error: Integer literal 9223372036854775808 exceeds the supported range -9223372036854775808
+    - 9223372036854775807
 ...
 box.sql.execute('SELECT -9223372036854775809;')
 ---
-- error: 'oversized integer: -9223372036854775809'
+- error: Integer literal -9223372036854775809 exceeds the supported range -9223372036854775808
+    - 9223372036854775807
 ...
 box.sql.execute('SELECT 9223372036854775808 - 1;')
 ---
-- error: 'oversized integer: 9223372036854775808'
+- error: Integer literal 9223372036854775808 exceeds the supported range -9223372036854775808
+    - 9223372036854775807
 ...
 -- Test that CAST may also leads to overflow.
 --
diff --git a/test/sql/iproto.result b/test/sql/iproto.result
index 938aea9..56099fa 100644
--- a/test/sql/iproto.result
+++ b/test/sql/iproto.result
@@ -363,7 +363,7 @@ sql = 'select '..string.rep('?, ', box.schema.SQL_BIND_PARAMETER_MAX)..'?'
 ...
 cn:execute(sql)
 ---
-- error: 'Failed to execute SQL statement: too many SQL variables'
+- error: 'Failed to execute SQL statement: SQL bind parameter limit reached: 65000'
 ...
 -- Try too many parameter values.
 sql = 'select ?'
@@ -571,8 +571,7 @@ cn:execute('select ?1, ?2, ?3', {1, 2, 3})
 ...
 cn:execute('select $name, $name2', {1, 2})
 ---
-- error: 'Failed to execute SQL statement: variable number must be between $1 and
-    $65000'
+- error: 'Failed to execute SQL statement: SQL bind parameter limit reached: 65000'
 ...
 parameters = {}
 ---
diff --git a/test/sql/transition.result b/test/sql/transition.result
index ea110c0..89035e5 100644
--- a/test/sql/transition.result
+++ b/test/sql/transition.result
@@ -180,7 +180,8 @@ box.sql.execute("DROP TABLE barfoo")
 -- attempt to create a table lacking PRIMARY KEY
 box.sql.execute("CREATE TABLE without_rowid_lacking_primary_key(x SCALAR)")
 ---
-- error: PRIMARY KEY missing on table WITHOUT_ROWID_LACKING_PRIMARY_KEY
+- error: 'Failed to create space ''WITHOUT_ROWID_LACKING_PRIMARY_KEY'': PRIMARY KEY
+    missing'
 ...
 -- create a table with implicit indices (used to SEGFAULT)
 box.sql.execute("CREATE TABLE implicit_indices(a INT PRIMARY KEY,b INT,c INT,d TEXT UNIQUE)")
diff --git a/test/sql/view.result b/test/sql/view.result
index e99a9bd..32967b3 100644
--- a/test/sql/view.result
+++ b/test/sql/view.result
@@ -19,7 +19,7 @@ box.sql.execute("CREATE VIEW v1 AS SELECT a+b FROM t1;");
 -- View can't have any indexes.
 box.sql.execute("CREATE INDEX i1 on v1(a);");
 ---
-- error: views can not be indexed
+- error: 'Can''t create or modify index ''I1'' in space ''V1'': views can not be indexed'
 ...
 v1 = box.space.V1;
 ---

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 8/8] sql: remove sqlErrorMsg()
  2019-03-15 13:36   ` [tarantool-patches] " n.pettik
@ 2019-03-25 18:47     ` Mergen Imeev
  2019-03-26 13:34       ` n.pettik
  0 siblings, 1 reply; 43+ messages in thread
From: Mergen Imeev @ 2019-03-25 18:47 UTC (permalink / raw)
  To: n.pettik; +Cc: tarantool-patches

Hi! Thank you for review! My answers, diff between versions and
new version below.

On Fri, Mar 15, 2019 at 04:36:46PM +0300, n.pettik wrote:
> 
> >>> +    parse->is_aborted = true;
> >>>   goto exit_create_index;
> >>> }
> >>> /*
> >>> @@ -2947,11 +2976,8 @@ sqlSavepoint(Parse * pParse, int op, Token * pName)
> >>>     return;
> >>>   }
> >>>   if (op == SAVEPOINT_BEGIN &&
> >>> -      sqlCheckIdentifierName(pParse, zName)
> >>> -        != SQL_OK) {
> >>> -      sqlErrorMsg(pParse, "bad savepoint name");
> >>> +      sqlCheckIdentifierName(pParse, zName) != SQL_OK)
> >> 
> >> != 0
> > Function sqlCheckIdentifierName() still works in terms of
> > {SQL_OK, SQL_ERROR}, so I left this part as it was.
> 
> Under the hood SQL_OK is 0. We are going to remove
> this status codes, so let's fix it now.
> 
Fixed in previous patch.

> >>> diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
> >>> index 85718e1..5a74c65 100644
> >>> --- a/src/box/sql/sqlInt.h
> >>> +++ b/src/box/sql/sqlInt.h
> >>> @@ -3182,7 +3182,6 @@ void sqlTreeViewWith(TreeView *, const With *);
> >>> #endif
> >>> 
> >>> void sqlSetString(char **, sql *, const char *);
> >>> -void sqlErrorMsg(Parse *, const char *, ...);
> >>> void sqlDequote(char *);
> >>> void sqlNormalizeName(char *z);
> >>> void sqlTokenInit(Token *, char *);
> >>> diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
> >>> index 2f1268a..297ba01 100644
> >>> --- a/src/box/sql/trigger.c
> >>> +++ b/src/box/sql/trigger.c
> >>> @@ -621,8 +621,11 @@ codeTriggerProgram(Parse * pParse,  /* The parser context */
> >>> sqlSubProgramsRemaining--;
> >>> 
> >>> if (sqlSubProgramsRemaining == 0) {
> >>> -    sqlErrorMsg(pParse,
> >>> -        "Maximum number of chained trigger activations exceeded.");
> >>> +    const char *err_msg = tt_sprintf("Maximum number of chained "\
> >>> +             "trigger activations "\
> >>> +             "exceeded.”);
> >> 
> >> Please, make sure that it is valid error. And the rest of errors
> >> I pointed in google doc file. If this is unreachable code, then
> >> replace error with assert.
Fixed. Replaced errors that I wasn't able to reproduce by assert.
Some of them were replaced in previous patch.

> > This error is valid. As for the other ones - I couldn't reach some
> > of them, but didn't remove them for now.
> 
> Why? Comment code and place assertion instead.
> Otherwise, provide solid argument to avoid doing that.
> 
Fixed.

> >>> -/*
> >>> * Convert an SQL-style quoted string into a normal string by removing
> >>> * the quote characters.  The conversion is done in-place.  If the
> >>> * input does not begin with a quote character, then this routine
> >>> diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
> >>> index 9957f3a..d4fbf17 100644
> >>> --- a/src/box/sql/vdbemem.c
> >>> +++ b/src/box/sql/vdbemem.c
> >>> @@ -1222,7 +1222,9 @@ valueFromFunction(sql * db,  /* The database connection */
> >>> pFunc->xSFunc(&ctx, nVal, apVal);
> >>> if (ctx.isError) {
> >>>   rc = ctx.isError;
> >>> -    sqlErrorMsg(pCtx->pParse, "%s", sql_value_text(pVal));
> >>> +    diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> >>> +       sql_value_text(pVal));
> >> 
> >> What kind of error is it? Please, add reasonable error message,
> >> not only text representation of value.
Replaced by assert.

> > I did, but not sure if this error is reachable. I wasn't able to
> > reproduce it.
> 
> See comment above.
> 
Fixed.

> >>> +    pCtx->pParse->is_aborted = true;
> >>> } else {
> >>>   sql_value_apply_type(pVal, type);
> >>>   assert(rc == SQL_OK);
> >>> diff --git a/src/box/sql/where.c b/src/box/sql/where.c
> >>> index 33885d0..e2c91e0 100644
> >>> --- a/src/box/sql/where.c
> >>> +++ b/src/box/sql/where.c
> >>> @@ -3914,7 +3914,9 @@ wherePathSolver(WhereInfo * pWInfo, LogEst nRowEst)
> >>> }
> >>> 
> >>> if (nFrom == 0) {
> >>> -    sqlErrorMsg(pParse, "no query solution");
> >>> +    const char *err_msg = tt_sprintf("no query solution”);
> >> 
> >> Same: this path seems to be unreachable.
> > I think so too, but left as it is for now.
> 
> The same.
> 
Fixed.

> > New version:
> > 
> > commit 99f4adc61352899a83a7097b965845bafaa968ef
> > Author: Mergen Imeev <imeevma@gmail.com>
> > Date:   Sat Mar 9 18:54:19 2019 +0300
> > 
> >    sql: remove sqlErrorMsg()
> > 
> >    This patch completely replaces sqlErrorMsg() with diag_set() and
> >    removes sqlErrorMsg().
> > 
> >    Closes #3965
> >    Closes #3036
> 
> I wouldn’t close both issues since there’s still place to work on:
> split generic error into specific ones, use diag for vdbe.
> 
I created new issue for VDBE errors:
https://github.com/tarantool/tarantool/issues/4074

I will write about replacing GENERIC errcode by specific one
when #3036 and #3965 will be closed.

I am going to close them after this patch pass through review.
Futher work will be done under new issues.

> > diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> > index de06ee0..7205cb0 100644
> > --- a/src/box/sql/expr.c
> > +++ b/src/box/sql/expr.c
> > @@ -668,7 +668,9 @@ codeVectorCompare(Parse * pParse, /* Code generator context */
> >  int addrDone = sqlVdbeMakeLabel(v);
> > 
> >  if (nLeft != sqlExprVectorSize(pRight)) {
> > -   sqlErrorMsg(pParse, "row value misused");
> > +   diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> > +      "row value misused”);
> 
> I see a lot of “row value misused” errors in source code,
> but only one test case on this error.
> 
I replaced three of them by assert. For remaining three I created
tests.

> > @@ -2959,15 +2935,26 @@ int
> > sqlExprCheckIN(Parse * pParse, Expr * pIn)
> > {
> >  int nVector = sqlExprVectorSize(pIn->pLeft);
> > + const char *err;
> >  if ((pIn->flags & EP_xIsSelect)) {
> >    if (nVector != pIn->x.pSelect->pEList->nExpr) {
> > -     sqlSubselectError(pParse,
> > -               pIn->x.pSelect->pEList->nExpr,
> > -               nVector);
> > +     err = "sub-select returns %d columns - expected %d";
> > +     int expr_count = pIn->x.pSelect->pEList->nExpr;
> > +     err = tt_sprintf(err, expr_count, nVector);
> > +     diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
> > +     pParse->is_aborted = true;
> >      return 1;
> >    }
> >  } else if (nVector != 1) {
> > -   sqlVectorErrorMsg(pParse, pIn->pLeft);
> > +   if (pIn->pLeft->flags & EP_xIsSelect) {
> > +     err = "sub-select returns %d columns - expected 1";
> > +     int expr_count = pIn->pLeft->x.pSelect->pEList->nExpr;
> > +     err = tt_sprintf(err, expr_count);
> > +   } else {
> > +     err = "row value misused”;
> 
> A bit shorter version. Please apply this and other diffs:
> 
Fixed.

> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index 7205cb0a8..344e1ab9a 100644
> --- a/src/box/sql/expr.c
> +++ b/src/box/sql/expr.c
> @@ -2940,16 +2940,16 @@ sqlExprCheckIN(Parse * pParse, Expr * pIn)
>                 if (nVector != pIn->x.pSelect->pEList->nExpr) {
>                         err = "sub-select returns %d columns - expected %d";
>                         int expr_count = pIn->x.pSelect->pEList->nExpr;
> -                       err = tt_sprintf(err, expr_count, nVector);
> -                       diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
> +                       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> +                                tt_sprintf(err, expr_count, nVector););
>                         pParse->is_aborted = true;
>                         return 1;
>                 }
>         } else if (nVector != 1) {
>                 if (pIn->pLeft->flags & EP_xIsSelect) {
> -                       err = "sub-select returns %d columns - expected 1";
>                         int expr_count = pIn->pLeft->x.pSelect->pEList->nExpr;
> -                       err = tt_sprintf(err, expr_count);
> +                       err = tt_sprintf("sub-select returns %d columns - "\
> +                                        "expected 1", expr_count);
> 
> > +   }
> > +   diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
> > +   pParse->is_aborted = true;
> >    return 1;
> >  }
> >  return 0;
> > @@ -4134,7 +4122,12 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
> >      testcase(op == TK_SELECT);
> >      if (op == TK_SELECT
> >          && (nCol = pExpr->x.pSelect->pEList->nExpr) != 1) {
> > -       sqlSubselectError(pParse, nCol, 1);
> > +       const char *err = "sub-select returns %d "\
> > +             "columns - expected 1";
> > +       err = tt_sprintf(err, nCol);
> 
> @@ -4124,9 +4124,8 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
>                             && (nCol = pExpr->x.pSelect->pEList->nExpr) != 1) {
>                                 const char *err = "sub-select returns %d "\
>                                                   "columns - expected 1";
> -                               err = tt_sprintf(err, nCol);
>                                 diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> -                                        err);
> +                                        tt_sprintf(err, nCol););
> 
> > @@ -510,14 +511,19 @@ sqlInsert(Parse * pParse, /* Parser context */
> > 
> >  if (pColumn != 0 && nColumn != pColumn->nId) {
> > -   sqlErrorMsg(pParse, "%d values for %d columns", nColumn,
> > -       pColumn->nId);
> > +   const char *err = "%d values for %d columns";
> > +   err = tt_sprintf(err, nColumn, pColumn->nId);
> > +   diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
> 
> diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
> index 7635f7b69..6f30ab37e 100644
> --- a/src/box/sql/insert.c
> +++ b/src/box/sql/insert.c
> @@ -521,8 +521,8 @@ sqlInsert(Parse * pParse,   /* Parser context */
>         }
>         if (pColumn != 0 && nColumn != pColumn->nId) {
>                 const char *err = "%d values for %d columns";
> -               err = tt_sprintf(err, nColumn, pColumn->nId);
> -               diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
> +               diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> +                        tt_sprintf(err, nColumn, pColumn->nId));
>                 pParse->is_aborted = true;
>                 goto insert_cleanup;
> 
> > +   pParse->is_aborted = true;
> >    goto insert_cleanup;
> >  }
> > 
> > 
> > // Disallow the INDEX BY and NOT INDEXED clauses on UPDATE and DELETE
> > diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
> > index 9171d05..6720914 100644
> > --- a/src/box/sql/resolve.c
> > +++ b/src/box/sql/resolve.c
> > @@ -390,16 +390,22 @@ lookupName(Parse * pParse,  /* The parsing context */
> >          assert(pExpr->x.pList == 0);
> >          assert(pExpr->x.pSelect == 0);
> >          pOrig = pEList->a[j].pExpr;
> > +         const char *err = "misuse of aliased "\
> > +               "aggregate %s";
> >          if ((pNC->ncFlags & NC_AllowAgg) == 0
> >              && ExprHasProperty(pOrig, EP_Agg)) {
> > -           sqlErrorMsg(pParse,
> > -               "misuse of aliased aggregate %s",
> > -               zAs);
> > +           err = tt_sprintf(err, zAs);
> > +           diag_set(ClientError,
> > +              ER_SQL_PARSER_GENERIC,
> > +              err);
> 
> diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
> index 672091465..30f2b1f5a 100644
> --- a/src/box/sql/resolve.c
> +++ b/src/box/sql/resolve.c
> @@ -394,10 +394,9 @@ lookupName(Parse * pParse, /* The parsing context */
>                                                           "aggregate %s";
>                                         if ((pNC->ncFlags & NC_AllowAgg) == 0
>                                             && ExprHasProperty(pOrig, EP_Agg)) {
> -                                               err = tt_sprintf(err, zAs);
>                                                 diag_set(ClientError,
>                                                          ER_SQL_PARSER_GENERIC,
> -                                                        err);
> +                                                        tt_sprintf(err, zAs));
>                                                 pParse->is_aborted = true;
> 
> > @@ -697,9 +708,12 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
> >        pParse->is_aborted = true;
> >        pNC->nErr++;
> >      } else if (wrong_num_args) {
> > -       sqlErrorMsg(pParse,
> > -           "wrong number of arguments to function %.*s()",
> > -           nId, zId);
> > +       const char *err = "wrong number of arguments "\
> > +             "to function %.*s()";
> > +       err = tt_sprintf(err, nId, zId);
> > +       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> > +          err);
> 
> @@ -710,9 +709,8 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
>                         } else if (wrong_num_args) {
>                                 const char *err = "wrong number of arguments "\
>                                                   "to function %.*s()";
> -                               err = tt_sprintf(err, nId, zId);
>                                 diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> -                                        err);
> +                                        tt_sprintf(err, nId, zId));
>                                 pParse->is_aborted = true;
>                                 pNC->nErr++;
> 
> > @@ -980,9 +981,16 @@ resolveCompoundOrderBy(Parse * pParse, /* Parsing context.  Leave error messages
> >      pE = sqlExprSkipCollate(pItem->pExpr);
> >      if (sqlExprIsInteger(pE, &iCol)) {
> >        if (iCol <= 0 || iCol > pEList->nExpr) {
> > -         resolveOutOfRangeError(pParse, "ORDER",
> > -                    i + 1,
> > -                    pEList->nExpr);
> > +         const char *err =
> > +           "Error at ORDER BY in place "\
> > +           "%d: term out of range - "\
> > +           "should be between 1 and %d";
> > +         err = tt_sprintf(err, i + 1,
> > +              pEList->nExpr);
> > +         diag_set(ClientError,
> > +            ER_SQL_PARSER_GENERIC,
> > +            err);
> 
> @@ -988,8 +986,7 @@ resolveCompoundOrderBy(Parse * pParse,      /* Parsing context.  Leave error messages
>                                         err = tt_sprintf(err, i + 1,
>                                                          pEList->nExpr);
>                                         diag_set(ClientError,
> -                                                ER_SQL_PARSER_GENERIC,
> -                                                err);
> +                                                ER_SQL_PARSER_GENERIC, err);
>                                         pParse->is_aborted = true;
>                                         return 1;
> 
> > @@ -1028,9 +1036,12 @@ resolveCompoundOrderBy(Parse * pParse, /* Parsing context.  Leave error messages
> >  }
> >  for (i = 0; i < pOrderBy->nExpr; i++) {
> >    if (pOrderBy->a[i].done == 0) {
> > -     sqlErrorMsg(pParse,
> > -         "%r ORDER BY term does not match any "
> > -         "column in the result set", i + 1);
> > +     const char *err = "Error at ORDER BY in place %d: "\
> > +           "term does not match any column in "\
> > +           "the result set";
> > +     err = tt_sprintf(err, i + 1);
> > +     diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
> 
> @@ -1039,8 +1036,8 @@ resolveCompoundOrderBy(Parse * pParse,    /* Parsing context.  Leave error messages
>                         const char *err = "Error at ORDER BY in place %d: "\
>                                           "term does not match any column in "\
>                                           "the result set";
> -                       err = tt_sprintf(err, i + 1);
> -                       diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
> +                       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> +                                tt_sprintf(err, i + 1));
> 
> > @@ -1444,10 +1469,37 @@ resolveSelectStep(Walker * pWalker, Select * p)
> >     * number of expressions in the select list.
> >     */
> >    if (p->pNext && p->pEList->nExpr != p->pNext->pEList->nExpr) {
> > -     sqlSelectWrongNumTermsError(pParse, p->pNext);
> > +     if (p->pNext->selFlags & SF_Values) {
> > +       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> > +          "all VALUES must have the same "\
> > +          "number of terms");
> > +     } else {
> > +       const char *err_msg =
> > +         "SELECTs to the left and right of %s "\
> > +         "do not have the same number of "\
> > +         "result columns";
> > +       switch (p->pNext->op) {
> > +       case TK_ALL:
> > +         err_msg = tt_sprintf(err_msg,
> > +                  "UNION ALL");
> > +         break;
> 
> Why don’t use selectOpName?
> Like it was in  sqlSelectWrongNumTermsError().
> 
Function selectOpName defined in different file and is static.
I think it isn't worth to share it for one error.

> > +       case TK_INTERSECT:
> > +         err_msg = tt_sprintf(err_msg,
> > +                  "INTERSECT");
> > +         break;
> > +       case TK_EXCEPT:
> > +         err_msg = tt_sprintf(err_msg, "EXCEPT");
> > +         break;
> > +       default:
> > +         err_msg = tt_sprintf(err_msg, "UNION");
> > +         break;
> > +       }
> > +       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> > +          err_msg);
> > +     }
> > +     pParse->is_aborted = true;
> >      return WRC_Abort;
> > 
> > diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
> > index f417c49..9f0dee4 100644
> > --- a/src/box/sql/vdbemem.c
> > +++ b/src/box/sql/vdbemem.c
> > @@ -1210,13 +1210,14 @@ valueFromFunction(sql * db, /* The database connection */
> >  pFunc->xSFunc(&ctx, nVal, apVal);
> >  if (ctx.isError) {
> >    rc = ctx.isError;
> > -   sqlErrorMsg(pCtx->pParse, "%s", sql_value_text(pVal));
> > +   const char *err = "Error in function '%s': %s”;
> 
> It doesn’t look better than previous variant. If you don’t
> know what kind of error this is and how it can appear -
> just replace it with assertion.
> 
Replaced bu assert.

> > +   err = tt_sprintf(err, pFunc->zName, sql_value_text(pVal));
> > +   diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
> > +   pCtx->pParse->is_aborted = true;
> >  } else {
> >    sql_value_apply_type(pVal, type);
> >    assert(rc == SQL_OK);
> >  }
> > - if (rc != SQL_OK)
> > -   pCtx->pParse->is_aborted = true;
> > 
> >  value_from_function_out:
> >  if (rc != SQL_OK) {
> > diff --git a/src/box/sql/where.c b/src/box/sql/where.c
> > index 33885d0..1e8c2e0 100644
> > --- a/src/box/sql/where.c
> > +++ b/src/box/sql/where.c
> > @@ -3914,7 +3914,9 @@ wherePathSolver(WhereInfo * pWInfo, LogEst nRowEst)
> >  }
> > 
> >  if (nFrom == 0) {
> > -   sqlErrorMsg(pParse, "no query solution");
> > +   diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> > +      "no query solution”);
> 
> As I already said in previous review - replace with assertion
> and comment.
> 
Fixed.

> > +   pParse->is_aborted = true;
> >    sqlDbFree(db, pSpace);
> >    return SQL_ERROR;
> >  }
> 


Diff between versions:

diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index e26596a..1077285 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -666,13 +666,7 @@ codeVectorCompare(Parse * pParse,	/* Code generator context */
 	int regRight = 0;
 	u8 op = pExpr->op;
 	int addrDone = sqlVdbeMakeLabel(v);
-
-	if (nLeft != sqlExprVectorSize(pRight)) {
-		diag_set(ClientError, ER_SQL_PARSER_GENERIC,
-			 "row value misused");
-		pParse->is_aborted = true;
-		return;
-	}
+	assert(nLeft == sqlExprVectorSize(pRight));
 	assert(pExpr->op == TK_EQ || pExpr->op == TK_NE
 	       || pExpr->op == TK_LT || pExpr->op == TK_GT
 	       || pExpr->op == TK_LE || pExpr->op == TK_GE);
@@ -2965,19 +2959,16 @@ sqlExprCheckIN(Parse * pParse, Expr * pIn)
 		if (nVector != pIn->x.pSelect->pEList->nExpr) {
 			err = "sub-select returns %d columns - expected %d";
 			int expr_count = pIn->x.pSelect->pEList->nExpr;
-			err = tt_sprintf(err, expr_count, nVector);
-			diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+				 tt_sprintf(err, expr_count, nVector));
 			pParse->is_aborted = true;
 			return 1;
 		}
 	} else if (nVector != 1) {
-		if (pIn->pLeft->flags & EP_xIsSelect) {
-			err = "sub-select returns %d columns - expected 1";
-			int expr_count = pIn->pLeft->x.pSelect->pEList->nExpr;
-			err = tt_sprintf(err, expr_count);
-		} else {
-			err = "row value misused";
-		}
+		assert((pIn->pLeft->flags & EP_xIsSelect) != 0);
+		int expr_count = pIn->pLeft->x.pSelect->pEList->nExpr;
+		err = tt_sprintf("sub-select returns %d columns - expected 1",
+				 expr_count);
 		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
 		pParse->is_aborted = true;
 		return 1;
@@ -4149,9 +4140,8 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 			    && (nCol = pExpr->x.pSelect->pEList->nExpr) != 1) {
 				const char *err = "sub-select returns %d "\
 						  "columns - expected 1";
-				err = tt_sprintf(err, nCol);
 				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
-					 err);
+					 tt_sprintf(err, nCol));
 				pParse->is_aborted = true;
 			} else {
 				return sqlCodeSubselect(pParse, pExpr, 0);
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index b89acdc..7e98f42 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -521,8 +521,8 @@ sqlInsert(Parse * pParse,	/* Parser context */
 	}
 	if (pColumn != 0 && nColumn != pColumn->nId) {
 		const char *err = "%d values for %d columns";
-		err = tt_sprintf(err, nColumn, pColumn->nId);
-		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+			 tt_sprintf(err, nColumn, pColumn->nId));
 		pParse->is_aborted = true;
 		goto insert_cleanup;
 	}
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 208ebd0..ef40927 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -394,10 +394,9 @@ lookupName(Parse * pParse,	/* The parsing context */
 							  "aggregate %s";
 					if ((pNC->ncFlags & NC_AllowAgg) == 0
 					    && ExprHasProperty(pOrig, EP_Agg)) {
-						err = tt_sprintf(err, zAs);
 						diag_set(ClientError,
 							 ER_SQL_PARSER_GENERIC,
-							 err);
+							 tt_sprintf(err, zAs));
 						pParse->is_aborted = true;
 						return WRC_Abort;
 					}
@@ -706,9 +705,8 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 			} else if (wrong_num_args) {
 				const char *err = "wrong number of arguments "\
 						  "to function %.*s()";
-				err = tt_sprintf(err, nId, zId);
 				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
-					 err);
+					 tt_sprintf(err, nId, zId));
 				pParse->is_aborted = true;
 				pNC->nErr++;
 			}
@@ -981,8 +979,7 @@ resolveCompoundOrderBy(Parse * pParse,	/* Parsing context.  Leave error messages
 					err = tt_sprintf(err, i + 1,
 							 pEList->nExpr);
 					diag_set(ClientError,
-						 ER_SQL_PARSER_GENERIC,
-						 err);
+						 ER_SQL_PARSER_GENERIC, err);
 					pParse->is_aborted = true;
 					return 1;
 				}
@@ -1032,8 +1029,8 @@ resolveCompoundOrderBy(Parse * pParse,	/* Parsing context.  Leave error messages
 			const char *err = "Error at ORDER BY in place %d: "\
 					  "term does not match any column in "\
 					  "the result set";
-			err = tt_sprintf(err, i + 1);
-			diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+				 tt_sprintf(err, i + 1));
 			pParse->is_aborted = true;
 			return 1;
 		}
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 1e9b7d7..1b85074 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -3624,18 +3624,13 @@ substExpr(Parse * pParse,	/* Report errors here */
 			assert(pEList != 0 && pExpr->iColumn < pEList->nExpr);
 			assert(pExpr->pLeft == 0 && pExpr->pRight == 0);
 			if (sqlExprIsVector(pCopy)) {
-				const char *err;
-				if (pCopy->flags & EP_xIsSelect) {
-					err = "sub-select returns %d columns "\
-					      "- expected 1";
-					int expr_count =
-						pCopy->x.pSelect->pEList->nExpr;
-					err = tt_sprintf(err, expr_count);
-				} else {
-					err = "row value misused";
-				}
+				assert((pCopy->flags & EP_xIsSelect) != 0);
+				const char *err = "sub-select returns %d "\
+						  "columns - expected 1";
+				int expr_count =
+					pCopy->x.pSelect->pEList->nExpr;
 				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
-					 err);
+					 tt_sprintf(err, expr_count));
 				pParse->is_aborted = true;
 			} else {
 				pNew = sqlExprDup(db, pCopy, 0);
diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
index 9f0dee4..15a2f55 100644
--- a/src/box/sql/vdbemem.c
+++ b/src/box/sql/vdbemem.c
@@ -1208,16 +1208,9 @@ valueFromFunction(sql * db,	/* The database connection */
 	ctx.pOut = pVal;
 	ctx.pFunc = pFunc;
 	pFunc->xSFunc(&ctx, nVal, apVal);
-	if (ctx.isError) {
-		rc = ctx.isError;
-		const char *err = "Error in function '%s': %s";
-		err = tt_sprintf(err, pFunc->zName, sql_value_text(pVal));
-		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
-		pCtx->pParse->is_aborted = true;
-	} else {
-		sql_value_apply_type(pVal, type);
-		assert(rc == SQL_OK);
-	}
+	assert(!ctx.isError);
+	sql_value_apply_type(pVal, type);
+	assert(rc == SQL_OK);
 
  value_from_function_out:
 	if (rc != SQL_OK) {
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index 8ba9039..c71a170 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -3913,13 +3913,7 @@ wherePathSolver(WhereInfo * pWInfo, LogEst nRowEst)
 		nFrom = nTo;
 	}
 
-	if (nFrom == 0) {
-		diag_set(ClientError, ER_SQL_PARSER_GENERIC,
-			 "no query solution");
-		pParse->is_aborted = true;
-		sqlDbFree(db, pSpace);
-		return SQL_ERROR;
-	}
+	assert(nFrom != 0);
 
 	/* Find the lowest cost path.  pFrom will be left pointing to that path */
 	pFrom = aFrom;
@@ -4384,10 +4378,8 @@ sqlWhereBegin(Parse * pParse,	/* The parser context */
 	 * equal to pTabList->nSrc but might be shortened to 1 if the
 	 * WHERE_OR_SUBCLAUSE flag is set.
 	 */
-	for (ii = 0; ii < pTabList->nSrc; ii++) {
+	for (ii = 0; ii < pTabList->nSrc; ii++)
 		createMask(pMaskSet, pTabList->a[ii].iCursor);
-		sqlWhereTabFuncArgs(pParse, &pTabList->a[ii], &pWInfo->sWC);
-	}
 #ifdef SQL_DEBUG
 	for (ii = 0; ii < pTabList->nSrc; ii++) {
 		Bitmask m =
diff --git a/src/box/sql/whereInt.h b/src/box/sql/whereInt.h
index 47430ae..9cf1eb8 100644
--- a/src/box/sql/whereInt.h
+++ b/src/box/sql/whereInt.h
@@ -479,7 +479,6 @@ void sqlWhereSplit(WhereClause *, Expr *, u8);
 Bitmask sqlWhereExprUsage(WhereMaskSet *, Expr *);
 Bitmask sqlWhereExprListUsage(WhereMaskSet *, ExprList *);
 void sqlWhereExprAnalyze(SrcList *, WhereClause *);
-void sqlWhereTabFuncArgs(Parse *, struct SrcList_item *, WhereClause *);
 
 /*
  * Bitmasks for the operators on WhereTerm objects.  These are all
diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c
index bc0964c..9d01a7e 100644
--- a/src/box/sql/whereexpr.c
+++ b/src/box/sql/whereexpr.c
@@ -1472,47 +1472,3 @@ sqlWhereExprAnalyze(SrcList * pTabList,	/* the FROM clause */
 		exprAnalyze(pTabList, pWC, i);
 	}
 }
-
-/*
- * For table-valued-functions, transform the function arguments into
- * new WHERE clause terms.
- */
-void
-sqlWhereTabFuncArgs(Parse * pParse,	/* Parsing context */
-			struct SrcList_item *pItem,	/* The FROM clause term to process */
-			WhereClause * pWC	/* Xfer function arguments to here */
-    )
-{
-	int j, k;
-	ExprList *pArgs;
-	Expr *pColRef;
-	Expr *pTerm;
-	if (pItem->fg.isTabFunc == 0)
-		return;
-	struct space_def *space_def = pItem->space->def;
-	pArgs = pItem->u1.pFuncArg;
-	if (pArgs == 0)
-		return;
-	for (j = k = 0; j < pArgs->nExpr; j++) {
-		while (k < (int)space_def->field_count)
-			k++;
-		if (k >= (int)space_def->field_count) {
-			const char *err =
-				tt_sprintf("too many arguments on %s() - max "\
-					   "%d", space_def->name, j);
-			diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
-			pParse->is_aborted = true;
-			return;
-		}
-		pColRef = sqlExprAlloc(pParse->db, TK_COLUMN, 0, 0);
-		if (pColRef == 0)
-			return;
-		pColRef->iTable = pItem->iCursor;
-		pColRef->iColumn = k++;
-		pColRef->space_def = space_def;
-		pTerm = sqlPExpr(pParse, TK_EQ, pColRef,
-				     sqlExprDup(pParse->db,
-						    pArgs->a[j].pExpr, 0));
-		whereClauseInsert(pWC, pTerm, TERM_DYNAMIC);
-	}
-}
diff --git a/test/sql-tap/sql-errors.test.lua b/test/sql-tap/sql-errors.test.lua
index 3f1dd14..b43400e 100755
--- a/test/sql-tap/sql-errors.test.lua
+++ b/test/sql-tap/sql-errors.test.lua
@@ -1,6 +1,6 @@
 #!/usr/bin/env tarantool
 test = require("sqltester")
-test:plan(40)
+test:plan(43)
 
 test:execsql([[
 	CREATE TABLE t0 (i INT PRIMARY KEY, a INT);
@@ -456,4 +456,34 @@ test:do_catchsql_test(
 		-- </sql-errors-1.40>
 	})
 
+test:do_catchsql_test(
+	"sql-errors-1.41",
+	[[
+		SELECT (1,2,3) == (1,2,3,4);
+	]], {
+		-- <sql-errors-1.41>
+		1,"row value misused"
+		-- </sql-errors-1.41>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.42",
+	[[
+		SELECT (1, 2);
+	]], {
+		-- <sql-errors-1.42>
+		1,"row value misused"
+		-- </sql-errors-1.42>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.43",
+	[[
+		SELECT (i,a) AS m FROM t0 WHERE m < 1;
+	]], {
+		-- <sql-errors-1.43>
+		1,"row value misused"
+		-- </sql-errors-1.43>
+	})
+
 test:finish_test()


New version:

commit d81875339f3312bfb4de1e516ecb9cf6de77f7fc
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Sat Mar 9 18:54:19 2019 +0300

    sql: remove sqlErrorMsg()
    
    This patch completely replaces sqlErrorMsg() with diag_set() and
    removes sqlErrorMsg().
    
    Closes #3965
    Closes #3036

diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index b6b6c24..704d636 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -499,13 +499,12 @@ sql_column_add_nullable_action(struct Parse *parser,
 	if (field->nullable_action != ON_CONFLICT_ACTION_DEFAULT &&
 	    nullable_action != field->nullable_action) {
 		/* Prevent defining nullable_action many times. */
-		const char *err_msg =
-			tt_sprintf("NULL declaration for column '%s' of table "
-				   "'%s' has been already set to '%s'",
-				   field->name, def->name,
-				   on_conflict_action_strs[field->
-							   nullable_action]);
-		diag_set(ClientError, ER_SQL, err_msg);
+		const char *err = "NULL declaration for column '%s' of table "
+				  "'%s' has been already set to '%s'";
+		const char *action =
+			on_conflict_action_strs[field->nullable_action];
+		err = tt_sprintf(err, field->name, def->name, action);
+		diag_set(ClientError, ER_SQL, err);
 		parser->is_aborted = true;
 		return;
 	}
@@ -3057,11 +3056,12 @@ sqlWithAdd(Parse * pParse,	/* Parsing context */
 	zName = sqlNameFromToken(pParse->db, pName);
 	if (zName && pWith) {
 		int i;
+		const char *err = "Ambiguous table name in WITH query: %s";
 		for (i = 0; i < pWith->nCte; i++) {
 			if (strcmp(zName, pWith->a[i].zName) == 0) {
-				sqlErrorMsg(pParse,
-						"duplicate WITH table name: %s",
-						zName);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 tt_sprintf(err, zName));
+				pParse->is_aborted = true;
 			}
 		}
 	}
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index 0eb28d7..f536bb8 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -102,10 +102,10 @@ sql_table_truncate(struct Parse *parse, struct SrcList *tab_list)
 		goto tarantool_error;
 	}
 	if (! rlist_empty(&space->parent_fk_constraint)) {
-		const char *err_msg =
-			tt_sprintf("can not truncate space '%s' because other "
-				   "objects depend on it", space->def->name);
-		diag_set(ClientError, ER_SQL, err_msg);
+		const char *err = "can not truncate space '%s' because other "
+				  "objects depend on it";
+		diag_set(ClientError, ER_SQL,
+			 tt_sprintf(err, space->def->name));
 		goto tarantool_error;
 	}
 	if (space->def->opts.is_view) {
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index de993c1..1077285 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -666,11 +666,7 @@ codeVectorCompare(Parse * pParse,	/* Code generator context */
 	int regRight = 0;
 	u8 op = pExpr->op;
 	int addrDone = sqlVdbeMakeLabel(v);
-
-	if (nLeft != sqlExprVectorSize(pRight)) {
-		sqlErrorMsg(pParse, "row value misused");
-		return;
-	}
+	assert(nLeft == sqlExprVectorSize(pRight));
 	assert(pExpr->op == TK_EQ || pExpr->op == TK_NE
 	       || pExpr->op == TK_LT || pExpr->op == TK_GT
 	       || pExpr->op == TK_LE || pExpr->op == TK_GE);
@@ -1201,7 +1197,14 @@ sqlExprAssignVarNumber(Parse * pParse, Expr * pExpr, u32 n)
 			testcase(i == 1);
 			testcase(i == SQL_BIND_PARAMETER_MAX - 1);
 			testcase(i == SQL_BIND_PARAMETER_MAX);
-			if (!is_ok || i < 1 || i > SQL_BIND_PARAMETER_MAX) {
+			if (i < 1) {
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 "Index of binding slots must start "\
+					 "from 1");
+				pParse->is_aborted = true;
+				return;
+			}
+			if (!is_ok || i > SQL_BIND_PARAMETER_MAX) {
 				diag_set(ClientError, ER_SQL_BIND_PARAMETER_MAX,
 					 SQL_BIND_PARAMETER_MAX);
 				pParse->is_aborted = true;
@@ -1785,8 +1788,10 @@ sqlExprListAppendVector(Parse * pParse,	/* Parsing context */
 	 */
 	if (pExpr->op != TK_SELECT
 	    && pColumns->nId != (n = sqlExprVectorSize(pExpr))) {
-		sqlErrorMsg(pParse, "%d columns assigned %d values",
-				pColumns->nId, n);
+		const char *err = tt_sprintf("%d columns assigned %d values",
+					     pColumns->nId, n);
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+		pParse->is_aborted = true;
 		goto vector_append_error;
 	}
 
@@ -2669,41 +2674,6 @@ expr_in_type(Parse *pParse, Expr *pExpr)
 }
 
 /*
- * Load the Parse object passed as the first argument with an error
- * message of the form:
- *
- *   "sub-select returns N columns - expected M"
- */
-void
-sqlSubselectError(Parse * pParse, int nActual, int nExpect)
-{
-	const char *zFmt = "sub-select returns %d columns - expected %d";
-	sqlErrorMsg(pParse, zFmt, nActual, nExpect);
-}
-
-/*
- * Expression pExpr is a vector that has been used in a context where
- * it is not permitted. If pExpr is a sub-select vector, this routine
- * loads the Parse object with a message of the form:
- *
- *   "sub-select returns N columns - expected 1"
- *
- * Or, if it is a regular scalar vector:
- *
- *   "row value misused"
- */
-void
-sqlVectorErrorMsg(Parse * pParse, Expr * pExpr)
-{
-	if (pExpr->flags & EP_xIsSelect) {
-		sqlSubselectError(pParse, pExpr->x.pSelect->pEList->nExpr,
-				      1);
-	} else {
-		sqlErrorMsg(pParse, "row value misused");
-	}
-}
-
-/*
  * Generate code for scalar subqueries used as a subquery expression, EXISTS,
  * or IN operators.  Examples:
  *
@@ -2984,15 +2954,23 @@ int
 sqlExprCheckIN(Parse * pParse, Expr * pIn)
 {
 	int nVector = sqlExprVectorSize(pIn->pLeft);
+	const char *err;
 	if ((pIn->flags & EP_xIsSelect)) {
 		if (nVector != pIn->x.pSelect->pEList->nExpr) {
-			sqlSubselectError(pParse,
-					      pIn->x.pSelect->pEList->nExpr,
-					      nVector);
+			err = "sub-select returns %d columns - expected %d";
+			int expr_count = pIn->x.pSelect->pEList->nExpr;
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+				 tt_sprintf(err, expr_count, nVector));
+			pParse->is_aborted = true;
 			return 1;
 		}
 	} else if (nVector != 1) {
-		sqlVectorErrorMsg(pParse, pIn->pLeft);
+		assert((pIn->pLeft->flags & EP_xIsSelect) != 0);
+		int expr_count = pIn->pLeft->x.pSelect->pEList->nExpr;
+		err = tt_sprintf("sub-select returns %d columns - expected 1",
+				 expr_count);
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+		pParse->is_aborted = true;
 		return 1;
 	}
 	return 0;
@@ -3990,9 +3968,10 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 			AggInfo *pInfo = pExpr->pAggInfo;
 			if (pInfo == 0) {
 				assert(!ExprHasProperty(pExpr, EP_IntValue));
-				sqlErrorMsg(pParse,
-						"misuse of aggregate: %s()",
-						pExpr->u.zToken);
+				const char *err = "misuse of aggregate: %s()";
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 tt_sprintf(err, pExpr->u.zToken));
+				pParse->is_aborted = true;
 			} else {
 				pExpr->type = pInfo->aFunc->pFunc->ret_type;
 				return pInfo->aFunc[pExpr->iAgg].iMem;
@@ -4159,7 +4138,11 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 			testcase(op == TK_SELECT);
 			if (op == TK_SELECT
 			    && (nCol = pExpr->x.pSelect->pEList->nExpr) != 1) {
-				sqlSubselectError(pParse, nCol, 1);
+				const char *err = "sub-select returns %d "\
+						  "columns - expected 1";
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 tt_sprintf(err, nCol));
+				pParse->is_aborted = true;
 			} else {
 				return sqlCodeSubselect(pParse, pExpr, 0);
 			}
@@ -4178,9 +4161,11 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 						 sqlExprVectorSize(pExpr->
 								       pLeft))
 			    ) {
-				sqlErrorMsg(pParse,
-						"%d columns assigned %d values",
-						pExpr->iTable, n);
+				const char *err =
+					"%d columns assigned %d values";
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 tt_sprintf(err, pExpr->iTable, n));
+				pParse->is_aborted = true;
 			}
 			return pExpr->pLeft->iTable + pExpr->iColumn;
 		}
@@ -4279,7 +4264,9 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 		}
 
 	case TK_VECTOR:{
-			sqlErrorMsg(pParse, "row value misused");
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+				 "row value misused");
+			pParse->is_aborted = true;
 			break;
 		}
 
@@ -4379,8 +4366,9 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 		}
 	case TK_RAISE:
 		if (pParse->triggered_space == NULL) {
-			sqlErrorMsg(pParse, "RAISE() may only be used "
-					"within a trigger-program");
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC, "RAISE() "\
+				 "may only be used within a trigger-program");
+			pParse->is_aborted = true;
 			return 0;
 		}
 		if (pExpr->on_conflict_action == ON_CONFLICT_ACTION_ABORT)
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 0ca38ae..7e98f42 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -396,10 +396,11 @@ sqlInsert(Parse * pParse,	/* Parser context */
 				goto insert_cleanup;
 			}
 			if (bit_test(used_columns, j)) {
-				const char *err;
-				err = "table id list: duplicate column name %s";
-				sqlErrorMsg(pParse,
-						err, pColumn->a[i].zName);
+				const char *err = "table id list: duplicate "\
+						  "column name %s";
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 tt_sprintf(err, pColumn->a[i].zName));
+				pParse->is_aborted = true;
 				goto insert_cleanup;
 			}
 			bit_set(used_columns, j);
@@ -510,14 +511,19 @@ sqlInsert(Parse * pParse,	/* Parser context */
 
 	if (pColumn == NULL && nColumn != 0 &&
 	    nColumn != (int)space_def->field_count) {
-		sqlErrorMsg(pParse,
-				"table %S has %d columns but %d values were supplied",
-				pTabList, 0, space_def->field_count, nColumn);
+		const char *err =
+			"table %s has %d columns but %d values were supplied";
+		err = tt_sprintf(err, pTabList->a[0].zName,
+				 space_def->field_count, nColumn);
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+		pParse->is_aborted = true;
 		goto insert_cleanup;
 	}
 	if (pColumn != 0 && nColumn != pColumn->nId) {
-		sqlErrorMsg(pParse, "%d values for %d columns", nColumn,
-				pColumn->nId);
+		const char *err = "%d values for %d columns";
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+			 tt_sprintf(err, nColumn, pColumn->nId));
+		pParse->is_aborted = true;
 		goto insert_cleanup;
 	}
 
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index ff668e0..a82b85a 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -904,7 +904,9 @@ expr(A) ::= VARIABLE(X).     {
   Token t = X;
   if (pParse->parse_only) {
     spanSet(&A, &t, &t);
-    sqlErrorMsg(pParse, "bindings are not allowed in DDL");
+    diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+             "bindings are not allowed in DDL");
+    pParse->is_aborted = true;
     A.pExpr = NULL;
   } else if (!(X.z[0]=='#' && sqlIsdigit(X.z[1]))) {
     u32 n = X.n;
@@ -1390,9 +1392,9 @@ trigger_cmd_list(A) ::= trigger_cmd(A) SEMI. {
 trnm(A) ::= nm(A).
 trnm(A) ::= nm DOT nm(X). {
   A = X;
-  sqlErrorMsg(pParse,
-        "qualified table names are not allowed on INSERT, UPDATE, and DELETE "
-        "statements within triggers");
+  diag_set(ClientError, ER_SQL_PARSER_GENERIC, "qualified table names are not "\
+           "allowed on INSERT, UPDATE, and DELETE statements within triggers");
+  pParse->is_aborted = true;
 }
 
 // Disallow the INDEX BY and NOT INDEXED clauses on UPDATE and DELETE
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 8cdcfd0..ef40927 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -390,16 +390,21 @@ lookupName(Parse * pParse,	/* The parsing context */
 					assert(pExpr->x.pList == 0);
 					assert(pExpr->x.pSelect == 0);
 					pOrig = pEList->a[j].pExpr;
+					const char *err = "misuse of aliased "\
+							  "aggregate %s";
 					if ((pNC->ncFlags & NC_AllowAgg) == 0
 					    && ExprHasProperty(pOrig, EP_Agg)) {
-						sqlErrorMsg(pParse,
-								"misuse of aliased aggregate %s",
-								zAs);
+						diag_set(ClientError,
+							 ER_SQL_PARSER_GENERIC,
+							 tt_sprintf(err, zAs));
+						pParse->is_aborted = true;
 						return WRC_Abort;
 					}
 					if (sqlExprVectorSize(pOrig) != 1) {
-						sqlErrorMsg(pParse,
-								"row value misused");
+						diag_set(ClientError,
+							 ER_SQL_PARSER_GENERIC,
+							 "row value misused");
+						pParse->is_aborted = true;
 						return WRC_Abort;
 					}
 					resolveAlias(pParse, pEList, j, pExpr,
@@ -426,12 +431,15 @@ lookupName(Parse * pParse,	/* The parsing context */
 	 * more matches.  Either way, we have an error.
 	 */
 	if (cnt > 1) {
+		const char *err;
 		if (zTab) {
-			sqlErrorMsg(pParse, "ambiguous column name: %s.%s",
-				    zTab, zCol);
+			err = tt_sprintf("ambiguous column name: %s.%s", zTab,
+					 zCol);
 		} else {
-			sqlErrorMsg(pParse, "ambiguous column name: %s", zCol);
+			err = tt_sprintf("ambiguous column name: %s", zCol);
 		}
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+		pParse->is_aborted = true;
 		pTopNC->nErr++;
 	}
 	if (cnt == 0) {
@@ -626,7 +634,7 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 			} else {
 				is_agg = pDef->xFinalize != 0;
 				pExpr->type = pDef->ret_type;
-				const char *err_msg =
+				const char *err =
 					"second argument to likelihood() must "\
 					"be a constant between 0.0 and 1.0";
 				if (pDef->funcFlags & SQL_FUNC_UNLIKELY) {
@@ -639,7 +647,7 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 						if (pExpr->iTable < 0) {
 							diag_set(ClientError,
 								 ER_ILLEGAL_PARAMS,
-								 err_msg);
+								 err);
 							pParse->is_aborted =
 								true;
 							pNC->nErr++;
@@ -679,9 +687,11 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 				}
 			}
 			if (is_agg && (pNC->ncFlags & NC_AllowAgg) == 0) {
-				sqlErrorMsg(pParse,
-						"misuse of aggregate function %.*s()",
-						nId, zId);
+				const char *err =
+					tt_sprintf("misuse of aggregate "\
+						   "function %.*s()", nId, zId);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+				pParse->is_aborted = true;
 				pNC->nErr++;
 				is_agg = 0;
 			} else if (no_such_func && pParse->db->init.busy == 0
@@ -693,9 +703,11 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 				pParse->is_aborted = true;
 				pNC->nErr++;
 			} else if (wrong_num_args) {
-				sqlErrorMsg(pParse,
-						"wrong number of arguments to function %.*s()",
-						nId, zId);
+				const char *err = "wrong number of arguments "\
+						  "to function %.*s()";
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 tt_sprintf(err, nId, zId));
+				pParse->is_aborted = true;
 				pNC->nErr++;
 			}
 			if (is_agg)
@@ -796,7 +808,9 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 				testcase(pExpr->op == TK_GT);
 				testcase(pExpr->op == TK_GE);
 				testcase(pExpr->op == TK_BETWEEN);
-				sqlErrorMsg(pParse, "row value misused");
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 "row value misused");
+				pParse->is_aborted = true;
 			}
 			break;
 		}
@@ -898,21 +912,6 @@ resolveOrderByTermToExprList(Parse * pParse,	/* Parsing context for error messag
 }
 
 /*
- * Generate an ORDER BY or GROUP BY term out-of-range error.
- */
-static void
-resolveOutOfRangeError(Parse * pParse,	/* The error context into which to write the error */
-		       const char *zType,	/* "ORDER" or "GROUP" */
-		       int i,	/* The index (1-based) of the term out of range */
-		       int mx	/* Largest permissible value of i */
-    )
-{
-	sqlErrorMsg(pParse,
-			"%r %s BY term out of range - should be "
-			"between 1 and %d", i, zType, mx);
-}
-
-/*
  * Analyze the ORDER BY clause in a compound SELECT statement.   Modify
  * each term of the ORDER BY clause is a constant integer between 1
  * and N where N is the number of columns in the compound SELECT.
@@ -973,9 +972,15 @@ resolveCompoundOrderBy(Parse * pParse,	/* Parsing context.  Leave error messages
 			pE = sqlExprSkipCollate(pItem->pExpr);
 			if (sqlExprIsInteger(pE, &iCol)) {
 				if (iCol <= 0 || iCol > pEList->nExpr) {
-					resolveOutOfRangeError(pParse, "ORDER",
-							       i + 1,
-							       pEList->nExpr);
+					const char *err =
+						"Error at ORDER BY in place "\
+						"%d: term out of range - "\
+						"should be between 1 and %d";
+					err = tt_sprintf(err, i + 1,
+							 pEList->nExpr);
+					diag_set(ClientError,
+						 ER_SQL_PARSER_GENERIC, err);
+					pParse->is_aborted = true;
 					return 1;
 				}
 			} else {
@@ -1021,9 +1026,12 @@ resolveCompoundOrderBy(Parse * pParse,	/* Parsing context.  Leave error messages
 	}
 	for (i = 0; i < pOrderBy->nExpr; i++) {
 		if (pOrderBy->a[i].done == 0) {
-			sqlErrorMsg(pParse,
-					"%r ORDER BY term does not match any "
-					"column in the result set", i + 1);
+			const char *err = "Error at ORDER BY in place %d: "\
+					  "term does not match any column in "\
+					  "the result set";
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+				 tt_sprintf(err, i + 1));
+			pParse->is_aborted = true;
 			return 1;
 		}
 	}
@@ -1073,8 +1081,14 @@ sqlResolveOrderGroupBy(Parse *pParse, Select *pSelect, ExprList *pOrderBy,
 	for (i = 0, pItem = pOrderBy->a; i < pOrderBy->nExpr; i++, pItem++) {
 		if (pItem->u.x.iOrderByCol) {
 			if (pItem->u.x.iOrderByCol > pEList->nExpr) {
-				resolveOutOfRangeError(pParse, zType, i + 1,
-						       pEList->nExpr);
+				const char *err = "Error at %s BY in place "\
+						  "%d: term out of range - "\
+						  "should be between 1 and %d";
+				err = tt_sprintf(err, zType, i + 1,
+						 pEList->nExpr);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 err);
+				pParse->is_aborted = true;
 				return 1;
 			}
 			resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol - 1,
@@ -1141,8 +1155,13 @@ resolveOrderGroupBy(NameContext *pNC, Select *pSelect, ExprList *pOrderBy,
 			 * order-by term to a copy of the result-set expression
 			 */
 			if (iCol < 1 || iCol > 0xffff) {
-				resolveOutOfRangeError(pParse, zType, i + 1,
-						       nResult);
+				const char *err = "Error at %s BY in place "\
+						  "%d: term out of range - "\
+						  "should be between 1 and %d";
+				err = tt_sprintf(err, zType, i + 1, nResult);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 err);
+				pParse->is_aborted = true;
 				return 1;
 			}
 			pItem->u.x.iOrderByCol = (u16) iCol;
@@ -1422,12 +1441,15 @@ resolveSelectStep(Walker * pWalker, Select * p)
 			    || db->mallocFailed) {
 				return WRC_Abort;
 			}
+			const char *err_msg = "aggregate functions are not "\
+					      "allowed in the GROUP BY clause";
 			for (i = 0, pItem = pGroupBy->a; i < pGroupBy->nExpr;
 			     i++, pItem++) {
 				if (ExprHasProperty(pItem->pExpr, EP_Agg)) {
-					sqlErrorMsg(pParse,
-							"aggregate functions are not allowed in "
-							"the GROUP BY clause");
+					diag_set(ClientError,
+						 ER_SQL_PARSER_GENERIC,
+						 err_msg);
+					pParse->is_aborted = true;
 					return WRC_Abort;
 				}
 			}
@@ -1437,10 +1459,37 @@ resolveSelectStep(Walker * pWalker, Select * p)
 		 * number of expressions in the select list.
 		 */
 		if (p->pNext && p->pEList->nExpr != p->pNext->pEList->nExpr) {
-			sqlSelectWrongNumTermsError(pParse, p->pNext);
+			if (p->pNext->selFlags & SF_Values) {
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 "all VALUES must have the same "\
+					 "number of terms");
+			} else {
+				const char *err_msg =
+					"SELECTs to the left and right of %s "\
+					"do not have the same number of "\
+					"result columns";
+				switch (p->pNext->op) {
+				case TK_ALL:
+					err_msg = tt_sprintf(err_msg,
+							     "UNION ALL");
+					break;
+				case TK_INTERSECT:
+					err_msg = tt_sprintf(err_msg,
+							     "INTERSECT");
+					break;
+				case TK_EXCEPT:
+					err_msg = tt_sprintf(err_msg, "EXCEPT");
+					break;
+				default:
+					err_msg = tt_sprintf(err_msg, "UNION");
+					break;
+				}
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 err_msg);
+			}
+			pParse->is_aborted = true;
 			return WRC_Abort;
 		}
-
 		/* Advance to the next term of the compound
 		 */
 		p = p->pPrior;
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 3f58f30..1b85074 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -397,13 +397,19 @@ sqlJoinType(Parse * pParse, Token * pA, Token * pB, Token * pC)
 	}
 	if ((jointype & (JT_INNER | JT_OUTER)) == (JT_INNER | JT_OUTER) ||
 	    (jointype & JT_ERROR) != 0) {
-		const char *zSp = " ";
 		assert(pB != 0);
-		if (pC == 0) {
-			zSp++;
+		const char *err;
+		if (pC == NULL) {
+			err = tt_sprintf("unknown or unsupported join type: "\
+					 "%.*s %.*s", pA->n, pA->z, pB->n,
+					 pB->z);
+		} else {
+			err = tt_sprintf("unknown or unsupported join type: "\
+					 "%.*s %.*s %.*s", pA->n, pA->z, pB->n,
+					 pB->z, pC->n, pC->z);
 		}
-		sqlErrorMsg(pParse, "unknown or unsupported join type: "
-				"%T %T%s%T", pA, pB, zSp, pC);
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+		pParse->is_aborted = true;
 		jointype = JT_INNER;
 	} else if ((jointype & JT_OUTER) != 0
 		   && (jointype & (JT_LEFT | JT_RIGHT)) != JT_LEFT) {
@@ -590,9 +596,10 @@ sqlProcessJoin(Parse * pParse, Select * p)
 		 */
 		if (pRight->fg.jointype & JT_NATURAL) {
 			if (pRight->pOn || pRight->pUsing) {
-				sqlErrorMsg(pParse,
-						"a NATURAL join may not have "
-						"an ON or USING clause", 0);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 "a NATURAL join may not have "
+					 "an ON or USING clause");
+				pParse->is_aborted = true;
 				return 1;
 			}
 			for (j = 0; j < (int)right_space->def->field_count; j++) {
@@ -613,8 +620,10 @@ sqlProcessJoin(Parse * pParse, Select * p)
 		/* Disallow both ON and USING clauses in the same join
 		 */
 		if (pRight->pOn && pRight->pUsing) {
-			sqlErrorMsg(pParse, "cannot have both ON and USING "
-					"clauses in the same join");
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+				 "cannot have both ON and USING clauses in "\
+				 "the same join");
+			pParse->is_aborted = true;
 			return 1;
 		}
 
@@ -637,6 +646,8 @@ sqlProcessJoin(Parse * pParse, Select * p)
 		 * not contained in both tables to be joined.
 		 */
 		if (pRight->pUsing) {
+			const char *err = "cannot join using column %s - "\
+					  "column not present in both tables";
 			IdList *pList = pRight->pUsing;
 			for (j = 0; j < pList->nId; j++) {
 				char *zName;	/* Name of the term in the USING clause */
@@ -650,10 +661,10 @@ sqlProcessJoin(Parse * pParse, Select * p)
 				    || !tableAndColumnIndex(pSrc, i + 1, zName,
 							    &iLeft, &iLeftCol)
 				    ) {
-					sqlErrorMsg(pParse,
-							"cannot join using column %s - column "
-							"not present in both tables",
-							zName);
+					err = tt_sprintf(err, zName);
+					diag_set(ClientError,
+						 ER_SQL_PARSER_GENERIC, err);
+					pParse->is_aborted = true;
 					return 1;
 				}
 				addWhereTerm(pParse, pSrc, iLeft, iLeftCol,
@@ -2601,16 +2612,20 @@ multiSelect(Parse * pParse,	/* Parsing context */
 	pPrior = p->pPrior;
 	dest = *pDest;
 	if (pPrior->pOrderBy) {
-		sqlErrorMsg(pParse,
-				"ORDER BY clause should come after %s not before",
-				selectOpName(p->op));
+		const char *err_msg =
+			tt_sprintf("ORDER BY clause should come after %s not "\
+				   "before", selectOpName(p->op));
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+		pParse->is_aborted = true;
 		rc = 1;
 		goto multi_select_end;
 	}
 	if (pPrior->pLimit) {
-		sqlErrorMsg(pParse,
-				"LIMIT clause should come after %s not before",
-				selectOpName(p->op));
+		const char *err_msg =
+			tt_sprintf("LIMIT clause should come after %s not "\
+				   "before", selectOpName(p->op));
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+		pParse->is_aborted = true;
 		rc = 1;
 		goto multi_select_end;
 	}
@@ -2988,19 +3003,6 @@ multiSelect(Parse * pParse,	/* Parsing context */
 }
 #endif				/* SQL_OMIT_COMPOUND_SELECT */
 
-void
-sqlSelectWrongNumTermsError(struct Parse *parse, struct Select * p)
-{
-	if (p->selFlags & SF_Values) {
-		sqlErrorMsg(parse, "all VALUES must have the same number "\
-				"of terms");
-	} else {
-		sqlErrorMsg(parse, "SELECTs to the left and right of %s "
-				"do not have the same number of result columns",
-				selectOpName(p->op));
-	}
-}
-
 /**
  * Code an output subroutine for a coroutine implementation of a
  * SELECT statment.
@@ -3622,7 +3624,14 @@ substExpr(Parse * pParse,	/* Report errors here */
 			assert(pEList != 0 && pExpr->iColumn < pEList->nExpr);
 			assert(pExpr->pLeft == 0 && pExpr->pRight == 0);
 			if (sqlExprIsVector(pCopy)) {
-				sqlVectorErrorMsg(pParse, pCopy);
+				assert((pCopy->flags & EP_xIsSelect) != 0);
+				const char *err = "sub-select returns %d "\
+						  "columns - expected 1";
+				int expr_count =
+					pCopy->x.pSelect->pEList->nExpr;
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 tt_sprintf(err, expr_count));
+				pParse->is_aborted = true;
 			} else {
 				pNew = sqlExprDup(db, pCopy, 0);
 				if (pNew && (pExpr->flags & EP_FromJoin)) {
@@ -4518,21 +4527,6 @@ convertCompoundSelectToSubquery(Walker * pWalker, Select * p)
 	return WRC_Continue;
 }
 
-/*
- * Check to see if the FROM clause term pFrom has table-valued function
- * arguments.  If it does, leave an error message in pParse and return
- * non-zero, since pFrom is not allowed to be a table-valued function.
- */
-static int
-cannotBeFunction(Parse * pParse, struct SrcList_item *pFrom)
-{
-	if (pFrom->fg.isTabFunc) {
-		sqlErrorMsg(pParse, "'%s' is not a function", pFrom->zName);
-		return 1;
-	}
-	return 0;
-}
-
 #ifndef SQL_OMIT_CTE
 /*
  * Argument pWith (which may be NULL) points to a linked list of nested
@@ -4627,11 +4621,18 @@ withExpand(Walker * pWalker, struct SrcList_item *pFrom)
 		 * In this case, proceed.
 		 */
 		if (pCte->zCteErr) {
-			sqlErrorMsg(pParse, pCte->zCteErr, pCte->zName);
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+				 tt_sprintf(pCte->zCteErr, pCte->zName));
+			pParse->is_aborted = true;
 			return SQL_ERROR;
 		}
-		if (cannotBeFunction(pParse, pFrom))
+		if (pFrom->fg.isTabFunc) {
+			const char *err = "'%s' is not a function";
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+				 tt_sprintf(err, pFrom->zName));
+			pParse->is_aborted = true;
 			return SQL_ERROR;
+		}
 
 		assert(pFrom->space == NULL);
 		pFrom->space = sql_ephemeral_space_new(pParse, pCte->zName);
@@ -4663,9 +4664,11 @@ withExpand(Walker * pWalker, struct SrcList_item *pFrom)
 			}
 		}
 		if (ref_counter > 1) {
-			sqlErrorMsg(pParse,
-					"multiple references to recursive table: %s",
-					pCte->zName);
+			const char *err_msg =
+				tt_sprintf("multiple references to recursive "\
+					   "table: %s", pCte->zName);
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+			pParse->is_aborted = true;
 			return SQL_ERROR;
 		}
 		assert(ref_counter == 0 ||
@@ -4681,10 +4684,13 @@ withExpand(Walker * pWalker, struct SrcList_item *pFrom)
 		pEList = pLeft->pEList;
 		if (pCte->pCols) {
 			if (pEList && pEList->nExpr != pCte->pCols->nExpr) {
-				sqlErrorMsg(pParse,
-						"table %s has %d values for %d columns",
-						pCte->zName, pEList->nExpr,
-						pCte->pCols->nExpr);
+				const char *err_msg =
+					tt_sprintf("table %s has %d values "\
+						   "for %d columns",
+						   pCte->zName, pEList->nExpr,
+						   pCte->pCols->nExpr);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+				pParse->is_aborted = true;
 				pParse->pWith = pSavedWith;
 				return SQL_ERROR;
 			}
@@ -4840,8 +4846,15 @@ selectExpander(Walker * pWalker, Select * p)
 			struct space *space = sql_lookup_space(pParse, pFrom);
 			if (space == NULL)
 				return WRC_Abort;
-			if (cannotBeFunction(pParse, pFrom))
+			if (pFrom->fg.isTabFunc) {
+				const char *err =
+					tt_sprintf("'%s' is not a function",
+						   pFrom->zName);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 err);
+				pParse->is_aborted = true;
 				return WRC_Abort;
+			}
 			if (space->def->opts.is_view) {
 				struct Select *select =
 					sql_view_compile(db, space->def->opts.sql);
@@ -5252,9 +5265,10 @@ resetAccumulator(Parse * pParse, AggInfo * pAggInfo)
 			Expr *pE = pFunc->pExpr;
 			assert(!ExprHasProperty(pE, EP_xIsSelect));
 			if (pE->x.pList == 0 || pE->x.pList->nExpr != 1) {
-				sqlErrorMsg(pParse,
-						"DISTINCT aggregates must have exactly one "
-						"argument");
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 "DISTINCT aggregates must have "\
+					 "exactly one argument");
+				pParse->is_aborted = true;
 				pFunc->iDistinct = -1;
 			} else {
 				struct sql_key_info *key_info =
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 363ee8d..166e3fd 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -3193,7 +3193,6 @@ void sqlTreeViewWith(TreeView *, const With *);
 #endif
 
 void sqlSetString(char **, sql *, const char *);
-void sqlErrorMsg(Parse *, const char *, ...);
 void sqlDequote(char *);
 void sqlNormalizeName(char *z);
 void sqlTokenInit(Token *, char *);
@@ -4427,16 +4426,6 @@ void sqlExpirePreparedStatements(sql *);
 int sqlCodeSubselect(Parse *, Expr *, int);
 void sqlSelectPrep(Parse *, Select *, NameContext *);
 
-/**
- * Error message for when two or more terms of a compound select
- * have different size result sets.
- *
- * @param parse Parsing context.
- * @param p Select struct to analyze.
- */
-void
-sqlSelectWrongNumTermsError(struct Parse *parse, struct Select *p);
-
 int sqlMatchSpanName(const char *, const char *, const char *);
 int sqlResolveExprNames(NameContext *, Expr *);
 int sqlResolveExprListNames(NameContext *, ExprList *);
@@ -4885,7 +4874,6 @@ int sqlExprVectorSize(Expr * pExpr);
 int sqlExprIsVector(Expr * pExpr);
 Expr *sqlVectorFieldSubexpr(Expr *, int);
 Expr *sqlExprForVectorField(Parse *, Expr *, int);
-void sqlVectorErrorMsg(Parse *, Expr *);
 
 /* Tarantool: right now query compilation is invoked on top of
  * fiber's stack. Need to limit number of nested programs under
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index b23d60a..c984950 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -621,8 +621,9 @@ codeTriggerProgram(Parse * pParse,	/* The parser context */
 	sqlSubProgramsRemaining--;
 
 	if (sqlSubProgramsRemaining == 0) {
-		sqlErrorMsg(pParse,
-				"Maximum number of chained trigger activations exceeded.");
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, "Maximum number "\
+			 "of chained trigger activations exceeded.");
+		pParse->is_aborted = true;
 	}
 
 	for (pStep = pStepList; pStep; pStep = pStep->pNext) {
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 71a1e00..0b645eb 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -179,10 +179,15 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 				    sql_space_column_is_in_pk(space, j))
 					is_pk_modified = true;
 				if (aXRef[j] != -1) {
-					sqlErrorMsg(pParse,
-							"set id list: duplicate"
-							" column name %s",
-							pChanges->a[i].zName);
+					const char *err =
+						"set id list: duplicate "\
+						"column name %s";
+					err = tt_sprintf(err,
+							 pChanges->a[i].zName);
+					diag_set(ClientError,
+						 ER_SQL_PARSER_GENERIC,
+						 err);
+					pParse->is_aborted = true;
 					goto update_cleanup;
 				}
 				aXRef[j] = i;
diff --git a/src/box/sql/util.c b/src/box/sql/util.c
index d9bb2af..cac404f 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -211,38 +211,6 @@ sqlErrorWithMsg(sql * db, int err_code, const char *zFormat, ...)
 }
 
 /*
- * Add an error to the diagnostics area and set
- * pParse->is_aborted.
- * The following formatting characters are allowed:
- *
- *      %s      Insert a string
- *      %z      A string that should be freed after use
- *      %d      Insert an integer
- *      %T      Insert a token
- *      %S      Insert the first element of a SrcList
- *
- * This function should be used to report any error that occurs while
- * compiling an SQL statement (i.e. within sql_prepare()). The
- * last thing the sql_prepare() function does is copy the error
- * stored by this function into the database handle using sqlError().
- * Functions sqlError() or sqlErrorWithMsg() should be used
- * during statement execution (sql_step() etc.).
- */
-void
-sqlErrorMsg(Parse * pParse, const char *zFormat, ...)
-{
-	char *zMsg;
-	va_list ap;
-	sql *db = pParse->db;
-	va_start(ap, zFormat);
-	zMsg = sqlVMPrintf(db, zFormat, ap);
-	va_end(ap);
-	diag_set(ClientError, ER_SQL_PARSER_GENERIC, zMsg);
-	sqlDbFree(db, zMsg);
-	pParse->is_aborted = true;
-}
-
-/*
  * Convert an SQL-style quoted string into a normal string by removing
  * the quote characters.  The conversion is done in-place.  If the
  * input does not begin with a quote character, then this routine
diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
index f417c49..15a2f55 100644
--- a/src/box/sql/vdbemem.c
+++ b/src/box/sql/vdbemem.c
@@ -1208,15 +1208,9 @@ valueFromFunction(sql * db,	/* The database connection */
 	ctx.pOut = pVal;
 	ctx.pFunc = pFunc;
 	pFunc->xSFunc(&ctx, nVal, apVal);
-	if (ctx.isError) {
-		rc = ctx.isError;
-		sqlErrorMsg(pCtx->pParse, "%s", sql_value_text(pVal));
-	} else {
-		sql_value_apply_type(pVal, type);
-		assert(rc == SQL_OK);
-	}
-	if (rc != SQL_OK)
-		pCtx->pParse->is_aborted = true;
+	assert(!ctx.isError);
+	sql_value_apply_type(pVal, type);
+	assert(rc == SQL_OK);
 
  value_from_function_out:
 	if (rc != SQL_OK) {
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index 2f83e81..c71a170 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -3913,11 +3913,7 @@ wherePathSolver(WhereInfo * pWInfo, LogEst nRowEst)
 		nFrom = nTo;
 	}
 
-	if (nFrom == 0) {
-		sqlErrorMsg(pParse, "no query solution");
-		sqlDbFree(db, pSpace);
-		return SQL_ERROR;
-	}
+	assert(nFrom != 0);
 
 	/* Find the lowest cost path.  pFrom will be left pointing to that path */
 	pFrom = aFrom;
@@ -4382,10 +4378,8 @@ sqlWhereBegin(Parse * pParse,	/* The parser context */
 	 * equal to pTabList->nSrc but might be shortened to 1 if the
 	 * WHERE_OR_SUBCLAUSE flag is set.
 	 */
-	for (ii = 0; ii < pTabList->nSrc; ii++) {
+	for (ii = 0; ii < pTabList->nSrc; ii++)
 		createMask(pMaskSet, pTabList->a[ii].iCursor);
-		sqlWhereTabFuncArgs(pParse, &pTabList->a[ii], &pWInfo->sWC);
-	}
 #ifdef SQL_DEBUG
 	for (ii = 0; ii < pTabList->nSrc; ii++) {
 		Bitmask m =
diff --git a/src/box/sql/whereInt.h b/src/box/sql/whereInt.h
index 47430ae..9cf1eb8 100644
--- a/src/box/sql/whereInt.h
+++ b/src/box/sql/whereInt.h
@@ -479,7 +479,6 @@ void sqlWhereSplit(WhereClause *, Expr *, u8);
 Bitmask sqlWhereExprUsage(WhereMaskSet *, Expr *);
 Bitmask sqlWhereExprListUsage(WhereMaskSet *, ExprList *);
 void sqlWhereExprAnalyze(SrcList *, WhereClause *);
-void sqlWhereTabFuncArgs(Parse *, struct SrcList_item *, WhereClause *);
 
 /*
  * Bitmasks for the operators on WhereTerm objects.  These are all
diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c
index 6df28ad..9d01a7e 100644
--- a/src/box/sql/whereexpr.c
+++ b/src/box/sql/whereexpr.c
@@ -1472,45 +1472,3 @@ sqlWhereExprAnalyze(SrcList * pTabList,	/* the FROM clause */
 		exprAnalyze(pTabList, pWC, i);
 	}
 }
-
-/*
- * For table-valued-functions, transform the function arguments into
- * new WHERE clause terms.
- */
-void
-sqlWhereTabFuncArgs(Parse * pParse,	/* Parsing context */
-			struct SrcList_item *pItem,	/* The FROM clause term to process */
-			WhereClause * pWC	/* Xfer function arguments to here */
-    )
-{
-	int j, k;
-	ExprList *pArgs;
-	Expr *pColRef;
-	Expr *pTerm;
-	if (pItem->fg.isTabFunc == 0)
-		return;
-	struct space_def *space_def = pItem->space->def;
-	pArgs = pItem->u1.pFuncArg;
-	if (pArgs == 0)
-		return;
-	for (j = k = 0; j < pArgs->nExpr; j++) {
-		while (k < (int)space_def->field_count)
-			k++;
-		if (k >= (int)space_def->field_count) {
-			sqlErrorMsg(pParse,
-					"too many arguments on %s() - max %d",
-					space_def->name, j);
-			return;
-		}
-		pColRef = sqlExprAlloc(pParse->db, TK_COLUMN, 0, 0);
-		if (pColRef == 0)
-			return;
-		pColRef->iTable = pItem->iCursor;
-		pColRef->iColumn = k++;
-		pColRef->space_def = space_def;
-		pTerm = sqlPExpr(pParse, TK_EQ, pColRef,
-				     sqlExprDup(pParse->db,
-						    pArgs->a[j].pExpr, 0));
-		whereClauseInsert(pWC, pTerm, TERM_DYNAMIC);
-	}
-}
diff --git a/test/sql-tap/e_select1.test.lua b/test/sql-tap/e_select1.test.lua
index e190ad7..8e9a2bb 100755
--- a/test/sql-tap/e_select1.test.lua
+++ b/test/sql-tap/e_select1.test.lua
@@ -1855,13 +1855,13 @@ test:do_catchsql_test(
     "e_select-8.7.1.1",
     "SELECT x FROM d1 UNION ALL SELECT a FROM d2 ORDER BY x*z",
     {
-        1, "1st ORDER BY term does not match any column in the result set"})
+        1, "Error at ORDER BY in place 1: term does not match any column in the result set"})
 
 test:do_catchsql_test(
     "e_select-8.7.1.2",
     "SELECT x,z FROM d1 UNION ALL SELECT a,b FROM d2 ORDER BY x, x/z",
     {
-        1, "2nd ORDER BY term does not match any column in the result set"})
+        1, "Error at ORDER BY in place 2: term does not match any column in the result set"})
 
 test:do_select_tests(
     "e_select-8.7.2",
@@ -2077,12 +2077,12 @@ test:do_select_tests(
 -- EVIDENCE-OF: R-39265-04070 If no matching expression can be found in
 -- the result columns of any constituent SELECT, it is an error.
 --
-for _, val in ipairs({{1, "SELECT a FROM d5 UNION SELECT c FROM d6 ORDER BY a+1", "1st"},
-    {2, "SELECT a FROM d5 UNION SELECT c FROM d6 ORDER BY a, a+1", "2nd"},
-    {3, "SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY \"hello\"", "1st"},
-    {4, "SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY blah", "1st"},
-    {5, "SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY c,d,c+d", "3rd"},
-    {6, "SELECT * FROM d5 EXCEPT SELECT * FROM d7 ORDER BY 1,2,b,a/b", "4th"}}) do
+for _, val in ipairs({{1, "SELECT a FROM d5 UNION SELECT c FROM d6 ORDER BY a+1", "1"},
+    {2, "SELECT a FROM d5 UNION SELECT c FROM d6 ORDER BY a, a+1", "2"},
+    {3, "SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY \"hello\"", "1"},
+    {4, "SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY blah", "1"},
+    {5, "SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY c,d,c+d", "3"},
+    {6, "SELECT * FROM d5 EXCEPT SELECT * FROM d7 ORDER BY 1,2,b,a/b", "4"}}) do
     local tn = val[1]
     local select = val[2]
     local err_param = val[3]
@@ -2090,7 +2090,7 @@ for _, val in ipairs({{1, "SELECT a FROM d5 UNION SELECT c FROM d6 ORDER BY a+1"
         "e_select-8.14."..tn,
         select,
         {
-            1, string.format("%s ORDER BY term does not match any column in the result set", err_param)})
+            1, string.format("Error at ORDER BY in place %s: term does not match any column in the result set", err_param)})
 end
 -- EVIDENCE-OF: R-03407-11483 Each term of the ORDER BY clause is
 -- processed separately and may be matched against result columns from
diff --git a/test/sql-tap/null.test.lua b/test/sql-tap/null.test.lua
index 50a2cfb..de4d503 100755
--- a/test/sql-tap/null.test.lua
+++ b/test/sql-tap/null.test.lua
@@ -295,7 +295,7 @@ test:do_catchsql_test(
         select b from t1 union select c from t1 order by t1.a;
     ]], {
         -- <null-6.5>
-        1, "1st ORDER BY term does not match any column in the result set"
+        1, "Error at ORDER BY in place 1: term does not match any column in the result set"
         -- </null-6.5>
     })
 
@@ -305,7 +305,7 @@ test:do_catchsql_test(
         select b from t1 union select c from t1 order by t1.a;
     ]], {
         -- <null-6.6>
-        1, "1st ORDER BY term does not match any column in the result set"
+        1, "Error at ORDER BY in place 1: term does not match any column in the result set"
         -- </null-6.6>
     })
 
diff --git a/test/sql-tap/select1.test.lua b/test/sql-tap/select1.test.lua
index d73429a..545ae9d 100755
--- a/test/sql-tap/select1.test.lua
+++ b/test/sql-tap/select1.test.lua
@@ -840,7 +840,7 @@ test:do_catchsql_test(
         SELECT * FROM t5 ORDER BY 3;
     ]], {
         -- <select1-4.10.1>
-        1, "1st ORDER BY term out of range - should be between 1 and 2"
+        1, "Error at ORDER BY in place 1: term out of range - should be between 1 and 2"
         -- </select1-4.10.1>
     })
 
@@ -850,7 +850,7 @@ test:do_catchsql_test(
         SELECT * FROM t5 ORDER BY -1;
     ]], {
         -- <select1-4.10.2>
-        1, "1st ORDER BY term out of range - should be between 1 and 2"
+        1, "Error at ORDER BY in place 1: term out of range - should be between 1 and 2"
         -- </select1-4.10.2>
     })
 
@@ -1334,7 +1334,7 @@ test:do_catchsql2_test(
             ORDER BY f2+101;
         ]], {
             -- <select1-6.11>
-            1, "1st ORDER BY term does not match any column in the result set"
+            1, "Error at ORDER BY in place 1: term does not match any column in the result set"
             -- </select1-6.11>
         })
 
diff --git a/test/sql-tap/select3.test.lua b/test/sql-tap/select3.test.lua
index 9fb825f..3c88f77 100755
--- a/test/sql-tap/select3.test.lua
+++ b/test/sql-tap/select3.test.lua
@@ -157,7 +157,7 @@ test:do_catchsql_test("select3-2.10", [[
   SELECT log, count(*) FROM t1 GROUP BY 0 ORDER BY log;
 ]], {
   -- <select3-2.10>
-  1, "1st GROUP BY term out of range - should be between 1 and 2"
+  1, "Error at GROUP BY in place 1: term out of range - should be between 1 and 2"
   -- </select3-2.10>
 })
 
@@ -165,7 +165,7 @@ test:do_catchsql_test("select3-2.11", [[
   SELECT log, count(*) FROM t1 GROUP BY 3 ORDER BY log;
 ]], {
   -- <select3-2.11>
-  1, "1st GROUP BY term out of range - should be between 1 and 2"
+  1, "Error at GROUP BY in place 1: term out of range - should be between 1 and 2"
   -- </select3-2.11>
 })
 
diff --git a/test/sql-tap/select4.test.lua b/test/sql-tap/select4.test.lua
index bd2ada9..3aafedb 100755
--- a/test/sql-tap/select4.test.lua
+++ b/test/sql-tap/select4.test.lua
@@ -450,7 +450,7 @@ test:do_catchsql_test(
         ORDER BY "xyzzy";
     ]], {
         -- <select4-5.2c>
-        1, "1st ORDER BY term does not match any column in the result set"
+        1, "Error at ORDER BY in place 1: term does not match any column in the result set"
         -- </select4-5.2c>
     })
 
@@ -463,7 +463,7 @@ test:do_catchsql_test(
         ORDER BY "xyzzy";
     ]], {
         -- <select4-5.2d>
-        1, "1st ORDER BY term does not match any column in the result set"
+        1, "Error at ORDER BY in place 1: term does not match any column in the result set"
         -- </select4-5.2d>
     })
 
@@ -515,7 +515,7 @@ test:do_catchsql_test(
         ORDER BY 2;
     ]], {
         -- <select4-5.2h>
-        1, "1st ORDER BY term out of range - should be between 1 and 1"
+        1, "Error at ORDER BY in place 1: term out of range - should be between 1 and 1"
         -- </select4-5.2h>
     })
 
diff --git a/test/sql-tap/sql-errors.test.lua b/test/sql-tap/sql-errors.test.lua
index ac242c4..b43400e 100755
--- a/test/sql-tap/sql-errors.test.lua
+++ b/test/sql-tap/sql-errors.test.lua
@@ -1,9 +1,9 @@
 #!/usr/bin/env tarantool
 test = require("sqltester")
-test:plan(36)
+test:plan(43)
 
 test:execsql([[
-	CREATE TABLE t0 (i INT PRIMARY KEY);
+	CREATE TABLE t0 (i INT PRIMARY KEY, a INT);
 	CREATE VIEW v0 AS SELECT * FROM t0;
 ]])
 format = {}
@@ -79,7 +79,7 @@ test:do_catchsql_test(
 test:do_catchsql_test(
 	"sql-errors-1.7",
 	[[
-		CREATE VIEW v7(a,b) AS SELECT * FROM t0;
+		CREATE VIEW v7(a,b,c) AS SELECT * FROM t0;
 	]], {
 		-- <sql-errors-1.7>
 		1,"Failed to create space 'V7': number of aliases doesn't match provided columns"
@@ -416,4 +416,74 @@ test:do_catchsql_test(
 		-- </sql-errors-1.36>
 	})
 
+test:do_catchsql_test(
+	"sql-errors-1.37",
+	[[
+		CREATE TRIGGER r0 AFTER INSERT ON t0 BEGIN INSERT INTO t0.i VALUES (2); END;
+	]], {
+		-- <sql-errors-1.37>
+		1,"qualified table names are not allowed on INSERT, UPDATE, and DELETE statements within triggers"
+		-- </sql-errors-1.37>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.38",
+	[[
+		UPDATE t0 SET (i, a) = (100,1,1);
+	]], {
+		-- <sql-errors-1.38>
+		1,"2 columns assigned 3 values"
+		-- </sql-errors-1.38>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.39",
+	[[
+		SELECT * FROM t0();
+	]], {
+		-- <sql-errors-1.39>
+		1,"'T0' is not a function"
+		-- </sql-errors-1.39>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.40",
+	[[
+		SELECT $0;
+	]], {
+		-- <sql-errors-1.40>
+		1,"Index of binding slots must start from 1"
+		-- </sql-errors-1.40>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.41",
+	[[
+		SELECT (1,2,3) == (1,2,3,4);
+	]], {
+		-- <sql-errors-1.41>
+		1,"row value misused"
+		-- </sql-errors-1.41>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.42",
+	[[
+		SELECT (1, 2);
+	]], {
+		-- <sql-errors-1.42>
+		1,"row value misused"
+		-- </sql-errors-1.42>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.43",
+	[[
+		SELECT (i,a) AS m FROM t0 WHERE m < 1;
+	]], {
+		-- <sql-errors-1.43>
+		1,"row value misused"
+		-- </sql-errors-1.43>
+	})
+
 test:finish_test()
diff --git a/test/sql-tap/tkt2822.test.lua b/test/sql-tap/tkt2822.test.lua
index 4212cbd..86674ae 100755
--- a/test/sql-tap/tkt2822.test.lua
+++ b/test/sql-tap/tkt2822.test.lua
@@ -200,7 +200,7 @@ test:do_catchsql_test(
         SELECT a, b, c FROM t1 UNION ALL SELECT a, b, c FROM t2 ORDER BY x
     ]], {
         -- <tkt2822-4.1>
-        1, "1st ORDER BY term does not match any column in the result set"
+        1, "Error at ORDER BY in place 1: term does not match any column in the result set"
         -- </tkt2822-4.1>
     })
 
@@ -298,7 +298,7 @@ test:do_test(
         ]]
     end, {
         -- <tkt2822-7.1>
-        1, "1st ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 1: term out of range - should be between 1 and 25"
         -- </tkt2822-7.1>
     })
 
@@ -308,7 +308,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 0;
     ]], {
         -- <tkt2822-7.2.1>
-        1, "2nd ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 2: term out of range - should be between 1 and 25"
         -- </tkt2822-7.2.1>
     })
 
@@ -318,7 +318,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 26;
     ]], {
         -- <tkt2822-7.2.2>
-        1, "2nd ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 2: term out of range - should be between 1 and 25"
         -- </tkt2822-7.2.2>
     })
 
@@ -328,7 +328,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 65536;
     ]], {
         -- <tkt2822-7.2.3>
-        1, "2nd ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 2: term out of range - should be between 1 and 25"
         -- </tkt2822-7.2.3>
     })
 
@@ -338,7 +338,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 2, 0;
     ]], {
         -- <tkt2822-7.3>
-        1, "3rd ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 3: term out of range - should be between 1 and 25"
         -- </tkt2822-7.3>
     })
 
@@ -348,7 +348,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 2, 3, 0;
     ]], {
         -- <tkt2822-7.4>
-        1, "4th ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 4: term out of range - should be between 1 and 25"
         -- </tkt2822-7.4>
     })
 
@@ -358,7 +358,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 0;
     ]], {
         -- <tkt2822-7.9>
-        1, "9th ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 9: term out of range - should be between 1 and 25"
         -- </tkt2822-7.9>
     })
 
@@ -368,7 +368,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 0;
     ]], {
         -- <tkt2822-7.10>
-        1, "10th ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 10: term out of range - should be between 1 and 25"
         -- </tkt2822-7.10>
     })
 
@@ -378,7 +378,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0;
     ]], {
         -- <tkt2822-7.11>
-        1, "11th ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 11: term out of range - should be between 1 and 25"
         -- </tkt2822-7.11>
     })
 
@@ -388,7 +388,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 0;
     ]], {
         -- <tkt2822-7.12>
-        1, "12th ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 12: term out of range - should be between 1 and 25"
         -- </tkt2822-7.12>
     })
 
@@ -398,7 +398,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 0;
     ]], {
         -- <tkt2822-7.13>
-        1, "13th ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 13: term out of range - should be between 1 and 25"
         -- </tkt2822-7.13>
     })
 
@@ -409,7 +409,7 @@ test:do_catchsql_test(
                                  11,12,13,14,15,16,17,18,19, 0
     ]], {
         -- <tkt2822-7.20>
-        1, "20th ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 20: term out of range - should be between 1 and 25"
         -- </tkt2822-7.20>
     })
 
@@ -420,7 +420,7 @@ test:do_catchsql_test(
                                  11,12,13,14,15,16,17,18,19, 20, 0
     ]], {
         -- <tkt2822-7.21>
-        1, "21st ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 21: term out of range - should be between 1 and 25"
         -- </tkt2822-7.21>
     })
 
@@ -431,7 +431,7 @@ test:do_catchsql_test(
                                  11,12,13,14,15,16,17,18,19, 20, 21, 0
     ]], {
         -- <tkt2822-7.22>
-        1, "22nd ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 22: term out of range - should be between 1 and 25"
         -- </tkt2822-7.22>
     })
 
diff --git a/test/sql-tap/with1.test.lua b/test/sql-tap/with1.test.lua
index 16c9b12..f1a1699 100755
--- a/test/sql-tap/with1.test.lua
+++ b/test/sql-tap/with1.test.lua
@@ -134,7 +134,7 @@ test:do_catchsql_test(3.2, [[
   SELECT * FROM tmp;
 ]], {
   -- <3.2>
-  1, "duplicate WITH table name: TMP"
+  1, "Ambiguous table name in WITH query: TMP"
   -- </3.2>
 })
 
@@ -782,7 +782,7 @@ test:do_catchsql_test("10.7.1", [[
   SELECT * FROM t
 ]], {
   -- <10.7.1>
-  1, "1st ORDER BY term does not match any column in the result set"
+  1, "Error at ORDER BY in place 1: term does not match any column in the result set"
   -- </10.7.1>
 })
 
diff --git a/test/sql/iproto.result b/test/sql/iproto.result
index 56099fa..48376c8 100644
--- a/test/sql/iproto.result
+++ b/test/sql/iproto.result
@@ -571,7 +571,8 @@ cn:execute('select ?1, ?2, ?3', {1, 2, 3})
 ...
 cn:execute('select $name, $name2', {1, 2})
 ---
-- error: 'Failed to execute SQL statement: SQL bind parameter limit reached: 65000'
+- error: 'Failed to execute SQL statement: Index of binding slots must start from
+    1'
 ...
 parameters = {}
 ---

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 8/8] sql: remove sqlErrorMsg()
  2019-03-25 18:47     ` Mergen Imeev
@ 2019-03-26 13:34       ` n.pettik
  2019-03-26 17:52         ` Mergen Imeev
  0 siblings, 1 reply; 43+ messages in thread
From: n.pettik @ 2019-03-26 13:34 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen


>> @@ -1039,8 +1036,8 @@ resolveCompoundOrderBy(Parse * pParse,    /* Parsing context.  Leave error messages
>>                        const char *err = "Error at ORDER BY in place %d: "\
>>                                          "term does not match any column in "\
>>                                          "the result set";
>> -                       err = tt_sprintf(err, i + 1);
>> -                       diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
>> +                       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
>> +                                tt_sprintf(err, i + 1));
>> 
>>> @@ -1444,10 +1469,37 @@ resolveSelectStep(Walker * pWalker, Select * p)
>>>    * number of expressions in the select list.
>>>    */
>>>   if (p->pNext && p->pEList->nExpr != p->pNext->pEList->nExpr) {
>>> -     sqlSelectWrongNumTermsError(pParse, p->pNext);
>>> +     if (p->pNext->selFlags & SF_Values) {
>>> +       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
>>> +          "all VALUES must have the same "\
>>> +          "number of terms");
>>> +     } else {
>>> +       const char *err_msg =
>>> +         "SELECTs to the left and right of %s "\
>>> +         "do not have the same number of "\
>>> +         "result columns";
>>> +       switch (p->pNext->op) {
>>> +       case TK_ALL:
>>> +         err_msg = tt_sprintf(err_msg,
>>> +                  "UNION ALL");
>>> +         break;
>> 
>> Why don’t use selectOpName?
>> Like it was in  sqlSelectWrongNumTermsError().
>> 
> Function selectOpName defined in different file and is static.
> I think it isn't worth to share it for one error.

I believe it is worth doing. For instance, you can return
sqlSelectWrongNumTermsError() which was placed in select.c
and called that static func.

> Diff between versions:
> 
> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index e26596a..1077285 100644
> --- a/src/box/sql/expr.c
> +++ b/src/box/sql/expr.c
> @@ -666,13 +666,7 @@ codeVectorCompare(Parse * pParse,	/* Code generator context */
> 	int regRight = 0;
> 	u8 op = pExpr->op;
> 	int addrDone = sqlVdbeMakeLabel(v);
> -
> -	if (nLeft != sqlExprVectorSize(pRight)) {
> -		diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> -			 "row value misused");
> -		pParse->is_aborted = true;
> -		return;
> -	}

Here the problem is that function is never called in our test suite:
I placed assert(0); and no one test failed. Please, add at least
simple tests like SELECT (1, 2) == (1, 2) to make sure that
codeVectorCompare() can be processed. Tests which you’ve added
fails before this func is called.

Added comment:

diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 107728590..397f8209c 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -666,6 +666,11 @@ codeVectorCompare(Parse * pParse,  /* Code generator context */
        int regRight = 0;
        u8 op = pExpr->op;
        int addrDone = sqlVdbeMakeLabel(v);
+       /*
+        * Situation when vectors have different dimensions is
+        * filtred way before - during expr resolution:
+        * see resolveExprStep().
+        */

> +	assert(nLeft == sqlExprVectorSize(pRight));
> 	assert(pExpr->op == TK_EQ || pExpr->op == TK_NE
> 	       || pExpr->op == TK_LT || pExpr->op == TK_GT
> 	       || pExpr->op == TK_LE || pExpr->op == TK_GE);
> 
> 
> @@ -4384,10 +4378,8 @@ sqlWhereBegin(Parse * pParse,	/* The parser context */
> 	 * equal to pTabList->nSrc but might be shortened to 1 if the
> 	 * WHERE_OR_SUBCLAUSE flag is set.
> 	 */
> -	for (ii = 0; ii < pTabList->nSrc; ii++) {
> +	for (ii = 0; ii < pTabList->nSrc; ii++)
> 		createMask(pMaskSet, pTabList->a[ii].iCursor);
> -		sqlWhereTabFuncArgs(pParse, &pTabList->a[ii], &pWInfo->sWC);

Move removal of this func to a separate patch pls alongside with mentions of
table-valued funcs. It is not related to errors.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 7/8] sql: rework semantic errors
  2019-03-22 12:48     ` Mergen Imeev
@ 2019-03-26 14:14       ` n.pettik
  2019-03-26 16:56         ` Mergen Imeev
  0 siblings, 1 reply; 43+ messages in thread
From: n.pettik @ 2019-03-26 14:14 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen

[-- Attachment #1: Type: text/plain, Size: 4453 bytes --]


>>> @@ -1042,27 +1044,34 @@ resolveCompoundOrderBy(Parse * pParse,	/* Parsing context.  Leave error messages
>>> * field) then convert that term into a copy of the corresponding result set
>>> * column.
>>> *
>>> - * If any errors are detected, add an error message to pParse and
>>> - * return non-zero.  Return zero if no errors are seen.
>>> + * @param pParse Parsing context.
>>> + * @param pSelect The SELECT statement containing the clause.
>>> + * @param pOrderBy The ORDER BY or GROUP BY clause to be
>>> + *                 processed.
>>> + * @param is_order_by True if pOrderBy is ORDER BY, false if
>>> + *                    pOrderBy is GROUP BY
>>> + * @retval 0 On success, not 0 elsewhere.
>>> */
>>> int
>>> -sqlResolveOrderGroupBy(Parse * pParse,	/* Parsing context.  Leave error messages here */
>>> -			   Select * pSelect,	/* The SELECT statement containing the clause */
>>> -			   ExprList * pOrderBy,	/* The ORDER BY or GROUP BY clause to be processed */
>>> -			   const char *zType	/* "ORDER" or "GROUP" */
>>> -    )
>>> +sqlResolveOrderGroupBy(Parse *pParse, Select *pSelect, ExprList *pOrderBy,
>>> +		       bool is_order_by)
>> 
>> Why did you decide to fix code style here? Anyway, you didn't finished it
>> (struct prefixes, param naming and so on and so forth)
>> 
> I fixed this because I thought that it is quite unreliable to
> differentiate names of term using first letter of its name. Should
> I remove these changes?

But below you anyway get string representation. So, let’s return
back previous version.

>>> {
>>> 	int i;
>>> 	sql *db = pParse->db;
>>> 	ExprList *pEList;
>>> 	struct ExprList_item *pItem;
>>> +	const char *zType = is_order_by ? "ORDER" : "GROUP";
>>> 
>>> 	if (pOrderBy == 0 || pParse->db->mallocFailed)
>>> 		return 0;
>>> 
>>> @@ -1096,22 +1105,23 @@ sqlResolveOrderGroupBy(Parse * pParse,	/* Parsing context.  Leave error messages
>>> * result-set expression.  Otherwise, the expression is resolved in
>>> * the usual way - using sqlResolveExprNames().
>>> *
>>> - * This routine returns the number of errors.  If errors occur, then
>>> - * an appropriate error message might be left in pParse.  (OOM errors
>>> - * excepted.)
>>> + * @param pNC The name context of the SELECT statement.
>>> + * @param pSelect The SELECT statement containing the clause.
>>> + * @param pOrderBy An ORDER BY or GROUP BY clause to resolve.
>>> + * @param is_order_by True if pOrderBy is ORDER BY, false if
>>> + *                    pOrderBy is GROUP BY
>>> + * @retval 0 On success, not 0 elsewhere.
>>> */
>>> static int
>>> -resolveOrderGroupBy(NameContext * pNC,	/* The name context of the SELECT statement */
>>> -		    Select * pSelect,	/* The SELECT statement holding pOrderBy */
>>> -		    ExprList * pOrderBy,	/* An ORDER BY or GROUP BY clause to resolve */
>>> -		    const char *zType	/* Either "ORDER" or "GROUP", as appropriate */
>>> -    )
>> 
>> The same question.
>> 
> Answer above.
> 
> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
> index 0022254..b6b6c24 100644
> --- a/src/box/sql/build.c
> +++ b/src/box/sql/build.c
> @@ -603,7 +603,7 @@ sqlAddPrimaryKey(Parse * pParse,	/* Parsing context */
> 		goto primary_key_exit;
> 	if (sql_space_primary_key(space) != NULL) {
> 		diag_set(ClientError, ER_CREATE_SPACE, space->def->name,
> -			 "too many primary keys");
> +			 "primary key already exists”);

Sorry, could you fix message to “primary key has been already declared”
or “can’t declare PRIMARY KEY more than once”? This error is related
only to CREATE TABLE processing, so nothing is created at this stage yet.
Hence, error msg may seem a little bit misleading.

> diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
> index de0f282..efb895f 100644
> --- a/src/box/sql/tokenize.c
> +++ b/src/box/sql/tokenize.c
> @@ -481,8 +481,8 @@ sqlRunParser(Parse * pParse, const char *zSql)
> 				      &pParse->sLastToken.isReserved);
> 			i += pParse->sLastToken.n;
> 			if (i > mxSqlLen) {
> -				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> -					 "string or blob too big");
> +				diag_set(ClientError, ER_SQL_PARSER_LIMIT,
> +					 "SQL command length", i, mxSqlLen);
> 				pParse->is_aborted = true;
> 				break;
> 			}
> 
> I will investigate this error a bit later.

Ok, then file it (and the rest of comments which you haven’t fixed).



[-- Attachment #2: Type: text/html, Size: 52560 bytes --]

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 7/8] sql: rework semantic errors
  2019-03-26 14:14       ` n.pettik
@ 2019-03-26 16:56         ` Mergen Imeev
  2019-03-26 18:16           ` n.pettik
  0 siblings, 1 reply; 43+ messages in thread
From: Mergen Imeev @ 2019-03-26 16:56 UTC (permalink / raw)
  To: n.pettik; +Cc: tarantool-patches

Hi! Thank you for review! My answers, diff and new patch below.

On Tue, Mar 26, 2019 at 05:14:22PM +0300, n.pettik wrote:
> 
> >>> @@ -1042,27 +1044,34 @@ resolveCompoundOrderBy(Parse * pParse,	/* Parsing context.  Leave error messages
> >>> * field) then convert that term into a copy of the corresponding result set
> >>> * column.
> >>> *
> >>> - * If any errors are detected, add an error message to pParse and
> >>> - * return non-zero.  Return zero if no errors are seen.
> >>> + * @param pParse Parsing context.
> >>> + * @param pSelect The SELECT statement containing the clause.
> >>> + * @param pOrderBy The ORDER BY or GROUP BY clause to be
> >>> + *                 processed.
> >>> + * @param is_order_by True if pOrderBy is ORDER BY, false if
> >>> + *                    pOrderBy is GROUP BY
> >>> + * @retval 0 On success, not 0 elsewhere.
> >>> */
> >>> int
> >>> -sqlResolveOrderGroupBy(Parse * pParse,	/* Parsing context.  Leave error messages here */
> >>> -			   Select * pSelect,	/* The SELECT statement containing the clause */
> >>> -			   ExprList * pOrderBy,	/* The ORDER BY or GROUP BY clause to be processed */
> >>> -			   const char *zType	/* "ORDER" or "GROUP" */
> >>> -    )
> >>> +sqlResolveOrderGroupBy(Parse *pParse, Select *pSelect, ExprList *pOrderBy,
> >>> +		       bool is_order_by)
> >> 
> >> Why did you decide to fix code style here? Anyway, you didn't finished it
> >> (struct prefixes, param naming and so on and so forth)
> >> 
> > I fixed this because I thought that it is quite unreliable to
> > differentiate names of term using first letter of its name. Should
> > I remove these changes?
> 
> But below you anyway get string representation. So, let’s return
> back previous version.
> 
Fixed.

> >>> {
> >>> 	int i;
> >>> 	sql *db = pParse->db;
> >>> 	ExprList *pEList;
> >>> 	struct ExprList_item *pItem;
> >>> +	const char *zType = is_order_by ? "ORDER" : "GROUP";
> >>> 
> >>> 	if (pOrderBy == 0 || pParse->db->mallocFailed)
> >>> 		return 0;
> >>> 
> >>> @@ -1096,22 +1105,23 @@ sqlResolveOrderGroupBy(Parse * pParse,	/* Parsing context.  Leave error messages
> >>> * result-set expression.  Otherwise, the expression is resolved in
> >>> * the usual way - using sqlResolveExprNames().
> >>> *
> >>> - * This routine returns the number of errors.  If errors occur, then
> >>> - * an appropriate error message might be left in pParse.  (OOM errors
> >>> - * excepted.)
> >>> + * @param pNC The name context of the SELECT statement.
> >>> + * @param pSelect The SELECT statement containing the clause.
> >>> + * @param pOrderBy An ORDER BY or GROUP BY clause to resolve.
> >>> + * @param is_order_by True if pOrderBy is ORDER BY, false if
> >>> + *                    pOrderBy is GROUP BY
> >>> + * @retval 0 On success, not 0 elsewhere.
> >>> */
> >>> static int
> >>> -resolveOrderGroupBy(NameContext * pNC,	/* The name context of the SELECT statement */
> >>> -		    Select * pSelect,	/* The SELECT statement holding pOrderBy */
> >>> -		    ExprList * pOrderBy,	/* An ORDER BY or GROUP BY clause to resolve */
> >>> -		    const char *zType	/* Either "ORDER" or "GROUP", as appropriate */
> >>> -    )
> >> 
> >> The same question.
> >> 
> > Answer above.
> > 
> > diff --git a/src/box/sql/build.c b/src/box/sql/build.c
> > index 0022254..b6b6c24 100644
> > --- a/src/box/sql/build.c
> > +++ b/src/box/sql/build.c
> > @@ -603,7 +603,7 @@ sqlAddPrimaryKey(Parse * pParse,	/* Parsing context */
> > 		goto primary_key_exit;
> > 	if (sql_space_primary_key(space) != NULL) {
> > 		diag_set(ClientError, ER_CREATE_SPACE, space->def->name,
> > -			 "too many primary keys");
> > +			 "primary key already exists”);
> 
> Sorry, could you fix message to “primary key has been already declared”
> or “can’t declare PRIMARY KEY more than once”? This error is related
> only to CREATE TABLE processing, so nothing is created at this stage yet.
> Hence, error msg may seem a little bit misleading.
> 
Fixed.


Diff:

diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index b6b6c24..fe53262 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -603,7 +603,7 @@ sqlAddPrimaryKey(Parse * pParse,	/* Parsing context */
 		goto primary_key_exit;
 	if (sql_space_primary_key(space) != NULL) {
 		diag_set(ClientError, ER_CREATE_SPACE, space->def->name,
-			 "primary key already exists");
+			 "primary key has been already declared");
 		pParse->is_aborted = true;
 		goto primary_key_exit;
 	}
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 8cdcfd0..30a6b5f 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -1037,31 +1037,29 @@ resolveCompoundOrderBy(Parse * pParse,	/* Parsing context.  Leave error messages
  * field) then convert that term into a copy of the corresponding result set
  * column.
  *
- * @param pParse Parsing context.
- * @param pSelect The SELECT statement containing the clause.
- * @param pOrderBy The ORDER BY or GROUP BY clause to be
- *                 processed.
- * @param is_order_by True if pOrderBy is ORDER BY, false if
- *                    pOrderBy is GROUP BY
  * @retval 0 On success, not 0 elsewhere.
  */
 int
-sqlResolveOrderGroupBy(Parse *pParse, Select *pSelect, ExprList *pOrderBy,
-		       bool is_order_by)
+sqlResolveOrderGroupBy(Parse * pParse,	/* Parsing context.  Leave error messages here */
+			   Select * pSelect,	/* The SELECT statement containing the clause */
+			   ExprList * pOrderBy,	/* The ORDER BY or GROUP BY clause to be processed */
+			   const char *zType	/* "ORDER" or "GROUP" */
+    )
 {
 	int i;
 	sql *db = pParse->db;
 	ExprList *pEList;
 	struct ExprList_item *pItem;
-	const char *zType = is_order_by ? "ORDER" : "GROUP";
 
 	if (pOrderBy == 0 || pParse->db->mallocFailed)
 		return 0;
 #if SQL_MAX_COLUMN
 	if (pOrderBy->nExpr > db->aLimit[SQL_LIMIT_COLUMN]) {
-		const char *err_msg =
-			is_order_by ? "The number of terms in ORDER BY clause" :
-				      "The number of terms in GROUP BY clause";
+		const char *err_msg;
+		if (zType[0] != 'G')
+			err_msg = "The number of terms in ORDER BY clause";
+		else
+			err_msg = "The number of terms in GROUP BY clause";
 		diag_set(ClientError, ER_SQL_PARSER_LIMIT, err_msg,
 			 pOrderBy->nExpr, db->aLimit[SQL_LIMIT_COLUMN]);
 		pParse->is_aborted = true;
@@ -1098,23 +1096,20 @@ sqlResolveOrderGroupBy(Parse *pParse, Select *pSelect, ExprList *pOrderBy,
  * result-set expression.  Otherwise, the expression is resolved in
  * the usual way - using sqlResolveExprNames().
  *
- * @param pNC The name context of the SELECT statement.
- * @param pSelect The SELECT statement containing the clause.
- * @param pOrderBy An ORDER BY or GROUP BY clause to resolve.
- * @param is_order_by True if pOrderBy is ORDER BY, false if
- *                    pOrderBy is GROUP BY
  * @retval 0 On success, not 0 elsewhere.
  */
 static int
-resolveOrderGroupBy(NameContext *pNC, Select *pSelect, ExprList *pOrderBy,
-		    bool is_order_by)
+resolveOrderGroupBy(NameContext * pNC,	/* The name context of the SELECT statement */
+		    Select * pSelect,	/* The SELECT statement holding pOrderBy */
+		    ExprList * pOrderBy,	/* An ORDER BY or GROUP BY clause to resolve */
+		    const char *zType	/* Either "ORDER" or "GROUP", as appropriate */
+    )
 {
 	int i, j;		/* Loop counters */
 	int iCol;		/* Column number */
 	struct ExprList_item *pItem;	/* A term of the ORDER BY clause */
 	Parse *pParse;		/* Parsing context */
 	int nResult;		/* Number of terms in the result set */
-	const char *zType = is_order_by ? "ORDER" : "GROUP";
 
 	if (pOrderBy == 0)
 		return 0;
@@ -1161,7 +1156,7 @@ resolveOrderGroupBy(NameContext *pNC, Select *pSelect, ExprList *pOrderBy,
 			}
 		}
 	}
-	return sqlResolveOrderGroupBy(pParse, pSelect, pOrderBy, is_order_by);
+	return sqlResolveOrderGroupBy(pParse, pSelect, pOrderBy, zType);
 }
 
 /*
@@ -1404,7 +1399,7 @@ resolveSelectStep(Walker * pWalker, Select * p)
 		 * resolve those symbols on the incorrect ORDER BY for consistency.
 		 */
 		if (isCompound <= nCompound	/* Defer right-most ORDER BY of a compound */
-		    && resolveOrderGroupBy(&sNC, p, p->pOrderBy, true)
+		    && resolveOrderGroupBy(&sNC, p, p->pOrderBy, "ORDER")
 		    ) {
 			return WRC_Abort;
 		}
@@ -1418,7 +1413,7 @@ resolveSelectStep(Walker * pWalker, Select * p)
 		if (pGroupBy) {
 			struct ExprList_item *pItem;
 
-			if (resolveOrderGroupBy(&sNC, p, pGroupBy, false)
+			if (resolveOrderGroupBy(&sNC, p, pGroupBy, "GROUP")
 			    || db->mallocFailed) {
 				return WRC_Abort;
 			}
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 3f58f30..23c9499 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -3396,9 +3396,11 @@ multiSelectOrderBy(Parse * pParse,	/* Parsing context */
 	 */
 	p->pPrior = 0;
 	pPrior->pNext = 0;
-	sqlResolveOrderGroupBy(pParse, p, p->pOrderBy, true);
-	if (pPrior->pPrior == 0)
-		sqlResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, true);
+	sqlResolveOrderGroupBy(pParse, p, p->pOrderBy, "ORDER");
+	if (pPrior->pPrior == 0) {
+		sqlResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy,
+					   "ORDER");
+	}
 
 	/* Compute the limit registers */
 	computeLimitRegisters(pParse, p, labelEnd);
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 207577d..8fb2b18 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -4441,7 +4441,7 @@ int sqlMatchSpanName(const char *, const char *, const char *);
 int sqlResolveExprNames(NameContext *, Expr *);
 int sqlResolveExprListNames(NameContext *, ExprList *);
 void sqlResolveSelectNames(Parse *, Select *, NameContext *);
-int sqlResolveOrderGroupBy(Parse *, Select *, ExprList *, bool);
+int sqlResolveOrderGroupBy(Parse *, Select *, ExprList *, const char *);
 
 /**
  * Generate code for default value.
diff --git a/test/sql-tap/misc1.test.lua b/test/sql-tap/misc1.test.lua
index 6b9f5fa..dbc0699 100755
--- a/test/sql-tap/misc1.test.lua
+++ b/test/sql-tap/misc1.test.lua
@@ -348,7 +348,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <misc1-7.1>
-        1, [[Failed to create space 'ERROR1': primary key already exists]]
+        1, [[Failed to create space 'ERROR1': primary key has been already declared]]
         -- </misc1-7.1>
     })
 
@@ -361,7 +361,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <misc1-7.2>
-        1, [[Failed to create space 'ERROR1': primary key already exists]]
+        1, [[Failed to create space 'ERROR1': primary key has been already declared]]
         -- </misc1-7.2>
     })
 
@@ -897,7 +897,7 @@ test:do_catchsql_test(
         CREATE TABLE test2(a text primary key, b text, primary key(a,b));
     ]], {
         -- <misc1-16.3>
-        1, [[Failed to create space 'TEST2': primary key already exists]]
+        1, [[Failed to create space 'TEST2': primary key has been already declared]]
         -- </misc1-16.3>
     })
 
diff --git a/test/sql-tap/sql-errors.test.lua b/test/sql-tap/sql-errors.test.lua
index ac242c4..635df6a 100755
--- a/test/sql-tap/sql-errors.test.lua
+++ b/test/sql-tap/sql-errors.test.lua
@@ -17,7 +17,7 @@ test:do_catchsql_test(
 		ANALYZE v0;
 	]], {
 		-- <sql-errors-1.1>
-		1,"ANALYZE statement argument V0 is not a base table"
+		1,"Syntax error near 'ANALYZE'"
 		-- </sql-errors-1.1>
 	})
 
@@ -52,7 +52,7 @@ test:do_catchsql_test(
 		CREATE TABLE t4 (i INT PRIMARY KEY, a INT PRIMARY KEY);
 	]], {
 		-- <sql-errors-1.4>
-		1,"Failed to create space 'T4': primary key already exists"
+		1,"Failed to create space 'T4': primary key has been already declared"
 		-- </sql-errors-1.4>
 	})
 
diff --git a/test/sql-tap/unique.test.lua b/test/sql-tap/unique.test.lua
index a3fa1c9..fbd73a6 100755
--- a/test/sql-tap/unique.test.lua
+++ b/test/sql-tap/unique.test.lua
@@ -36,7 +36,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <unique-1.1>
-        1, [[Failed to create space 'T1': primary key already exists]]
+        1, [[Failed to create space 'T1': primary key has been already declared]]
         -- </unique-1.1>
     })
 


New patch:

commit 6ae80f806033df2ac181e2ad5469fda3b0393640
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Thu Mar 7 21:28:06 2019 +0300

    sql: rework semantic errors
    
    This patch reworks some of SQL semantic errors.
    
    Part of #3965

diff --git a/src/box/errcode.h b/src/box/errcode.h
index 7764aa3..3f8cb8e 100644
--- a/src/box/errcode.h
+++ b/src/box/errcode.h
@@ -122,7 +122,7 @@ struct errcode_record {
 	/* 67 */_(ER_REPLICA_ID_IS_RESERVED,	"Can't initialize replica id with a reserved value %u") \
 	/* 68 */_(ER_INVALID_ORDER,		"Invalid LSN order for instance %u: previous LSN = %llu, new lsn = %llu") \
 	/* 69 */_(ER_MISSING_REQUEST_FIELD,	"Missing mandatory field '%s' in request") \
-	/* 70 */_(ER_IDENTIFIER,		"Invalid identifier '%s' (expected printable symbols only)") \
+	/* 70 */_(ER_IDENTIFIER,		"Invalid identifier '%s' (expected printable symbols only or it is too long)") \
 	/* 71 */_(ER_DROP_FUNCTION,		"Can't drop function %u: %s") \
 	/* 72 */_(ER_ITERATOR_TYPE,		"Unknown iterator type '%s'") \
 	/* 73 */_(ER_REPLICA_MAX,		"Replica count limit reached: %u") \
@@ -240,6 +240,12 @@ struct errcode_record {
 	/*185 */_(ER_SQL_UNKNOWN_TOKEN,		"Syntax error: unrecognized token: '%.*s'") \
 	/*186 */_(ER_SQL_PARSER_GENERIC,	"%s") \
 	/*187 */_(ER_SQL_ANALYZE_ARGUMENT,	"ANALYZE statement argument %s is not a base table") \
+	/*188 */_(ER_SQL_COLUMN_COUNT_MAX,	"Failed to create space '%s': space column count %d exceeds the limit (%d)") \
+	/*189 */_(ER_HEX_LITERAL_MAX,		"Hex literal %s%s length %d exceeds the supported limit (%d)") \
+	/*190 */_(ER_INT_LITERAL_MAX,		"Integer literal %s%s exceeds the supported range %lld - %lld") \
+	/*191 */_(ER_SQL_PARSER_LIMIT,		"%s %d exceeds the limit (%d)") \
+	/*192 */_(ER_INDEX_DEF_UNSUPPORTED,	"%s are prohibited in an index definition") \
+	/*193 */_(ER_CK_DEF_UNSUPPORTED,	"%s are prohibited in a CHECK constraint definition") \
 
 /*
  * !IMPORTANT! Please follow instructions at start of the file
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 0c06555..fe53262 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -262,19 +262,28 @@ sqlNameFromToken(sql * db, Token * pName)
  * (e.g. table, index, column name of a real table)
  * All names are legal except those that cantain non-printable
  * characters or have length greater than BOX_NAME_MAX.
+ *
+ * @param pParse Parser context.
+ * @param zName Identifier to check.
+ *
+ * @retval 0 on success.
+ * @retval -1 on error.
  */
 int
 sqlCheckIdentifierName(Parse *pParse, char *zName)
 {
 	ssize_t len = strlen(zName);
-
-	if (len > BOX_NAME_MAX || identifier_check(zName, len) != 0) {
-		sqlErrorMsg(pParse,
-				"identifier name is invalid: %s",
-				zName);
-		return SQL_ERROR;
+	if (len > BOX_NAME_MAX) {
+		diag_set(ClientError, ER_IDENTIFIER,
+			 tt_cstr(zName, BOX_INVALID_NAME_MAX));
+		pParse->is_aborted = true;
+		return -1;
+	}
+	if (identifier_check(zName, len) != 0) {
+		pParse->is_aborted = true;
+		return -1;
 	}
-	return SQL_OK;
+	return 0;
 }
 
 /**
@@ -334,7 +343,7 @@ sqlStartTable(Parse *pParse, Token *pName, int noErr)
 	pParse->sNameToken = *pName;
 	if (zName == 0)
 		return;
-	if (sqlCheckIdentifierName(pParse, zName) != SQL_OK)
+	if (sqlCheckIdentifierName(pParse, zName) != 0)
 		goto cleanup;
 
 	struct space *space = space_by_name(zName);
@@ -432,7 +441,9 @@ sqlAddColumn(Parse * pParse, Token * pName, struct type_def *type_def)
 
 #if SQL_MAX_COLUMN
 	if ((int)def->field_count + 1 > db->aLimit[SQL_LIMIT_COLUMN]) {
-		sqlErrorMsg(pParse, "too many columns on %s", def->name);
+		diag_set(ClientError, ER_SQL_COLUMN_COUNT_MAX, def->name,
+			 def->field_count + 1, db->aLimit[SQL_LIMIT_COLUMN]);
+		pParse->is_aborted = true;
 		return;
 	}
 #endif
@@ -521,9 +532,12 @@ sqlAddDefaultValue(Parse * pParse, ExprSpan * pSpan)
 		struct space_def *def = pParse->new_space->def;
 		if (!sqlExprIsConstantOrFunction
 		    (pSpan->pExpr, db->init.busy)) {
-			sqlErrorMsg(pParse,
-					"default value of column [%s] is not constant",
-					def->fields[def->field_count - 1].name);
+			const char *column_name =
+				def->fields[def->field_count - 1].name;
+			diag_set(ClientError, ER_CREATE_SPACE, def->name,
+				 tt_sprintf("default value of column '%s' is "\
+					    "not constant", column_name));
+			pParse->is_aborted = true;
 		} else {
 			assert(def != NULL);
 			struct field_def *field =
@@ -588,9 +602,9 @@ sqlAddPrimaryKey(Parse * pParse,	/* Parsing context */
 	if (space == NULL)
 		goto primary_key_exit;
 	if (sql_space_primary_key(space) != NULL) {
-		sqlErrorMsg(pParse,
-				"table \"%s\" has more than one primary key",
-				space->def->name);
+		diag_set(ClientError, ER_CREATE_SPACE, space->def->name,
+			 "primary key has been already declared");
+		pParse->is_aborted = true;
 		goto primary_key_exit;
 	}
 	if (pList == NULL) {
@@ -603,8 +617,9 @@ sqlAddPrimaryKey(Parse * pParse,	/* Parsing context */
 			    sqlExprSkipCollate(pList->a[i].pExpr);
 			assert(pCExpr != 0);
 			if (pCExpr->op != TK_ID) {
-				sqlErrorMsg(pParse, "expressions prohibited"
-							" in PRIMARY KEY");
+				diag_set(ClientError, ER_INDEX_DEF_UNSUPPORTED,
+					 "Expressions");
+				pParse->is_aborted = true;
 				goto primary_key_exit;
 			}
 			const char *name = pCExpr->u.zToken;
@@ -636,8 +651,10 @@ sqlAddPrimaryKey(Parse * pParse,	/* Parsing context */
 		if (db->mallocFailed)
 			goto primary_key_exit;
 	} else if (autoInc) {
-		sqlErrorMsg(pParse, "AUTOINCREMENT is only allowed on an "
-				"INTEGER PRIMARY KEY or INT PRIMARY KEY");
+		diag_set(ClientError, ER_CREATE_SPACE, space->def->name,
+			 "AUTOINCREMENT is only allowed on an INTEGER PRIMARY "\
+			 "KEY or INT PRIMARY KEY");
+		pParse->is_aborted = true;
 		goto primary_key_exit;
 	} else {
 		sql_create_index(pParse, 0, 0, pList, 0, sortOrder, false,
@@ -1144,9 +1161,10 @@ sqlEndTable(Parse * pParse,	/* Parse context */
 
 	if (!new_space->def->opts.is_view) {
 		if (sql_space_primary_key(new_space) == NULL) {
-			sqlErrorMsg(pParse,
-					"PRIMARY KEY missing on table %s",
-					new_space->def->name);
+			diag_set(ClientError, ER_CREATE_SPACE,
+				 new_space->def->name,
+				 "PRIMARY KEY missing");
+			pParse->is_aborted = true;
 			goto cleanup;
 		}
 	}
@@ -1264,8 +1282,10 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
 {
 	struct sql *db = parse_context->db;
 	if (parse_context->nVar > 0) {
-		sqlErrorMsg(parse_context,
-				"parameters are not allowed in views");
+		diag_set(ClientError, ER_CREATE_SPACE,
+			 sqlNameFromToken(db, name),
+			 "parameters are not allowed in views");
+		parse_context->is_aborted = true;
 		goto create_view_fail;
 	}
 	sqlStartTable(parse_context, name, if_exists);
@@ -1279,10 +1299,10 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
 		goto create_view_fail;
 	if (aliases != NULL) {
 		if ((int)select_res_space->def->field_count != aliases->nExpr) {
-			sqlErrorMsg(parse_context, "expected %d columns "\
-					"for '%s' but got %d", aliases->nExpr,
-					space->def->name,
-					select_res_space->def->field_count);
+			diag_set(ClientError, ER_CREATE_SPACE, space->def->name,
+				 "number of aliases doesn't match provided "\
+				 "columns");
+			parse_context->is_aborted = true;
 			goto create_view_fail;
 		}
 		sqlColumnsFromExprList(parse_context, aliases, space->def);
@@ -1604,13 +1624,15 @@ sql_drop_table(struct Parse *parse_context, struct SrcList *table_name_list,
 	 * and DROP VIEW is not used on a table.
 	 */
 	if (is_view && !space->def->opts.is_view) {
-		sqlErrorMsg(parse_context, "use DROP TABLE to delete table %s",
-				space_name);
+		diag_set(ClientError, ER_DROP_SPACE, space_name,
+			 "use DROP TABLE");
+		parse_context->is_aborted = true;
 		goto exit_drop_table;
 	}
 	if (!is_view && space->def->opts.is_view) {
-		sqlErrorMsg(parse_context, "use DROP VIEW to delete view %s",
-				space_name);
+		diag_set(ClientError, ER_DROP_SPACE, space_name,
+			 "use DROP VIEW");
+		parse_context->is_aborted = true;
 		goto exit_drop_table;
 	}
 	/*
@@ -1753,12 +1775,6 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child,
 			diag_set(ClientError, ER_NO_SUCH_SPACE, parent_name);;
 			goto tnt_error;
 		}
-	} else {
-		if (parent_space->def->opts.is_view) {
-			sqlErrorMsg(parse_context,
-					"referenced table can't be view");
-			goto exit_create_fk;
-		}
 	}
 	if (constraint == NULL && !is_alter) {
 		if (parse_context->constraintName.n == 0) {
@@ -1775,6 +1791,11 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child,
 	}
 	if (constraint_name == NULL)
 		goto exit_create_fk;
+	if (!is_self_referenced && parent_space->def->opts.is_view) {
+		diag_set(ClientError, ER_CREATE_FK_CONSTRAINT, constraint_name,
+			"referenced space can't be VIEW");
+		goto tnt_error;
+	}
 	const char *error_msg = "number of columns in foreign key does not "
 				"match the number of columns in the primary "
 				"index of referenced table";
@@ -2144,7 +2165,10 @@ sql_create_index(struct Parse *parse, struct Token *token,
 	struct space_def *def = space->def;
 
 	if (def->opts.is_view) {
-		sqlErrorMsg(parse, "views can not be indexed");
+		diag_set(ClientError, ER_MODIFY_INDEX,
+			 sqlNameFromToken(db, token), def->name,
+			 "views can not be indexed");
+		parse->is_aborted = true;
 		goto exit_create_index;
 	}
 	/*
@@ -2245,7 +2269,12 @@ sql_create_index(struct Parse *parse, struct Token *token,
 		assert(col_list->nExpr == 1);
 		sqlExprListSetSortOrder(col_list, sort_order);
 	} else {
-		sqlExprListCheckLength(parse, col_list, "index");
+		if (col_list->nExpr > db->aLimit[SQL_LIMIT_COLUMN]) {
+			diag_set(ClientError, ER_SQL_PARSER_LIMIT,
+				 "The number of columns in index",
+				 col_list->nExpr, db->aLimit[SQL_LIMIT_COLUMN]);
+			parse->is_aborted = true;
+		}
 	}
 
 	index = (struct index *) region_alloc(&parse->region, sizeof(*index));
@@ -2942,11 +2971,8 @@ sqlSavepoint(Parse * pParse, int op, Token * pName)
 			return;
 		}
 		if (op == SAVEPOINT_BEGIN &&
-			sqlCheckIdentifierName(pParse, zName)
-				!= SQL_OK) {
-			sqlErrorMsg(pParse, "bad savepoint name");
+		    sqlCheckIdentifierName(pParse, zName) != 0)
 			return;
-		}
 		sqlVdbeAddOp4(v, OP_Savepoint, op, 0, 0, zName, P4_DYNAMIC);
 	}
 }
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index f4d0334..0eb28d7 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -110,8 +110,8 @@ sql_table_truncate(struct Parse *parse, struct SrcList *tab_list)
 	}
 	if (space->def->opts.is_view) {
 		const char *err_msg =
-			tt_sprintf("can not truncate space '%s' because it is "
-				   "a view", space->def->name);
+			tt_sprintf("can not truncate space '%s' because space "\
+				   "is a view", space->def->name);
 		diag_set(ClientError, ER_SQL, err_msg);
 		goto tarantool_error;
 	}
@@ -163,8 +163,9 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 			goto delete_from_cleanup;
 
 		if (trigger_list == NULL) {
-			sqlErrorMsg(parse, "cannot modify %s because it is a"
-					" view", space->def->name);
+			diag_set(ClientError, ER_ALTER_SPACE, space->def->name,
+				 "space is a view");
+			parse->is_aborted = true;
 			goto delete_from_cleanup;
 		}
 	}
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index a2c7093..de993c1 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -746,19 +746,24 @@ codeVectorCompare(Parse * pParse,	/* Code generator context */
  * Check that argument nHeight is less than or equal to the maximum
  * expression depth allowed. If it is not, leave an error message in
  * pParse.
+ *
+ * @param pParse Parser context.
+ * @param zName Depth to check.
+ *
+ * @retval 0 on success.
+ * @retval -1 on error.
  */
 int
 sqlExprCheckHeight(Parse * pParse, int nHeight)
 {
-	int rc = SQL_OK;
 	int mxHeight = pParse->db->aLimit[SQL_LIMIT_EXPR_DEPTH];
 	if (nHeight > mxHeight) {
-		sqlErrorMsg(pParse,
-				"Expression tree is too large (maximum depth %d)",
-				mxHeight);
-		rc = SQL_ERROR;
+		diag_set(ClientError, ER_SQL_PARSER_LIMIT, "Number of nodes "\
+			 "in expression tree", nHeight, mxHeight);
+		pParse->is_aborted = true;
+		return -1;
 	}
-	return rc;
+	return 0;
 }
 
 /* The following three functions, heightOfExpr(), heightOfExprList()
@@ -1197,9 +1202,9 @@ sqlExprAssignVarNumber(Parse * pParse, Expr * pExpr, u32 n)
 			testcase(i == SQL_BIND_PARAMETER_MAX - 1);
 			testcase(i == SQL_BIND_PARAMETER_MAX);
 			if (!is_ok || i < 1 || i > SQL_BIND_PARAMETER_MAX) {
-				sqlErrorMsg(pParse,
-						"variable number must be between $1 and $%d",
-						SQL_BIND_PARAMETER_MAX);
+				diag_set(ClientError, ER_SQL_BIND_PARAMETER_MAX,
+					 SQL_BIND_PARAMETER_MAX);
+				pParse->is_aborted = true;
 				return;
 			}
 			if (x > pParse->nVar) {
@@ -1227,7 +1232,9 @@ sqlExprAssignVarNumber(Parse * pParse, Expr * pExpr, u32 n)
 	}
 	pExpr->iColumn = x;
 	if (x > SQL_BIND_PARAMETER_MAX) {
-		sqlErrorMsg(pParse, "too many SQL variables");
+		diag_set(ClientError, ER_SQL_BIND_PARAMETER_MAX,
+			 SQL_BIND_PARAMETER_MAX);
+		pParse->is_aborted = true;
 	}
 }
 
@@ -1907,22 +1914,6 @@ sqlExprListSetSpan(Parse * pParse,	/* Parsing context */
 }
 
 /*
- * If the expression list pEList contains more than iLimit elements,
- * leave an error message in pParse.
- */
-void
-sqlExprListCheckLength(Parse * pParse,
-			   ExprList * pEList, const char *zObject)
-{
-	int mx = pParse->db->aLimit[SQL_LIMIT_COLUMN];
-	testcase(pEList && pEList->nExpr == mx);
-	testcase(pEList && pEList->nExpr == mx + 1);
-	if (pEList && pEList->nExpr > mx) {
-		sqlErrorMsg(pParse, "too many columns in %s", zObject);
-	}
-}
-
-/*
  * Delete an entire expression list.
  */
 static SQL_NOINLINE void
@@ -3355,15 +3346,15 @@ expr_code_int(struct Parse *parse, struct Expr *expr, bool is_neg,
 		int c = sql_dec_or_hex_to_i64(z, &value);
 		if (c == 1 || (c == 2 && !is_neg) ||
 		    (is_neg && value == SMALLEST_INT64)) {
+			const char *sign = is_neg ? "-" : "";
 			if (sql_strnicmp(z, "0x", 2) == 0) {
-				sqlErrorMsg(parse,
-						"hex literal too big: %s%s",
-						is_neg ? "-" : "", z);
+				diag_set(ClientError, ER_HEX_LITERAL_MAX, sign,
+					 z, strlen(z) - 2, 16);
 			} else {
-				sqlErrorMsg(parse,
-						"oversized integer: %s%s",
-						is_neg ? "-" : "", z);
+				diag_set(ClientError, ER_INT_LITERAL_MAX, sign,
+					 z, INT64_MIN, INT64_MAX);
 			}
+			parse->is_aborted = true;
 		} else {
 			if (is_neg)
 				value = c == 2 ? SMALLEST_INT64 : -value;
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 6f7f020..0ca38ae 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -322,8 +322,9 @@ sqlInsert(Parse * pParse,	/* Parser context */
 
 	/* Cannot insert into a read-only table. */
 	if (is_view && tmask == 0) {
-		sqlErrorMsg(pParse, "cannot modify %s because it is a view",
-				space_def->name);
+		diag_set(ClientError, ER_ALTER_SPACE, space->def->name,
+			 "space is a view");
+		pParse->is_aborted = true;
 		goto insert_cleanup;
 	}
 
@@ -388,9 +389,10 @@ sqlInsert(Parse * pParse,	/* Parser context */
 				}
 			}
 			if (j >= (int) space_def->field_count) {
-				sqlErrorMsg(pParse,
-						"table %S has no column named %s",
-						pTabList, 0, pColumn->a[i].zName);
+				diag_set(ClientError, ER_NO_SUCH_FIELD_NAME,
+					 pColumn->a[i].zName,
+					 pTabList->a[0].zName);
+				pParse->is_aborted = true;
 				goto insert_cleanup;
 			}
 			if (bit_test(used_columns, j)) {
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index dfbb006..a141065 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -410,9 +410,10 @@ cmd ::= select(X).  {
         (mxSelect = pParse->db->aLimit[SQL_LIMIT_COMPOUND_SELECT])>0 &&
         cnt>mxSelect
       ){
-        sqlErrorMsg(pParse, "Too many UNION or EXCEPT or INTERSECT "
-                        "operations (limit %d is set)",
-                        pParse->db->aLimit[SQL_LIMIT_COMPOUND_SELECT]);
+         diag_set(ClientError, ER_SQL_PARSER_LIMIT, "The number of UNION or "\
+                  "EXCEPT or INTERSECT operations", cnt,
+                  pParse->db->aLimit[SQL_LIMIT_COMPOUND_SELECT]);
+         pParse->is_aborted = true;
       }
     }
   }
@@ -744,7 +745,11 @@ cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
         where_opt(W).  {
   sqlWithPush(pParse, C, 1);
   sqlSrcListIndexedBy(pParse, X, &I);
-  sqlExprListCheckLength(pParse,Y,"set list");
+  if (Y != NULL && Y->nExpr > pParse->db->aLimit[SQL_LIMIT_COLUMN]) {
+    diag_set(ClientError, ER_SQL_PARSER_LIMIT, "The number of columns in set "\
+             "list", Y->nExpr, pParse->db->aLimit[SQL_LIMIT_COLUMN]);
+    pParse->is_aborted = true;
+  }
   sqlSubProgramsRemaining = SQL_MAX_COMPILING_TRIGGERS;
   /* Instruct SQL to initate Tarantool's transaction.  */
   pParse->initiateTTrans = true;
@@ -932,7 +937,11 @@ expr(A) ::= CAST(X) LP expr(E) AS typedef(T) RP(Y). {
 %endif  SQL_OMIT_CAST
 expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP(E). {
   if( Y && Y->nExpr>pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG] ){
-    sqlErrorMsg(pParse, "too many arguments on function %T", &X);
+    const char *err =
+      tt_sprintf("Number of arguments to function %.*s", X.n, X.z);
+    diag_set(ClientError, ER_SQL_PARSER_LIMIT, err, Y->nExpr,
+             pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]);
+    pParse->is_aborted = true;
   }
   A.pExpr = sqlExprFunction(pParse, Y, &X);
   spanSet(&A,&X,&E);
@@ -948,7 +957,11 @@ expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP(E). {
 type_func(A) ::= CHAR(A) .
 expr(A) ::= type_func(X) LP distinct(D) exprlist(Y) RP(E). {
   if( Y && Y->nExpr>pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG] ){
-    sqlErrorMsg(pParse, "too many arguments on function %T", &X);
+    const char *err =
+      tt_sprintf("Number of arguments to function %.*s", X.n, X.z);
+    diag_set(ClientError, ER_SQL_PARSER_LIMIT, err, Y->nExpr,
+             pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]);
+    pParse->is_aborted = true;
   }
   A.pExpr = sqlExprFunction(pParse, Y, &X);
   spanSet(&A,&X,&E);
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 94bb0af..30a6b5f 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -510,30 +510,6 @@ sqlCreateColumnExpr(sql * db, SrcList * pSrc, int iSrc, int iCol)
 }
 
 /*
- * Report an error that an expression is not valid for some set of
- * pNC->ncFlags values determined by validMask.
- */
-static void
-notValid(Parse * pParse,	/* Leave error message here */
-	 NameContext * pNC,	/* The name context */
-	 const char *zMsg,	/* Type of error */
-	 int validMask		/* Set of contexts for which prohibited */
-    )
-{
-	assert((validMask & ~(NC_IsCheck | NC_IdxExpr)) == 0);
-	if ((pNC->ncFlags & validMask) != 0) {
-		const char *zIn;
-		if (pNC->ncFlags & NC_IdxExpr)
-			zIn = "index expressions";
-		else if (pNC->ncFlags & NC_IsCheck)
-			zIn = "CHECK constraints";
-		else
-			unreachable();
-		sqlErrorMsg(pParse, "%s prohibited in %s", zMsg, zIn);
-	}
-}
-
-/*
  * Expression p should encode a floating point value between 1.0 and 0.0.
  * Return 1024 times this value.  Or return -1 if p is not a floating point
  * value between 1.0 and 0.0.
@@ -605,7 +581,11 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 			Expr *pRight;
 
 			/* if( pSrcList==0 ) break; */
-			notValid(pParse, pNC, "the \".\" operator", NC_IdxExpr);
+			if (pNC->ncFlags & NC_IdxExpr) {
+				diag_set(ClientError, ER_INDEX_DEF_UNSUPPORTED,
+					 "Expressions");
+				pParse->is_aborted = true;
+			}
 			pRight = pExpr->pRight;
 			if (pRight->op == TK_ID) {
 				zTable = pExpr->pLeft->u.zToken;
@@ -646,6 +626,9 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 			} else {
 				is_agg = pDef->xFinalize != 0;
 				pExpr->type = pDef->ret_type;
+				const char *err_msg =
+					"second argument to likelihood() must "\
+					"be a constant between 0.0 and 1.0";
 				if (pDef->funcFlags & SQL_FUNC_UNLIKELY) {
 					ExprSetProperty(pExpr,
 							EP_Unlikely | EP_Skip);
@@ -654,9 +637,11 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 						    exprProbability(pList->a[1].
 								    pExpr);
 						if (pExpr->iTable < 0) {
-							sqlErrorMsg(pParse,
-									"second argument to likelihood() must be a "
-									"constant between 0.0 and 1.0");
+							diag_set(ClientError,
+								 ER_ILLEGAL_PARAMS,
+								 err_msg);
+							pParse->is_aborted =
+								true;
 							pNC->nErr++;
 						}
 					} else {
@@ -690,9 +675,7 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 					 * that might change over time cannot be used
 					 * in an index.
 					 */
-					notValid(pParse, pNC,
-						 "non-deterministic functions",
-						 NC_IdxExpr);
+					assert((pNC->ncFlags & NC_IdxExpr) == 0);
 				}
 			}
 			if (is_agg && (pNC->ncFlags & NC_AllowAgg) == 0) {
@@ -754,8 +737,13 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 			testcase(pExpr->op == TK_IN);
 			if (ExprHasProperty(pExpr, EP_xIsSelect)) {
 				int nRef = pNC->nRef;
-				notValid(pParse, pNC, "subqueries",
-					 NC_IsCheck | NC_IdxExpr);
+				assert((pNC->ncFlags & NC_IdxExpr) == 0);
+				if (pNC->ncFlags & NC_IsCheck) {
+					diag_set(ClientError,
+						 ER_CK_DEF_UNSUPPORTED,
+						 "Subqueries");
+					pParse->is_aborted = true;
+				}
 				sqlWalkSelect(pWalker, pExpr->x.pSelect);
 				assert(pNC->nRef >= nRef);
 				if (nRef != pNC->nRef) {
@@ -766,8 +754,12 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 			break;
 		}
 	case TK_VARIABLE:{
-			notValid(pParse, pNC, "parameters",
-				 NC_IsCheck | NC_IdxExpr);
+			assert((pNC->ncFlags & NC_IsCheck) == 0);
+			if (pNC->ncFlags & NC_IdxExpr) {
+				diag_set(ClientError, ER_INDEX_DEF_UNSUPPORTED,
+					 "Parameter markers");
+				pParse->is_aborted = true;
+			}
 			break;
 		}
 	case TK_BETWEEN:
@@ -952,7 +944,10 @@ resolveCompoundOrderBy(Parse * pParse,	/* Parsing context.  Leave error messages
 	db = pParse->db;
 #if SQL_MAX_COLUMN
 	if (pOrderBy->nExpr > db->aLimit[SQL_LIMIT_COLUMN]) {
-		sqlErrorMsg(pParse, "too many terms in ORDER BY clause");
+		diag_set(ClientError, ER_SQL_PARSER_LIMIT,
+			 "The number of terms in ORDER BY clause",
+			 pOrderBy->nExpr, db->aLimit[SQL_LIMIT_COLUMN]);
+		pParse->is_aborted = true;
 		return 1;
 	}
 #endif
@@ -1042,8 +1037,7 @@ resolveCompoundOrderBy(Parse * pParse,	/* Parsing context.  Leave error messages
  * field) then convert that term into a copy of the corresponding result set
  * column.
  *
- * If any errors are detected, add an error message to pParse and
- * return non-zero.  Return zero if no errors are seen.
+ * @retval 0 On success, not 0 elsewhere.
  */
 int
 sqlResolveOrderGroupBy(Parse * pParse,	/* Parsing context.  Leave error messages here */
@@ -1061,8 +1055,14 @@ sqlResolveOrderGroupBy(Parse * pParse,	/* Parsing context.  Leave error messages
 		return 0;
 #if SQL_MAX_COLUMN
 	if (pOrderBy->nExpr > db->aLimit[SQL_LIMIT_COLUMN]) {
-		sqlErrorMsg(pParse, "too many terms in %s BY clause",
-				zType);
+		const char *err_msg;
+		if (zType[0] != 'G')
+			err_msg = "The number of terms in ORDER BY clause";
+		else
+			err_msg = "The number of terms in GROUP BY clause";
+		diag_set(ClientError, ER_SQL_PARSER_LIMIT, err_msg,
+			 pOrderBy->nExpr, db->aLimit[SQL_LIMIT_COLUMN]);
+		pParse->is_aborted = true;
 		return 1;
 	}
 #endif
@@ -1096,9 +1096,7 @@ sqlResolveOrderGroupBy(Parse * pParse,	/* Parsing context.  Leave error messages
  * result-set expression.  Otherwise, the expression is resolved in
  * the usual way - using sqlResolveExprNames().
  *
- * This routine returns the number of errors.  If errors occur, then
- * an appropriate error message might be left in pParse.  (OOM errors
- * excepted.)
+ * @retval 0 On success, not 0 elsewhere.
  */
 static int
 resolveOrderGroupBy(NameContext * pNC,	/* The name context of the SELECT statement */
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 5195656..23c9499 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -5064,7 +5064,10 @@ selectExpander(Walker * pWalker, Select * p)
 	}
 #if SQL_MAX_COLUMN
 	if (p->pEList && p->pEList->nExpr > db->aLimit[SQL_LIMIT_COLUMN]) {
-		sqlErrorMsg(pParse, "too many columns in result set");
+		diag_set(ClientError, ER_SQL_PARSER_LIMIT, "The number of "\
+			 "columns in result set", p->pEList->nExpr,
+			 db->aLimit[SQL_LIMIT_COLUMN]);
+		pParse->is_aborted = true;
 		return WRC_Abort;
 	}
 #endif
@@ -5525,10 +5528,10 @@ sqlSelect(Parse * pParse,		/* The parser context */
 		 * columns in the SELECT on the RHS
 		 */
 		if ((int)space->def->field_count != pSub->pEList->nExpr) {
-			sqlErrorMsg(pParse,
-					"expected %d columns for '%s' but got %d",
-					space->def->field_count, space->def->name,
-					pSub->pEList->nExpr);
+			diag_set(ClientError, ER_CREATE_SPACE, space->def->name,
+				 "number of aliases doesn't match provided "\
+				 "columns");
+			pParse->is_aborted = true;
 			goto select_end;
 		}
 
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index aebd131..8fb2b18 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -4648,7 +4648,6 @@ sql_int64 sqlStmtCurrentTime(sql_context *);
 int sqlVdbeParameterIndex(Vdbe *, const char *, int);
 int sqlTransferBindings(sql_stmt *, sql_stmt *);
 int sqlReprepare(Vdbe *);
-void sqlExprListCheckLength(Parse *, ExprList *, const char *);
 
 /**
  * This function verifies that two collations (to be more precise
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index de0f282..efb895f 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -481,8 +481,8 @@ sqlRunParser(Parse * pParse, const char *zSql)
 				      &pParse->sLastToken.isReserved);
 			i += pParse->sLastToken.n;
 			if (i > mxSqlLen) {
-				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
-					 "string or blob too big");
+				diag_set(ClientError, ER_SQL_PARSER_LIMIT,
+					 "SQL command length", i, mxSqlLen);
 				pParse->is_aborted = true;
 				break;
 			}
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index 7eacd33..b23d60a 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -86,7 +86,7 @@ sql_trigger_begin(struct Parse *parse, struct Token *name, int tr_tm,
 	if (trigger_name == NULL)
 		goto trigger_cleanup;
 
-	if (sqlCheckIdentifierName(parse, trigger_name) != SQL_OK)
+	if (sqlCheckIdentifierName(parse, trigger_name) != 0)
 		goto trigger_cleanup;
 
 	const char *table_name = table->a[0].zName;
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 05ceeb4..71a1e00 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -139,8 +139,9 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 		goto update_cleanup;
 	}
 	if (is_view && tmask == 0) {
-		sqlErrorMsg(pParse, "cannot modify %s because it is a view",
-				space->def->name);
+		diag_set(ClientError, ER_ALTER_SPACE, space->def->name,
+			 "space is a view");
+		pParse->is_aborted = true;
 		goto update_cleanup;
 	}
 
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index ebc2624..2f83e81 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -4286,7 +4286,9 @@ sqlWhereBegin(Parse * pParse,	/* The parser context */
 	 */
 	testcase(pTabList->nSrc == BMS);
 	if (pTabList->nSrc > BMS) {
-		sqlErrorMsg(pParse, "at most %d tables in a join", BMS);
+		diag_set(ClientError, ER_SQL_PARSER_LIMIT, "The number of "\
+			 "tables in a join", pTabList->nSrc, BMS);
+		pParse->is_aborted = true;
 		return 0;
 	}
 
diff --git a/test/box/alter.result b/test/box/alter.result
index 9a1086e..37bc51c 100644
--- a/test/box/alter.result
+++ b/test/box/alter.result
@@ -367,7 +367,7 @@ s:drop()
 ...
 box.schema.space.create('')
 ---
-- error: Invalid identifier '' (expected printable symbols only)
+- error: Invalid identifier '' (expected printable symbols only or it is too long)
 ...
 -- valid identifiers
 box.schema.space.create('_Abcde'):drop()
@@ -438,7 +438,7 @@ i:drop()
 ...
 space:create_index('')
 ---
-- error: Invalid identifier '' (expected printable symbols only)
+- error: Invalid identifier '' (expected printable symbols only or it is too long)
 ...
 space:drop()
 ---
diff --git a/test/box/misc.result b/test/box/misc.result
index c350bbd..5dda752 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -516,6 +516,12 @@ t;
   185: box.error.SQL_UNKNOWN_TOKEN
   186: box.error.SQL_PARSER_GENERIC
   187: box.error.SQL_ANALYZE_ARGUMENT
+  188: box.error.SQL_COLUMN_COUNT_MAX
+  189: box.error.HEX_LITERAL_MAX
+  190: box.error.INT_LITERAL_MAX
+  191: box.error.SQL_PARSER_LIMIT
+  192: box.error.INDEX_DEF_UNSUPPORTED
+  193: box.error.CK_DEF_UNSUPPORTED
 ...
 test_run:cmd("setopt delimiter ''");
 ---
diff --git a/test/sql-tap/autoinc.test.lua b/test/sql-tap/autoinc.test.lua
index dc2f60e..7abb679 100755
--- a/test/sql-tap/autoinc.test.lua
+++ b/test/sql-tap/autoinc.test.lua
@@ -561,7 +561,7 @@ test:do_catchsql_test(
         CREATE TABLE t8(x TEXT PRIMARY KEY AUTOINCREMENT);
     ]], {
         -- <autoinc-7.2>
-        1, "AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY or INT PRIMARY KEY"
+        1, "Failed to create space 'T8': AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY or INT PRIMARY KEY"
         -- </autoinc-7.2>
     })
 
diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
index 0d8bf15..dd2de92 100755
--- a/test/sql-tap/check.test.lua
+++ b/test/sql-tap/check.test.lua
@@ -319,7 +319,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <check-3.1>
-        1, "Failed to create space 'T3': subqueries prohibited in CHECK constraints"
+        1, "Failed to create space 'T3': Subqueries are prohibited in a CHECK constraint definition"
         -- </check-3.1>
     })
 
diff --git a/test/sql-tap/colname.test.lua b/test/sql-tap/colname.test.lua
index 29fdf13..253497c 100755
--- a/test/sql-tap/colname.test.lua
+++ b/test/sql-tap/colname.test.lua
@@ -637,7 +637,7 @@ test:do_test(
 test:do_catchsql_test(
     "colname-11.1",
     [[ create table t1(a INT, b INT, c INT, primary key('A'))]],
-    {1, "expressions prohibited in PRIMARY KEY"})
+    {1, "Expressions are prohibited in an index definition"})
 
 test:do_catchsql_test(
     "colname-11.2",
diff --git a/test/sql-tap/default.test.lua b/test/sql-tap/default.test.lua
index 67151b0..564d373 100755
--- a/test/sql-tap/default.test.lua
+++ b/test/sql-tap/default.test.lua
@@ -66,7 +66,7 @@ test:do_catchsql_test(
 	);
 	]], {
 	-- <default-1.3>
-	1, "default value of column [Y] is not constant"
+	1, "Failed to create space 'T3': default value of column 'Y' is not constant"
 	-- </default-1.3>
 })
 
@@ -173,7 +173,7 @@ test:do_catchsql_test(
 	);
 	]], {
 	-- <default-4.2>
-	1, "default value of column [B] is not constant"
+	1, "Failed to create space 'T2': default value of column 'B' is not constant"
 	-- </default-4.2>
 })
 
@@ -187,7 +187,7 @@ test:do_catchsql_test(
 	);
 	]], {
 	-- <default-4.3>
-	1, "default value of column [B] is not constant"
+	1, "Failed to create space 'T2': default value of column 'B' is not constant"
 	-- </default-4.3>
 })
 
@@ -201,7 +201,7 @@ test:do_catchsql_test(
 	);
 	]], {
 	-- <default-4.4>
-	1, "default value of column [B] is not constant"
+	1, "Failed to create space 'T2': default value of column 'B' is not constant"
 	-- </default-4.4>
 })
 
@@ -214,7 +214,7 @@ test:do_catchsql_test(
         CREATE TABLE t6(id INTEGER PRIMARY KEY, b TEXT DEFAULT(id));
     ]], {
     -- <default-5.1>
-    1, "default value of column [B] is not constant"
+    1, "Failed to create space 'T6': default value of column 'B' is not constant"
     -- </default-5.1>
 })
 
diff --git a/test/sql-tap/fkey2.test.lua b/test/sql-tap/fkey2.test.lua
index d347e5a..678b857 100755
--- a/test/sql-tap/fkey2.test.lua
+++ b/test/sql-tap/fkey2.test.lua
@@ -729,7 +729,7 @@ test:do_catchsql_test(
         CREATE TABLE c(x  INT PRIMARY KEY REFERENCES v(y));
     ]], {
         -- <fkey2-7.2>
-        1, "referenced table can't be view"
+        1, "Failed to create foreign key constraint 'FK_CONSTRAINT_1_C': referenced space can't be VIEW"
         -- </fkey2-7.2>
     })
 
@@ -1157,7 +1157,7 @@ test:do_catchsql_test(
         CREATE TABLE t1(x INT PRIMARY KEY REFERENCES v);
     ]], {
         -- <fkey2-10.20>
-        1, "referenced table can't be view"
+        1, "Failed to create foreign key constraint 'FK_CONSTRAINT_1_T1': referenced space can't be VIEW"
         -- </fkey2-10.20>
     })
 
diff --git a/test/sql-tap/func3.test.lua b/test/sql-tap/func3.test.lua
index 4ea9a5c..6d6411c 100755
--- a/test/sql-tap/func3.test.lua
+++ b/test/sql-tap/func3.test.lua
@@ -104,7 +104,7 @@ test:do_catchsql_test(
         SELECT likelihood(123, 1.000001);
     ]], {
         -- <func3-5.8>
-        1, "second argument to likelihood() must be a constant between 0.0 and 1.0"
+        1, "Illegal parameters, second argument to likelihood() must be a constant between 0.0 and 1.0"
         -- </func3-5.8>
     })
 
@@ -114,7 +114,7 @@ test:do_catchsql_test(
         SELECT likelihood(123, -0.000001);
     ]], {
         -- <func3-5.9>
-        1, "second argument to likelihood() must be a constant between 0.0 and 1.0"
+        1, "Illegal parameters, second argument to likelihood() must be a constant between 0.0 and 1.0"
         -- </func3-5.9>
     })
 
@@ -124,7 +124,7 @@ test:do_catchsql_test(
         SELECT likelihood(123, 0.5+0.3);
     ]], {
         -- <func3-5.10>
-        1, "second argument to likelihood() must be a constant between 0.0 and 1.0"
+        1, "Illegal parameters, second argument to likelihood() must be a constant between 0.0 and 1.0"
         -- </func3-5.10>
     })
 
diff --git a/test/sql-tap/gh-2549-many-columns.test.lua b/test/sql-tap/gh-2549-many-columns.test.lua
index 3de4d67..ed8c9d9 100755
--- a/test/sql-tap/gh-2549-many-columns.test.lua
+++ b/test/sql-tap/gh-2549-many-columns.test.lua
@@ -35,7 +35,7 @@ test:do_catchsql_test(
 	"columns-1.2",
 	fail_statement, {
 		-- <columns-1.2>
-		1, "too many columns on T2"
+		1, "Failed to create space 'T2': space column count 2001 exceeds the limit (2000)"
 		-- <columns-1.2>
 	})
 
diff --git a/test/sql-tap/gh2548-select-compound-limit.test.lua b/test/sql-tap/gh2548-select-compound-limit.test.lua
index 5494a66..e8c8d95 100755
--- a/test/sql-tap/gh2548-select-compound-limit.test.lua
+++ b/test/sql-tap/gh2548-select-compound-limit.test.lua
@@ -58,7 +58,7 @@ test:do_catchsql_test(
     "gh2548-select-compound-limit-2",
     select_string_last, {
         -- <gh2548-select-compound-limit-2>
-        1, "Too many UNION or EXCEPT or INTERSECT operations (limit 30 is set)"
+        1, "The number of UNION or EXCEPT or INTERSECT operations 31 exceeds the limit (30)"
         -- </gh2548-select-compound-limit-2>
     })
 
diff --git a/test/sql-tap/hexlit.test.lua b/test/sql-tap/hexlit.test.lua
index 158eda7..288d823 100755
--- a/test/sql-tap/hexlit.test.lua
+++ b/test/sql-tap/hexlit.test.lua
@@ -107,7 +107,7 @@ test:do_catchsql_test(
         SELECT 0x10000000000000000;
     ]], {
         -- <hexlist-400>
-        1, "hex literal too big: 0x10000000000000000"
+        1, "Hex literal 0x10000000000000000 length 17 exceeds the supported limit (16)"
         -- </hexlist-400>
     })
 
@@ -119,7 +119,7 @@ test:do_catchsql_test(
         INSERT INTO t1 VALUES(1+0x10000000000000000);
     ]], {
         -- <hexlist-410>
-        1, "hex literal too big: 0x10000000000000000"
+        1, "Hex literal 0x10000000000000000 length 17 exceeds the supported limit (16)"
         -- </hexlist-410>
     })
 
diff --git a/test/sql-tap/insert1.test.lua b/test/sql-tap/insert1.test.lua
index 363fa8a..031fd22 100755
--- a/test/sql-tap/insert1.test.lua
+++ b/test/sql-tap/insert1.test.lua
@@ -77,7 +77,7 @@ test:do_catchsql_test("insert-1.4", [[
   INSERT INTO test1(one,four) VALUES(1,2)
 ]], {
   -- <insert-1.4>
-  1, "table TEST1 has no column named FOUR"
+  1, "Field 'FOUR' was not found in the space 'TEST1' format"
   -- </insert-1.4>
 })
 
diff --git a/test/sql-tap/join.test.lua b/test/sql-tap/join.test.lua
index da29f77..ef60609 100755
--- a/test/sql-tap/join.test.lua
+++ b/test/sql-tap/join.test.lua
@@ -1067,11 +1067,11 @@ end
 jointest("join-12.2", 30, {0, {1}})
 jointest("join-12.3", 63, {0, {1}})
 jointest("join-12.4", 64, {0, {1}})
-jointest("join-12.5", 65, {1, 'at most 64 tables in a join'})
-jointest("join-12.6", 66, {1, 'at most 64 tables in a join'})
-jointest("join-12.7", 127, {1, 'at most 64 tables in a join'})
-jointest("join-12.8", 128, {1, 'at most 64 tables in a join'})
-jointest("join-12.9", 1000, {1, 'at most 64 tables in a join'})
+jointest("join-12.5", 65, {1, 'The number of tables in a join 65 exceeds the limit (64)'})
+jointest("join-12.6", 66, {1, 'The number of tables in a join 66 exceeds the limit (64)'})
+jointest("join-12.7", 127, {1, 'The number of tables in a join 127 exceeds the limit (64)'})
+jointest("join-12.8", 128, {1, 'The number of tables in a join 128 exceeds the limit (64)'})
+jointest("join-12.9", 1000, {1, 'The number of tables in a join 1000 exceeds the limit (64)'})
 -- If sql is built with sql_MEMDEBUG, then the huge number of realloc()
 -- calls made by the following test cases are too time consuming to run.
 -- Without sql_MEMDEBUG, realloc() is fast enough that these are not
@@ -1079,10 +1079,10 @@ jointest("join-12.9", 1000, {1, 'at most 64 tables in a join'})
 --if X(0, "X!capable", [["pragma&&compileoption_diags"]]) then
 --    if X(703, "X!cmd", [=[["expr","[lsearch [db eval {PRAGMA compile_options}] MEMDEBUG]<0"]]=])
 -- then
-jointest("join-12.10", 65534, {1, 'at most 64 tables in a join'})
-jointest("join-12.11", 65535, {1, 'at most 64 tables in a join'})
-jointest("join-12.12", 65536, {1, 'at most 64 tables in a join'})
-jointest("join-12.13", 65537, {1, 'at most 64 tables in a join'})
+jointest("join-12.10", 65534, {1, 'The number of tables in a join 65534 exceeds the limit (64)'})
+jointest("join-12.11", 65535, {1, 'The number of tables in a join 65535 exceeds the limit (64)'})
+jointest("join-12.12", 65536, {1, 'The number of tables in a join 65536 exceeds the limit (64)'})
+jointest("join-12.13", 65537, {1, 'The number of tables in a join 65537 exceeds the limit (64)'})
 --    end
 --end
 
diff --git a/test/sql-tap/join3.test.lua b/test/sql-tap/join3.test.lua
index 6b822de..876b312 100755
--- a/test/sql-tap/join3.test.lua
+++ b/test/sql-tap/join3.test.lua
@@ -85,7 +85,7 @@ test:do_test(
         return test:catchsql(sql)
     end, {
         -- <join3-3.1>
-        1, "at most "..bitmask_size.." tables in a join"
+        1, "The number of tables in a join " .. bitmask_size + 1 .. " exceeds the limit (".. bitmask_size ..")"
         -- </join3-3.1>
     })
 
diff --git a/test/sql-tap/misc1.test.lua b/test/sql-tap/misc1.test.lua
index 1b288da..dbc0699 100755
--- a/test/sql-tap/misc1.test.lua
+++ b/test/sql-tap/misc1.test.lua
@@ -348,7 +348,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <misc1-7.1>
-        1, [[table "ERROR1" has more than one primary key]]
+        1, [[Failed to create space 'ERROR1': primary key has been already declared]]
         -- </misc1-7.1>
     })
 
@@ -361,7 +361,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <misc1-7.2>
-        1, [[table "ERROR1" has more than one primary key]]
+        1, [[Failed to create space 'ERROR1': primary key has been already declared]]
         -- </misc1-7.2>
     })
 
@@ -897,7 +897,7 @@ test:do_catchsql_test(
         CREATE TABLE test2(a text primary key, b text, primary key(a,b));
     ]], {
         -- <misc1-16.3>
-        1, [[table "TEST2" has more than one primary key]]
+        1, [[Failed to create space 'TEST2': primary key has been already declared]]
         -- </misc1-16.3>
     })
 
diff --git a/test/sql-tap/select7.test.lua b/test/sql-tap/select7.test.lua
index 4029c20..7037d20 100755
--- a/test/sql-tap/select7.test.lua
+++ b/test/sql-tap/select7.test.lua
@@ -179,7 +179,7 @@ test:do_catchsql_test(
     "select7-6.2",
     sql, {
         -- <select7-6.2>
-        1, "Too many UNION or EXCEPT or INTERSECT operations (limit 30 is set)"
+        1, "The number of UNION or EXCEPT or INTERSECT operations 33 exceeds the limit (30)"
         -- </select7-6.2>
     })
 
diff --git a/test/sql-tap/sql-errors.test.lua b/test/sql-tap/sql-errors.test.lua
new file mode 100755
index 0000000..635df6a
--- /dev/null
+++ b/test/sql-tap/sql-errors.test.lua
@@ -0,0 +1,419 @@
+#!/usr/bin/env tarantool
+test = require("sqltester")
+test:plan(36)
+
+test:execsql([[
+	CREATE TABLE t0 (i INT PRIMARY KEY);
+	CREATE VIEW v0 AS SELECT * FROM t0;
+]])
+format = {}
+for i = 1, 2001 do format[i] = {name = 'A' .. i, type = 'unsigned'} end
+s0 = box.schema.space.create('S0', {format = format})
+i0 = s0:create_index('I0')
+
+test:do_catchsql_test(
+	"sql-errors-1.1",
+	[[
+		ANALYZE v0;
+	]], {
+		-- <sql-errors-1.1>
+		1,"Syntax error near 'ANALYZE'"
+		-- </sql-errors-1.1>
+	})
+
+create_statement = 'CREATE TABLE t2 (i INT PRIMARY KEY'
+for i = 1, 2001 do
+	create_statement = create_statement .. ', s' .. i .. ' INT'
+end
+create_statement = create_statement .. ');'
+
+test:do_catchsql_test(
+	"sql-errors-1.2",
+	create_statement,
+	{
+		-- <sql-errors-1.2>
+		1,"Failed to create space 'T2': space column count 2001 exceeds the limit (2000)"
+		-- </sql-errors-1.2>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.3",
+	[[
+		CREATE TABLE t3 (i INT PRIMARY KEY, a INT DEFAULT(MAX(i, 1)));
+	]], {
+		-- <sql-errors-1.3>
+		1,"Failed to create space 'T3': default value of column 'A' is not constant"
+		-- </sql-errors-1.3>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.4",
+	[[
+		CREATE TABLE t4 (i INT PRIMARY KEY, a INT PRIMARY KEY);
+	]], {
+		-- <sql-errors-1.4>
+		1,"Failed to create space 'T4': primary key has been already declared"
+		-- </sql-errors-1.4>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.5",
+	[[
+		CREATE TABLE t5 (i TEXT PRIMARY KEY AUTOINCREMENT);
+	]], {
+		-- <sql-errors-1.5>
+		1,"Failed to create space 'T5': AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY or INT PRIMARY KEY"
+		-- </sql-errors-1.5>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.6",
+	[[
+		CREATE TABLE t6 (i INT);
+	]], {
+		-- <sql-errors-1.6>
+		1,"Failed to create space 'T6': PRIMARY KEY missing"
+		-- </sql-errors-1.6>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.7",
+	[[
+		CREATE VIEW v7(a,b) AS SELECT * FROM t0;
+	]], {
+		-- <sql-errors-1.7>
+		1,"Failed to create space 'V7': number of aliases doesn't match provided columns"
+		-- </sql-errors-1.7>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.8",
+	[[
+		DROP VIEW t0;
+	]], {
+		-- <sql-errors-1.8>
+		1,"Can't drop space 'T0': use DROP TABLE"
+		-- </sql-errors-1.8>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.9",
+	[[
+		DROP TABLE v0;
+	]], {
+		-- <sql-errors-1.9>
+		1,"Can't drop space 'V0': use DROP VIEW"
+		-- </sql-errors-1.9>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.10",
+	[[
+		CREATE TABLE t10(i INT PRIMARY KEY REFERENCES v0);
+	]], {
+		-- <sql-errors-1.10>
+		1,"Failed to create foreign key constraint 'FK_CONSTRAINT_1_T10': referenced space can't be VIEW"
+		-- </sql-errors-1.10>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.11",
+	[[
+		CREATE VIEW v11 AS SELECT * FROM t0 WHERE i = ?;
+	]], {
+		-- <sql-errors-1.11>
+		1,"Failed to create space 'V11': parameters are not allowed in views"
+		-- </sql-errors-1.11>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.12",
+	[[
+		CREATE INDEX i12 ON v0(i);
+	]], {
+		-- <sql-errors-1.12>
+		1,"Can't create or modify index 'I12' in space 'V0': views can not be indexed"
+		-- </sql-errors-1.12>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.13",
+	[[
+		SELECT 9223372036854775808;
+	]], {
+		-- <sql-errors-1.13>
+		1,"Integer literal 9223372036854775808 exceeds the supported range -9223372036854775808 - 9223372036854775807"
+		-- </sql-errors-1.13>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.14",
+	[[
+		SELECT 0x10000000000000000;
+	]], {
+		-- <sql-errors-1.14>
+		1,"Hex literal 0x10000000000000000 length 17 exceeds the supported limit (16)"
+		-- </sql-errors-1.14>
+	})
+
+select_statement = 'SELECT i FROM t0 WHERE i = 0'
+for i = 1, 200 do
+	select_statement = select_statement .. ' OR i = ' .. i
+end
+select_statement = select_statement .. ';'
+
+test:do_catchsql_test(
+	"sql-errors-1.15",
+	select_statement,
+	{
+		-- <sql-errors-1.15>
+		1,"Number of nodes in expression tree 201 exceeds the limit (200)"
+		-- </sql-errors-1.15>
+	})
+
+select_statement = 'SELECT CHAR(1'
+for i = 1, 127 do
+	select_statement = select_statement .. ', ' .. i
+end
+select_statement = select_statement .. ');'
+
+test:do_catchsql_test(
+	"sql-errors-1.16",
+	select_statement,
+	{
+		-- <sql-errors-1.16>
+		1,"Number of arguments to function CHAR 128 exceeds the limit (127)"
+		-- </sql-errors-1.16>
+	})
+
+select_statement = 'SELECT MAX(1'
+for i = 1, 127 do
+	select_statement = select_statement .. ', ' .. i
+end
+select_statement = select_statement .. ');'
+
+test:do_catchsql_test(
+	"sql-errors-1.17",
+	select_statement,
+	{
+		-- <sql-errors-1.17>
+		1,"Number of arguments to function MAX 128 exceeds the limit (127)"
+		-- </sql-errors-1.17>
+	})
+
+select_statement = 'SELECT 0'
+for i = 1, 30 do
+	select_statement = select_statement .. ' UNION ALL SELECT ' .. i
+end
+select_statement = select_statement .. ';'
+
+test:do_catchsql_test(
+	"sql-errors-1.18",
+	select_statement,
+	{
+		-- <sql-errors-1.18>
+		1,"The number of UNION or EXCEPT or INTERSECT operations 31 exceeds the limit (30)"
+		-- </sql-errors-1.18>
+	})
+
+select_statement = 'SELECT 0'
+for i = 1, 2000 do
+	select_statement = select_statement .. ', ' .. i
+end
+select_statement = select_statement .. ';'
+
+test:do_catchsql_test(
+	"sql-errors-1.19",
+	select_statement,
+	{
+		-- <sql-errors-1.19>
+		1,"The number of columns in result set 2001 exceeds the limit (2000)"
+		-- </sql-errors-1.19>
+	})
+
+select_statement = 'SELECT * FROM t0'
+for i = 1, 64 do
+	select_statement = select_statement .. ', t0 as t' .. i
+end
+select_statement = select_statement .. ';'
+
+test:do_catchsql_test(
+	"sql-errors-1.20",
+	select_statement,
+	{
+		-- <sql-errors-1.20>
+		1,"The number of tables in a join 65 exceeds the limit (64)"
+		-- </sql-errors-1.20>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.21",
+	[[
+		SELECT $65001;
+	]], {
+		-- <sql-errors-1.21>
+		1,"SQL bind parameter limit reached: 65000"
+		-- </sql-errors-1.21>
+	})
+
+select_statement = 'SELECT '..string.rep('?, ', box.schema.SQL_BIND_PARAMETER_MAX)..'?;'
+
+test:do_catchsql_test(
+	"sql-errors-1.22",
+	select_statement,
+	{
+		-- <sql-errors-1.22>
+		1,"SQL bind parameter limit reached: 65000"
+		-- </sql-errors-1.22>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.23",
+	[[
+		INSERT INTO v0 VALUES (2);
+	]], {
+		-- <sql-errors-1.23>
+		1,"Can't modify space 'V0': space is a view"
+		-- </sql-errors-1.23>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.24",
+	[[
+		UPDATE v0 SET i = 2 WHERE i = 1;
+	]], {
+		-- <sql-errors-1.24>
+		1,"Can't modify space 'V0': space is a view"
+		-- </sql-errors-1.24>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.25",
+	[[
+		DELETE FROM v0;
+	]], {
+		-- <sql-errors-1.25>
+		1,"Can't modify space 'V0': space is a view"
+		-- </sql-errors-1.25>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.26",
+	[[
+		CREATE TABLE t26 (i INT, PRIMARY KEY('i'));
+	]], {
+		-- <sql-errors-1.26>
+		1,"Expressions are prohibited in an index definition"
+		-- </sql-errors-1.26>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.27",
+	[[
+		CREATE TABLE t27 (i INT PRIMARY KEY, CHECK(i < (SELECT * FROM t0)));
+	]], {
+		-- <sql-errors-1.27>
+		1,"Failed to create space 'T27': Subqueries are prohibited in a CHECK constraint definition"
+		-- </sql-errors-1.27>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.28",
+	[[
+		CREATE INDEX i28 ON t0(t0.i);
+	]], {
+		-- <sql-errors-1.28>
+		1,"Expressions are prohibited in an index definition"
+		-- </sql-errors-1.28>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.29",
+	[[
+		CREATE INDEX i29 ON t0($1);
+	]], {
+		-- <sql-errors-1.29>
+		1,"Parameter markers are prohibited in an index definition"
+		-- </sql-errors-1.29>
+	})
+
+create_index_statement = 'CREATE INDEX i30 on t0(i'..string.rep(', i', 2000)..');'
+
+test:do_catchsql_test(
+	"sql-errors-1.30",
+	create_index_statement,
+	{
+		-- <sql-errors-1.30>
+		1,"The number of columns in index 2001 exceeds the limit (2000)"
+		-- </sql-errors-1.30>
+	})
+
+update_statement = 'UPDATE s0 SET a1 = a1 + 1'
+for i = 2, 2001 do
+	update_statement = update_statement .. ', a' .. i .. ' = a' .. i .. ' + 1'
+end
+update_statement = update_statement .. ';'
+
+test:do_catchsql_test(
+	"sql-errors-1.31",
+	update_statement,
+	{
+		-- <sql-errors-1.31>
+		1,"The number of columns in set list 2001 exceeds the limit (2000)"
+		-- </sql-errors-1.31>
+	})
+
+select_statement = 'SELECT * FROM (SELECT 1 UNION ALL SELECT 1 ORDER BY 1'..string.rep(', 1', 2000)..')'
+
+test:do_catchsql_test(
+	"sql-errors-1.32",
+	select_statement,
+	{
+		-- <sql-errors-1.32>
+		1,"The number of terms in ORDER BY clause 2001 exceeds the limit (2000)"
+		-- </sql-errors-1.32>
+	})
+
+select_statement = 'SELECT 1 ORDER BY 1'..string.rep(', 1', 2000)
+
+test:do_catchsql_test(
+	"sql-errors-1.33",
+	select_statement,
+	{
+		-- <sql-errors-1.33>
+		1,"The number of terms in ORDER BY clause 2001 exceeds the limit (2000)"
+		-- </sql-errors-1.33>
+	})
+
+select_statement = 'SELECT 1 as '..string.rep('x', 65001)
+
+test:do_catchsql_test(
+	"sql-errors-1.34",
+	select_statement,
+	{
+		-- <sql-errors-1.34>
+		1,"Invalid identifier 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' (expected printable symbols only or it is too long)"
+		-- </sql-errors-1.34>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.35",
+	[[
+		SELECT 1 as "";
+	]], {
+		-- <sql-errors-1.35>
+		1,"Invalid identifier '' (expected printable symbols only or it is too long)"
+		-- </sql-errors-1.35>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.36",
+	[[
+		SELECT likelihood(1, 2);
+	]], {
+		-- <sql-errors-1.36>
+		1,"Illegal parameters, second argument to likelihood() must be a constant between 0.0 and 1.0"
+		-- </sql-errors-1.36>
+	})
+
+test:finish_test()
diff --git a/test/sql-tap/unique.test.lua b/test/sql-tap/unique.test.lua
index 56ac74a..fbd73a6 100755
--- a/test/sql-tap/unique.test.lua
+++ b/test/sql-tap/unique.test.lua
@@ -36,7 +36,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <unique-1.1>
-        1, [[table "T1" has more than one primary key]]
+        1, [[Failed to create space 'T1': primary key has been already declared]]
         -- </unique-1.1>
     })
 
diff --git a/test/sql-tap/view.test.lua b/test/sql-tap/view.test.lua
index 72fdab8..0032e1b 100755
--- a/test/sql-tap/view.test.lua
+++ b/test/sql-tap/view.test.lua
@@ -189,7 +189,7 @@ test:do_catchsql_test(
         INSERT INTO v2 VALUES(1,2,3,4);
     ]], {
         -- <view-2.2>
-        1, "cannot modify V2 because it is a view"
+        1, "Can't modify space 'V2': space is a view"
         -- </view-2.2>
     })
 
@@ -199,7 +199,7 @@ test:do_catchsql_test(
         UPDATE v2 SET a=10 WHERE a=5;
     ]], {
         -- <view-2.3>
-        1, "cannot modify V2 because it is a view"
+        1, "Can't modify space 'V2': space is a view"
         -- </view-2.3>
     })
 
@@ -209,7 +209,7 @@ test:do_catchsql_test(
         DELETE FROM v2;
     ]], {
         -- <view-2.4>
-        1, "cannot modify V2 because it is a view"
+        1, "Can't modify space 'V2': space is a view"
         -- </view-2.4>
     })
 
@@ -302,7 +302,7 @@ test:do_catchsql_test(
     [[
         CREATE VIEW v1err(x,y) AS SELECT a, b+c, c-b FROM t1;
         SELECT * FROM v1err;
-    ]], {1, "expected 2 columns for 'V1ERR' but got 3"})
+    ]], {1, "Failed to create space 'V1ERR': number of aliases doesn't match provided columns"})
 
 test:do_catchsql_test(
     "view-3.3.5.2",
@@ -310,7 +310,7 @@ test:do_catchsql_test(
         DROP VIEW IF EXISTS v1err;
         CREATE VIEW v1err(w,x,y,z) AS SELECT a, b+c, c-b FROM t1;
         SELECT * FROM v1err;
-    ]], {1, "expected 4 columns for 'V1ERR' but got 3"})
+    ]], {1, "Failed to create space 'V1ERR': number of aliases doesn't match provided columns"})
 
 -- #MUST_WORK_TEST no query solution
 -- # ifcapable compound {
@@ -332,7 +332,7 @@ test:do_catchsql_test(
         DROP VIEW t1;
     ]], {
         -- <view-4.1>
-        1, "use DROP TABLE to delete table T1"
+        1, "Can't drop space 'T1': use DROP TABLE"
         -- </view-4.1>
     })
 
@@ -352,7 +352,7 @@ test:do_catchsql_test(
         DROP TABLE v1;
     ]], {
         -- <view-4.3>
-        1, "use DROP VIEW to delete view V1"
+        1, "Can't drop space 'V1': use DROP VIEW"
         -- </view-4.3>
     })
 
@@ -372,7 +372,7 @@ test:do_catchsql_test(
         CREATE INDEX i1v1 ON v1(xyz);
     ]], {
         -- <view-4.5>
-        1, "views can not be indexed"
+        1, "Can't create or modify index 'I1V1' in space 'V1': views can not be indexed"
         -- </view-4.5>
     })
 
@@ -842,7 +842,7 @@ test:do_catchsql_test(
         CREATE VIEW v12 AS SELECT a FROM t1 WHERE b=?
     ]], {
         -- <view-12.1>
-        1, "parameters are not allowed in views"
+        1, "Failed to create space 'V12': parameters are not allowed in views"
         -- </view-12.1>
     })
 
@@ -852,7 +852,7 @@ test:do_catchsql_test(
         CREATE VIEW v12(x) AS SELECT a FROM t1 WHERE b=?
     ]], {
         -- <view-12.2>
-        1, "parameters are not allowed in views"
+        1, "Failed to create space 'V12': parameters are not allowed in views"
         -- </view-12.2>
     })
 
diff --git a/test/sql-tap/where7.test.lua b/test/sql-tap/where7.test.lua
index 2e6f116..ecd0d24 100755
--- a/test/sql-tap/where7.test.lua
+++ b/test/sql-tap/where7.test.lua
@@ -325,7 +325,7 @@ test:do_test(
         end
         return test:catchsql(sql)
     end, {
-        1, "Expression tree is too large (maximum depth 200)"
+        1, "Number of nodes in expression tree 201 exceeds the limit (200)"
     })
 
 test:do_test(
diff --git a/test/sql-tap/whereG.test.lua b/test/sql-tap/whereG.test.lua
index 2d7592b..510df2e 100755
--- a/test/sql-tap/whereG.test.lua
+++ b/test/sql-tap/whereG.test.lua
@@ -193,7 +193,7 @@ test:do_catchsql_test(
            AND album.aid=track.aid;
     ]], {
         -- <whereG-2.1>
-        1, "second argument to likelihood() must be a constant between 0.0 and 1.0"
+        1, "Illegal parameters, second argument to likelihood() must be a constant between 0.0 and 1.0"
         -- </whereG-2.1>
     })
 
@@ -207,7 +207,7 @@ test:do_catchsql_test(
            AND album.aid=track.aid;
     ]], {
         -- <whereG-2.2>
-        1, "second argument to likelihood() must be a constant between 0.0 and 1.0"
+        1, "Illegal parameters, second argument to likelihood() must be a constant between 0.0 and 1.0"
         -- </whereG-2.2>
     })
 
@@ -221,7 +221,7 @@ test:do_catchsql_test(
            AND album.aid=track.aid;
     ]], {
         -- <whereG-2.3>
-        1, "second argument to likelihood() must be a constant between 0.0 and 1.0"
+        1, "Illegal parameters, second argument to likelihood() must be a constant between 0.0 and 1.0"
         -- </whereG-2.3>
     })
 
diff --git a/test/sql/delete.result b/test/sql/delete.result
index e024dd6..29459cc 100644
--- a/test/sql/delete.result
+++ b/test/sql/delete.result
@@ -93,7 +93,7 @@ box.sql.execute("CREATE VIEW v1 AS SELECT * FROM t1;")
 ...
 box.sql.execute("TRUNCATE TABLE v1;")
 ---
-- error: 'SQL error: can not truncate space ''V1'' because it is a view'
+- error: 'SQL error: can not truncate space ''V1'' because space is a view'
 ...
 -- Can't truncate table with FK.
 box.sql.execute("CREATE TABLE t2(x INT PRIMARY KEY REFERENCES t1(id));")
diff --git a/test/sql/gh-2347-max-int-literals.result b/test/sql/gh-2347-max-int-literals.result
index c289a80..b511440 100644
--- a/test/sql/gh-2347-max-int-literals.result
+++ b/test/sql/gh-2347-max-int-literals.result
@@ -20,9 +20,11 @@ box.sql.execute("select (-9223372036854775808)")
 ...
 box.sql.execute("select (9223372036854775808)")
 ---
-- error: 'oversized integer: 9223372036854775808'
+- error: Integer literal 9223372036854775808 exceeds the supported range -9223372036854775808
+    - 9223372036854775807
 ...
 box.sql.execute("select (-9223372036854775809)")
 ---
-- error: 'oversized integer: -9223372036854775809'
+- error: Integer literal -9223372036854775809 exceeds the supported range -9223372036854775808
+    - 9223372036854775807
 ...
diff --git a/test/sql/gh-2929-primary-key.result b/test/sql/gh-2929-primary-key.result
index 4052665..e36f4e4 100644
--- a/test/sql/gh-2929-primary-key.result
+++ b/test/sql/gh-2929-primary-key.result
@@ -18,19 +18,19 @@ box.sql.execute("CREATE TABLE t1(a INT PRIMARY KEY, b INT UNIQUE)")
 ...
 box.sql.execute("CREATE TABLE t2(a INT UNIQUE, b INT)")
 ---
-- error: PRIMARY KEY missing on table T2
+- error: 'Failed to create space ''T2'': PRIMARY KEY missing'
 ...
 box.sql.execute("CREATE TABLE t3(a FLOAT)")
 ---
-- error: PRIMARY KEY missing on table T3
+- error: 'Failed to create space ''T3'': PRIMARY KEY missing'
 ...
 box.sql.execute("CREATE TABLE t4(a FLOAT, b TEXT)")
 ---
-- error: PRIMARY KEY missing on table T4
+- error: 'Failed to create space ''T4'': PRIMARY KEY missing'
 ...
 box.sql.execute("CREATE TABLE t5(a FLOAT, b FLOAT UNIQUE)")
 ---
-- error: PRIMARY KEY missing on table T5
+- error: 'Failed to create space ''T5'': PRIMARY KEY missing'
 ...
 box.sql.execute("DROP TABLE t1")
 ---
diff --git a/test/sql/integer-overflow.result b/test/sql/integer-overflow.result
index 4754c04..09e864e 100644
--- a/test/sql/integer-overflow.result
+++ b/test/sql/integer-overflow.result
@@ -30,15 +30,18 @@ box.sql.execute('SELECT (9223372036854775807 + 1);')
 --
 box.sql.execute('SELECT 9223372036854775808;')
 ---
-- error: 'oversized integer: 9223372036854775808'
+- error: Integer literal 9223372036854775808 exceeds the supported range -9223372036854775808
+    - 9223372036854775807
 ...
 box.sql.execute('SELECT -9223372036854775809;')
 ---
-- error: 'oversized integer: -9223372036854775809'
+- error: Integer literal -9223372036854775809 exceeds the supported range -9223372036854775808
+    - 9223372036854775807
 ...
 box.sql.execute('SELECT 9223372036854775808 - 1;')
 ---
-- error: 'oversized integer: 9223372036854775808'
+- error: Integer literal 9223372036854775808 exceeds the supported range -9223372036854775808
+    - 9223372036854775807
 ...
 -- Test that CAST may also leads to overflow.
 --
diff --git a/test/sql/iproto.result b/test/sql/iproto.result
index 938aea9..56099fa 100644
--- a/test/sql/iproto.result
+++ b/test/sql/iproto.result
@@ -363,7 +363,7 @@ sql = 'select '..string.rep('?, ', box.schema.SQL_BIND_PARAMETER_MAX)..'?'
 ...
 cn:execute(sql)
 ---
-- error: 'Failed to execute SQL statement: too many SQL variables'
+- error: 'Failed to execute SQL statement: SQL bind parameter limit reached: 65000'
 ...
 -- Try too many parameter values.
 sql = 'select ?'
@@ -571,8 +571,7 @@ cn:execute('select ?1, ?2, ?3', {1, 2, 3})
 ...
 cn:execute('select $name, $name2', {1, 2})
 ---
-- error: 'Failed to execute SQL statement: variable number must be between $1 and
-    $65000'
+- error: 'Failed to execute SQL statement: SQL bind parameter limit reached: 65000'
 ...
 parameters = {}
 ---
diff --git a/test/sql/transition.result b/test/sql/transition.result
index ea110c0..89035e5 100644
--- a/test/sql/transition.result
+++ b/test/sql/transition.result
@@ -180,7 +180,8 @@ box.sql.execute("DROP TABLE barfoo")
 -- attempt to create a table lacking PRIMARY KEY
 box.sql.execute("CREATE TABLE without_rowid_lacking_primary_key(x SCALAR)")
 ---
-- error: PRIMARY KEY missing on table WITHOUT_ROWID_LACKING_PRIMARY_KEY
+- error: 'Failed to create space ''WITHOUT_ROWID_LACKING_PRIMARY_KEY'': PRIMARY KEY
+    missing'
 ...
 -- create a table with implicit indices (used to SEGFAULT)
 box.sql.execute("CREATE TABLE implicit_indices(a INT PRIMARY KEY,b INT,c INT,d TEXT UNIQUE)")
diff --git a/test/sql/view.result b/test/sql/view.result
index e99a9bd..32967b3 100644
--- a/test/sql/view.result
+++ b/test/sql/view.result
@@ -19,7 +19,7 @@ box.sql.execute("CREATE VIEW v1 AS SELECT a+b FROM t1;");
 -- View can't have any indexes.
 box.sql.execute("CREATE INDEX i1 on v1(a);");
 ---
-- error: views can not be indexed
+- error: 'Can''t create or modify index ''I1'' in space ''V1'': views can not be indexed'
 ...
 v1 = box.space.V1;
 ---

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 8/8] sql: remove sqlErrorMsg()
  2019-03-26 13:34       ` n.pettik
@ 2019-03-26 17:52         ` Mergen Imeev
  2019-03-26 18:28           ` n.pettik
  0 siblings, 1 reply; 43+ messages in thread
From: Mergen Imeev @ 2019-03-26 17:52 UTC (permalink / raw)
  To: n.pettik; +Cc: tarantool-patches

Hi! Thank you for review. My answers, diff and new patch below.

On Tue, Mar 26, 2019 at 04:34:26PM +0300, n.pettik wrote:
> 
> >> @@ -1039,8 +1036,8 @@ resolveCompoundOrderBy(Parse * pParse,    /* Parsing context.  Leave error messages
> >>                        const char *err = "Error at ORDER BY in place %d: "\
> >>                                          "term does not match any column in "\
> >>                                          "the result set";
> >> -                       err = tt_sprintf(err, i + 1);
> >> -                       diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
> >> +                       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> >> +                                tt_sprintf(err, i + 1));
> >> 
> >>> @@ -1444,10 +1469,37 @@ resolveSelectStep(Walker * pWalker, Select * p)
> >>>    * number of expressions in the select list.
> >>>    */
> >>>   if (p->pNext && p->pEList->nExpr != p->pNext->pEList->nExpr) {
> >>> -     sqlSelectWrongNumTermsError(pParse, p->pNext);
> >>> +     if (p->pNext->selFlags & SF_Values) {
> >>> +       diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> >>> +          "all VALUES must have the same "\
> >>> +          "number of terms");
> >>> +     } else {
> >>> +       const char *err_msg =
> >>> +         "SELECTs to the left and right of %s "\
> >>> +         "do not have the same number of "\
> >>> +         "result columns";
> >>> +       switch (p->pNext->op) {
> >>> +       case TK_ALL:
> >>> +         err_msg = tt_sprintf(err_msg,
> >>> +                  "UNION ALL");
> >>> +         break;
> >> 
> >> Why don’t use selectOpName?
> >> Like it was in  sqlSelectWrongNumTermsError().
> >> 
> > Function selectOpName defined in different file and is static.
> > I think it isn't worth to share it for one error.
> 
> I believe it is worth doing. For instance, you can return
> sqlSelectWrongNumTermsError() which was placed in select.c
> and called that static func.
> 
Fixed.

> > Diff between versions:
> > 
> > diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> > index e26596a..1077285 100644
> > --- a/src/box/sql/expr.c
> > +++ b/src/box/sql/expr.c
> > @@ -666,13 +666,7 @@ codeVectorCompare(Parse * pParse,	/* Code generator context */
> > 	int regRight = 0;
> > 	u8 op = pExpr->op;
> > 	int addrDone = sqlVdbeMakeLabel(v);
> > -
> > -	if (nLeft != sqlExprVectorSize(pRight)) {
> > -		diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> > -			 "row value misused");
> > -		pParse->is_aborted = true;
> > -		return;
> > -	}
> 
> Here the problem is that function is never called in our test suite:
> I placed assert(0); and no one test failed. Please, add at least
> simple tests like SELECT (1, 2) == (1, 2) to make sure that
> codeVectorCompare() can be processed. Tests which you’ve added
> fails before this func is called.
> 
> Added comment:
> 
> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index 107728590..397f8209c 100644
> --- a/src/box/sql/expr.c
> +++ b/src/box/sql/expr.c
> @@ -666,6 +666,11 @@ codeVectorCompare(Parse * pParse,  /* Code generator context */
>         int regRight = 0;
>         u8 op = pExpr->op;
>         int addrDone = sqlVdbeMakeLabel(v);
> +       /*
> +        * Situation when vectors have different dimensions is
> +        * filtred way before - during expr resolution:
> +        * see resolveExprStep().
> +        */
> 
Added.

> > +	assert(nLeft == sqlExprVectorSize(pRight));
> > 	assert(pExpr->op == TK_EQ || pExpr->op == TK_NE
> > 	       || pExpr->op == TK_LT || pExpr->op == TK_GT
> > 	       || pExpr->op == TK_LE || pExpr->op == TK_GE);
> > 
> > 
> > @@ -4384,10 +4378,8 @@ sqlWhereBegin(Parse * pParse,	/* The parser context */
> > 	 * equal to pTabList->nSrc but might be shortened to 1 if the
> > 	 * WHERE_OR_SUBCLAUSE flag is set.
> > 	 */
> > -	for (ii = 0; ii < pTabList->nSrc; ii++) {
> > +	for (ii = 0; ii < pTabList->nSrc; ii++)
> > 		createMask(pMaskSet, pTabList->a[ii].iCursor);
> > -		sqlWhereTabFuncArgs(pParse, &pTabList->a[ii], &pWInfo->sWC);
> 
> Move removal of this func to a separate patch pls alongside with mentions of
> table-valued funcs. It is not related to errors.
> 
> 
Returned this function, replaced error in it by assert.


Diff:

diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 1077285..80d17d1 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -666,6 +666,12 @@ codeVectorCompare(Parse * pParse,	/* Code generator context */
 	int regRight = 0;
 	u8 op = pExpr->op;
 	int addrDone = sqlVdbeMakeLabel(v);
+
+	/*
+	 * Situation when vectors have different dimensions is
+	 * filtred way before - during expr resolution:
+	 * see resolveExprStep().
+	 */
 	assert(nLeft == sqlExprVectorSize(pRight));
 	assert(pExpr->op == TK_EQ || pExpr->op == TK_NE
 	       || pExpr->op == TK_LT || pExpr->op == TK_GT
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index c08d934..e4e3697 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -1459,28 +1459,13 @@ resolveSelectStep(Walker * pWalker, Select * p)
 					 "all VALUES must have the same "\
 					 "number of terms");
 			} else {
-				const char *err_msg =
+				const char *err =
 					"SELECTs to the left and right of %s "\
 					"do not have the same number of "\
 					"result columns";
-				switch (p->pNext->op) {
-				case TK_ALL:
-					err_msg = tt_sprintf(err_msg,
-							     "UNION ALL");
-					break;
-				case TK_INTERSECT:
-					err_msg = tt_sprintf(err_msg,
-							     "INTERSECT");
-					break;
-				case TK_EXCEPT:
-					err_msg = tt_sprintf(err_msg, "EXCEPT");
-					break;
-				default:
-					err_msg = tt_sprintf(err_msg, "UNION");
-					break;
-				}
+				const char *op = select_op_name(p->pNext->op);
 				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
-					 err_msg);
+					 tt_sprintf(err, op));
 			}
 			pParse->is_aborted = true;
 			return WRC_Abort;
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index e217c19..778de2c 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -1466,25 +1466,25 @@ sql_expr_list_to_key_info(struct Parse *parse, struct ExprList *list, int start)
 /*
  * Name of the connection operator, used for error messages.
  */
-static const char *
-selectOpName(int id)
+const char *
+select_op_name(int op_id)
 {
-	char *z;
-	switch (id) {
+	const char *op_name;
+	switch (op_id) {
 	case TK_ALL:
-		z = "UNION ALL";
+		op_name = "UNION ALL";
 		break;
 	case TK_INTERSECT:
-		z = "INTERSECT";
+		op_name = "INTERSECT";
 		break;
 	case TK_EXCEPT:
-		z = "EXCEPT";
+		op_name = "EXCEPT";
 		break;
 	default:
-		z = "UNION";
+		op_name = "UNION";
 		break;
 	}
-	return z;
+	return op_name;
 }
 
 /*
@@ -1542,7 +1542,7 @@ explainComposite(Parse * pParse,	/* Parse context */
 				   "COMPOUND SUBQUERIES %d AND %d %s(%s)",
 				   iSub1, iSub2,
 				   bUseTmp ? "USING TEMP B-TREE " : "",
-				   selectOpName(op)
+				   select_op_name(op)
 		    );
 		sqlVdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg,
 				  P4_DYNAMIC);
@@ -2614,7 +2614,7 @@ multiSelect(Parse * pParse,	/* Parsing context */
 	if (pPrior->pOrderBy) {
 		const char *err_msg =
 			tt_sprintf("ORDER BY clause should come after %s not "\
-				   "before", selectOpName(p->op));
+				   "before", select_op_name(p->op));
 		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
 		pParse->is_aborted = true;
 		rc = 1;
@@ -2623,7 +2623,7 @@ multiSelect(Parse * pParse,	/* Parsing context */
 	if (pPrior->pLimit) {
 		const char *err_msg =
 			tt_sprintf("LIMIT clause should come after %s not "\
-				   "before", selectOpName(p->op));
+				   "before", select_op_name(p->op));
 		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
 		pParse->is_aborted = true;
 		rc = 1;
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 15e6f22..50fc812 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -4426,6 +4426,15 @@ void sqlExpirePreparedStatements(sql *);
 int sqlCodeSubselect(Parse *, Expr *, int);
 void sqlSelectPrep(Parse *, Select *, NameContext *);
 
+/**
+ * Returns name of the connection operator.
+ *
+ * @param op_id ID of the connection operator.
+ * @retval Name of the connection operator.
+ */
+const char *
+select_op_name(int op_id);
+
 int sqlMatchSpanName(const char *, const char *, const char *);
 int sqlResolveExprNames(NameContext *, Expr *);
 int sqlResolveExprListNames(NameContext *, ExprList *);
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index c71a170..19ee2d0 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -4378,8 +4378,10 @@ sqlWhereBegin(Parse * pParse,	/* The parser context */
 	 * equal to pTabList->nSrc but might be shortened to 1 if the
 	 * WHERE_OR_SUBCLAUSE flag is set.
 	 */
-	for (ii = 0; ii < pTabList->nSrc; ii++)
+	for (ii = 0; ii < pTabList->nSrc; ii++) {
 		createMask(pMaskSet, pTabList->a[ii].iCursor);
+		sqlWhereTabFuncArgs(pParse, &pTabList->a[ii], &pWInfo->sWC);
+	}
 #ifdef SQL_DEBUG
 	for (ii = 0; ii < pTabList->nSrc; ii++) {
 		Bitmask m =
diff --git a/src/box/sql/whereInt.h b/src/box/sql/whereInt.h
index 9cf1eb8..47430ae 100644
--- a/src/box/sql/whereInt.h
+++ b/src/box/sql/whereInt.h
@@ -479,6 +479,7 @@ void sqlWhereSplit(WhereClause *, Expr *, u8);
 Bitmask sqlWhereExprUsage(WhereMaskSet *, Expr *);
 Bitmask sqlWhereExprListUsage(WhereMaskSet *, ExprList *);
 void sqlWhereExprAnalyze(SrcList *, WhereClause *);
+void sqlWhereTabFuncArgs(Parse *, struct SrcList_item *, WhereClause *);
 
 /*
  * Bitmasks for the operators on WhereTerm objects.  These are all
diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c
index 9d01a7e..ec08889 100644
--- a/src/box/sql/whereexpr.c
+++ b/src/box/sql/whereexpr.c
@@ -1472,3 +1472,45 @@ sqlWhereExprAnalyze(SrcList * pTabList,	/* the FROM clause */
 		exprAnalyze(pTabList, pWC, i);
 	}
 }
+
+/*
+ * For table-valued-functions, transform the function arguments into
+ * new WHERE clause terms.
+ */
+void
+sqlWhereTabFuncArgs(Parse * pParse,	/* Parsing context */
+			struct SrcList_item *pItem,	/* The FROM clause term to process */
+			WhereClause * pWC	/* Xfer function arguments to here */
+    )
+{
+	int j, k;
+	ExprList *pArgs;
+	Expr *pColRef;
+	Expr *pTerm;
+	if (pItem->fg.isTabFunc == 0)
+		return;
+	struct space_def *space_def = pItem->space->def;
+	pArgs = pItem->u1.pFuncArg;
+	if (pArgs == 0)
+		return;
+	for (j = k = 0; j < pArgs->nExpr; j++) {
+		while (k < (int)space_def->field_count)
+			k++;
+		/*
+		 * This assert replaces error. At the moment, this
+		 * error cannot appear due to this function being
+		 * unused.
+		 */
+		assert(k < (int)space_def->field_count);
+		pColRef = sqlExprAlloc(pParse->db, TK_COLUMN, 0, 0);
+		if (pColRef == 0)
+			return;
+		pColRef->iTable = pItem->iCursor;
+		pColRef->iColumn = k++;
+		pColRef->space_def = space_def;
+		pTerm = sqlPExpr(pParse, TK_EQ, pColRef,
+				     sqlExprDup(pParse->db,
+						    pArgs->a[j].pExpr, 0));
+		whereClauseInsert(pWC, pTerm, TERM_DYNAMIC);
+	}
+}
diff --git a/test/sql-tap/sql-errors.test.lua b/test/sql-tap/sql-errors.test.lua
index a132139..5585dbb 100755
--- a/test/sql-tap/sql-errors.test.lua
+++ b/test/sql-tap/sql-errors.test.lua
@@ -1,6 +1,6 @@
 #!/usr/bin/env tarantool
 test = require("sqltester")
-test:plan(43)
+test:plan(45)
 
 test:execsql([[
 	CREATE TABLE t0 (i INT PRIMARY KEY, a INT);
@@ -486,4 +486,24 @@ test:do_catchsql_test(
 		-- </sql-errors-1.43>
 	})
 
+test:do_execsql_test(
+	"sql-errors-1.44",
+	[[
+		SELECT (1, 2, 3) < (1, 2, 4);
+	]], {
+		-- <sql-errors-1.44>
+		1
+		-- </sql-errors-1.44>
+	})
+
+test:do_execsql_test(
+	"sql-errors-1.45",
+	[[
+		SELECT (1, 2, 3) < (1, 2, 2);
+	]], {
+		-- <sql-errors-1.45>
+		0
+		-- </sql-errors-1.45>
+	})
+
 test:finish_test()


New patch:

commit 43d2787aeec1eb133e0261308d4b6242fe220833
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Sat Mar 9 18:54:19 2019 +0300

    sql: remove sqlErrorMsg()
    
    This patch completely replaces sqlErrorMsg() with diag_set() and
    removes sqlErrorMsg().
    
    Closes #3965
    Closes #3036

diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index fe53262..39c86bc 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -499,13 +499,12 @@ sql_column_add_nullable_action(struct Parse *parser,
 	if (field->nullable_action != ON_CONFLICT_ACTION_DEFAULT &&
 	    nullable_action != field->nullable_action) {
 		/* Prevent defining nullable_action many times. */
-		const char *err_msg =
-			tt_sprintf("NULL declaration for column '%s' of table "
-				   "'%s' has been already set to '%s'",
-				   field->name, def->name,
-				   on_conflict_action_strs[field->
-							   nullable_action]);
-		diag_set(ClientError, ER_SQL, err_msg);
+		const char *err = "NULL declaration for column '%s' of table "
+				  "'%s' has been already set to '%s'";
+		const char *action =
+			on_conflict_action_strs[field->nullable_action];
+		err = tt_sprintf(err, field->name, def->name, action);
+		diag_set(ClientError, ER_SQL, err);
 		parser->is_aborted = true;
 		return;
 	}
@@ -3057,11 +3056,12 @@ sqlWithAdd(Parse * pParse,	/* Parsing context */
 	zName = sqlNameFromToken(pParse->db, pName);
 	if (zName && pWith) {
 		int i;
+		const char *err = "Ambiguous table name in WITH query: %s";
 		for (i = 0; i < pWith->nCte; i++) {
 			if (strcmp(zName, pWith->a[i].zName) == 0) {
-				sqlErrorMsg(pParse,
-						"duplicate WITH table name: %s",
-						zName);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 tt_sprintf(err, zName));
+				pParse->is_aborted = true;
 			}
 		}
 	}
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index 0eb28d7..f536bb8 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -102,10 +102,10 @@ sql_table_truncate(struct Parse *parse, struct SrcList *tab_list)
 		goto tarantool_error;
 	}
 	if (! rlist_empty(&space->parent_fk_constraint)) {
-		const char *err_msg =
-			tt_sprintf("can not truncate space '%s' because other "
-				   "objects depend on it", space->def->name);
-		diag_set(ClientError, ER_SQL, err_msg);
+		const char *err = "can not truncate space '%s' because other "
+				  "objects depend on it";
+		diag_set(ClientError, ER_SQL,
+			 tt_sprintf(err, space->def->name));
 		goto tarantool_error;
 	}
 	if (space->def->opts.is_view) {
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index de993c1..80d17d1 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -667,10 +667,12 @@ codeVectorCompare(Parse * pParse,	/* Code generator context */
 	u8 op = pExpr->op;
 	int addrDone = sqlVdbeMakeLabel(v);
 
-	if (nLeft != sqlExprVectorSize(pRight)) {
-		sqlErrorMsg(pParse, "row value misused");
-		return;
-	}
+	/*
+	 * Situation when vectors have different dimensions is
+	 * filtred way before - during expr resolution:
+	 * see resolveExprStep().
+	 */
+	assert(nLeft == sqlExprVectorSize(pRight));
 	assert(pExpr->op == TK_EQ || pExpr->op == TK_NE
 	       || pExpr->op == TK_LT || pExpr->op == TK_GT
 	       || pExpr->op == TK_LE || pExpr->op == TK_GE);
@@ -1201,7 +1203,14 @@ sqlExprAssignVarNumber(Parse * pParse, Expr * pExpr, u32 n)
 			testcase(i == 1);
 			testcase(i == SQL_BIND_PARAMETER_MAX - 1);
 			testcase(i == SQL_BIND_PARAMETER_MAX);
-			if (!is_ok || i < 1 || i > SQL_BIND_PARAMETER_MAX) {
+			if (i < 1) {
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 "Index of binding slots must start "\
+					 "from 1");
+				pParse->is_aborted = true;
+				return;
+			}
+			if (!is_ok || i > SQL_BIND_PARAMETER_MAX) {
 				diag_set(ClientError, ER_SQL_BIND_PARAMETER_MAX,
 					 SQL_BIND_PARAMETER_MAX);
 				pParse->is_aborted = true;
@@ -1785,8 +1794,10 @@ sqlExprListAppendVector(Parse * pParse,	/* Parsing context */
 	 */
 	if (pExpr->op != TK_SELECT
 	    && pColumns->nId != (n = sqlExprVectorSize(pExpr))) {
-		sqlErrorMsg(pParse, "%d columns assigned %d values",
-				pColumns->nId, n);
+		const char *err = tt_sprintf("%d columns assigned %d values",
+					     pColumns->nId, n);
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+		pParse->is_aborted = true;
 		goto vector_append_error;
 	}
 
@@ -2669,41 +2680,6 @@ expr_in_type(Parse *pParse, Expr *pExpr)
 }
 
 /*
- * Load the Parse object passed as the first argument with an error
- * message of the form:
- *
- *   "sub-select returns N columns - expected M"
- */
-void
-sqlSubselectError(Parse * pParse, int nActual, int nExpect)
-{
-	const char *zFmt = "sub-select returns %d columns - expected %d";
-	sqlErrorMsg(pParse, zFmt, nActual, nExpect);
-}
-
-/*
- * Expression pExpr is a vector that has been used in a context where
- * it is not permitted. If pExpr is a sub-select vector, this routine
- * loads the Parse object with a message of the form:
- *
- *   "sub-select returns N columns - expected 1"
- *
- * Or, if it is a regular scalar vector:
- *
- *   "row value misused"
- */
-void
-sqlVectorErrorMsg(Parse * pParse, Expr * pExpr)
-{
-	if (pExpr->flags & EP_xIsSelect) {
-		sqlSubselectError(pParse, pExpr->x.pSelect->pEList->nExpr,
-				      1);
-	} else {
-		sqlErrorMsg(pParse, "row value misused");
-	}
-}
-
-/*
  * Generate code for scalar subqueries used as a subquery expression, EXISTS,
  * or IN operators.  Examples:
  *
@@ -2984,15 +2960,23 @@ int
 sqlExprCheckIN(Parse * pParse, Expr * pIn)
 {
 	int nVector = sqlExprVectorSize(pIn->pLeft);
+	const char *err;
 	if ((pIn->flags & EP_xIsSelect)) {
 		if (nVector != pIn->x.pSelect->pEList->nExpr) {
-			sqlSubselectError(pParse,
-					      pIn->x.pSelect->pEList->nExpr,
-					      nVector);
+			err = "sub-select returns %d columns - expected %d";
+			int expr_count = pIn->x.pSelect->pEList->nExpr;
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+				 tt_sprintf(err, expr_count, nVector));
+			pParse->is_aborted = true;
 			return 1;
 		}
 	} else if (nVector != 1) {
-		sqlVectorErrorMsg(pParse, pIn->pLeft);
+		assert((pIn->pLeft->flags & EP_xIsSelect) != 0);
+		int expr_count = pIn->pLeft->x.pSelect->pEList->nExpr;
+		err = tt_sprintf("sub-select returns %d columns - expected 1",
+				 expr_count);
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+		pParse->is_aborted = true;
 		return 1;
 	}
 	return 0;
@@ -3990,9 +3974,10 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 			AggInfo *pInfo = pExpr->pAggInfo;
 			if (pInfo == 0) {
 				assert(!ExprHasProperty(pExpr, EP_IntValue));
-				sqlErrorMsg(pParse,
-						"misuse of aggregate: %s()",
-						pExpr->u.zToken);
+				const char *err = "misuse of aggregate: %s()";
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 tt_sprintf(err, pExpr->u.zToken));
+				pParse->is_aborted = true;
 			} else {
 				pExpr->type = pInfo->aFunc->pFunc->ret_type;
 				return pInfo->aFunc[pExpr->iAgg].iMem;
@@ -4159,7 +4144,11 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 			testcase(op == TK_SELECT);
 			if (op == TK_SELECT
 			    && (nCol = pExpr->x.pSelect->pEList->nExpr) != 1) {
-				sqlSubselectError(pParse, nCol, 1);
+				const char *err = "sub-select returns %d "\
+						  "columns - expected 1";
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 tt_sprintf(err, nCol));
+				pParse->is_aborted = true;
 			} else {
 				return sqlCodeSubselect(pParse, pExpr, 0);
 			}
@@ -4178,9 +4167,11 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 						 sqlExprVectorSize(pExpr->
 								       pLeft))
 			    ) {
-				sqlErrorMsg(pParse,
-						"%d columns assigned %d values",
-						pExpr->iTable, n);
+				const char *err =
+					"%d columns assigned %d values";
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 tt_sprintf(err, pExpr->iTable, n));
+				pParse->is_aborted = true;
 			}
 			return pExpr->pLeft->iTable + pExpr->iColumn;
 		}
@@ -4279,7 +4270,9 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 		}
 
 	case TK_VECTOR:{
-			sqlErrorMsg(pParse, "row value misused");
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+				 "row value misused");
+			pParse->is_aborted = true;
 			break;
 		}
 
@@ -4379,8 +4372,9 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 		}
 	case TK_RAISE:
 		if (pParse->triggered_space == NULL) {
-			sqlErrorMsg(pParse, "RAISE() may only be used "
-					"within a trigger-program");
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC, "RAISE() "\
+				 "may only be used within a trigger-program");
+			pParse->is_aborted = true;
 			return 0;
 		}
 		if (pExpr->on_conflict_action == ON_CONFLICT_ACTION_ABORT)
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 0ca38ae..7e98f42 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -396,10 +396,11 @@ sqlInsert(Parse * pParse,	/* Parser context */
 				goto insert_cleanup;
 			}
 			if (bit_test(used_columns, j)) {
-				const char *err;
-				err = "table id list: duplicate column name %s";
-				sqlErrorMsg(pParse,
-						err, pColumn->a[i].zName);
+				const char *err = "table id list: duplicate "\
+						  "column name %s";
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 tt_sprintf(err, pColumn->a[i].zName));
+				pParse->is_aborted = true;
 				goto insert_cleanup;
 			}
 			bit_set(used_columns, j);
@@ -510,14 +511,19 @@ sqlInsert(Parse * pParse,	/* Parser context */
 
 	if (pColumn == NULL && nColumn != 0 &&
 	    nColumn != (int)space_def->field_count) {
-		sqlErrorMsg(pParse,
-				"table %S has %d columns but %d values were supplied",
-				pTabList, 0, space_def->field_count, nColumn);
+		const char *err =
+			"table %s has %d columns but %d values were supplied";
+		err = tt_sprintf(err, pTabList->a[0].zName,
+				 space_def->field_count, nColumn);
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+		pParse->is_aborted = true;
 		goto insert_cleanup;
 	}
 	if (pColumn != 0 && nColumn != pColumn->nId) {
-		sqlErrorMsg(pParse, "%d values for %d columns", nColumn,
-				pColumn->nId);
+		const char *err = "%d values for %d columns";
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+			 tt_sprintf(err, nColumn, pColumn->nId));
+		pParse->is_aborted = true;
 		goto insert_cleanup;
 	}
 
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index a141065..d2614d9 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -904,7 +904,9 @@ expr(A) ::= VARIABLE(X).     {
   Token t = X;
   if (pParse->parse_only) {
     spanSet(&A, &t, &t);
-    sqlErrorMsg(pParse, "bindings are not allowed in DDL");
+    diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+             "bindings are not allowed in DDL");
+    pParse->is_aborted = true;
     A.pExpr = NULL;
   } else if (!(X.z[0]=='#' && sqlIsdigit(X.z[1]))) {
     u32 n = X.n;
@@ -1390,9 +1392,9 @@ trigger_cmd_list(A) ::= trigger_cmd(A) SEMI. {
 trnm(A) ::= nm(A).
 trnm(A) ::= nm DOT nm(X). {
   A = X;
-  sqlErrorMsg(pParse,
-        "qualified table names are not allowed on INSERT, UPDATE, and DELETE "
-        "statements within triggers");
+  diag_set(ClientError, ER_SQL_PARSER_GENERIC, "qualified table names are not "\
+           "allowed on INSERT, UPDATE, and DELETE statements within triggers");
+  pParse->is_aborted = true;
 }
 
 // Disallow the INDEX BY and NOT INDEXED clauses on UPDATE and DELETE
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 30a6b5f..e4e3697 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -390,16 +390,21 @@ lookupName(Parse * pParse,	/* The parsing context */
 					assert(pExpr->x.pList == 0);
 					assert(pExpr->x.pSelect == 0);
 					pOrig = pEList->a[j].pExpr;
+					const char *err = "misuse of aliased "\
+							  "aggregate %s";
 					if ((pNC->ncFlags & NC_AllowAgg) == 0
 					    && ExprHasProperty(pOrig, EP_Agg)) {
-						sqlErrorMsg(pParse,
-								"misuse of aliased aggregate %s",
-								zAs);
+						diag_set(ClientError,
+							 ER_SQL_PARSER_GENERIC,
+							 tt_sprintf(err, zAs));
+						pParse->is_aborted = true;
 						return WRC_Abort;
 					}
 					if (sqlExprVectorSize(pOrig) != 1) {
-						sqlErrorMsg(pParse,
-								"row value misused");
+						diag_set(ClientError,
+							 ER_SQL_PARSER_GENERIC,
+							 "row value misused");
+						pParse->is_aborted = true;
 						return WRC_Abort;
 					}
 					resolveAlias(pParse, pEList, j, pExpr,
@@ -426,12 +431,15 @@ lookupName(Parse * pParse,	/* The parsing context */
 	 * more matches.  Either way, we have an error.
 	 */
 	if (cnt > 1) {
+		const char *err;
 		if (zTab) {
-			sqlErrorMsg(pParse, "ambiguous column name: %s.%s",
-				    zTab, zCol);
+			err = tt_sprintf("ambiguous column name: %s.%s", zTab,
+					 zCol);
 		} else {
-			sqlErrorMsg(pParse, "ambiguous column name: %s", zCol);
+			err = tt_sprintf("ambiguous column name: %s", zCol);
 		}
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+		pParse->is_aborted = true;
 		pTopNC->nErr++;
 	}
 	if (cnt == 0) {
@@ -626,7 +634,7 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 			} else {
 				is_agg = pDef->xFinalize != 0;
 				pExpr->type = pDef->ret_type;
-				const char *err_msg =
+				const char *err =
 					"second argument to likelihood() must "\
 					"be a constant between 0.0 and 1.0";
 				if (pDef->funcFlags & SQL_FUNC_UNLIKELY) {
@@ -639,7 +647,7 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 						if (pExpr->iTable < 0) {
 							diag_set(ClientError,
 								 ER_ILLEGAL_PARAMS,
-								 err_msg);
+								 err);
 							pParse->is_aborted =
 								true;
 							pNC->nErr++;
@@ -679,9 +687,11 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 				}
 			}
 			if (is_agg && (pNC->ncFlags & NC_AllowAgg) == 0) {
-				sqlErrorMsg(pParse,
-						"misuse of aggregate function %.*s()",
-						nId, zId);
+				const char *err =
+					tt_sprintf("misuse of aggregate "\
+						   "function %.*s()", nId, zId);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+				pParse->is_aborted = true;
 				pNC->nErr++;
 				is_agg = 0;
 			} else if (no_such_func && pParse->db->init.busy == 0
@@ -693,9 +703,11 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 				pParse->is_aborted = true;
 				pNC->nErr++;
 			} else if (wrong_num_args) {
-				sqlErrorMsg(pParse,
-						"wrong number of arguments to function %.*s()",
-						nId, zId);
+				const char *err = "wrong number of arguments "\
+						  "to function %.*s()";
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 tt_sprintf(err, nId, zId));
+				pParse->is_aborted = true;
 				pNC->nErr++;
 			}
 			if (is_agg)
@@ -796,7 +808,9 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 				testcase(pExpr->op == TK_GT);
 				testcase(pExpr->op == TK_GE);
 				testcase(pExpr->op == TK_BETWEEN);
-				sqlErrorMsg(pParse, "row value misused");
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 "row value misused");
+				pParse->is_aborted = true;
 			}
 			break;
 		}
@@ -898,21 +912,6 @@ resolveOrderByTermToExprList(Parse * pParse,	/* Parsing context for error messag
 }
 
 /*
- * Generate an ORDER BY or GROUP BY term out-of-range error.
- */
-static void
-resolveOutOfRangeError(Parse * pParse,	/* The error context into which to write the error */
-		       const char *zType,	/* "ORDER" or "GROUP" */
-		       int i,	/* The index (1-based) of the term out of range */
-		       int mx	/* Largest permissible value of i */
-    )
-{
-	sqlErrorMsg(pParse,
-			"%r %s BY term out of range - should be "
-			"between 1 and %d", i, zType, mx);
-}
-
-/*
  * Analyze the ORDER BY clause in a compound SELECT statement.   Modify
  * each term of the ORDER BY clause is a constant integer between 1
  * and N where N is the number of columns in the compound SELECT.
@@ -973,9 +972,15 @@ resolveCompoundOrderBy(Parse * pParse,	/* Parsing context.  Leave error messages
 			pE = sqlExprSkipCollate(pItem->pExpr);
 			if (sqlExprIsInteger(pE, &iCol)) {
 				if (iCol <= 0 || iCol > pEList->nExpr) {
-					resolveOutOfRangeError(pParse, "ORDER",
-							       i + 1,
-							       pEList->nExpr);
+					const char *err =
+						"Error at ORDER BY in place "\
+						"%d: term out of range - "\
+						"should be between 1 and %d";
+					err = tt_sprintf(err, i + 1,
+							 pEList->nExpr);
+					diag_set(ClientError,
+						 ER_SQL_PARSER_GENERIC, err);
+					pParse->is_aborted = true;
 					return 1;
 				}
 			} else {
@@ -1021,9 +1026,12 @@ resolveCompoundOrderBy(Parse * pParse,	/* Parsing context.  Leave error messages
 	}
 	for (i = 0; i < pOrderBy->nExpr; i++) {
 		if (pOrderBy->a[i].done == 0) {
-			sqlErrorMsg(pParse,
-					"%r ORDER BY term does not match any "
-					"column in the result set", i + 1);
+			const char *err = "Error at ORDER BY in place %d: "\
+					  "term does not match any column in "\
+					  "the result set";
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+				 tt_sprintf(err, i + 1));
+			pParse->is_aborted = true;
 			return 1;
 		}
 	}
@@ -1071,8 +1079,14 @@ sqlResolveOrderGroupBy(Parse * pParse,	/* Parsing context.  Leave error messages
 	for (i = 0, pItem = pOrderBy->a; i < pOrderBy->nExpr; i++, pItem++) {
 		if (pItem->u.x.iOrderByCol) {
 			if (pItem->u.x.iOrderByCol > pEList->nExpr) {
-				resolveOutOfRangeError(pParse, zType, i + 1,
-						       pEList->nExpr);
+				const char *err = "Error at %s BY in place "\
+						  "%d: term out of range - "\
+						  "should be between 1 and %d";
+				err = tt_sprintf(err, zType, i + 1,
+						 pEList->nExpr);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 err);
+				pParse->is_aborted = true;
 				return 1;
 			}
 			resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol - 1,
@@ -1136,8 +1150,13 @@ resolveOrderGroupBy(NameContext * pNC,	/* The name context of the SELECT stateme
 			 * order-by term to a copy of the result-set expression
 			 */
 			if (iCol < 1 || iCol > 0xffff) {
-				resolveOutOfRangeError(pParse, zType, i + 1,
-						       nResult);
+				const char *err = "Error at %s BY in place "\
+						  "%d: term out of range - "\
+						  "should be between 1 and %d";
+				err = tt_sprintf(err, zType, i + 1, nResult);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 err);
+				pParse->is_aborted = true;
 				return 1;
 			}
 			pItem->u.x.iOrderByCol = (u16) iCol;
@@ -1417,12 +1436,15 @@ resolveSelectStep(Walker * pWalker, Select * p)
 			    || db->mallocFailed) {
 				return WRC_Abort;
 			}
+			const char *err_msg = "aggregate functions are not "\
+					      "allowed in the GROUP BY clause";
 			for (i = 0, pItem = pGroupBy->a; i < pGroupBy->nExpr;
 			     i++, pItem++) {
 				if (ExprHasProperty(pItem->pExpr, EP_Agg)) {
-					sqlErrorMsg(pParse,
-							"aggregate functions are not allowed in "
-							"the GROUP BY clause");
+					diag_set(ClientError,
+						 ER_SQL_PARSER_GENERIC,
+						 err_msg);
+					pParse->is_aborted = true;
 					return WRC_Abort;
 				}
 			}
@@ -1432,10 +1454,22 @@ resolveSelectStep(Walker * pWalker, Select * p)
 		 * number of expressions in the select list.
 		 */
 		if (p->pNext && p->pEList->nExpr != p->pNext->pEList->nExpr) {
-			sqlSelectWrongNumTermsError(pParse, p->pNext);
+			if (p->pNext->selFlags & SF_Values) {
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 "all VALUES must have the same "\
+					 "number of terms");
+			} else {
+				const char *err =
+					"SELECTs to the left and right of %s "\
+					"do not have the same number of "\
+					"result columns";
+				const char *op = select_op_name(p->pNext->op);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 tt_sprintf(err, op));
+			}
+			pParse->is_aborted = true;
 			return WRC_Abort;
 		}
-
 		/* Advance to the next term of the compound
 		 */
 		p = p->pPrior;
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 23c9499..778de2c 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -397,13 +397,19 @@ sqlJoinType(Parse * pParse, Token * pA, Token * pB, Token * pC)
 	}
 	if ((jointype & (JT_INNER | JT_OUTER)) == (JT_INNER | JT_OUTER) ||
 	    (jointype & JT_ERROR) != 0) {
-		const char *zSp = " ";
 		assert(pB != 0);
-		if (pC == 0) {
-			zSp++;
+		const char *err;
+		if (pC == NULL) {
+			err = tt_sprintf("unknown or unsupported join type: "\
+					 "%.*s %.*s", pA->n, pA->z, pB->n,
+					 pB->z);
+		} else {
+			err = tt_sprintf("unknown or unsupported join type: "\
+					 "%.*s %.*s %.*s", pA->n, pA->z, pB->n,
+					 pB->z, pC->n, pC->z);
 		}
-		sqlErrorMsg(pParse, "unknown or unsupported join type: "
-				"%T %T%s%T", pA, pB, zSp, pC);
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+		pParse->is_aborted = true;
 		jointype = JT_INNER;
 	} else if ((jointype & JT_OUTER) != 0
 		   && (jointype & (JT_LEFT | JT_RIGHT)) != JT_LEFT) {
@@ -590,9 +596,10 @@ sqlProcessJoin(Parse * pParse, Select * p)
 		 */
 		if (pRight->fg.jointype & JT_NATURAL) {
 			if (pRight->pOn || pRight->pUsing) {
-				sqlErrorMsg(pParse,
-						"a NATURAL join may not have "
-						"an ON or USING clause", 0);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 "a NATURAL join may not have "
+					 "an ON or USING clause");
+				pParse->is_aborted = true;
 				return 1;
 			}
 			for (j = 0; j < (int)right_space->def->field_count; j++) {
@@ -613,8 +620,10 @@ sqlProcessJoin(Parse * pParse, Select * p)
 		/* Disallow both ON and USING clauses in the same join
 		 */
 		if (pRight->pOn && pRight->pUsing) {
-			sqlErrorMsg(pParse, "cannot have both ON and USING "
-					"clauses in the same join");
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+				 "cannot have both ON and USING clauses in "\
+				 "the same join");
+			pParse->is_aborted = true;
 			return 1;
 		}
 
@@ -637,6 +646,8 @@ sqlProcessJoin(Parse * pParse, Select * p)
 		 * not contained in both tables to be joined.
 		 */
 		if (pRight->pUsing) {
+			const char *err = "cannot join using column %s - "\
+					  "column not present in both tables";
 			IdList *pList = pRight->pUsing;
 			for (j = 0; j < pList->nId; j++) {
 				char *zName;	/* Name of the term in the USING clause */
@@ -650,10 +661,10 @@ sqlProcessJoin(Parse * pParse, Select * p)
 				    || !tableAndColumnIndex(pSrc, i + 1, zName,
 							    &iLeft, &iLeftCol)
 				    ) {
-					sqlErrorMsg(pParse,
-							"cannot join using column %s - column "
-							"not present in both tables",
-							zName);
+					err = tt_sprintf(err, zName);
+					diag_set(ClientError,
+						 ER_SQL_PARSER_GENERIC, err);
+					pParse->is_aborted = true;
 					return 1;
 				}
 				addWhereTerm(pParse, pSrc, iLeft, iLeftCol,
@@ -1455,25 +1466,25 @@ sql_expr_list_to_key_info(struct Parse *parse, struct ExprList *list, int start)
 /*
  * Name of the connection operator, used for error messages.
  */
-static const char *
-selectOpName(int id)
+const char *
+select_op_name(int op_id)
 {
-	char *z;
-	switch (id) {
+	const char *op_name;
+	switch (op_id) {
 	case TK_ALL:
-		z = "UNION ALL";
+		op_name = "UNION ALL";
 		break;
 	case TK_INTERSECT:
-		z = "INTERSECT";
+		op_name = "INTERSECT";
 		break;
 	case TK_EXCEPT:
-		z = "EXCEPT";
+		op_name = "EXCEPT";
 		break;
 	default:
-		z = "UNION";
+		op_name = "UNION";
 		break;
 	}
-	return z;
+	return op_name;
 }
 
 /*
@@ -1531,7 +1542,7 @@ explainComposite(Parse * pParse,	/* Parse context */
 				   "COMPOUND SUBQUERIES %d AND %d %s(%s)",
 				   iSub1, iSub2,
 				   bUseTmp ? "USING TEMP B-TREE " : "",
-				   selectOpName(op)
+				   select_op_name(op)
 		    );
 		sqlVdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg,
 				  P4_DYNAMIC);
@@ -2601,16 +2612,20 @@ multiSelect(Parse * pParse,	/* Parsing context */
 	pPrior = p->pPrior;
 	dest = *pDest;
 	if (pPrior->pOrderBy) {
-		sqlErrorMsg(pParse,
-				"ORDER BY clause should come after %s not before",
-				selectOpName(p->op));
+		const char *err_msg =
+			tt_sprintf("ORDER BY clause should come after %s not "\
+				   "before", select_op_name(p->op));
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+		pParse->is_aborted = true;
 		rc = 1;
 		goto multi_select_end;
 	}
 	if (pPrior->pLimit) {
-		sqlErrorMsg(pParse,
-				"LIMIT clause should come after %s not before",
-				selectOpName(p->op));
+		const char *err_msg =
+			tt_sprintf("LIMIT clause should come after %s not "\
+				   "before", select_op_name(p->op));
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+		pParse->is_aborted = true;
 		rc = 1;
 		goto multi_select_end;
 	}
@@ -2988,19 +3003,6 @@ multiSelect(Parse * pParse,	/* Parsing context */
 }
 #endif				/* SQL_OMIT_COMPOUND_SELECT */
 
-void
-sqlSelectWrongNumTermsError(struct Parse *parse, struct Select * p)
-{
-	if (p->selFlags & SF_Values) {
-		sqlErrorMsg(parse, "all VALUES must have the same number "\
-				"of terms");
-	} else {
-		sqlErrorMsg(parse, "SELECTs to the left and right of %s "
-				"do not have the same number of result columns",
-				selectOpName(p->op));
-	}
-}
-
 /**
  * Code an output subroutine for a coroutine implementation of a
  * SELECT statment.
@@ -3624,7 +3626,14 @@ substExpr(Parse * pParse,	/* Report errors here */
 			assert(pEList != 0 && pExpr->iColumn < pEList->nExpr);
 			assert(pExpr->pLeft == 0 && pExpr->pRight == 0);
 			if (sqlExprIsVector(pCopy)) {
-				sqlVectorErrorMsg(pParse, pCopy);
+				assert((pCopy->flags & EP_xIsSelect) != 0);
+				const char *err = "sub-select returns %d "\
+						  "columns - expected 1";
+				int expr_count =
+					pCopy->x.pSelect->pEList->nExpr;
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 tt_sprintf(err, expr_count));
+				pParse->is_aborted = true;
 			} else {
 				pNew = sqlExprDup(db, pCopy, 0);
 				if (pNew && (pExpr->flags & EP_FromJoin)) {
@@ -4520,21 +4529,6 @@ convertCompoundSelectToSubquery(Walker * pWalker, Select * p)
 	return WRC_Continue;
 }
 
-/*
- * Check to see if the FROM clause term pFrom has table-valued function
- * arguments.  If it does, leave an error message in pParse and return
- * non-zero, since pFrom is not allowed to be a table-valued function.
- */
-static int
-cannotBeFunction(Parse * pParse, struct SrcList_item *pFrom)
-{
-	if (pFrom->fg.isTabFunc) {
-		sqlErrorMsg(pParse, "'%s' is not a function", pFrom->zName);
-		return 1;
-	}
-	return 0;
-}
-
 #ifndef SQL_OMIT_CTE
 /*
  * Argument pWith (which may be NULL) points to a linked list of nested
@@ -4629,11 +4623,18 @@ withExpand(Walker * pWalker, struct SrcList_item *pFrom)
 		 * In this case, proceed.
 		 */
 		if (pCte->zCteErr) {
-			sqlErrorMsg(pParse, pCte->zCteErr, pCte->zName);
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+				 tt_sprintf(pCte->zCteErr, pCte->zName));
+			pParse->is_aborted = true;
 			return SQL_ERROR;
 		}
-		if (cannotBeFunction(pParse, pFrom))
+		if (pFrom->fg.isTabFunc) {
+			const char *err = "'%s' is not a function";
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+				 tt_sprintf(err, pFrom->zName));
+			pParse->is_aborted = true;
 			return SQL_ERROR;
+		}
 
 		assert(pFrom->space == NULL);
 		pFrom->space = sql_ephemeral_space_new(pParse, pCte->zName);
@@ -4665,9 +4666,11 @@ withExpand(Walker * pWalker, struct SrcList_item *pFrom)
 			}
 		}
 		if (ref_counter > 1) {
-			sqlErrorMsg(pParse,
-					"multiple references to recursive table: %s",
-					pCte->zName);
+			const char *err_msg =
+				tt_sprintf("multiple references to recursive "\
+					   "table: %s", pCte->zName);
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+			pParse->is_aborted = true;
 			return SQL_ERROR;
 		}
 		assert(ref_counter == 0 ||
@@ -4683,10 +4686,13 @@ withExpand(Walker * pWalker, struct SrcList_item *pFrom)
 		pEList = pLeft->pEList;
 		if (pCte->pCols) {
 			if (pEList && pEList->nExpr != pCte->pCols->nExpr) {
-				sqlErrorMsg(pParse,
-						"table %s has %d values for %d columns",
-						pCte->zName, pEList->nExpr,
-						pCte->pCols->nExpr);
+				const char *err_msg =
+					tt_sprintf("table %s has %d values "\
+						   "for %d columns",
+						   pCte->zName, pEList->nExpr,
+						   pCte->pCols->nExpr);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+				pParse->is_aborted = true;
 				pParse->pWith = pSavedWith;
 				return SQL_ERROR;
 			}
@@ -4842,8 +4848,15 @@ selectExpander(Walker * pWalker, Select * p)
 			struct space *space = sql_lookup_space(pParse, pFrom);
 			if (space == NULL)
 				return WRC_Abort;
-			if (cannotBeFunction(pParse, pFrom))
+			if (pFrom->fg.isTabFunc) {
+				const char *err =
+					tt_sprintf("'%s' is not a function",
+						   pFrom->zName);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 err);
+				pParse->is_aborted = true;
 				return WRC_Abort;
+			}
 			if (space->def->opts.is_view) {
 				struct Select *select =
 					sql_view_compile(db, space->def->opts.sql);
@@ -5254,9 +5267,10 @@ resetAccumulator(Parse * pParse, AggInfo * pAggInfo)
 			Expr *pE = pFunc->pExpr;
 			assert(!ExprHasProperty(pE, EP_xIsSelect));
 			if (pE->x.pList == 0 || pE->x.pList->nExpr != 1) {
-				sqlErrorMsg(pParse,
-						"DISTINCT aggregates must have exactly one "
-						"argument");
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 "DISTINCT aggregates must have "\
+					 "exactly one argument");
+				pParse->is_aborted = true;
 				pFunc->iDistinct = -1;
 			} else {
 				struct sql_key_info *key_info =
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 8fb2b18..50fc812 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -3193,7 +3193,6 @@ void sqlTreeViewWith(TreeView *, const With *);
 #endif
 
 void sqlSetString(char **, sql *, const char *);
-void sqlErrorMsg(Parse *, const char *, ...);
 void sqlDequote(char *);
 void sqlNormalizeName(char *z);
 void sqlTokenInit(Token *, char *);
@@ -4428,14 +4427,13 @@ int sqlCodeSubselect(Parse *, Expr *, int);
 void sqlSelectPrep(Parse *, Select *, NameContext *);
 
 /**
- * Error message for when two or more terms of a compound select
- * have different size result sets.
+ * Returns name of the connection operator.
  *
- * @param parse Parsing context.
- * @param p Select struct to analyze.
+ * @param op_id ID of the connection operator.
+ * @retval Name of the connection operator.
  */
-void
-sqlSelectWrongNumTermsError(struct Parse *parse, struct Select *p);
+const char *
+select_op_name(int op_id);
 
 int sqlMatchSpanName(const char *, const char *, const char *);
 int sqlResolveExprNames(NameContext *, Expr *);
@@ -4884,7 +4882,6 @@ int sqlExprVectorSize(Expr * pExpr);
 int sqlExprIsVector(Expr * pExpr);
 Expr *sqlVectorFieldSubexpr(Expr *, int);
 Expr *sqlExprForVectorField(Parse *, Expr *, int);
-void sqlVectorErrorMsg(Parse *, Expr *);
 
 /* Tarantool: right now query compilation is invoked on top of
  * fiber's stack. Need to limit number of nested programs under
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index b23d60a..c984950 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -621,8 +621,9 @@ codeTriggerProgram(Parse * pParse,	/* The parser context */
 	sqlSubProgramsRemaining--;
 
 	if (sqlSubProgramsRemaining == 0) {
-		sqlErrorMsg(pParse,
-				"Maximum number of chained trigger activations exceeded.");
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, "Maximum number "\
+			 "of chained trigger activations exceeded.");
+		pParse->is_aborted = true;
 	}
 
 	for (pStep = pStepList; pStep; pStep = pStep->pNext) {
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 71a1e00..0b645eb 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -179,10 +179,15 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 				    sql_space_column_is_in_pk(space, j))
 					is_pk_modified = true;
 				if (aXRef[j] != -1) {
-					sqlErrorMsg(pParse,
-							"set id list: duplicate"
-							" column name %s",
-							pChanges->a[i].zName);
+					const char *err =
+						"set id list: duplicate "\
+						"column name %s";
+					err = tt_sprintf(err,
+							 pChanges->a[i].zName);
+					diag_set(ClientError,
+						 ER_SQL_PARSER_GENERIC,
+						 err);
+					pParse->is_aborted = true;
 					goto update_cleanup;
 				}
 				aXRef[j] = i;
diff --git a/src/box/sql/util.c b/src/box/sql/util.c
index d9bb2af..cac404f 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -211,38 +211,6 @@ sqlErrorWithMsg(sql * db, int err_code, const char *zFormat, ...)
 }
 
 /*
- * Add an error to the diagnostics area and set
- * pParse->is_aborted.
- * The following formatting characters are allowed:
- *
- *      %s      Insert a string
- *      %z      A string that should be freed after use
- *      %d      Insert an integer
- *      %T      Insert a token
- *      %S      Insert the first element of a SrcList
- *
- * This function should be used to report any error that occurs while
- * compiling an SQL statement (i.e. within sql_prepare()). The
- * last thing the sql_prepare() function does is copy the error
- * stored by this function into the database handle using sqlError().
- * Functions sqlError() or sqlErrorWithMsg() should be used
- * during statement execution (sql_step() etc.).
- */
-void
-sqlErrorMsg(Parse * pParse, const char *zFormat, ...)
-{
-	char *zMsg;
-	va_list ap;
-	sql *db = pParse->db;
-	va_start(ap, zFormat);
-	zMsg = sqlVMPrintf(db, zFormat, ap);
-	va_end(ap);
-	diag_set(ClientError, ER_SQL_PARSER_GENERIC, zMsg);
-	sqlDbFree(db, zMsg);
-	pParse->is_aborted = true;
-}
-
-/*
  * Convert an SQL-style quoted string into a normal string by removing
  * the quote characters.  The conversion is done in-place.  If the
  * input does not begin with a quote character, then this routine
diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
index f417c49..15a2f55 100644
--- a/src/box/sql/vdbemem.c
+++ b/src/box/sql/vdbemem.c
@@ -1208,15 +1208,9 @@ valueFromFunction(sql * db,	/* The database connection */
 	ctx.pOut = pVal;
 	ctx.pFunc = pFunc;
 	pFunc->xSFunc(&ctx, nVal, apVal);
-	if (ctx.isError) {
-		rc = ctx.isError;
-		sqlErrorMsg(pCtx->pParse, "%s", sql_value_text(pVal));
-	} else {
-		sql_value_apply_type(pVal, type);
-		assert(rc == SQL_OK);
-	}
-	if (rc != SQL_OK)
-		pCtx->pParse->is_aborted = true;
+	assert(!ctx.isError);
+	sql_value_apply_type(pVal, type);
+	assert(rc == SQL_OK);
 
  value_from_function_out:
 	if (rc != SQL_OK) {
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index 2f83e81..19ee2d0 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -3913,11 +3913,7 @@ wherePathSolver(WhereInfo * pWInfo, LogEst nRowEst)
 		nFrom = nTo;
 	}
 
-	if (nFrom == 0) {
-		sqlErrorMsg(pParse, "no query solution");
-		sqlDbFree(db, pSpace);
-		return SQL_ERROR;
-	}
+	assert(nFrom != 0);
 
 	/* Find the lowest cost path.  pFrom will be left pointing to that path */
 	pFrom = aFrom;
diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c
index 6df28ad..ec08889 100644
--- a/src/box/sql/whereexpr.c
+++ b/src/box/sql/whereexpr.c
@@ -1496,12 +1496,12 @@ sqlWhereTabFuncArgs(Parse * pParse,	/* Parsing context */
 	for (j = k = 0; j < pArgs->nExpr; j++) {
 		while (k < (int)space_def->field_count)
 			k++;
-		if (k >= (int)space_def->field_count) {
-			sqlErrorMsg(pParse,
-					"too many arguments on %s() - max %d",
-					space_def->name, j);
-			return;
-		}
+		/*
+		 * This assert replaces error. At the moment, this
+		 * error cannot appear due to this function being
+		 * unused.
+		 */
+		assert(k < (int)space_def->field_count);
 		pColRef = sqlExprAlloc(pParse->db, TK_COLUMN, 0, 0);
 		if (pColRef == 0)
 			return;
diff --git a/test/sql-tap/e_select1.test.lua b/test/sql-tap/e_select1.test.lua
index e190ad7..8e9a2bb 100755
--- a/test/sql-tap/e_select1.test.lua
+++ b/test/sql-tap/e_select1.test.lua
@@ -1855,13 +1855,13 @@ test:do_catchsql_test(
     "e_select-8.7.1.1",
     "SELECT x FROM d1 UNION ALL SELECT a FROM d2 ORDER BY x*z",
     {
-        1, "1st ORDER BY term does not match any column in the result set"})
+        1, "Error at ORDER BY in place 1: term does not match any column in the result set"})
 
 test:do_catchsql_test(
     "e_select-8.7.1.2",
     "SELECT x,z FROM d1 UNION ALL SELECT a,b FROM d2 ORDER BY x, x/z",
     {
-        1, "2nd ORDER BY term does not match any column in the result set"})
+        1, "Error at ORDER BY in place 2: term does not match any column in the result set"})
 
 test:do_select_tests(
     "e_select-8.7.2",
@@ -2077,12 +2077,12 @@ test:do_select_tests(
 -- EVIDENCE-OF: R-39265-04070 If no matching expression can be found in
 -- the result columns of any constituent SELECT, it is an error.
 --
-for _, val in ipairs({{1, "SELECT a FROM d5 UNION SELECT c FROM d6 ORDER BY a+1", "1st"},
-    {2, "SELECT a FROM d5 UNION SELECT c FROM d6 ORDER BY a, a+1", "2nd"},
-    {3, "SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY \"hello\"", "1st"},
-    {4, "SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY blah", "1st"},
-    {5, "SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY c,d,c+d", "3rd"},
-    {6, "SELECT * FROM d5 EXCEPT SELECT * FROM d7 ORDER BY 1,2,b,a/b", "4th"}}) do
+for _, val in ipairs({{1, "SELECT a FROM d5 UNION SELECT c FROM d6 ORDER BY a+1", "1"},
+    {2, "SELECT a FROM d5 UNION SELECT c FROM d6 ORDER BY a, a+1", "2"},
+    {3, "SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY \"hello\"", "1"},
+    {4, "SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY blah", "1"},
+    {5, "SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY c,d,c+d", "3"},
+    {6, "SELECT * FROM d5 EXCEPT SELECT * FROM d7 ORDER BY 1,2,b,a/b", "4"}}) do
     local tn = val[1]
     local select = val[2]
     local err_param = val[3]
@@ -2090,7 +2090,7 @@ for _, val in ipairs({{1, "SELECT a FROM d5 UNION SELECT c FROM d6 ORDER BY a+1"
         "e_select-8.14."..tn,
         select,
         {
-            1, string.format("%s ORDER BY term does not match any column in the result set", err_param)})
+            1, string.format("Error at ORDER BY in place %s: term does not match any column in the result set", err_param)})
 end
 -- EVIDENCE-OF: R-03407-11483 Each term of the ORDER BY clause is
 -- processed separately and may be matched against result columns from
diff --git a/test/sql-tap/null.test.lua b/test/sql-tap/null.test.lua
index 50a2cfb..de4d503 100755
--- a/test/sql-tap/null.test.lua
+++ b/test/sql-tap/null.test.lua
@@ -295,7 +295,7 @@ test:do_catchsql_test(
         select b from t1 union select c from t1 order by t1.a;
     ]], {
         -- <null-6.5>
-        1, "1st ORDER BY term does not match any column in the result set"
+        1, "Error at ORDER BY in place 1: term does not match any column in the result set"
         -- </null-6.5>
     })
 
@@ -305,7 +305,7 @@ test:do_catchsql_test(
         select b from t1 union select c from t1 order by t1.a;
     ]], {
         -- <null-6.6>
-        1, "1st ORDER BY term does not match any column in the result set"
+        1, "Error at ORDER BY in place 1: term does not match any column in the result set"
         -- </null-6.6>
     })
 
diff --git a/test/sql-tap/select1.test.lua b/test/sql-tap/select1.test.lua
index d73429a..545ae9d 100755
--- a/test/sql-tap/select1.test.lua
+++ b/test/sql-tap/select1.test.lua
@@ -840,7 +840,7 @@ test:do_catchsql_test(
         SELECT * FROM t5 ORDER BY 3;
     ]], {
         -- <select1-4.10.1>
-        1, "1st ORDER BY term out of range - should be between 1 and 2"
+        1, "Error at ORDER BY in place 1: term out of range - should be between 1 and 2"
         -- </select1-4.10.1>
     })
 
@@ -850,7 +850,7 @@ test:do_catchsql_test(
         SELECT * FROM t5 ORDER BY -1;
     ]], {
         -- <select1-4.10.2>
-        1, "1st ORDER BY term out of range - should be between 1 and 2"
+        1, "Error at ORDER BY in place 1: term out of range - should be between 1 and 2"
         -- </select1-4.10.2>
     })
 
@@ -1334,7 +1334,7 @@ test:do_catchsql2_test(
             ORDER BY f2+101;
         ]], {
             -- <select1-6.11>
-            1, "1st ORDER BY term does not match any column in the result set"
+            1, "Error at ORDER BY in place 1: term does not match any column in the result set"
             -- </select1-6.11>
         })
 
diff --git a/test/sql-tap/select3.test.lua b/test/sql-tap/select3.test.lua
index 9fb825f..3c88f77 100755
--- a/test/sql-tap/select3.test.lua
+++ b/test/sql-tap/select3.test.lua
@@ -157,7 +157,7 @@ test:do_catchsql_test("select3-2.10", [[
   SELECT log, count(*) FROM t1 GROUP BY 0 ORDER BY log;
 ]], {
   -- <select3-2.10>
-  1, "1st GROUP BY term out of range - should be between 1 and 2"
+  1, "Error at GROUP BY in place 1: term out of range - should be between 1 and 2"
   -- </select3-2.10>
 })
 
@@ -165,7 +165,7 @@ test:do_catchsql_test("select3-2.11", [[
   SELECT log, count(*) FROM t1 GROUP BY 3 ORDER BY log;
 ]], {
   -- <select3-2.11>
-  1, "1st GROUP BY term out of range - should be between 1 and 2"
+  1, "Error at GROUP BY in place 1: term out of range - should be between 1 and 2"
   -- </select3-2.11>
 })
 
diff --git a/test/sql-tap/select4.test.lua b/test/sql-tap/select4.test.lua
index bd2ada9..3aafedb 100755
--- a/test/sql-tap/select4.test.lua
+++ b/test/sql-tap/select4.test.lua
@@ -450,7 +450,7 @@ test:do_catchsql_test(
         ORDER BY "xyzzy";
     ]], {
         -- <select4-5.2c>
-        1, "1st ORDER BY term does not match any column in the result set"
+        1, "Error at ORDER BY in place 1: term does not match any column in the result set"
         -- </select4-5.2c>
     })
 
@@ -463,7 +463,7 @@ test:do_catchsql_test(
         ORDER BY "xyzzy";
     ]], {
         -- <select4-5.2d>
-        1, "1st ORDER BY term does not match any column in the result set"
+        1, "Error at ORDER BY in place 1: term does not match any column in the result set"
         -- </select4-5.2d>
     })
 
@@ -515,7 +515,7 @@ test:do_catchsql_test(
         ORDER BY 2;
     ]], {
         -- <select4-5.2h>
-        1, "1st ORDER BY term out of range - should be between 1 and 1"
+        1, "Error at ORDER BY in place 1: term out of range - should be between 1 and 1"
         -- </select4-5.2h>
     })
 
diff --git a/test/sql-tap/sql-errors.test.lua b/test/sql-tap/sql-errors.test.lua
index 635df6a..5585dbb 100755
--- a/test/sql-tap/sql-errors.test.lua
+++ b/test/sql-tap/sql-errors.test.lua
@@ -1,9 +1,9 @@
 #!/usr/bin/env tarantool
 test = require("sqltester")
-test:plan(36)
+test:plan(45)
 
 test:execsql([[
-	CREATE TABLE t0 (i INT PRIMARY KEY);
+	CREATE TABLE t0 (i INT PRIMARY KEY, a INT);
 	CREATE VIEW v0 AS SELECT * FROM t0;
 ]])
 format = {}
@@ -79,7 +79,7 @@ test:do_catchsql_test(
 test:do_catchsql_test(
 	"sql-errors-1.7",
 	[[
-		CREATE VIEW v7(a,b) AS SELECT * FROM t0;
+		CREATE VIEW v7(a,b,c) AS SELECT * FROM t0;
 	]], {
 		-- <sql-errors-1.7>
 		1,"Failed to create space 'V7': number of aliases doesn't match provided columns"
@@ -416,4 +416,94 @@ test:do_catchsql_test(
 		-- </sql-errors-1.36>
 	})
 
+test:do_catchsql_test(
+	"sql-errors-1.37",
+	[[
+		CREATE TRIGGER r0 AFTER INSERT ON t0 BEGIN INSERT INTO t0.i VALUES (2); END;
+	]], {
+		-- <sql-errors-1.37>
+		1,"qualified table names are not allowed on INSERT, UPDATE, and DELETE statements within triggers"
+		-- </sql-errors-1.37>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.38",
+	[[
+		UPDATE t0 SET (i, a) = (100,1,1);
+	]], {
+		-- <sql-errors-1.38>
+		1,"2 columns assigned 3 values"
+		-- </sql-errors-1.38>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.39",
+	[[
+		SELECT * FROM t0();
+	]], {
+		-- <sql-errors-1.39>
+		1,"'T0' is not a function"
+		-- </sql-errors-1.39>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.40",
+	[[
+		SELECT $0;
+	]], {
+		-- <sql-errors-1.40>
+		1,"Index of binding slots must start from 1"
+		-- </sql-errors-1.40>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.41",
+	[[
+		SELECT (1,2,3) == (1,2,3,4);
+	]], {
+		-- <sql-errors-1.41>
+		1,"row value misused"
+		-- </sql-errors-1.41>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.42",
+	[[
+		SELECT (1, 2);
+	]], {
+		-- <sql-errors-1.42>
+		1,"row value misused"
+		-- </sql-errors-1.42>
+	})
+
+test:do_catchsql_test(
+	"sql-errors-1.43",
+	[[
+		SELECT (i,a) AS m FROM t0 WHERE m < 1;
+	]], {
+		-- <sql-errors-1.43>
+		1,"row value misused"
+		-- </sql-errors-1.43>
+	})
+
+test:do_execsql_test(
+	"sql-errors-1.44",
+	[[
+		SELECT (1, 2, 3) < (1, 2, 4);
+	]], {
+		-- <sql-errors-1.44>
+		1
+		-- </sql-errors-1.44>
+	})
+
+test:do_execsql_test(
+	"sql-errors-1.45",
+	[[
+		SELECT (1, 2, 3) < (1, 2, 2);
+	]], {
+		-- <sql-errors-1.45>
+		0
+		-- </sql-errors-1.45>
+	})
+
 test:finish_test()
diff --git a/test/sql-tap/tkt2822.test.lua b/test/sql-tap/tkt2822.test.lua
index 4212cbd..86674ae 100755
--- a/test/sql-tap/tkt2822.test.lua
+++ b/test/sql-tap/tkt2822.test.lua
@@ -200,7 +200,7 @@ test:do_catchsql_test(
         SELECT a, b, c FROM t1 UNION ALL SELECT a, b, c FROM t2 ORDER BY x
     ]], {
         -- <tkt2822-4.1>
-        1, "1st ORDER BY term does not match any column in the result set"
+        1, "Error at ORDER BY in place 1: term does not match any column in the result set"
         -- </tkt2822-4.1>
     })
 
@@ -298,7 +298,7 @@ test:do_test(
         ]]
     end, {
         -- <tkt2822-7.1>
-        1, "1st ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 1: term out of range - should be between 1 and 25"
         -- </tkt2822-7.1>
     })
 
@@ -308,7 +308,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 0;
     ]], {
         -- <tkt2822-7.2.1>
-        1, "2nd ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 2: term out of range - should be between 1 and 25"
         -- </tkt2822-7.2.1>
     })
 
@@ -318,7 +318,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 26;
     ]], {
         -- <tkt2822-7.2.2>
-        1, "2nd ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 2: term out of range - should be between 1 and 25"
         -- </tkt2822-7.2.2>
     })
 
@@ -328,7 +328,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 65536;
     ]], {
         -- <tkt2822-7.2.3>
-        1, "2nd ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 2: term out of range - should be between 1 and 25"
         -- </tkt2822-7.2.3>
     })
 
@@ -338,7 +338,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 2, 0;
     ]], {
         -- <tkt2822-7.3>
-        1, "3rd ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 3: term out of range - should be between 1 and 25"
         -- </tkt2822-7.3>
     })
 
@@ -348,7 +348,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 2, 3, 0;
     ]], {
         -- <tkt2822-7.4>
-        1, "4th ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 4: term out of range - should be between 1 and 25"
         -- </tkt2822-7.4>
     })
 
@@ -358,7 +358,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 0;
     ]], {
         -- <tkt2822-7.9>
-        1, "9th ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 9: term out of range - should be between 1 and 25"
         -- </tkt2822-7.9>
     })
 
@@ -368,7 +368,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 0;
     ]], {
         -- <tkt2822-7.10>
-        1, "10th ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 10: term out of range - should be between 1 and 25"
         -- </tkt2822-7.10>
     })
 
@@ -378,7 +378,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0;
     ]], {
         -- <tkt2822-7.11>
-        1, "11th ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 11: term out of range - should be between 1 and 25"
         -- </tkt2822-7.11>
     })
 
@@ -388,7 +388,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 0;
     ]], {
         -- <tkt2822-7.12>
-        1, "12th ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 12: term out of range - should be between 1 and 25"
         -- </tkt2822-7.12>
     })
 
@@ -398,7 +398,7 @@ test:do_catchsql_test(
         SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 0;
     ]], {
         -- <tkt2822-7.13>
-        1, "13th ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 13: term out of range - should be between 1 and 25"
         -- </tkt2822-7.13>
     })
 
@@ -409,7 +409,7 @@ test:do_catchsql_test(
                                  11,12,13,14,15,16,17,18,19, 0
     ]], {
         -- <tkt2822-7.20>
-        1, "20th ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 20: term out of range - should be between 1 and 25"
         -- </tkt2822-7.20>
     })
 
@@ -420,7 +420,7 @@ test:do_catchsql_test(
                                  11,12,13,14,15,16,17,18,19, 20, 0
     ]], {
         -- <tkt2822-7.21>
-        1, "21st ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 21: term out of range - should be between 1 and 25"
         -- </tkt2822-7.21>
     })
 
@@ -431,7 +431,7 @@ test:do_catchsql_test(
                                  11,12,13,14,15,16,17,18,19, 20, 21, 0
     ]], {
         -- <tkt2822-7.22>
-        1, "22nd ORDER BY term out of range - should be between 1 and 25"
+        1, "Error at ORDER BY in place 22: term out of range - should be between 1 and 25"
         -- </tkt2822-7.22>
     })
 
diff --git a/test/sql-tap/with1.test.lua b/test/sql-tap/with1.test.lua
index 16c9b12..f1a1699 100755
--- a/test/sql-tap/with1.test.lua
+++ b/test/sql-tap/with1.test.lua
@@ -134,7 +134,7 @@ test:do_catchsql_test(3.2, [[
   SELECT * FROM tmp;
 ]], {
   -- <3.2>
-  1, "duplicate WITH table name: TMP"
+  1, "Ambiguous table name in WITH query: TMP"
   -- </3.2>
 })
 
@@ -782,7 +782,7 @@ test:do_catchsql_test("10.7.1", [[
   SELECT * FROM t
 ]], {
   -- <10.7.1>
-  1, "1st ORDER BY term does not match any column in the result set"
+  1, "Error at ORDER BY in place 1: term does not match any column in the result set"
   -- </10.7.1>
 })
 
diff --git a/test/sql/iproto.result b/test/sql/iproto.result
index 56099fa..48376c8 100644
--- a/test/sql/iproto.result
+++ b/test/sql/iproto.result
@@ -571,7 +571,8 @@ cn:execute('select ?1, ?2, ?3', {1, 2, 3})
 ...
 cn:execute('select $name, $name2', {1, 2})
 ---
-- error: 'Failed to execute SQL statement: SQL bind parameter limit reached: 65000'
+- error: 'Failed to execute SQL statement: Index of binding slots must start from
+    1'
 ...
 parameters = {}
 ---

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 7/8] sql: rework semantic errors
  2019-03-26 16:56         ` Mergen Imeev
@ 2019-03-26 18:16           ` n.pettik
  2019-03-26 19:20             ` Mergen Imeev
  0 siblings, 1 reply; 43+ messages in thread
From: n.pettik @ 2019-03-26 18:16 UTC (permalink / raw)
  To: tarantool-patches; +Cc: imeev Mergen

[-- Attachment #1: Type: text/plain, Size: 663 bytes --]


> #if SQL_MAX_COLUMN
> 	if (pOrderBy->nExpr > db->aLimit[SQL_LIMIT_COLUMN]) {
> -		const char *err_msg =
> -			is_order_by ? "The number of terms in ORDER BY clause" :
> -				      "The number of terms in GROUP BY clause";
> +		const char *err_msg;
> +		if (zType[0] != 'G')
> +			err_msg = "The number of terms in ORDER BY clause";
> +		else
> +			err_msg = "The number of terms in GROUP BY clause”;

Just use format string and sprintf - it allows to remove branching
(like it was in original SQLite code).

> 
> New patch:
> 

Please, don’t attach whole huge patch when it comes for several light-fixes.
I won’t look at it anyway.


[-- Attachment #2: Type: text/html, Size: 20652 bytes --]

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 8/8] sql: remove sqlErrorMsg()
  2019-03-26 17:52         ` Mergen Imeev
@ 2019-03-26 18:28           ` n.pettik
  2019-03-26 19:21             ` Mergen Imeev
  0 siblings, 1 reply; 43+ messages in thread
From: n.pettik @ 2019-03-26 18:28 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen

[-- Attachment #1: Type: text/plain, Size: 1022 bytes --]


> diff --git a/src/box/sql/select.c b/src/box/sql/select.c
> index e217c19..778de2c 100644
> --- a/src/box/sql/select.c
> +++ b/src/box/sql/select.c
> @@ -1466,25 +1466,25 @@ sql_expr_list_to_key_info(struct Parse *parse, struct ExprList *list, int start)
> /*
>  * Name of the connection operator, used for error messages.
>  */
> -static const char *
> -selectOpName(int id)
> +const char *
> +select_op_name(int op_id)

Add sql_ prefix.

> {
> -	char *z;
> -	switch (id) {
> +	const char *op_name;

IMHO don’t see much sense in this renaming: function is small and straight
enough to use old brief names.

> +	switch (op_id) {
> 	case TK_ALL:
> -		z = "UNION ALL";
> +		op_name = "UNION ALL";
> 		break;
> 	case TK_INTERSECT:
> -		z = "INTERSECT";
> +		op_name = "INTERSECT";
> 		break;
> 	case TK_EXCEPT:
> -		z = "EXCEPT";
> +		op_name = "EXCEPT";
> 		break;
> 	default:
> -		z = "UNION";
> +		op_name = "UNION";
> 		break;
> 	}
> -	return z;
> +	return op_name;
> }


[-- Attachment #2: Type: text/html, Size: 45535 bytes --]

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 7/8] sql: rework semantic errors
  2019-03-26 18:16           ` n.pettik
@ 2019-03-26 19:20             ` Mergen Imeev
  2019-03-26 21:36               ` n.pettik
  0 siblings, 1 reply; 43+ messages in thread
From: Mergen Imeev @ 2019-03-26 19:20 UTC (permalink / raw)
  To: n.pettik; +Cc: tarantool-patches

On Tue, Mar 26, 2019 at 09:16:08PM +0300, n.pettik wrote:
> 
> > #if SQL_MAX_COLUMN
> > 	if (pOrderBy->nExpr > db->aLimit[SQL_LIMIT_COLUMN]) {
> > -		const char *err_msg =
> > -			is_order_by ? "The number of terms in ORDER BY clause" :
> > -				      "The number of terms in GROUP BY clause";
> > +		const char *err_msg;
> > +		if (zType[0] != 'G')
> > +			err_msg = "The number of terms in ORDER BY clause";
> > +		else
> > +			err_msg = "The number of terms in GROUP BY clause”;
> 
> Just use format string and sprintf - it allows to remove branching
> (like it was in original SQLite code).
> 
Fixed:

diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 30a6b5f..747e4c4 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -1055,12 +1055,9 @@ sqlResolveOrderGroupBy(Parse * pParse,	/* Parsing context.  Leave error messages
 		return 0;
 #if SQL_MAX_COLUMN
 	if (pOrderBy->nExpr > db->aLimit[SQL_LIMIT_COLUMN]) {
-		const char *err_msg;
-		if (zType[0] != 'G')
-			err_msg = "The number of terms in ORDER BY clause";
-		else
-			err_msg = "The number of terms in GROUP BY clause";
-		diag_set(ClientError, ER_SQL_PARSER_LIMIT, err_msg,
+		const char *err = tt_sprintf("The number of terms in %s BY "\
+					     "clause", zType);
+		diag_set(ClientError, ER_SQL_PARSER_LIMIT, err,
 			 pOrderBy->nExpr, db->aLimit[SQL_LIMIT_COLUMN]);
 		pParse->is_aborted = true;
 		return 1;

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 8/8] sql: remove sqlErrorMsg()
  2019-03-26 18:28           ` n.pettik
@ 2019-03-26 19:21             ` Mergen Imeev
  2019-03-26 21:36               ` n.pettik
  0 siblings, 1 reply; 43+ messages in thread
From: Mergen Imeev @ 2019-03-26 19:21 UTC (permalink / raw)
  To: n.pettik; +Cc: tarantool-patches

On Tue, Mar 26, 2019 at 09:28:21PM +0300, n.pettik wrote:
> 
> > diff --git a/src/box/sql/select.c b/src/box/sql/select.c
> > index e217c19..778de2c 100644
> > --- a/src/box/sql/select.c
> > +++ b/src/box/sql/select.c
> > @@ -1466,25 +1466,25 @@ sql_expr_list_to_key_info(struct Parse *parse, struct ExprList *list, int start)
> > /*
> >  * Name of the connection operator, used for error messages.
> >  */
> > -static const char *
> > -selectOpName(int id)
> > +const char *
> > +select_op_name(int op_id)
> 
> Add sql_ prefix.
> 
Fixed.

> > {
> > -	char *z;
> > -	switch (id) {
> > +	const char *op_name;
> 
> IMHO don’t see much sense in this renaming: function is small and straight
> enough to use old brief names.
> 
Reverted these changes back.

Diff:

diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index a12e7ad..dd51151 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -1460,7 +1460,8 @@ resolveSelectStep(Walker * pWalker, Select * p)
 					"SELECTs to the left and right of %s "\
 					"do not have the same number of "\
 					"result columns";
-				const char *op = select_op_name(p->pNext->op);
+				const char *op =
+					sql_select_op_name(p->pNext->op);
 				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
 					 tt_sprintf(err, op));
 			}
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 778de2c..2ab4761 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -1463,28 +1463,25 @@ sql_expr_list_to_key_info(struct Parse *parse, struct ExprList *list, int start)
 	return key_info;
 }
 
-/*
- * Name of the connection operator, used for error messages.
- */
 const char *
-select_op_name(int op_id)
+sql_select_op_name(int id)
 {
-	const char *op_name;
-	switch (op_id) {
+	char *z;
+	switch (id) {
 	case TK_ALL:
-		op_name = "UNION ALL";
+		z = "UNION ALL";
 		break;
 	case TK_INTERSECT:
-		op_name = "INTERSECT";
+		z = "INTERSECT";
 		break;
 	case TK_EXCEPT:
-		op_name = "EXCEPT";
+		z = "EXCEPT";
 		break;
 	default:
-		op_name = "UNION";
+		z = "UNION";
 		break;
 	}
-	return op_name;
+	return z;
 }
 
 /*
@@ -1542,7 +1539,7 @@ explainComposite(Parse * pParse,	/* Parse context */
 				   "COMPOUND SUBQUERIES %d AND %d %s(%s)",
 				   iSub1, iSub2,
 				   bUseTmp ? "USING TEMP B-TREE " : "",
-				   select_op_name(op)
+				   sql_select_op_name(op)
 		    );
 		sqlVdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg,
 				  P4_DYNAMIC);
@@ -2614,7 +2611,7 @@ multiSelect(Parse * pParse,	/* Parsing context */
 	if (pPrior->pOrderBy) {
 		const char *err_msg =
 			tt_sprintf("ORDER BY clause should come after %s not "\
-				   "before", select_op_name(p->op));
+				   "before", sql_select_op_name(p->op));
 		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
 		pParse->is_aborted = true;
 		rc = 1;
@@ -2623,7 +2620,7 @@ multiSelect(Parse * pParse,	/* Parsing context */
 	if (pPrior->pLimit) {
 		const char *err_msg =
 			tt_sprintf("LIMIT clause should come after %s not "\
-				   "before", select_op_name(p->op));
+				   "before", sql_select_op_name(p->op));
 		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
 		pParse->is_aborted = true;
 		rc = 1;
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 50fc812..b32c3d3 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -4429,11 +4429,11 @@ void sqlSelectPrep(Parse *, Select *, NameContext *);
 /**
  * Returns name of the connection operator.
  *
- * @param op_id ID of the connection operator.
+ * @param id ID of the connection operator.
  * @retval Name of the connection operator.
  */
 const char *
-select_op_name(int op_id);
+sql_select_op_name(int id);
 
 int sqlMatchSpanName(const char *, const char *, const char *);
 int sqlResolveExprNames(NameContext *, Expr *);

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 7/8] sql: rework semantic errors
  2019-03-26 19:20             ` Mergen Imeev
@ 2019-03-26 21:36               ` n.pettik
  0 siblings, 0 replies; 43+ messages in thread
From: n.pettik @ 2019-03-26 21:36 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen

LGTM

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 8/8] sql: remove sqlErrorMsg()
  2019-03-26 19:21             ` Mergen Imeev
@ 2019-03-26 21:36               ` n.pettik
  0 siblings, 0 replies; 43+ messages in thread
From: n.pettik @ 2019-03-26 21:36 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen

LGTM

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 7/8] sql: rework semantic errors
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 7/8] sql: rework semantic errors imeevma
  2019-03-15 15:49   ` [tarantool-patches] " n.pettik
@ 2019-03-27  6:48   ` Kirill Yukhin
  1 sibling, 0 replies; 43+ messages in thread
From: Kirill Yukhin @ 2019-03-27  6:48 UTC (permalink / raw)
  To: tarantool-patches; +Cc: korablev

Hello,

On 13 Mar 20:03, imeevma@tarantool.org wrote:
> This patch reworks some of SQL semantic errors.
> 
> Part of #3965

I've checked yout patch into master and 2.1 branches.

--
Regards, Kirill Yukhin

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [tarantool-patches] Re: [PATCH v4 8/8] sql: remove sqlErrorMsg()
  2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 8/8] sql: remove sqlErrorMsg() imeevma
  2019-03-15 13:36   ` [tarantool-patches] " n.pettik
@ 2019-03-27  6:49   ` Kirill Yukhin
  1 sibling, 0 replies; 43+ messages in thread
From: Kirill Yukhin @ 2019-03-27  6:49 UTC (permalink / raw)
  To: tarantool-patches; +Cc: korablev

Hello,

On 13 Mar 20:03, imeevma@tarantool.org wrote:
> Hi! Thank you for review! My answers and new version of patch
> below. Here won't be diff between patches due to a ot of changes
> in previous patches. Some of functionality of previous version of
> this patch were moved in new patch "sql: rework semantic errors"

I've checked your patch into master and 2.1 branches.

--
Regards, Kirill Yukhin

^ permalink raw reply	[flat|nested] 43+ messages in thread

end of thread, other threads:[~2019-03-27  6:49 UTC | newest]

Thread overview: 43+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-13 17:03 [tarantool-patches] [PATCH v4 0/8] sql: use diag_set() for errors in SQL imeevma
2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 1/8] sql: rework syntax errors imeevma
2019-03-14 18:24   ` [tarantool-patches] " n.pettik
2019-03-14 18:28     ` Imeev Mergen
2019-03-15 14:09   ` Kirill Yukhin
2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 2/8] sql: set SQL parser errors via diag_set() imeevma
2019-03-14 19:26   ` [tarantool-patches] " n.pettik
2019-03-14 19:36     ` n.pettik
2019-03-18 15:06     ` Mergen Imeev
2019-03-19  9:41       ` n.pettik
2019-03-19 11:24   ` Kirill Yukhin
2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 3/8] sql: replace rc with is_aborted status in struct Parse imeevma
2019-03-14 19:53   ` [tarantool-patches] " n.pettik
2019-03-18 15:28     ` Mergen Imeev
2019-03-19  9:54       ` n.pettik
2019-03-19 13:17   ` Kirill Yukhin
2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 4/8] sql: remove field nErr from " imeevma
2019-03-14 19:58   ` [tarantool-patches] " n.pettik
2019-03-19 13:27   ` Kirill Yukhin
2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 5/8] sql: remove field zErrMsg " imeevma
2019-03-14 22:15   ` [tarantool-patches] " n.pettik
2019-03-19 13:20   ` Kirill Yukhin
2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 6/8] sql: rework three errors of "unsupported" type imeevma
2019-03-14 22:15   ` [tarantool-patches] " n.pettik
2019-03-19 13:30   ` Kirill Yukhin
2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 7/8] sql: rework semantic errors imeevma
2019-03-15 15:49   ` [tarantool-patches] " n.pettik
2019-03-22 12:48     ` Mergen Imeev
2019-03-26 14:14       ` n.pettik
2019-03-26 16:56         ` Mergen Imeev
2019-03-26 18:16           ` n.pettik
2019-03-26 19:20             ` Mergen Imeev
2019-03-26 21:36               ` n.pettik
2019-03-27  6:48   ` Kirill Yukhin
2019-03-13 17:03 ` [tarantool-patches] [PATCH v4 8/8] sql: remove sqlErrorMsg() imeevma
2019-03-15 13:36   ` [tarantool-patches] " n.pettik
2019-03-25 18:47     ` Mergen Imeev
2019-03-26 13:34       ` n.pettik
2019-03-26 17:52         ` Mergen Imeev
2019-03-26 18:28           ` n.pettik
2019-03-26 19:21             ` Mergen Imeev
2019-03-26 21:36               ` n.pettik
2019-03-27  6:49   ` Kirill Yukhin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox