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 BDBE926259 for ; Wed, 4 Apr 2018 12:34:44 -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 NYVisoOBx76b for ; Wed, 4 Apr 2018 12:34:44 -0400 (EDT) Received: from mail-wm0-f48.google.com (mail-wm0-f48.google.com [74.125.82.48]) (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 32674206A8 for ; Wed, 4 Apr 2018 12:34:43 -0400 (EDT) Received: by mail-wm0-f48.google.com with SMTP id u189so7589011wmd.1 for ; Wed, 04 Apr 2018 09:34:43 -0700 (PDT) MIME-Version: 1.0 References: <1522791436-8221-1-git-send-email-hollow653@gmail.com> In-Reply-To: From: Hollow111 Date: Wed, 04 Apr 2018 16:34:31 +0000 Message-ID: Subject: [tarantool-patches] Re: [PATCH 2/2] sql: statistics removal after dropping an index Content-Type: multipart/alternative; boundary="f4f5e80eff906e2fef056908667c" 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 --f4f5e80eff906e2fef056908667c Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable =D1=81=D1=80, 4 =D0=B0=D0=BF=D1=80. 2018 =D0=B3. =D0=B2 19:11, n.pettik : > Now, patch looks way better. Keep fixing minor remarks. > Also, it is better to push your changes, so that I can check > Travis status (i.e. make sure that all tests have passed). > > > On 4 Apr 2018, at 18:46, Hollow111 wrote: > > > > =D1=81=D1=80, 4 =D0=B0=D0=BF=D1=80. 2018 =D0=B3. =D0=B2 17:06, n.pettik <= korablev@tarantool.org>: > >> Please, don=E2=80=99t hurry when working on patch. >> Consider carefully each fix and comment: there is no any deadline. >> >> You don=E2=80=99t have to send second patch version. >> This patch is pretty small, so you can answer to this letter and >> pin your changes. For example: you won=E2=80=99t change tests (since the= y are OK), >> so don=E2=80=99t include them in provided diff. >> >> > On 4 Apr 2018, at 00:37, N.Tatunov 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/tree/N_Tatunov/gh-3264-stat-table= -entries-removal >> > Issue: https://github.com/tarantool/tarantool/issues/3264 >> > >> > src/box/sql/build.c | 41 ++++++----- >> > test/sql/sql-statN-index-drop.result | 127 >> +++++++++++++++++++++++++++++++++ >> > test/sql/sql-statN-index-drop.test.lua | 54 ++++++++++++++ >> > 3 files changed, 206 insertions(+), 16 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..44d7548 100644 >> > --- a/src/box/sql/build.c >> > +++ b/src/box/sql/build.c >> > @@ -2207,25 +2207,34 @@ sqliteViewResetAll(sqlite3 * db) >> > #endif /* SQLITE_OMIT_VIEW */ >> > >> > /* >> > - * Remove entries from the sqlite_statN tables (for N in (1,2,3)) >> > + * Remove entries from the _sql_statN tables (for N in (1, 4)) >> > * after a DROP INDEX or DROP TABLE command. >> > */ >> >> Comment prior to function starts from /**. >> Your provide short description (it is already there), >> describe arguments and return value with @param and @retval tags. >> You can find more information by searching =E2=80=98doxygen comments=E2= =80=99. >> >> > static void >> > -sqlite3ClearStatTables(Parse * pParse, /* The parsing context *= / >> > - const char *zType, /* "idx" or "tbl" */ >> > - const char *zName /* Name of index or tabl= e >> */ >> > +sql_clear_stat_tables(Parse * pParse, /* The parsing context *= / >> >> pParse and zType are examples of Hungarian notation: >> you don=E2=80=99t need =E2=80=98p=E2=80=99 and =E2=80=98z=E2=80=99 prefi= xes. Moreover, you can remove comment >> right after arguments, since args will be described above within oxygen >> comment. >> >> > + const char *zType, /* "idx" or "tbl" */ >> > + const char *table_name, /* Name of the table*/ >> > + const char *idx_name /* Name of the 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); >> > - } >> > - } >> > + int i, j; >> >> You don=E2=80=99t need to declare vars beforehand: >> it is is required in obsolete C standards (such as KR or C89). >> However, we are using *modern* C99: >> for (int i =3D 0; =E2=80=A6) - is OK. >> >> > + if(strcmp(zType, "idx") =3D=3D 0) >> >> As you suggest, you can get rid of this =E2=80=99type=E2=80=99. Instead,= just >> check index name on nullability. >> >> > + for(i =3D 1, j =3D 1; i <=3D 2; i++, j++) { >> >> This cycle-for is completely unreadable. Don=E2=80=99t make things to be >> complicated: >> it is better to use *less* beautiful, but more obvious and clear approac= h. >> Don=E2=80=99t be afraid of changing code: this function is declared as s= tatic, >> so it is not available for public usage. >> >> I would like to suggest you to get rid of cycle and snprintf, >> but add two almost the same calls of sqlite3NestedParse(). >> It will result in plain and readable code: >> sqlite3NestedParse(=E2=80=A6, =E2=80=9D =E2=80=A6 WHERE tbl =3D stat1=E2= =80=9D); >> sqlite3NestedParse(=E2=80=A6, =E2=80=9D =E2=80=A6 WHERE tbl =3D stat4=E2= =80=9D); >> >> > + char zTab[24]; >> > + sqlite3_snprintf(sizeof(zTab), zTab, "_sql_stat%d", i * j= ); >> > + sqlite3NestedParse(pParse, >> > + "DELETE FROM \"%s\" WHERE (\"idx\"=3D%Q AND " >> > + "\"tbl\"=3D%Q)", >> > + zTab, idx_name, table_name); >> > + } >> > + else >> > + for(i =3D 1, j =3D 1; i <=3D 2; i++, j++) { >> > + char zTab[24]; >> > + sqlite3_snprintf(sizeof(zTab), zTab, "_sql_stat%d", i * j= ); >> > + sqlite3NestedParse(pParse, >> > + "DELETE FROM \"%s\" WHERE \"tbl\"=3D%Q", >> > + zTab, table_name); >> > + } >> > } >> > >> > /* >> > @@ -2415,7 +2424,7 @@ sqlite3DropTable(Parse * pParse, SrcList * pName= , >> int isView, int noErr) >> > */ >> > >> > sqlite3BeginWriteOperation(pParse, 1); >> > - sqlite3ClearStatTables(pParse, "tbl", pTab->zName); >> > + sql_clear_stat_tables(pParse, "tbl", pTab->zName, NULL); >> > sqlite3FkDropTable(pParse, pName, pTab); >> > sqlite3CodeDropTable(pParse, pTab, isView); >> > >> > @@ -3417,7 +3426,7 @@ sqlite3DropIndex(Parse * pParse, SrcList * pName= , >> Token * pName2, int ifExists) >> > * But firstly, delete statistics since schema >> > * changes after DDL. >> > */ >> > - sqlite3ClearStatTables(pParse, "idx", pIndex->zName); >> > + sql_clear_stat_tables(pParse, "idx", pIndex->pTable->zName, >> pIndex->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..c7e476f >> > --- /dev/null >> > +++ b/test/sql/sql-statN-index-drop.result >> > @@ -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..bf4a752 >> > --- /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. >> > +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. >> > +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 >> > >> > >> >> Suggested changes were applied so this is diff: > > > diff --git a/src/box/sql/build.c b/src/box/sql/build.c > index 44d7548..16ae042 100644 > --- a/src/box/sql/build.c > +++ b/src/box/sql/build.c > @@ -2206,34 +2206,35 @@ sqliteViewResetAll(sqlite3 * db) > #define sqliteViewResetAll(A,B) > #endif /* SQLITE_OMIT_VIEW */ > > -/* > +/** > * Remove entries from the _sql_statN tables (for N in (1, 4)) > * after a DROP INDEX or DROP TABLE command. > + * > + * @param table_name table to be dropped or > + * the table that contains index to be dropped > + * @param idx_name index to be dropped > > > Nitpicking: put dot at the end of comments and > start sentences from capital letter. > (This is just advice. You may skip most =E2=80=99nitpicking=E2=80=99 comm= ents.) > > Moreover, you forget to describe first arg: > even despite the fact that it is obviously =E2=80=99The parsing context'. > > */ > static void > -sql_clear_stat_tables(Parse * pParse, /* The parsing context */ > - const char *zType, /* "idx" or "tbl" */ > - const char *table_name, /* Name of the table*/ > - const char *idx_name /* Name of the index*/ > - ) > +sql_clear_stat_tables(Parse *parse, > + const char *table_name, > + const char *idx_name) > > > You don=E2=80=99t need to place each arg at new line. > The only restriction is 80 chars per line. > > { > - int i, j; > - if(strcmp(zType, "idx") =3D=3D 0) > - for(i =3D 1, j =3D 1; i <=3D 2; i++, j++) { > - char zTab[24]; > - sqlite3_snprintf(sizeof(zTab), zTab, "_sql_stat%d", i * j); > - sqlite3NestedParse(pParse, > - "DELETE FROM \"%s\" WHERE (\"idx\"=3D%Q AND " > - "\"tbl\"=3D%Q)", > - zTab, idx_name, table_name); > - } > - else > - for(i =3D 1, j =3D 1; i <=3D 2; i++, j++) { > - char zTab[24]; > - sqlite3_snprintf(sizeof(zTab), zTab, "_sql_stat%d", i * j); > - sqlite3NestedParse(pParse, > - "DELETE FROM \"%s\" WHERE \"tbl\"=3D%Q", > - zTab, table_name); > + if(idx_name) { > > > It is better to use explicit =3D=3D NULL check. > > + sqlite3NestedParse(parse, > + "DELETE FROM \"_sql_stat1\" WHERE (\"idx\"=3D%Q AND = " > + "\"tbl\"=3D%Q)", > + idx_name, table_name); > + sqlite3NestedParse(parse, > + "DELETE FROM \"_sql_stat4\" WHERE (\"idx\"=3D%Q AND = " > + "\"tbl\"=3D%Q)", > + idx_name, table_name); > + } else { > + sqlite3NestedParse(parse, > + "DELETE FROM \"_sql_stat1\" WHERE \"tbl\"=3D%Q", > + table_name); > + sqlite3NestedParse(parse, > + "DELETE FROM \"_sql_stat4\" WHERE \"tbl\"=3D%Q", > + table_name); > } > } > > @@ -2424,7 +2425,7 @@ sqlite3DropTable(Parse * pParse, SrcList * pName, > int isView, int noErr) > */ > > sqlite3BeginWriteOperation(pParse, 1); > - sql_clear_stat_tables(pParse, "tbl", pTab->zName, NULL); > + sql_clear_stat_tables(pParse, pTab->zName, NULL); > sqlite3FkDropTable(pParse, pName, pTab); > sqlite3CodeDropTable(pParse, pTab, isView); > > @@ -3426,7 +3427,7 @@ sqlite3DropIndex(Parse * pParse, SrcList * pName, > Token * pName2, int ifExists) > * But firstly, delete statistics since schema > * changes after DDL. > */ > - sql_clear_stat_tables(pParse, "idx", pIndex->pTable->zName, > pIndex->zName); > + sql_clear_stat_tables(pParse, pIndex->pTable->zName, pIndex->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/xlog/checkpoint_daemon.result > b/test/xlog/checkpoint_daemon.result > index d5ed666..1c28336 100644 > --- a/test/xlog/checkpoint_daemon.result > +++ b/test/xlog/checkpoint_daemon.result > @@ -96,11 +96,11 @@ fiber.sleep(3 * PERIOD) > -- check that it's not first snapshot > test_run:grep_log("default", "saving snapshot", 400) =3D=3D nil > --- > -- true > +- false > ... > test_run:grep_log("default", "making snapshot", 400) ~=3D nil > --- > -- true > +- false > > > I guess, this diff doesn=E2=80=99t belong to the patch. > > ... > -- restore default options > box.cfg{checkpoint_interval =3D 3600 * 4, checkpoint_count =3D 4 } > > > > Diff after the changes: diff --git a/src/box/sql/build.c b/src/box/sql/build.c index 16ae042..958927e 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -2210,16 +2210,17 @@ sqliteViewResetAll(sqlite3 * db) * Remove entries from the _sql_statN tables (for N in (1, 4)) * after a DROP INDEX or DROP TABLE command. * - * @param table_name table to be dropped or - * the table that contains index to be dropped - * @param idx_name index to be dropped + * @param parse The parsing context. + * @param table_name The table to be dropped or + * the table that contains index to be dropped. + * @param idx_name Index to be dropped. */ static void -sql_clear_stat_tables(Parse *parse, - const char *table_name, - const char *idx_name) +sql_clear_stat_tables(Parse *parse, const char *table_name, + const char *idx_name + ) { - if(idx_name) { + if(idx_name !=3D NULL) { sqlite3NestedParse(parse, "DELETE FROM \"_sql_stat1\" WHERE (\"idx\"=3D%Q AND " "\"tbl\"=3D%Q)", --f4f5e80eff906e2fef056908667c Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


=D1=81= =D1=80, 4 =D0=B0=D0=BF=D1=80. 2018 =D0=B3. =D0=B2 19:11, n.pettik <korablev@tarantool.org>:
Now, patch looks way better. Keep fixing minor remarks.
A= lso, it is better to push your changes, so that I can check
Travi= s status (i.e. make sure that all tests have passed).

<= div>
On 4 Apr 2018, at 18:46, Hollow= 111 <hollow653@= gmail.com> wrote:



=D1=81=D1=80, 4 =D0=B0=D0=BF=D1=80. 2018 =D0=B3. =D0= =B2 17:06, n.pettik <korablev@tarantool.org>:
Please, don=E2=80=99t hurry when working on patch.=
Consider carefully each fix and comment: there is no any deadline.

You don=E2=80=99t have to send second patch version.
This patch is pretty small, so you can answer to this letter and
pin your changes. For example: you won=E2=80=99t change tests (since they a= re OK),
so don=E2=80=99t include them in provided diff.

> On 4 Apr 2018, at 00:37, 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/tree/N_Tatunov/gh-3264-stat-table-e= ntries-removal
> Issue: https://github.com/tarantool/tarantool= /issues/3264
>
> src/box/sql/build.c=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 |=C2=A0 41 ++++++-----
> test/sql/sql-statN-index-drop.result=C2=A0 =C2=A0| 127 +++++++++++++++= ++++++++++++++++++
> test/sql/sql-statN-index-drop.test.lua |=C2=A0 54 ++++++++++++++
> 3 files changed, 206 insertions(+), 16 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..44d7548 100644
> --- a/src/box/sql/build.c
> +++ b/src/box/sql/build.c
> @@ -2207,25 +2207,34 @@ sqliteViewResetAll(sqlite3 * db)
> #endif=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 /* SQLITE_OMIT_VIEW */
>
> /*
> - * Remove entries from the sqlite_statN tables (for N in (1,2,3))
> + * Remove entries from the _sql_statN tables (for N in (1, 4))
>=C2=A0 * after a DROP INDEX or DROP TABLE command.
>=C2=A0 */

Comment prior to function starts from /**.
Your provide short description (it is already there),
describe arguments and return value with @param and @retval tags.
You can find more information by searching =E2=80=98doxygen comments=E2=80= =99.

> static void
> -sqlite3ClearStatTables(Parse * pParse,=C2=A0 =C2=A0 =C2=A0 =C2=A0/* T= he parsing context */
> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= const char *zType,=C2=A0 =C2=A0 =C2=A0 =C2=A0/* "idx" or "t= bl" */
> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= const char *zName=C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Name of index or table */<= br> > +sql_clear_stat_tables(Parse * pParse,=C2=A0 =C2=A0 =C2=A0 =C2=A0 /* T= he parsing context */

pParse and zType are examples of Hungarian notation:
you don=E2=80=99t need =E2=80=98p=E2=80=99 and =E2=80=98z=E2=80=99 prefixes= . Moreover, you can remove comment
right after arguments, since args will be described above within oxygen com= ment.

> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= const char *zType,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* "idx&qu= ot; or "tbl" */
> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0const char *ta= ble_name,=C2=A0 /* Name of the table*/
> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= const char *idx_name=C2=A0 =C2=A0 =C2=A0/* Name of the index*/
>=C2=A0 =C2=A0 =C2=A0)
> {
> -=C2=A0 =C2=A0 =C2=A0int i;
> -=C2=A0 =C2=A0 =C2=A0for (i =3D 1; i <=3D 4; i++) {
> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0char zTab[24];
> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0sqlite3_snprintf(size= of(zTab), zTab, "_sql_stat%d", i);
> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (sqlite3FindTable(= pParse->db, zTab)) {
> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0sqlite3NestedParse(pParse,
> -=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=A0 =C2=A0 =C2=A0 &quo= t;DELETE FROM \"%s\" WHERE \"%s\"=3D%Q",
> -=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=A0 =C2=A0 =C2=A0 zTab= , zType, zName);
> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}
> -=C2=A0 =C2=A0 =C2=A0}
> +=C2=A0 =C2=A0 int i, j;

You don=E2=80=99t need to declare vars beforehand:
it is is required in obsolete C standards (such as KR or C89).
However, we are using *modern* C99:
for (int i =3D 0; =E2=80=A6)=C2=A0 - is OK.

> +=C2=A0 =C2=A0 if(strcmp(zType, "idx") =3D=3D 0)

As you suggest, you can get rid of this =E2=80=99type=E2=80=99. Instead, ju= st
check index name on nullability.

> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 for(i =3D 1, j =3D 1; i <=3D 2; i++, j= ++) {

This cycle-for is completely unreadable. Don=E2=80=99t make things to be co= mplicated:
it is better to use *less* beautiful, but more obvious and clear approach.<= br> Don=E2=80=99t be afraid of changing code: this function is declared as stat= ic,
so it is not available for public usage.

I would like to suggest you to get rid of cycle and snprintf,
but add two almost the same calls of sqlite3NestedParse().
It will result in plain and readable code:
sqlite3NestedParse(=E2=80=A6, =E2=80=9D =E2=80=A6 WHERE tbl =3D stat1=E2=80= =9D);
sqlite3NestedParse(=E2=80=A6, =E2=80=9D =E2=80=A6 WHERE tbl =3D stat4=E2=80= =9D);

> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 char zTab[24];
> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 sqlite3_snprintf(sizeof(zTa= b), zTab, "_sql_stat%d", i * j);
> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 sqlite3NestedParse(pParse,<= br> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 "DELETE FROM \"%s\" WHERE (\"idx\"= =3D%Q AND "
> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 "\"tbl\"=3D%Q)",
> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 zTab, idx_name, table_name);
> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
> +=C2=A0 =C2=A0 else
> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 for(i =3D 1, j =3D 1; i <=3D 2; i++, j= ++) {
> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 char zTab[24];
> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 sqlite3_snprintf(sizeof(zTa= b), zTab, "_sql_stat%d", i * j);
> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 sqlite3NestedParse(pParse,<= br> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 "DELETE FROM \"%s\" WHERE \"tbl\"= =3D%Q",
> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 zTab, table_name);
> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
> }
>
> /*
> @@ -2415,7 +2424,7 @@ sqlite3DropTable(Parse * pParse, SrcList * pName= , int isView, int noErr)
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 */
>
>=C2=A0 =C2=A0 =C2=A0 =C2=A0sqlite3BeginWriteOperation(pParse, 1);
> -=C2=A0 =C2=A0 =C2=A0sqlite3ClearStatTables(pParse, "tbl", p= Tab->zName);
> +=C2=A0 =C2=A0 sql_clear_stat_tables(pParse, "tbl", pTab->= ;zName, NULL);
>=C2=A0 =C2=A0 =C2=A0 =C2=A0sqlite3FkDropTable(pParse, pName, pTab);
>=C2=A0 =C2=A0 =C2=A0 =C2=A0sqlite3CodeDropTable(pParse, pTab, isView);<= br> >
> @@ -3417,7 +3426,7 @@ sqlite3DropIndex(Parse * pParse, SrcList * pName= , Token * pName2, int ifExists)
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 * But firstly, delete statistics since sche= ma
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 * changes after DDL.
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 */
> -=C2=A0 =C2=A0 =C2=A0sqlite3ClearStatTables(pParse, "idx", p= Index->zName);
> +=C2=A0 =C2=A0 sql_clear_stat_tables(pParse, "idx", pIndex-&= gt;pTable->zName, pIndex->zName);
>=C2=A0 =C2=A0 =C2=A0 =C2=A0int record_reg =3D ++pParse->nMem;
>=C2=A0 =C2=A0 =C2=A0 =C2=A0int space_id_reg =3D ++pParse->nMem;
>=C2=A0 =C2=A0 =C2=A0 =C2=A0sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_PAGE= NO_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..c7e476f
> --- /dev/null
> +++ b/test/sql/sql-statN-index-drop.result
> @@ -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\";")<= br> > +---
> +- - ['T1', 'I1', '1', '0', '0'= ;, !!binary kQI=3D]
> +=C2=A0 - ['T1', 'T1', '1', '0', '= 0', !!binary kQE=3D]
> +=C2=A0 - ['T2', 'I1', '1', '0', '= 0', !!binary kQI=3D]
> +=C2=A0 - ['T2', 'T2', '1', '0', '= 0', !!binary kQE=3D]
> +...
> +box.sql.execute("SELECT * FROM \"_sql_stat1\";")<= br> > +---
> +- - ['T1', 'I1', '1 1']
> +=C2=A0 - ['T1', 'T1', '1 1']
> +=C2=A0 - ['T2', 'I1', '1 1']
> +=C2=A0 - ['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\";")<= br> > +---
> +- - ['T1', 'T1', '1', '0', '0'= ;, !!binary kQE=3D]
> +=C2=A0 - ['T2', 'I1', '1', '0', '= 0', !!binary kQI=3D]
> +=C2=A0 - ['T2', 'T2', '1', '0', '= 0', !!binary kQE=3D]
> +...
> +box.sql.execute("SELECT * FROM \"_sql_stat1\";")<= br> > +---
> +- - ['T1', 'T1', '1 1']
> +=C2=A0 - ['T2', 'I1', '1 1']
> +=C2=A0 - ['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\";")<= br> > +---
> +- - ['T1', 'I1', '1', '0', '0'= ;, !!binary kQI=3D]
> +=C2=A0 - ['T1', 'T1', '1', '0', '= 0', !!binary kQE=3D]
> +=C2=A0 - ['T2', 'I1', '1', '0', '= 0', !!binary kQI=3D]
> +=C2=A0 - ['T2', 'T2', '1', '0', '= 0', !!binary kQE=3D]
> +...
> +box.sql.execute("SELECT * FROM \"_sql_stat1\";")<= br> > +---
> +- - ['T1', 'I1', '1 1']
> +=C2=A0 - ['T1', 'T1', '1 1']
> +=C2=A0 - ['T2', 'I1', '1 1']
> +=C2=A0 - ['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\";")<= br> > +---
> +- - ['T1', 'I1', '1', '0', '0'= ;, !!binary kQI=3D]
> +=C2=A0 - ['T1', 'T1', '1', '0', '= 0', !!binary kQE=3D]
> +=C2=A0 - ['T2', 'T2', '1', '0', '= 0', !!binary kQE=3D]
> +...
> +box.sql.execute("SELECT * FROM \"_sql_stat1\";")<= br> > +---
> +- - ['T1', 'I1', '1 1']
> +=C2=A0 - ['T1', 'T1', '1 1']
> +=C2=A0 - ['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-sta= tN-index-drop.test.lua
> new file mode 100644
> index 0000000..bf4a752
> --- /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\";")<= br> > +box.sql.execute("SELECT * FROM \"_sql_stat1\";")<= br> > +
> +-- Dropping an index.
> +box.sql.execute("DROP INDEX i1 ON t1;")
> +
> +-- Checking the DROP INDEX results.
> +box.sql.execute("SELECT * FROM \"_sql_stat4\";")<= br> > +box.sql.execute("SELECT * FROM \"_sql_stat1\";")<= br> > +
> +--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\";")<= br> > +box.sql.execute("SELECT * FROM \"_sql_stat1\";")<= br> > +
> +-- Dropping an index.
> +box.sql.execute("DROP INDEX i1 ON t2;")
> +
> +-- Checking the DROP INDEX results.
> +box.sql.execute("SELECT * FROM \"_sql_stat4\";")<= br> > +box.sql.execute("SELECT * FROM \"_sql_stat1\";")<= br> > +
> +--Cleaning up.
> +box.sql.execute("DROP TABLE t1;")
> +box.sql.execute("DROP TABLE t2;")
> --
> 2.7.4
>
>

Suggested changes were applied so this is diff:
=C2=A0
diff --git a/src/box/sql/build.c b/src/box/sql/buil= d.c
index 44d7548..16ae042 100644
--- a/src/box/sql/bui= ld.c
+++ b/src/box/sql/build.c
@@ -2206,34 +2206,35 @@ = sqliteViewResetAll(sqlite3 * db)
=C2=A0#define sqliteViewResetAll= (A,B)
=C2=A0#endif /* SQLITE_OMIT_VIEW */
=C2=A0
-/*
+/**
=
=C2=A0 * Remove entries from the _sql_statN tables (for N in (1, 4))
=C2=A0 * after a DROP INDEX or DROP TABLE command.
+ *= =C2=A0
+ * @param table_name table to be dropped or
+ *= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0the ta= ble that contains index to be dropped
+ * @param idx_name index t= o be dropped

= Nitpicking: put dot at the end of comments and
start sentences fr= om capital letter.
(This is just advice. You may skip most =E2=80= =99nitpicking=E2=80=99 comments.)

Moreover, you fo= rget to describe first arg:
even despite the fact that it is obvi= ously =E2=80=99The parsing context'.=C2=A0

=C2=A0 */=
=C2=A0static void
-sql_clear_stat_tables(Parse * pPars= e, /* The parsing context */
- =C2=A0 =C2=A0 =C2=A0= =C2=A0const char *zType, =C2= =A0 =C2=A0 /* "idx" or "tbl" */
-=C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0const char *table_name,=C2=A0 = /* Name of the table*/
- =C2=A0 =C2=A0 =C2=A0 =C2=A0const char *idx_name /* Name of the index*/
-=C2=A0 =C2=A0 )
+sql_clear_stat_tables(Parse *parse,
+=C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0const char *table_name,
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0const c= har *idx_name)

You don=E2=80=99t need to place each arg at new line.
The only = restriction is 80 chars per line.

<= div dir=3D"ltr">
=C2=A0{
-=C2= =A0 =C2=A0 int i, j;
-=C2=A0 =C2=A0 if(strcmp(zType, "idx&qu= ot;) =3D=3D 0)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 for(i =3D 1, j =3D 1;= i <=3D 2; i++, j++) {
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 char zTab[24];
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 sql= ite3_snprintf(sizeof(zTab), zTab, "_sql_stat%d", i * j);
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 sqlite3NestedParse(pParse,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 "DELETE FROM \"%s\" WHERE (\"idx\&quo= t;=3D%Q AND "
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "\"tbl\"=3D%Q)",=
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 zTab, idx_name, table_name);
-=C2=A0 =C2=A0 = =C2=A0 =C2=A0 }
-=C2=A0 =C2=A0 else
-=C2=A0 =C2=A0 =C2= =A0 =C2=A0 for(i =3D 1, j =3D 1; i <=3D 2; i++, j++) {
-=C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 char zTab[24];
-=C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 sqlite3_snprintf(sizeof(zTab), zTab, "_sq= l_stat%d", i * j);
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 sqlite3NestedParse(pParse,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "DELETE FROM \&qu= ot;%s\" WHERE \"tbl\"=3D%Q",
-=C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 zTab,= table_name);
+=C2=A0 =C2=A0 if(idx_name) {

It is better to use explicit =3D= =3D NULL check.

+=C2=A0 =C2=A0 =C2=A0 =C2=A0 sqlite3Nest= edParse(parse,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 "DELETE FROM \"_sql_stat1\" WHERE (\&qu= ot;idx\"=3D%Q AND "
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "\"tbl\"=3D%Q)",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 idx_name, table_name);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 sqlite3Ne= stedParse(parse,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 "DELETE FROM \"_sql_stat4\" WHERE (= \"idx\"=3D%Q AND "
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "\"tbl\"=3D%Q)&quo= t;,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 idx_name, table_name);
+=C2=A0 =C2=A0 } else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 sqlite3NestedParse(parse,
+=C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "DELET= E FROM \"_sql_stat1\" WHERE \"tbl\"=3D%Q",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 t= able_name);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 sqlite3NestedParse(parse= ,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 "DELETE FROM \"_sql_stat4\" WHERE \"tbl\"= =3D%Q",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 table_name);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0}
=C2=A0}
=C2=A0
@@ -2424,7 +2425,7 @@ sql= ite3DropTable(Parse * pParse, SrcList * pName, int isView, int noErr)
=
=C2=A0 */
=C2= =A0
=C2=A0 sqlite3Beg= inWriteOperation(pParse, 1);
-=C2=A0 =C2=A0 sql_clear_stat_tables= (pParse, "tbl", pTab->zName, NULL);
+=C2=A0 =C2=A0 s= ql_clear_stat_tables(pParse, pTab->zName, NULL);
=C2=A0 sqlite3FkDropTable(pParse, pName, pTa= b);
=C2=A0 sqlite3Cod= eDropTable(pParse, pTab, isView);
=C2=A0
@@ -3426,7 +34= 27,7 @@ sqlite3DropIndex(Parse * pParse, SrcList * pName, Token * pName2, i= nt ifExists)
=C2=A0 = * But firstly, delete statistics since schema
=C2=A0 * changes after DDL.
=C2=A0 */
-=C2=A0 =C2=A0 sql_= clear_stat_tables(pParse, "idx", pIndex->pTable->zName, pIn= dex->zName);
+=C2=A0 =C2=A0 sql_clear_stat_tables(pParse, pInd= ex->pTable->zName, pIndex->zName);
=C2=A0 int record_reg =3D ++pParse->nMem;
=C2=A0 int space_id_reg =3D = ++pParse->nMem;
=C2=A0 sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_PAGENO_TO_SPACEID(pIndex->t= num),
diff --git a/test/xlog/checkpoint_daemon.result b/test/xlog= /checkpoint_daemon.result
index d5ed666..1c28336 100644
--- a/test/xlog/checkpoint_daemon.result
+++ b/test/xlog/checkpo= int_daemon.result
@@ -96,11 +96,11 @@ fiber.sleep(3 * PERIOD)
=C2=A0-- check that it's not first snapshot
=C2=A0test= _run:grep_log("default", "saving snapshot", 400) =3D=3D= nil
=C2=A0---
-- true
+- false
=C2= =A0...
=C2=A0test_run:grep_log("default", "making = snapshot", 400) ~=3D nil
=C2=A0---
-- true
+- false

I= guess, this diff doesn=E2=80=99t belong to the patch.

= =C2=A0...
=C2=A0-- restore default options
=C2=A0box.cf= g{checkpoint_interval =3D 3600 * 4, checkpoint_count =3D 4 }
=C2=A0

Diff after the c= hanges:

diff --git a/src/box/sql/build.c b/src/box/sql/build.c<= /div>
index 16ae042..958927e 100644
--- a/src/box/sql/build.c=
+++ b/src/box/sql/build.c
@@ -2210,16 +2210,17 @@ sqli= teViewResetAll(sqlite3 * db)
=C2=A0 * Remove entries from the _sq= l_statN tables (for N in (1, 4))
=C2=A0 * after a DROP INDEX or D= ROP TABLE command.
=C2=A0 *=C2=A0
- * @param table_name= table to be dropped or
- *=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0the table that contains index to be dropped<= /div>
- * @param idx_name index to be dropped
+ * @param pars= e=C2=A0 =C2=A0 =C2=A0 The parsing context.
+ * @param table_name = The table to be dropped or
+ *=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0the table that contains index to be dropp= ed.
+ * @param idx_name=C2=A0 =C2=A0Index to be dropped.
=C2=A0 */
=C2=A0static void
-sql_clear_stat_tables(Pa= rse *parse,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0const char *table_name,
- =C2=A0 =C2=A0 =C2=A0 =C2=A0const char *idx_name)
+sql_clear_s= tat_tables(Parse *parse, const char *table_name,=C2=A0
+=C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 const= char *idx_name
+=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 if= (idx_name) {
+=C2=A0 =C2=A0 if(idx_name !=3D NULL) {
= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0sqlite3NestedParse(parse,
=C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0&q= uot;DELETE FROM \"_sql_stat1\" WHERE (\"idx\"=3D%Q AND = "
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0"\"tbl\"=3D%Q)",
=C2=A0
<= /div>
--f4f5e80eff906e2fef056908667c--