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 9D5412BE68 for ; Tue, 3 Apr 2018 15:40:32 -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 XPN87wnDj5kR for ; Tue, 3 Apr 2018 15:40:32 -0400 (EDT) Received: from smtp62.i.mail.ru (smtp62.i.mail.ru [217.69.128.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id 9646C29E44 for ; Tue, 3 Apr 2018 15:40:31 -0400 (EDT) From: "n.pettik" Message-Id: Content-Type: multipart/alternative; boundary="Apple-Mail=_26283FBD-5438-4A21-971B-61F6F95BBE16" Mime-Version: 1.0 (Mac OS X Mail 10.3 \(3273\)) Subject: [tarantool-patches] Re: [PATCH] sql: statistics removal after dropping an index Date: Tue, 3 Apr 2018 22:40:28 +0300 In-Reply-To: <1522778833-27252-1-git-send-email-hollow653@gmail.com> References: <1522778833-27252-1-git-send-email-hollow653@gmail.com> 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: tarantool-patches@freelists.org Cc: "N. Tatunov" --Apple-Mail=_26283FBD-5438-4A21-971B-61F6F95BBE16 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 Hello. Consider comments below. > On 3 Apr 2018, at 21:07, N.Tatunov wrote: >=20 > Currently dropping an index leads to removal of > all the entries containing the certain index name > in "_sql_statN" tables. Thus far analyze routine was fixed > so it seems that the indexes from the different tables but > with the same names should work more properly. >=20 > Closes: #3264 > --- >=20 > Branch: https://github.com/tarantool/tarantool This is link to the whole repo. You should place here link to your particular branch. Example: = https://github.com/tarantool/tarantool/tree/N_Tatunov/gh-3264-stat-table-e= ntries-removal = > Issue: https://github.com/tarantool/tarantool/issues/3264 >=20 > src/box/sql/build.c | 36 ++++++---- > test/sql/sql-statN-index-drop.result | 127 = +++++++++++++++++++++++++++++++++ > test/sql/sql-statN-index-drop.test.lua | 54 ++++++++++++++ > 3 files changed, 205 insertions(+), 12 deletions(-) > create mode 100644 test/sql/sql-statN-index-drop.result > create mode 100644 test/sql/sql-statN-index-drop.test.lua >=20 > diff --git a/src/box/sql/build.c b/src/box/sql/build.c > index 5e3ed0f..baaade2 100644 > --- a/src/box/sql/build.c > +++ b/src/box/sql/build.c > @@ -2213,19 +2213,31 @@ sqliteViewResetAll(sqlite3 * db) > static void > sqlite3ClearStatTables(Parse * pParse, /* The parsing context = */ > const char *zType, /* "idx" or "tbl" */ > - const char *zName /* Name of index or = table */ > + const char *zName, /* Name of index or = table */ > + const char *zTable /* Name of the indexed = table*/ > ) Way you has chosen is very confusing. Here table name is passed as third arg and index is NULL: > + sqlite3ClearStatTables(pParse, "tbl", pTab->zName, NULL); But here - vice versa: > + sqlite3ClearStatTables(pParse, "idx", pIndex->zName, = pIndex->pTable->zName); Lets strictly specify, that third arg always is a table's name, and fourth one - index. > { > int i; > - for (i =3D 1; i <=3D 4; i++) { > - char zTab[24]; > - sqlite3_snprintf(sizeof(zTab), zTab, "_sql_stat%d", i); > - if (sqlite3FindTable(pParse->db, zTab)) { > - sqlite3NestedParse(pParse, > - "DELETE FROM \"%s\" WHERE = \"%s\"=3D%Q", > - zTab, zType, zName); > - } > - } > + if(strcmp(zType, "idx") =3D=3D 0) > + for (i =3D 1; i <=3D 4; i++) { Actually, it is legacy code: it derived from SQLite codebase. In SQLite really can exist all four[1-4] tables. But in our SQL only _stat1 and _stat4 tables are valid tables.=20 > + char zTab[24]; > + sqlite3_snprintf(sizeof(zTab), zTab, "_sql_stat%d", i); > + if (sqlite3FindTable(pParse->db, zTab)) { Statistic tables are considered to be system. This means that they are created when Tarantool is launching. Thus, they always exist and you don=E2=80=99t need to check if table = exists. In this case existence is checked due to the fact that tables _stat2 and _stat3 does not exist. So I propose you to involve only required tables and get rid of this check.=20 > + sqlite3NestedParse(pParse, > + "DELETE FROM \"%s\" WHERE (\"%s\"=3D%Q AND " > + "\"tbl\"=3D%Q)", > + zTab, zType, zName, zTable); > + } > + } else > + for (i =3D 1; i <=3D 4; i++) { > + char zTab[24]; > + sqlite3_snprintf(sizeof(zTab), zTab, "_sql_stat%d", i); > + if (sqlite3FindTable(pParse->db, zTab)) { > + sqlite3NestedParse(pParse, > + "DELETE FROM \"%s\" WHERE \"%s\"=3D%Q", > + zTab, zType, zName); > + } > + } > } Overall comment: you may notice that now SQL codebase is written using legacy codestyle (including obsolete var declaration style and Hungarian notation). I suggest you to rewrite whole function using=20 Tarantool (aka Linux Kernel) codestyle and eventually rename it to 'sql_clear_stat_tables'. Also, we are using comments for functions in doxygen format. You can check it out in any src/box source file. >=20 > /* > @@ -2415,7 +2427,7 @@ sqlite3DropTable(Parse * pParse, SrcList * = pName, int isView, int noErr) > */ >=20 > sqlite3BeginWriteOperation(pParse, 1); > - sqlite3ClearStatTables(pParse, "tbl", pTab->zName); > + sqlite3ClearStatTables(pParse, "tbl", pTab->zName, NULL); > sqlite3FkDropTable(pParse, pName, pTab); > sqlite3CodeDropTable(pParse, pTab, isView); >=20 > @@ -3417,7 +3429,7 @@ sqlite3DropIndex(Parse * pParse, SrcList * = pName, Token * pName2, int ifExists) > * But firstly, delete statistics since schema > * changes after DDL. > */ > - sqlite3ClearStatTables(pParse, "idx", pIndex->zName); > + sqlite3ClearStatTables(pParse, "idx", pIndex->zName, = pIndex->pTable->zName); > int record_reg =3D ++pParse->nMem; > int space_id_reg =3D ++pParse->nMem; > sqlite3VdbeAddOp2(v, OP_Integer, = SQLITE_PAGENO_TO_SPACEID(pIndex->tnum), > diff --git a/test/sql/sql-statN-index-drop.result = b/test/sql/sql-statN-index-drop.result > new file mode 100644 > index 0000000..707d7e0 > --- /dev/null > +++ b/test/sql/sql-statN-index-drop.result Actually, you don=E2=80=99t have to create separate test file for this = issue: you can add these tests to one of sql-tap/analyze[1-11].test.lua But it is up to you. > @@ -0,0 +1,127 @@ > +test_run =3D require('test_run').new() > +--- > +... > +-- Initializing some things. > +box.sql.execute("CREATE TABLE t1(id PRIMARY KEY, a);") > +--- > +... > +box.sql.execute("CREATE TABLE t2(id PRIMARY KEY, a);") > +--- > +... > +box.sql.execute("CREATE INDEX i1 ON t1(a);") > +--- > +... > +box.sql.execute("CREATE INDEX i1 ON t2(a);") > +--- > +... > +box.sql.execute("INSERT INTO t1 VALUES(1, 2);") > +--- > +... > +box.sql.execute("INSERT INTO t2 VALUES(1, 2);") > +--- > +... > +-- Analyze. > +box.sql.execute("ANALYZE;") > +--- > +... > +-- Checking the data. > +box.sql.execute("SELECT * FROM \"_sql_stat4\";") > +--- > +- - ['T1', 'I1', '1', '0', '0', !!binary kQI=3D] > + - ['T1', 'T1', '1', '0', '0', !!binary kQE=3D] > + - ['T2', 'I1', '1', '0', '0', !!binary kQI=3D] > + - ['T2', 'T2', '1', '0', '0', !!binary kQE=3D] > +... > +box.sql.execute("SELECT * FROM \"_sql_stat1\";") > +--- > +- - ['T1', 'I1', '1 1'] > + - ['T1', 'T1', '1 1'] > + - ['T2', 'I1', '1 1'] > + - ['T2', 'T2', '1 1'] > +... > +-- Dropping an index > +box.sql.execute("DROP INDEX i1 ON t1;") > +--- > +... > +-- Checking the DROP INDEX results. > +box.sql.execute("SELECT * FROM \"_sql_stat4\";") > +--- > +- - ['T1', 'T1', '1', '0', '0', !!binary kQE=3D] > + - ['T2', 'I1', '1', '0', '0', !!binary kQI=3D] > + - ['T2', 'T2', '1', '0', '0', !!binary kQE=3D] > +... > +box.sql.execute("SELECT * FROM \"_sql_stat1\";") > +--- > +- - ['T1', 'T1', '1 1'] > + - ['T2', 'I1', '1 1'] > + - ['T2', 'T2', '1 1'] > +... > +--Cleaning up. > +box.sql.execute("DROP TABLE t1;") > +--- > +... > +box.sql.execute("DROP TABLE t2;") > +--- > +... > +-- Same test but dropping an INDEX ON t2. > +box.sql.execute("CREATE TABLE t1(id PRIMARY KEY, a);") > +--- > +... > +box.sql.execute("CREATE TABLE t2(id PRIMARY KEY, a);") > +--- > +... > +box.sql.execute("CREATE INDEX i1 ON t1(a);") > +--- > +... > +box.sql.execute("CREATE INDEX i1 ON t2(a);") > +--- > +... > +box.sql.execute("INSERT INTO t1 VALUES(1, 2);") > +--- > +... > +box.sql.execute("INSERT INTO t2 VALUES(1, 2);") > +--- > +... > +-- Analyze. > +box.sql.execute("ANALYZE;") > +--- > +... > +-- Checking the data. > +box.sql.execute("SELECT * FROM \"_sql_stat4\";") > +--- > +- - ['T1', 'I1', '1', '0', '0', !!binary kQI=3D] > + - ['T1', 'T1', '1', '0', '0', !!binary kQE=3D] > + - ['T2', 'I1', '1', '0', '0', !!binary kQI=3D] > + - ['T2', 'T2', '1', '0', '0', !!binary kQE=3D] > +... > +box.sql.execute("SELECT * FROM \"_sql_stat1\";") > +--- > +- - ['T1', 'I1', '1 1'] > + - ['T1', 'T1', '1 1'] > + - ['T2', 'I1', '1 1'] > + - ['T2', 'T2', '1 1'] > +... > +-- Dropping an index > +box.sql.execute("DROP INDEX i1 ON t2;") > +--- > +... > +-- Checking the DROP INDEX results. > +box.sql.execute("SELECT * FROM \"_sql_stat4\";") > +--- > +- - ['T1', 'I1', '1', '0', '0', !!binary kQI=3D] > + - ['T1', 'T1', '1', '0', '0', !!binary kQE=3D] > + - ['T2', 'T2', '1', '0', '0', !!binary kQE=3D] > +... > +box.sql.execute("SELECT * FROM \"_sql_stat1\";") > +--- > +- - ['T1', 'I1', '1 1'] > + - ['T1', 'T1', '1 1'] > + - ['T2', 'T2', '1 1'] > +... > +--Cleaning up. > +box.sql.execute("DROP TABLE t1;") > +--- > +... > +box.sql.execute("DROP TABLE t2;") > +--- > +... > diff --git a/test/sql/sql-statN-index-drop.test.lua = b/test/sql/sql-statN-index-drop.test.lua > new file mode 100644 > index 0000000..9b919de > --- /dev/null > +++ b/test/sql/sql-statN-index-drop.test.lua > @@ -0,0 +1,54 @@ > +test_run =3D require('test_run').new() > + > +-- Initializing some things. > +box.sql.execute("CREATE TABLE t1(id PRIMARY KEY, a);") > +box.sql.execute("CREATE TABLE t2(id PRIMARY KEY, a);") > +box.sql.execute("CREATE INDEX i1 ON t1(a);") > +box.sql.execute("CREATE INDEX i1 ON t2(a);") > +box.sql.execute("INSERT INTO t1 VALUES(1, 2);") > +box.sql.execute("INSERT INTO t2 VALUES(1, 2);") > + > +-- Analyze. > +box.sql.execute("ANALYZE;") > + > +-- Checking the data. > +box.sql.execute("SELECT * FROM \"_sql_stat4\";") > +box.sql.execute("SELECT * FROM \"_sql_stat1\";") > + > +-- Dropping an index Nitpicking: put a dot at the end of sentence. > +box.sql.execute("DROP INDEX i1 ON t1;") > + > +-- Checking the DROP INDEX results. > +box.sql.execute("SELECT * FROM \"_sql_stat4\";") > +box.sql.execute("SELECT * FROM \"_sql_stat1\";") > + > +--Cleaning up. > +box.sql.execute("DROP TABLE t1;") > +box.sql.execute("DROP TABLE t2;") > + > +-- Same test but dropping an INDEX ON t2. > + > +box.sql.execute("CREATE TABLE t1(id PRIMARY KEY, a);") > +box.sql.execute("CREATE TABLE t2(id PRIMARY KEY, a);") > +box.sql.execute("CREATE INDEX i1 ON t1(a);") > +box.sql.execute("CREATE INDEX i1 ON t2(a);") > +box.sql.execute("INSERT INTO t1 VALUES(1, 2);") > +box.sql.execute("INSERT INTO t2 VALUES(1, 2);") > + > +-- Analyze. > +box.sql.execute("ANALYZE;") > + > +-- Checking the data. > +box.sql.execute("SELECT * FROM \"_sql_stat4\";") > +box.sql.execute("SELECT * FROM \"_sql_stat1\";") > + > +-- Dropping an index Nitpicking: put a dot at the end of sentence. > +box.sql.execute("DROP INDEX i1 ON t2;") > + > +-- Checking the DROP INDEX results. > +box.sql.execute("SELECT * FROM \"_sql_stat4\";") > +box.sql.execute("SELECT * FROM \"_sql_stat1\";") > + > +--Cleaning up. > +box.sql.execute("DROP TABLE t1;") > +box.sql.execute("DROP TABLE t2;") > --=20 > 2.7.4 >=20 >=20 --Apple-Mail=_26283FBD-5438-4A21-971B-61F6F95BBE16 Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=utf-8
Hello. Consider comments below.


On 3 Apr 2018, at 21:07, = N.Tatunov <hollow653@gmail.com> wrote:

Currently dropping an index leads to removal of
all the entries containing the certain index name
in "_sql_statN" tables. Thus far analyze routine was fixed
so it seems that the indexes from the different tables but
with the same names should work more properly.

Closes: #3264
---

Branch: https://github.com/tarantool/tarantool

This = is link to the whole repo.
You should place here link to your = particular branch.
Example:
https://github.com/tarantool/tarantool/tree/N_Tatunov/gh-3264-s= tat-table-entries-removal

Issue: https://github.com/tarantool/tarantool/issues/3264

src/box/sql/build.c =             &n= bsp;      |  36 ++++++----
test/sql/sql-statN-index-drop.result   | 127 = +++++++++++++++++++++++++++++++++
= test/sql/sql-statN-index-drop.test.lua |  54 ++++++++++++++
3 files changed, 205 insertions(+), 12 deletions(-)
create mode 100644 test/sql/sql-statN-index-drop.result
create mode 100644 test/sql/sql-statN-index-drop.test.lua

diff --git a/src/box/sql/build.c = b/src/box/sql/build.c
index 5e3ed0f..baaade2 100644
--- a/src/box/sql/build.c
+++ = b/src/box/sql/build.c
@@ -2213,19 +2213,31 @@ = sqliteViewResetAll(sqlite3 * db)
static void
= sqlite3ClearStatTables(Parse * pParse, /* The parsing context */
= =       const char *zType, /* "idx" = or "tbl" */
- =       const char *zName /* Name = of index or table */
+ =       const char *zName, /* Name = of index or table */
+ =       const char *zTable   /* = Name of the indexed table*/
    )

Way = you has chosen is very confusing.
Here table name is passed as = third arg and index is NULL:
+ = sqlite3ClearStatTables(pParse, "tbl", pTab->zName, = NULL);
But here - vice = versa:
+ = sqlite3ClearStatTables(pParse, "idx", pIndex->zName, = pIndex->pTable->zName);

Lets strictly = specify, that third arg always is a table's name,
and fourth = one - index.

{
int i;
- = for (i =3D 1; i <=3D 4; i++) {
- char = zTab[24];
- sqlite3_snprintf(sizeof(zTab), = zTab, "_sql_stat%d", i);
- if = (sqlite3FindTable(pParse->db, zTab)) {
- = sqlite3NestedParse(pParse,
- =   "DELETE FROM \"%s\" WHERE \"%s\"=3D%Q",
- =   zTab, zType, zName);
- }
- = }
+ if(strcmp(zType, "idx") =3D=3D = 0)
+        for (i =3D = 1; i <=3D 4; i++) {

Actually, it is legacy code: it derived from = SQLite codebase.
In SQLite really can exist all four[1-4] = tables. But in our SQL
only _stat1 and _stat4 tables are valid = tables. 

+ =            char = zTab[24];
+ =            sqlite3_= snprintf(sizeof(zTab), zTab, "_sql_stat%d", i);
+ =            if = (sqlite3FindTable(pParse->db, zTab)) {

Statistic tables are considered to be = system.
This means that they are created when Tarantool is = launching.
Thus, they always exist and you don=E2=80=99t need = to check if table exists.
In this case existence is checked = due to the fact that tables
_stat2 and _stat3 does not exist. = So I propose you to involve only
required tables and get rid = of this check. 

+ =             &n= bsp;  sqlite3NestedParse(pParse,
+ =             &n= bsp;          "DELETE = FROM \"%s\" WHERE (\"%s\"=3D%Q AND "
+ =             &n= bsp;          "\"tbl\"=3D= %Q)",
+ =             &n= bsp;          zTab, = zType, zName, zTable);
+ =            }
+        } else
+        for (i =3D 1; i = <=3D 4; i++) {
+ =            char = zTab[24];
+ =            sqlite3_= snprintf(sizeof(zTab), zTab, "_sql_stat%d", i);
+ =            if = (sqlite3FindTable(pParse->db, zTab)) {
+ =             &n= bsp;  sqlite3NestedParse(pParse,
+ =             &n= bsp;          "DELETE = FROM \"%s\" WHERE \"%s\"=3D%Q",
+ =             &n= bsp;          zTab, = zType, zName);
+ =            }
+        }
= }

Overall comment: you may notice that now = SQL codebase is written using
legacy codestyle (including = obsolete var declaration style and
Hungarian notation). I = suggest you to rewrite whole function using 
Tarantool = (aka Linux Kernel) codestyle and eventually rename it = to
'sql_clear_stat_tables'. Also, we are using comments for = functions
in doxygen format. You can check it out in any = src/box source file.


= /*
@@ -2415,7 +2427,7 @@ sqlite3DropTable(Parse * pParse, = SrcList * pName, int isView, int noErr)
*/

= sqlite3BeginWriteOperation(pParse, 1);
- = sqlite3ClearStatTables(pParse, "tbl", pTab->zName);
+ = sqlite3ClearStatTables(pParse, "tbl", pTab->zName, NULL);
= sqlite3FkDropTable(pParse, pName, pTab);
= sqlite3CodeDropTable(pParse, pTab, isView);

@@ -3417,7 +3429,7 @@ sqlite3DropIndex(Parse * pParse, = SrcList * pName, Token * pName2, int ifExists)
* But = firstly, delete statistics since schema
* = changes after DDL.
*/
- = sqlite3ClearStatTables(pParse, "idx", pIndex->zName);
+ = sqlite3ClearStatTables(pParse, "idx", pIndex->zName, = pIndex->pTable->zName);
int = record_reg =3D ++pParse->nMem;
int = space_id_reg =3D ++pParse->nMem;
= sqlite3VdbeAddOp2(v, OP_Integer, = SQLITE_PAGENO_TO_SPACEID(pIndex->tnum),
diff --git = a/test/sql/sql-statN-index-drop.result = b/test/sql/sql-statN-index-drop.result
new file mode = 100644
index 0000000..707d7e0
--- = /dev/null
+++ b/test/sql/sql-statN-index-drop.result

Actually, you don=E2=80=99t have to create = separate test file for this issue:
you can add these tests to = one of sql-tap/analyze[1-11].test.lua
But it is up to = you.

@@ -0,0 +1,127 @@
+test_run =3D = require('test_run').new()
+---
+...
+-- Initializing some things.
+box.sql.execute("CREATE TABLE t1(id PRIMARY KEY, a);")
+---
+...
+box.sql.execute("CREATE = TABLE t2(id PRIMARY KEY, a);")
+---
+...
+box.sql.execute("CREATE INDEX i1 ON t1(a);")
+---
+...
+box.sql.execute("CREATE = INDEX i1 ON t2(a);")
+---
+...
+box.sql.execute("INSERT INTO t1 VALUES(1, 2);")
+---
+...
+box.sql.execute("INSERT = INTO t2 VALUES(1, 2);")
+---
+...
+-- Analyze.
+box.sql.execute("ANALYZE;")
+---
+...
+-- Checking the = data.
+box.sql.execute("SELECT * FROM \"_sql_stat4\";")
+---
+- - ['T1', 'I1', '1', '0', '0', !!binary = kQI=3D]
+  - ['T1', 'T1', '1', '0', '0', !!binary = kQE=3D]
+  - ['T2', 'I1', '1', '0', '0', !!binary = kQI=3D]
+  - ['T2', 'T2', '1', '0', '0', !!binary = kQE=3D]
+...
+box.sql.execute("SELECT * FROM = \"_sql_stat1\";")
+---
+- - ['T1', 'I1', '1 = 1']
+  - ['T1', 'T1', '1 1']
+  - = ['T2', 'I1', '1 1']
+  - ['T2', 'T2', '1 1']
+...
+-- Dropping an index
+box.sql.execute("DROP INDEX i1 ON t1;")
+---
+...
+-- Checking the DROP INDEX results.
+box.sql.execute("SELECT * FROM \"_sql_stat4\";")
+---
+- - ['T1', 'T1', '1', '0', '0', !!binary = kQE=3D]
+  - ['T2', 'I1', '1', '0', '0', !!binary = kQI=3D]
+  - ['T2', 'T2', '1', '0', '0', !!binary = kQE=3D]
+...
+box.sql.execute("SELECT * FROM = \"_sql_stat1\";")
+---
+- - ['T1', 'T1', '1 = 1']
+  - ['T2', 'I1', '1 1']
+  - = ['T2', 'T2', '1 1']
+...
+--Cleaning up.
+box.sql.execute("DROP TABLE t1;")
+---
+...
+box.sql.execute("DROP TABLE t2;")
+---
+...
+-- Same test but = dropping an INDEX ON t2.
+box.sql.execute("CREATE TABLE = t1(id PRIMARY KEY, a);")
+---
+...
+box.sql.execute("CREATE TABLE t2(id PRIMARY KEY, a);")
+---
+...
+box.sql.execute("CREATE = INDEX i1 ON t1(a);")
+---
+...
+box.sql.execute("CREATE INDEX i1 ON t2(a);")
+---
+...
+box.sql.execute("INSERT = INTO t1 VALUES(1, 2);")
+---
+...
+box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
+---
+...
+-- Analyze.
+box.sql.execute("ANALYZE;")
+---
+...
+-- Checking the data.
+box.sql.execute("SELECT * FROM \"_sql_stat4\";")
+---
+- - ['T1', 'I1', '1', '0', '0', !!binary = kQI=3D]
+  - ['T1', 'T1', '1', '0', '0', !!binary = kQE=3D]
+  - ['T2', 'I1', '1', '0', '0', !!binary = kQI=3D]
+  - ['T2', 'T2', '1', '0', '0', !!binary = kQE=3D]
+...
+box.sql.execute("SELECT * FROM = \"_sql_stat1\";")
+---
+- - ['T1', 'I1', '1 = 1']
+  - ['T1', 'T1', '1 1']
+  - = ['T2', 'I1', '1 1']
+  - ['T2', 'T2', '1 1']
+...
+-- Dropping an index
+box.sql.execute("DROP INDEX i1 ON t2;")
+---
+...
+-- Checking the DROP INDEX results.
+box.sql.execute("SELECT * FROM \"_sql_stat4\";")
+---
+- - ['T1', 'I1', '1', '0', '0', !!binary = kQI=3D]
+  - ['T1', 'T1', '1', '0', '0', !!binary = kQE=3D]
+  - ['T2', 'T2', '1', '0', '0', !!binary = kQE=3D]
+...
+box.sql.execute("SELECT * FROM = \"_sql_stat1\";")
+---
+- - ['T1', 'I1', '1 = 1']
+  - ['T1', 'T1', '1 1']
+  - = ['T2', 'T2', '1 1']
+...
+--Cleaning up.
+box.sql.execute("DROP TABLE t1;")
+---
+...
+box.sql.execute("DROP TABLE t2;")
+---
+...
diff --git = a/test/sql/sql-statN-index-drop.test.lua = b/test/sql/sql-statN-index-drop.test.lua
new file mode = 100644
index 0000000..9b919de
--- = /dev/null
+++ b/test/sql/sql-statN-index-drop.test.lua
@@ -0,0 +1,54 @@
+test_run =3D = require('test_run').new()
+
+-- Initializing = some things.
+box.sql.execute("CREATE TABLE t1(id PRIMARY = KEY, a);")
+box.sql.execute("CREATE TABLE t2(id PRIMARY = KEY, a);")
+box.sql.execute("CREATE INDEX i1 ON = t1(a);")
+box.sql.execute("CREATE INDEX i1 ON t2(a);")
+box.sql.execute("INSERT INTO t1 VALUES(1, 2);")
+box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
+
+-- Analyze.
+box.sql.execute("ANALYZE;")
+
+-- = Checking the data.
+box.sql.execute("SELECT * FROM = \"_sql_stat4\";")
+box.sql.execute("SELECT * FROM = \"_sql_stat1\";")
+
+-- Dropping an index

Nitpicking: put a dot at the end of = sentence.


+box.sql.execute("DROP INDEX i1 ON t1;")
+
+-- Checking the DROP INDEX results.
+box.sql.execute("SELECT * FROM \"_sql_stat4\";")
+box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+
+--Cleaning up.
+box.sql.execute("DROP TABLE t1;")
+box.sql.execute("DROP TABLE t2;")
+
+-- Same test but dropping an INDEX ON t2.
+
+box.sql.execute("CREATE TABLE t1(id PRIMARY KEY, a);")
+box.sql.execute("CREATE TABLE t2(id PRIMARY KEY, a);")
+box.sql.execute("CREATE INDEX i1 ON t1(a);")
+box.sql.execute("CREATE INDEX i1 ON t2(a);")
+box.sql.execute("INSERT INTO t1 VALUES(1, 2);")
+box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
+
+-- Analyze.
+box.sql.execute("ANALYZE;")
+
+-- = Checking the data.
+box.sql.execute("SELECT * FROM = \"_sql_stat4\";")
+box.sql.execute("SELECT * FROM = \"_sql_stat1\";")
+
+-- Dropping an index

Nitpicking: put a dot at the end of = sentence.

+box.sql.execute("DROP INDEX i1 ON t2;")
+
+-- Checking the DROP INDEX results.
+box.sql.execute("SELECT * FROM \"_sql_stat4\";")
+box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+
+--Cleaning up.
+box.sql.execute("DROP TABLE t1;")
+box.sql.execute("DROP TABLE t2;")
--
2.7.4



= --Apple-Mail=_26283FBD-5438-4A21-971B-61F6F95BBE16--