Tarantool development patches archive
 help / color / mirror / Atom feed
* [tarantool-patches] [PATCH v3 0/9] sql: use diag_set() for errors in SQL
@ 2019-03-02 13:07 imeevma
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 1/9] sql: rework syntax errors imeevma
                   ` (8 more replies)
  0 siblings, 9 replies; 30+ messages in thread
From: imeevma @ 2019-03-02 13:07 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 five
pathes that rewords all parser errors. Only part of these errors
were completely reworked.

Difference from last version:
 - Added five more patches.
 - A bit changed position of patches in the set.

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

Mergen Imeev (9):
  sql: rework syntax errors
  sql: save SQL parser errors in diag_set()
  sql: remove field nErr of struct Parse
  sql: remove field rc of struct Parse
  sql: remove field zErrMsg of struct Parse
  sql: rework six syntax errors
  sql: rework four semantic errors
  sql: rework three errors of "unsupported" type
  sql: remove sqlErrorMsg()

 src/box/errcode.h                                  |  16 ++
 src/box/sql.c                                      |  12 +-
 src/box/sql/alter.c                                |   3 +-
 src/box/sql/analyze.c                              |  13 +-
 src/box/sql/build.c                                | 215 +++++++++++----------
 src/box/sql/callback.c                             |   3 +-
 src/box/sql/delete.c                               |  17 +-
 src/box/sql/expr.c                                 | 141 ++++++++------
 src/box/sql/insert.c                               |  48 +++--
 src/box/sql/parse.y                                |  69 ++++---
 src/box/sql/pragma.c                               |  11 +-
 src/box/sql/prepare.c                              |  20 +-
 src/box/sql/resolve.c                              | 215 ++++++++++++---------
 src/box/sql/select.c                               | 212 +++++++++++---------
 src/box/sql/sqlInt.h                               |  16 +-
 src/box/sql/tokenize.c                             |  67 +++----
 src/box/sql/trigger.c                              |  37 +---
 src/box/sql/update.c                               |  23 ++-
 src/box/sql/util.c                                 |  39 ----
 src/box/sql/vdbemem.c                              |   9 +-
 src/box/sql/where.c                                |  13 +-
 src/box/sql/wherecode.c                            |   2 +-
 src/box/sql/whereexpr.c                            |   8 +-
 test/box/misc.result                               |  15 ++
 test/sql-tap/alter2.test.lua                       |   4 +-
 test/sql-tap/analyzeD.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                      |   4 +-
 test/sql-tap/e_select1.test.lua                    |  38 ++--
 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/join.test.lua                         |  22 +--
 test/sql-tap/join3.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                         |  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/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/view.test.lua                         |   4 +-
 test/sql-tap/where7.test.lua                       |   2 +-
 test/sql-tap/with1.test.lua                        |  10 +-
 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-3888-values-blob-assert.result         |   8 +-
 test/sql/integer-overflow.result                   |   9 +-
 test/sql/iproto.result                             |  15 +-
 test/sql/misc.result                               |  14 +-
 test/sql/on-conflict.result                        |  16 +-
 test/sql/triggers.result                           |   4 +-
 test/sql/types.result                              |  13 +-
 72 files changed, 895 insertions(+), 798 deletions(-)

-- 
2.7.4

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

* [tarantool-patches] [PATCH v3 1/9] sql: rework syntax errors
  2019-03-02 13:07 [tarantool-patches] [PATCH v3 0/9] sql: use diag_set() for errors in SQL imeevma
@ 2019-03-02 13:07 ` imeevma
  2019-03-04 17:47   ` [tarantool-patches] " n.pettik
  2019-03-05  8:31   ` Konstantin Osipov
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 2/9] sql: save SQL parser errors in diag_set() imeevma
                   ` (7 subsequent siblings)
  8 siblings, 2 replies; 30+ messages in thread
From: imeevma @ 2019-03-02 13:07 UTC (permalink / raw)
  To: korablev; +Cc: tarantool-patches

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

On 2/25/19 11:02 PM, n.pettik wrote:
>> diff --git a/src/box/errcode.h b/src/box/errcode.h
>> index a1fcf01..6546b2f 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_SYNTAX_NEAR,       "Unrecognized syntax near '%.*s'") \
>
> Quite strange name for err code.
> I’d rather say ER_SQL_UNRECOGNIZED_SYMBOL/SYNTAX.
> Is this message suggested by Konstantin? To be honest, I would
> prefer old one. “Unrecognized syntax” doesn’t sound good and clear enough,
> at least for me. Personally I would say “Syntax error near %s”.
> The last one is used in several DBs, so I suppose it is common way to raise
> errors like that.
>

Fixed. Changed to ER_SQL_UNRECOGNIZED_SYNTAX with description:
"Syntax error near '%.*s'".


Diff between versions:

commit b71703e1ad00454e81d2d8a4d91022a7c7ffb905
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Tue Feb 26 20:09:57 2019 +0300

    Temporary: review fix
    
    Replace ER_SQL_SYNTAX_NEAR by ER_SQL_UNRECOGNIZED_SYNTAX.

diff --git a/src/box/errcode.h b/src/box/errcode.h
index 6546b2f..f2f47c0 100644
--- a/src/box/errcode.h
+++ b/src/box/errcode.h
@@ -236,7 +236,7 @@ struct errcode_record {
    /*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_SYNTAX_NEAR,       "Unrecognized syntax near '%.*s'") \
+   /*184 */_(ER_SQL_UNRECOGNIZED_SYNTAX,   "Syntax error near '%.*s'") \
    /*185 */_(ER_SQL_UNKNOWN_TOKEN,     "Syntax error: unrecognized token: '%.*s'") \
 
 /*
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index 884f59b..f980945 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -36,7 +36,7 @@
     diag_set(ClientError, ER_SQL_KEYWORD_IS_RESERVED, TOKEN.n, TOKEN.z,
              TOKEN.n, TOKEN.z);
   } else {
-    diag_set(ClientError, ER_SQL_SYNTAX_NEAR, TOKEN.n, TOKEN.z);
+    diag_set(ClientError, ER_SQL_UNRECOGNIZED_SYNTAX, TOKEN.n, TOKEN.z);
   }
   sql_parser_error(pParse);
 }
@@ -903,7 +903,7 @@ expr(A) ::= VARIABLE(X).     {
     u32 n = X.n;
     spanExpr(&A, pParse, TK_VARIABLE, X);
     if (A.pExpr->u.zToken[0] == '?' && n > 1) {
-      diag_set(ClientError, ER_SQL_SYNTAX_NEAR, t.n, t.z);
+      diag_set(ClientError, ER_SQL_UNRECOGNIZED_SYNTAX, t.n, t.z);
       sql_parser_error(pParse);
     } else {
       sqlExprAssignVarNumber(pParse, A.pExpr, n);
@@ -911,7 +911,7 @@ expr(A) ::= VARIABLE(X).     {
   }else{
     assert( t.n>=2 );
     spanSet(&A, &t, &t);
-    diag_set(ClientError, ER_SQL_SYNTAX_NEAR, t.n, t.z);
+    diag_set(ClientError, ER_SQL_UNRECOGNIZED_SYNTAX, t.n, t.z);
     sql_parser_error(pParse);
     A.pExpr = NULL;
   }
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 81da0f6..ef24760 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -2092,7 +2092,7 @@ computeLimitRegisters(Parse * pParse, Select * p, int iBreak)
        if((p->pLimit->flags & EP_Collate) != 0 ||
           (p->pOffset != NULL &&
           (p->pOffset->flags & EP_Collate) != 0)) {
-           diag_set(ClientError, ER_SQL_SYNTAX_NEAR,
+           diag_set(ClientError, ER_SQL_UNRECOGNIZED_SYNTAX,
                 sizeof("COLLATE"), "COLLATE");
            sql_parser_error(pParse);
            return;
diff --git a/test/box/misc.result b/test/box/misc.result
index a1c94bf..27579c6 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -512,7 +512,7 @@ t;
   180: box.error.SQL_STACK_OVERFLOW
   181: box.error.SQL_SELECT_WILDCARD
   182: box.error.SQL_STATEMENT_EMPTY
-  184: box.error.SQL_SYNTAX_NEAR
+  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 ef57955..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, "Unrecognized syntax near 'REFERENCES'"
+        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, "Unrecognized syntax near '('"
+        1, "Syntax error near '('"
         -- </alter2-4.2>
     })
 
diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
index f7d3ffe..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,"Unrecognized syntax near ','"
+        1,"Syntax error near ','"
         -- </check-2.10>
     })
 
@@ -295,7 +295,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <check-2.10>
-        1,"Unrecognized syntax near ','"
+        1,"Syntax error near ','"
         -- </check-2.10>
     })
 
diff --git a/test/sql-tap/colname.test.lua b/test/sql-tap/colname.test.lua
index db655a7..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, "/Unrecognized syntax/"}}, -- 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 fb2d403..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, [[Unrecognized syntax near '*']]
+        1, [[Syntax error near '*']]
         -- </count-2.2>
     })
 
diff --git a/test/sql-tap/default.test.lua b/test/sql-tap/default.test.lua
index ec8928a..9be4614 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, "Unrecognized syntax near 'id'"
+    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, "Unrecognized syntax near '\"id\"'"
+    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 9ade46c..e190ad7 100755
--- a/test/sql-tap/e_select1.test.lua
+++ b/test/sql-tap/e_select1.test.lua
@@ -807,10 +807,10 @@ test:do_select_tests(
 -- FROM clause.
 --
 data = {
-    {"1.1", "SELECT a, b, c FROM z1 WHERE *",  "Unrecognized syntax near '*'"},
-    {"1.2", "SELECT a, b, c FROM z1 GROUP BY *", "Unrecognized syntax near '*'"},
-    {"1.3", "SELECT 1 + * FROM z1",  "Unrecognized syntax near '*'"},
-    {"1.4", "SELECT * + 1 FROM z1", "Unrecognized syntax near '+'"},
+    {"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"},
diff --git a/test/sql-tap/gh2168-temp-tables.test.lua b/test/sql-tap/gh2168-temp-tables.test.lua
index 0de4be5..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, "Unrecognized syntax near 'TEMP'"
+   1, "Syntax error near 'TEMP'"
        -- <trigger2-10.1>
 });
 
@@ -23,7 +23,7 @@ test:do_catchsql_test(
        END;
    ]], {
        -- <trigger2-10.1>
-   1, "Unrecognized syntax near 'TEMP'"
+   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 e2b408a..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, [[Unrecognized syntax near ''binary'']]}},
+    { 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 499f620..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, "Unrecognized syntax near '.'",
+        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 5dbeb84..7fec294 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, "Unrecognized syntax near '.'"
+        1, "Syntax error near '.'"
         -- </index-21.1>
     })
 
diff --git a/test/sql-tap/misc1.test.lua b/test/sql-tap/misc1.test.lua
index 6d2d813..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, [[Unrecognized syntax near 'WHEREwww']]
+        1, [[Syntax error near 'WHEREwww']]
         -- </misc1-5.1>
     })
 
@@ -1037,7 +1037,7 @@ test:do_catchsql_test(
         select''like''like''like#0;
     ]], {
         -- <misc1-21.1>
-        1, [[Unrecognized syntax near '#0']]
+        1, [[Syntax error near '#0']]
         -- </misc1-21.1>
     })
 
@@ -1047,7 +1047,7 @@ test:do_catchsql_test(
         VALUES(0,0x0MATCH#0;
     ]], {
         -- <misc1-21.2>
-        1, [[Unrecognized syntax near ';']]
+        1, [[Syntax error near ';']]
         -- </misc1-21.2>
     })
 
diff --git a/test/sql-tap/null.test.lua b/test/sql-tap/null.test.lua
index 456ec03..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, "Unrecognized syntax near '1'"
+    1, "Syntax error near '1'"
     -- <index-1.3>
     })
 
@@ -528,7 +528,7 @@ test:do_catchsql_test(
     ]],
     {
     -- <index-1.3>
-    1, "Unrecognized syntax near '1'"
+    1, "Syntax error near '1'"
     -- <index-1.3>
     })
 
@@ -539,7 +539,7 @@ test:do_catchsql_test(
     ]],
     {
     -- <index-1.3>
-    1, "Unrecognized syntax near '1'"
+    1, "Syntax error near '1'"
     -- <index-1.3>
     })
 
@@ -550,7 +550,7 @@ test:do_catchsql_test(
     ]],
     {
     -- <index-1.3>
-    1, "Unrecognized syntax near '1'"
+    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 b3fc207..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, "Unrecognized syntax near ''memtx''"
+   1, "Syntax error near ''memtx''"
 })
 
 test:do_catchsql_test(
@@ -51,7 +51,7 @@ test:do_catchsql_test(
    [[
        pragma sql_default_engine 1;
    ]], {
-   1, "Unrecognized syntax near '1'"
+   1, "Syntax error near '1'"
 })
 
 --
diff --git a/test/sql-tap/select1.test.lua b/test/sql-tap/select1.test.lua
index 57f3fcb..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, [[Unrecognized syntax near ';']]
+        1, [[Syntax error near ';']]
         -- </select1-7.1>
     })
 
@@ -1438,7 +1438,7 @@ test:do_catchsql_test(
         SELECT f1 FROM test1 ORDER BY;
     ]], {
         -- <select1-7.4>
-        1, [[Unrecognized syntax near ';']]
+        1, [[Syntax error near ';']]
         -- </select1-7.4>
     })
 
@@ -1468,7 +1468,7 @@ test:do_catchsql_test(
         SELECT count(f1,f2+) FROM test1;
     ]], {
         -- <select1-7.7>
-        1, [[Unrecognized syntax near ')']]
+        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, [[Unrecognized syntax near ';']]
+        1, [[Syntax error near ';']]
         -- </select1-7.8>
     })
 
diff --git a/test/sql-tap/select3.test.lua b/test/sql-tap/select3.test.lua
index 6ae3135..9fb825f 100755
--- a/test/sql-tap/select3.test.lua
+++ b/test/sql-tap/select3.test.lua
@@ -190,7 +190,7 @@ test:do_catchsql_test("select3-2.14", [[
   SELECT log, count(*) FROM t1 GROUP BY;
 ]], {
   -- <select3-2.14>
-  1, [[Unrecognized syntax near ';']]
+  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 feb55a2..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, "Unrecognized syntax near 'BEGIN'"
+       1, "Syntax error near 'BEGIN'"
        -- <start-transaction-1.0>
    })
 
@@ -46,7 +46,7 @@ test:do_catchsql_test(
        COMMIT;
    ]], {
        -- <start-transaction-1.1>
-       1, "Unrecognized syntax near 'BEGIN'"
+       1, "Syntax error near 'BEGIN'"
        -- <start-transaction-1.1>
    })
 
diff --git a/test/sql-tap/table.test.lua b/test/sql-tap/table.test.lua
index c487e1e..770d00c 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, "Unrecognized syntax near 'TEMP'"
+   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, "Unrecognized syntax near 'TEMPORARY'"
+   1, "Syntax error near 'TEMPORARY'"
    -- <temporary>
    })
 
diff --git a/test/sql-tap/tokenize.test.lua b/test/sql-tap/tokenize.test.lua
index 0c79f75..b1a097e 100755
--- a/test/sql-tap/tokenize.test.lua
+++ b/test/sql-tap/tokenize.test.lua
@@ -145,7 +145,7 @@ test:do_catchsql_test(
     [[
         SELECT 1, 2 /*]], {
         -- <tokenize-2.1>
-        1, [[Unrecognized syntax near '*']]
+        1, [[Syntax error near '*']]
         -- </tokenize-2.1>
     })
 
diff --git a/test/sql-tap/trigger1.test.lua b/test/sql-tap/trigger1.test.lua
index d0edcd7..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, [[Unrecognized syntax near 'STATEMENT']]
+        1, [[Syntax error near 'STATEMENT']]
         -- </trigger1-1.1.3>
     })
 
@@ -297,7 +297,7 @@ test:do_catchsql_test(
         END;
     ]], {
         -- <trigger1-2.1>
-        1, [[Unrecognized syntax near ';']]
+        1, [[Syntax error near ';']]
         -- </trigger1-2.1>
     })
 
@@ -310,7 +310,7 @@ test:do_catchsql_test(
         END;
     ]], {
         -- <trigger1-2.2>
-        1, [[Unrecognized syntax near ';']]
+        1, [[Syntax error near ';']]
         -- </trigger1-2.2>
     })
 
diff --git a/test/sql-tap/view.test.lua b/test/sql-tap/view.test.lua
index 2808ac7..72fdab8 100755
--- a/test/sql-tap/view.test.lua
+++ b/test/sql-tap/view.test.lua
@@ -961,7 +961,7 @@ test:do_catchsql_test(
         DROP VIEW main.nosuchview
     ]], {
         -- <view-17.2>
-        1, "Unrecognized syntax near '.'"
+        1, "Syntax error near '.'"
         -- </view-17.2>
     })
 
diff --git a/test/sql-tap/with2.test.lua b/test/sql-tap/with2.test.lua
index 9700729..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, [[Unrecognized syntax near ')']]
+    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, [[Unrecognized syntax near ')']]
+    1, [[Syntax error near ')']]
     -- </6.2>
 })
 
@@ -555,7 +555,7 @@ test:do_catchsql_test(6.5, [[
     DELETE FROM t2 WHERE;
 ]], {
     -- <6.5>
-    1, [[Unrecognized syntax near ';']]
+    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, "Unrecognized syntax near '\n'"
+    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, "Unrecognized syntax near 'WHRE'"
+    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, "Unrecognized syntax near ';'"
+    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, "Unrecognized syntax near '='"
+    1, "Syntax error near '='"
     -- </6.9>
 })
 
diff --git a/test/sql/checks.result b/test/sql/checks.result
index cfce2e4..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:
-    Unrecognized syntax near ''<'')'
+    Syntax error near ''<'')'
 ...
 opts = {checks = {{expr = 'X>5'}}}
 ---
diff --git a/test/sql/collation.result b/test/sql/collation.result
index bad265d..fbdb1b8 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: Unrecognized syntax near 'COLLATE'
+- error: Syntax error near 'COLLATE'
 ...
 box.sql.execute("SELECT 1 LIMIT 1 COLLATE BINARY OFFSET 1;")
 ---
-- error: Unrecognized syntax near 'COLLATE'
+- error: Syntax error near 'COLLATE'
 ...
 box.sql.execute("SELECT 1 LIMIT 1 OFFSET 1 COLLATE BINARY;")
 ---
-- error: Unrecognized syntax near 'COLLATE'
+- error: Syntax error near 'COLLATE'
 ...
 box.sql.execute("SELECT 1 LIMIT 1, 1 COLLATE BINARY;")
 ---
-- error: Unrecognized syntax near 'COLLATE'
+- error: Syntax error near 'COLLATE'
 ...
 box.sql.execute("SELECT 1 LIMIT 1 COLLATE BINARY, 1;")
 ---
-- error: Unrecognized syntax near 'COLLATE'
+- 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: Unrecognized syntax near ''COLLATE'''
+- error: 'Failed to execute SQL statement: Syntax error near ''COLLATE'''
 ...
 cn:close()
 ---
diff --git a/test/sql/gh-3888-values-blob-assert.result b/test/sql/gh-3888-values-blob-assert.result
index 600acf7..9045572 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(blob)')
 ---
-- error: Unrecognized syntax near 'blob'
+- error: Syntax error near 'blob'
 ...
 box.sql.execute('VALUES(float)')
 ---
-- error: Unrecognized syntax near 'float'
+- error: Syntax error near 'float'
 ...
 -- check 'SELECT' against typedef keywords (should fail)
 box.sql.execute('SELECT blob')
 ---
-- error: Unrecognized syntax near 'blob'
+- error: Syntax error near 'blob'
 ...
 box.sql.execute('SELECT float')
 ---
-- error: Unrecognized syntax near 'float'
+- 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 48f877a..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: Unrecognized syntax near ''qwerty'''
+- 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: Unrecognized syntax near ''100'''
+- error: 'Failed to execute SQL statement: Syntax error near ''100'''
 ...
 cn:execute('select 1', nil, {dry_run = true})
 ---
@@ -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: Unrecognized syntax near ''?1'''
+- error: 'Failed to execute SQL statement: Syntax error near ''?1'''
 ...
 cn:execute('select $name, $name2', {1, 2})
 ---
diff --git a/test/sql/types.result b/test/sql/types.result
index 82f2873..98464f8 100644
--- a/test/sql/types.result
+++ b/test/sql/types.result
@@ -13,7 +13,7 @@ box.sql.execute("CREATE TABLE t1 (id PRIMARY KEY);")
 ...
 box.sql.execute("CREATE TABLE t1 (a, id INT PRIMARY KEY);")
 ---
-- error: Unrecognized syntax near ','
+- error: Syntax error near ','
 ...
 box.sql.execute("CREATE TABLE t1 (id PRIMARY KEY, a INT);")
 ---
@@ -22,7 +22,7 @@ box.sql.execute("CREATE TABLE t1 (id PRIMARY KEY, a INT);")
 ...
 box.sql.execute("CREATE TABLE t1 (id INT PRIMARY KEY, a);")
 ---
-- error: Unrecognized syntax near ')'
+- error: Syntax error near ')'
 ...
 box.sql.execute("CREATE TABLE t1 (id INT PRIMARY KEY, a INT, b UNIQUE);")
 ---


New version:

commit 8bd6b8f7e60a581d62673d548ecf5a3039d16d6e
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Thu Feb 21 11:25:24 2019 +0300

    sql: rework syntax errors
    
    This patch reworks SQL syntax errors. After this patch, these
    error will be set as Tarantool errors.
    
    Part of #3965

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 e2504c4..f980945 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;
   }
 }
@@ -1374,14 +1382,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 c7b328a..e2cc4d9 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 e0dbb91..9be4614 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 2ed1451..7fec294 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 6895dc1..33873d1 100755
--- a/test/sql-tap/keyword1.test.lua
+++ b/test/sql-tap/keyword1.test.lua
@@ -237,7 +237,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 2bcc5aa..770d00c 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>
    })
 
@@ -1253,7 +1253,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>
     })
 
@@ -1318,7 +1318,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>
     })
 
@@ -1332,7 +1332,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 daea355..fbdb1b8 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 67948cd..9045572 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(blob)')
 ---
-- error: 'near "blob": syntax error'
+- error: Syntax error near 'blob'
 ...
 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 blob')
 ---
-- error: 'near "blob": syntax error'
+- error: Syntax error near 'blob'
 ...
 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 a0159d6..98464f8 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] 30+ messages in thread

* [tarantool-patches] [PATCH v3 2/9] sql: save SQL parser errors in diag_set()
  2019-03-02 13:07 [tarantool-patches] [PATCH v3 0/9] sql: use diag_set() for errors in SQL imeevma
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 1/9] sql: rework syntax errors imeevma
@ 2019-03-02 13:07 ` imeevma
  2019-03-05  8:40   ` [tarantool-patches] " Konstantin Osipov
  2019-03-05  9:06   ` n.pettik
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 3/9] sql: remove field nErr of struct Parse imeevma
                   ` (6 subsequent siblings)
  8 siblings, 2 replies; 30+ messages in thread
From: imeevma @ 2019-03-02 13:07 UTC (permalink / raw)
  To: korablev; +Cc: tarantool-patches

After this patch all SQL parser errors will be saved in diag_set()
instead of field zErrMsg of struct Parse.

Part of #3965
---
 src/box/errcode.h           | 1 +
 src/box/sql/build.c         | 3 ++-
 src/box/sql/util.c          | 6 +++---
 test/box/misc.result        | 1 +
 test/sql-tap/check.test.lua | 2 +-
 test/sql/triggers.result    | 4 ++--
 6 files changed, 10 insertions(+), 7 deletions(-)

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/util.c b/src/box/sql/util.c
index c89e2e8..e4c93cb 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -236,10 +236,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 bbfff33..9ab169b 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] 30+ messages in thread

* [tarantool-patches] [PATCH v3 3/9] sql: remove field nErr of struct Parse
  2019-03-02 13:07 [tarantool-patches] [PATCH v3 0/9] sql: use diag_set() for errors in SQL imeevma
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 1/9] sql: rework syntax errors imeevma
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 2/9] sql: save SQL parser errors in diag_set() imeevma
@ 2019-03-02 13:07 ` imeevma
  2019-03-05  8:41   ` [tarantool-patches] " Konstantin Osipov
  2019-03-05  9:06   ` n.pettik
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 4/9] sql: remove field rc " imeevma
                   ` (5 subsequent siblings)
  8 siblings, 2 replies; 30+ messages in thread
From: imeevma @ 2019-03-02 13:07 UTC (permalink / raw)
  To: korablev; +Cc: tarantool-patches

Hi! Thank you for review. My answers and new version below. There
won't be diff between versions as I completely rewritten this
patch to place it before removing of nErr and zErrMsg.

On 2/26/19 5:48 PM, n.pettik wrote:
>
>> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
>> index 6afca4a..6621af4 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->rc = SQL_TARANTOOL_ERROR;
>> -    parser->nErr++;
>>    return;
>>  }
>>  record->space_id = space_id;
>> @@ -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)
>
> rc == 0 / ! abort
>
Fixed in next patch.

>> +    return;
>> +  if (db->mallocFailed) {
>> +    diag_set(ClientError, ER_SQL_PARSER_GENERIC, "Out of memory”);
>
> Why generic when it is OOM error?
>
I fixed this, but not sure that I wrote right error description.

>> +    parse_context->rc = SQL_TARANTOOL_ERROR;
>
> rc = -1 / abort = true
>
Fixed in next patch.

>>    return;
>>  }
>>  /*
>> @@ -189,13 +190,13 @@ 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_TARANTOOL_ERROR && !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(ClientError, ER_SQL_PARSER_GENERIC, "Out of memory");
>> +    parse_context->rc = SQL_TARANTOOL_ERROR;
>
> The same is here and in other places .
>
If it is about flag than it was fixed in next patch. If this is
about OOM error than anvwer is the same as for second comment.


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
    
    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

diff --git a/src/box/sql.c b/src/box/sql.c
index a2937a0..cb0facf 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->rc = SQL_TARANTOOL_ERROR;
-		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->rc = SQL_TARANTOOL_ERROR;
-		parser->nErr++;
 		return NULL;
 	}
 
diff --git a/src/box/sql/alter.c b/src/box/sql/alter.c
index d49ebb8..214e0f8 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->rc = SQL_TARANTOOL_ERROR;
-	parse->nErr++;
 	goto exit_rename_table;
 }
 
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index 8c83288..96e5e4c 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->rc = SQL_TARANTOOL_ERROR;
-			parse->nErr++;
 			return;
 		}
 		/*
@@ -1131,7 +1130,6 @@ sqlAnalyze(Parse * pParse, Token * pName)
 			} else {
 				diag_set(ClientError, ER_NO_SUCH_SPACE, z);
 				pParse->rc = SQL_TARANTOOL_ERROR;
-				pParse->nErr++;
 			}
 			sqlDbFree(db, z);
 		}
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index deb5b89..7d6c1c9 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->rc = SQL_TARANTOOL_ERROR;
-		parser->nErr++;
 		return;
 	}
 	record->space_id = space_id;
@@ -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");
+		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;
 	}
 }
 /**
@@ -398,7 +398,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->rc = SQL_TARANTOOL_ERROR;
-			parser->nErr++;
 			return NULL;
 		}
 
@@ -454,7 +453,6 @@ sqlAddColumn(Parse * pParse, Token * pName, struct type_def *type_def)
 		diag_set(OutOfMemory, pName->n + 1,
 			 "region_alloc", "z");
 		pParse->rc = SQL_TARANTOOL_ERROR;
-		pParse->nErr++;
 		return;
 	}
 	memcpy(z, pName->z, pName->n);
@@ -502,7 +500,6 @@ sql_column_add_nullable_action(struct Parse *parser,
 							   nullable_action]);
 		diag_set(ClientError, ER_SQL, err_msg);
 		parser->rc = SQL_TARANTOOL_ERROR;
-		parser->nErr++;
 		return;
 	}
 	field->nullable_action = nullable_action;
@@ -544,7 +541,6 @@ sqlAddDefaultValue(Parse * pParse, ExprSpan * pSpan)
 					 "region_alloc",
 					 "field->default_value");
 				pParse->rc = SQL_TARANTOOL_ERROR;
-				pParse->nErr++;
 				return;
 			}
 			strncpy(field->default_value, pSpan->zStart,
@@ -563,7 +559,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->rc = SQL_TARANTOOL_ERROR;
-		parser->nErr++;
 		return -1;
 	} else if (field->nullable_action == ON_CONFLICT_ACTION_DEFAULT) {
 		field->nullable_action = ON_CONFLICT_ACTION_ABORT;
@@ -652,7 +647,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->rc == SQL_TARANTOOL_ERROR)
 			goto primary_key_exit;
 	}
 
@@ -852,8 +847,6 @@ vdbe_emit_create_index(struct Parse *parse, struct space_def *def,
 	return;
 error:
 	parse->rc = SQL_TARANTOOL_ERROR;
-	parse->nErr++;
-
 }
 
 /*
@@ -911,7 +904,6 @@ createSpace(Parse * pParse, int iSpaceId, char *zStmt)
 	save_record(pParse, BOX_SPACE_ID, iFirstCol, 1, v->nOp - 1);
 	return;
 error:
-	pParse->nErr++;
 	pParse->rc = SQL_TARANTOOL_ERROR;
 }
 
@@ -1092,7 +1084,6 @@ vdbe_emit_fk_constraint_create(struct Parse *parse_context,
 	sqlReleaseTempRange(parse_context, constr_tuple_reg, 10);
 	return;
 error:
-	parse_context->nErr++;
 	parse_context->rc = SQL_TARANTOOL_ERROR;
 }
 
@@ -1122,7 +1113,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->rc = SQL_TARANTOOL_ERROR;
-	parse_context->nErr++;
 	return -1;
 }
 
@@ -1254,7 +1244,6 @@ sqlEndTable(Parse * pParse,	/* Parse context */
 					 "number of columns in the primary "\
 					 "index of referenced table");
 				pParse->rc = SQL_TARANTOOL_ERROR;
-				pParse->nErr++;
 				return;
 			}
 			for (uint32_t i = 0; i < fk_def->field_count; ++i) {
@@ -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)
 		goto create_view_fail;
 
 	struct space *select_res_space =
@@ -1330,7 +1319,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->rc = SQL_TARANTOOL_ERROR;
-		parse_context->nErr++;
 		goto create_view_fail;
 	}
 
@@ -1603,7 +1591,7 @@ 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->rc != SQL_TARANTOOL_ERROR);
 	assert(table_name_list->nSrc == 1);
 	const char *space_name = table_name_list->a[0].zName;
 	struct space *space = space_by_name(space_name);
@@ -1650,7 +1638,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->rc = SQL_TARANTOOL_ERROR;
-			parse_context->nErr++;
 			goto exit_drop_table;
 		}
 	}
@@ -1686,7 +1673,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->rc = SQL_TARANTOOL_ERROR;
-		parse_context->nErr++;
 		return -1;
 	}
 	return 0;
@@ -1896,7 +1882,6 @@ exit_create_fk:
 	return;
 tnt_error:
 	parse_context->rc = SQL_TARANTOOL_ERROR;
-	parse_context->nErr++;
 	goto exit_create_fk;
 }
 
@@ -1921,7 +1906,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->rc = SQL_TARANTOOL_ERROR;
-		parse_context->nErr++;
 		return;
 	}
 	char *constraint_name = sqlNameFromToken(parse_context->db,
@@ -2048,7 +2032,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->rc == SQL_TARANTOOL_ERROR)
 			goto cleanup;
 
 		struct Expr *column_expr = sqlExprSkipCollate(expr);
@@ -2100,7 +2084,6 @@ cleanup:
 	return rc;
 tnt_error:
 	parse->rc = SQL_TARANTOOL_ERROR;
-	++parse->nErr;
 	goto cleanup;
 }
 
@@ -2128,7 +2111,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->rc == SQL_TARANTOOL_ERROR)
 		goto exit_create_index;
 	if (idx_type == SQL_INDEX_TYPE_UNIQUE ||
 	    idx_type == SQL_INDEX_TYPE_NON_UNIQUE) {
@@ -2151,7 +2134,6 @@ sql_create_index(struct Parse *parse, struct Token *token,
 			if (! if_not_exist) {
 				diag_set(ClientError, ER_NO_SUCH_SPACE, name);
 				parse->rc = SQL_TARANTOOL_ERROR;
-				parse->nErr++;
 			}
 			goto exit_create_index;
 		}
@@ -2244,7 +2226,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->rc = SQL_TARANTOOL_ERROR;
 		goto exit_create_index;
 	}
@@ -2274,7 +2255,6 @@ sql_create_index(struct Parse *parse, struct Token *token,
 	if (index == NULL) {
 		diag_set(OutOfMemory, sizeof(*index), "region", "index");
 		parse->rc = SQL_TARANTOOL_ERROR;
-		parse->nErr++;
 		goto exit_create_index;
 	}
 	memset(index, 0, sizeof(*index));
@@ -2444,7 +2424,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->rc != SQL_TARANTOOL_ERROR);
 	assert(table_token != NULL);
 	const char *table_name = sqlNameFromToken(db, table_token);
 	if (db->mallocFailed) {
diff --git a/src/box/sql/callback.c b/src/box/sql/callback.c
index 4594cac..6c26402 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->rc = SQL_TARANTOOL_ERROR;
-		parser->nErr++;
 		return NULL;
 	} else {
 		*coll_id = p->id;
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index 5170c7f..fba07a9 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -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->rc = SQL_TARANTOOL_ERROR;
-		parse->nErr++;
 		return NULL;
 	}
 	space_name->space = space;
@@ -117,7 +116,6 @@ cleanup:
 
 tarantool_error:
 	parse->rc = SQL_TARANTOOL_ERROR;
-	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->rc == SQL_TARANTOOL_ERROR || 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 a75f237..0911eee 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->rc != SQL_TARANTOOL_ERROR) {
 						diag_set(ClientError,
 							 ER_ILLEGAL_COLLATION_MIX);
-						parse->nErr++;
 						parse->rc = SQL_TARANTOOL_ERROR;
 					}
 					return -1;
@@ -411,7 +410,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->rc = SQL_TARANTOOL_ERROR;
-		parser->nErr++;
 		return -1;
 	}
 	return 0;
@@ -821,7 +819,7 @@ exprSetHeight(Expr * p)
 void
 sqlExprSetHeightAndFlags(Parse * pParse, Expr * p)
 {
-	if (pParse->nErr)
+	if (pParse->rc == SQL_TARANTOOL_ERROR)
 		return;
 	exprSetHeight(p);
 	sqlExprCheckHeight(pParse, p->nHeight);
@@ -1005,7 +1003,7 @@ sqlPExpr(Parse * pParse,	/* Parsing context */
     )
 {
 	Expr *p;
-	if (op == TK_AND && pParse->nErr == 0) {
+	if (op == TK_AND && pParse->rc != SQL_TARANTOOL_ERROR) {
 		/* Take advantage of short-circuit false optimization for AND */
 		p = sqlExprAnd(pParse->db, pLeft, pRight);
 	} else {
@@ -2407,7 +2405,8 @@ 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->rc != SQL_TARANTOOL_ERROR &&
+	    (p = isCandidateForInOpt(pX)) != 0) {
 		sql *db = pParse->db;	/* Database connection */
 		ExprList *pEList = p->pEList;
 		int nExpr = pEList->nExpr;
@@ -3041,8 +3040,9 @@ sqlExprCodeIN(Parse * pParse,	/* Parsing and code generating context */
 				   destIfFalse == destIfNull ? 0 : &rRhsHasNull,
 				   aiMap, 0);
 
-	assert(pParse->nErr || nVector == 1 || eType == IN_INDEX_EPH
-	       || eType == IN_INDEX_INDEX_ASC || eType == IN_INDEX_INDEX_DESC);
+	assert(pParse->rc == SQL_TARANTOOL_ERROR || 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
 	 * nVector-1.
@@ -3364,7 +3364,8 @@ 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->rc == SQL_TARANTOOL_ERROR ||
+	       pParse->db->mallocFailed);
 	assert(iCol >= -1 && iCol < 32768);	/* Finite column numbers */
 
 	/* The SQL_ColumnCache flag disables the column cache.  This is used
@@ -4341,8 +4342,9 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 			} else {
 				sqlVdbeAddOp2(v, OP_Null, 0, target);
 			}
-			assert(pParse->db->mallocFailed || pParse->nErr > 0
-			       || pParse->iCacheLevel == iCacheLevel);
+			assert(pParse->db->mallocFailed ||
+			       pParse->rc == SQL_TARANTOOL_ERROR ||
+			       pParse->iCacheLevel == iCacheLevel);
 			sqlVdbeResolveLabel(v, endLabel);
 			break;
 		}
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 17fbdec..e8cdd39 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->rc == SQL_TARANTOOL_ERROR || 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->rc == SQL_TARANTOOL_ERROR)
 			goto insert_cleanup;
 		sqlVdbeEndCoroutine(v, regYield);
 		sqlVdbeJumpHere(v, addrTop - 1);	/* label B: */
diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
index 8b909f2..6dc41a3 100644
--- a/src/box/sql/pragma.c
+++ b/src/box/sql/pragma.c
@@ -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->rc = SQL_TARANTOOL_ERROR;
-			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->rc = SQL_TARANTOOL_ERROR;
-			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->rc = SQL_TARANTOOL_ERROR;
-				pParse->nErr++;
 				goto pragma_out;
 			}
 			sqlVdbeAddOp0(v, OP_Expire);
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index aed9e26..dfb6238 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -809,7 +809,7 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 			break;
 		}
 	}
-	return (pParse->nErr
+	return (pParse->rc == SQL_TARANTOOL_ERROR
 		|| 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->rc == SQL_TARANTOOL_ERROR
 			|| db->mallocFailed) ? WRC_Abort : WRC_Prune;
 	}
 
@@ -1252,7 +1252,8 @@ resolveSelectStep(Walker * pWalker, Select * p)
 				sqlResolveSelectNames(pParse,
 							  pItem->pSelect,
 							  pOuterNC);
-				if (pParse->nErr || db->mallocFailed)
+				if (pParse->rc == SQL_TARANTOOL_ERROR ||
+				    db->mallocFailed)
 					return WRC_Abort;
 
 				for (pNC = pOuterNC; pNC; pNC = pNC->pNext)
@@ -1336,7 +1337,6 @@ resolveSelectStep(Walker * pWalker, Select * p)
 					 "argument must appear in the GROUP BY "
 					 "clause or be used in an aggregate "
 					 "function");
-				pParse->nErr++;
 				pParse->rc = SQL_TARANTOOL_ERROR;
 				return WRC_Abort;
 			}
@@ -1532,7 +1532,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->rc == SQL_TARANTOOL_ERROR) {
 		ExprSetProperty(pExpr, EP_Error);
 	}
 	if (pNC->ncFlags & NC_HasAgg) {
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index ef24760..4698bb4 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->rc == SQL_TARANTOOL_ERROR
 	       || 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->rc == SQL_TARANTOOL_ERROR);
 	}
 	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->rc == SQL_TARANTOOL_ERROR)
 		return NULL;
 	while (pSelect->pPrior)
 		pSelect = pSelect->pPrior;
@@ -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->rc = SQL_TARANTOOL_ERROR;
-		parser->nErr++;
 		return 0;
 	}
 	*is_forced_coll = (is_prior_forced || is_current_forced);
@@ -3115,7 +3114,8 @@ 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->rc == SQL_TARANTOOL_ERROR);
 			testcase(in->nSdst != 1);
 			sqlExprCodeMove(parse, in->iSdst, dest->iSDParm,
 					    1);
@@ -3585,7 +3585,7 @@ multiSelectOrderBy(Parse * pParse,	/* Parsing context */
   *** subqueries ***
   */
 	explainComposite(pParse, p->op, iSub1, iSub2, 0);
-	return pParse->nErr != 0;
+	return pParse->rc == SQL_TARANTOOL_ERROR;
 }
 #endif
 
@@ -5096,7 +5096,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->rc
  * and/or pParse->db->mallocFailed.
  */
 static void
@@ -5205,10 +5205,10 @@ sqlSelectPrep(Parse * pParse,	/* The parser context */
 	if (p->selFlags & SF_HasTypeInfo)
 		return;
 	sqlSelectExpand(pParse, p);
-	if (pParse->nErr || db->mallocFailed)
+	if (pParse->rc == SQL_TARANTOOL_ERROR || db->mallocFailed)
 		return;
 	sqlResolveSelectNames(pParse, p, pOuterNC);
-	if (pParse->nErr || db->mallocFailed)
+	if (pParse->rc == SQL_TARANTOOL_ERROR || db->mallocFailed)
 		return;
 	sqlSelectAddTypeInfo(pParse, p);
 }
@@ -5463,7 +5463,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->rc == SQL_TARANTOOL_ERROR) {
 		return 1;
 	}
 	memset(&sAggInfo, 0, sizeof(sAggInfo));
@@ -5498,7 +5498,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->rc == SQL_TARANTOOL_ERROR || db->mallocFailed) {
 		goto select_end;
 	}
 	assert(p->pEList != 0);
@@ -6392,7 +6392,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->rc == SQL_TARANTOOL_ERROR);
 
 	/* 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 387cfa9..719b85e 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -2654,7 +2654,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[] */
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;
 			}
 		} 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,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;
 	}
 	if (pParse->rc != SQL_OK && pParse->rc != SQL_DONE
 	    && pParse->zErrMsg == 0) {
@@ -547,7 +552,7 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 		sql_log(pParse->rc, "%s", *pzErrMsg);
 		nErr++;
 	}
-	if (pParse->pVdbe != NULL && pParse->nErr > 0) {
+	if (pParse->pVdbe != NULL && pParse->rc == SQL_TARANTOOL_ERROR) {
 		sqlVdbeDelete(pParse->pVdbe);
 		pParse->pVdbe = 0;
 	}
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index f7e6189..6816028 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->rc = SQL_TARANTOOL_ERROR;
-	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->rc == SQL_TARANTOOL_ERROR) || 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->rc == SQL_TARANTOOL_ERROR);
+	assert(pTo->zErrMsg == 0 || pTo->rc == SQL_TARANTOOL_ERROR);
+	if (pTo->rc != SQL_TARANTOOL_ERROR) {
 		pTo->zErrMsg = pFrom->zErrMsg;
-		pTo->nErr = pFrom->nErr;
 		pTo->rc = pFrom->rc;
 	} 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->rc == SQL_TARANTOOL_ERROR ||
 	       parser->db->mallocFailed != 0);
 
 	/*
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 670e547..caf7911 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->rc == SQL_TARANTOOL_ERROR || db->mallocFailed) {
 		goto update_cleanup;
 	}
 	assert(pTabList->nSrc == 1);
diff --git a/src/box/sql/util.c b/src/box/sql/util.c
index e4c93cb..502d7ab 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -211,7 +211,7 @@ sqlErrorWithMsg(sql * db, int err_code, const char *zFormat, ...)
 }
 
 /*
- * Add an error message to pParse->zErrMsg and increment pParse->nErr.
+ * Add an error message to pParse->zErrMsg.
  * The following formatting characters are allowed:
  *
  *      %s      Insert a string
@@ -238,14 +238,12 @@ sqlErrorMsg(Parse * pParse, const char *zFormat, ...)
 	va_end(ap);
 	diag_set(ClientError, ER_SQL_PARSER_GENERIC, zMsg);
 	sqlDbFree(db, zMsg);
-	pParse->nErr++;
 	pParse->rc = SQL_TARANTOOL_ERROR;
 }
 
 void
 sql_parser_error(struct Parse *parse_context)
 {
-	parse_context->nErr++;
 	parse_context->rc = SQL_TARANTOOL_ERROR;
 }
 
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;
 
  value_from_function_out:
 	if (rc != SQL_OK) {
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index 5a3c9be..d86b608 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->rc = SQL_TARANTOOL_ERROR;
 			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->rc == SQL_TARANTOOL_ERROR || NEVER(db->mallocFailed)) {
 		goto whereBeginError;
 	}
 #ifdef SQL_DEBUG
diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
index 018fd8a..bcb10a5 100644
--- a/src/box/sql/wherecode.c
+++ b/src/box/sql/wherecode.c
@@ -1472,7 +1472,8 @@ sqlWhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about the W
 				    sqlWhereBegin(pParse, pOrTab, pOrExpr,
 						      0, 0, wctrlFlags,
 						      iCovCur);
-				assert(pSubWInfo || pParse->nErr
+				assert(pSubWInfo ||
+				       pParse->rc == SQL_TARANTOOL_ERROR
 				       || db->mallocFailed);
 				if (pSubWInfo) {
 					WhereLoop *pSubLoop;
-- 
2.7.4

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

* [tarantool-patches] [PATCH v3 4/9] sql: remove field rc of struct Parse
  2019-03-02 13:07 [tarantool-patches] [PATCH v3 0/9] sql: use diag_set() for errors in SQL imeevma
                   ` (2 preceding siblings ...)
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 3/9] sql: remove field nErr of struct Parse imeevma
@ 2019-03-02 13:07 ` imeevma
  2019-03-05  8:42   ` [tarantool-patches] " Konstantin Osipov
  2019-03-05  9:06   ` n.pettik
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 5/9] sql: remove field zErrMsg " imeevma
                   ` (4 subsequent siblings)
  8 siblings, 2 replies; 30+ messages in thread
From: imeevma @ 2019-03-02 13:07 UTC (permalink / raw)
  To: korablev; +Cc: tarantool-patches

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.

Part of #3965
---
 src/box/sql.c               | 10 ++----
 src/box/sql/alter.c         |  2 +-
 src/box/sql/analyze.c       |  4 +--
 src/box/sql/build.c         | 76 ++++++++++++++++++++++-----------------------
 src/box/sql/callback.c      |  2 +-
 src/box/sql/delete.c        |  8 ++---
 src/box/sql/expr.c          | 22 ++++++-------
 src/box/sql/insert.c        |  4 +--
 src/box/sql/parse.y         | 16 +++++-----
 src/box/sql/pragma.c        |  8 ++---
 src/box/sql/prepare.c       |  8 ++---
 src/box/sql/resolve.c       | 14 ++++-----
 src/box/sql/select.c        | 33 ++++++++++----------
 src/box/sql/sqlInt.h        | 11 ++-----
 src/box/sql/tokenize.c      | 42 +++++++++----------------
 src/box/sql/trigger.c       | 14 ++++-----
 src/box/sql/update.c        |  4 +--
 src/box/sql/util.c          |  8 +----
 src/box/sql/vdbemem.c       |  4 +--
 src/box/sql/where.c         |  4 +--
 src/box/sql/wherecode.c     |  3 +-
 test/sql-tap/check.test.lua |  4 +--
 test/sql/checks.result      | 12 +++----
 23 files changed, 139 insertions(+), 174 deletions(-)

diff --git a/src/box/sql.c b/src/box/sql.c
index cb0facf..1004bb7 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;
 		return NULL;
 	}
 
@@ -1293,7 +1293,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;
 		return NULL;
 	}
 
@@ -1361,12 +1361,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 214e0f8..bd9b034 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;
 	goto exit_rename_table;
 }
 
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index 96e5e4c..9bcbe49 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;
 			return;
 		}
 		/*
@@ -1129,7 +1129,7 @@ sqlAnalyze(Parse * pParse, Token * pName)
 				}
 			} else {
 				diag_set(ClientError, ER_NO_SUCH_SPACE, z);
-				pParse->rc = SQL_TARANTOOL_ERROR;
+				pParse->is_aborted = true;
 			}
 			sqlDbFree(db, z);
 		}
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 7d6c1c9..a1f4b1b 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;
 		return;
 	}
 	record->space_id = space_id;
@@ -144,11 +144,11 @@ sql_finish_coding(struct Parse *parse_context)
 			     "Exit with an error if CREATE statement fails"));
 	}
 
-	if (parse_context->rc == SQL_TARANTOOL_ERROR)
+	if (parse_context->is_aborted)
 		return;
 	if (db->mallocFailed) {
 		diag_set(OutOfMemory, 0, "SQL", "db");
-		parse_context->rc = SQL_TARANTOOL_ERROR;
+		parse_context->is_aborted = true;
 		return;
 	}
 	/*
@@ -190,12 +190,12 @@ sql_finish_coding(struct Parse *parse_context)
 		sqlVdbeGoto(v, 1);
 	}
 	/* Get the VDBE program ready for execution. */
-	if (parse_context->rc == SQL_OK && !db->mallocFailed) {
+	if (!parse_context->is_aborted && !db->mallocFailed) {
 		assert(parse_context->iCacheLevel == 0);
 		sqlVdbeMakeReady(v, parse_context);
-	} else if (parse_context->rc != SQL_TARANTOOL_ERROR){
+	} else if (!parse_context->is_aborted){
 		diag_set(OutOfMemory, 0, "SQL", "db");
-		parse_context->rc = SQL_TARANTOOL_ERROR;
+		parse_context->is_aborted = true;
 	}
 }
 /**
@@ -345,7 +345,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);
 		}
@@ -397,7 +397,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;
 			return NULL;
 		}
 
@@ -452,7 +452,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;
 		return;
 	}
 	memcpy(z, pName->z, pName->n);
@@ -461,7 +461,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 +499,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;
 		return;
 	}
 	field->nullable_action = nullable_action;
@@ -540,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;
 				return;
 			}
 			strncpy(field->default_value, pSpan->zStart,
@@ -558,7 +558,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;
 		return -1;
 	} else if (field->nullable_action == ON_CONFLICT_ACTION_DEFAULT) {
 		field->nullable_action = ON_CONFLICT_ACTION_ABORT;
@@ -647,7 +647,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->rc == SQL_TARANTOOL_ERROR)
+		if (pParse->is_aborted)
 			goto primary_key_exit;
 	}
 
@@ -846,7 +846,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;
 }
 
 /*
@@ -904,7 +904,7 @@ createSpace(Parse * pParse, int iSpaceId, char *zStmt)
 	save_record(pParse, BOX_SPACE_ID, iFirstCol, 1, v->nOp - 1);
 	return;
 error:
-	pParse->rc = SQL_TARANTOOL_ERROR;
+	pParse->is_aborted = true;
 }
 
 int
@@ -1084,7 +1084,7 @@ vdbe_emit_fk_constraint_create(struct Parse *parse_context,
 	sqlReleaseTempRange(parse_context, constr_tuple_reg, 10);
 	return;
 error:
-	parse_context->rc = SQL_TARANTOOL_ERROR;
+	parse_context->is_aborted = true;
 }
 
 /**
@@ -1112,7 +1112,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;
 	return -1;
 }
 
@@ -1243,7 +1243,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;
 				return;
 			}
 			for (uint32_t i = 0; i < fk_def->field_count; ++i) {
@@ -1273,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->rc == SQL_TARANTOOL_ERROR)
+	if (space == NULL || parse_context->is_aborted)
 		goto create_view_fail;
 
 	struct space *select_res_space =
@@ -1318,7 +1318,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;
 		goto create_view_fail;
 	}
 
@@ -1591,14 +1591,14 @@ sql_drop_table(struct Parse *parse_context, struct SrcList *table_name_list,
 		goto exit_drop_table;
 	}
 	sqlVdbeCountChanges(v);
-	assert(parse_context->rc != SQL_TARANTOOL_ERROR);
+	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;
 	}
@@ -1637,7 +1637,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;
 			goto exit_drop_table;
 		}
 	}
@@ -1672,7 +1672,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;
 		return -1;
 	}
 	return 0;
@@ -1881,7 +1881,7 @@ exit_create_fk:
 	sqlDbFree(db, constraint_name);
 	return;
 tnt_error:
-	parse_context->rc = SQL_TARANTOOL_ERROR;
+	parse_context->is_aborted = true;
 	goto exit_create_fk;
 }
 
@@ -1905,7 +1905,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;
 		return;
 	}
 	char *constraint_name = sqlNameFromToken(parse_context->db,
@@ -2032,7 +2032,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->rc == SQL_TARANTOOL_ERROR)
+		if (parse->is_aborted)
 			goto cleanup;
 
 		struct Expr *column_expr = sqlExprSkipCollate(expr);
@@ -2083,7 +2083,7 @@ cleanup:
 		key_def_delete(key_def);
 	return rc;
 tnt_error:
-	parse->rc = SQL_TARANTOOL_ERROR;
+	parse->is_aborted = true;
 	goto cleanup;
 }
 
@@ -2111,7 +2111,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
 	struct sql *db = parse->db;
 	assert(!db->init.busy);
 
-	if (db->mallocFailed || parse->rc == SQL_TARANTOOL_ERROR)
+	if (db->mallocFailed || parse->is_aborted)
 		goto exit_create_index;
 	if (idx_type == SQL_INDEX_TYPE_UNIQUE ||
 	    idx_type == SQL_INDEX_TYPE_NON_UNIQUE) {
@@ -2133,7 +2133,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;
 			}
 			goto exit_create_index;
 		}
@@ -2179,7 +2179,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;
 		}
@@ -2226,7 +2226,7 @@ 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->rc = SQL_TARANTOOL_ERROR;
+		parse->is_aborted = true;
 		goto exit_create_index;
 	}
 
@@ -2254,7 +2254,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;
 		goto exit_create_index;
 	}
 	memset(index, 0, sizeof(*index));
@@ -2424,7 +2424,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->rc != SQL_TARANTOOL_ERROR);
+	assert(!parse_context->is_aborted);
 	assert(table_token != NULL);
 	const char *table_name = sqlNameFromToken(db, table_token);
 	if (db->mallocFailed) {
@@ -2437,7 +2437,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;
 	}
@@ -2448,7 +2448,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;
 	}
@@ -2806,7 +2806,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 6c26402..4919753 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;
 		return NULL;
 	} else {
 		*coll_id = p->id;
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index fba07a9..87d4ed4 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -43,14 +43,14 @@ 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);
 	if (space->def->field_count == 0) {
 		diag_set(ClientError, ER_UNSUPPORTED, "SQL",
 			 "space without format");
-		parse->rc = SQL_TARANTOOL_ERROR;
+		parse->is_aborted = true;
 		return NULL;
 	}
 	space_name->space = space;
@@ -115,7 +115,7 @@ cleanup:
 	return;
 
 tarantool_error:
-	parse->rc = SQL_TARANTOOL_ERROR;
+	parse->is_aborted = true;
 	goto cleanup;
 }
 
@@ -124,7 +124,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 		      struct Expr *where)
 {
 	struct sql *db = parse->db;
-	if (parse->rc == SQL_TARANTOOL_ERROR || 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 0911eee..7ea344b 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -254,10 +254,10 @@ sql_expr_coll(Parse *parse, Expr *p, bool *is_explicit_coll, uint32_t *coll_id,
 					 * several times: this
 					 * function is recursive.
 					 */
-					if (parse->rc != SQL_TARANTOOL_ERROR) {
+					if (!parse->is_aborted) {
 						diag_set(ClientError,
 							 ER_ILLEGAL_COLLATION_MIX);
-						parse->rc = SQL_TARANTOOL_ERROR;
+						parse->is_aborted = true;
 					}
 					return -1;
 				}
@@ -409,7 +409,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;
 		return -1;
 	}
 	return 0;
@@ -819,7 +819,7 @@ exprSetHeight(Expr * p)
 void
 sqlExprSetHeightAndFlags(Parse * pParse, Expr * p)
 {
-	if (pParse->rc == SQL_TARANTOOL_ERROR)
+	if (pParse->is_aborted)
 		return;
 	exprSetHeight(p);
 	sqlExprCheckHeight(pParse, p->nHeight);
@@ -1003,7 +1003,7 @@ sqlPExpr(Parse * pParse,	/* Parsing context */
     )
 {
 	Expr *p;
-	if (op == TK_AND && pParse->rc != SQL_TARANTOOL_ERROR) {
+	if (op == TK_AND && !pParse->is_aborted) {
 		/* Take advantage of short-circuit false optimization for AND */
 		p = sqlExprAnd(pParse->db, pLeft, pRight);
 	} else {
@@ -2405,8 +2405,7 @@ sqlFindInIndex(Parse * pParse,	/* Parsing context */
 	 * satisfy the query.  This is preferable to generating a new
 	 * ephemeral table.
 	 */
-	if (pParse->rc != SQL_TARANTOOL_ERROR &&
-	    (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;
@@ -3040,7 +3039,7 @@ sqlExprCodeIN(Parse * pParse,	/* Parsing and code generating context */
 				   destIfFalse == destIfNull ? 0 : &rRhsHasNull,
 				   aiMap, 0);
 
-	assert(pParse->rc == SQL_TARANTOOL_ERROR || nVector == 1 ||
+	assert(pParse->is_aborted || nVector == 1 ||
 	       eType == IN_INDEX_EPH || eType == IN_INDEX_INDEX_ASC ||
 	       eType == IN_INDEX_INDEX_DESC);
 #ifdef SQL_DEBUG
@@ -3364,7 +3363,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->rc == SQL_TARANTOOL_ERROR ||
+	assert(iReg > 0 || pParse->is_aborted ||
 	       pParse->db->mallocFailed);
 	assert(iCol >= -1 && iCol < 32768);	/* Finite column numbers */
 
@@ -3998,7 +3997,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;
 			}
 
@@ -4342,8 +4341,7 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 			} else {
 				sqlVdbeAddOp2(v, OP_Null, 0, target);
 			}
-			assert(pParse->db->mallocFailed ||
-			       pParse->rc == SQL_TARANTOOL_ERROR ||
+			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 e8cdd39..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->rc == SQL_TARANTOOL_ERROR || 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->rc == SQL_TARANTOOL_ERROR)
+		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 f980945..b8c0515 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;
   }
 }
@@ -1384,13 +1384,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 6dc41a3..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 */
@@ -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;
 			goto pragma_out;
 		}
 		int rc = box_iterator_next(iter, &tuple);
@@ -564,7 +564,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;
 			goto pragma_out;
 		}
 		if (zRight == NULL) {
@@ -575,7 +575,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;
 				goto pragma_out;
 			}
 			sqlVdbeAddOp0(v, OP_Expire);
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;
 	}
 	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 dfb6238..3215db3 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->rc == SQL_TARANTOOL_ERROR
+	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->rc == SQL_TARANTOOL_ERROR
+		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->rc == SQL_TARANTOOL_ERROR ||
+				if (pParse->is_aborted ||
 				    db->mallocFailed)
 					return WRC_Abort;
 
@@ -1337,7 +1337,7 @@ resolveSelectStep(Walker * pWalker, Select * p)
 					 "argument must appear in the GROUP BY "
 					 "clause or be used in an aggregate "
 					 "function");
-				pParse->rc = SQL_TARANTOOL_ERROR;
+				pParse->is_aborted = true;
 				return WRC_Abort;
 			}
 			/*
@@ -1532,7 +1532,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->rc == SQL_TARANTOOL_ERROR) {
+	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 4698bb4..cfac553 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->rc == SQL_TARANTOOL_ERROR
+	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->rc == SQL_TARANTOOL_ERROR);
+		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->rc == SQL_TARANTOOL_ERROR)
+	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;
@@ -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;
 		return 0;
 	}
 	*is_forced_coll = (is_prior_forced || is_current_forced);
@@ -3115,7 +3115,7 @@ generateOutputSubroutine(struct Parse *parse, struct Select *p,
 		 */
 	case SRT_Mem:{
 			assert(in->nSdst == 1 ||
-			       parse->rc == SQL_TARANTOOL_ERROR);
+			       parse->is_aborted);
 			testcase(in->nSdst != 1);
 			sqlExprCodeMove(parse, in->iSdst, dest->iSDParm,
 					    1);
@@ -3585,7 +3585,7 @@ multiSelectOrderBy(Parse * pParse,	/* Parsing context */
   *** subqueries ***
   */
 	explainComposite(pParse, p->op, iSub1, iSub2, 0);
-	return pParse->rc == SQL_TARANTOOL_ERROR;
+	return pParse->is_aborted;
 }
 #endif
 
@@ -4428,7 +4428,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 +5054,7 @@ selectExpander(Walker * pWalker, Select * p)
 						diag_set(ClientError,
 							 ER_SQL_SELECT_WILDCARD);
 					}
-					sql_parser_error(pParse);
+					pParse->is_aborted = true;
 				}
 			}
 		}
@@ -5095,9 +5095,8 @@ sqlExprWalkNoop(Walker * NotUsed, Expr * NotUsed2)
  * SELECT statement.  The SELECT statement must be expanded before
  * 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->rc
- * and/or pParse->db->mallocFailed.
+ * The calling function can detect the problem by looking at
+ * pParse->is_aborted and/or pParse->db->mallocFailed.
  */
 static void
 sqlSelectExpand(Parse * pParse, Select * pSelect)
@@ -5205,10 +5204,10 @@ sqlSelectPrep(Parse * pParse,	/* The parser context */
 	if (p->selFlags & SF_HasTypeInfo)
 		return;
 	sqlSelectExpand(pParse, p);
-	if (pParse->rc == SQL_TARANTOOL_ERROR || db->mallocFailed)
+	if (pParse->is_aborted || db->mallocFailed)
 		return;
 	sqlResolveSelectNames(pParse, p, pOuterNC);
-	if (pParse->rc == SQL_TARANTOOL_ERROR || 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->rc == SQL_TARANTOOL_ERROR) {
+	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->rc == SQL_TARANTOOL_ERROR || 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->rc == SQL_TARANTOOL_ERROR);
+	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 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. */
+	bool is_aborted;
 
   /**************************************************************************
   * Fields above must be initialized to zero.  The fields that follow,
@@ -3190,14 +3191,6 @@ int sqlKeywordCode(const unsigned char *, int);
 int sqlRunParser(Parse *, const char *, 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 fac2781..4ca3c2e 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -460,7 +460,6 @@ 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);
@@ -485,7 +484,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,21 +505,21 @@ 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) {
 				diag_set(ClientError, ER_SQL_UNKNOWN_TOKEN,
 					 pParse->sLastToken.n,
 					 pParse->sLastToken.z);
-				sql_parser_error(pParse);
+				pParse->is_aborted = true;
 				break;
 			}
 		} else {
 			sqlParser(pEngine, tokenType, pParse->sLastToken,
 				      pParse);
 			lastTokenParsed = tokenType;
-			if (pParse->rc != SQL_OK || db->mallocFailed)
+			if (pParse->is_aborted || db->mallocFailed)
 				break;
 		}
 	}
@@ -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);
 		nErr++;
 	}
-	if (pParse->pVdbe != NULL && pParse->rc == SQL_TARANTOOL_ERROR) {
+	if (pParse->pVdbe != NULL && pParse->is_aborted) {
 		sqlVdbeDelete(pParse->pVdbe);
 		pParse->pVdbe = 0;
 	}
@@ -560,7 +551,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);
+	assert(nErr == 0 || pParse->is_aborted);
 	return nErr;
 }
 
@@ -583,10 +574,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, &sql_error) == SQL_OK &&
+	    parser.parsed_ast_type == AST_TYPE_EXPR) {
 		expression = parser.parsed_ast.expr;
 		parser.parsed_ast.expr = NULL;
 	}
@@ -625,11 +614,8 @@ sql_trigger_compile(struct sql *db, const char *sql)
 	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, &sql_error) == SQL_OK &&
+	    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 6816028..7eacd33 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;
 	goto trigger_cleanup;
 }
 
@@ -168,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->rc == SQL_TARANTOOL_ERROR) || trigger == NULL)
+	if (NEVER(parse->is_aborted) || trigger == NULL)
 		goto cleanup;
 	char *trigger_name = trigger->zName;
 	trigger->step_list = step_list;
@@ -729,11 +729,11 @@ onErrorText(int onError)
 static void
 transferParseError(Parse * pTo, Parse * pFrom)
 {
-	assert(pFrom->zErrMsg == 0 || pFrom->rc == SQL_TARANTOOL_ERROR);
-	assert(pTo->zErrMsg == 0 || pTo->rc == SQL_TARANTOOL_ERROR);
-	if (pTo->rc != SQL_TARANTOOL_ERROR) {
+	assert(pFrom->zErrMsg == 0 || pFrom->is_aborted);
+	assert(pTo->zErrMsg == 0 || pTo->is_aborted);
+	if (!pTo->is_aborted) {
 		pTo->zErrMsg = pFrom->zErrMsg;
-		pTo->rc = pFrom->rc;
+		pTo->is_aborted = pFrom->is_aborted;
 	} else {
 		sqlDbFree(pFrom->db, pFrom->zErrMsg);
 	}
@@ -922,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->rc == SQL_TARANTOOL_ERROR ||
+	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 caf7911..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->rc == SQL_TARANTOOL_ERROR || 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 502d7ab..6129d5b 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -238,13 +238,7 @@ sqlErrorMsg(Parse * pParse, const char *zFormat, ...)
 	va_end(ap);
 	diag_set(ClientError, ER_SQL_PARSER_GENERIC, zMsg);
 	sqlDbFree(db, zMsg);
-	pParse->rc = SQL_TARANTOOL_ERROR;
-}
-
-void
-sql_parser_error(struct Parse *parse_context)
-{
-	parse_context->rc = SQL_TARANTOOL_ERROR;
+	pParse->is_aborted = true;
 }
 
 /*
diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
index a42e872..9957f3a 100644
--- a/src/box/sql/vdbemem.c
+++ b/src/box/sql/vdbemem.c
@@ -1215,7 +1215,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;
@@ -1228,7 +1228,7 @@ valueFromFunction(sql * db,	/* The database connection */
 		assert(rc == SQL_OK);
 	}
 	if (rc != SQL_OK)
-		pCtx->pParse->rc = SQL_TARANTOOL_ERROR;
+		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 d86b608..cf70e06 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -2799,7 +2799,7 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder,	/* WHERE clause information */
 		struct key_def *key_def = key_def_new(&part, 1);
 		if (key_def == NULL) {
 tnt_error:
-			pWInfo->pParse->rc = SQL_TARANTOOL_ERROR;
+			pWInfo->pParse->is_aborted = true;
 			return SQL_TARANTOOL_ERROR;
 		}
 
@@ -4457,7 +4457,7 @@ sqlWhereBegin(Parse * pParse,	/* The parser context */
 	    (user_session->sql_flags & SQL_ReverseOrder) != 0) {
 		pWInfo->revMask = ALLBITS;
 	}
-	if (pParse->rc == SQL_TARANTOOL_ERROR || 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 bcb10a5..e1c3366 100644
--- a/src/box/sql/wherecode.c
+++ b/src/box/sql/wherecode.c
@@ -1472,8 +1472,7 @@ sqlWhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about the W
 				    sqlWhereBegin(pParse, pOrTab, pOrExpr,
 						      0, 0, wctrlFlags,
 						      iCovCur);
-				assert(pSubWInfo ||
-				       pParse->rc == SQL_TARANTOOL_ERROR
+				assert(pSubWInfo || pParse->is_aborted
 				       || db->mallocFailed);
 				if (pSubWInfo) {
 					WhereLoop *pSubLoop;
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] 30+ messages in thread

* [tarantool-patches] [PATCH v3 5/9] sql: remove field zErrMsg of struct Parse
  2019-03-02 13:07 [tarantool-patches] [PATCH v3 0/9] sql: use diag_set() for errors in SQL imeevma
                   ` (3 preceding siblings ...)
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 4/9] sql: remove field rc " imeevma
@ 2019-03-02 13:07 ` imeevma
  2019-03-05  8:43   ` [tarantool-patches] " Konstantin Osipov
  2019-03-05  9:06   ` n.pettik
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 6/9] sql: rework six syntax errors imeevma
                   ` (3 subsequent siblings)
  8 siblings, 2 replies; 30+ messages in thread
From: imeevma @ 2019-03-02 13:07 UTC (permalink / raw)
  To: korablev; +Cc: tarantool-patches

Hi! Thank you for review. My answers and new patch below. There
won't be diff between versions as I completely rewritten this
patch after placein it after removing of rc and nErr.

On 2/26/19 5:47 PM, n.pettik wrote:
>
>> diff --git a/src/box/sql.c b/src/box/sql.c
>> index 580f3fa..116e3e8 100644
>> --- a/src/box/sql.c
>> +++ b/src/box/sql.c
>> @@ -1362,13 +1362,8 @@ 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.rc != SQL_OK) {
>> -    /* Tarantool error may be already set with diag. */
>> -    if (parser.rc != SQL_TARANTOOL_ERROR)
>> -      diag_set(ClientError, ER_SQL, parser.zErrMsg);
>> -    rc = -1;
>> -  }
>> +  if (parser.rc != SQL_OK)
>> +    return -1;
>
> Since now we have only one possible RC, lets remove
> its name and simply check (parser.rc != 0).
> Or, as suggested Konstantin, better replace rc with bool is_aborted flag.
>
Done in previous patch.

>>  sql_parser_destroy(&parser);
>> -  return rc;
>> +  return 0;
>> }
>> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
>> index deb5b89..6afca4a 100644
>> --- a/src/box/sql/build.c
>> +++ b/src/box/sql/build.c
>> @@ -493,16 +493,10 @@ sql_column_add_nullable_action(struct Parse *parser,
>>  struct field_def *field = &def->fields[def->field_count - 1];
>>  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);
>> -    parser->rc = SQL_TARANTOOL_ERROR;
>> -    parser->nErr++;
>> +    sqlErrorMsg(parser, "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]);
>
> This looks like step back in our attempt at using diag_set.
> We do you need to incapsulate diag into sqlErrorMsg?
>
Didn't include this change in new version. Actually, in a few
patches all left mentions of sqlErrorMsg() will be replaced by
tt_sprintf() + diag_set() + parser->is_aborted = true;

>>    return;
>>  }
>>  field->nullable_action = nullable_action;
>> diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
>> index 5170c7f..a7bf3b3 100644
>> --- a/src/box/sql/delete.c
>> +++ b/src/box/sql/delete.c
>> @@ -94,31 +94,22 @@ sql_table_truncate(struct Parse *parse, struct SrcList *tab_list)
>>  struct space *space = space_by_name(tab_name);
>>  if (space == NULL) {
>>    diag_set(ClientError, ER_NO_SUCH_SPACE, tab_name);
>> -    goto tarantool_error;
>> +    sql_parser_error(parse);
>
> Look, anyway you remove this function in next commit.
> Next time please consider order of refactoring.
>
Fixed due to changed position of this patch.

>>  }
>>  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);
>> -    goto tarantool_error;
>> +    sqlErrorMsg(parse, "can not truncate space '%s' because other "
>> +          "objects depend on it", space->def->name);
>
> Replace invocation of sqlErrorMsg with diag_set + parser->rc.
> The same in other places.
>
Fixed, see my second answer.

>> @@ -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.


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);
 
  /* 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.
  *
  * This routine does NOT free the Select structure passed in.  The
  * calling function needs to do that.
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 42ff4b8..85718e1 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -2641,7 +2641,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[] */
@@ -3188,7 +3187,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 *);
 
 /**
  * This routine is called after a single SQL statement has been
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.
  */
 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 */
@@ -462,7 +459,6 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
  }
  pParse->zTail = zSql;
  i = 0;
- assert(pzErrMsg != 0);
  /* sqlParserTrace(stdout, "parser: "); */
  pEngine = sqlParserAlloc(sqlMalloc);
  if (pEngine == 0) {
@@ -535,14 +531,8 @@ sqlRunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
    diag_set(OutOfMemory, 0, "SQL", "db");
    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(SQL_TARANTOOL_ERROR, "%s", *pzErrMsg);
+ if (pParse->is_aborted)
    nErr++;
- }
  if (pParse->pVdbe != NULL && pParse->is_aborted) {
    sqlVdbeDelete(pParse->pVdbe);
    pParse->pVdbe = 0;
@@ -573,8 +563,7 @@ 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 &&
+ if (sqlRunParser(&parser, stmt) == SQL_OK &&
      parser.parsed_ast_type == AST_TYPE_EXPR) {
    expression = parser.parsed_ast.expr;
    parser.parsed_ast.expr = NULL;
@@ -593,8 +582,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) != SQL_OK ||
      parser.parsed_ast_type != AST_TYPE_SELECT) {
    diag_set(ClientError, ER_SQL_EXECUTE, view_stmt);
  } else {
@@ -612,9 +600,8 @@ 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 &&
+ if (sqlRunParser(&parser, sql) == SQL_OK &&
      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 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,
diff --git a/src/box/sql/util.c b/src/box/sql/util.c
index 6129d5b..07527ff 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -211,7 +211,7 @@ sqlErrorWithMsg(sql * db, int err_code, const char *zFormat, ...)
 }
 
 /*
- * Add an error message to pParse->zErrMsg.
+ * Add an error message to diag.
  * The following formatting characters are allowed:
  *
  *      %s      Insert a string
-- 
2.7.4

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

* [tarantool-patches] [PATCH v3 6/9] sql: rework six syntax errors
  2019-03-02 13:07 [tarantool-patches] [PATCH v3 0/9] sql: use diag_set() for errors in SQL imeevma
                   ` (4 preceding siblings ...)
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 5/9] sql: remove field zErrMsg " imeevma
@ 2019-03-02 13:07 ` imeevma
  2019-03-05  8:45   ` [tarantool-patches] " Konstantin Osipov
  2019-03-05  9:07   ` n.pettik
  2019-03-02 13:08 ` [tarantool-patches] [PATCH v3 7/9] sql: rework four semantic errors imeevma
                   ` (2 subsequent siblings)
  8 siblings, 2 replies; 30+ messages in thread
From: imeevma @ 2019-03-02 13:07 UTC (permalink / raw)
  To: korablev; +Cc: tarantool-patches

This patch rewords six more syntax errors.

Part of #3965
---
 src/box/errcode.h             |  3 +++
 src/box/sql/build.c           |  4 +--
 src/box/sql/resolve.c         | 62 +++++++++++++++++++++----------------------
 test/box/misc.result          |  3 +++
 test/sql-tap/check.test.lua   |  2 +-
 test/sql-tap/colname.test.lua |  2 +-
 6 files changed, 40 insertions(+), 36 deletions(-)

diff --git a/src/box/errcode.h b/src/box/errcode.h
index d234d26..057a6d3 100644
--- a/src/box/errcode.h
+++ b/src/box/errcode.h
@@ -239,6 +239,9 @@ 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_INDEX_DEF,			"%s prohibited in an index definition") \
+	/*188 */_(ER_CHECK_CONSTRAINT_DEF,	"%s prohibited in prohibited in a CHECK constraint definition") \
+	/*189 */_(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 a1f4b1b..651e02a 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -607,8 +607,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;
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 3215db3..02eca37 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;
@@ -690,9 +669,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 +736,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 +756,16 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 			break;
 		}
 	case TK_VARIABLE:{
-			notValid(pParse, pNC, "parameters",
-				 NC_IsCheck | NC_IdxExpr);
+			if (pNC->ncFlags & NC_IdxExpr) {
+				diag_set(ClientError, ER_INDEX_DEF,
+					 "Parameter markers are");
+				pParse->is_aborted = true;
+			} else if (pNC->ncFlags & NC_IsCheck) {
+				diag_set(ClientError,
+					 ER_CHECK_CONSTRAINT_DEF,
+					 "Parameter markers are");
+				pParse->is_aborted = true;
+			}
 			break;
 		}
 	case TK_BETWEEN:
diff --git a/test/box/misc.result b/test/box/misc.result
index 9f0b2c7..a3bc7b7 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -515,6 +515,9 @@ t;
   184: box.error.SQL_UNRECOGNIZED_SYNTAX
   185: box.error.SQL_UNKNOWN_TOKEN
   186: box.error.SQL_PARSER_GENERIC
+  187: box.error.INDEX_DEF
+  188: box.error.CHECK_CONSTRAINT_DEF
+  189: box.error.PRIMARY_KEY_DEF
 ...
 test_run:cmd("setopt delimiter ''");
 ---
diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
index 0d8bf15..e1d2d48 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 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",
-- 
2.7.4

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

* [tarantool-patches] [PATCH v3 7/9] sql: rework four semantic errors
  2019-03-02 13:07 [tarantool-patches] [PATCH v3 0/9] sql: use diag_set() for errors in SQL imeevma
                   ` (5 preceding siblings ...)
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 6/9] sql: rework six syntax errors imeevma
@ 2019-03-02 13:08 ` imeevma
  2019-03-05  8:46   ` [tarantool-patches] " Konstantin Osipov
  2019-03-05  9:16   ` n.pettik
  2019-03-02 13:08 ` [tarantool-patches] [PATCH v3 8/9] sql: rework three errors of "unsupported" type imeevma
  2019-03-02 13:08 ` [tarantool-patches] [PATCH v3 9/9] sql: remove sqlErrorMsg() imeevma
  8 siblings, 2 replies; 30+ messages in thread
From: imeevma @ 2019-03-02 13:08 UTC (permalink / raw)
  To: korablev; +Cc: tarantool-patches

This patch reworks four semantic errors.

Part of #3965
---
 src/box/errcode.h                                  |  4 ++++
 src/box/sql/build.c                                |  4 +++-
 src/box/sql/expr.c                                 | 28 ++++++++++++----------
 src/box/sql/parse.y                                | 16 +++++++++----
 src/box/sql/select.c                               |  5 +++-
 src/box/sql/where.c                                |  4 +++-
 test/box/misc.result                               |  4 ++++
 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/join.test.lua                         | 18 +++++++-------
 test/sql-tap/join3.test.lua                        |  2 +-
 test/sql-tap/select7.test.lua                      |  2 +-
 test/sql-tap/where7.test.lua                       |  2 +-
 test/sql/gh-2347-max-int-literals.result           |  6 +++--
 test/sql/integer-overflow.result                   |  9 ++++---
 test/sql/iproto.result                             |  5 ++--
 17 files changed, 72 insertions(+), 45 deletions(-)

diff --git a/src/box/errcode.h b/src/box/errcode.h
index 057a6d3..06f7a63 100644
--- a/src/box/errcode.h
+++ b/src/box/errcode.h
@@ -242,6 +242,10 @@ struct errcode_record {
 	/*187 */_(ER_INDEX_DEF,			"%s prohibited in an index definition") \
 	/*188 */_(ER_CHECK_CONSTRAINT_DEF,	"%s prohibited in prohibited in a CHECK constraint definition") \
 	/*189 */_(ER_PRIMARY_KEY_DEF,		"Expressions are prohibited in a primary key definition") \
+	/*190 */_(ER_COLUMN_COUNT_MAX,		"Failed to create space '%s': space column count %d exceeds the limit (%d)") \
+	/*191 */_(ER_HEX_LITERAL_MAX,		"Hex literal %s%s length %d exceeds the supported limit (%d)") \
+	/*192 */_(ER_INT_LITERAL_MAX,		"Integer literal %s%s exceeds the supported range %lld - %lld") \
+	/*193 */_(ER_SQL_PARSER_LIMIT,		"%s%.*s %d exceeds the limit (%d)") \
 
 /*
  * !IMPORTANT! Please follow instructions at start of the file
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 651e02a..64d2690 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -436,7 +436,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_COLUMN_COUNT_MAX, def->name,
+			 def->field_count + 1, db->aLimit[SQL_LIMIT_COLUMN]);
+		pParse->is_aborted = true;
 		return;
 	}
 #endif
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 7ea344b..ffcb455 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -730,9 +730,9 @@ 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);
+		diag_set(ClientError, ER_SQL_PARSER_LIMIT, "Number of nodes "\
+			 "in expression tree", 0, "", nHeight, mxHeight);
+		pParse->is_aborted = true;
 		rc = SQL_ERROR;
 	}
 	return rc;
@@ -1174,9 +1174,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) {
@@ -1204,7 +1204,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;
 	}
 }
 
@@ -3314,15 +3316,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/parse.y b/src/box/sql/parse.y
index b8c0515..b69f059 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -410,9 +410,9 @@ 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, mxSelect);
+        pParse->is_aborted = true;
       }
     }
   }
@@ -930,7 +930,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);
@@ -944,7 +947,10 @@ type_func(A) ::= DATETIME(A) .
 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/select.c b/src/box/sql/select.c
index 66cbc73..30ba947 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -5063,7 +5063,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
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/misc.result b/test/box/misc.result
index a3bc7b7..2bb6613 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -518,6 +518,10 @@ t;
   187: box.error.INDEX_DEF
   188: box.error.CHECK_CONSTRAINT_DEF
   189: box.error.PRIMARY_KEY_DEF
+  190: box.error.COLUMN_COUNT_MAX
+  191: box.error.HEX_LITERAL_MAX
+  192: box.error.INT_LITERAL_MAX
+  193: box.error.SQL_PARSER_LIMIT
 ...
 test_run:cmd("setopt delimiter ''");
 ---
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/join.test.lua b/test/sql-tap/join.test.lua
index df272a9..ce0d9e9 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..91118dc 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 65 exceeds the limit (64)"
         -- </join3-3.1>
     })
 
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/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/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/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 = {}
 ---
-- 
2.7.4

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

* [tarantool-patches] [PATCH v3 8/9] sql: rework three errors of "unsupported" type
  2019-03-02 13:07 [tarantool-patches] [PATCH v3 0/9] sql: use diag_set() for errors in SQL imeevma
                   ` (6 preceding siblings ...)
  2019-03-02 13:08 ` [tarantool-patches] [PATCH v3 7/9] sql: rework four semantic errors imeevma
@ 2019-03-02 13:08 ` imeevma
  2019-03-05  8:47   ` [tarantool-patches] " Konstantin Osipov
  2019-03-05  9:34   ` n.pettik
  2019-03-02 13:08 ` [tarantool-patches] [PATCH v3 9/9] sql: remove sqlErrorMsg() imeevma
  8 siblings, 2 replies; 30+ messages in thread
From: imeevma @ 2019-03-02 13:08 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 06f7a63..f2ea27a 100644
--- a/src/box/errcode.h
+++ b/src/box/errcode.h
@@ -246,6 +246,7 @@ struct errcode_record {
 	/*191 */_(ER_HEX_LITERAL_MAX,		"Hex literal %s%s length %d exceeds the supported limit (%d)") \
 	/*192 */_(ER_INT_LITERAL_MAX,		"Integer literal %s%s exceeds the supported range %lld - %lld") \
 	/*193 */_(ER_SQL_PARSER_LIMIT,		"%s%.*s %d exceeds the limit (%d)") \
+	/*194 */_(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 9bcbe49..796c7bc 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 30ba947..261a8bd 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 2bb6613..6370592 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -522,6 +522,7 @@ t;
   191: box.error.HEX_LITERAL_MAX
   192: box.error.INT_LITERAL_MAX
   193: box.error.SQL_PARSER_LIMIT
+  194: 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 ce0d9e9..ef60609 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] 30+ messages in thread

* [tarantool-patches] [PATCH v3 9/9] sql: remove sqlErrorMsg()
  2019-03-02 13:07 [tarantool-patches] [PATCH v3 0/9] sql: use diag_set() for errors in SQL imeevma
                   ` (7 preceding siblings ...)
  2019-03-02 13:08 ` [tarantool-patches] [PATCH v3 8/9] sql: rework three errors of "unsupported" type imeevma
@ 2019-03-02 13:08 ` imeevma
  2019-03-05  8:48   ` [tarantool-patches] " Konstantin Osipov
  2019-03-05 12:16   ` n.pettik
  8 siblings, 2 replies; 30+ messages in thread
From: imeevma @ 2019-03-02 13:08 UTC (permalink / raw)
  To: korablev; +Cc: tarantool-patches

This patch replaces sqlErrorMsg() by tt_sprintf() + diag_set().

Part of #3965
---
 src/box/sql/build.c             | 100 ++++++++++++++++----------
 src/box/sql/delete.c            |   7 +-
 src/box/sql/expr.c              |  85 +++++++++++++---------
 src/box/sql/insert.c            |  44 ++++++++----
 src/box/sql/parse.y             |  12 ++--
 src/box/sql/resolve.c           | 137 +++++++++++++++++++++++-------------
 src/box/sql/select.c            | 151 +++++++++++++++++++++++++---------------
 src/box/sql/sqlInt.h            |   1 -
 src/box/sql/trigger.c           |   7 +-
 src/box/sql/update.c            |  19 +++--
 src/box/sql/util.c              |  31 ---------
 src/box/sql/vdbemem.c           |   4 +-
 src/box/sql/where.c             |   4 +-
 src/box/sql/whereexpr.c         |   8 ++-
 test/sql-tap/e_select1.test.lua |  20 +++---
 test/sql-tap/null.test.lua      |   4 +-
 test/sql-tap/select1.test.lua   |   6 +-
 test/sql-tap/select3.test.lua   |   4 +-
 test/sql-tap/select4.test.lua   |   6 +-
 test/sql-tap/tkt2822.test.lua   |  30 ++++----
 test/sql-tap/with1.test.lua     |   2 +-
 21 files changed, 406 insertions(+), 276 deletions(-)

diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 64d2690..5d2a7dc 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -271,11 +271,16 @@ 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);
+	if (len > BOX_NAME_MAX) {
+		const char *err_msg =
+			tt_sprintf("identifier name is invalid: %s",
+				   tt_cstr(zName, BOX_INVALID_NAME_MAX));
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+		pParse->is_aborted = true;
+		return SQL_ERROR;
+	}
+	if (identifier_check(zName, len) != 0) {
+		pParse->is_aborted = true;
 		return SQL_ERROR;
 	}
 	return SQL_OK;
@@ -527,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 *err_msg =
+				tt_sprintf("default value of column [%s] is "\
+					   "not constant",
+					   def->fields[def->field_count - 1].name);
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+			pParse->is_aborted = true;
 		} else {
 			assert(def != NULL);
 			struct field_def *field =
@@ -594,9 +602,11 @@ 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);
+		const char *err_msg = tt_sprintf("table \"%s\" has more than "\
+						 "one primary key",
+						 space->def->name);
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+		pParse->is_aborted = true;
 		goto primary_key_exit;
 	}
 	if (pList == NULL) {
@@ -642,8 +652,12 @@ 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");
+		const char *err_msg = tt_sprintf("AUTOINCREMENT is only "\
+						 "allowed on an INTEGER "\
+						 "PRIMARY KEY or INT PRIMARY "\
+						 "KEY");
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+		pParse->is_aborted = true;
 		goto primary_key_exit;
 	} else {
 		sql_create_index(pParse, 0, 0, pList, 0, sortOrder, false,
@@ -1149,9 +1163,11 @@ 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);
+			const char *err_msg = tt_sprintf("PRIMARY KEY missing "\
+							 "on table %s",
+							 new_space->def->name);
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+			pParse->is_aborted = true;
 			goto cleanup;
 		}
 	}
@@ -1269,8 +1285,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");
+		const char *err_msg = tt_sprintf("parameters are not allowed "\
+						 "in views");
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+		parse_context->is_aborted = true;
 		goto create_view_fail;
 	}
 	sqlStartTable(parse_context, name, if_exists);
@@ -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);
+		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);
+		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);
+			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);
+		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)
 			return;
-		}
 		sqlVdbeAddOp4(v, OP_Savepoint, op, 0, 0, zName, P4_DYNAMIC);
 	}
 }
@@ -3038,9 +3064,11 @@ sqlWithAdd(Parse * pParse,	/* Parsing context */
 		int i;
 		for (i = 0; i < pWith->nCte; i++) {
 			if (strcmp(zName, pWith->a[i].zName) == 0) {
-				sqlErrorMsg(pParse,
-						"duplicate WITH table name: %s",
-						zName);
+				const char *err_msg =
+					tt_sprintf("duplicate WITH table "\
+						   "name: %s", zName);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+				pParse->is_aborted = true;
 			}
 		}
 	}
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index 87d4ed4..d99a8ea 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -157,8 +157,11 @@ 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);
+			const char *err_msg = tt_sprintf("cannot modify %s "\
+							 "because it is a view",
+							 space->def->name);
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+			parse->is_aborted = true;
 			goto delete_from_cleanup;
 		}
 	}
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index ffcb455..14b2985 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -645,7 +645,9 @@ codeVectorCompare(Parse * pParse,	/* Code generator context */
 	int addrDone = sqlVdbeMakeLabel(v);
 
 	if (nLeft != sqlExprVectorSize(pRight)) {
-		sqlErrorMsg(pParse, "row value misused");
+		const char *err_msg = tt_sprintf("row value misused");
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+		pParse->is_aborted = true;
 		return;
 	}
 	assert(pExpr->op == TK_EQ || pExpr->op == TK_NE
@@ -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);
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+		pParse->is_aborted = true;
 	}
 }
 
@@ -2638,19 +2645,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:
@@ -2664,12 +2658,16 @@ sqlSubselectError(Parse * pParse, int nActual, int nExpect)
 void
 sqlVectorErrorMsg(Parse * pParse, Expr * pExpr)
 {
+	const char *err_msg;
 	if (pExpr->flags & EP_xIsSelect) {
-		sqlSubselectError(pParse, pExpr->x.pSelect->pEList->nExpr,
-				      1);
+		err_msg = tt_sprintf("sub-select returns %d columns - "\
+				     "expected %d",
+				     pExpr->x.pSelect->pEList->nExpr, 1);
 	} else {
-		sqlErrorMsg(pParse, "row value misused");
+		err_msg = tt_sprintf("row value misused");
 	}
+	diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+	pParse->is_aborted = true;
 }
 
 /*
@@ -2955,9 +2953,13 @@ sqlExprCheckIN(Parse * pParse, Expr * pIn)
 	int nVector = sqlExprVectorSize(pIn->pLeft);
 	if ((pIn->flags & EP_xIsSelect)) {
 		if (nVector != pIn->x.pSelect->pEList->nExpr) {
-			sqlSubselectError(pParse,
-					      pIn->x.pSelect->pEList->nExpr,
-					      nVector);
+			const char *err_msg =
+				tt_sprintf("sub-select returns %d columns - "\
+					   "expected %d",
+					   pIn->x.pSelect->pEList->nExpr,
+					   nVector);
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+			pParse->is_aborted = true;
 			return 1;
 		}
 	} else if (nVector != 1) {
@@ -3961,9 +3963,12 @@ 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_msg =
+					tt_sprintf("misuse of aggregate: %s()",
+						   pExpr->u.zToken);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 err_msg);
+				pParse->is_aborted = true;
 			} else {
 				pExpr->type = pInfo->aFunc->pFunc->ret_type;
 				return pInfo->aFunc[pExpr->iAgg].iMem;
@@ -4130,7 +4135,13 @@ 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_msg =
+					tt_sprintf("sub-select returns %d "\
+						   "columns - expected %d",
+						   nCol, 1);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 err_msg);
+				pParse->is_aborted = true;
 			} else {
 				return sqlCodeSubselect(pParse, pExpr, 0);
 			}
@@ -4149,9 +4160,12 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 						 sqlExprVectorSize(pExpr->
 								       pLeft))
 			    ) {
-				sqlErrorMsg(pParse,
-						"%d columns assigned %d values",
-						pExpr->iTable, n);
+				const char *err_msg =
+					tt_sprintf("%d columns assigned %d "\
+						   "values", pExpr->iTable, n);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 err_msg);
+				pParse->is_aborted = true;
 			}
 			return pExpr->pLeft->iTable + pExpr->iColumn;
 		}
@@ -4250,7 +4264,9 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 		}
 
 	case TK_VECTOR:{
-			sqlErrorMsg(pParse, "row value misused");
+			const char *err_msg = tt_sprintf("row value misused");
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+			pParse->is_aborted = true;
 			break;
 		}
 
@@ -4350,8 +4366,11 @@ 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");
+			const char *err_msg = tt_sprintf("RAISE() may only be "\
+							 "used within a "\
+							 "trigger-program");
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+			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 6f7f020..67aa675 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -322,8 +322,10 @@ 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);
+		const char *err_msg = tt_sprintf("cannot modify %s because it "\
+						 "is a view", space_def->name);
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+		pParse->is_aborted = true;
 		goto insert_cleanup;
 	}
 
@@ -388,16 +390,23 @@ 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);
+				const char *space_name =
+					pTabList->a[0].space->def->name;
+				const char *err_msg =
+					tt_sprintf("table %s has no column "\
+						   "named %s", space_name,
+						   pColumn->a[i].zName);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+				pParse->is_aborted = true;
 				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_msg =
+					tt_sprintf("table id list: duplicate "\
+						   "column name %s",
+						   pColumn->a[i].zName);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+				pParse->is_aborted = true;
 				goto insert_cleanup;
 			}
 			bit_set(used_columns, j);
@@ -508,14 +517,21 @@ 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 *space_name = pTabList->a[0].space->def->name;
+		const char *err_msg =
+			tt_sprintf("table %s has %d columns but %d values "\
+				   "were supplied", space_name,
+				   space_def->field_count, nColumn);
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+		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_msg =
+			tt_sprintf("%d values for %d columns", nColumn,
+				   pColumn->nId);
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+		pParse->is_aborted = true;
 		goto insert_cleanup;
 	}
 
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");
+    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;
@@ -1377,9 +1379,11 @@ 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");
+  const char *err_msg = tt_sprintf("qualified table names are not allowed on "\
+                                   "INSERT, UPDATE, and DELETE statements "\
+                                   "within triggers");
+  diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+  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 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);
+						diag_set(ClientError,
+							 ER_SQL_PARSER_GENERIC,
+							 err_msg);
+						pParse->is_aborted = true;
 						return WRC_Abort;
 					}
 					if (sqlExprVectorSize(pOrig) != 1) {
-						sqlErrorMsg(pParse,
-								"row value misused");
+						const char *err_msg =
+							tt_sprintf("row value "\
+								   "misused");
+						diag_set(ClientError,
+							 ER_SQL_PARSER_GENERIC,
+							 err_msg);
+						pParse->is_aborted = true;
 						return WRC_Abort;
 					}
 					resolveAlias(pParse, pEList, j, pExpr,
@@ -426,12 +437,15 @@ lookupName(Parse * pParse,	/* The parsing context */
 	 * more matches.  Either way, we have an error.
 	 */
 	if (cnt > 1) {
+		const char *err_msg;
 		if (zTab) {
-			sqlErrorMsg(pParse, "ambiguous column name: %s.%s",
-				    zTab, zCol);
+			err_msg = tt_sprintf("ambiguous column name: %s.%s",
+					     zTab, zCol);
 		} else {
-			sqlErrorMsg(pParse, "ambiguous column name: %s", zCol);
+			err_msg = tt_sprintf("ambiguous column name: %s", zCol);
 		}
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+		pParse->is_aborted = true;
 		pTopNC->nErr++;
 	}
 	if (cnt == 0) {
@@ -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");
+							diag_set(ClientError,
+								 ER_SQL_PARSER_GENERIC,
+								 err_msg);
+							pParse->is_aborted = true;
 							pNC->nErr++;
 						}
 					} else {
@@ -678,9 +696,12 @@ 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_msg =
+					tt_sprintf("misuse of aggregate "\
+						   "function %.*s()", nId, zId);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 err_msg);
+				pParse->is_aborted = true;
 				pNC->nErr++;
 				is_agg = 0;
 			} else if (no_such_func && pParse->db->init.busy == 0
@@ -692,9 +713,13 @@ 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_msg =
+					tt_sprintf("wrong number of arguments "\
+						   "to function %.*s()", nId,
+						   zId);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 err_msg);
+				pParse->is_aborted = true;
 				pNC->nErr++;
 			}
 			if (is_agg)
@@ -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");
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 err_msg);
+				pParse->is_aborted = true;
 			}
 			break;
 		}
@@ -904,21 +933,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.
@@ -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");
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+		pParse->is_aborted = true;
 		return 1;
 	}
 #endif
@@ -976,9 +993,13 @@ 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_msg =
+						tt_sprintf("ORDER BY term out "\
+							   "of range - should "\
+							   "be between 1 and "\
+							   "%d", pEList->nExpr);
+					diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+					pParse->is_aborted = true;
 					return 1;
 				}
 			} else {
@@ -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");
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+			pParse->is_aborted = true;
 			return 1;
 		}
 	}
@@ -1059,8 +1082,10 @@ 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 = tt_sprintf("too many terms in %s BY "\
+						 "clause", zType);
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+		pParse->is_aborted = true;
 		return 1;
 	}
 #endif
@@ -1069,8 +1094,12 @@ 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_msg =
+					tt_sprintf("%s BY term out of range - "\
+						   "should be between 1 and %d",
+						   zType, pEList->nExpr);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+				pParse->is_aborted = true;
 				return 1;
 			}
 			resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol - 1,
@@ -1136,8 +1165,12 @@ 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_msg =
+					tt_sprintf("%s BY term out of range - "\
+						   "should be between 1 and %d",
+						   zType, nResult);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+				pParse->is_aborted = true;
 				return 1;
 			}
 			pItem->u.x.iOrderByCol = (u16) iCol;
@@ -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");
+					diag_set(ClientError,
+						 ER_SQL_PARSER_GENERIC,
+						 err_msg);
+					pParse->is_aborted = true;
 					return WRC_Abort;
 				}
 			}
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 261a8bd..f15e453 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -397,13 +397,18 @@ 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);
+		const char *err_msg;
 		if (pC == 0) {
-			zSp++;
+			err_msg = tt_sprintf("unknown or unsupported join "\
+					     "type: %.*s %.*s", pA->n, pA->z,
+					     pB->n, pB->z);
+		} else {
+			err_msg = 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_msg);
+		pParse->is_aborted = true;
 		jointype = JT_INNER;
 	} else if ((jointype & JT_OUTER) != 0
 		   && (jointype & (JT_LEFT | JT_RIGHT)) != JT_LEFT) {
@@ -590,9 +595,13 @@ 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);
+				const char *err_msg =
+					tt_sprintf("a NATURAL join may not "\
+						   "have an ON or USING "\
+						   "clause");
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 err_msg);
+				pParse->is_aborted = true;
 				return 1;
 			}
 			for (j = 0; j < (int)right_space->def->field_count; j++) {
@@ -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);
+					diag_set(ClientError,
+						 ER_SQL_PARSER_GENERIC,
+						 err_msg);
+					pParse->is_aborted = true;
 					return 1;
 				}
 				addWhereTerm(pParse, pSrc, iLeft, iLeftCol,
@@ -2601,16 +2619,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;
 	}
@@ -2991,14 +3013,17 @@ multiSelect(Parse * pParse,	/* Parsing context */
 void
 sqlSelectWrongNumTermsError(struct Parse *parse, struct Select * p)
 {
+	const char *err_msg;
 	if (p->selFlags & SF_Values) {
-		sqlErrorMsg(parse, "all VALUES must have the same number "\
-				"of terms");
+		err_msg = tt_sprintf("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));
+		err_msg = tt_sprintf("SELECTs to the left and right of %s do "\
+				     "not have the same number of result "\
+				     "columns", selectOpName(p->op));
 	}
+	diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+	parse->is_aborted = true;
 }
 
 /**
@@ -4521,21 +4546,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
@@ -4630,11 +4640,20 @@ withExpand(Walker * pWalker, struct SrcList_item *pFrom)
 		 * In this case, proceed.
 		 */
 		if (pCte->zCteErr) {
-			sqlErrorMsg(pParse, pCte->zCteErr, pCte->zName);
+			const char *err_msg =
+				tt_sprintf(pCte->zCteErr, pCte->zName);
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+			pParse->is_aborted = true;
 			return SQL_ERROR;
 		}
-		if (cannotBeFunction(pParse, pFrom))
+		if (pFrom->fg.isTabFunc) {
+			const char *err_msg =
+				tt_sprintf("'%s' is not a function",
+					   pFrom->zName);
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+			pParse->is_aborted = true;
 			return SQL_ERROR;
+		}
 
 		assert(pFrom->space == NULL);
 		pFrom->space = sql_ephemeral_space_new(pParse, pCte->zName);
@@ -4666,9 +4685,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 ||
@@ -4684,10 +4705,14 @@ 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;
 			}
@@ -4843,8 +4868,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_msg =
+					tt_sprintf("'%s' is not a function",
+						   pFrom->zName);
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+					 err_msg);
+				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 +5286,11 @@ 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");
+				const char *err_msg =
+					tt_sprintf("DISTINCT aggregates must "\
+						   "have exactly one argument");
+				diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+				pParse->is_aborted = true;
 				pFunc->iDistinct = -1;
 			} else {
 				struct sql_key_info *key_info =
@@ -5526,10 +5560,13 @@ 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);
+			const char *err_msg =
+				tt_sprintf("expected %d columns for '%s' but "\
+					   "got %d", space->def->field_count,
+					   space->def->name,
+					   pSub->pEList->nExpr);
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+			pParse->is_aborted = true;
 			goto select_end;
 		}
 
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.");
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+		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 05ceeb4..88ebc90 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -139,8 +139,10 @@ 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);
+		const char *err_msg = tt_sprintf("cannot modify %s because it "\
+						 "is a view", space->def->name);
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+		pParse->is_aborted = true;
 		goto update_cleanup;
 	}
 
@@ -178,10 +180,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_msg =
+						tt_sprintf("set id list: "\
+							   "duplicate column "\
+							   "name %s",
+							   pChanges->a[i].zName);
+					diag_set(ClientError,
+						 ER_SQL_PARSER_GENERIC,
+						 err_msg);
+					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 07527ff..cac404f 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -211,37 +211,6 @@ sqlErrorWithMsg(sql * db, int err_code, const char *zFormat, ...)
 }
 
 /*
- * Add an error message to diag.
- * 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 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));
+		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");
+		diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+		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..535914f 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_msg =
+				tt_sprintf("too many arguments on %s() - max "\
+					   "%d", space_def->name, j);
+			diag_set(ClientError, ER_SQL_PARSER_GENERIC, err_msg);
+			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..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"})
 
 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, "ORDER BY term does not match any column in the result set"})
 
 test:do_select_tests(
     "e_select-8.7.2",
@@ -2077,20 +2077,20 @@ 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"},
+    {2, "SELECT a FROM d5 UNION SELECT c FROM d6 ORDER BY a, a+1"},
+    {3, "SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY \"hello\""},
+    {4, "SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY blah"},
+    {5, "SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY c,d,c+d"},
+    {6, "SELECT * FROM d5 EXCEPT SELECT * FROM d7 ORDER BY 1,2,b,a/b"}}) do
     local tn = val[1]
     local select = val[2]
-    local err_param = val[3]
     test:do_catchsql_test(
         "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, "ORDER BY term does not match any column in the result set"
+        })
 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..cf6bcc9 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, "ORDER BY 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, "ORDER BY 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..470a8f7 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, "ORDER BY 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, "ORDER BY 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, "ORDER BY 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..d2807bf 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, "GROUP BY 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, "GROUP BY 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..e451038 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, "ORDER BY 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, "ORDER BY 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, "ORDER BY term out of range - should be between 1 and 1"
         -- </select4-5.2h>
     })
 
diff --git a/test/sql-tap/tkt2822.test.lua b/test/sql-tap/tkt2822.test.lua
index 4212cbd..d125008 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, "ORDER BY 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, "ORDER BY 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, "ORDER BY 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, "ORDER BY 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, "ORDER BY 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, "ORDER BY 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, "ORDER BY 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, "ORDER BY 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, "ORDER BY 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, "ORDER BY 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, "ORDER BY 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, "ORDER BY 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, "ORDER BY 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, "ORDER BY 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, "ORDER BY 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..deb4cbb 100755
--- a/test/sql-tap/with1.test.lua
+++ b/test/sql-tap/with1.test.lua
@@ -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, "ORDER BY term does not match any column in the result set"
   -- </10.7.1>
 })
 
-- 
2.7.4

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

* [tarantool-patches] Re: [PATCH v3 1/9] sql: rework syntax errors
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 1/9] sql: rework syntax errors imeevma
@ 2019-03-04 17:47   ` n.pettik
  2019-03-05  8:31   ` Konstantin Osipov
  1 sibling, 0 replies; 30+ messages in thread
From: n.pettik @ 2019-03-04 17:47 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen

This one LGTM.

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

* [tarantool-patches] Re: [PATCH v3 1/9] sql: rework syntax errors
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 1/9] sql: rework syntax errors imeevma
  2019-03-04 17:47   ` [tarantool-patches] " n.pettik
@ 2019-03-05  8:31   ` Konstantin Osipov
  1 sibling, 0 replies; 30+ messages in thread
From: Konstantin Osipov @ 2019-03-05  8:31 UTC (permalink / raw)
  To: tarantool-patches; +Cc: korablev

* imeevma@tarantool.org <imeevma@tarantool.org> [19/03/03 23:25]:
> Hi! Thank you for review. My answer, diff between versions and new
> version below.

OK to push.

-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov

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

* [tarantool-patches] Re: [PATCH v3 2/9] sql: save SQL parser errors in diag_set()
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 2/9] sql: save SQL parser errors in diag_set() imeevma
@ 2019-03-05  8:40   ` Konstantin Osipov
  2019-03-05  9:06   ` n.pettik
  1 sibling, 0 replies; 30+ messages in thread
From: Konstantin Osipov @ 2019-03-05  8:40 UTC (permalink / raw)
  To: tarantool-patches; +Cc: korablev

* imeevma@tarantool.org <imeevma@tarantool.org> [19/03/03 23:25]:
> +	/*186 */_(ER_SQL_PARSER_GENERIC,	"%s") \

Simply ER_SQL_PARSER
>  
>  /*
>   * !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;

Shouldn't this become bool abort?


-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov

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

* [tarantool-patches] Re: [PATCH v3 3/9] sql: remove field nErr of struct Parse
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 3/9] sql: remove field nErr of struct Parse imeevma
@ 2019-03-05  8:41   ` Konstantin Osipov
  2019-03-05  9:06   ` n.pettik
  1 sibling, 0 replies; 30+ messages in thread
From: Konstantin Osipov @ 2019-03-05  8:41 UTC (permalink / raw)
  To: tarantool-patches; +Cc: korablev

* imeevma@tarantool.org <imeevma@tarantool.org> [19/03/03 23:25]:
> Hi! Thank you for review. My answers and new version below. There
> won't be diff between versions as I completely rewritten this
> patch to place it before removing of nErr and zErrMsg.

OK to push.


-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov

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

* [tarantool-patches] Re: [PATCH v3 4/9] sql: remove field rc of struct Parse
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 4/9] sql: remove field rc " imeevma
@ 2019-03-05  8:42   ` Konstantin Osipov
  2019-03-05  9:06   ` n.pettik
  1 sibling, 0 replies; 30+ messages in thread
From: Konstantin Osipov @ 2019-03-05  8:42 UTC (permalink / raw)
  To: tarantool-patches; +Cc: korablev

* imeevma@tarantool.org <imeevma@tarantool.org> [19/03/03 23:25]:
> 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.

OK to push.

-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov

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

* [tarantool-patches] Re: [PATCH v3 5/9] sql: remove field zErrMsg of struct Parse
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 5/9] sql: remove field zErrMsg " imeevma
@ 2019-03-05  8:43   ` Konstantin Osipov
  2019-03-05  9:06   ` n.pettik
  1 sibling, 0 replies; 30+ messages in thread
From: Konstantin Osipov @ 2019-03-05  8:43 UTC (permalink / raw)
  To: tarantool-patches; +Cc: korablev

* imeevma@tarantool.org <imeevma@tarantool.org> [19/03/03 23:25]:
> Hi! Thank you for review. My answers and new patch below. There
> won't be diff between versions as I completely rewritten this
> patch after placein it after removing of rc and nErr.

OK to push.


-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov

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

* [tarantool-patches] Re: [PATCH v3 6/9] sql: rework six syntax errors
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 6/9] sql: rework six syntax errors imeevma
@ 2019-03-05  8:45   ` Konstantin Osipov
  2019-03-05  9:07   ` n.pettik
  1 sibling, 0 replies; 30+ messages in thread
From: Konstantin Osipov @ 2019-03-05  8:45 UTC (permalink / raw)
  To: tarantool-patches; +Cc: korablev

* imeevma@tarantool.org <imeevma@tarantool.org> [19/03/03 23:25]:
> This patch rewords six more syntax errors.
> 
> Part of #3965
> ---
>  src/box/errcode.h             |  3 +++
>  src/box/sql/build.c           |  4 +--
>  src/box/sql/resolve.c         | 62 +++++++++++++++++++++----------------------
>  test/box/misc.result          |  3 +++
>  test/sql-tap/check.test.lua   |  2 +-
>  test/sql-tap/colname.test.lua |  2 +-
>  6 files changed, 40 insertions(+), 36 deletions(-)

Please avoid adding a verb into error parameter and use singular:

"Subqueries are" + "prohibited in ..." -> "A subquery" + "is
prohibited in ..."

Otherwise OK to push.


-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov

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

* [tarantool-patches] Re: [PATCH v3 7/9] sql: rework four semantic errors
  2019-03-02 13:08 ` [tarantool-patches] [PATCH v3 7/9] sql: rework four semantic errors imeevma
@ 2019-03-05  8:46   ` Konstantin Osipov
  2019-03-05  9:16   ` n.pettik
  1 sibling, 0 replies; 30+ messages in thread
From: Konstantin Osipov @ 2019-03-05  8:46 UTC (permalink / raw)
  To: tarantool-patches; +Cc: korablev

* imeevma@tarantool.org <imeevma@tarantool.org> [19/03/03 23:25]:
> This patch reworks four semantic errors.
> 
> Part of #3965

OK to push.


-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov

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

* [tarantool-patches] Re: [PATCH v3 8/9] sql: rework three errors of "unsupported" type
  2019-03-02 13:08 ` [tarantool-patches] [PATCH v3 8/9] sql: rework three errors of "unsupported" type imeevma
@ 2019-03-05  8:47   ` Konstantin Osipov
  2019-03-05  9:34   ` n.pettik
  1 sibling, 0 replies; 30+ messages in thread
From: Konstantin Osipov @ 2019-03-05  8:47 UTC (permalink / raw)
  To: tarantool-patches; +Cc: korablev

* imeevma@tarantool.org <imeevma@tarantool.org> [19/03/03 23:25]:
> Three errors of "unsupported" type were reworked in this patch.
> 
> Part of #3965

OK to push.


-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov

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

* [tarantool-patches] Re: [PATCH v3 9/9] sql: remove sqlErrorMsg()
  2019-03-02 13:08 ` [tarantool-patches] [PATCH v3 9/9] sql: remove sqlErrorMsg() imeevma
@ 2019-03-05  8:48   ` Konstantin Osipov
  2019-03-05 12:16   ` n.pettik
  1 sibling, 0 replies; 30+ messages in thread
From: Konstantin Osipov @ 2019-03-05  8:48 UTC (permalink / raw)
  To: tarantool-patches; +Cc: korablev

* imeevma@tarantool.org <imeevma@tarantool.org> [19/03/03 23:25]:
> This patch replaces sqlErrorMsg() by tt_sprintf() + diag_set().

OK to push, but I thought we agreed to eliminate these few
remaining generic errors. Which errors do you need guidance for? 

 

-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov

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

* [tarantool-patches] Re: [PATCH v3 2/9] sql: save SQL parser errors in diag_set()
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 2/9] sql: save SQL parser errors in diag_set() imeevma
  2019-03-05  8:40   ` [tarantool-patches] " Konstantin Osipov
@ 2019-03-05  9:06   ` n.pettik
  1 sibling, 0 replies; 30+ messages in thread
From: n.pettik @ 2019-03-05  9:06 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen

Not ‘save’ but set via diag().

LGTM. 

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

* [tarantool-patches] Re: [PATCH v3 3/9] sql: remove field nErr of struct Parse
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 3/9] sql: remove field nErr of struct Parse imeevma
  2019-03-05  8:41   ` [tarantool-patches] " Konstantin Osipov
@ 2019-03-05  9:06   ` n.pettik
  1 sibling, 0 replies; 30+ messages in thread
From: n.pettik @ 2019-03-05  9:06 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen


> 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).

> 
>    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.

> @@ -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.

> +		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.

> @@ -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.

> 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.

> @@ -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.

> @@ -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.

> 	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?

>  value_from_function_out:
> 	if (rc != SQL_OK) {

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

* [tarantool-patches] Re: [PATCH v3 4/9] sql: remove field rc of struct Parse
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 4/9] sql: remove field rc " imeevma
  2019-03-05  8:42   ` [tarantool-patches] " Konstantin Osipov
@ 2019-03-05  9:06   ` n.pettik
  1 sibling, 0 replies; 30+ messages in thread
From: n.pettik @ 2019-03-05  9:06 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen

Nit: remove from.

> 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

> 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.

> 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.

> +	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?

> 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?

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

* [tarantool-patches] Re: [PATCH v3 5/9] sql: remove field zErrMsg of struct Parse
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 5/9] sql: remove field zErrMsg " imeevma
  2019-03-05  8:43   ` [tarantool-patches] " Konstantin Osipov
@ 2019-03-05  9:06   ` n.pettik
  1 sibling, 0 replies; 30+ messages in thread
From: n.pettik @ 2019-03-05  9:06 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen


>>> @@ -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.

> 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.

> 
>  /* 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?

>  */
> 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 */

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

* [tarantool-patches] Re: [PATCH v3 6/9] sql: rework six syntax errors
  2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 6/9] sql: rework six syntax errors imeevma
  2019-03-05  8:45   ` [tarantool-patches] " Konstantin Osipov
@ 2019-03-05  9:07   ` n.pettik
  1 sibling, 0 replies; 30+ messages in thread
From: n.pettik @ 2019-03-05  9:07 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen



> On 2 Mar 2019, at 16:07, imeevma@tarantool.org wrote:
> 
> This patch rewords six more syntax errors.

Are these errors considered to be syntax?
AFAIR we’ve classified them as semantic errors.
And I counted only three new error (and two changed tests)
codes in errcode.h

> Part of #3965
> ---
> src/box/errcode.h             |  3 +++
> src/box/sql/build.c           |  4 +--
> src/box/sql/resolve.c         | 62 +++++++++++++++++++++----------------------
> test/box/misc.result          |  3 +++
> test/sql-tap/check.test.lua   |  2 +-
> test/sql-tap/colname.test.lua |  2 +-
> 6 files changed, 40 insertions(+), 36 deletions(-)
> 
> diff --git a/src/box/errcode.h b/src/box/errcode.h
> index d234d26..057a6d3 100644
> --- a/src/box/errcode.h
> +++ b/src/box/errcode.h
> @@ -239,6 +239,9 @@ 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_INDEX_DEF,			"%s prohibited in an index definition") \
> +	/*188 */_(ER_CHECK_CONSTRAINT_DEF,	"%s prohibited in prohibited in a CHECK constraint definition") \

Prohibited in prohibited? I guess this is typo.

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

Make errcodes names be more specific:
ER_INDEX_DEF -> ER_INDEX_EXPR_UNSUPPORTED etc.

> diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
> index 3215db3..02eca37 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)
> }
> 
> -/*
>  * 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;
> +			}

Is this code reachable? I don’t see any tests connected with it.
Please, figure out whether these errors can be raised or not,
and if they can be raised, add corresponding tests.

> test_run:cmd("setopt delimiter ''");
> ---
> diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
> index 0d8bf15..e1d2d48 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 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”}

Ok, I see three new error codes and two changed tests.

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

* [tarantool-patches] Re: [PATCH v3 7/9] sql: rework four semantic errors
  2019-03-02 13:08 ` [tarantool-patches] [PATCH v3 7/9] sql: rework four semantic errors imeevma
  2019-03-05  8:46   ` [tarantool-patches] " Konstantin Osipov
@ 2019-03-05  9:16   ` n.pettik
  1 sibling, 0 replies; 30+ messages in thread
From: n.pettik @ 2019-03-05  9:16 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen


> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index 7ea344b..ffcb455 100644
> --- a/src/box/sql/expr.c
> +++ b/src/box/sql/expr.c
> @@ -730,9 +730,9 @@ 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);
> +		diag_set(ClientError, ER_SQL_PARSER_LIMIT, "Number of nodes "\
> +			 "in expression tree", 0, "", nHeight, mxHeight);
> +		pParse->is_aborted = true;
> 		rc = SQL_ERROR;
> 	}
> 	return rc;
> @@ -1174,9 +1174,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);

You still can get here if i < 1. Please add an assertion here.

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

* [tarantool-patches] Re: [PATCH v3 8/9] sql: rework three errors of "unsupported" type
  2019-03-02 13:08 ` [tarantool-patches] [PATCH v3 8/9] sql: rework three errors of "unsupported" type imeevma
  2019-03-05  8:47   ` [tarantool-patches] " Konstantin Osipov
@ 2019-03-05  9:34   ` n.pettik
  2019-03-05  9:43     ` Konstantin Osipov
  1 sibling, 1 reply; 30+ messages in thread
From: n.pettik @ 2019-03-05  9:34 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen


> diff --git a/src/box/errcode.h b/src/box/errcode.h
> index 06f7a63..f2ea27a 100644
> --- a/src/box/errcode.h
> +++ b/src/box/errcode.h
> @@ -246,6 +246,7 @@ struct errcode_record {
> 	/*191 */_(ER_HEX_LITERAL_MAX,		"Hex literal %s%s length %d exceeds the supported limit (%d)") \
> 	/*192 */_(ER_INT_LITERAL_MAX,		"Integer literal %s%s exceeds the supported range %lld - %lld") \
> 	/*193 */_(ER_SQL_PARSER_LIMIT,		"%s%.*s %d exceeds the limit (%d)") \
> +	/*194 */_(ER_SQL_ANALYZE_ARGUMENT,	"ANALYZE statement argument %s is not a base table") \

Why does this error deserve to be so special?
To be honest, I would remove it at all and simply
do nothing in case of view is analysed. Or make it
raise not only for views, but in several other cases.
For instance, what if we analyse space without
format or indexes? Spoiler: it leads to assertion fault

Assertion failed: (space->index_count != 0), function vdbe_emit_analyze_space, file /Users/n.pettik/tarantool/src/box/sql/analyze.c, line 799.
Abort trap: 6

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

* [tarantool-patches] Re: [PATCH v3 8/9] sql: rework three errors of "unsupported" type
  2019-03-05  9:34   ` n.pettik
@ 2019-03-05  9:43     ` Konstantin Osipov
  0 siblings, 0 replies; 30+ messages in thread
From: Konstantin Osipov @ 2019-03-05  9:43 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen

* n.pettik <korablev@tarantool.org> [19/03/05 12:37]:

> > +	/*194 */_(ER_SQL_ANALYZE_ARGUMENT,	"ANALYZE statement argument %s is not a base table") \
> 
> Why does this error deserve to be so special?

I don't know. Should analyze work for a sysview?

> To be honest, I would remove it at all and simply
> do nothing in case of view is analysed.

This would seep DBA mistakes under the carpet.

> Or make it
> raise not only for views, but in several other cases.
> For instance, what if we analyse space without
> format or indexes? Spoiler: it leads to assertion fault
> 
> Assertion failed: (space->index_count != 0), function vdbe_emit_analyze_space, file /Users/n.pettik/tarantool/src/box/sql/analyze.c, line 799.
> Abort trap: 6

This should work.

Generally, I think it's bikeshed and not worth arguing about. Do
you see any practical consequence from following my preference?
> 

-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov

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

* [tarantool-patches] Re: [PATCH v3 9/9] sql: remove sqlErrorMsg()
  2019-03-02 13:08 ` [tarantool-patches] [PATCH v3 9/9] sql: remove sqlErrorMsg() imeevma
  2019-03-05  8:48   ` [tarantool-patches] " Konstantin Osipov
@ 2019-03-05 12:16   ` n.pettik
  2019-03-05 15:44     ` Konstantin Osipov
  1 sibling, 1 reply; 30+ messages in thread
From: n.pettik @ 2019-03-05 12:16 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen


> @@ -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?

> +		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.

> +		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

> +			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

> +		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

> 			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?

> +		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");

> +    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.

> @@ -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.

> +							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.

> +				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.

> +		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.

> +			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.

> @@ -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.

> 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.

> -/*
>  * 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.

> +		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.

> 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.

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

* [tarantool-patches] Re: [PATCH v3 9/9] sql: remove sqlErrorMsg()
  2019-03-05 12:16   ` n.pettik
@ 2019-03-05 15:44     ` Konstantin Osipov
  0 siblings, 0 replies; 30+ messages in thread
From: Konstantin Osipov @ 2019-03-05 15:44 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen

* n.pettik <korablev@tarantool.org> [19/03/05 18:26]:
> > @@ -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.

It's not the formatting, it's that the problem calls for splitting
this function into a few smaller ones.


-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov

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

end of thread, other threads:[~2019-03-05 15:44 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-02 13:07 [tarantool-patches] [PATCH v3 0/9] sql: use diag_set() for errors in SQL imeevma
2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 1/9] sql: rework syntax errors imeevma
2019-03-04 17:47   ` [tarantool-patches] " n.pettik
2019-03-05  8:31   ` Konstantin Osipov
2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 2/9] sql: save SQL parser errors in diag_set() imeevma
2019-03-05  8:40   ` [tarantool-patches] " Konstantin Osipov
2019-03-05  9:06   ` n.pettik
2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 3/9] sql: remove field nErr of struct Parse imeevma
2019-03-05  8:41   ` [tarantool-patches] " Konstantin Osipov
2019-03-05  9:06   ` n.pettik
2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 4/9] sql: remove field rc " imeevma
2019-03-05  8:42   ` [tarantool-patches] " Konstantin Osipov
2019-03-05  9:06   ` n.pettik
2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 5/9] sql: remove field zErrMsg " imeevma
2019-03-05  8:43   ` [tarantool-patches] " Konstantin Osipov
2019-03-05  9:06   ` n.pettik
2019-03-02 13:07 ` [tarantool-patches] [PATCH v3 6/9] sql: rework six syntax errors imeevma
2019-03-05  8:45   ` [tarantool-patches] " Konstantin Osipov
2019-03-05  9:07   ` n.pettik
2019-03-02 13:08 ` [tarantool-patches] [PATCH v3 7/9] sql: rework four semantic errors imeevma
2019-03-05  8:46   ` [tarantool-patches] " Konstantin Osipov
2019-03-05  9:16   ` n.pettik
2019-03-02 13:08 ` [tarantool-patches] [PATCH v3 8/9] sql: rework three errors of "unsupported" type imeevma
2019-03-05  8:47   ` [tarantool-patches] " Konstantin Osipov
2019-03-05  9:34   ` n.pettik
2019-03-05  9:43     ` Konstantin Osipov
2019-03-02 13:08 ` [tarantool-patches] [PATCH v3 9/9] sql: remove sqlErrorMsg() imeevma
2019-03-05  8:48   ` [tarantool-patches] " Konstantin Osipov
2019-03-05 12:16   ` n.pettik
2019-03-05 15:44     ` Konstantin Osipov

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