From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id B54B520F26 for ; Mon, 16 Jul 2018 08:22:55 -0400 (EDT) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 6Swvm-RC57DL for ; Mon, 16 Jul 2018 08:22:55 -0400 (EDT) Received: from mail-lf0-f49.google.com (mail-lf0-f49.google.com [209.85.215.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id ED06020EDB for ; Mon, 16 Jul 2018 08:22:54 -0400 (EDT) Received: by mail-lf0-f49.google.com with SMTP id a134-v6so32085652lfe.6 for ; Mon, 16 Jul 2018 05:22:54 -0700 (PDT) MIME-Version: 1.0 References: <1530787337-18302-1-git-send-email-hollow653@gmail.com> In-Reply-To: From: Nikita Tatunov Date: Mon, 16 Jul 2018 15:22:41 +0300 Message-ID: Subject: [tarantool-patches] Re: [PATCH] sql: Remove 'BEGIN TRANSACTION' Content-Type: multipart/alternative; boundary="0000000000007e8bc305711ce356" Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-help: List-unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-subscribe: List-owner: List-post: List-archive: To: korablev@tarantool.org Cc: tarantool-patches@freelists.org --0000000000007e8bc305711ce356 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable As Alexander asked I've added table content checking in tests, fixed the causes of failing tests in Travis & fixed Nikita's remarks. Diff for the newer version is in the end. =D0=BF=D1=82, 13 =D0=B8=D1=8E=D0=BB. 2018 =D0=B3. =D0=B2 5:15, n.pettik : > Nitpicking ffter module prefix (i.e. sql:) don=E2=80=99t use upper case f= or first > word > > Fixed. > > Patch is aimed on making our sql closer to ANSI sql. > > Nitpicking: write SQL in upper case - it is an abbreviation. > > Fixed. > > > > With the patch applied only following commands can be used: > > - "START TRANSACTION" to begin transaction. > > - "COMMIT" to end transaction. > > - "ROLLBACK" to rollback transaction without savepoints. > > - "ROLLBACK TO .." to rollback transaction to savepoint. > > > > Closes #2164 > > --- > > > > diff --git a/extra/mkkeywordhash.c b/extra/mkkeywordhash.c > > index 990c419..1ec1538 100644 > > --- a/extra/mkkeywordhash.c > > +++ b/extra/mkkeywordhash.c > > @@ -120,7 +120,7 @@ static Keyword aKeywordTable[] =3D { > > { "ASC", "TK_ASC", ALWAYS, true > }, > > { "AUTOINCREMENT", "TK_AUTOINCR", AUTOINCR, false > }, > > { "BEFORE", "TK_BEFORE", TRIGGER, false > }, > > - { "BEGIN", "TK_BEGIN", ALWAYS, true > }, > > + { "BEGIN", "TK_BEGIN", TRIGGER, true > }, > > { "BETWEEN", "TK_BETWEEN", ALWAYS, true > }, > > { "BY", "TK_BY", ALWAYS, true > }, > > { "CASCADE", "TK_CASCADE", FKEY, false > }, > > @@ -210,6 +210,7 @@ static Keyword aKeywordTable[] =3D { > > { "SAVEPOINT", "TK_SAVEPOINT", ALWAYS, true > }, > > { "SELECT", "TK_SELECT", ALWAYS, true > }, > > { "SET", "TK_SET", ALWAYS, true > }, > > + { "START", "TK_START", ALWAYS, true > }, > > { "TABLE", "TK_TABLE", ALWAYS, true > }, > > { "THEN", "TK_THEN", ALWAYS, true > }, > > { "TO", "TK_TO", ALWAYS, true > }, > > @@ -274,7 +275,6 @@ static Keyword aKeywordTable[] =3D { > > { "SIGNAL", "TK_STANDARD", RESERVED, true > }, > > { "SMALLINT", "TK_ID", RESERVED, true > }, > > { "SPECIFIC", "TK_STANDARD", RESERVED, true > }, > > - { "START", "TK_STANDARD", RESERVED, true > }, > > { "SYSTEM", "TK_STANDARD", RESERVED, true > }, > > { "SQL", "TK_STANDARD", RESERVED, true > }, > > { "USER", "TK_STANDARD", RESERVED, true > }, > > diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c > > index 3183e3d..42ab600 100644 > > --- a/src/box/sql/expr.c > > +++ b/src/box/sql/expr.c > > @@ -4835,11 +4835,13 @@ sqlite3ExprIfFalse(Parse * pParse, Expr * pExpr= , > int dest, int jumpIfNull) > > * Assert()s verify that the computation is correct. > > */ > > > > - op =3D ((pExpr->op + (TK_ISNULL & 1)) ^ 1) - (TK_ISNULL & 1); > > + if (pExpr->op >=3D TK_NE && pExpr->op <=3D TK_GE) > > + op =3D ((pExpr->op + (TK_NE & 1)) ^ 1) - (TK_NE & 1); > > + if (pExpr->op =3D=3D TK_ISNULL || pExpr->op =3D=3D TK_NOTNULL) > > + op =3D ((pExpr->op + (TK_ISNULL & 1)) ^ 1) - (TK_ISNULL &= 1); > > Please, leave comment how this code work. > It isn't even close to be obvious, really. > Fixed. > > > > > /* > > * Verify correct alignment of TK_ and OP_ constants. > > - * Tokens TK_ISNULL and TK_NE shoud have the same parity. > > */ > > assert(pExpr->op !=3D TK_NE || op =3D=3D OP_Eq); > > assert(pExpr->op !=3D TK_EQ || op =3D=3D OP_Ne); > > diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y > > index b2940b7..e956dfc 100644 > > --- a/src/box/sql/parse.y > > +++ b/src/box/sql/parse.y > > @@ -147,13 +147,11 @@ cmdx ::=3D cmd. > > ///////////////////// Begin and end transactions. > //////////////////////////// > > // > > > > -cmd ::=3D BEGIN trans_opt. {sql_transaction_begin(pParse);} > > +cmd ::=3D START TRANSACTION trans_opt. {sql_transaction_begin(pParse)= ;} > > trans_opt ::=3D . > > -trans_opt ::=3D TRANSACTION. > > -trans_opt ::=3D TRANSACTION nm. > > -cmd ::=3D COMMIT trans_opt. {sql_transaction_commit(pParse);} > > -cmd ::=3D END trans_opt. {sql_transaction_commit(pParse);} > > -cmd ::=3D ROLLBACK trans_opt. {sql_transaction_rollback(pParse);} > > +trans_opt ::=3D nm. > > What is the point of named transactions, if rollback and commit > don=E2=80=99t use name? I mean, now we ofc can=E2=80=99t use its name, bu= t > what does ANSI say? > Fixed it. Ansi only asks for after 'START TRANSACTION'. > > > +cmd ::=3D COMMIT. {sql_transaction_commit(pParse);} > > +cmd ::=3D ROLLBACK. {sql_transaction_rollback(pParse);} > > > > savepoint_opt ::=3D SAVEPOINT. > > savepoint_opt ::=3D . > > @@ -163,7 +161,7 @@ cmd ::=3D SAVEPOINT nm(X). { > > cmd ::=3D RELEASE savepoint_opt nm(X). { > > sqlite3Savepoint(pParse, SAVEPOINT_RELEASE, &X); > > } > > -cmd ::=3D ROLLBACK trans_opt TO savepoint_opt nm(X). { > > +cmd ::=3D ROLLBACK TO savepoint_opt nm(X). { > > sqlite3Savepoint(pParse, SAVEPOINT_ROLLBACK, &X); > > } > > > > diff --git a/test/sql-tap/analyze3.test.lua > b/test/sql-tap/analyze3.test.lua > > index 26f8793..5079962 100755 > > --- a/test/sql-tap/analyze3.test.lua > > +++ b/test/sql-tap/analyze3.test.lua > > @@ -340,7 +340,7 @@ test:do_execsql_test( > > "analyze3-1.3.1", > > [[ > > CREATE TABLE t3(id INTEGER PRIMARY KEY, y TEXT, x INTEGER); > > - BEGIN; > > + START TRANSACTION; > > INSERT INTO t3 SELECT id, y, x FROM t1; > > COMMIT; > > CREATE INDEX i3 ON t3(x); > > @@ -465,7 +465,7 @@ test:do_test( > > -- function() > > -- test:execsql([[ > > -- PRAGMA case_sensitive_like=3Doff; > > --- BEGIN; > > +-- START TRANSACTION; > > I guess, you can remove it at all. It makes no sense to add dead code. > > > --- a/test/sql-tap/in1.test.lua > > +++ b/test/sql-tap/in1.test.lua > > @@ -26,7 +26,7 @@ test:do_test( > > function() > > test:execsql [[ > > CREATE TABLE t1(a PRIMARY KEY, b); > > - BEGIN; > > + START TRANSACTION; > > ]] > > -- for _ in X(0, "X!for", [=3D[["set i 1","$i<=3D10","incr i"]]= =3D]) do > > local j =3D 1 > > @@ -1073,7 +1073,7 @@ test:do_execsql_test( > > -- MUST_WORK_TEST > > -- do_test in-13.14 { > > -- execsql { > > --- BEGIN TRANSACTION; > > +-- START TRANSACTION; > > The same is here and in other places. > > Fixed. diff --git a/extra/lempar.c b/extra/lempar.c index 00fd79c..d043e39 100644 --- a/extra/lempar.c +++ b/extra/lempar.c @@ -336,8 +336,8 @@ void *ParseAlloc(void *(*mallocProc)(YYMALLOCARGTYPE)){ if( pParser ){ #ifdef YYTRACKMAXSTACKDEPTH pParser->yyhwm =3D 0; - pParser->is_fallback_failed =3D false; #endif + pParser->is_fallback_failed =3D false; #if YYSTACKDEPTH<=3D0 pParser->yytos =3D NULL; pParser->yystack =3D NULL; diff --git a/extra/mkkeywordhash.c b/extra/mkkeywordhash.c index 990c419..1ec1538 100644 --- a/extra/mkkeywordhash.c +++ b/extra/mkkeywordhash.c @@ -120,7 +120,7 @@ static Keyword aKeywordTable[] =3D { { "ASC", "TK_ASC", ALWAYS, true }, { "AUTOINCREMENT", "TK_AUTOINCR", AUTOINCR, false }, { "BEFORE", "TK_BEFORE", TRIGGER, false }, - { "BEGIN", "TK_BEGIN", ALWAYS, true }, + { "BEGIN", "TK_BEGIN", TRIGGER, true }, { "BETWEEN", "TK_BETWEEN", ALWAYS, true }, { "BY", "TK_BY", ALWAYS, true }, { "CASCADE", "TK_CASCADE", FKEY, false }, @@ -210,6 +210,7 @@ static Keyword aKeywordTable[] =3D { { "SAVEPOINT", "TK_SAVEPOINT", ALWAYS, true }, { "SELECT", "TK_SELECT", ALWAYS, true }, { "SET", "TK_SET", ALWAYS, true }, + { "START", "TK_START", ALWAYS, true }, { "TABLE", "TK_TABLE", ALWAYS, true }, { "THEN", "TK_THEN", ALWAYS, true }, { "TO", "TK_TO", ALWAYS, true }, @@ -274,7 +275,6 @@ static Keyword aKeywordTable[] =3D { { "SIGNAL", "TK_STANDARD", RESERVED, true }, { "SMALLINT", "TK_ID", RESERVED, true }, { "SPECIFIC", "TK_STANDARD", RESERVED, true }, - { "START", "TK_STANDARD", RESERVED, true }, { "SYSTEM", "TK_STANDARD", RESERVED, true }, { "SQL", "TK_STANDARD", RESERVED, true }, { "USER", "TK_STANDARD", RESERVED, true }, diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c index b1650cf..b26728d 100644 --- a/src/box/sql/expr.c +++ b/src/box/sql/expr.c @@ -4826,23 +4826,32 @@ sqlite3ExprIfFalse(Parse * pParse, Expr * pExpr, int dest, int jumpIfNull) * TK_EQ OP_Ne * TK_GT OP_Le * TK_LE OP_Gt - * TK_GE OP_Lt * TK_LT OP_Ge + * TK_GE OP_Lt * ... ... * TK_ISNULL OP_NotNull * TK_NOTNULL OP_IsNull * - * For other values of pExpr->op, op is undefined and unused. - * The value of TK_ and OP_ constants are arranged such that we - * can compute the mapping above using the following expression. + * For other values of pExpr->op, op is undefined + * and unused. The value of TK_ and OP_ constants + * are arranged such that we can compute the mapping + * above using the following expression. The idea + * is that both for OP_'s and TK_'s the first elements + * in the given mapping ranges of codes and tokens are + * 'Not equal' and 'Is null'. Moreover the 'excluding' + * ones (like 'Greater than' and 'Lower than or Equal') + * are paired and follow one each other, hence have n + * and n + 1 numbers. * Assert()s verify that the computation is correct. */ - op =3D ((pExpr->op + (TK_ISNULL & 1)) ^ 1) - (TK_ISNULL & 1); + if (pExpr->op >=3D TK_NE && pExpr->op <=3D TK_GE) + op =3D ((pExpr->op + (TK_NE & 1)) ^ 1) - (TK_NE & 1); + if (pExpr->op =3D=3D TK_ISNULL || pExpr->op =3D=3D TK_NOTNULL) + op =3D ((pExpr->op + (TK_ISNULL & 1)) ^ 1) - (TK_ISNULL & 1); /* * Verify correct alignment of TK_ and OP_ constants. - * Tokens TK_ISNULL and TK_NE shoud have the same parity. */ assert(pExpr->op !=3D TK_NE || op =3D=3D OP_Eq); assert(pExpr->op !=3D TK_EQ || op =3D=3D OP_Ne); diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y index ac935fd..b4f11e5 100644 --- a/src/box/sql/parse.y +++ b/src/box/sql/parse.y @@ -147,13 +147,9 @@ cmdx ::=3D cmd. ///////////////////// Begin and end transactions. //////////////////////////// // -cmd ::=3D BEGIN trans_opt. {sql_transaction_begin(pParse);} -trans_opt ::=3D . -trans_opt ::=3D TRANSACTION. -trans_opt ::=3D TRANSACTION nm. -cmd ::=3D COMMIT trans_opt. {sql_transaction_commit(pParse);} -cmd ::=3D END trans_opt. {sql_transaction_commit(pParse);} -cmd ::=3D ROLLBACK trans_opt. {sql_transaction_rollback(pParse);} +cmd ::=3D START TRANSACTION. {sql_transaction_begin(pParse);} +cmd ::=3D COMMIT. {sql_transaction_commit(pParse);} +cmd ::=3D ROLLBACK. {sql_transaction_rollback(pParse);} savepoint_opt ::=3D SAVEPOINT. savepoint_opt ::=3D . @@ -163,7 +159,7 @@ cmd ::=3D SAVEPOINT nm(X). { cmd ::=3D RELEASE savepoint_opt nm(X). { sqlite3Savepoint(pParse, SAVEPOINT_RELEASE, &X); } -cmd ::=3D ROLLBACK trans_opt TO savepoint_opt nm(X). { +cmd ::=3D ROLLBACK TO savepoint_opt nm(X). { sqlite3Savepoint(pParse, SAVEPOINT_ROLLBACK, &X); } diff --git a/test/sql-tap/start-transaction.test.lua b/test/sql-tap/start-transaction.test.lua new file mode 100755 index 0000000..eece4cd --- /dev/null +++ b/test/sql-tap/start-transaction.test.lua @@ -0,0 +1,266 @@ +#!/usr/bin/env tarantool +test =3D require("sqltester") +test:plan(21) + +test:do_catchsql_test( + "start-transaction-1.0", + [[ + CREATE TABLE IF NOT EXISTS t(id int PRIMARY KEY); + DELETE FROM t; + BEGIN; + INSERT INTO t VALUES (1); + INSERT INTO t VALUES (2); + COMMIT; + ]], { + -- + 1, "near \"BEGIN\": syntax error" + -- + }) + +test:do_execsql_test( + "start-transaction-1.1", + [[ + SELECT * FROM t; + ]], { + -- + + -- + }) + +test:do_catchsql_test( + "start-transaction-1.2", + [[ + CREATE TABLE IF NOT EXISTS t(id int primary key); + delete from t; + BEGIN TRANSACTION; + INSERT INTO t VALUES (1); + INSERT INTO t VALUES (2); + COMMIT; + ]], { + -- + 1, "near \"BEGIN\": syntax error" + -- + }) + +test:do_execsql_test( + "start-transaction-1.3", + [[ + SELECT * FROM t; + ]], { + -- + + -- + }) + +test:do_catchsql_test( + "start-transaction-1.4", + [[ + DELETE FROM t; + START TRANSACTION; + INSERT INTO t VALUES (1); + INSERT INTO t VALUES (2); + COMMIT; + ]], { + -- + 0 + -- + }) + +test:do_execsql_test( + "start-transaction-1.5", + [[ + SELECT * FROM t; + ]], { + -- + 1, 2 + -- + }) + +test:do_catchsql_test( + "start-transaction-1.6", + [[ + DELETE FROM t; + START TRANSACTION; + INSERT INTO t VALUES (1); + INSERT INTO t VALUES (2); + COMMIT TRANSACTION; + ]], { + -- + 1, "keyword \"TRANSACTION\" is reserved" + -- + }) + +test:do_execsql_test( + "start-transaction-1.7", + [[ + COMMIT; + SELECT * FROM t; + ]], { + -- + 1, 2 + -- + }) + +test:do_catchsql_test( + "start-transaction-1.8", + [[ + DELETE FROM t; + START TRANSACTION; + INSERT INTO t VALUES (1); + INSERT INTO t VALUES (2); + END; + ]], { + -- + 1, "keyword \"END\" is reserved" + -- + }) + +test:do_execsql_test( + "start-transaction-1.9", + [[ + COMMIT; + SELECT * FROM t; + ]], { + -- + 1, 2 + -- + }) + +test:do_catchsql_test( + "start-transaction-1.10", + [[ + DELETE FROM t; + START TRANSACTION; + INSERT INTO t VALUES (1); + INSERT INTO t VALUES (2); + END TRANSACTION; + ]], { + -- + 1, "keyword \"END\" is reserved" + -- + }) + +test:do_execsql_test( + "start-transaction-1.11", + [[ + COMMIT; + SELECT * FROM t; + ]], { + -- + 1, 2 + -- + }) + +test:do_catchsql_test( + "start-transaction-1.12", + [[ + DELETE FROM t; + START TRANSACTION; + INSERT INTO t VALUES (1); + INSERT INTO t VALUES (2); + ROLLBACK; + ]], { + -- + 0 + -- + }) + +test:do_execsql_test( + "start-transaction-1.13", + [[ + SELECT * FROM t; + ]], { + -- + + -- + }) + +test:do_catchsql_test( + "start-transaction-1.14", + [[ + START TRANSACTION; + INSERT INTO t VALUES (1); + INSERT INTO t VALUES (2); + ROLLBACK TRANSACTION; + COMMIT; + ]], { + -- + 1, "keyword \"TRANSACTION\" is reserved" + -- + }) + +test:do_execsql_test( + "start-transaction-1.15", + [[ + COMMIT; + SELECT * FROM t; + ]], { + -- + 1, 2 + -- + }) + +test:do_catchsql_test( + "start-transaction-1.16", + [[ + DELETE FROM t; + START TRANSACTION; + INSERT INTO t VALUES (1); + SAVEPOINT s1; + INSERT INTO t VALUES (2); + ROLLBACK TO s1; + COMMIT; + ]], { + -- + 0 + -- + }) + +test:do_execsql_test( + "start-transaction-1.17", + [[ + SELECT * FROM t; + ]], { + -- + 1 + -- + }) + +test:do_catchsql_test( + "start-transaction-1.18", + [[ + DELETE FROM t; + START TRANSACTION; + INSERT INTO t VALUES (1); + SAVEPOINT s1; + INSERT INTO t VALUES (2); + ROLLBACK TRANSACTION TO s1; + COMMIT; + ]], { + -- + 1, "keyword \"TRANSACTION\" is reserved" + -- + }) + +test:do_execsql_test( + "start-transaction-1.19", + [[ + COMMIT; + SELECT * FROM t; + ]], { + -- + 1, 2 + -- + }) + +test:do_execsql_test( + "start-transaction-1.20", + [[ + drop table t; + ]], { + -- + + -- + }) + +test:finish_test() --0000000000007e8bc305711ce356 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
As Alexander asked I've added table content checking i= n tests, fixed the causes of failing tests in Travis & fixed Nikita'= ;s remarks. Diff for the newer version is in the end.

=D0=BF=D1=82, 13 =D0=B8=D1=8E=D0=BB. 2018 =D0= =B3. =D0=B2 5:15, n.pettik <korablev@tarantool.org>:
Nitpicking ffter module prefix (i.e. sql:)= don=E2=80=99t use upper case for first word

=C2=A0
Fixed.
=C2=A0
> Patch is aimed on making our sql closer to ANSI sql.

Nitpicking: write SQL in upper case - it is an abbreviation.


Fixed.
=C2=A0
>
> With the patch applied only following commands can be used:
> - "START TRANSACTION" to begin transaction.
> - "COMMIT" to end transaction.
> - "ROLLBACK" to rollback transaction without savepoints.
> - "ROLLBACK TO .." to rollback transaction to savepoint.
>
> Closes #2164
> ---
>
> diff --git a/extra/mkkeywordhash.c b/extra/mkkeywordhash.c
> index 990c419..1ec1538 100644
> --- a/extra/mkkeywordhash.c
> +++ b/extra/mkkeywordhash.c
> @@ -120,7 +120,7 @@ static Keyword aKeywordTable[] =3D {
>=C2=A0 =C2=A0{ "ASC",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "TK_ASC",=C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0ALWAYS,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
>=C2=A0 =C2=A0{ "AUTOINCREMENT",=C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 "TK_AUTOINCR",=C2=A0 =C2=A0 AUTOINCR,=C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0false },
>=C2=A0 =C2=A0{ "BEFORE",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0"TK_BEFORE",=C2=A0 =C2=A0 =C2=A0 TRIGG= ER,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 false },
> -=C2=A0 { "BEGIN",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 "TK_BEGIN",=C2=A0 =C2=A0 =C2=A0 =C2=A0ALWAYS= ,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
> +=C2=A0 { "BEGIN",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 "TK_BEGIN",=C2=A0 =C2=A0 =C2=A0 =C2=A0TRIGGE= R,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 true=C2=A0 },
>=C2=A0 =C2=A0{ "BETWEEN",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 "TK_BETWEEN",=C2=A0 =C2=A0 =C2=A0ALWAYS,=C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
>=C2=A0 =C2=A0{ "BY",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"TK_BY",=C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 ALWAYS,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },=
>=C2=A0 =C2=A0{ "CASCADE",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 "TK_CASCADE",=C2=A0 =C2=A0 =C2=A0FKEY,=C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0false },
> @@ -210,6 +210,7 @@ static Keyword aKeywordTable[] =3D {
>=C2=A0 =C2=A0{ "SAVEPOINT",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 "TK_SAVEPOINT",=C2=A0 =C2=A0ALWAYS,=C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
>=C2=A0 =C2=A0{ "SELECT",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0"TK_SELECT",=C2=A0 =C2=A0 =C2=A0 ALWAY= S,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
>=C2=A0 =C2=A0{ "SET",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "TK_SET",=C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0ALWAYS,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
> +=C2=A0 { "START",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 "TK_START",=C2=A0 =C2=A0 =C2=A0 =C2=A0ALWAYS= ,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
>=C2=A0 =C2=A0{ "TABLE",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 "TK_TABLE",=C2=A0 =C2=A0 =C2=A0 =C2=A0AL= WAYS,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
>=C2=A0 =C2=A0{ "THEN",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"TK_THEN",=C2=A0 =C2=A0 =C2=A0 =C2= =A0 ALWAYS,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
>=C2=A0 =C2=A0{ "TO",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"TK_TO",=C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 ALWAYS,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },=
> @@ -274,7 +275,6 @@ static Keyword aKeywordTable[] =3D {
>=C2=A0 =C2=A0{ "SIGNAL",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0"TK_STANDARD",=C2=A0 =C2=A0 RESERVED,= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
>=C2=A0 =C2=A0{ "SMALLINT",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0"TK_ID",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 RE= SERVED,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
>=C2=A0 =C2=A0{ "SPECIFIC",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0"TK_STANDARD",=C2=A0 =C2=A0 RESERVED,=C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
> -=C2=A0 { "START",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 "TK_STANDARD",=C2=A0 =C2=A0 RESERVED,=C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
>=C2=A0 =C2=A0{ "SYSTEM",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0"TK_STANDARD",=C2=A0 =C2=A0 RESERVED,= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
>=C2=A0 =C2=A0{ "SQL",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "TK_STANDARD",=C2=A0 =C2=A0 RESER= VED,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
>=C2=A0 =C2=A0{ "USER",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"TK_STANDARD",=C2=A0 =C2=A0 RESERV= ED,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index 3183e3d..42ab600 100644
> --- a/src/box/sql/expr.c
> +++ b/src/box/sql/expr.c
> @@ -4835,11 +4835,13 @@ sqlite3ExprIfFalse(Parse * pParse, Expr * pExp= r, int dest, int jumpIfNull)
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 * Assert()s verify that the computation is = correct.
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 */
>
> -=C2=A0 =C2=A0 =C2=A0op =3D ((pExpr->op + (TK_ISNULL & 1)) ^ 1)= - (TK_ISNULL & 1);
> +=C2=A0 =C2=A0 =C2=A0if (pExpr->op >=3D TK_NE && pExpr-&= gt;op <=3D TK_GE)
> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0op =3D ((pExpr->op= + (TK_NE & 1)) ^ 1) - (TK_NE & 1);
> +=C2=A0 =C2=A0 =C2=A0if (pExpr->op =3D=3D TK_ISNULL || pExpr->op= =3D=3D TK_NOTNULL)
> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0op =3D ((pExpr->op= + (TK_ISNULL & 1)) ^ 1) - (TK_ISNULL & 1);

Please, leave comment how this code work.
It isn't even close to be obvious, really.

Fixed.
=C2=A0

>
>=C2=A0 =C2=A0 =C2=A0 =C2=A0/*
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 * Verify correct alignment of TK_ and OP_ c= onstants.
> -=C2=A0 =C2=A0 =C2=A0 * Tokens TK_ISNULL and TK_NE shoud have the same= parity.
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 */
>=C2=A0 =C2=A0 =C2=A0 =C2=A0assert(pExpr->op !=3D TK_NE || op =3D=3D = OP_Eq);
>=C2=A0 =C2=A0 =C2=A0 =C2=A0assert(pExpr->op !=3D TK_EQ || op =3D=3D = OP_Ne);
> diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
> index b2940b7..e956dfc 100644
> --- a/src/box/sql/parse.y
> +++ b/src/box/sql/parse.y
> @@ -147,13 +147,11 @@ cmdx ::=3D cmd.
> ///////////////////// Begin and end transactions. ////////////////////= ////////
> //
>
> -cmd ::=3D BEGIN trans_opt.=C2=A0 {sql_transaction_begin(pParse);}
> +cmd ::=3D START TRANSACTION trans_opt.=C2=A0 {sql_transaction_begin(p= Parse);}
> trans_opt ::=3D .
> -trans_opt ::=3D TRANSACTION.
> -trans_opt ::=3D TRANSACTION nm.
> -cmd ::=3D COMMIT trans_opt.=C2=A0 =C2=A0 =C2=A0 {sql_transaction_comm= it(pParse);}
> -cmd ::=3D END trans_opt.=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0{sql_transa= ction_commit(pParse);}
> -cmd ::=3D ROLLBACK trans_opt.=C2=A0 =C2=A0 {sql_transaction_rollback(= pParse);}
> +trans_opt ::=3D nm.

What is the point of named transactions, if rollback and commit
don=E2=80=99t use name? I mean, now we ofc can=E2=80=99t use its name, but<= br> what does ANSI say?

Fixed it.
Ansi only asks for <transaction characteristics> after 'START TR= ANSACTION'.
=C2=A0

> +cmd ::=3D COMMIT.=C2=A0 =C2=A0 =C2=A0 {sql_transaction_commit(pParse)= ;}
> +cmd ::=3D ROLLBACK.=C2=A0 =C2=A0 {sql_transaction_rollback(pParse);}<= br> >
> savepoint_opt ::=3D SAVEPOINT.
> savepoint_opt ::=3D .
> @@ -163,7 +161,7 @@ cmd ::=3D SAVEPOINT nm(X). {
> cmd ::=3D RELEASE savepoint_opt nm(X). {
>=C2=A0 =C2=A0sqlite3Savepoint(pParse, SAVEPOINT_RELEASE, &X);
> }
> -cmd ::=3D ROLLBACK trans_opt TO savepoint_opt nm(X). {
> +cmd ::=3D ROLLBACK TO savepoint_opt nm(X). {
>=C2=A0 =C2=A0sqlite3Savepoint(pParse, SAVEPOINT_ROLLBACK, &X);
> }
>
> diff --git a/test/sql-tap/analyze3.test.lua b/test/sql-tap/analyze3.te= st.lua
> index 26f8793..5079962 100755
> --- a/test/sql-tap/analyze3.test.lua
> +++ b/test/sql-tap/analyze3.test.lua
> @@ -340,7 +340,7 @@ test:do_execsql_test(
>=C2=A0 =C2=A0 =C2=A0"analyze3-1.3.1",
>=C2=A0 =C2=A0 =C2=A0[[
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0CREATE TABLE t3(id INTEGER PRIMARY KE= Y, y TEXT, x INTEGER);
> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 BEGIN;
> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 START TRANSACTION;
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0INSERT INTO t3 SELECT id, y, x= FROM t1;
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0COMMIT;
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0CREATE INDEX i3 ON t3(x);
> @@ -465,7 +465,7 @@ test:do_test(
> --=C2=A0 =C2=A0 =C2=A0function()
> --=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0test:execsql([[
> --=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0PRAGMA case_sensitiv= e_like=3Doff;
> ---=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0BEGIN;
> +--=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0START TRANSACTION;<= br>
I guess, you can remove it at all. It makes no sense to add dead code.

> --- a/test/sql-tap/in1.test.lua
> +++ b/test/sql-tap/in1.test.lua
> @@ -26,7 +26,7 @@ test:do_test(
>=C2=A0 =C2=A0 =C2=A0function()
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0test:execsql [[
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0CREATE TABLE t1(a PRIMA= RY KEY, b);
> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 BEGIN;
> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 START TRANSACTION;
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0]]
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0-- for _ in X(0, "X!for", [= =3D[["set i 1","$i<=3D10","incr i"]]=3D]) = do
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0local j =3D 1
> @@ -1073,7 +1073,7 @@ test:do_execsql_test(
> -- MUST_WORK_TEST
> -- do_test in-13.14 {
> --=C2=A0 =C2=A0execsql {
> ---=C2=A0 =C2=A0 =C2=A0BEGIN TRANSACTION;
> +--=C2=A0 =C2=A0 =C2=A0START TRANSACTION;

The same is here and in other places.


Fixed.=C2=A0

=
diff --git a/extra/lempar.c b/extra/lempar.c
index 00fd79c..= d043e39 100644
--- a/extra/lempar.c
+++ b/extra/lempar.= c
@@ -336,8 +336,8 @@ void *ParseAlloc(void *(*mallocProc)(YYMALL= OCARGTYPE)){
=C2=A0 =C2=A0if( pParser ){
=C2=A0#ifdef Y= YTRACKMAXSTACKDEPTH
=C2=A0 =C2=A0 =C2=A0pParser->yyhwm =3D 0;<= /div>
-=C2=A0 =C2=A0 pParser->is_fallback_failed =3D false;
=C2=A0#endif
+=C2=A0 =C2=A0 pParser->is_fallback_failed =3D = false;
=C2=A0#if YYSTACKDEPTH<=3D0
=C2=A0 =C2=A0 =C2= =A0pParser->yytos =3D NULL;
=C2=A0 =C2=A0 =C2=A0pParser->yy= stack =3D NULL;
diff --git a/extra/mkkeywordhash.c b/extra/mkkeyw= ordhash.c
index 990c419..1ec1538 100644
--- a/extra/mkk= eywordhash.c
+++ b/extra/mkkeywordhash.c
@@ -120,7 +120= ,7 @@ static Keyword aKeywordTable[] =3D {
=C2=A0 =C2=A0{ "A= SC",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 "TK_ASC",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ALWAYS,=C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
=C2=A0 =C2=A0{ "= ;AUTOINCREMENT",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "TK_AUTOINCR&q= uot;,=C2=A0 =C2=A0 AUTOINCR,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0false },
=C2=A0 =C2=A0{ "BEFORE",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0"TK_BEFORE",=C2=A0 =C2=A0 =C2=A0 TRIGG= ER,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 false },
-=C2=A0 { "BE= GIN",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 &q= uot;TK_BEGIN",=C2=A0 =C2=A0 =C2=A0 =C2=A0ALWAYS,=C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
+=C2=A0 { "BEGIN",=C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "TK_BEGIN&= quot;,=C2=A0 =C2=A0 =C2=A0 =C2=A0TRIGGER,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= true=C2=A0 },
=C2=A0 =C2=A0{ "BETWEEN",=C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "TK_BETWEEN",=C2=A0 =C2= =A0 =C2=A0ALWAYS,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
=C2=A0 =C2=A0{ "BY",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"TK_BY",=C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 ALWAYS,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2= =A0 },
=C2=A0 =C2=A0{ "CASCADE",=C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "TK_CASCADE",=C2=A0 =C2=A0 =C2= =A0FKEY,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0false },
= @@ -210,6 +210,7 @@ static Keyword aKeywordTable[] =3D {
=C2=A0 = =C2=A0{ "SAVEPOINT",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 "TK_SAVEPOINT",=C2=A0 =C2=A0ALWAYS,=C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0true=C2=A0 },
=C2=A0 =C2=A0{ "SELECT",= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"TK_SELE= CT",=C2=A0 =C2=A0 =C2=A0 ALWAYS,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0true=C2=A0 },
=C2=A0 =C2=A0{ "SET",=C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "TK_SET",=C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ALWAYS,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0true=C2=A0 },
+=C2=A0 { "START",=C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "TK_START",=C2=A0 = =C2=A0 =C2=A0 =C2=A0ALWAYS,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2= =A0 },
=C2=A0 =C2=A0{ "TABLE",=C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "TK_TABLE",=C2=A0 =C2=A0 = =C2=A0 =C2=A0ALWAYS,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },<= /div>
=C2=A0 =C2=A0{ "THEN",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"TK_THEN",=C2=A0 =C2=A0 =C2= =A0 =C2=A0 ALWAYS,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
=C2=A0 =C2=A0{ "TO",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"TK_TO",=C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 ALWAYS,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true= =C2=A0 },
@@ -274,7 +275,6 @@ static Keyword aKeywordTable[] =3D = {
=C2=A0 =C2=A0{ "SIGNAL",=C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"TK_STANDARD",=C2=A0 =C2=A0 RES= ERVED,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
=C2=A0 =C2= =A0{ "SMALLINT",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0"TK_ID",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 RESERVED,=C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
=C2=A0 =C2=A0{ "SPE= CIFIC",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"TK= _STANDARD",=C2=A0 =C2=A0 RESERVED,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0tr= ue=C2=A0 },
-=C2=A0 { "START",=C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "TK_STANDARD",=C2=A0 =C2= =A0 RESERVED,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
=C2= =A0 =C2=A0{ "SYSTEM",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0"TK_STANDARD",=C2=A0 =C2=A0 RESERVED,=C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2=A0 },
=C2=A0 =C2=A0{ "SQL= ",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 "TK_STANDARD",=C2=A0 =C2=A0 RESERVED,=C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0true=C2=A0 },
=C2=A0 =C2=A0{ "USER",=C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"TK_STAN= DARD",=C2=A0 =C2=A0 RESERVED,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0true=C2= =A0 },
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
=
index b1650cf..b26728d 100644
--- a/src/box/sql/expr.c
=
+++ b/src/box/sql/expr.c
@@ -4826,23 +4826,32 @@ sqlite3Expr= IfFalse(Parse * pParse, Expr * pExpr, int dest, int jumpIfNull)
= =C2=A0 *=C2=A0 =C2=A0 =C2=A0 = =C2=A0TK_EQ=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 OP_Ne
=C2=A0 *=C2=A0 =C2=A0 =C2=A0 = =C2=A0TK_GT=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 OP_Le
=C2=A0 *=C2=A0 =C2=A0 =C2=A0 = =C2=A0TK_LE=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 OP_Gt
- *=C2=A0 =C2=A0 =C2=A0 =C2= =A0TK_GE=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 OP_Lt
= =C2=A0 *=C2=A0 =C2=A0 =C2=A0 = =C2=A0TK_LT=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 OP_Ge
+ *=C2=A0 =C2=A0 =C2=A0 =C2= =A0TK_GE=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 OP_Lt
= =C2=A0 *=C2=A0 =C2=A0 =C2=A0 = =C2=A0 ...=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ...
=
=C2=A0 *=C2=A0 =C2=A0 =C2= =A0 =C2=A0TK_ISNULL=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 OP_NotNull
= =C2=A0 *=C2=A0 =C2=A0 =C2=A0 = =C2=A0TK_NOTNULL=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0OP_IsNull
=C2= =A0 *
- * For other values of pExpr->op, op i= s undefined and unused.
- * The value of TK_ and OP_ constants are arranged such that we
<= div>- * can compute the mappin= g above using the following expression.
+ * For other values of pExpr->op, op is undefined
+ * and unused. The v= alue of TK_ and OP_ constants
+ * are arranged such that we can compute the mapping
+= * above using the following e= xpression. The idea
+ * is that both for OP_'s and TK_'s the first elements
+= * in the given mapping ranges= of codes and tokens are
+ <= /span> * 'Not equal' and 'Is null'. Moreover the 'exclu= ding'
+ * ones (= like 'Greater than' and 'Lower than or Equal')
+<= span style=3D"white-space:pre-wrap"> * are paired and follow one ea= ch other, hence have n
+ * and n + 1 numbers.
=C2=A0 * Assert()s verify that the computation is correct.
= =C2=A0 */
=C2=A0
- op =3D ((pExpr->op = + (TK_ISNULL & 1)) ^ 1) - (TK_ISNULL & 1);
+ if (pExpr->op >=3D TK_NE &&= pExpr->op <=3D TK_GE)
+ op =3D ((pExpr->op + (TK_NE & 1)) ^ 1) - (TK_NE & 1);=
+ if (pExpr->op = =3D=3D TK_ISNULL || pExpr->op =3D=3D TK_NOTNULL)
+ op =3D ((pExpr->op + (TK_ISNULL &= 1)) ^ 1) - (TK_ISNULL & 1);
=C2=A0
=C2=A0 /*
=C2=A0 * Verify correct alignment of TK_ and OP_ constan= ts.
- * Tokens TK_IS= NULL and TK_NE shoud have the same parity.
=C2=A0 */
=C2=A0 assert(pExpr->op !=3D TK_NE || op =3D=3D OP_Eq);
=C2=A0 assert(pExpr->o= p !=3D TK_EQ || op =3D=3D OP_Ne);
diff --git a/src/box/sql/parse.= y b/src/box/sql/parse.y
index ac935fd..b4f11e5 100644
-= -- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -= 147,13 +147,9 @@ cmdx ::=3D cmd.
=C2=A0///////////////////// Begi= n and end transactions. ////////////////////////////
=C2=A0//
=C2=A0
-cmd ::=3D BEGIN trans_opt.=C2=A0 {sql_transaction_= begin(pParse);}
-trans_opt ::=3D .
-trans_opt ::=3D TRA= NSACTION.
-trans_opt ::=3D TRANSACTION nm.
-cmd ::=3D C= OMMIT trans_opt.=C2=A0 =C2=A0 =C2=A0 {sql_transaction_commit(pParse);}
-cmd ::=3D END trans_opt.=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0{sql_trans= action_commit(pParse);}
-cmd ::=3D ROLLBACK trans_opt.=C2=A0 =C2= =A0 {sql_transaction_rollback(pParse);}
+cmd ::=3D START TRANSACT= ION.=C2=A0 {sql_transaction_begin(pParse);}
+cmd ::=3D COMMIT.=C2= =A0 =C2=A0 =C2=A0 {sql_transaction_commit(pParse);}
+cmd ::=3D RO= LLBACK.=C2=A0 =C2=A0 {sql_transaction_rollback(pParse);}
=C2=A0
=C2=A0savepoint_opt ::=3D SAVEPOINT.
=C2=A0savepoint_opt= ::=3D .
@@ -163,7 +159,7 @@ cmd ::=3D SAVEPOINT nm(X). {
=C2=A0cmd ::=3D RELEASE savepoint_opt nm(X). {
=C2=A0 =C2=A0sq= lite3Savepoint(pParse, SAVEPOINT_RELEASE, &X);
=C2=A0}
<= div>-cmd ::=3D ROLLBACK trans_opt TO savepoint_opt nm(X). {
+cmd = ::=3D ROLLBACK TO savepoint_opt nm(X). {
=C2=A0 =C2=A0sqlite3Save= point(pParse, SAVEPOINT_ROLLBACK, &X);
=C2=A0}
diff= --git a/test/sql-tap/start-transaction.test.lua b/test/sql-tap/start-trans= action.test.lua
new file mode 100755
index 0000000..eec= e4cd
--- /dev/null
+++ b/test/sql-tap/start-transaction= .test.lua
@@ -0,0 +1,266 @@
+#!/usr/bin/env tarantool
+test =3D require("sqltester")
+test:plan(21)<= /div>
+
+test:do_catchsql_test(
+ "start-transaction-1.0",
+ [[
+ CREATE TABLE IF NOT EXISTS t(id int PRIMARY KEY= );
+ DELETE FROM t;<= /div>
+ BEGIN;
+= INSERT INTO t VALUES (1);
+ INSERT INTO t VALUE= S (2);
+ COMMIT;
+ ]], {
+ -- <start-transaction-1.0>
+ 1, "near \&quo= t;BEGIN\": syntax error"
+ -- <start-transaction-1.0>
+ })
+
+test:do_execsql_te= st(
+ "start-tra= nsaction-1.1",
+ [[
+ SELECT * FROM = t;
+ ]], {
= + -- <start-transaction-1.1= >
+
+ -= - <start-transaction-1.1>
+ })
+
+test:do_catchsql_test(
+ "start-transaction-1.2"= ;,
+ [[
+ CREATE TABLE IF NOT EXISTS t(id= int primary key);
+ delete from t;
+ BE= GIN TRANSACTION;
+ = INSERT INTO t VALUES (1);
+ = INSERT INTO t VALUES (2);
+ COMMIT;
+ <= /span>]], {
+ -- <= ;start-transaction-1.1>
+= 1, "near \"BEGIN\": syntax error"
+<= span style=3D"white-space:pre-wrap"> -- <start-transaction-1.1&g= t;
+ })
+
+test:do_execsql_test(
+ "start-transaction-1.3",
+ [[
+ SELECT * FROM t;
+ ]], {
+ = -- <start-transaction-1.3>
+
+ -- <start-transaction-1.3>
+ })
+
+test:do_= catchsql_test(
+ &quo= t;start-transaction-1.4",
+ [[
+ DEL= ETE FROM t;
+ START = TRANSACTION;
+ INSE= RT INTO t VALUES (1);
+ INSERT INTO t VALUES (2);
+ COMMIT;
+ ]], {
+ -- <sta= rt-transaction-1.4>
+ 0
+ -- <star= t-transaction-1.4>
+ })
+
+test:do_execsql_test(
+ "start-transaction-1.5",
<= div>+ [[
+ SELECT * FROM t;
+ ]], {
+ -- <start-transaction-1.5>
+ 1, 2
+ -- <start-transaction-1.5>
+ })
+
+test:do_cat= chsql_test(
+ "s= tart-transaction-1.6",
+ [[
+ DELETE= FROM t;
+ START TRA= NSACTION;
+ INSERT = INTO t VALUES (1);
+ INSERT INTO t VALUES (2);
+ COMMIT TRANSACTION;
+ ]], {
+ -= - <start-transaction-1.6>
+ 1, "keyword \"TRANSACTION\" is reserved"<= /div>
+ -- <start-trans= action-1.6>
+ })
+
+test:do_execsql_test(
+ "start-transaction-1.7",
+ [[
+ COMMIT;
+ SELECT * FROM t;
+ ]], {
+ -- <start-transaction-1.7>
+ 1, 2
+ -- <start-transaction-1.7>
+ })
+
+test:do_catchsql_test(
+ "start-transaction-1.8= ",
+ [[
+ DELETE FROM t;
+= START TRANSACTION;
= + INSERT INTO t VALUES (1);
+ INSERT INTO t VALU= ES (2);
+ END;
=
+ ]], {
+ -- <start-transaction-1.8>
+ 1, "keyword \&qu= ot;END\" is reserved"
+ -- <start-transaction-1.8>
+ })
+
+test:do_execsql_test(=
+ "start-transa= ction-1.9",
+ [[=
+ COMMIT;
+ SELECT * FROM t;
= + ]], {
+ -- <start-transaction-1.9>
+ 1, 2
+ -- <start-transaction-1.9>
=
+ })
+
+test:do_catchsql_test(
+ <= /span>"start-transaction-1.10",
+ [[
+ = DELETE FROM t;
+ START TRANSACTION;
+ = INSERT INTO t VALUES (1);
+ INSERT INTO t VALUES (2);
+ END TRANSACTION;
+ ]], {
+ -- <start-transaction-1.10>
+ 1, "keyword \"END\" is reserved&qu= ot;
+ -- <start-t= ransaction-1.10>
+ })
+
+test:do_execsql_test(
+ "start-transaction-1.11",
+ [[
+ COMMIT;
+ SELECT * FROM t;
+ ]], {
+ = -- <start-transaction-1.11>
+ 1, 2
+ -- <start-transaction-1.11>
+ })
+
+test:do_catchsql_test(<= /div>
+ "start-transac= tion-1.12",
+ [[=
+ DELETE FROM t;
+ START TRANSACTION;
+ INSERT INTO t VALU= ES (1);
+ INSERT IN= TO t VALUES (2);
+ R= OLLBACK;
+ ]], {
+ -- <start-transacti= on-1.12>
+ 0
+ -- <start-transacti= on-1.12>
+ })
+
+test:do_execsql_test(
+ "start-transaction-1.13",
+ [[
+ SELECT * FROM t;
+ ]], {
+ -- <start-transaction-1.13>
+
+ -- <start-transaction-1.13>
+ })
+
+test:do_catchsql_test(
+= "start-transaction-1.14",
+ [[
+ START TRANSACTION;
+ INSERT INTO t VALUES (1);
+ INSERT INTO t VALUES (2);
+ ROLLBACK TRANSACTION;
+ COMMIT;
+ ]], {
+ -- <start-transaction-1.14>
+ 1, "keyword \"TRANSACTION\" is r= eserved"
+ -- &= lt;start-transaction-1.14>
+ })
+
+test:do_execsql_test(
+ "start-transaction-1.15",=
+ [[
+ COMMIT;
+ SELECT * FROM t;
+ ]], {
+ -- <start-transaction-1.15>
+ 1, 2
+ -- <start-transaction-1.15>
+ })
+
+test:do_catc= hsql_test(
+ "st= art-transaction-1.16",
+ [[
+ DELETE= FROM t;
+ START TRA= NSACTION;
+ INSERT = INTO t VALUES (1);
+ SAVEPOINT s1;
+ I= NSERT INTO t VALUES (2);
+ = ROLLBACK TO s1;
+ <= /span>COMMIT;
+ ]], {=
+ -- <start-tran= saction-1.16>
+ 0=
+ -- <start-tran= saction-1.16>
+ })=
+
+test:do_execsql_test(
+ "start-transaction-1.17",
+<= span style=3D"white-space:pre-wrap"> [[
+ SELECT * FROM t;
+ ]], {
+ -- <start-transaction-1.17>
+ 1
+ -- <start-transaction-1.17>
+ })
+
+test:do_catchsql_te= st(
+ "start-tra= nsaction-1.18",
+ [[
+ DELETE FROM t= ;
+ START TRANSACTIO= N;
+ INSERT INTO t = VALUES (1);
+ SAVEP= OINT s1;
+ INSERT I= NTO t VALUES (2);
+ ROLLBACK TRANSACTION TO s1;
+ COMMIT;
+ = ]], {
+ -- <start= -transaction-1.18>
+ 1, "keyword \"TRANSACTION\" is reserved"
= + -- <start-transaction-1.1= 8>
+ })
= +
+test:do_execsql_test(
+ "start-transaction-1.19",
+ [[
+ COMMIT;
+= SELECT * FROM t;
+ = ]], {
+ -- &l= t;start-transaction-1.19>
+ 1, 2
+ --= <start-transaction-1.19>
+ })
+
+test:do_execsql_test(
+ "start-transaction-1.20"= ;,
+ [[
+ drop table t;
+ ]], {
+ -- <start0transaction-1.20>
+
+ -- <start-transac= tion-1.20>
+ })
+
+test:finish_test()
--0000000000007e8bc305711ce356--