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 C2CA32C200 for ; Wed, 4 Apr 2018 12:11:24 -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 yrVt5WiCwOMG for ; Wed, 4 Apr 2018 12:11:24 -0400 (EDT) Received: from smtp20.mail.ru (smtp20.mail.ru [94.100.179.251]) (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 276B329439 for ; Wed, 4 Apr 2018 12:11:23 -0400 (EDT) From: "n.pettik" Message-Id: Content-Type: multipart/alternative; boundary="Apple-Mail=_A42A8C41-1068-4C7A-82DE-B704E25D528D" Mime-Version: 1.0 (Mac OS X Mail 10.3 \(3273\)) Subject: [tarantool-patches] Re: [PATCH 2/2] sql: statistics removal after dropping an index Date: Wed, 4 Apr 2018 19:11:21 +0300 In-Reply-To: References: <1522791436-8221-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: Hollow111 Cc: tarantool-patches@freelists.org --Apple-Mail=_A42A8C41-1068-4C7A-82DE-B704E25D528D Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 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: >=20 >=20 >=20 > =D1=81=D1=80, 4 =D0=B0=D0=BF=D1=80. 2018 =D0=B3. =D0=B2 17:06, = n.pettik >: > Please, don=E2=80=99t hurry when working on patch. > Consider carefully each fix and comment: there is no any deadline. >=20 > 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 are OK), > so don=E2=80=99t include them in provided diff. >=20 > > 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-e= ntries-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. > > */ >=20 > 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. >=20 > > static void > > -sqlite3ClearStatTables(Parse * pParse, /* The parsing context = */ > > - const char *zType, /* "idx" or "tbl" */ > > - const char *zName /* Name of index or = table */ > > +sql_clear_stat_tables(Parse * pParse, /* The parsing context = */ >=20 > 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 comment. >=20 > > + 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; >=20 > 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. >=20 > > + if(strcmp(zType, "idx") =3D=3D 0) >=20 > As you suggest, you can get rid of this =E2=80=99type=E2=80=99. = Instead, just > check index name on nullability. >=20 > > + for(i =3D 1, j =3D 1; i <=3D 2; i++, j++) { >=20 > 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 = approach. > Don=E2=80=99t be afraid of changing code: this function is declared as = static, > so it is not available for public usage. >=20 > 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); >=20 > > + 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 > > > > >=20 > Suggested changes were applied so this is diff: >=20 > =20 > 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 */ > =20 > -/* > +/** > * Remove entries from the _sql_statN tables (for N in (1, 4)) > * after a DROP INDEX or DROP TABLE command. > + *=20 > + * @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 = comments.) Moreover, you forget to describe first arg: even despite the fact that it is obviously =E2=80=99The parsing = context'.=20 > */ > 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); > } > } > =20 > @@ -2424,7 +2425,7 @@ sqlite3DropTable(Parse * pParse, SrcList * = pName, int isView, int noErr) > */ > =20 > 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); > =20 > @@ -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 } > =20 --Apple-Mail=_A42A8C41-1068-4C7A-82DE-B704E25D528D Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=utf-8 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 <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 are 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-s= tat-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 = table */
> +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 = prefixes. 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 = approach.
Don=E2=80=99t be afraid of changing code: this function is declared as = static,
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 = comments.)

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 }
 

= --Apple-Mail=_A42A8C41-1068-4C7A-82DE-B704E25D528D--