Tarantool development patches archive
 help / color / mirror / Atom feed
* [tarantool-patches] [PATCH v2 1/1] sql: hold in stat tables space/index id instead of name
@ 2018-08-31 10:39 imeevma
  2018-09-05 23:13 ` [tarantool-patches] " n.pettik
  0 siblings, 1 reply; 17+ messages in thread
From: imeevma @ 2018-08-31 10:39 UTC (permalink / raw)
  To: tarantool-patches, korablev; +Cc: v.shpilevoy

To avoid problems with table and index renaming it is good idea
to save ids of tables and indexes instead of their names. Ids of
tables and indexes are fixed values.

Closes 3242
---
Branch: https://github.com/tarantool/tarantool/tree/imeevma/gh-3242-in-stat-tables-hold-ids
Issue: https://github.com/tarantool/tarantool/issues/3242

 src/box/bootstrap.snap                  | Bin 1888 -> 1883 bytes
 src/box/lua/upgrade.lua                 |  12 ++--
 src/box/schema.cc                       |  16 ++---
 src/box/sql.c                           |   8 +--
 src/box/sql/analyze.c                   | 120 ++++++++++----------------------
 src/box/sql/build.c                     |  91 +++++++++---------------
 src/box/sql/sqliteInt.h                 |  10 +--
 test/box-py/bootstrap.result            |  17 ++---
 test/box/access_misc.result             |  11 +--
 test/box/alter.result                   |   6 +-
 test/sql-tap/analyze1.test.lua          | 100 +++++++++++++-------------
 test/sql-tap/analyze4.test.lua          |  79 +++++++++++----------
 test/sql-tap/analyze5.test.lua          |  11 +--
 test/sql-tap/analyze9.test.lua          | 115 ++++++++++++------------------
 test/sql-tap/analyzeC.test.lua          |  49 ++++++++-----
 test/sql-tap/analyzeD.test.lua          |  24 +++----
 test/sql-tap/gh-3350-skip-scan.test.lua |  15 ++--
 test/sql/sql-statN-index-drop.result    |  72 +++++++++----------
 test/sql/sql-statN-index-drop.test.lua  |  16 ++---
 test/sql/upgrade.result                 |  14 ++--
 20 files changed, 359 insertions(+), 427 deletions(-)

diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap
index 65739384a66d6ba4a538553ccf4677536ba15280..d30edc52ca54af34fddda421155f1fd52279db35 100644
GIT binary patch
delta 1879
zcmV-d2dMbq4%-fp8Gku2Gc9LgH#0e6IXDVQZgX^DZewLSAZ24RVKy-~IW1*nWnnEe
zFk~<-IXPrGEjBr3FfchYIWRCWHwsomY;R+0Iv{&}3JTS_3%bn);sDM)R`=7T00000
z04TLD{QywSHUK(HC=f}|SWN){Up&AU%!u&-$_+F?r{WElHh+oxA<SM)JYdKg)Y=l6
zlA4sXYmx~0l1?iL#YPDNesuTOj>WZE)^IMCE>u0pMxM%)Qt1HV0QUge@|^S|cwZ$<
zUl|GeA~KfFyK~x5%*YJtu6LE!^)1Y8jkXB5d*AXwaqOk$pzM3i%3P1O7h!$&W&8Mw
z-1e@kXG2Bw%YRlJ!_Be@%?HC)5qv?_?fZlO+3)xM)qfU*>HM-#D5XkBx|aZ4og{2u
zSKeZ+)mi#?=Qg2?Em8%*)kzvrk|Tu+8T~KBS&ub()VXk6o#dodhneqx)sAp=l1o(S
z%Qon`Xq2my#5ni_VwK84>F9W-5I8E7>E=7-m~K=z*MFq)bW$Mj{V-hF-}wmQTC8iK
zb5>~?(jbH7#7-6~JX8SGITT!-B<sQa%3Hi$@M2E8*^ZxxDopwhyS|%Md*v<KlA;_K
z?dRjE0aqv4>f<%q&qrHQkHHbF$JNWyXiW=OCpq@#N3b4Owxl!*j$XD#I|ciSWtJTU
z{im~~(SMl)NnRF-${^_XvrQ<416-Y?z7K|>3dFE);fu?@Z|7yu)!8N6&-$)&P5jPh
zN%W6D&+U!I7y$bG;xJx&*&2l@(64KUD;n#9tFr4c#P$O}uab0T>8ByFH2Ok-tCJM3
zOUAnx&Mpo?nh5LrHBeYCInrV77X(9Wk*Lc6u76H)%7dy4f@3_Xoz#gKt)fg!R0%Ft
zgd%YoaT0M#c_=T0r&9x>CWoj7hvtUn#MMa>(Rkg5SZ6b1ip_YC=^(>Frl!Sjivmr|
zh8PVo8DKELT!67q>q|#tL{jL>%u5AIU1YdANf!z;Ml|KepR7lk(EskU*+P|$x|tZR
zPJc293^CIE#8HR07Vg(o@|>i9U-9;VxrLK-o$u~khu9Wb*(UUxz|~2*E~kCEgZk&S
z>?kLQgwAI{UzUNYE<5$xxdnf?MWR4pzVCOxewO8#Nm7v$isOZ=lPqK3FV^Gk_C|do
zaCMSJELPs#+Gr01u1<1{SsUGX@XA|Exqk#9PM(*-NqOEXrDIBcsg%k4yd2a0e^W~5
zq;qfZW4J<N-Hxilr9*Z&TYZG2f}DWV{OEkh^q??2xH`$|LY2z8gX%DMtjCqDQlk!~
zV`*ur3eCaQNxsbI`(Ig)HXK}?B!D`pQHQ#*UmST!E7-sp6n`gCuEvAmp5Y9tMt_k;
zt5N6C(Ao9flF}Jmon#5tswKiW$n$~)?oh+W1Hoi;PkaINuhuKd>9)8@ye)ha+Rw*X
z=RdMaI*mF{23IG^lLLl~hAW+I_VdxkCMZ-rq133Oi9tUYT%Ba>dl4X36vQV6#D$Ia
z;Ar8)g7&QFQ9*Z7?4U>mfo!PpV1Hz&1Vj)300Bq<(FF%F=!ymsV6Z@rgD{X`7z#rG
zisBkaKmZIv3TP0B1p{Ub#RIxGdx=&sAZ$7Z?Ls|h7pisO1JkORtvo%l9=6Ot4A)DC
z9T3<MMKBcogD(e3q%89k6D+$t4_8d==;7CzR~s@}v%|rQ_2edz6gOuU1b=KRQ>aqk
zZipYX`EEuts;Uq6Db>n+-m_Mz`xB^`>eh?FBV0(C`GIS!HLN!-^vEhi<EaxzS2tQ!
z1&&ZGz#cJmF<S<MgJu&NYkYO-?B_x}-7{~HB-$T+De73Qj5g>7GvFtPNpyq!2Agg;
zk7#9pE$9+^vR4yj<_JK>h=1Ed=fpnwJi>8H0WhSFmlnn#d5G7BerEFFS!7FS8iG$!
zkgT-D;py4T?iX7Nf+hlFBHijV!rzawgyFk<MvgW`bl!HJkI^6?Crh@MDVc<)RwZoF
z|KD1(4rTXo<G?n&d}zw(n4F7>j3&0H6dKoAr7&R71v$df7WxH9q<<zBinf+Z9PAQ(
z3&w3XnpGd{$FBCSNwpd&nzl4@D8n$4w7p=u)Pk@-CQ3!ZxDh{|TNi8+ed_!o=`~{D
z2rehPYVvK>!TwkEHPWI)>fdU5aEH^2;MOqR?rXEOg{Yz&ha3^1T$wmb4fMZOA=rN*
zQ(}<lw&vyKWUeCF9e*WSTq3d*!cm+<?ntY(oJQTi-%Jit(k5Yq>0F-peJxHjg@IEv
zPCK_@$eAvmGqGntQ8VG42oM-@w^q(4SqWzucLLzN_~<TJ`lnttE@&WObRRGIEj42l
zAbN`q*R+$#;*j2_ER788R<CG<_}`zEjUpC_-qn*NQ^g;oy-<^OQ#d@$pMX+|o8(q>
z_&`h)L~9VGz>tKJ>=pBfqpT5+AHy1@Nb(8M_;G^AnH;I<qNR9BKs)^dy-t>N4hy~=
Rq0ZN1dy+6hI@J)Z?Ft=Wj;a6v

delta 1865
zcmV-P2e$az4&V-u8Gkr8F)e2^GcaK>HD+RCWo2XvNp5p=VQyn(Iv_JOH85i~GdV3Y
zFk)gYG&W{4EjTbaH7z(bV=y;4HDfV0VKWL=Lu_wjYdRo%eF_TIx(m9^2IBzE8B5kp
zr2qf`001bpFZ}>e)iwY+OEeHlz*tcMD2k#eiXtk`qKvM%V1EiWL)em3b`#*lvi_S6
zVolVDM5d%DC9y8u`%99#NGLWYcx^TOZG^7Pxv_?GG5Ax_=-4Q{OeqEF0OA1m0NV15
z^dop<rA%KMDf=Nbm(II$o6wBN1nRJdl^6Ce%<YY`2)KLS@<DOzrRE^*d+o|xkGL0Q
zz4m4M_=+6&uz#y-LPYe-Rvg3a!UfF-!`BdeL00bjga6s@_x{#@7L@56vrZ_bQ;c{n
z0lGR#*}ky6#oMd9^zY7bK^fbl3ZSc#B!c9|2^KW^Ux>3FZ`8<Z>AE_}NvjSs-ygdb
z>FOkt=#(#8pbN9nu1@mW+_YLasnmDMxhdJOY*wYu#((CfbTZwWQceqnaZoun7_98?
zd<5}r=CRN@sjvt!Nx~7NuT<HQ0-#QzU7e)s!5qt5%w6zeZp+z@UkEEi`VYIl+jWcO
zE!vW*92D*6<EcScC;95*E!xjVTT&B)B3O^Dn6*(Fmaa~6?9Yo}J+^E~XBHU6?2UQ~
z_7%%aGk*g5PiJeRG6{~v>=BJY(C=rLPzndSI!S#W3_})(W&gq#mwn&POQ5SWjJDtP
zUFVzlo!5@&AAO$N9ECCP^Et+4{1&q}`cj}@*bZAZ*9BW;*JFt72YzlL>Fm;PGh%Jj
zg+Ny)DP9<jcQLG89D+7c*7xh5uuNj4!`v?jhJV-|(Ut*Son({;Sr-JyE1gw|nXRfw
zOx1{PMx+vP3ULB)TA5U)91{*risojB1~p?dbL#3Ohh|LPhnZI+Q<}+mi0Kf+A%^C~
z?~B3=%mx?@Fd1Gjyj*y(P^*haXG2h^i;PPJN?T&OI>{Cab2b#^N1v=mThRaRwaG%K
z7=L*(FkPJ_5*Xs6`-!6tb1&R4tmGL<|Gr}G1M>?X={n!txel=%(y~kFH=(POgk5g?
zbPd!rpVu^_n;#N7uK|6T2C_Qs)Nkh&^x^i1`gr-i-~IYsmS-hML`<j+m#$8-oPEDo
zkGtC(?TOIUNfNnSd3TGWJP^7%$#G_JRDb6|EN^ku5~z!F!l_k`38z%0)XJnPp;|aM
zB^JwsDy>jB*py14Fj%28Plr|E(jYpWtvx_iKTbYqdU!lycF-3cU7ciYp;OA5gX%DM
ztj89v(jrfVBPl6~3cb<QNxIDE`(s&;I2&D^B!D_;k*BJ$UmST!E6~6h6n`VpZhyvu
z;jZBfvPRKHYmsM?p)>5eC8aUCI>`~P)=QLeaHoX|++hZf|B=DyCb7lO$6m1~r~Be1
z@xE{_Xg?ooo&V@6=`8Y0FuFR)of|G_G;HZyv!9POH9#S&38h8e%nSOt=;|cD76E2N
zKYC_9Sl}p+jTJpAC{GF=6jbMgjs=O;PJwHajRY8fjH57)fe;8o5Q;(@M?e4!LJBm9
zS}=4B#RIxEn21&|AZ$7Z?Ls|h7pisO1IDVER+`Qj9=66n4Ax6$9T30}nP4dNgDek{
zFxl`FBslhR60Tq@(ZZLtuQp^{vqQ&gb?ZWLR5xRmglikvbEc)^3I6iQ7eRo4wKc!x
zbL5_XD}BUitf~4ZsJQB)7ssVt2(|gSE3DY9H$3z}Dn#+AE5}x0QB{Y(QY1ppnfjY8
zg4#j<5*mM8HEecrA(8Ikqev3<k3JK1x3;qxnn!p189&8r>Cdofp75r&6U;%E*pqym
z)G|W^Qr6bCIw$a?)wqpYq3Nt1b6SYuc?j-*hCX8Y%|T{P;49r{?o1on@bi8)oBPF9
zf`AY4tWLK#jqvvymdJjW&q&pl2$Gkb{W01JNMy<P(jtrSp>+sb^#5<Gxq-3+uxK$2
zDgT)aGA48}(a@B)ltSY&YwAJ@xgf_^+M@m<ukL~vOV53E$k##HGZ*agm8@DX`M_9z
z9D!3&n$*al8pBA^_JZkBdtiTnC>0IkNc{M0jo2mn-1*I<*E|D9vz++alW$Z9`b*V!
zPKy$$f2-4jABHc2TcdTmuSL=pP!;7Ya-<4{$i!i4y#KWd!S@S^ih^XfHD@m;GZC5X
zDAD2)kyR;2p<QuDTCL?a>JI&8bdZvNHVGF@C-cPbd2ymu44gva^tuhHoaz5L<9!Ac
zH51<Xuz(?VYvp_rnQ)eS0RXqf$8EvlKlK`MK>-QF`e@1DsTrdH(R*~bpq(%lhx9Im
zXk=iwdPP^n|MjeT6p=~v%ATZ&D*hn#owS?6;c5PaOKII?w&KGFV&V|3Mbr&ALlDZf
zSIi@hvPK?1PHWU72@|5_#|4iwxum9_mewf&Rr&{h9a~a3EEqdNny+W}Br%6{)ex=i
Dkrjtt

diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
index d9c2ae4..bb8dc36 100644
--- a/src/box/lua/upgrade.lua
+++ b/src/box/lua/upgrade.lua
@@ -520,11 +520,11 @@ local function upgrade_to_2_1_0()
     _index:insert{_trigger.id, 1, 'space_id', 'tree', { unique = false },
                   {{1, 'unsigned'}}}
 
-    local stat1_ft = {{name='tbl', type='string'},
-                      {name='idx', type='string'},
+    local stat1_ft = {{name='tbl', type='unsigned'},
+                      {name='idx', type='unsigned'},
                       {name='stat', type='string'}}
-    local stat4_ft = {{name='tbl', type='string'},
-                      {name='idx', type='string'},
+    local stat4_ft = {{name='tbl', type='unsigned'},
+                      {name='idx', type='unsigned'},
                       {name='neq', type='string'},
                       {name='nlt', type='string'},
                       {name='ndlt', type='string'},
@@ -536,7 +536,7 @@ local function upgrade_to_2_1_0()
 
     log.info("create index primary on _sql_stat1")
     _index:insert{box.schema.SQL_STAT1_ID, 0, 'primary', 'tree',
-                  {unique = true}, {{0, 'string'}, {1, 'string'}}}
+                  {unique = true}, {{0, 'unsigned'}, {1, 'unsigned'}}}
 
     log.info("create space _sql_stat4")
     _space:insert{box.schema.SQL_STAT4_ID, ADMIN, '_sql_stat4', 'memtx', 0,
@@ -544,7 +544,7 @@ local function upgrade_to_2_1_0()
 
     log.info("create index primary on _sql_stat4")
     _index:insert{box.schema.SQL_STAT4_ID, 0, 'primary', 'tree',
-                  {unique = true}, {{0, 'string'}, {1, 'string'},
+                  {unique = true}, {{0, 'unsigned'}, {1, 'unsigned'},
                                     {5, 'scalar'}}}
 
     local fk_constr_ft = {{name='name', type='string'},
diff --git a/src/box/schema.cc b/src/box/schema.cc
index a6e413a..8b71d65 100644
--- a/src/box/schema.cc
+++ b/src/box/schema.cc
@@ -383,13 +383,13 @@ schema_init()
 	sc_space_new(BOX_INDEX_ID, "_index", key_def,
 		     &alter_space_on_replace_index, &on_stmt_begin_index);
 
-	/* space name */
+	/* space id */
 	key_def_set_part(key_def, 0 /* part no */, 0 /* field no */,
-			 FIELD_TYPE_STRING, ON_CONFLICT_ACTION_DEFAULT, NULL,
+			 FIELD_TYPE_UNSIGNED, ON_CONFLICT_ACTION_DEFAULT, NULL,
 			 COLL_NONE, SORT_ORDER_ASC);
-	/* index name */
+	/* index id */
 	key_def_set_part(key_def, 1 /* part no */, 1 /* field no */,
-			 FIELD_TYPE_STRING, ON_CONFLICT_ACTION_DEFAULT, NULL,
+			 FIELD_TYPE_UNSIGNED, ON_CONFLICT_ACTION_DEFAULT, NULL,
 			 COLL_NONE, SORT_ORDER_ASC);
 	/* _sql_stat1 - a simpler statistics on space, seen in SQL. */
 	sc_space_new(BOX_SQL_STAT1_ID, "_sql_stat1", key_def, NULL, NULL);
@@ -399,13 +399,13 @@ schema_init()
 	if (key_def == NULL)
 		diag_raise();
 
-	/* space name */
+	/* space id */
 	key_def_set_part(key_def, 0 /* part no */, 0 /* field no */,
-			 FIELD_TYPE_STRING, ON_CONFLICT_ACTION_DEFAULT, NULL,
+			 FIELD_TYPE_UNSIGNED, ON_CONFLICT_ACTION_DEFAULT, NULL,
 			 COLL_NONE, SORT_ORDER_ASC);
-	/* index name */
+	/* index id */
 	key_def_set_part(key_def, 1 /* part no */, 1 /* field no */,
-			 FIELD_TYPE_STRING, ON_CONFLICT_ACTION_DEFAULT, NULL,
+			 FIELD_TYPE_UNSIGNED, ON_CONFLICT_ACTION_DEFAULT, NULL,
 			 COLL_NONE, SORT_ORDER_ASC);
 	/* sample */
 	key_def_set_part(key_def, 2 /* part no */, 5 /* field no */,
diff --git a/src/box/sql.c b/src/box/sql.c
index b158c50..aac00fb 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -1217,16 +1217,16 @@ void tarantoolSqlite3LoadSchema(struct init_data *init)
 	sql_init_callback(init, TARANTOOL_SYS_SQL_STAT1_NAME,
 			  BOX_SQL_STAT1_ID, 0,
 			  "CREATE TABLE \""TARANTOOL_SYS_SQL_STAT1_NAME
-			       "\"(\"tbl\" text,"
-			       "\"idx\" text,"
+			       "\"(\"tbl\" INT,"
+			       "\"idx\" INT,"
 			       "\"stat\" not null,"
 			       "PRIMARY KEY(\"tbl\", \"idx\"))");
 
 	sql_init_callback(init, TARANTOOL_SYS_SQL_STAT4_NAME,
 			  BOX_SQL_STAT4_ID, 0,
 			  "CREATE TABLE \""TARANTOOL_SYS_SQL_STAT4_NAME
-			       "\"(\"tbl\" text,"
-			       "\"idx\" text,"
+			       "\"(\"tbl\" INT,"
+			       "\"idx\" INT,"
 			       "\"neq\" text,"
 			       "\"nlt\" text,"
 			       "\"ndlt\" text,"
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index 76ae153..c25d337 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -124,28 +124,29 @@
  * @param parse Parsing context.
  * @param stat_cursor Open the _sql_stat1 table on this cursor.
  *        you should allocate |stat_names| cursors before call.
- * @param table_name Delete records of this index if specified.
+ * @param space_id Delete records of this table if id is not
+ *        BOX_ID_NIL.
  */
 static void
 vdbe_emit_stat_space_open(struct Parse *parse, int stat_cursor,
-			  const char *table_name)
+			  uint32_t space_id)
 {
-	const char *stat_names[] = {"_sql_stat1", "_sql_stat4"};
+	const char *stat_names[] = {TARANTOOL_SYS_SQL_STAT1_NAME,
+				    TARANTOOL_SYS_SQL_STAT4_NAME};
 	const uint32_t stat_ids[] = {BOX_SQL_STAT1_ID, BOX_SQL_STAT4_ID};
 	struct Vdbe *v = sqlite3GetVdbe(parse);
 	assert(v != NULL);
 	assert(sqlite3VdbeDb(v) == parse->db);
 	for (uint i = 0; i < lengthof(stat_names); ++i) {
-		const char *space_name = stat_names[i];
 		/*
 		 * The table already exists, because it is a
 		 * system space.
 		 */
 		assert(sqlite3HashFind(&parse->db->pSchema->tblHash,
-				       space_name) != NULL);
-		if (table_name != NULL) {
-			vdbe_emit_stat_space_clear(parse, space_name, NULL,
-						   table_name);
+				       stat_names[i]) != NULL);
+		if (space_id != BOX_ID_NIL) {
+			sql_remove_from_stat(parse, stat_names[i], space_id,
+					     BOX_ID_NIL);
 		} else {
 			sqlite3VdbeAddOp1(v, OP_Clear, stat_ids[i]);
 		}
@@ -793,8 +794,8 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
 	int regChng = iMem++;	/* Index of changed index field */
 	int regKey = iMem++;	/* Key argument passed to stat_push() */
 	int regTemp = iMem++;	/* Temporary use register */
-	int regTabname = iMem++;	/* Register containing table name */
-	int regIdxname = iMem++;	/* Register containing index name */
+	int reg_space_id = iMem++;	/* Register containing table id */
+	int reg_index_id = iMem++;	/* Register containing index id */
 	int regStat1 = iMem++;	/* Value for the stat column of _sql_stat1 */
 	int regPrev = iMem;	/* MUST BE LAST (see below) */
 
@@ -818,7 +819,7 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
 	iIdxCur = iTab++;
 	pParse->nTab = MAX(pParse->nTab, iTab);
 	sqlite3OpenTable(pParse, iTabCur, pTab, OP_OpenRead);
-	sqlite3VdbeLoadString(v, regTabname, pTab->def->name);
+	sqlite3VdbeAddOp2(v, OP_Integer, pTab->def->id, reg_space_id);
 
 	for (pIdx = pTab->pIndex; pIdx; pIdx = pIdx->pNext) {
 		int addrRewind;	/* Address of "OP_Rewind iIdxCur" */
@@ -837,8 +838,8 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
 			idx_name = pIdx->def->name;
 		int part_count = pIdx->def->key_def->part_count;
 
-		/* Populate the register containing the index name. */
-		sqlite3VdbeLoadString(v, regIdxname, idx_name);
+		/* Populate the register containing the index id. */
+		sqlite3VdbeAddOp2(v, OP_Integer, pIdx->def->iid, reg_index_id);
 		VdbeComment((v, "Analysis for %s.%s", pTab->def->name,
 			    idx_name));
 
@@ -1015,9 +1016,8 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
 
 		/* Add the entry to the stat1 table. */
 		callStatGet(v, regStat4, STAT_GET_STAT1, regStat1);
-		assert("BBB"[0] == AFFINITY_TEXT);
-		sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp,
-				  "BBB", 0);
+		sqlite3VdbeAddOp4(v, OP_MakeRecord, reg_space_id, 3, regTemp,
+				  "DDB", 0);
 		sqlite3VdbeAddOp2(v, OP_IdxInsert, iStatCur, regTemp);
 
 		/* Add the entries to the stat4 table. */
@@ -1053,7 +1053,7 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
 		}
 		sqlite3VdbeAddOp3(v, OP_MakeRecord, regCol, part_count,
 				  regSample);
-		sqlite3VdbeAddOp3(v, OP_MakeRecord, regTabname, 6, regTemp);
+		sqlite3VdbeAddOp3(v, OP_MakeRecord, reg_space_id, 6, regTemp);
 		sqlite3VdbeAddOp2(v, OP_IdxReplace, iStatCur + 1, regTemp);
 		sqlite3VdbeAddOp2(v, OP_Goto, 1, addrNext);	/* P1==1 for end-of-loop */
 		sqlite3VdbeJumpHere(v, addrIsNull);
@@ -1086,7 +1086,7 @@ sql_analyze_database(Parse *parser)
 	sql_set_multi_write(parser, false);
 	int stat_cursor = parser->nTab;
 	parser->nTab += 3;
-	vdbe_emit_stat_space_open(parser, stat_cursor, NULL);
+	vdbe_emit_stat_space_open(parser, stat_cursor, BOX_ID_NIL);
 	int reg = parser->nMem + 1;
 	int tab_cursor = parser->nTab;
 	for (struct HashElem *k = sqliteHashFirst(&schema->tblHash); k != NULL;
@@ -1114,7 +1114,7 @@ vdbe_emit_analyze_table(struct Parse *parse, struct Table *table)
 	sql_set_multi_write(parse, false);
 	int stat_cursor = parse->nTab;
 	parse->nTab += 3;
-	vdbe_emit_stat_space_open(parse, stat_cursor, table->def->name);
+	vdbe_emit_stat_space_open(parse, stat_cursor, table->def->id);
 	analyzeOneTable(parse, table, NULL, stat_cursor, parse->nMem + 1,
 			parse->nTab);
 	loadAnalysis(parse);
@@ -1246,24 +1246,14 @@ analysis_loader(void *data, int argc, char **argv, char **unused)
 	struct analysis_index_info *info = (struct analysis_index_info *) data;
 	assert(info->stats != NULL);
 	struct index_stat *stat = &info->stats[info->index_count++];
-	uint32_t space_id = box_space_id_by_name(argv[0], strlen(argv[0]));
+	uint32_t space_id = atoll(argv[0]);
 	if (space_id == BOX_ID_NIL)
 		return -1;
 	struct space *space = space_by_id(space_id);
 	assert(space != NULL);
 	struct index *index;
-	uint32_t iid = box_index_id_by_name(space_id, argv[1], strlen(argv[1]));
-	/*
-	 * Convention is if index's name matches with space's
-	 * one, then it is primary index.
-	 */
-	if (iid != BOX_ID_NIL) {
-		index = space_index(space, iid);
-	} else {
-		if (sqlite3_stricmp(argv[0], argv[1]) != 0)
-			return -1;
-		index = space_index(space, 0);
-	}
+	uint32_t iid = atoll(argv[1]);
+	index = space_index(space, iid);
 	assert(index != NULL);
 	/*
 	 * Additional field is used to describe total
@@ -1410,27 +1400,17 @@ load_stat_from_space(struct sqlite3 *db, const char *sql_select_prepare,
 		goto finalize;
 	uint32_t current_idx_count = 0;
 	while (sqlite3_step(stmt) == SQLITE_ROW) {
-		const char *space_name = (char *)sqlite3_column_text(stmt, 0);
-		if (space_name == NULL)
-			continue;
-		const char *index_name = (char *)sqlite3_column_text(stmt, 1);
-		if (index_name == NULL)
+		if (sqlite3_column_text(stmt, 0) == NULL ||
+		    sqlite3_column_text(stmt, 1) == NULL)
 			continue;
-		uint32_t sample_count = sqlite3_column_int(stmt, 2);
-		uint32_t space_id = box_space_id_by_name(space_name,
-							 strlen(space_name));
+		uint32_t space_id = sqlite3_column_int(stmt, 0);
 		assert(space_id != BOX_ID_NIL);
 		struct space *space = space_by_id(space_id);
 		assert(space != NULL);
-		struct index *index;
-		uint32_t iid = box_index_id_by_name(space_id, index_name,
-						    strlen(index_name));
-		if (sqlite3_stricmp(space_name, index_name) == 0 &&
-		    iid == BOX_ID_NIL)
-			index = space_index(space, 0);
-		else
-			index = space_index(space, iid);
+		uint32_t iid = sqlite3_column_int(stmt, 1);
+		struct index *index = space_index(space, iid);
 		assert(index != NULL);
+		uint32_t sample_count = sqlite3_column_int(stmt, 2);
 		uint32_t column_count = index->def->key_def->part_count;
 		struct index_stat *stat = &stats[current_idx_count];
 		stat->sample_field_count = column_count;
@@ -1482,27 +1462,15 @@ load_stat_from_space(struct sqlite3 *db, const char *sql_select_prepare,
 	struct index *prev_index = NULL;
 	current_idx_count = 0;
 	while (sqlite3_step(stmt) == SQLITE_ROW) {
-		const char *space_name = (char *)sqlite3_column_text(stmt, 0);
-		if (space_name == NULL)
-			continue;
-		const char *index_name = (char *)sqlite3_column_text(stmt, 1);
-		if (index_name == NULL)
+		if (sqlite3_column_text(stmt, 0) == NULL ||
+		    sqlite3_column_text(stmt, 1) == NULL)
 			continue;
-		uint32_t space_id = box_space_id_by_name(space_name,
-							 strlen(space_name));
+		uint32_t space_id = sqlite3_column_int(stmt, 0);
 		assert(space_id != BOX_ID_NIL);
 		struct space *space = space_by_id(space_id);
 		assert(space != NULL);
-		struct index *index;
-		uint32_t iid = box_index_id_by_name(space_id, index_name,
-						    strlen(index_name));
-		if (iid != BOX_ID_NIL) {
-			index = space_index(space, iid);
-		} else {
-			if (sqlite3_stricmp(space_name, index_name) != 0)
-				return -1;
-			index = space_index(space, 0);
-		}
+		uint32_t iid = sqlite3_column_int(stmt, 1);
+		struct index *index = space_index(space, iid);
 		assert(index != NULL);
 		uint32_t column_count = index->def->key_def->part_count;
 		if (index != prev_index) {
@@ -1566,28 +1534,16 @@ load_stat_to_index(struct sqlite3 *db, const char *sql_select_load,
 		return -1;
 	uint32_t current_idx_count = 0;
 	while (sqlite3_step(stmt) == SQLITE_ROW) {
-		const char *space_name = (char *)sqlite3_column_text(stmt, 0);
-		if (space_name == NULL)
+		if (sqlite3_column_text(stmt, 0) == NULL ||
+		    sqlite3_column_text(stmt, 1) == NULL)
 			continue;
-		const char *index_name = (char *)sqlite3_column_text(stmt, 1);
-		if (index_name == NULL)
-			continue;
-		uint32_t space_id = box_space_id_by_name(space_name,
-							 strlen(space_name));
+		uint32_t space_id = sqlite3_column_int(stmt, 0);
 		if (space_id == BOX_ID_NIL)
 			return -1;
 		struct space *space = space_by_id(space_id);
 		assert(space != NULL);
-		struct index *index;
-		uint32_t iid = box_index_id_by_name(space_id, index_name,
-						    strlen(index_name));
-		if (iid != BOX_ID_NIL) {
-			index = space_index(space, iid);
-		} else {
-			if (sqlite3_stricmp(space_name, index_name) != 0)
-				return -1;
-			index = space_index(space, 0);
-		}
+		uint32_t iid = sqlite3_column_int(stmt, 1);
+		struct index *index = space_index(space, iid);
 		assert(index != NULL);
 		free(index->def->opts.stat);
 		index->def->opts.stat = stats[current_idx_count++];
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index a1e16b2..ece1092 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -1888,75 +1888,45 @@ sql_store_select(struct Parse *parse_context, struct Select *select)
 	parse_context->parsed_ast.select = select_copy;
 }
 
-/**
- * Create expression record "@col_name = '@col_value'".
- *
- * @param parse The parsing context.
- * @param col_name Name of column.
- * @param col_value Name of row.
- * @retval not NULL on success.
- * @retval NULL on failure.
- */
-static struct Expr *
-sql_id_eq_str_expr(struct Parse *parse, const char *col_name,
-		   const char *col_value)
-{
-	struct sqlite3 *db = parse->db;
-
-	struct Expr *col_name_expr = sqlite3Expr(db, TK_ID, col_name);
-	if (col_name_expr == NULL)
-		return NULL;
-	struct Expr *col_value_expr = sqlite3Expr(db, TK_STRING, col_value);
-	if (col_value_expr == NULL) {
-		sql_expr_delete(db, col_name_expr, false);
-		return NULL;
-	}
-	return sqlite3PExpr(parse, TK_EQ, col_name_expr, col_value_expr);
-}
-
 void
-vdbe_emit_stat_space_clear(struct Parse *parse, const char *stat_table_name,
-			   const char *idx_name, const char *table_name)
+sql_remove_from_stat(struct Parse *parse, const char *stat_table_name,
+		     uint32_t space_id, uint32_t index_id)
 {
-	assert(idx_name != NULL || table_name != NULL);
 	struct sqlite3 *db = parse->db;
 	assert(!db->mallocFailed);
 	struct SrcList *src_list = sql_alloc_src_list(db);
 	if (src_list != NULL)
 		src_list->a[0].zName = sqlite3DbStrDup(db, stat_table_name);
-	struct Expr *where = NULL;
-	if (idx_name != NULL) {
-		struct Expr *expr = sql_id_eq_str_expr(parse, "idx", idx_name);
-		if (expr != NULL)
-			where = sqlite3ExprAnd(db, expr, where);
-	}
-	if (table_name != NULL) {
-		struct Expr *expr = sql_id_eq_str_expr(parse, "tbl", table_name);
-		if (expr != NULL)
-			where = sqlite3ExprAnd(db, expr, where);
-	}
-	/**
-	 * On memory allocation error sql_table delete_from
-	 * releases memory for its own.
-	 */
-	sql_table_delete_from(parse, src_list, where);
+	struct Expr *expr = NULL;
+	struct Expr *col = sqlite3Expr(db, TK_ID, "tbl");
+	struct Expr *val = sqlite3ExprInteger(db, space_id);
+	if (col != NULL && val != NULL)
+		expr = sqlite3PExpr(parse, TK_EQ, col, val);
+	if (index_id != BOX_ID_NIL && expr != NULL) {
+		col = sqlite3Expr(db, TK_ID, "idx");
+		val = sqlite3ExprInteger(db, index_id);
+		expr = sqlite3ExprAnd(db, sqlite3PExpr(parse, TK_EQ, col, val),
+				      expr);
+	}
+	sql_table_delete_from(parse, src_list, expr);
 }
 
 /**
- * Remove entries from the _sql_stat1 and _sql_stat4
- * system spaces after a DROP INDEX or DROP TABLE command.
+ * Remove entries from the _sql_stat1 and _sql_stat4 system spaces
+ * after a DROP INDEX or DROP TABLE command.
  *
- * @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.
+ * @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_spaces(struct Parse *parse, const char *table_name,
-		      const char *idx_name)
+sql_clear_stat_spaces(struct Parse *parse, uint32_t space_id, uint32_t index_id)
 {
-	vdbe_emit_stat_space_clear(parse, "_sql_stat4", idx_name, table_name);
-	vdbe_emit_stat_space_clear(parse, "_sql_stat1", idx_name, table_name);
+	sql_remove_from_stat(parse, TARANTOOL_SYS_SQL_STAT1_NAME, space_id,
+			     index_id);
+	sql_remove_from_stat(parse, TARANTOOL_SYS_SQL_STAT4_NAME, space_id,
+			     index_id);
 }
 
 /**
@@ -2177,7 +2147,7 @@ sql_drop_table(struct Parse *parse_context, struct SrcList *table_name_list,
 			goto exit_drop_table;
 		}
 	}
-	sql_clear_stat_spaces(parse_context, space_name, NULL);
+	sql_clear_stat_spaces(parse_context, space->def->id, BOX_ID_NIL);
 	sql_code_drop_table(parse_context, space, is_view);
 
  exit_drop_table:
@@ -3119,11 +3089,12 @@ sql_drop_index(struct Parse *parse_context, struct SrcList *index_name_list,
 	}
 
 	/*
-	 * Generate code to remove entry from _index space
-	 * But firstly, delete statistics since schema
-	 * changes after DDL.
+	 * Generate code to remove entry from _index space.
+	 * But firstly, delete statistics since schema changes
+	 * after DDL.
 	 */
-	sql_clear_stat_spaces(parse_context, table_name, index->def->name);
+	sql_clear_stat_spaces(parse_context, space->def->id, index->def->iid);
+
 	int record_reg = ++parse_context->nMem;
 	int space_id_reg = ++parse_context->nMem;
 	sqlite3VdbeAddOp2(v, OP_Integer, space_id, space_id_reg);
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 1d32c9a..e6d05fb 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -4862,11 +4862,13 @@ vdbe_emit_halt_with_presence_test(struct Parse *parser, int space_id,
  *
  * @param parse The parsing context.
  * @param stat_table_name System stat table name.
- * @param idx_name Index name.
- * @param table_name Table name.
+ * @param table_id Id of table of which analysis will be deleted.
+ * @param index_id Id of index of which analysis will be deleted.
+ *        If index_id is BOX_ID_NIL than analysis of all indexes
+ *        of selected table will be deleted.
  */
 void
-vdbe_emit_stat_space_clear(struct Parse *parse, const char *stat_table_name,
-			   const char *idx_name, const char *table_name);
+sql_remove_from_stat(struct Parse *parse, const char *stat_table_name,
+		     uint32_t table_id, uint32_t index_id);
 
 #endif				/* SQLITEINT_H */
diff --git a/test/box-py/bootstrap.result b/test/box-py/bootstrap.result
index 506aca3..0884159 100644
--- a/test/box-py/bootstrap.result
+++ b/test/box-py/bootstrap.result
@@ -74,11 +74,12 @@ box.space._space:select{}
         'type': 'unsigned'}]]
   - [340, 1, '_space_sequence', 'memtx', 0, {}, [{'name': 'id', 'type': 'unsigned'},
       {'name': 'sequence_id', 'type': 'unsigned'}, {'name': 'is_generated', 'type': 'boolean'}]]
-  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'tbl', 'type': 'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'stat', 'type': 'string'}]]
-  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'tbl', 'type': 'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'neq', 'type': 'string'}, {'name': 'nlt', 'type': 'string'},
-      {'name': 'ndlt', 'type': 'string'}, {'name': 'sample', 'type': 'scalar'}]]
+  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'tbl', 'type': 'unsigned'}, {
+        'name': 'idx', 'type': 'unsigned'}, {'name': 'stat', 'type': 'string'}]]
+  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'tbl', 'type': 'unsigned'}, {
+        'name': 'idx', 'type': 'unsigned'}, {'name': 'neq', 'type': 'string'}, {'name': 'nlt',
+        'type': 'string'}, {'name': 'ndlt', 'type': 'string'}, {'name': 'sample',
+        'type': 'scalar'}]]
   - [356, 1, '_fk_constraint', 'memtx', 0, {}, [{'name': 'name', 'type': 'string'},
       {'name': 'child_id', 'type': 'unsigned'}, {'name': 'parent_id', 'type': 'unsigned'},
       {'name': 'is_deferred', 'type': 'boolean'}, {'name': 'match', 'type': 'string'},
@@ -134,9 +135,9 @@ box.space._index:select{}
   - [330, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
   - [340, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
   - [340, 1, 'sequence', 'tree', {'unique': false}, [[1, 'unsigned']]]
-  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 'string']]]
-  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 'string'], [
-        5, 'scalar']]]
+  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 'unsigned']]]
+  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 'unsigned'],
+      [5, 'scalar']]]
   - [356, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 'unsigned']]]
   - [356, 1, 'child_id', 'tree', {'unique': false}, [[1, 'unsigned']]]
 ...
diff --git a/test/box/access_misc.result b/test/box/access_misc.result
index 87ccc46..8344de5 100644
--- a/test/box/access_misc.result
+++ b/test/box/access_misc.result
@@ -813,11 +813,12 @@ box.space._space:select()
         'type': 'unsigned'}]]
   - [340, 1, '_space_sequence', 'memtx', 0, {}, [{'name': 'id', 'type': 'unsigned'},
       {'name': 'sequence_id', 'type': 'unsigned'}, {'name': 'is_generated', 'type': 'boolean'}]]
-  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'tbl', 'type': 'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'stat', 'type': 'string'}]]
-  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'tbl', 'type': 'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'neq', 'type': 'string'}, {'name': 'nlt', 'type': 'string'},
-      {'name': 'ndlt', 'type': 'string'}, {'name': 'sample', 'type': 'scalar'}]]
+  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'tbl', 'type': 'unsigned'}, {
+        'name': 'idx', 'type': 'unsigned'}, {'name': 'stat', 'type': 'string'}]]
+  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'tbl', 'type': 'unsigned'}, {
+        'name': 'idx', 'type': 'unsigned'}, {'name': 'neq', 'type': 'string'}, {'name': 'nlt',
+        'type': 'string'}, {'name': 'ndlt', 'type': 'string'}, {'name': 'sample',
+        'type': 'scalar'}]]
   - [356, 1, '_fk_constraint', 'memtx', 0, {}, [{'name': 'name', 'type': 'string'},
       {'name': 'child_id', 'type': 'unsigned'}, {'name': 'parent_id', 'type': 'unsigned'},
       {'name': 'is_deferred', 'type': 'boolean'}, {'name': 'match', 'type': 'string'},
diff --git a/test/box/alter.result b/test/box/alter.result
index 9a1086e..86e8c3a 100644
--- a/test/box/alter.result
+++ b/test/box/alter.result
@@ -228,9 +228,9 @@ _index:select{}
   - [330, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
   - [340, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
   - [340, 1, 'sequence', 'tree', {'unique': false}, [[1, 'unsigned']]]
-  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 'string']]]
-  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 'string'], [
-        5, 'scalar']]]
+  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 'unsigned']]]
+  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 'unsigned'],
+      [5, 'scalar']]]
   - [356, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 'unsigned']]]
   - [356, 1, 'child_id', 'tree', {'unique': false}, [[1, 'unsigned']]]
 ...
diff --git a/test/sql-tap/analyze1.test.lua b/test/sql-tap/analyze1.test.lua
index dab7255..27465a4 100755
--- a/test/sql-tap/analyze1.test.lua
+++ b/test/sql-tap/analyze1.test.lua
@@ -160,7 +160,7 @@ test:do_execsql_test(
         SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
     ]], {
         -- <analyze-3.1>
-        "T1", "2 1", "T1I1", "2 2", "T1I2", "2 1", "T1I3", "2 2 1"
+        0, "2 1", 1, "2 2", 2, "2 1", 3, "2 2 1"
         -- </analyze-3.1>
     })
 
@@ -173,7 +173,7 @@ test:do_execsql_test(
         SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
     ]], {
         -- <analyze-3.2>
-        "T1", "4 1", "T1I1", "4 4", "T1I2", "4 1", "T1I3", "4 4 1"
+        0, "4 1", 1, "4 4", 2, "4 1", 3, "4 4 1"
         -- </analyze-3.2>
     })
 
@@ -185,7 +185,7 @@ test:do_execsql_test(
         SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
     ]], {
         -- <analyze-3.3>
-        "T1","5 1", "T1I1", "5 3", "T1I2", "5 2", "T1I3", "5 3 1"
+        0,"5 1", 1, "5 3", 2, "5 2", 3, "5 3 1"
         -- </analyze-3.3>
     })
 
@@ -201,7 +201,7 @@ test:do_execsql_test(
         SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
     ]], {
         -- <analyze-3.4>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 1","T2I1","5 3","T2I2","5 2","T2I3","5 3 1"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",2,"5 2",3,"5 3 1",3,"5 3 1"
         -- </analyze-3.4>
     })
 
@@ -213,7 +213,7 @@ test:do_execsql_test(
         SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
     ]], {
         -- <analyze-3.5>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 1","T2I1","5 3","T2I2","5 2"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",2,"5 2",3,"5 3 1"
         -- </analyze-3.5>
     })
 
@@ -224,7 +224,7 @@ test:do_execsql_test(
         SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
     ]], {
         -- <analyze-3.6>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 1","T2I1","5 3","T2I2","5 2"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",2,"5 2",3,"5 3 1"
         -- </analyze-3.6>
     })
 
@@ -236,7 +236,7 @@ test:do_execsql_test(
         SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
     ]], {
         -- <analyze-3.7>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 1","T2I1","5 3"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",3,"5 3 1"
         -- </analyze-3.7>
     })
 
@@ -263,7 +263,7 @@ test:do_execsql_test(
         SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
     ]], {
         -- <analyze-3.9>
-        "T3","5 1","T3I1","5 3","T3I2","5 3 1 1 1","T3I3","5 5 2 1 1"
+        0,"5 1",1,"5 3",2,"5 3 1 1 1",3,"5 5 2 1 1"
         -- </analyze-3.9>
     })
 
@@ -320,28 +320,30 @@ test:do_execsql_test(
         SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
     ]], {
         -- <analyze-4.0>
-        "T3", "5 1", "T3I1", "5 3", "T3I2", "5 3 1 1 1", "T3I3", "5 5 2 1 1", "T4", "5 1", "T4I1", "5 3", "T4I2", "5 2"
+        0, "5 1", 0, "5 1", 1, "5 3", 1, "5 3", 2, "5 3 1 1 1", 2, "5 2", 3, "5 5 2 1 1"
         -- </analyze-4.0>
     })
 
+t4 = box.space.T4
+
 test:do_execsql_test(
     "analyze-4.1",
-    [[
+    string.format([[
         DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1" VALUES('t4', 't4i1', 'nonsense');
-        INSERT INTO "_sql_stat1" VALUES('t4', 't4i2', '432653287412874653284129847632');
+        INSERT INTO "_sql_stat1" VALUES(%i, %i, 'nonsense');
+        INSERT INTO "_sql_stat1" VALUES(%i, %i, '432653287412874653284129847632');
         SELECT * FROM t4 WHERE x = 1234;
-    ]], {
+    ]], t4.id, t4.index['T4I1'].id, t4.id, t4.index['T4I2'].id), {
         -- <analyze-4.1>
         -- </analyze-4.1>
     })
 
 test:do_execsql_test(
     "analyze-4.2",
-    [[
-        INSERT INTO "_sql_stat1" VALUES('t4', 'xyzzy', '0 1 2 3');
+    string.format([[
+        INSERT INTO "_sql_stat1" VALUES(%i, 12345, '0 1 2 3');
         SELECT * FROM t4 WHERE x = 1234;
-    ]], {
+    ]], t4.id), {
         -- <analyze-4.2>
         -- </analyze-4.2>
     })
@@ -367,20 +369,20 @@ test:do_execsql_test(
         INSERT INTO t3 (a,b,c,d) SELECT a+64, b+64, c+64, d+64 FROM t3;
         INSERT INTO t4 (x,y,z) SELECT a, b, c FROM t3;
         ANALYZE;
-        SELECT DISTINCT "tbl" FROM "_sql_stat1" ORDER BY 1;
+        SELECT COUNT(DISTINCT "tbl") FROM "_sql_stat1" ORDER BY 1;
     ]], {
         -- <analyze-5.0>
-        "T3", "T4"
+        2
         -- </analyze-5.0>
     })
 
 test:do_execsql_test(
     "analyze-5.0.1",
     [[
-        SELECT DISTINCT "idx" FROM "_sql_stat1" ORDER BY 1;
+        SELECT "idx" FROM "_sql_stat1" ORDER BY 1;
     ]], {
         -- <analyze-5.0>
-        "T3", "T3I1", "T3I2", "T3I3", "T4", "T4I1", "T4I2"
+        0, 0, 1, 1, 2, 2, 3
         -- </analyze-5.0>
     })
 
@@ -392,17 +394,17 @@ test:do_execsql_test(
             SELECT DISTINCT "idx" FROM "%s" ORDER BY 1;
         ]], stat, stat), {
         -- <analyze-5.1>
-        "T3", "T3I1", "T3I2", "T3I3", "T4", "T4I1", "T4I2"
+        0, 1, 2, 3
         -- </analyze-5.1>
     })
 
 test:do_execsql_test(
     "analyze-5.1.1",
     string.format([[
-            SELECT DISTINCT "tbl" FROM "%s" ORDER BY 1;
+            SELECT COUNT(DISTINCT "tbl") FROM "%s" ORDER BY 1;
         ]], stat, stat), {
         -- <analyze-5.1>
-        "T3", "T4"
+        2
         -- </analyze-5.1>
     })
 
@@ -411,20 +413,20 @@ test:do_execsql_test(
     [[
         DROP INDEX t3i2 ON t3;
         ANALYZE;
-        SELECT DISTINCT "idx" FROM "_sql_stat1" ORDER BY 1;
+        SELECT "idx" FROM "_sql_stat1" ORDER BY 1;
     ]], {
         -- <analyze-5.2>
-        "T3", "T3I1", "T3I3", "T4", "T4I1", "T4I2"
+        0, 0, 1, 1, 2, 3
         -- </analyze-5.2>
     })
 
 test:do_execsql_test(
     "analyze-5.2.1",
     [[
-        SELECT DISTINCT "tbl" FROM "_sql_stat1" ORDER BY 1;
+        SELECT COUNT(DISTINCT "tbl") FROM "_sql_stat1" ORDER BY 1;
     ]], {
         -- <analyze-5.2>
-        "T3", "T4"
+        2
         -- </analyze-5.2>
     })
 
@@ -434,17 +436,17 @@ test:do_execsql_test(
             SELECT DISTINCT "idx" FROM "%s" ORDER BY 1;
         ]], stat, stat), {
         -- <analyze-5.3>
-        "T3", "T3I1", "T3I3", "T4", "T4I1", "T4I2"
+        0, 1, 2, 3
         -- </analyze-5.3>
     })
 
 test:do_execsql_test(
     "analyze-5.3.1",
     string.format([[
-            SELECT DISTINCT "tbl" FROM "%s" ORDER BY 1;
+            SELECT COUNT(DISTINCT "tbl") FROM "%s" ORDER BY 1;
         ]], stat, stat), {
         -- <analyze-5.3>
-        "T3", "T4"
+        2
         -- </analyze-5.3>
     })
 
@@ -456,17 +458,17 @@ test:do_execsql_test(
         SELECT DISTINCT "idx" FROM "_sql_stat1" ORDER BY 1;
     ]], {
         -- <analyze-5.4>
-        "T4", "T4I1", "T4I2"
+        0, 1, 2
         -- </analyze-5.4>
     })
 
 test:do_execsql_test(
     "analyze-5.4.1",
     [[
-        SELECT DISTINCT "tbl" FROM "_sql_stat1" ORDER BY 1;
+        SELECT COUNT(DISTINCT "tbl") FROM "_sql_stat1" ORDER BY 1;
     ]], {
         -- <analyze-5.4>
-        "T4"
+        1
         -- </analyze-5.4>
     })
 
@@ -476,17 +478,17 @@ test:do_execsql_test(
             SELECT DISTINCT "idx" FROM "%s" ORDER BY 1;
         ]], stat), {
         -- <analyze-5.5>
-        "T4", "T4I1", "T4I2"
+        0, 1, 2
         -- </analyze-5.5>
     })
 
 test:do_execsql_test(
     "analyze-5.5.1",
     string.format([[
-            SELECT DISTINCT "tbl" FROM "%s" ORDER BY 1;
+            SELECT COUNT(DISTINCT "tbl") FROM "%s" ORDER BY 1;
         ]], stat), {
         -- <analyze-5.5>
-        "T4"
+        1
         -- </analyze-5.5>
     })
 
@@ -516,33 +518,35 @@ test:do_test(
     -- </analyze-6.1.1>
 })
 
+t1 = box.space.T1
+
 test:do_execsql_test(
     "analyze-6.1.2",
-    [[
-            SELECT * FROM "_sql_stat1" where "tbl"='T1' and "idx"='I1' LIMIT 1;
-    ]], {
+    string.format([[
+            SELECT "idx", "stat" FROM "_sql_stat1" where "tbl"=%i and "idx"=%i LIMIT 1;
+    ]], t1.id, t1.index['I1'].id), {
     -- <analyze-6.1.2>
-    "T1", "I1", "221 221 221 221 2"
+    1, "221 221 221 221 2"
     -- </analyze-6.1.2>
 })
 
 test:do_execsql_test(
     "analyze-6.1.3",
-    [[
-            SELECT "tbl", "idx", "neq", "nlt", "ndlt" FROM "_sql_stat4" where "tbl"='T1' and "idx"='I1' ORDER BY "nlt" LIMIT 1;
-    ]], {
+    string.format([[
+            SELECT "idx", "neq", "nlt", "ndlt" FROM "_sql_stat4" where "tbl"=%i and "idx"=%i ORDER BY "nlt" LIMIT 1;
+    ]], t1.id, t1.index['I1'].id), {
     -- <analyze-6.1.3>
-    "T1", "I1", "221 221 221 1", "0 0 0 10", "0 0 0 10"
+    1, "221 221 221 1", "0 0 0 10", "0 0 0 10"
     -- </analyze-6.1.3>
 })
 
 test:do_execsql_test(
     "analyze-6.1.4",
-    [[
-            SELECT "tbl", "idx", "neq", "nlt", "ndlt" FROM "_sql_stat4" where "tbl"='T1' and "idx"='I1' ORDER BY "nlt" DESC LIMIT 1;
-    ]], {
+    string.format([[
+            SELECT "idx", "neq", "nlt", "ndlt" FROM "_sql_stat4" where "tbl"=%i and "idx"=%i ORDER BY "nlt" DESC LIMIT 1;
+    ]], t1.id, t1.index['I1'].id), {
     -- <analyze-6.1.4>
-    "T1", "I1", "221 221 221 1", "0 0 0 99", "0 0 0 99"
+    1, "221 221 221 1", "0 0 0 99", "0 0 0 99"
     -- </analyze-6.1.4>
 })
 
diff --git a/test/sql-tap/analyze4.test.lua b/test/sql-tap/analyze4.test.lua
index c2cc190..dd5533b 100755
--- a/test/sql-tap/analyze4.test.lua
+++ b/test/sql-tap/analyze4.test.lua
@@ -49,15 +49,19 @@ test:do_test(
         -- </analyze4-1.0>
     })
 
+t1 = box.space.T1
+
 -- Verify that the t1b index shows that it does not narrow down the
 -- search any at all.
 --
 test:do_execsql_test(
     "analyze4-1.1",
-    [[ SELECT "idx", "stat" FROM "_sql_stat1" WHERE "tbl"='T1' ORDER BY "idx"; ]],
+    string.format([[
+        SELECT "idx", "stat" FROM "_sql_stat1" WHERE "tbl"=%i ORDER BY "idx";
+    ]], t1.id),
     {
         -- <analyze4-1.1>
-        "T1","128 1", "T1A", "128 1", "T1B", "128 128"
+        0,"128 1", 1, "128 1", 2, "128 128"
         -- </analyze4-1.1>
     })
 
@@ -68,16 +72,16 @@ test:do_execsql_test(
 test:do_test(
     "analyze4-1.2",
     function()
-        return test:execsql([[
-            UPDATE t1 SET b='x' WHERE a%2;
+        return test:execsql(string.format([[
+            UPDATE t1 SET b='x' WHERE a%%2;
 -- pragma vdbe_debug=1;
             ANALYZE;
 -- pragma vdbe_debug=0;
-            SELECT "idx", "stat" FROM "_sql_stat1" WHERE "tbl"='T1' ORDER BY "idx";
-        ]])
+            SELECT "idx", "stat" FROM "_sql_stat1" WHERE "tbl"=%i ORDER BY "idx";
+        ]], t1.id))
     end, {
         -- <analyze4-1.2>
-        "T1", "128 1", "T1A", "128 1", "T1B", "128 64"
+        0, "128 1", 1, "128 1", 2, "128 64"
         -- </analyze4-1.2>
     })
 
@@ -85,37 +89,42 @@ test:do_test(
 -- Create a multi-column indices using t1.b and verify that ANALYZE 
 -- processes them correctly.
 --
+
+test:execsql([[
+    -- Tarantool doesn't suppoort ALTER stmt yet.
+    -- UPDATE t1 SET b=NULL;
+    --ALTER TABLE t1 ADD COLUMN c;
+    --ALTER TABLE t1 ADD COLUMN d;
+    -- So, re-create the table and its contents
+    DROP TABLE t1;
+    CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a,b,c DEFAULT NULL,d DEFAULT NULL);
+    CREATE INDEX t1a ON t1(a);
+    CREATE INDEX t1b ON t1(b);
+    INSERT INTO t1 (a,b) VALUES(1,NULL);
+    INSERT INTO t1 (a,b) SELECT a+1, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+2, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+4, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+8, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+16, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+32, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+64, b FROM t1;
+
+    UPDATE t1 SET c=a/4, d=a/2;
+    CREATE INDEX t1bcd ON t1(b,c,d);
+    CREATE INDEX t1cdb ON t1(c,d,b);
+    CREATE INDEX t1cbd ON t1(c,b,d);
+    ANALYZE;
+]])
+
+t1 = box.space.T1
+
 test:do_execsql_test(
     "analyze4-1.3",
-    [[
-            -- Tarantool doesn't suppoort ALTER stmt yet.
-            -- UPDATE t1 SET b=NULL;
-            --ALTER TABLE t1 ADD COLUMN c;
-            --ALTER TABLE t1 ADD COLUMN d;
-            -- So, re-create the table and its contents
-            DROP TABLE t1;
-            CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a,b,c DEFAULT NULL,d DEFAULT NULL);
-            CREATE INDEX t1a ON t1(a);
-            CREATE INDEX t1b ON t1(b);
-            INSERT INTO t1 (a,b) VALUES(1,NULL);
-            INSERT INTO t1 (a,b) SELECT a+1, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+2, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+4, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+8, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+16, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+32, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+64, b FROM t1;
-
-            UPDATE t1 SET c=a/4, d=a/2;
-            CREATE INDEX t1bcd ON t1(b,c,d);
-            CREATE INDEX t1cdb ON t1(c,d,b);
-            CREATE INDEX t1cbd ON t1(c,b,d);
-            ANALYZE;
-            SELECT "idx", "stat" FROM "_sql_stat1" WHERE "tbl"='T1' ORDER BY "idx";
-    ]]
-    , {
+    string.format([[
+            SELECT "idx", "stat" FROM "_sql_stat1" WHERE "tbl"=%i ORDER BY "idx";
+    ]], t1.id), {
         -- <analyze4-1.3>
-        "T1","128 1", "T1A", "128 1", "T1B", "128 128", "T1BCD", "128 128 4 2", "T1CBD", "128 4 4 2", "T1CDB", "128 4 2 2"
+        0,"128 1", 1, "128 1", 2, "128 128", 3, "128 128 4 2", 4, "128 4 2 2", 5, "128 4 4 2"
         -- </analyze4-1.3>
     })
 
diff --git a/test/sql-tap/analyze5.test.lua b/test/sql-tap/analyze5.test.lua
index d68bd3c..198f397 100755
--- a/test/sql-tap/analyze5.test.lua
+++ b/test/sql-tap/analyze5.test.lua
@@ -114,10 +114,11 @@ test:do_test(
 
         -- DISTINCT idx, sample -- lindex(test_decode(sample),0)
         -- WHERE idx='t1u' ORDER BY nlt;
-        return test:execsql([[ SELECT DISTINCT msgpack_decode("sample")
-                                 FROM "_sql_stat4"
-                                 WHERE "idx"='T1U'
-                                 ORDER BY "nlt"]])
+        return test:execsql(string.format([[
+            SELECT DISTINCT msgpack_decode("sample")
+            FROM "_sql_stat4"
+            WHERE "idx"= %i
+            ORDER BY "nlt"]], box.space.T1.index['T1U'].id))
     end, {
         -- <analyze5-1.0>
         "alpha", "bravo", "charlie", "delta"
@@ -146,7 +147,7 @@ test:do_test(
         return test:execsql([[SELECT "idx", count(*) FROM "_sql_stat4" GROUP BY 1 ORDER BY 1]])
     end, {
         -- <analyze5-1.2>
-        "T1",24,"T1T",4,"T1U",4,"T1V",1,"T1W",4,"T1X",4,"T1Y",2,"T1Z",4
+        0,24,1,4,2,4,3,1,4,4,5,4,6,2,7,4
         -- </analyze5-1.2>
     })
 
diff --git a/test/sql-tap/analyze9.test.lua b/test/sql-tap/analyze9.test.lua
index 1dbfe5d..2c6b4b8 100755
--- a/test/sql-tap/analyze9.test.lua
+++ b/test/sql-tap/analyze9.test.lua
@@ -1,6 +1,6 @@
 #!/usr/bin/env tarantool
 test = require("sqltester")
-test:plan(121)
+test:plan(118)
 
 testprefix = "analyze9"
 
@@ -62,29 +62,29 @@ msgpack_decode_sample = function(txt)
 end
 
 box.internal.sql_create_function("msgpack_decode_sample", msgpack_decode_sample)
+t1 = box.space.T1
 
 test:do_execsql_test(
     1.2,
-    [[
-        SELECT "tbl","idx","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM "_sql_stat4" where "idx" = 'I1';
-    ]], {
+    string.format([[
+        SELECT "idx","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM "_sql_stat4" where "idx" = %i;
+    ]], t1.index['I1'].id), {
         -- <1.2>
-        "T1", "I1", "1 1", "0 0", "0 0", "(0) (0)", "T1", "I1", "1 1", "1 1", "1 1", "(1) (1)", 
-        "T1", "I1", "1 1", "2 2", "2 2", "(2) (2)", "T1", "I1", "1 1", "3 3", "3 3", "(3) (3)", 
-        "T1", "I1", "1 1", "4 4", "4 4", "(4) (4)"
+        1, "1 1", "0 0", "0 0", "(0) (0)", 1, "1 1", "1 1", "1 1", "(1) (1)",
+        1, "1 1", "2 2", "2 2", "(2) (2)", 1, "1 1", "3 3", "3 3", "(3) (3)",
+        1, "1 1", "4 4", "4 4", "(4) (4)"
         -- </1.2>
     })
 
 test:do_execsql_test(
     1.3,
-    [[
-        SELECT "tbl","idx","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM "_sql_stat4" where "idx" = 'T1';
-
-    ]], {
+    string.format([[
+        SELECT "idx","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM "_sql_stat4" where "idx" = %i;
+    ]], t1.index[0].id), {
         -- <1.3>
-        'T1', 'T1', '1', '0', '0', '(0)', 'T1', 'T1', '1', '1', '1', '(1)', 
-        'T1', 'T1', '1', '2', '2', '(2)', 'T1', 'T1', '1', '3', '3', '(3)', 
-        'T1', 'T1', '1', '4', '4', '(4)'
+        0, '1', '0', '0', '(0)', 0, '1', '1', '1', '(1)',
+        0, '1', '2', '2', '(2)', 0, '1', '3', '3', '(3)',
+        0, '1', '4', '4', '(4)'
         -- </1.3>
     })
 
@@ -101,10 +101,10 @@ test:do_execsql_test(
         INSERT INTO t1 VALUES(22.0, 'some text');
         CREATE INDEX i1 ON t1(a, b);
         ANALYZE;
-        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4";
+        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4" ORDER BY 1;
     ]], {
         -- <2.1>
-        "some text 14", "22 some text", "some text", 22
+        22, "22 some text", "some text", "some text 14"
         -- </2.1>
     })
 
@@ -116,6 +116,7 @@ test:do_execsql_test(
         CREATE TABLE t2(id INTEGER PRIMARY KEY AUTOINCREMENT, a, b);
         CREATE INDEX i2 ON t2(a, b);
     ]])
+t2 = box.space.T2
 
 test:do_test(
     3.2,
@@ -184,10 +185,10 @@ test:do_execsql_test(
 --      
 test:do_execsql_test(
     "3.3.2",
-    [[
+    string.format([[
         ANALYZE;
-        SELECT lrange("neq", 1, 1) FROM "_sql_stat4" WHERE "idx" = 'I2';
-    ]], generate_tens_str(24))
+        SELECT lrange("neq", 1, 1) FROM "_sql_stat4" WHERE "tbl" = %i and "idx" = %i;
+    ]], t2.id, t2.index['I2'].id) , generate_tens_str(24))
 
 ---------------------------------------------------------------------------
 -- 
@@ -232,6 +233,7 @@ test:do_execsql_test(
         CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a, b, c);
         CREATE INDEX i1 ON t1(c, b, a);
     ]])
+t1 = box.space.T1
 
 insert_filler_rows_n = function(iStart, nCopy, nVal)
     for i = 0, nVal-1 do
@@ -282,10 +284,10 @@ test:do_execsql_test(
 
 test:do_execsql_test(
     4.3,
-    [[
+    string.format([[
         SELECT "neq", lrange("nlt", 1, 3), lrange("ndlt", 1, 3), lrange(msgpack_decode_sample("sample"), 1, 3) 
-            FROM "_sql_stat4" WHERE "idx" = 'I1' ORDER BY "sample" LIMIT 16;
-    ]], {
+            FROM "_sql_stat4" WHERE "tbl" = %i and "idx" = %i ORDER BY "sample" LIMIT 16;
+    ]], t1.id, t1.index['I1'].id), {
         -- <4.3>
         "10 10 10","0 0 0","0 0 0","0 0 0","10 10 10","10 10 10","1 1 1","1 1 1","10 10 10","20 20 20",
         "2 2 2","2 2 2","10 10 10","30 30 30","3 3 3","3 3 3","10 10 10","40 40 40","4 4 4","4 4 4",
@@ -299,10 +301,10 @@ test:do_execsql_test(
 
 test:do_execsql_test(
     4.4,
-    [[
+    string.format([[
         SELECT "neq", lrange("nlt", 1, 3), lrange("ndlt", 1, 3), lrange(msgpack_decode_sample("sample"), 1, 3) 
-        FROM "_sql_stat4" WHERE "idx" = 'I1' ORDER BY "sample" DESC LIMIT 2;
-    ]], {
+        FROM "_sql_stat4" WHERE "tbl" = %i and "idx" = %i ORDER BY "sample" DESC LIMIT 2;
+    ]], t1.id, t1.index['I1'].id), {
         -- <4.4>
         "2 1 1","295 296 296","120 122 125","201 4 h","5 3 1","290 290 291","119 119 120","200 1 b"
         -- </4.4>
@@ -364,11 +366,12 @@ test:do_test(
 test:do_execsql_test(
     4.9,
     [[
-        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4";
+        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4" ORDER BY 1;
     ]], {
         -- <4.9>
-        "x", 1110, 2230, 2750, 3350, 4090, 4470, 4980, 5240, 5280, 5290, 5590, 5920, 
-        5930, 6220, 6710, 7000, 7710, 7830, 7970, 8890, 8950, 9240, 9250, 9680
+        1110, 2230, 2750, 3350, 4090, 4470, 4980, 5240, 5280, 5290, 5590, 5920,
+        5930, 6220, 6710, 7000, 7710, 7830, 7970, 8890, 8950, 9240, 9250, 9680,
+        "x"
         -- </4.9>
     })
 
@@ -394,6 +397,8 @@ test:do_execsql_test(
         INSERT INTO "_sql_stat4" SELECT * FROM x1;
         ANALYZE;
     ]])
+t1 = box.space.T1
+x1 = box.space.X1
 
 test:do_execsql_test(
     6.2,
@@ -407,7 +412,7 @@ test:do_execsql_test(
 --
 test:do_execsql_test(
     7.1,
-    [[
+    string.format([[
         DROP TABLE IF EXISTS t1;
         CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a, b);
         CREATE INDEX i1 ON t1(a, b);
@@ -418,9 +423,9 @@ test:do_execsql_test(
         INSERT INTO t1 VALUES(null, 5, 5);
         ANALYZE;
         UPDATE "_sql_stat4" SET "sample" = '' WHERE "sample" =
-            (SELECT "sample" FROM "_sql_stat4" WHERE "tbl" = 't1' AND "idx" = 'i1' LIMIT 1);
+            (SELECT "sample" FROM "_sql_stat4" WHERE "tbl" = %i AND "idx" = %i LIMIT 1);
         ANALYZE;
-    ]])
+    ]], t1.id, t1.index['I1'].id))
 
 -- Doesn't work due to the fact that in Tarantool rowid has been removed,
 -- and tbl, idx and sample have been united into primary key.
@@ -1014,15 +1019,15 @@ test:do_execsql_test(
 --
 test:do_execsql_test(
     15.1,
-    [[
+    string.format([[
         DROP TABLE IF EXISTS x1;
         CREATE TABLE x1(a PRIMARY KEY, b, UNIQUE(a, b));
         INSERT INTO x1 VALUES(1, 2);
         INSERT INTO x1 VALUES(3, 4);
         INSERT INTO x1 VALUES(5, 6);
         ANALYZE;
-        INSERT INTO "_sql_stat4" VALUES('x1', 'abc', 0, 0, 0, '');
-    ]])
+        INSERT INTO "_sql_stat4" VALUES(%i,  12345, 0, 0, 0, '');
+    ]], x1.id))
 
 test:do_execsql_test(
     15.2,
@@ -1050,42 +1055,6 @@ test:do_execsql_test(
         -- </15.4>
     })
 
-test:do_execsql_test(
-    15.7,
-    [[
-        ANALYZE;
-        UPDATE "_sql_stat1" SET "tbl" = 'no such tbl';
-    ]])
-
-test:do_execsql_test(
-    15.8,
-    [[
-        SELECT * FROM x1 ;
-    ]], {
-        -- <15.8>
-        1, 2, 3, 4, 5, 6
-        -- </15.8>
-    })
-
--- Tarantool: this test seems to be useless. There's no reason
--- for these fields to be nullable.
--- test:do_execsql_test(
---    15.9,
---    [[
---        ANALYZE;
---        UPDATE "_sql_stat4" SET "neq" = NULL, "nlt" = NULL, "ndlt" = NULL;
---    ]])
-
-test:do_execsql_test(
-    15.10,
-    [[
-        SELECT * FROM x1;
-    ]], {
-        -- <15.10>
-        1, 2, 3, 4, 5, 6
-        -- </15.10>
-    })
-
 -- This is just for coverage....
 test:do_execsql_test(
     15.11,
@@ -1135,6 +1104,7 @@ test:do_test(
         -- <17.1>
         -- </17.1>
     })
+t1 = box.space.T1
 
 test:do_execsql_test(
     17.2,
@@ -1197,7 +1167,7 @@ test:do_test(
             test:execsql(string.format("INSERT INTO t1 VALUES(%s, 0);", i))
         end
         test:execsql("ANALYZE")
-        return test:execsql([[ SELECT count(*) FROM "_sql_stat4" WHERE "idx" = 'I1'; ]])
+        return test:execsql(string.format('SELECT count(*) FROM "_sql_stat4" WHERE "tbl" = %i AND "idx" = %i;', box.space.T1.id, box.space.T1.index['I1'].id))
     end, {
         -- <18.1>
         9
@@ -1239,12 +1209,13 @@ test:do_execsql_test(
         -- </20.2>
     })
 
+t1 = box.space.T1
 for i = 0, 15 do
     test:do_test(
         "20.3."..i,
         function()
             return test:execsql(string.format(
-                [[SELECT count(*) FROM "_sql_stat4" WHERE "idx" = 'I1' AND lrange(msgpack_decode_sample("sample"), 1, 1) = '%s']], i))
+                [[SELECT count(*) FROM "_sql_stat4" WHERE "tbl" = %i AND "idx" = %i AND lrange(msgpack_decode_sample("sample"), 1, 1) = '%s']], t1.id, t1.index['I1'].id, i))
         end, {
             1
         })
diff --git a/test/sql-tap/analyzeC.test.lua b/test/sql-tap/analyzeC.test.lua
index a3cea70..4bfb4f6 100755
--- a/test/sql-tap/analyzeC.test.lua
+++ b/test/sql-tap/analyzeC.test.lua
@@ -30,9 +30,7 @@ testprefix = "analyzeC"
 -- Baseline case.  Range queries work OK.  Indexes can be used for
 -- ORDER BY.
 
-test:do_execsql_test(
-    1.0,
-    [[
+test:execsql([[
         DROP TABLE IF EXISTS t1;
         CREATE TABLE t1(a PRIMARY KEY, b, c, d);
         INSERT INTO t1(a,b,c,d) VALUES(1,1,2,3),(2,7,8,9),(3,4,5,6),(4,10,11,12),(5,4,8,12),(6,1,11,111);
@@ -40,10 +38,17 @@ test:do_execsql_test(
         CREATE INDEX t1c ON t1(c);
         ANALYZE;
         DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") VALUES('t1','t1b','12345 2'),('t1','t1c','12345 4');
+    ]]
+)
+t1 = box.space.T1
+
+test:do_execsql_test(
+    1.0,
+    string.format([[
+        INSERT INTO "_sql_stat1"("tbl","idx","stat") VALUES(%i,1,'12345 2'),(%i,2,'12345 4');
         ANALYZE;
         SELECT b,c,d, '#' FROM t1 WHERE b BETWEEN 3 AND 8 ORDER BY d;
-    ]], {
+    ]], t1.id, t1.id), {
         -- <1.0>
         4, 5, 6, "#", 7, 8, 9, "#", 4, 8, 12, "#"
         -- </1.0>
@@ -173,17 +178,22 @@ test:do_execsql_test(
 
 -- The sz=NNN parameter determines which index to scan
 --
-test:do_execsql_test(
-    4.0,
-    [[
+
+test:execsql([[
         DROP INDEX t1b ON t1;
         CREATE INDEX t1bc ON t1(b,c);
         CREATE INDEX t1db ON t1(d,b);
         DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") VALUES('t1','t1bc','12345 3 2 sz=10'),('t1','t1db','12345 3 2 sz=20');
+    ]]
+)
+
+test:do_execsql_test(
+    4.0,
+    string.format([[
+        INSERT INTO "_sql_stat1"("tbl","idx","stat") VALUES(%i,%i,'12345 3 2 sz=10'),(%i,%i,'12345 3 2 sz=20');
         ANALYZE;
         SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
         -- <4.0>
         6
         -- </4.0>
@@ -201,12 +211,13 @@ test:do_execsql_test(
 
 test:do_execsql_test(
     4.2,
+    string.format(
     [[
         DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") VALUES('t1','t1bc','12345 3 2 sz=20'),('t1','t1db','12345 3 2 sz=10');
+        INSERT INTO "_sql_stat1"("tbl","idx","stat") VALUES(%i,%i,'12345 3 2 sz=20'),(%i,%i,'12345 3 2 sz=10');
         ANALYZE;
         SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
         -- <4.2>
         6
         -- </4.2>
@@ -227,14 +238,14 @@ test:do_execsql_test(
 --
 test:do_execsql_test(
     5.0,
-    [[
+    string.format([[
         DELETE FROM "_sql_stat1";
         INSERT INTO "_sql_stat1"("tbl","idx","stat")
-          VALUES('t1','t1bc','12345 3 2 x=5 sz=10 y=10'),
-                ('t1','t1db','12345 3 2 whatever sz=20 junk');
+          VALUES(%i,%i,'12345 3 2 x=5 sz=10 y=10'),
+                (%i,%i,'12345 3 2 whatever sz=20 junk');
         ANALYZE;
         SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
         -- <5.0>
         6
         -- </5.0>
@@ -253,12 +264,12 @@ test:do_execsql_test(
 
 test:do_execsql_test(
     5.2,
-    [[
+    string.format([[
         DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") VALUES('t1','t1db','12345 3 2 x=5 sz=10 y=10'), ('t1','t1bc','12345 3 2 whatever sz=20 junk');
+        INSERT INTO "_sql_stat1"("tbl","idx","stat") VALUES(%i,%i,'12345 3 2 x=5 sz=10 y=10'), (%i,%i,'12345 3 2 whatever sz=20 junk');
         ANALYZE;
         SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
         -- <5.2>
         6
         -- </5.2>
diff --git a/test/sql-tap/analyzeD.test.lua b/test/sql-tap/analyzeD.test.lua
index ef6aced..27a19c8 100755
--- a/test/sql-tap/analyzeD.test.lua
+++ b/test/sql-tap/analyzeD.test.lua
@@ -153,9 +153,9 @@ test:do_catchsql_test(
 
 test:do_execsql_test(
 	"analyzeD-1.10",
-	[[
-		SELECT * FROM "_sql_stat4" WHERE "tbl" = 'v';
-	]], {
+	string.format([[
+		SELECT * FROM "_sql_stat4" WHERE "tbl" = %i;
+	]], box.space.V.id), {
 		-- <analyzeD-1.10>
 		
 		-- <analyzeD-1.10>
@@ -163,9 +163,9 @@ test:do_execsql_test(
 
 test:do_execsql_test(
 	"analyzeD-1.11",
-	[[
-		SELECT * FROM "_sql_stat1" WHERE "tbl" = 'v';
-	]], {
+	string.format([[
+		SELECT * FROM "_sql_stat1" WHERE "tbl" = %i;
+	]], box.space.V.id), {
 		-- <analyzeD-1.11>
 		
 		-- <analyzeD-1.11>
@@ -183,9 +183,9 @@ test:do_catchsql_test(
 
 test:do_execsql_test(
 	"analyzeD-1.13",
-	[[
-		SELECT * FROM "_sql_stat4" WHERE "tbl" = 'v';
-	]], {
+	string.format([[
+		SELECT * FROM "_sql_stat4" WHERE "tbl" = %i;
+	]], box.space.V.id), {
 		-- <analyzeD-1.13>
 		
 		-- <analyzeD-1.13>
@@ -193,9 +193,9 @@ test:do_execsql_test(
 
 test:do_execsql_test(
 	"analyzeD-1.14",
-	[[
-		SELECT * FROM "_sql_stat1" WHERE "tbl" = 'v';
-	]], {
+	string.format([[
+		SELECT * FROM "_sql_stat1" WHERE "tbl" = %i;
+	]], box.space.V.id), {
 		-- <analyzeD-1.14>
 		
 		-- <analyzeD-1.14>
diff --git a/test/sql-tap/gh-3350-skip-scan.test.lua b/test/sql-tap/gh-3350-skip-scan.test.lua
index 301b04a..917c74c 100755
--- a/test/sql-tap/gh-3350-skip-scan.test.lua
+++ b/test/sql-tap/gh-3350-skip-scan.test.lua
@@ -77,9 +77,7 @@ test:do_execsql_test(
         }
 )
 
-test:do_execsql_test(
-        "skip-scan-1.4",
-        [[
+test:execsql([[
             DROP TABLE IF EXISTS t1;
             CREATE TABLE t1(id INTEGER PRIMARY KEY, a TEXT, b INT, c INT, d INT);
             CREATE INDEX t1abc ON t1(a,b,c);
@@ -96,10 +94,17 @@ test:do_execsql_test(
             ANALYZE;
             DELETE FROM "_sql_stat1";
             DELETE FROM "_sql_stat4";
-            INSERT INTO "_sql_stat1" VALUES('T1','T1ABC','10000 5000 2000 10');
+        ]]
+)
+
+test:do_execsql_test(
+        "skip-scan-1.4",
+        string.format(
+        [[
+            INSERT INTO "_sql_stat1" VALUES(%i,%i,'10000 5000 2000 10');
             ANALYZE t2;
             SELECT a,b,c,d FROM t1 WHERE b=345;
-        ]], {
+        ]], box.space.T1.id, box.space.T1.index['T1ABC'].id), {
             "abc", 345, 7, 8, "def", 345, 9, 10
         }
 )
diff --git a/test/sql/sql-statN-index-drop.result b/test/sql/sql-statN-index-drop.result
index a751eca..0e5f2a3 100644
--- a/test/sql/sql-statN-index-drop.result
+++ b/test/sql/sql-statN-index-drop.result
@@ -31,36 +31,36 @@ box.sql.execute("ANALYZE;")
 ---
 ...
 -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
+box.sql.execute('SELECT "idx","neq","nlt","ndlt","sample" FROM \"_sql_stat4\";')
 ---
-- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
+  - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
 ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "idx","stat" FROM \"_sql_stat1\";')
 ---
-- - ['T1', 'I1', '1 1']
-  - ['T1', 'T1', '1 1']
-  - ['T2', 'I1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - [0, '1 1']
+  - [1, '1 1']
+  - [0, '1 1']
+  - [1, '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\";")
+box.sql.execute('SELECT "idx","neq","nlt","ndlt","sample" FROM \"_sql_stat4\";')
 ---
-- - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - [0, '1', '0', '0', !!binary kQE=]
+  - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
 ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "idx","stat" FROM \"_sql_stat1\";')
 ---
-- - ['T1', 'T1', '1 1']
-  - ['T2', 'I1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - [0, '1 1']
+  - [0, '1 1']
+  - [1, '1 1']
 ...
 --Cleaning up.
 box.sql.execute("DROP TABLE t1;")
@@ -93,36 +93,36 @@ box.sql.execute("ANALYZE;")
 ---
 ...
 -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
+box.sql.execute('SELECT "idx","neq","nlt","ndlt","sample" FROM \"_sql_stat4\";')
 ---
-- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
+  - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
 ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "idx","stat" FROM \"_sql_stat1\";')
 ---
-- - ['T1', 'I1', '1 1']
-  - ['T1', 'T1', '1 1']
-  - ['T2', 'I1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - [0, '1 1']
+  - [1, '1 1']
+  - [0, '1 1']
+  - [1, '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\";")
+box.sql.execute('SELECT "idx","neq","nlt","ndlt","sample" FROM \"_sql_stat4\";')
 ---
-- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
+  - [0, '1', '0', '0', !!binary kQE=]
 ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "idx","stat" FROM \"_sql_stat1\";')
 ---
-- - ['T1', 'I1', '1 1']
-  - ['T1', 'T1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - [0, '1 1']
+  - [1, '1 1']
+  - [0, '1 1']
 ...
 --Cleaning up.
 box.sql.execute("DROP TABLE t1;")
diff --git a/test/sql/sql-statN-index-drop.test.lua b/test/sql/sql-statN-index-drop.test.lua
index fe7e15b..6cc6400 100644
--- a/test/sql/sql-statN-index-drop.test.lua
+++ b/test/sql/sql-statN-index-drop.test.lua
@@ -14,15 +14,15 @@ box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
 box.sql.execute("ANALYZE;")
 
 -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "idx","neq","nlt","ndlt","sample" FROM \"_sql_stat4\";')
+box.sql.execute('SELECT "idx","stat" 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\";")
+box.sql.execute('SELECT "idx","neq","nlt","ndlt","sample" FROM \"_sql_stat4\";')
+box.sql.execute('SELECT "idx","stat" FROM \"_sql_stat1\";')
 
 --Cleaning up.
 box.sql.execute("DROP TABLE t1;")
@@ -41,15 +41,15 @@ box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
 box.sql.execute("ANALYZE;")
 
 -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "idx","neq","nlt","ndlt","sample" FROM \"_sql_stat4\";')
+box.sql.execute('SELECT "idx","stat" 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\";")
+box.sql.execute('SELECT "idx","neq","nlt","ndlt","sample" FROM \"_sql_stat4\";')
+box.sql.execute('SELECT "idx","stat" FROM \"_sql_stat1\";')
 
 --Cleaning up.
 box.sql.execute("DROP TABLE t1;")
diff --git a/test/sql/upgrade.result b/test/sql/upgrade.result
index 5e7d851..6890c5d 100644
--- a/test/sql/upgrade.result
+++ b/test/sql/upgrade.result
@@ -30,13 +30,13 @@ box.space._space.index['name']:get('_trigger')
 ...
 box.space._space.index['name']:get('_sql_stat1')
 ---
-- [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'tbl', 'type': 'string'}, {'name': 'idx',
-      'type': 'string'}, {'name': 'stat', 'type': 'string'}]]
+- [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'tbl', 'type': 'unsigned'}, {'name': 'idx',
+      'type': 'unsigned'}, {'name': 'stat', 'type': 'string'}]]
 ...
 box.space._space.index['name']:get('_sql_stat4')
 ---
-- [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'tbl', 'type': 'string'}, {'name': 'idx',
-      'type': 'string'}, {'name': 'neq', 'type': 'string'}, {'name': 'nlt', 'type': 'string'},
+- [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'tbl', 'type': 'unsigned'}, {'name': 'idx',
+      'type': 'unsigned'}, {'name': 'neq', 'type': 'string'}, {'name': 'nlt', 'type': 'string'},
     {'name': 'ndlt', 'type': 'string'}, {'name': 'sample', 'type': 'scalar'}]]
 ...
 box.space._index:get({box.space._space.index['name']:get('_trigger').id, 0})
@@ -45,12 +45,12 @@ box.space._index:get({box.space._space.index['name']:get('_trigger').id, 0})
 ...
 box.space._index:get({box.space._space.index['name']:get('_sql_stat1').id, 0})
 ---
-- [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 'string']]]
+- [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 'unsigned']]]
 ...
 box.space._index:get({box.space._space.index['name']:get('_sql_stat4').id, 0})
 ---
-- [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 'string'], [5,
-      'scalar']]]
+- [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 'unsigned'],
+    [5, 'scalar']]]
 ...
 box.space._schema:format()
 ---
-- 
2.7.4

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

* [tarantool-patches] Re: [PATCH v2 1/1] sql: hold in stat tables space/index id instead of name
  2018-08-31 10:39 [tarantool-patches] [PATCH v2 1/1] sql: hold in stat tables space/index id instead of name imeevma
@ 2018-09-05 23:13 ` n.pettik
  2018-09-07 16:49   ` Imeev Mergen
  0 siblings, 1 reply; 17+ messages in thread
From: n.pettik @ 2018-09-05 23:13 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen


> diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
> index d9c2ae4..bb8dc36 100644
> --- a/src/box/lua/upgrade.lua
> +++ b/src/box/lua/upgrade.lua
> @@ -520,11 +520,11 @@ local function upgrade_to_2_1_0()
>     _index:insert{_trigger.id, 1, 'space_id', 'tree', { unique = false },
>                   {{1, 'unsigned'}}}
> 
> -    local stat1_ft = {{name='tbl', type='string'},
> -                      {name='idx', type='string'},
> +    local stat1_ft = {{name='tbl', type='unsigned'},
> +                      {name='idx', type='unsigned’},

Nit: why you didn’t rename these fields to ’space_id’ and ‘index_id’
(like in _index)? The same for stat4.

> @@ -793,8 +794,8 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
> 	int regChng = iMem++;	/* Index of changed index field */
> 	int regKey = iMem++;	/* Key argument passed to stat_push() */
> 	int regTemp = iMem++;	/* Temporary use register */
> -	int regTabname = iMem++;	/* Register containing table name */
> -	int regIdxname = iMem++;	/* Register containing index name */
> +	int reg_space_id = iMem++;	/* Register containing table id */
> +	int reg_index_id = iMem++;	/* Register containing index id */

Nit: put comments above the code to be commented and put dots
at the end of comment.

> @@ -1246,24 +1246,14 @@ analysis_loader(void *data, int argc, char **argv, char **unused)
> 	struct analysis_index_info *info = (struct analysis_index_info *) data;
> 	assert(info->stats != NULL);
> 	struct index_stat *stat = &info->stats[info->index_count++];
> -	uint32_t space_id = box_space_id_by_name(argv[0], strlen(argv[0]));
> +	uint32_t space_id = atoll(argv[0]);

I would add assertion like:
assert(space_id != 0);
Since atoll in case of fail returns 0 and BOX_ID_NIL != 0.

> 	if (space_id == BOX_ID_NIL)
> 		return -1;
> 	struct space *space = space_by_id(space_id);
> 	assert(space != NULL);
> 	struct index *index;
> -	uint32_t iid = box_index_id_by_name(space_id, argv[1], strlen(argv[1]));
> -	/*
> -	 * Convention is if index's name matches with space's
> -	 * one, then it is primary index.
> -	 */
> -	if (iid != BOX_ID_NIL) {
> -		index = space_index(space, iid);
> -	} else {
> -		if (sqlite3_stricmp(argv[0], argv[1]) != 0)
> -			return -1;
> -		index = space_index(space, 0);
> -	}
> +	uint32_t iid = atoll(argv[1]);
> +	index = space_index(space, iid);
> 	assert(index != NULL);
> 	/*
> 	 * Additional field is used to describe total
> @@ -1410,27 +1400,17 @@ load_stat_from_space(struct sqlite3 *db, const char *sql_select_prepare,
> 		goto finalize;
> 	uint32_t current_idx_count = 0;
> 	while (sqlite3_step(stmt) == SQLITE_ROW) {
> -		const char *space_name = (char *)sqlite3_column_text(stmt, 0);
> -		if (space_name == NULL)
> -			continue;
> -		const char *index_name = (char *)sqlite3_column_text(stmt, 1);
> -		if (index_name == NULL)
> +		if (sqlite3_column_text(stmt, 0) == NULL ||
> +		    sqlite3_column_text(stmt, 1) == NULL)
> 			continue;
> -		uint32_t sample_count = sqlite3_column_int(stmt, 2);
> -		uint32_t space_id = box_space_id_by_name(space_name,
> -							 strlen(space_name));
> +		uint32_t space_id = sqlite3_column_int(stmt, 0);

Why do you check on nullability text value, but fetch int?
It looks suspicious…
The same for other usages.

> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
> index a1e16b2..ece1092 100644
> --- a/src/box/sql/build.c
> +++ b/src/box/sql/build.c
> @@ -1888,75 +1888,45 @@ sql_store_select(struct Parse *parse_context, struct Select *select)
> 	parse_context->parsed_ast.select = select_copy;
> }
> 
> -/**
> - * Create expression record "@col_name = '@col_value'".
> - *
> - * @param parse The parsing context.
> - * @param col_name Name of column.
> - * @param col_value Name of row.
> - * @retval not NULL on success.
> - * @retval NULL on failure.
> - */
> -static struct Expr *
> -sql_id_eq_str_expr(struct Parse *parse, const char *col_name,
> -		   const char *col_value)
> -{
> -	struct sqlite3 *db = parse->db;
> -
> -	struct Expr *col_name_expr = sqlite3Expr(db, TK_ID, col_name);
> -	if (col_name_expr == NULL)
> -		return NULL;
> -	struct Expr *col_value_expr = sqlite3Expr(db, TK_STRING, col_value);
> -	if (col_value_expr == NULL) {
> -		sql_expr_delete(db, col_name_expr, false);
> -		return NULL;
> -	}
> -	return sqlite3PExpr(parse, TK_EQ, col_name_expr, col_value_expr);
> -}
> -
> void
> -vdbe_emit_stat_space_clear(struct Parse *parse, const char *stat_table_name,
> -			   const char *idx_name, const char *table_name)
> +sql_remove_from_stat(struct Parse *parse, const char *stat_table_name,
> +		     uint32_t space_id, uint32_t index_id)

Remove what? Why did you rename function?

> 
> /**
> - * Remove entries from the _sql_stat1 and _sql_stat4
> - * system spaces after a DROP INDEX or DROP TABLE command.
> + * Remove entries from the _sql_stat1 and _sql_stat4 system spaces
> + * after a DROP INDEX or DROP TABLE command.

Nit: noise diff.

>  *
> - * @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.
> + * @param parse The parsing context.
> + * @param table_name The table to be dropped or the table that
> + *        contains index to be dropped.

You forgot to rename args here.

> + * @param idx_name Index to be dropped.
>  */
> static void
> -sql_clear_stat_spaces(struct Parse *parse, const char *table_name,
> -		      const char *idx_name)
> +sql_clear_stat_spaces(struct Parse *parse, uint32_t space_id, uint32_t index_id)
> {
> -	vdbe_emit_stat_space_clear(parse, "_sql_stat4", idx_name, table_name);
> -	vdbe_emit_stat_space_clear(parse, "_sql_stat1", idx_name, table_name);
> +	sql_remove_from_stat(parse, TARANTOOL_SYS_SQL_STAT1_NAME, space_id,
> +			     index_id);
> +	sql_remove_from_stat(parse, TARANTOOL_SYS_SQL_STAT4_NAME, space_id,
> +			     index_id);

Don’t use this macros: it is awful and going to disappear.
Just simply inline name of stat space.

> }
> 
> /**
> @@ -2177,7 +2147,7 @@ sql_drop_table(struct Parse *parse_context, struct SrcList *table_name_list,
> 			goto exit_drop_table;
> 		}
> 	}
> -	sql_clear_stat_spaces(parse_context, space_name, NULL);
> +	sql_clear_stat_spaces(parse_context, space->def->id, BOX_ID_NIL);
> 	sql_code_drop_table(parse_context, space, is_view);
> 
>  exit_drop_table:
> @@ -3119,11 +3089,12 @@ sql_drop_index(struct Parse *parse_context, struct SrcList *index_name_list,
> 	}
> 
> 	/*
> -	 * Generate code to remove entry from _index space
> -	 * But firstly, delete statistics since schema
> -	 * changes after DDL.
> +	 * Generate code to remove entry from _index space.
> +	 * But firstly, delete statistics since schema changes
> +	 * after DDL.

Nit: again noise diff.

> diff --git a/test/sql-tap/analyze9.test.lua b/test/sql-tap/analyze9.test.lua
> index 1dbfe5d..2c6b4b8 100755
> --- a/test/sql-tap/analyze9.test.lua
> +++ b/test/sql-tap/analyze9.test.lua
> @@ -1,6 +1,6 @@
> #!/usr/bin/env tarantool
> test = require("sqltester")
> -test:plan(121)
> +test:plan(118)
> 
> -test:do_execsql_test(
> -    15.7,
> -    [[
> -        ANALYZE;
> -        UPDATE "_sql_stat1" SET "tbl" = 'no such tbl';
> -    ]])

Why did you simply delete this test? Rewrite it pls using space id.

> diff --git a/test/sql/sql-statN-index-drop.result b/test/sql/sql-statN-index-drop.result
> index a751eca..0e5f2a3 100644
> --- a/test/sql/sql-statN-index-drop.result
> +++ b/test/sql/sql-statN-index-drop.result
> @@ -31,36 +31,36 @@ box.sql.execute("ANALYZE;")
> ---
> ...
> -- Checking the data.
> -box.sql.execute("SELECT * FROM \"_sql_stat4\";")
> +box.sql.execute('SELECT "idx","neq","nlt","ndlt","sample" FROM \"_sql_stat4\";')
> ---
> -- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
> -  - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
> -  - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
> -  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
> +- - [0, '1', '0', '0', !!binary kQE=]
> +  - [1, '1', '0', '0', !!binary kQI=]
> +  - [0, '1', '0', '0', !!binary kQE=]
> +  - [1, '1', '0', '0', !!binary kQI=]
> ...
> -box.sql.execute("SELECT * FROM \"_sql_stat1\";")
> +box.sql.execute('SELECT "idx","stat" FROM \"_sql_stat1\";’)

Without space_id/space name this test is less representative.
The same for other similar tests.

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

* [tarantool-patches] Re: [PATCH v2 1/1] sql: hold in stat tables space/index id instead of name
  2018-09-05 23:13 ` [tarantool-patches] " n.pettik
@ 2018-09-07 16:49   ` Imeev Mergen
  2018-09-30 23:40     ` n.pettik
  0 siblings, 1 reply; 17+ messages in thread
From: Imeev Mergen @ 2018-09-07 16:49 UTC (permalink / raw)
  To: n.pettik, tarantool-patches; +Cc: Vladislav Shpilevoy

Hello! Thank you for review.


On 09/06/2018 02:13 AM, n.pettik wrote:
>> diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
>> index d9c2ae4..bb8dc36 100644
>> --- a/src/box/lua/upgrade.lua
>> +++ b/src/box/lua/upgrade.lua
>> @@ -520,11 +520,11 @@ local function upgrade_to_2_1_0()
>>      _index:insert{_trigger.id, 1, 'space_id', 'tree', { unique = false },
>>                    {{1, 'unsigned'}}}
>>
>> -    local stat1_ft = {{name='tbl', type='string'},
>> -                      {name='idx', type='string'},
>> +    local stat1_ft = {{name='tbl', type='unsigned'},
>> +                      {name='idx', type='unsigned’},
> Nit: why you didn’t rename these fields to ’space_id’ and ‘index_id’
> (like in _index)? The same for stat4.
Done.
>
>> @@ -793,8 +794,8 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
>> 	int regChng = iMem++;	/* Index of changed index field */
>> 	int regKey = iMem++;	/* Key argument passed to stat_push() */
>> 	int regTemp = iMem++;	/* Temporary use register */
>> -	int regTabname = iMem++;	/* Register containing table name */
>> -	int regIdxname = iMem++;	/* Register containing index name */
>> +	int reg_space_id = iMem++;	/* Register containing table id */
>> +	int reg_index_id = iMem++;	/* Register containing index id */
> Nit: put comments above the code to be commented and put dots
> at the end of comment.
Fixed.
>
>> @@ -1246,24 +1246,14 @@ analysis_loader(void *data, int argc, char **argv, char **unused)
>> 	struct analysis_index_info *info = (struct analysis_index_info *) data;
>> 	assert(info->stats != NULL);
>> 	struct index_stat *stat = &info->stats[info->index_count++];
>> -	uint32_t space_id = box_space_id_by_name(argv[0], strlen(argv[0]));
>> +	uint32_t space_id = atoll(argv[0]);
> I would add assertion like:
> assert(space_id != 0);
> Since atoll in case of fail returns 0 and BOX_ID_NIL != 0.
Actually now we cannot get BOX_ID_NIL, but we can get 0. Changed "if"
that placed next line after "atoll".
>
>> 	if (space_id == BOX_ID_NIL)
>> 		return -1;
>> 	struct space *space = space_by_id(space_id);
>> 	assert(space != NULL);
>> 	struct index *index;
>> -	uint32_t iid = box_index_id_by_name(space_id, argv[1], strlen(argv[1]));
>> -	/*
>> -	 * Convention is if index's name matches with space's
>> -	 * one, then it is primary index.
>> -	 */
>> -	if (iid != BOX_ID_NIL) {
>> -		index = space_index(space, iid);
>> -	} else {
>> -		if (sqlite3_stricmp(argv[0], argv[1]) != 0)
>> -			return -1;
>> -		index = space_index(space, 0);
>> -	}
>> +	uint32_t iid = atoll(argv[1]);
>> +	index = space_index(space, iid);
>> 	assert(index != NULL);
>> 	/*
>> 	 * Additional field is used to describe total
>> @@ -1410,27 +1400,17 @@ load_stat_from_space(struct sqlite3 *db, const char *sql_select_prepare,
>> 		goto finalize;
>> 	uint32_t current_idx_count = 0;
>> 	while (sqlite3_step(stmt) == SQLITE_ROW) {
>> -		const char *space_name = (char *)sqlite3_column_text(stmt, 0);
>> -		if (space_name == NULL)
>> -			continue;
>> -		const char *index_name = (char *)sqlite3_column_text(stmt, 1);
>> -		if (index_name == NULL)
>> +		if (sqlite3_column_text(stmt, 0) == NULL ||
>> +		    sqlite3_column_text(stmt, 1) == NULL)
>> 			continue;
>> -		uint32_t sample_count = sqlite3_column_int(stmt, 2);
>> -		uint32_t space_id = box_space_id_by_name(space_name,
>> -							 strlen(space_name));
>> +		uint32_t space_id = sqlite3_column_int(stmt, 0);
> Why do you check on nullability text value, but fetch int?
> It looks suspicious…
> The same for other usages.
Changed, now error occurs on space_id being 0.
>
>> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
>> index a1e16b2..ece1092 100644
>> --- a/src/box/sql/build.c
>> +++ b/src/box/sql/build.c
>> @@ -1888,75 +1888,45 @@ sql_store_select(struct Parse *parse_context, struct Select *select)
>> 	parse_context->parsed_ast.select = select_copy;
>> }
>>
>> -/**
>> - * Create expression record "@col_name = '@col_value'".
>> - *
>> - * @param parse The parsing context.
>> - * @param col_name Name of column.
>> - * @param col_value Name of row.
>> - * @retval not NULL on success.
>> - * @retval NULL on failure.
>> - */
>> -static struct Expr *
>> -sql_id_eq_str_expr(struct Parse *parse, const char *col_name,
>> -		   const char *col_value)
>> -{
>> -	struct sqlite3 *db = parse->db;
>> -
>> -	struct Expr *col_name_expr = sqlite3Expr(db, TK_ID, col_name);
>> -	if (col_name_expr == NULL)
>> -		return NULL;
>> -	struct Expr *col_value_expr = sqlite3Expr(db, TK_STRING, col_value);
>> -	if (col_value_expr == NULL) {
>> -		sql_expr_delete(db, col_name_expr, false);
>> -		return NULL;
>> -	}
>> -	return sqlite3PExpr(parse, TK_EQ, col_name_expr, col_value_expr);
>> -}
>> -
>> void
>> -vdbe_emit_stat_space_clear(struct Parse *parse, const char *stat_table_name,
>> -			   const char *idx_name, const char *table_name)
>> +sql_remove_from_stat(struct Parse *parse, const char *stat_table_name,
>> +		     uint32_t space_id, uint32_t index_id)
> Remove what? Why did you rename function?
Fixed.
>
>> /**
>> - * Remove entries from the _sql_stat1 and _sql_stat4
>> - * system spaces after a DROP INDEX or DROP TABLE command.
>> + * Remove entries from the _sql_stat1 and _sql_stat4 system spaces
>> + * after a DROP INDEX or DROP TABLE command.
> Nit: noise diff.
Fixed.
>
>>   *
>> - * @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.
>> + * @param parse The parsing context.
>> + * @param table_name The table to be dropped or the table that
>> + *        contains index to be dropped.
> You forgot to rename args here.
Fixed.
>
>> + * @param idx_name Index to be dropped.
>>   */
>> static void
>> -sql_clear_stat_spaces(struct Parse *parse, const char *table_name,
>> -		      const char *idx_name)
>> +sql_clear_stat_spaces(struct Parse *parse, uint32_t space_id, uint32_t index_id)
>> {
>> -	vdbe_emit_stat_space_clear(parse, "_sql_stat4", idx_name, table_name);
>> -	vdbe_emit_stat_space_clear(parse, "_sql_stat1", idx_name, table_name);
>> +	sql_remove_from_stat(parse, TARANTOOL_SYS_SQL_STAT1_NAME, space_id,
>> +			     index_id);
>> +	sql_remove_from_stat(parse, TARANTOOL_SYS_SQL_STAT4_NAME, space_id,
>> +			     index_id);
> Don’t use this macros: it is awful and going to disappear.
> Just simply inline name of stat space.
Fixed.
>
>> }
>>
>> /**
>> @@ -2177,7 +2147,7 @@ sql_drop_table(struct Parse *parse_context, struct SrcList *table_name_list,
>> 			goto exit_drop_table;
>> 		}
>> 	}
>> -	sql_clear_stat_spaces(parse_context, space_name, NULL);
>> +	sql_clear_stat_spaces(parse_context, space->def->id, BOX_ID_NIL);
>> 	sql_code_drop_table(parse_context, space, is_view);
>>
>>   exit_drop_table:
>> @@ -3119,11 +3089,12 @@ sql_drop_index(struct Parse *parse_context, struct SrcList *index_name_list,
>> 	}
>>
>> 	/*
>> -	 * Generate code to remove entry from _index space
>> -	 * But firstly, delete statistics since schema
>> -	 * changes after DDL.
>> +	 * Generate code to remove entry from _index space.
>> +	 * But firstly, delete statistics since schema changes
>> +	 * after DDL.
> Nit: again noise diff.
Fixed.
>
>> diff --git a/test/sql-tap/analyze9.test.lua b/test/sql-tap/analyze9.test.lua
>> index 1dbfe5d..2c6b4b8 100755
>> --- a/test/sql-tap/analyze9.test.lua
>> +++ b/test/sql-tap/analyze9.test.lua
>> @@ -1,6 +1,6 @@
>> #!/usr/bin/env tarantool
>> test = require("sqltester")
>> -test:plan(121)
>> +test:plan(118)
>>
>> -test:do_execsql_test(
>> -    15.7,
>> -    [[
>> -        ANALYZE;
>> -        UPDATE "_sql_stat1" SET "tbl" = 'no such tbl';
>> -    ]])
> Why did you simply delete this test? Rewrite it pls using space id.
Done.
>
>> diff --git a/test/sql/sql-statN-index-drop.result b/test/sql/sql-statN-index-drop.result
>> index a751eca..0e5f2a3 100644
>> --- a/test/sql/sql-statN-index-drop.result
>> +++ b/test/sql/sql-statN-index-drop.result
>> @@ -31,36 +31,36 @@ box.sql.execute("ANALYZE;")
>> ---
>> ...
>> -- Checking the data.
>> -box.sql.execute("SELECT * FROM \"_sql_stat4\";")
>> +box.sql.execute('SELECT "idx","neq","nlt","ndlt","sample" FROM \"_sql_stat4\";')
>> ---
>> -- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
>> -  - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
>> -  - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
>> -  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
>> +- - [0, '1', '0', '0', !!binary kQE=]
>> +  - [1, '1', '0', '0', !!binary kQI=]
>> +  - [0, '1', '0', '0', !!binary kQE=]
>> +  - [1, '1', '0', '0', !!binary kQI=]
>> ...
>> -box.sql.execute("SELECT * FROM \"_sql_stat1\";")
>> +box.sql.execute('SELECT "idx","stat" FROM \"_sql_stat1\";’)
> Without space_id/space name this test is less representative.
> The same for other similar tests.
Done.
>

commit 94d83afa5703a7ccf558202df752651646ba9c72
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Fri Aug 31 12:37:48 2018 +0300

     sql: hold in stat tables space/index id instead of name

     To avoid problems with table and index renaming it is good idea
     to save ids of tables and indexes instead of their names. Ids of
     tables and indexes are fixed values.

     Closes #3242

diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap
index 6573938..54ec5c7 100644
Binary files a/src/box/bootstrap.snap and b/src/box/bootstrap.snap differ
diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
index d9c2ae4..bc18b49 100644
--- a/src/box/lua/upgrade.lua
+++ b/src/box/lua/upgrade.lua
@@ -520,11 +520,11 @@ local function upgrade_to_2_1_0()
      _index:insert{_trigger.id, 1, 'space_id', 'tree', { unique = false },
                    {{1, 'unsigned'}}}

-    local stat1_ft = {{name='tbl', type='string'},
-                      {name='idx', type='string'},
+    local stat1_ft = {{name='space_id', type='unsigned'},
+                      {name='index_id', type='unsigned'},
                        {name='stat', type='string'}}
-    local stat4_ft = {{name='tbl', type='string'},
-                      {name='idx', type='string'},
+    local stat4_ft = {{name='space_id', type='unsigned'},
+                      {name='index_id', type='unsigned'},
                        {name='neq', type='string'},
                        {name='nlt', type='string'},
                        {name='ndlt', type='string'},
@@ -536,7 +536,7 @@ local function upgrade_to_2_1_0()

      log.info("create index primary on _sql_stat1")
      _index:insert{box.schema.SQL_STAT1_ID, 0, 'primary', 'tree',
-                  {unique = true}, {{0, 'string'}, {1, 'string'}}}
+                  {unique = true}, {{0, 'unsigned'}, {1, 'unsigned'}}}

      log.info("create space _sql_stat4")
      _space:insert{box.schema.SQL_STAT4_ID, ADMIN, '_sql_stat4', 
'memtx', 0,
@@ -544,7 +544,7 @@ local function upgrade_to_2_1_0()

      log.info("create index primary on _sql_stat4")
      _index:insert{box.schema.SQL_STAT4_ID, 0, 'primary', 'tree',
-                  {unique = true}, {{0, 'string'}, {1, 'string'},
+                  {unique = true}, {{0, 'unsigned'}, {1, 'unsigned'},
                                      {5, 'scalar'}}}

      local fk_constr_ft = {{name='name', type='string'},
diff --git a/src/box/schema.cc b/src/box/schema.cc
index a6e413a..8b71d65 100644
--- a/src/box/schema.cc
+++ b/src/box/schema.cc
@@ -383,13 +383,13 @@ schema_init()
      sc_space_new(BOX_INDEX_ID, "_index", key_def,
               &alter_space_on_replace_index, &on_stmt_begin_index);

-    /* space name */
+    /* space id */
      key_def_set_part(key_def, 0 /* part no */, 0 /* field no */,
-             FIELD_TYPE_STRING, ON_CONFLICT_ACTION_DEFAULT, NULL,
+             FIELD_TYPE_UNSIGNED, ON_CONFLICT_ACTION_DEFAULT, NULL,
               COLL_NONE, SORT_ORDER_ASC);
-    /* index name */
+    /* index id */
      key_def_set_part(key_def, 1 /* part no */, 1 /* field no */,
-             FIELD_TYPE_STRING, ON_CONFLICT_ACTION_DEFAULT, NULL,
+             FIELD_TYPE_UNSIGNED, ON_CONFLICT_ACTION_DEFAULT, NULL,
               COLL_NONE, SORT_ORDER_ASC);
      /* _sql_stat1 - a simpler statistics on space, seen in SQL. */
      sc_space_new(BOX_SQL_STAT1_ID, "_sql_stat1", key_def, NULL, NULL);
@@ -399,13 +399,13 @@ schema_init()
      if (key_def == NULL)
          diag_raise();

-    /* space name */
+    /* space id */
      key_def_set_part(key_def, 0 /* part no */, 0 /* field no */,
-             FIELD_TYPE_STRING, ON_CONFLICT_ACTION_DEFAULT, NULL,
+             FIELD_TYPE_UNSIGNED, ON_CONFLICT_ACTION_DEFAULT, NULL,
               COLL_NONE, SORT_ORDER_ASC);
-    /* index name */
+    /* index id */
      key_def_set_part(key_def, 1 /* part no */, 1 /* field no */,
-             FIELD_TYPE_STRING, ON_CONFLICT_ACTION_DEFAULT, NULL,
+             FIELD_TYPE_UNSIGNED, ON_CONFLICT_ACTION_DEFAULT, NULL,
               COLL_NONE, SORT_ORDER_ASC);
      /* sample */
      key_def_set_part(key_def, 2 /* part no */, 5 /* field no */,
diff --git a/src/box/sql.c b/src/box/sql.c
index b158c50..87f2088 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -1217,21 +1217,21 @@ void tarantoolSqlite3LoadSchema(struct init_data 
*init)
      sql_init_callback(init, TARANTOOL_SYS_SQL_STAT1_NAME,
                BOX_SQL_STAT1_ID, 0,
                "CREATE TABLE \""TARANTOOL_SYS_SQL_STAT1_NAME
-                   "\"(\"tbl\" text,"
-                   "\"idx\" text,"
+                   "\"(\"space_id\" INT,"
+                   "\"index_id\" INT,"
                     "\"stat\" not null,"
-                   "PRIMARY KEY(\"tbl\", \"idx\"))");
+                   "PRIMARY KEY(\"space_id\", \"index_id\"))");

      sql_init_callback(init, TARANTOOL_SYS_SQL_STAT4_NAME,
                BOX_SQL_STAT4_ID, 0,
                "CREATE TABLE \""TARANTOOL_SYS_SQL_STAT4_NAME
-                   "\"(\"tbl\" text,"
-                   "\"idx\" text,"
+                   "\"(\"space_id\" INT,"
+                   "\"index_id\" INT,"
                     "\"neq\" text,"
                     "\"nlt\" text,"
                     "\"ndlt\" text,"
                     "\"sample\","
-                   "PRIMARY KEY(\"tbl\", \"idx\", \"sample\"))");
+                   "PRIMARY KEY(\"space_id\", \"index_id\", \"sample\"))");

      sql_init_callback(init, TARANTOOL_SYS_FK_CONSTRAINT_NAME,
                BOX_FK_CONSTRAINT_ID, 0,
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index 76ae153..6938dcb 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -38,8 +38,8 @@
   *
   * The following system tables are or have been supported:
   *
- *    CREATE TABLE _sql_stat1(tbl, idx, stat);
- *    CREATE TABLE _sql_stat4(tbl, idx, nEq, nLt, nDLt, sample);
+ *    CREATE TABLE _sql_stat1(space_id, index_id, stat);
+ *    CREATE TABLE _sql_stat4(space_id, index_id, nEq, nLt, nDLt, sample);
   *
   * For most applications, _sql_stat1 provides all the statistics required
   * for the query planner to make good choices.
@@ -47,7 +47,7 @@
   * Format of _sql_stat1:
   *
   * There is normally one row per index, with the index identified by the
- * name in the idx column.  The tbl column is the name of the table to
+ * name in the index_id column.  The space_id column is the name of the 
table to
   * which the index belongs.  In each such row, the stat column will be
   * a string consisting of a list of integers.  The first integer in this
   * list is the number of rows in the index.  (This is the same as the
@@ -66,9 +66,9 @@
   * "unordered" keyword is present, then the query planner assumes that
   * the index is unordered and will not use the index for a range query.
   *
- * If the _sql_stat1.idx column is NULL, then the _sql_stat1.stat
+ * If the _sql_stat1.index_id column is NULL, then the _sql_stat1.stat
   * column contains a single integer which is the (estimated) number of
- * rows in the table identified by _sql_stat1.tbl.
+ * rows in the table identified by _sql_stat1.space_id.
   *
   * Format for _sql_stat4:
   *
@@ -78,9 +78,9 @@
   * queries.
   *
   * The _sql_stat4 table contains multiple entries for each index.
- * The idx column names the index and the tbl column is the table of the
- * index.  If the idx and tbl columns are the same, then the sample is
- * of the INTEGER PRIMARY KEY.  The sample column is a blob which is the
+ * The index_id column names the index and the space_id column is the 
table of
+ * the index. If the index_id and space_id columns are the same, then 
the sample
+ * is of the INTEGER PRIMARY KEY.  The sample column is a blob which is the
   * binary encoding of a key from the index.  The nEq column is a
   * list of integers.  The first integer is the approximate number
   * of entries in the index whose left-most column exactly matches
@@ -124,11 +124,12 @@
   * @param parse Parsing context.
   * @param stat_cursor Open the _sql_stat1 table on this cursor.
   *        you should allocate |stat_names| cursors before call.
- * @param table_name Delete records of this index if specified.
+ * @param space_id Delete records of this table if id is not
+ *        BOX_ID_NIL.
   */
  static void
  vdbe_emit_stat_space_open(struct Parse *parse, int stat_cursor,
-              const char *table_name)
+              uint32_t space_id)
  {
      const char *stat_names[] = {"_sql_stat1", "_sql_stat4"};
      const uint32_t stat_ids[] = {BOX_SQL_STAT1_ID, BOX_SQL_STAT4_ID};
@@ -136,16 +137,15 @@ vdbe_emit_stat_space_open(struct Parse *parse, int 
stat_cursor,
      assert(v != NULL);
      assert(sqlite3VdbeDb(v) == parse->db);
      for (uint i = 0; i < lengthof(stat_names); ++i) {
-        const char *space_name = stat_names[i];
          /*
           * The table already exists, because it is a
           * system space.
           */
assert(sqlite3HashFind(&parse->db->pSchema->tblHash,
-                       space_name) != NULL);
-        if (table_name != NULL) {
-            vdbe_emit_stat_space_clear(parse, space_name, NULL,
-                           table_name);
+                       stat_names[i]) != NULL);
+        if (space_id != BOX_ID_NIL) {
+            vdbe_emit_stat_space_clear(parse, stat_names[i],
+                           space_id, BOX_ID_NIL);
          } else {
              sqlite3VdbeAddOp1(v, OP_Clear, stat_ids[i]);
          }
@@ -793,8 +793,10 @@ analyzeOneTable(Parse * pParse,    /* Parser context */
      int regChng = iMem++;    /* Index of changed index field */
      int regKey = iMem++;    /* Key argument passed to stat_push() */
      int regTemp = iMem++;    /* Temporary use register */
-    int regTabname = iMem++;    /* Register containing table name */
-    int regIdxname = iMem++;    /* Register containing index name */
+    /* Register containing table id. */
+    int reg_space_id = iMem++;
+    /* Register containing index id. */
+    int reg_index_id = iMem++;
      int regStat1 = iMem++;    /* Value for the stat column of 
_sql_stat1 */
      int regPrev = iMem;    /* MUST BE LAST (see below) */

@@ -818,7 +820,7 @@ analyzeOneTable(Parse * pParse,    /* Parser context */
      iIdxCur = iTab++;
      pParse->nTab = MAX(pParse->nTab, iTab);
      sqlite3OpenTable(pParse, iTabCur, pTab, OP_OpenRead);
-    sqlite3VdbeLoadString(v, regTabname, pTab->def->name);
+    sqlite3VdbeAddOp2(v, OP_Integer, pTab->def->id, reg_space_id);

      for (pIdx = pTab->pIndex; pIdx; pIdx = pIdx->pNext) {
          int addrRewind;    /* Address of "OP_Rewind iIdxCur" */
@@ -837,8 +839,8 @@ analyzeOneTable(Parse * pParse,    /* Parser context */
              idx_name = pIdx->def->name;
          int part_count = pIdx->def->key_def->part_count;

-        /* Populate the register containing the index name. */
-        sqlite3VdbeLoadString(v, regIdxname, idx_name);
+        /* Populate the register containing the index id. */
+        sqlite3VdbeAddOp2(v, OP_Integer, pIdx->def->iid, reg_index_id);
          VdbeComment((v, "Analysis for %s.%s", pTab->def->name,
                  idx_name));

@@ -1015,9 +1017,8 @@ analyzeOneTable(Parse * pParse,    /* Parser 
context */

          /* Add the entry to the stat1 table. */
          callStatGet(v, regStat4, STAT_GET_STAT1, regStat1);
-        assert("BBB"[0] == AFFINITY_TEXT);
-        sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp,
-                  "BBB", 0);
+        sqlite3VdbeAddOp4(v, OP_MakeRecord, reg_space_id, 3, regTemp,
+                  "DDB", 0);
          sqlite3VdbeAddOp2(v, OP_IdxInsert, iStatCur, regTemp);

          /* Add the entries to the stat4 table. */
@@ -1053,7 +1054,7 @@ analyzeOneTable(Parse * pParse,    /* Parser 
context */
          }
          sqlite3VdbeAddOp3(v, OP_MakeRecord, regCol, part_count,
                    regSample);
-        sqlite3VdbeAddOp3(v, OP_MakeRecord, regTabname, 6, regTemp);
+        sqlite3VdbeAddOp3(v, OP_MakeRecord, reg_space_id, 6, regTemp);
          sqlite3VdbeAddOp2(v, OP_IdxReplace, iStatCur + 1, regTemp);
          sqlite3VdbeAddOp2(v, OP_Goto, 1, addrNext);    /* P1==1 for 
end-of-loop */
          sqlite3VdbeJumpHere(v, addrIsNull);
@@ -1086,7 +1087,7 @@ sql_analyze_database(Parse *parser)
      sql_set_multi_write(parser, false);
      int stat_cursor = parser->nTab;
      parser->nTab += 3;
-    vdbe_emit_stat_space_open(parser, stat_cursor, NULL);
+    vdbe_emit_stat_space_open(parser, stat_cursor, BOX_ID_NIL);
      int reg = parser->nMem + 1;
      int tab_cursor = parser->nTab;
      for (struct HashElem *k = sqliteHashFirst(&schema->tblHash); k != 
NULL;
@@ -1114,7 +1115,7 @@ vdbe_emit_analyze_table(struct Parse *parse, 
struct Table *table)
      sql_set_multi_write(parse, false);
      int stat_cursor = parse->nTab;
      parse->nTab += 3;
-    vdbe_emit_stat_space_open(parse, stat_cursor, table->def->name);
+    vdbe_emit_stat_space_open(parse, stat_cursor, table->def->id);
      analyzeOneTable(parse, table, NULL, stat_cursor, parse->nMem + 1,
              parse->nTab);
      loadAnalysis(parse);
@@ -1246,24 +1247,14 @@ analysis_loader(void *data, int argc, char 
**argv, char **unused)
      struct analysis_index_info *info = (struct analysis_index_info *) 
data;
      assert(info->stats != NULL);
      struct index_stat *stat = &info->stats[info->index_count++];
-    uint32_t space_id = box_space_id_by_name(argv[0], strlen(argv[0]));
-    if (space_id == BOX_ID_NIL)
+    uint32_t space_id = atoll(argv[0]);
+    if (space_id == 0)
          return -1;
      struct space *space = space_by_id(space_id);
      assert(space != NULL);
      struct index *index;
-    uint32_t iid = box_index_id_by_name(space_id, argv[1], 
strlen(argv[1]));
-    /*
-     * Convention is if index's name matches with space's
-     * one, then it is primary index.
-     */
-    if (iid != BOX_ID_NIL) {
-        index = space_index(space, iid);
-    } else {
-        if (sqlite3_stricmp(argv[0], argv[1]) != 0)
-            return -1;
-        index = space_index(space, 0);
-    }
+    uint32_t iid = atoll(argv[1]);
+    index = space_index(space, iid);
      assert(index != NULL);
      /*
       * Additional field is used to describe total
@@ -1377,8 +1368,12 @@ sample_compare(const void *a, const void *b, void 
*arg)
   * Arguments must point to SQL statements that return
   * data equivalent to the following:
   *
- *    prepare: SELECT tbl,idx,count(*) FROM _sql_stat4 GROUP BY tbl,idx;
- *    load: SELECT tbl,idx,neq,nlt,ndlt,sample FROM _sql_stat4;
+ * prepare:
+ *     SELECT space_id,index_id,count(*) FROM _sql_stat4
+ *     GROUP BY space_id,index_id;
+ * load:
+ *     SELECT space_id,index_id,neq,nlt,ndlt,sample
+ *     FROM _sql_stat4;
   *
   * 'prepare' statement is used to allocate enough memory for
   * statistics (i.e. arrays lt, dt, dlt and avg_eq). 'load' query
@@ -1410,27 +1405,14 @@ load_stat_from_space(struct sqlite3 *db, const 
char *sql_select_prepare,
          goto finalize;
      uint32_t current_idx_count = 0;
      while (sqlite3_step(stmt) == SQLITE_ROW) {
-        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
-        if (space_name == NULL)
-            continue;
-        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
-        if (index_name == NULL)
-            continue;
-        uint32_t sample_count = sqlite3_column_int(stmt, 2);
-        uint32_t space_id = box_space_id_by_name(space_name,
-                             strlen(space_name));
-        assert(space_id != BOX_ID_NIL);
+        uint32_t space_id = sqlite3_column_int(stmt, 0);
+        assert(space_id != BOX_ID_NIL && space_id != 0);
          struct space *space = space_by_id(space_id);
          assert(space != NULL);
-        struct index *index;
-        uint32_t iid = box_index_id_by_name(space_id, index_name,
-                            strlen(index_name));
-        if (sqlite3_stricmp(space_name, index_name) == 0 &&
-            iid == BOX_ID_NIL)
-            index = space_index(space, 0);
-        else
-            index = space_index(space, iid);
+        uint32_t iid = sqlite3_column_int(stmt, 1);
+        struct index *index = space_index(space, iid);
          assert(index != NULL);
+        uint32_t sample_count = sqlite3_column_int(stmt, 2);
          uint32_t column_count = index->def->key_def->part_count;
          struct index_stat *stat = &stats[current_idx_count];
          stat->sample_field_count = column_count;
@@ -1482,27 +1464,12 @@ load_stat_from_space(struct sqlite3 *db, const 
char *sql_select_prepare,
      struct index *prev_index = NULL;
      current_idx_count = 0;
      while (sqlite3_step(stmt) == SQLITE_ROW) {
-        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
-        if (space_name == NULL)
-            continue;
-        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
-        if (index_name == NULL)
-            continue;
-        uint32_t space_id = box_space_id_by_name(space_name,
-                             strlen(space_name));
-        assert(space_id != BOX_ID_NIL);
+        uint32_t space_id = sqlite3_column_int(stmt, 0);
+        assert(space_id != BOX_ID_NIL && space_id != 0);
          struct space *space = space_by_id(space_id);
          assert(space != NULL);
-        struct index *index;
-        uint32_t iid = box_index_id_by_name(space_id, index_name,
-                            strlen(index_name));
-        if (iid != BOX_ID_NIL) {
-            index = space_index(space, iid);
-        } else {
-            if (sqlite3_stricmp(space_name, index_name) != 0)
-                return -1;
-            index = space_index(space, 0);
-        }
+        uint32_t iid = sqlite3_column_int(stmt, 1);
+        struct index *index = space_index(space, iid);
          assert(index != NULL);
          uint32_t column_count = index->def->key_def->part_count;
          if (index != prev_index) {
@@ -1566,28 +1533,13 @@ load_stat_to_index(struct sqlite3 *db, const 
char *sql_select_load,
          return -1;
      uint32_t current_idx_count = 0;
      while (sqlite3_step(stmt) == SQLITE_ROW) {
-        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
-        if (space_name == NULL)
-            continue;
-        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
-        if (index_name == NULL)
-            continue;
-        uint32_t space_id = box_space_id_by_name(space_name,
-                             strlen(space_name));
-        if (space_id == BOX_ID_NIL)
+        uint32_t space_id = sqlite3_column_int(stmt, 0);
+        if (space_id == BOX_ID_NIL || space_id == 0)
              return -1;
          struct space *space = space_by_id(space_id);
          assert(space != NULL);
-        struct index *index;
-        uint32_t iid = box_index_id_by_name(space_id, index_name,
-                            strlen(index_name));
-        if (iid != BOX_ID_NIL) {
-            index = space_index(space, iid);
-        } else {
-            if (sqlite3_stricmp(space_name, index_name) != 0)
-                return -1;
-            index = space_index(space, 0);
-        }
+        uint32_t iid = sqlite3_column_int(stmt, 1);
+        struct index *index = space_index(space, iid);
          assert(index != NULL);
          free(index->def->opts.stat);
          index->def->opts.stat = stats[current_idx_count++];
@@ -1727,7 +1679,7 @@ sql_analysis_load(struct sqlite3 *db)
      info.stats = stats;
      info.index_count = 0;
      const char *load_stat1 =
-        "SELECT \"tbl\",\"idx\",\"stat\" FROM \"_sql_stat1\"";
+        "SELECT \"space_id\",\"index_id\",\"stat\" FROM \"_sql_stat1\"";
      /* Load new statistics out of the _sql_stat1 table. */
      if (sqlite3_exec(db, load_stat1, analysis_loader, &info, 0) != 0)
          goto fail;
@@ -1740,10 +1692,10 @@ sql_analysis_load(struct sqlite3 *db)
       * statistics. Result rows are given in a form:
       * <table name>, <index name>, <count of samples>
       */
-    const char *init_query = "SELECT \"tbl\",\"idx\",count(*) FROM "
-                 "\"_sql_stat4\" GROUP BY \"tbl\",\"idx\"";
+    const char *init_query = "SELECT \"space_id\",\"index_id\",count(*) 
FROM "
+                 "\"_sql_stat4\" GROUP BY \"space_id\",\"index_id\"";
      /* Query for loading statistics into in-memory structs. */
-    const char *load_query = "SELECT \"tbl\",\"idx\",\"neq\",\"nlt\","
+    const char *load_query = "SELECT 
\"space_id\",\"index_id\",\"neq\",\"nlt\","
                   "\"ndlt\",\"sample\" FROM \"_sql_stat4\"";
      /* Load the statistics from the _sql_stat4 table. */
      if (load_stat_from_space(db, init_query, load_query, stats) != 0)
@@ -1787,8 +1739,8 @@ sql_analysis_load(struct sqlite3 *db)
       * Ordered query is needed to be sure that indexes come
       * in the same order as in previous SELECTs.
       */
-    const char *order_query = "SELECT \"tbl\",\"idx\" FROM "
-                  "\"_sql_stat4\" GROUP BY \"tbl\",\"idx\"";
+    const char *order_query = "SELECT \"space_id\",\"index_id\" FROM "
+                  "\"_sql_stat4\" GROUP BY \"space_id\",\"index_id\"";
      if (load_stat_to_index(db, order_query, heap_stats) != 0)
          goto fail;
      if (box_txn_commit() != 0)
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index a1e16b2..5aea4ea 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -1888,75 +1888,43 @@ sql_store_select(struct Parse *parse_context, 
struct Select *select)
      parse_context->parsed_ast.select = select_copy;
  }

-/**
- * Create expression record "@col_name = '@col_value'".
- *
- * @param parse The parsing context.
- * @param col_name Name of column.
- * @param col_value Name of row.
- * @retval not NULL on success.
- * @retval NULL on failure.
- */
-static struct Expr *
-sql_id_eq_str_expr(struct Parse *parse, const char *col_name,
-           const char *col_value)
-{
-    struct sqlite3 *db = parse->db;
-
-    struct Expr *col_name_expr = sqlite3Expr(db, TK_ID, col_name);
-    if (col_name_expr == NULL)
-        return NULL;
-    struct Expr *col_value_expr = sqlite3Expr(db, TK_STRING, col_value);
-    if (col_value_expr == NULL) {
-        sql_expr_delete(db, col_name_expr, false);
-        return NULL;
-    }
-    return sqlite3PExpr(parse, TK_EQ, col_name_expr, col_value_expr);
-}
-
  void
  vdbe_emit_stat_space_clear(struct Parse *parse, const char 
*stat_table_name,
-               const char *idx_name, const char *table_name)
+               uint32_t space_id, uint32_t index_id)
  {
-    assert(idx_name != NULL || table_name != NULL);
      struct sqlite3 *db = parse->db;
      assert(!db->mallocFailed);
      struct SrcList *src_list = sql_alloc_src_list(db);
      if (src_list != NULL)
          src_list->a[0].zName = sqlite3DbStrDup(db, stat_table_name);
-    struct Expr *where = NULL;
-    if (idx_name != NULL) {
-        struct Expr *expr = sql_id_eq_str_expr(parse, "idx", idx_name);
-        if (expr != NULL)
-            where = sqlite3ExprAnd(db, expr, where);
-    }
-    if (table_name != NULL) {
-        struct Expr *expr = sql_id_eq_str_expr(parse, "tbl", table_name);
-        if (expr != NULL)
-            where = sqlite3ExprAnd(db, expr, where);
-    }
-    /**
-     * On memory allocation error sql_table delete_from
-     * releases memory for its own.
-     */
-    sql_table_delete_from(parse, src_list, where);
+    struct Expr *expr = NULL;
+    struct Expr *col = sqlite3Expr(db, TK_ID, "space_id");
+    struct Expr *val = sqlite3ExprInteger(db, space_id);
+    if (col != NULL && val != NULL)
+        expr = sqlite3PExpr(parse, TK_EQ, col, val);
+    if (index_id != BOX_ID_NIL && expr != NULL) {
+        col = sqlite3Expr(db, TK_ID, "index_id");
+        val = sqlite3ExprInteger(db, index_id);
+        expr = sqlite3ExprAnd(db, sqlite3PExpr(parse, TK_EQ, col, val),
+                      expr);
+    }
+    sql_table_delete_from(parse, src_list, expr);
  }

  /**
   * Remove entries from the _sql_stat1 and _sql_stat4
   * system spaces after a DROP INDEX or DROP TABLE command.
   *
- * @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.
+ * @param parse The parsing context.
+ * @param space_id Id of table to be dropped or table that
+ *        contains index to be dropped.
+ * @param index_id Id of index to be dropped.
   */
  static void
-sql_clear_stat_spaces(struct Parse *parse, const char *table_name,
-              const char *idx_name)
+sql_clear_stat_spaces(struct Parse *parse, uint32_t space_id, uint32_t 
index_id)
  {
-    vdbe_emit_stat_space_clear(parse, "_sql_stat4", idx_name, table_name);
-    vdbe_emit_stat_space_clear(parse, "_sql_stat1", idx_name, table_name);
+    vdbe_emit_stat_space_clear(parse, "_sql_stat4", space_id, index_id);
+    vdbe_emit_stat_space_clear(parse, "_sql_stat1", space_id, index_id);
  }

  /**
@@ -2177,7 +2145,7 @@ sql_drop_table(struct Parse *parse_context, struct 
SrcList *table_name_list,
              goto exit_drop_table;
          }
      }
-    sql_clear_stat_spaces(parse_context, space_name, NULL);
+    sql_clear_stat_spaces(parse_context, space->def->id, BOX_ID_NIL);
      sql_code_drop_table(parse_context, space, is_view);

   exit_drop_table:
@@ -3123,7 +3091,8 @@ sql_drop_index(struct Parse *parse_context, struct 
SrcList *index_name_list,
       * But firstly, delete statistics since schema
       * changes after DDL.
       */
-    sql_clear_stat_spaces(parse_context, table_name, index->def->name);
+    sql_clear_stat_spaces(parse_context, space->def->id, index->def->iid);
+
      int record_reg = ++parse_context->nMem;
      int space_id_reg = ++parse_context->nMem;
      sqlite3VdbeAddOp2(v, OP_Integer, space_id, space_id_reg);
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 1d32c9a..9bfa259 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -4862,11 +4862,13 @@ vdbe_emit_halt_with_presence_test(struct Parse 
*parser, int space_id,
   *
   * @param parse The parsing context.
   * @param stat_table_name System stat table name.
- * @param idx_name Index name.
- * @param table_name Table name.
+ * @param table_id Id of table of which analysis will be deleted.
+ * @param index_id Id of index of which analysis will be deleted.
+ *        If index_id is BOX_ID_NIL than analysis of all indexes
+ *        of selected table will be deleted.
   */
  void
  vdbe_emit_stat_space_clear(struct Parse *parse, const char 
*stat_table_name,
-               const char *idx_name, const char *table_name);
+               uint32_t table_id, uint32_t index_id);

  #endif                /* SQLITEINT_H */
diff --git a/test/box-py/bootstrap.result b/test/box-py/bootstrap.result
index 506aca3..b5850c2 100644
--- a/test/box-py/bootstrap.result
+++ b/test/box-py/bootstrap.result
@@ -74,11 +74,12 @@ box.space._space:select{}
          'type': 'unsigned'}]]
    - [340, 1, '_space_sequence', 'memtx', 0, {}, [{'name': 'id', 
'type': 'unsigned'},
        {'name': 'sequence_id', 'type': 'unsigned'}, {'name': 
'is_generated', 'type': 'boolean'}]]
-  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'stat', 'type': 'string'}]]
-  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'neq', 'type': 'string'}, {'name': 
'nlt', 'type': 'string'},
-      {'name': 'ndlt', 'type': 'string'}, {'name': 'sample', 'type': 
'scalar'}]]
+  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'space_id', 
'type': 'unsigned'},
+      {'name': 'index_id', 'type': 'unsigned'}, {'name': 'stat', 
'type': 'string'}]]
+  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'space_id', 
'type': 'unsigned'},
+      {'name': 'index_id', 'type': 'unsigned'}, {'name': 'neq', 'type': 
'string'},
+      {'name': 'nlt', 'type': 'string'}, {'name': 'ndlt', 'type': 
'string'}, {'name': 'sample',
+        'type': 'scalar'}]]
    - [356, 1, '_fk_constraint', 'memtx', 0, {}, [{'name': 'name', 
'type': 'string'},
        {'name': 'child_id', 'type': 'unsigned'}, {'name': 'parent_id', 
'type': 'unsigned'},
        {'name': 'is_deferred', 'type': 'boolean'}, {'name': 'match', 
'type': 'string'},
@@ -134,9 +135,9 @@ box.space._index:select{}
    - [330, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
    - [340, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
    - [340, 1, 'sequence', 'tree', {'unique': false}, [[1, 'unsigned']]]
-  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string']]]
-  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string'], [
-        5, 'scalar']]]
+  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned']]]
+  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned'],
+      [5, 'scalar']]]
    - [356, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'unsigned']]]
    - [356, 1, 'child_id', 'tree', {'unique': false}, [[1, 'unsigned']]]
  ...
diff --git a/test/box/access_misc.result b/test/box/access_misc.result
index 87ccc46..a55ec49 100644
--- a/test/box/access_misc.result
+++ b/test/box/access_misc.result
@@ -813,11 +813,12 @@ box.space._space:select()
          'type': 'unsigned'}]]
    - [340, 1, '_space_sequence', 'memtx', 0, {}, [{'name': 'id', 
'type': 'unsigned'},
        {'name': 'sequence_id', 'type': 'unsigned'}, {'name': 
'is_generated', 'type': 'boolean'}]]
-  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'stat', 'type': 'string'}]]
-  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'neq', 'type': 'string'}, {'name': 
'nlt', 'type': 'string'},
-      {'name': 'ndlt', 'type': 'string'}, {'name': 'sample', 'type': 
'scalar'}]]
+  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'space_id', 
'type': 'unsigned'},
+      {'name': 'index_id', 'type': 'unsigned'}, {'name': 'stat', 
'type': 'string'}]]
+  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'space_id', 
'type': 'unsigned'},
+      {'name': 'index_id', 'type': 'unsigned'}, {'name': 'neq', 'type': 
'string'},
+      {'name': 'nlt', 'type': 'string'}, {'name': 'ndlt', 'type': 
'string'}, {'name': 'sample',
+        'type': 'scalar'}]]
    - [356, 1, '_fk_constraint', 'memtx', 0, {}, [{'name': 'name', 
'type': 'string'},
        {'name': 'child_id', 'type': 'unsigned'}, {'name': 'parent_id', 
'type': 'unsigned'},
        {'name': 'is_deferred', 'type': 'boolean'}, {'name': 'match', 
'type': 'string'},
diff --git a/test/box/alter.result b/test/box/alter.result
index 9a1086e..86e8c3a 100644
--- a/test/box/alter.result
+++ b/test/box/alter.result
@@ -228,9 +228,9 @@ _index:select{}
    - [330, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
    - [340, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
    - [340, 1, 'sequence', 'tree', {'unique': false}, [[1, 'unsigned']]]
-  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string']]]
-  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string'], [
-        5, 'scalar']]]
+  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned']]]
+  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned'],
+      [5, 'scalar']]]
    - [356, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'unsigned']]]
    - [356, 1, 'child_id', 'tree', {'unique': false}, [[1, 'unsigned']]]
  ...
diff --git a/test/sql-tap/analyze1.test.lua b/test/sql-tap/analyze1.test.lua
index dab7255..afd8901 100755
--- a/test/sql-tap/analyze1.test.lua
+++ b/test/sql-tap/analyze1.test.lua
@@ -65,7 +65,7 @@ test:do_execsql_test(
  test:do_execsql_test(
      "analyze-1.7",
      [[
-        SELECT * FROM "_sql_stat1" WHERE "idx" IS NOT NULL
+        SELECT * FROM "_sql_stat1" WHERE "index_id" IS NOT NULL
      ]], {
          -- <analyze-1.7>
          -- </analyze-1.7>
@@ -84,7 +84,7 @@ test:do_catchsql_test(
  test:do_execsql_test(
      "analyze-1.9",
      [[
-        SELECT * FROM "_sql_stat1" WHERE "idx" IS NOT NULL
+        SELECT * FROM "_sql_stat1" WHERE "index_id" IS NOT NULL
      ]], {
          -- <analyze-1.9>
          -- </analyze-1.9>
@@ -119,7 +119,7 @@ test:do_execsql_test(
      [[
          CREATE INDEX t1i1 ON t1(a);
          ANALYZE t1;
-        SELECT * FROM "_sql_stat1" ORDER BY "idx";
+        SELECT * FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-2.1>
          -- </analyze-2.1>
@@ -130,7 +130,7 @@ test:do_execsql_test(
      [[
          CREATE INDEX t1i2 ON t1(b);
          ANALYZE t1;
-        SELECT * FROM "_sql_stat1" ORDER BY "idx";
+        SELECT * FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-2.2>
          -- </analyze-2.2>
@@ -141,7 +141,7 @@ test:do_execsql_test(
      [[
          CREATE INDEX t1i3 ON t1(a,b);
          ANALYZE;
-        SELECT * FROM "_sql_stat1" ORDER BY "idx";
+        SELECT * FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-2.3>
          -- </analyze-2.3>
@@ -157,10 +157,10 @@ test:do_execsql_test(
          INSERT INTO t1 VALUES(1, 1,2);
          INSERT INTO t1 VALUES(2, 1,3);
          ANALYZE t1;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.1>
-        "T1", "2 1", "T1I1", "2 2", "T1I2", "2 1", "T1I3", "2 2 1"
+        0, "2 1", 1, "2 2", 2, "2 1", 3, "2 2 1"
          -- </analyze-3.1>
      })

@@ -170,10 +170,10 @@ test:do_execsql_test(
          INSERT INTO t1 VALUES(3, 1,4);
          INSERT INTO t1 VALUES(4, 1,5);
          ANALYZE t1;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.2>
-        "T1", "4 1", "T1I1", "4 4", "T1I2", "4 1", "T1I3", "4 4 1"
+        0, "4 1", 1, "4 4", 2, "4 1", 3, "4 4 1"
          -- </analyze-3.2>
      })

@@ -182,10 +182,10 @@ test:do_execsql_test(
      [[
          INSERT INTO t1 (a,b) VALUES(2,5);
          ANALYZE;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.3>
-        "T1","5 1", "T1I1", "5 3", "T1I2", "5 2", "T1I3", "5 3 1"
+        0,"5 1", 1, "5 3", 2, "5 2", 3, "5 3 1"
          -- </analyze-3.3>
      })

@@ -198,10 +198,10 @@ test:do_execsql_test(
          CREATE INDEX t2i2 ON t2(b);
          CREATE INDEX t2i3 ON t2(a,b);
          ANALYZE;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.4>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 
1","T2I1","5 3","T2I2","5 2","T2I3","5 3 1"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",2,"5 2",3,"5 3 1",3,"5 3 1"
          -- </analyze-3.4>
      })

@@ -210,10 +210,10 @@ test:do_execsql_test(
      [[
          DROP INDEX t2i3 ON t2;;
          ANALYZE t1;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.5>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 
1","T2I1","5 3","T2I2","5 2"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",2,"5 2",3,"5 3 1"
          -- </analyze-3.5>
      })

@@ -221,10 +221,10 @@ test:do_execsql_test(
      "analyze-3.6",
      [[
          ANALYZE t2;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.6>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 
1","T2I1","5 3","T2I2","5 2"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",2,"5 2",3,"5 3 1"
          -- </analyze-3.6>
      })

@@ -233,10 +233,10 @@ test:do_execsql_test(
      [[
          DROP INDEX t2i2 ON t2;
          ANALYZE t2;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.7>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 
1","T2I1","5 3"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",3,"5 3 1"
          -- </analyze-3.7>
      })

@@ -250,7 +250,7 @@ test:do_execsql_test(
          CREATE INDEX t3i3 ON t3(d,b,c,a);
          DROP TABLE t1;
          DROP TABLE t2;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.8>
          -- </analyze-3.8>
@@ -260,10 +260,10 @@ test:do_execsql_test(
      "analyze-3.9",
      [[
          ANALYZE;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.9>
-        "T3","5 1","T3I1","5 3","T3I2","5 3 1 1 1","T3I3","5 5 2 1 1"
+        0,"5 1",1,"5 3",2,"5 3 1 1 1",3,"5 5 2 1 1"
          -- </analyze-3.9>
      })

@@ -277,7 +277,7 @@ test:do_execsql_test(
  --         INSERT INTO [silly " name] (a,b,c) VALUES(1, 2, 3);
  --         INSERT INTO [silly " name] (a,b,c) VALUES(4, 5, 6);
  --         ANALYZE;
---         SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+--         SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
  --     ]], {
  --         -- <analyze-3.10>
  --         "another foolish ' name", "2 1", "foolish ' name", "2 1 1", 
"t3i1", "5 3", "t3i2", "5 3 1 1 1", "t3i3", "5 5 2 1 1"
@@ -288,7 +288,7 @@ test:do_execsql_test(
  --     "analyze-3.11",
  --     [[
  --         DROP INDEX "foolish ' name";
---         SELECT "idx", "stat" FROM sqlite_stat1 ORDER BY "idx";
+--         SELECT "index_id", "stat" FROM sqlite_stat1 ORDER BY "index_id";
  --     ]], {
  --         -- <analyze-3.11>
  --         "another foolish ' name", "2 1", "t3i1", "5 3", "t3i2", "5 
3 1 1 1", "t3i3", "5 5 2 1 1"
@@ -299,7 +299,7 @@ test:do_execsql_test(
  --     "analyze-3.11",
  --     [[
  --         DROP TABLE "silly "" name";
---         SELECT "idx", "stat" FROM sqlite_stat1 ORDER BY "idx";
+--         SELECT "index_id", "stat" FROM sqlite_stat1 ORDER BY "index_id";
  --     ]], {
  --         -- <analyze-3.11>
  --         "t3i1", "5 3", "t3i2", "5 3 1 1 1", "t3i3", "5 5 2 1 1"
@@ -317,31 +317,33 @@ test:do_execsql_test(
          CREATE INDEX t4i2 ON t4(y);
          INSERT INTO t4 SELECT id,a,b,c FROM t3;
          ANALYZE;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-4.0>
-        "T3", "5 1", "T3I1", "5 3", "T3I2", "5 3 1 1 1", "T3I3", "5 5 2 
1 1", "T4", "5 1", "T4I1", "5 3", "T4I2", "5 2"
+        0, "5 1", 0, "5 1", 1, "5 3", 1, "5 3", 2, "5 3 1 1 1", 2, "5 
2", 3, "5 5 2 1 1"
          -- </analyze-4.0>
      })

+t4 = box.space.T4
+
  test:do_execsql_test(
      "analyze-4.1",
-    [[
+    string.format([[
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1" VALUES('t4', 't4i1', 'nonsense');
-        INSERT INTO "_sql_stat1" VALUES('t4', 't4i2', 
'432653287412874653284129847632');
+        INSERT INTO "_sql_stat1" VALUES(%i, %i, 'nonsense');
+        INSERT INTO "_sql_stat1" VALUES(%i, %i, 
'432653287412874653284129847632');
          SELECT * FROM t4 WHERE x = 1234;
-    ]], {
+    ]], t4.id, t4.index['T4I1'].id, t4.id, t4.index['T4I2'].id), {
          -- <analyze-4.1>
          -- </analyze-4.1>
      })

  test:do_execsql_test(
      "analyze-4.2",
-    [[
-        INSERT INTO "_sql_stat1" VALUES('t4', 'xyzzy', '0 1 2 3');
+    string.format([[
+        INSERT INTO "_sql_stat1" VALUES(%i, 12345, '0 1 2 3');
          SELECT * FROM t4 WHERE x = 1234;
-    ]], {
+    ]], t4.id), {
          -- <analyze-4.2>
          -- </analyze-4.2>
      })
@@ -367,7 +369,7 @@ test:do_execsql_test(
          INSERT INTO t3 (a,b,c,d) SELECT a+64, b+64, c+64, d+64 FROM t3;
          INSERT INTO t4 (x,y,z) SELECT a, b, c FROM t3;
          ANALYZE;
-        SELECT DISTINCT "tbl" FROM "_sql_stat1" ORDER BY 1;
+        SELECT DISTINCT "name" FROM "_sql_stat1" JOIN "_space" ON 
"_sql_stat1"."space_id" = "_space"."id" ORDER BY 1;
      ]], {
          -- <analyze-5.0>
          "T3", "T4"
@@ -377,10 +379,10 @@ test:do_execsql_test(
  test:do_execsql_test(
      "analyze-5.0.1",
      [[
-        SELECT DISTINCT "idx" FROM "_sql_stat1" ORDER BY 1;
+        SELECT "index_id" FROM "_sql_stat1" ORDER BY 1;
      ]], {
          -- <analyze-5.0>
-        "T3", "T3I1", "T3I2", "T3I3", "T4", "T4I1", "T4I2"
+        0, 0, 1, 1, 2, 2, 3
          -- </analyze-5.0>
      })

@@ -389,18 +391,18 @@ stat = "_sql_stat4"
  test:do_execsql_test(
      "analyze-5.1",
      string.format([[
-            SELECT DISTINCT "idx" FROM "%s" ORDER BY 1;
+            SELECT DISTINCT "index_id" FROM "%s" ORDER BY 1;
          ]], stat, stat), {
          -- <analyze-5.1>
-        "T3", "T3I1", "T3I2", "T3I3", "T4", "T4I1", "T4I2"
+        0, 1, 2, 3
          -- </analyze-5.1>
      })

  test:do_execsql_test(
      "analyze-5.1.1",
      string.format([[
-            SELECT DISTINCT "tbl" FROM "%s" ORDER BY 1;
-        ]], stat, stat), {
+            SELECT DISTINCT "name" FROM "%s" JOIN "_space" ON 
"%s"."space_id" = "_space"."id" ORDER BY 1;
+        ]], stat, stat, stat), {
          -- <analyze-5.1>
          "T3", "T4"
          -- </analyze-5.1>
@@ -411,17 +413,17 @@ test:do_execsql_test(
      [[
          DROP INDEX t3i2 ON t3;
          ANALYZE;
-        SELECT DISTINCT "idx" FROM "_sql_stat1" ORDER BY 1;
+        SELECT "index_id" FROM "_sql_stat1" ORDER BY 1;
      ]], {
          -- <analyze-5.2>
-        "T3", "T3I1", "T3I3", "T4", "T4I1", "T4I2"
+        0, 0, 1, 1, 2, 3
          -- </analyze-5.2>
      })

  test:do_execsql_test(
      "analyze-5.2.1",
      [[
-        SELECT DISTINCT "tbl" FROM "_sql_stat1" ORDER BY 1;
+        SELECT DISTINCT "name" FROM "_sql_stat1" JOIN "_space" ON 
"_sql_stat1"."space_id" = "_space"."id" ORDER BY 1;
      ]], {
          -- <analyze-5.2>
          "T3", "T4"
@@ -431,18 +433,18 @@ test:do_execsql_test(
  test:do_execsql_test(
      "analyze-5.3",
      string.format([[
-            SELECT DISTINCT "idx" FROM "%s" ORDER BY 1;
+            SELECT DISTINCT "index_id" FROM "%s" ORDER BY 1;
          ]], stat, stat), {
          -- <analyze-5.3>
-        "T3", "T3I1", "T3I3", "T4", "T4I1", "T4I2"
+        0, 1, 2, 3
          -- </analyze-5.3>
      })

  test:do_execsql_test(
      "analyze-5.3.1",
      string.format([[
-            SELECT DISTINCT "tbl" FROM "%s" ORDER BY 1;
-        ]], stat, stat), {
+            SELECT DISTINCT "name" FROM "%s" JOIN "_space" ON 
"%s"."space_id" = "_space"."id" ORDER BY 1;
+        ]], stat, stat, stat), {
          -- <analyze-5.3>
          "T3", "T4"
          -- </analyze-5.3>
@@ -453,17 +455,17 @@ test:do_execsql_test(
      [[
          DROP TABLE IF EXISTS t3;
          ANALYZE;
-        SELECT DISTINCT "idx" FROM "_sql_stat1" ORDER BY 1;
+        SELECT DISTINCT "index_id" FROM "_sql_stat1" ORDER BY 1;
      ]], {
          -- <analyze-5.4>
-        "T4", "T4I1", "T4I2"
+        0, 1, 2
          -- </analyze-5.4>
      })

  test:do_execsql_test(
      "analyze-5.4.1",
      [[
-        SELECT DISTINCT "tbl" FROM "_sql_stat1" ORDER BY 1;
+        SELECT DISTINCT "name" FROM "_sql_stat1" JOIN "_space" ON 
"_sql_stat1"."space_id" = "_space"."id" ORDER BY 1;
      ]], {
          -- <analyze-5.4>
          "T4"
@@ -473,18 +475,18 @@ test:do_execsql_test(
  test:do_execsql_test(
      "analyze-5.5",
      string.format([[
-            SELECT DISTINCT "idx" FROM "%s" ORDER BY 1;
+            SELECT DISTINCT "index_id" FROM "%s" ORDER BY 1;
          ]], stat), {
          -- <analyze-5.5>
-        "T4", "T4I1", "T4I2"
+        0, 1, 2
          -- </analyze-5.5>
      })

  test:do_execsql_test(
      "analyze-5.5.1",
      string.format([[
-            SELECT DISTINCT "tbl" FROM "%s" ORDER BY 1;
-        ]], stat), {
+            SELECT DISTINCT "name" FROM "%s" JOIN "_space" ON 
"%s"."space_id" = "_space"."id" ORDER BY 1;
+        ]], stat, stat), {
          -- <analyze-5.5>
          "T4"
          -- </analyze-5.5>
@@ -516,33 +518,35 @@ test:do_test(
      -- </analyze-6.1.1>
  })

+t1 = box.space.T1
+
  test:do_execsql_test(
      "analyze-6.1.2",
-    [[
-            SELECT * FROM "_sql_stat1" where "tbl"='T1' and "idx"='I1' 
LIMIT 1;
-    ]], {
+    string.format([[
+            SELECT "index_id", "stat" FROM "_sql_stat1" where 
"space_id"=%i and "index_id"=%i LIMIT 1;
+    ]], t1.id, t1.index['I1'].id), {
      -- <analyze-6.1.2>
-    "T1", "I1", "221 221 221 221 2"
+    1, "221 221 221 221 2"
      -- </analyze-6.1.2>
  })

  test:do_execsql_test(
      "analyze-6.1.3",
-    [[
-            SELECT "tbl", "idx", "neq", "nlt", "ndlt" FROM "_sql_stat4" 
where "tbl"='T1' and "idx"='I1' ORDER BY "nlt" LIMIT 1;
-    ]], {
+    string.format([[
+            SELECT "index_id", "neq", "nlt", "ndlt" FROM "_sql_stat4" 
where "space_id"=%i and "index_id"=%i ORDER BY "nlt" LIMIT 1;
+    ]], t1.id, t1.index['I1'].id), {
      -- <analyze-6.1.3>
-    "T1", "I1", "221 221 221 1", "0 0 0 10", "0 0 0 10"
+    1, "221 221 221 1", "0 0 0 10", "0 0 0 10"
      -- </analyze-6.1.3>
  })

  test:do_execsql_test(
      "analyze-6.1.4",
-    [[
-            SELECT "tbl", "idx", "neq", "nlt", "ndlt" FROM "_sql_stat4" 
where "tbl"='T1' and "idx"='I1' ORDER BY "nlt" DESC LIMIT 1;
-    ]], {
+    string.format([[
+            SELECT "index_id", "neq", "nlt", "ndlt" FROM "_sql_stat4" 
where "space_id"=%i and "index_id"=%i ORDER BY "nlt" DESC LIMIT 1;
+    ]], t1.id, t1.index['I1'].id), {
      -- <analyze-6.1.4>
-    "T1", "I1", "221 221 221 1", "0 0 0 99", "0 0 0 99"
+    1, "221 221 221 1", "0 0 0 99", "0 0 0 99"
      -- </analyze-6.1.4>
  })

diff --git a/test/sql-tap/analyze4.test.lua b/test/sql-tap/analyze4.test.lua
index c2cc190..d64cba8 100755
--- a/test/sql-tap/analyze4.test.lua
+++ b/test/sql-tap/analyze4.test.lua
@@ -49,15 +49,19 @@ test:do_test(
          -- </analyze4-1.0>
      })

+t1 = box.space.T1
+
  -- Verify that the t1b index shows that it does not narrow down the
  -- search any at all.
  --
  test:do_execsql_test(
      "analyze4-1.1",
-    [[ SELECT "idx", "stat" FROM "_sql_stat1" WHERE "tbl"='T1' ORDER BY 
"idx"; ]],
+    string.format([[
+        SELECT "index_id", "stat" FROM "_sql_stat1" WHERE "space_id"=%i 
ORDER BY "index_id";
+    ]], t1.id),
      {
          -- <analyze4-1.1>
-        "T1","128 1", "T1A", "128 1", "T1B", "128 128"
+        0,"128 1", 1, "128 1", 2, "128 128"
          -- </analyze4-1.1>
      })

@@ -68,16 +72,16 @@ test:do_execsql_test(
  test:do_test(
      "analyze4-1.2",
      function()
-        return test:execsql([[
-            UPDATE t1 SET b='x' WHERE a%2;
+        return test:execsql(string.format([[
+            UPDATE t1 SET b='x' WHERE a%%2;
  -- pragma vdbe_debug=1;
              ANALYZE;
  -- pragma vdbe_debug=0;
-            SELECT "idx", "stat" FROM "_sql_stat1" WHERE "tbl"='T1' 
ORDER BY "idx";
-        ]])
+            SELECT "index_id", "stat" FROM "_sql_stat1" WHERE 
"space_id"=%i ORDER BY "index_id";
+        ]], t1.id))
      end, {
          -- <analyze4-1.2>
-        "T1", "128 1", "T1A", "128 1", "T1B", "128 64"
+        0, "128 1", 1, "128 1", 2, "128 64"
          -- </analyze4-1.2>
      })

@@ -85,37 +89,42 @@ test:do_test(
  -- Create a multi-column indices using t1.b and verify that ANALYZE
  -- processes them correctly.
  --
+
+test:execsql([[
+    -- Tarantool doesn't suppoort ALTER stmt yet.
+    -- UPDATE t1 SET b=NULL;
+    --ALTER TABLE t1 ADD COLUMN c;
+    --ALTER TABLE t1 ADD COLUMN d;
+    -- So, re-create the table and its contents
+    DROP TABLE t1;
+    CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a,b,c DEFAULT 
NULL,d DEFAULT NULL);
+    CREATE INDEX t1a ON t1(a);
+    CREATE INDEX t1b ON t1(b);
+    INSERT INTO t1 (a,b) VALUES(1,NULL);
+    INSERT INTO t1 (a,b) SELECT a+1, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+2, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+4, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+8, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+16, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+32, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+64, b FROM t1;
+
+    UPDATE t1 SET c=a/4, d=a/2;
+    CREATE INDEX t1bcd ON t1(b,c,d);
+    CREATE INDEX t1cdb ON t1(c,d,b);
+    CREATE INDEX t1cbd ON t1(c,b,d);
+    ANALYZE;
+]])
+
+t1 = box.space.T1
+
  test:do_execsql_test(
      "analyze4-1.3",
-    [[
-            -- Tarantool doesn't suppoort ALTER stmt yet.
-            -- UPDATE t1 SET b=NULL;
-            --ALTER TABLE t1 ADD COLUMN c;
-            --ALTER TABLE t1 ADD COLUMN d;
-            -- So, re-create the table and its contents
-            DROP TABLE t1;
-            CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a,b,c 
DEFAULT NULL,d DEFAULT NULL);
-            CREATE INDEX t1a ON t1(a);
-            CREATE INDEX t1b ON t1(b);
-            INSERT INTO t1 (a,b) VALUES(1,NULL);
-            INSERT INTO t1 (a,b) SELECT a+1, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+2, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+4, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+8, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+16, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+32, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+64, b FROM t1;
-
-            UPDATE t1 SET c=a/4, d=a/2;
-            CREATE INDEX t1bcd ON t1(b,c,d);
-            CREATE INDEX t1cdb ON t1(c,d,b);
-            CREATE INDEX t1cbd ON t1(c,b,d);
-            ANALYZE;
-            SELECT "idx", "stat" FROM "_sql_stat1" WHERE "tbl"='T1' 
ORDER BY "idx";
-    ]]
-    , {
+    string.format([[
+            SELECT "index_id", "stat" FROM "_sql_stat1" WHERE 
"space_id"=%i ORDER BY "index_id";
+    ]], t1.id), {
          -- <analyze4-1.3>
-        "T1","128 1", "T1A", "128 1", "T1B", "128 128", "T1BCD", "128 
128 4 2", "T1CBD", "128 4 4 2", "T1CDB", "128 4 2 2"
+        0,"128 1", 1, "128 1", 2, "128 128", 3, "128 128 4 2", 4, "128 
4 2 2", 5, "128 4 4 2"
          -- </analyze4-1.3>
      })

diff --git a/test/sql-tap/analyze5.test.lua b/test/sql-tap/analyze5.test.lua
index d68bd3c..ece26e4 100755
--- a/test/sql-tap/analyze5.test.lua
+++ b/test/sql-tap/analyze5.test.lua
@@ -114,10 +114,11 @@ test:do_test(

          -- DISTINCT idx, sample -- lindex(test_decode(sample),0)
          -- WHERE idx='t1u' ORDER BY nlt;
-        return test:execsql([[ SELECT DISTINCT msgpack_decode("sample")
-                                 FROM "_sql_stat4"
-                                 WHERE "idx"='T1U'
-                                 ORDER BY "nlt"]])
+        return test:execsql(string.format([[
+            SELECT DISTINCT msgpack_decode("sample")
+            FROM "_sql_stat4"
+            WHERE "index_id"= %i
+            ORDER BY "nlt"]], box.space.T1.index['T1U'].id))
      end, {
          -- <analyze5-1.0>
          "alpha", "bravo", "charlie", "delta"
@@ -143,10 +144,10 @@ test:do_test(
  test:do_test(
      "analyze5-1.2",
      function()
-        return test:execsql([[SELECT "idx", count(*) FROM "_sql_stat4" 
GROUP BY 1 ORDER BY 1]])
+        return test:execsql([[SELECT "index_id", count(*) FROM 
"_sql_stat4" GROUP BY 1 ORDER BY 1]])
      end, {
          -- <analyze5-1.2>
- "T1",24,"T1T",4,"T1U",4,"T1V",1,"T1W",4,"T1X",4,"T1Y",2,"T1Z",4
+        0,24,1,4,2,4,3,1,4,4,5,4,6,2,7,4
          -- </analyze5-1.2>
      })

diff --git a/test/sql-tap/analyze9.test.lua b/test/sql-tap/analyze9.test.lua
index 1dbfe5d..d105c6f 100755
--- a/test/sql-tap/analyze9.test.lua
+++ b/test/sql-tap/analyze9.test.lua
@@ -1,6 +1,6 @@
  #!/usr/bin/env tarantool
  test = require("sqltester")
-test:plan(121)
+test:plan(120)

  testprefix = "analyze9"

@@ -62,29 +62,29 @@ msgpack_decode_sample = function(txt)
  end

  box.internal.sql_create_function("msgpack_decode_sample", 
msgpack_decode_sample)
+t1 = box.space.T1

  test:do_execsql_test(
      1.2,
-    [[
-        SELECT 
"tbl","idx","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM 
"_sql_stat4" where "idx" = 'I1';
-    ]], {
+    string.format([[
+        SELECT 
"index_id","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM 
"_sql_stat4" where "index_id" = %i;
+    ]], t1.index['I1'].id), {
          -- <1.2>
-        "T1", "I1", "1 1", "0 0", "0 0", "(0) (0)", "T1", "I1", "1 1", 
"1 1", "1 1", "(1) (1)",
-        "T1", "I1", "1 1", "2 2", "2 2", "(2) (2)", "T1", "I1", "1 1", 
"3 3", "3 3", "(3) (3)",
-        "T1", "I1", "1 1", "4 4", "4 4", "(4) (4)"
+        1, "1 1", "0 0", "0 0", "(0) (0)", 1, "1 1", "1 1", "1 1", "(1) 
(1)",
+        1, "1 1", "2 2", "2 2", "(2) (2)", 1, "1 1", "3 3", "3 3", "(3) 
(3)",
+        1, "1 1", "4 4", "4 4", "(4) (4)"
          -- </1.2>
      })

  test:do_execsql_test(
      1.3,
-    [[
-        SELECT 
"tbl","idx","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM 
"_sql_stat4" where "idx" = 'T1';
-
-    ]], {
+    string.format([[
+        SELECT 
"index_id","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM 
"_sql_stat4" where "index_id" = %i;
+    ]], t1.index[0].id), {
          -- <1.3>
-        'T1', 'T1', '1', '0', '0', '(0)', 'T1', 'T1', '1', '1', '1', 
'(1)',
-        'T1', 'T1', '1', '2', '2', '(2)', 'T1', 'T1', '1', '3', '3', 
'(3)',
-        'T1', 'T1', '1', '4', '4', '(4)'
+        0, '1', '0', '0', '(0)', 0, '1', '1', '1', '(1)',
+        0, '1', '2', '2', '(2)', 0, '1', '3', '3', '(3)',
+        0, '1', '4', '4', '(4)'
          -- </1.3>
      })

@@ -101,10 +101,10 @@ test:do_execsql_test(
          INSERT INTO t1 VALUES(22.0, 'some text');
          CREATE INDEX i1 ON t1(a, b);
          ANALYZE;
-        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4";
+        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4" ORDER 
BY 1;
      ]], {
          -- <2.1>
-        "some text 14", "22 some text", "some text", 22
+        22, "22 some text", "some text", "some text 14"
          -- </2.1>
      })

@@ -116,6 +116,7 @@ test:do_execsql_test(
          CREATE TABLE t2(id INTEGER PRIMARY KEY AUTOINCREMENT, a, b);
          CREATE INDEX i2 ON t2(a, b);
      ]])
+t2 = box.space.T2

  test:do_test(
      3.2,
@@ -184,10 +185,10 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      "3.3.2",
-    [[
+    string.format([[
          ANALYZE;
-        SELECT lrange("neq", 1, 1) FROM "_sql_stat4" WHERE "idx" = 'I2';
-    ]], generate_tens_str(24))
+        SELECT lrange("neq", 1, 1) FROM "_sql_stat4" WHERE "space_id" = 
%i and "index_id" = %i;
+    ]], t2.id, t2.index['I2'].id) , generate_tens_str(24))

  ---------------------------------------------------------------------------
  --
@@ -232,6 +233,7 @@ test:do_execsql_test(
          CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a, b, c);
          CREATE INDEX i1 ON t1(c, b, a);
      ]])
+t1 = box.space.T1

  insert_filler_rows_n = function(iStart, nCopy, nVal)
      for i = 0, nVal-1 do
@@ -282,10 +284,10 @@ test:do_execsql_test(

  test:do_execsql_test(
      4.3,
-    [[
+    string.format([[
          SELECT "neq", lrange("nlt", 1, 3), lrange("ndlt", 1, 3), 
lrange(msgpack_decode_sample("sample"), 1, 3)
-            FROM "_sql_stat4" WHERE "idx" = 'I1' ORDER BY "sample" 
LIMIT 16;
-    ]], {
+            FROM "_sql_stat4" WHERE "space_id" = %i and "index_id" = %i 
ORDER BY "sample" LIMIT 16;
+    ]], t1.id, t1.index['I1'].id), {
          -- <4.3>
          "10 10 10","0 0 0","0 0 0","0 0 0","10 10 10","10 10 10","1 1 
1","1 1 1","10 10 10","20 20 20",
          "2 2 2","2 2 2","10 10 10","30 30 30","3 3 3","3 3 3","10 10 
10","40 40 40","4 4 4","4 4 4",
@@ -299,10 +301,10 @@ test:do_execsql_test(

  test:do_execsql_test(
      4.4,
-    [[
+    string.format([[
          SELECT "neq", lrange("nlt", 1, 3), lrange("ndlt", 1, 3), 
lrange(msgpack_decode_sample("sample"), 1, 3)
-        FROM "_sql_stat4" WHERE "idx" = 'I1' ORDER BY "sample" DESC 
LIMIT 2;
-    ]], {
+        FROM "_sql_stat4" WHERE "space_id" = %i and "index_id" = %i 
ORDER BY "sample" DESC LIMIT 2;
+    ]], t1.id, t1.index['I1'].id), {
          -- <4.4>
          "2 1 1","295 296 296","120 122 125","201 4 h","5 3 1","290 290 
291","119 119 120","200 1 b"
          -- </4.4>
@@ -364,11 +366,12 @@ test:do_test(
  test:do_execsql_test(
      4.9,
      [[
-        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4";
+        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4" ORDER 
BY 1;
      ]], {
          -- <4.9>
-        "x", 1110, 2230, 2750, 3350, 4090, 4470, 4980, 5240, 5280, 
5290, 5590, 5920,
-        5930, 6220, 6710, 7000, 7710, 7830, 7970, 8890, 8950, 9240, 
9250, 9680
+        1110, 2230, 2750, 3350, 4090, 4470, 4980, 5240, 5280, 5290, 
5590, 5920,
+        5930, 6220, 6710, 7000, 7710, 7830, 7970, 8890, 8950, 9240, 
9250, 9680,
+        "x"
          -- </4.9>
      })

@@ -394,6 +397,8 @@ test:do_execsql_test(
          INSERT INTO "_sql_stat4" SELECT * FROM x1;
          ANALYZE;
      ]])
+t1 = box.space.T1
+x1 = box.space.X1

  test:do_execsql_test(
      6.2,
@@ -407,7 +412,7 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      7.1,
-    [[
+    string.format([[
          DROP TABLE IF EXISTS t1;
          CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a, b);
          CREATE INDEX i1 ON t1(a, b);
@@ -418,9 +423,9 @@ test:do_execsql_test(
          INSERT INTO t1 VALUES(null, 5, 5);
          ANALYZE;
          UPDATE "_sql_stat4" SET "sample" = '' WHERE "sample" =
-            (SELECT "sample" FROM "_sql_stat4" WHERE "tbl" = 't1' AND 
"idx" = 'i1' LIMIT 1);
+            (SELECT "sample" FROM "_sql_stat4" WHERE "space_id" = %i 
AND "index_id" = %i LIMIT 1);
          ANALYZE;
-    ]])
+    ]], t1.id, t1.index['I1'].id))

  -- Doesn't work due to the fact that in Tarantool rowid has been removed,
  -- and tbl, idx and sample have been united into primary key.
@@ -1014,15 +1019,15 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      15.1,
-    [[
+    string.format([[
          DROP TABLE IF EXISTS x1;
          CREATE TABLE x1(a PRIMARY KEY, b, UNIQUE(a, b));
          INSERT INTO x1 VALUES(1, 2);
          INSERT INTO x1 VALUES(3, 4);
          INSERT INTO x1 VALUES(5, 6);
          ANALYZE;
-        INSERT INTO "_sql_stat4" VALUES('x1', 'abc', 0, 0, 0, '');
-    ]])
+        INSERT INTO "_sql_stat4" VALUES(%i,  12345, 0, 0, 0, '');
+    ]], x1.id))

  test:do_execsql_test(
      15.2,
@@ -1054,7 +1059,7 @@ test:do_execsql_test(
      15.7,
      [[
          ANALYZE;
-        UPDATE "_sql_stat1" SET "tbl" = 'no such tbl';
+        UPDATE "_sql_stat1" SET "space_id" = "space_id" + 123456;
      ]])

  test:do_execsql_test(
@@ -1067,25 +1072,6 @@ test:do_execsql_test(
          -- </15.8>
      })

--- Tarantool: this test seems to be useless. There's no reason
--- for these fields to be nullable.
--- test:do_execsql_test(
---    15.9,
---    [[
---        ANALYZE;
---        UPDATE "_sql_stat4" SET "neq" = NULL, "nlt" = NULL, "ndlt" = 
NULL;
---    ]])
-
-test:do_execsql_test(
-    15.10,
-    [[
-        SELECT * FROM x1;
-    ]], {
-        -- <15.10>
-        1, 2, 3, 4, 5, 6
-        -- </15.10>
-    })
-
  -- This is just for coverage....
  test:do_execsql_test(
      15.11,
@@ -1135,6 +1121,7 @@ test:do_test(
          -- <17.1>
          -- </17.1>
      })
+t1 = box.space.T1

  test:do_execsql_test(
      17.2,
@@ -1197,7 +1184,7 @@ test:do_test(
              test:execsql(string.format("INSERT INTO t1 VALUES(%s, 
0);", i))
          end
          test:execsql("ANALYZE")
-        return test:execsql([[ SELECT count(*) FROM "_sql_stat4" WHERE 
"idx" = 'I1'; ]])
+        return test:execsql(string.format('SELECT count(*) FROM 
"_sql_stat4" WHERE "space_id" = %i AND "index_id" = %i;', 
box.space.T1.id, box.space.T1.index['I1'].id))
      end, {
          -- <18.1>
          9
@@ -1239,12 +1226,13 @@ test:do_execsql_test(
          -- </20.2>
      })

+t1 = box.space.T1
  for i = 0, 15 do
      test:do_test(
          "20.3."..i,
          function()
              return test:execsql(string.format(
-                [[SELECT count(*) FROM "_sql_stat4" WHERE "idx" = 'I1' 
AND lrange(msgpack_decode_sample("sample"), 1, 1) = '%s']], i))
+                [[SELECT count(*) FROM "_sql_stat4" WHERE "space_id" = 
%i AND "index_id" = %i AND lrange(msgpack_decode_sample("sample"), 1, 1) 
= '%s']], t1.id, t1.index['I1'].id, i))
          end, {
              1
          })
diff --git a/test/sql-tap/analyzeC.test.lua b/test/sql-tap/analyzeC.test.lua
index a3cea70..e772d71 100755
--- a/test/sql-tap/analyzeC.test.lua
+++ b/test/sql-tap/analyzeC.test.lua
@@ -30,9 +30,7 @@ testprefix = "analyzeC"
  -- Baseline case.  Range queries work OK.  Indexes can be used for
  -- ORDER BY.

-test:do_execsql_test(
-    1.0,
-    [[
+test:execsql([[
          DROP TABLE IF EXISTS t1;
          CREATE TABLE t1(a PRIMARY KEY, b, c, d);
          INSERT INTO t1(a,b,c,d) 
VALUES(1,1,2,3),(2,7,8,9),(3,4,5,6),(4,10,11,12),(5,4,8,12),(6,1,11,111);
@@ -40,10 +38,17 @@ test:do_execsql_test(
          CREATE INDEX t1c ON t1(c);
          ANALYZE;
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") 
VALUES('t1','t1b','12345 2'),('t1','t1c','12345 4');
+    ]]
+)
+t1 = box.space.T1
+
+test:do_execsql_test(
+    1.0,
+    string.format([[
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat") 
VALUES(%i,1,'12345 2'),(%i,2,'12345 4');
          ANALYZE;
          SELECT b,c,d, '#' FROM t1 WHERE b BETWEEN 3 AND 8 ORDER BY d;
-    ]], {
+    ]], t1.id, t1.id), {
          -- <1.0>
          4, 5, 6, "#", 7, 8, 9, "#", 4, 8, 12, "#"
          -- </1.0>
@@ -85,11 +90,12 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      2.0,
+    string.format(
      [[
-        UPDATE "_sql_stat1" SET "stat"='12345 2 unordered' WHERE 
"idx"='t1b';
+        UPDATE "_sql_stat1" SET "stat"='12345 2 unordered' WHERE 
"index_id"=%i;
          ANALYZE;
          SELECT b, c, d, '#' FROM t1 WHERE b BETWEEN 3 AND 8 ORDER BY d;
-    ]], {
+    ]], box.space.T1.index['T1B'].id), {
          -- <2.0>
          4, 5, 6, "#", 7, 8, 9, "#", 4, 8, 12, "#"
          -- </2.0>
@@ -130,11 +136,12 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      3.0,
+    string.format(
      [[
-        UPDATE "_sql_stat1" SET "stat"='12345 2 whatever=5 unordered 
xyzzy=11' WHERE "idx"='t1b';
+        UPDATE "_sql_stat1" SET "stat"='12345 2 whatever=5 unordered 
xyzzy=11' WHERE "index_id"=%i;
          ANALYZE;
          SELECT b, c, d, '#' FROM t1 WHERE b BETWEEN 3 AND 8 ORDER BY d;
-    ]], {
+    ]], box.space.T1.index['T1B'].id), {
          -- <3.0>
          4, 5, 6, "#", 7, 8, 9, "#", 4, 8, 12, "#"
          -- </3.0>
@@ -173,17 +180,22 @@ test:do_execsql_test(

  -- The sz=NNN parameter determines which index to scan
  --
-test:do_execsql_test(
-    4.0,
-    [[
+
+test:execsql([[
          DROP INDEX t1b ON t1;
          CREATE INDEX t1bc ON t1(b,c);
          CREATE INDEX t1db ON t1(d,b);
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") 
VALUES('t1','t1bc','12345 3 2 sz=10'),('t1','t1db','12345 3 2 sz=20');
+    ]]
+)
+
+test:do_execsql_test(
+    4.0,
+    string.format([[
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat") 
VALUES(%i,%i,'12345 3 2 sz=10'),(%i,%i,'12345 3 2 sz=20');
          ANALYZE;
          SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
          -- <4.0>
          6
          -- </4.0>
@@ -201,12 +213,13 @@ test:do_execsql_test(

  test:do_execsql_test(
      4.2,
+    string.format(
      [[
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") 
VALUES('t1','t1bc','12345 3 2 sz=20'),('t1','t1db','12345 3 2 sz=10');
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat") 
VALUES(%i,%i,'12345 3 2 sz=20'),(%i,%i,'12345 3 2 sz=10');
          ANALYZE;
          SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
          -- <4.2>
          6
          -- </4.2>
@@ -227,14 +240,14 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      5.0,
-    [[
+    string.format([[
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat")
-          VALUES('t1','t1bc','12345 3 2 x=5 sz=10 y=10'),
-                ('t1','t1db','12345 3 2 whatever sz=20 junk');
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat")
+          VALUES(%i,%i,'12345 3 2 x=5 sz=10 y=10'),
+                (%i,%i,'12345 3 2 whatever sz=20 junk');
          ANALYZE;
          SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
          -- <5.0>
          6
          -- </5.0>
@@ -253,12 +266,12 @@ test:do_execsql_test(

  test:do_execsql_test(
      5.2,
-    [[
+    string.format([[
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") 
VALUES('t1','t1db','12345 3 2 x=5 sz=10 y=10'), ('t1','t1bc','12345 3 2 
whatever sz=20 junk');
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat") 
VALUES(%i,%i,'12345 3 2 x=5 sz=10 y=10'), (%i,%i,'12345 3 2 whatever 
sz=20 junk');
          ANALYZE;
          SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
          -- <5.2>
          6
          -- </5.2>
diff --git a/test/sql-tap/analyzeD.test.lua b/test/sql-tap/analyzeD.test.lua
index ef6aced..9d89933 100755
--- a/test/sql-tap/analyzeD.test.lua
+++ b/test/sql-tap/analyzeD.test.lua
@@ -153,9 +153,9 @@ test:do_catchsql_test(

  test:do_execsql_test(
      "analyzeD-1.10",
-    [[
-        SELECT * FROM "_sql_stat4" WHERE "tbl" = 'v';
-    ]], {
+    string.format([[
+        SELECT * FROM "_sql_stat4" WHERE "space_id" = %i;
+    ]], box.space.V.id), {
          -- <analyzeD-1.10>

          -- <analyzeD-1.10>
@@ -163,9 +163,9 @@ test:do_execsql_test(

  test:do_execsql_test(
      "analyzeD-1.11",
-    [[
-        SELECT * FROM "_sql_stat1" WHERE "tbl" = 'v';
-    ]], {
+    string.format([[
+        SELECT * FROM "_sql_stat1" WHERE "space_id" = %i;
+    ]], box.space.V.id), {
          -- <analyzeD-1.11>

          -- <analyzeD-1.11>
@@ -183,9 +183,9 @@ test:do_catchsql_test(

  test:do_execsql_test(
      "analyzeD-1.13",
-    [[
-        SELECT * FROM "_sql_stat4" WHERE "tbl" = 'v';
-    ]], {
+    string.format([[
+        SELECT * FROM "_sql_stat4" WHERE "space_id" = %i;
+    ]], box.space.V.id), {
          -- <analyzeD-1.13>

          -- <analyzeD-1.13>
@@ -193,9 +193,9 @@ test:do_execsql_test(

  test:do_execsql_test(
      "analyzeD-1.14",
-    [[
-        SELECT * FROM "_sql_stat1" WHERE "tbl" = 'v';
-    ]], {
+    string.format([[
+        SELECT * FROM "_sql_stat1" WHERE "space_id" = %i;
+    ]], box.space.V.id), {
          -- <analyzeD-1.14>

          -- <analyzeD-1.14>
diff --git a/test/sql-tap/gh-3350-skip-scan.test.lua 
b/test/sql-tap/gh-3350-skip-scan.test.lua
index 301b04a..917c74c 100755
--- a/test/sql-tap/gh-3350-skip-scan.test.lua
+++ b/test/sql-tap/gh-3350-skip-scan.test.lua
@@ -77,9 +77,7 @@ test:do_execsql_test(
          }
  )

-test:do_execsql_test(
-        "skip-scan-1.4",
-        [[
+test:execsql([[
              DROP TABLE IF EXISTS t1;
              CREATE TABLE t1(id INTEGER PRIMARY KEY, a TEXT, b INT, c 
INT, d INT);
              CREATE INDEX t1abc ON t1(a,b,c);
@@ -96,10 +94,17 @@ test:do_execsql_test(
              ANALYZE;
              DELETE FROM "_sql_stat1";
              DELETE FROM "_sql_stat4";
-            INSERT INTO "_sql_stat1" VALUES('T1','T1ABC','10000 5000 
2000 10');
+        ]]
+)
+
+test:do_execsql_test(
+        "skip-scan-1.4",
+        string.format(
+        [[
+            INSERT INTO "_sql_stat1" VALUES(%i,%i,'10000 5000 2000 10');
              ANALYZE t2;
              SELECT a,b,c,d FROM t1 WHERE b=345;
-        ]], {
+        ]], box.space.T1.id, box.space.T1.index['T1ABC'].id), {
              "abc", 345, 7, 8, "def", 345, 9, 10
          }
  )
diff --git a/test/sql/sql-statN-index-drop.result 
b/test/sql/sql-statN-index-drop.result
index a751eca..cd35d3b 100644
--- a/test/sql/sql-statN-index-drop.result
+++ b/test/sql/sql-statN-index-drop.result
@@ -31,36 +31,36 @@ box.sql.execute("ANALYZE;")
  ---
  ...
  -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
+box.sql.execute('SELECT "name","index_id","neq","nlt","ndlt","sample" 
FROM "_sql_stat4" join "_space" on "_sql_stat4"."space_id" = 
"_space"."id";')
  ---
-- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - ['T1', 0, '1', '0', '0', !!binary kQE=]
+  - ['T1', 1, '1', '0', '0', !!binary kQI=]
+  - ['T2', 0, '1', '0', '0', !!binary kQE=]
+  - ['T2', 1, '1', '0', '0', !!binary kQI=]
  ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "name","index_id","stat" FROM "_sql_stat1" join 
"_space" on "_sql_stat1"."space_id" = "_space"."id";')
  ---
-- - ['T1', 'I1', '1 1']
-  - ['T1', 'T1', '1 1']
-  - ['T2', 'I1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - ['T1', 0, '1 1']
+  - ['T1', 1, '1 1']
+  - ['T2', 0, '1 1']
+  - ['T2', 1, '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\";")
+box.sql.execute('SELECT "name","index_id","neq","nlt","ndlt","sample" 
FROM "_sql_stat4" join "_space" on "_sql_stat4"."space_id" = 
"_space"."id";')
  ---
-- - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - ['T1', 0, '1', '0', '0', !!binary kQE=]
+  - ['T2', 0, '1', '0', '0', !!binary kQE=]
+  - ['T2', 1, '1', '0', '0', !!binary kQI=]
  ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "name","index_id","stat" FROM "_sql_stat1" join 
"_space" on "_sql_stat1"."space_id" = "_space"."id";')
  ---
-- - ['T1', 'T1', '1 1']
-  - ['T2', 'I1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - ['T1', 0, '1 1']
+  - ['T2', 0, '1 1']
+  - ['T2', 1, '1 1']
  ...
  --Cleaning up.
  box.sql.execute("DROP TABLE t1;")
@@ -93,36 +93,36 @@ box.sql.execute("ANALYZE;")
  ---
  ...
  -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
+box.sql.execute('SELECT "name","index_id","neq","nlt","ndlt","sample" 
FROM "_sql_stat4" join "_space" on "_sql_stat4"."space_id" = 
"_space"."id";')
  ---
-- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - ['T1', 0, '1', '0', '0', !!binary kQE=]
+  - ['T1', 1, '1', '0', '0', !!binary kQI=]
+  - ['T2', 0, '1', '0', '0', !!binary kQE=]
+  - ['T2', 1, '1', '0', '0', !!binary kQI=]
  ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "name","index_id","stat" FROM "_sql_stat1" join 
"_space" on "_sql_stat1"."space_id" = "_space"."id";')
  ---
-- - ['T1', 'I1', '1 1']
-  - ['T1', 'T1', '1 1']
-  - ['T2', 'I1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - ['T1', 0, '1 1']
+  - ['T1', 1, '1 1']
+  - ['T2', 0, '1 1']
+  - ['T2', 1, '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\";")
+box.sql.execute('SELECT "name","index_id","neq","nlt","ndlt","sample" 
FROM "_sql_stat4" join "_space" on "_sql_stat4"."space_id" = 
"_space"."id";')
  ---
-- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - ['T1', 0, '1', '0', '0', !!binary kQE=]
+  - ['T1', 1, '1', '0', '0', !!binary kQI=]
+  - ['T2', 0, '1', '0', '0', !!binary kQE=]
  ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "name","index_id","stat" FROM "_sql_stat1" join 
"_space" on "_sql_stat1"."space_id" = "_space"."id";')
  ---
-- - ['T1', 'I1', '1 1']
-  - ['T1', 'T1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - ['T1', 0, '1 1']
+  - ['T1', 1, '1 1']
+  - ['T2', 0, '1 1']
  ...
  --Cleaning up.
  box.sql.execute("DROP TABLE t1;")
diff --git a/test/sql/sql-statN-index-drop.test.lua 
b/test/sql/sql-statN-index-drop.test.lua
index fe7e15b..dacc1eb 100644
--- a/test/sql/sql-statN-index-drop.test.lua
+++ b/test/sql/sql-statN-index-drop.test.lua
@@ -14,15 +14,15 @@ box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
  box.sql.execute("ANALYZE;")

  -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "name","index_id","neq","nlt","ndlt","sample" 
FROM "_sql_stat4" join "_space" on "_sql_stat4"."space_id" = 
"_space"."id";')
+box.sql.execute('SELECT "name","index_id","stat" FROM "_sql_stat1" join 
"_space" on "_sql_stat1"."space_id" = "_space"."id";')

  -- 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\";")
+box.sql.execute('SELECT "name","index_id","neq","nlt","ndlt","sample" 
FROM "_sql_stat4" join "_space" on "_sql_stat4"."space_id" = 
"_space"."id";')
+box.sql.execute('SELECT "name","index_id","stat" FROM "_sql_stat1" join 
"_space" on "_sql_stat1"."space_id" = "_space"."id";')

  --Cleaning up.
  box.sql.execute("DROP TABLE t1;")
@@ -41,15 +41,15 @@ box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
  box.sql.execute("ANALYZE;")

  -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "name","index_id","neq","nlt","ndlt","sample" 
FROM "_sql_stat4" join "_space" on "_sql_stat4"."space_id" = 
"_space"."id";')
+box.sql.execute('SELECT "name","index_id","stat" FROM "_sql_stat1" join 
"_space" on "_sql_stat1"."space_id" = "_space"."id";')

  -- 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\";")
+box.sql.execute('SELECT "name","index_id","neq","nlt","ndlt","sample" 
FROM "_sql_stat4" join "_space" on "_sql_stat4"."space_id" = 
"_space"."id";')
+box.sql.execute('SELECT "name","index_id","stat" FROM "_sql_stat1" join 
"_space" on "_sql_stat1"."space_id" = "_space"."id";')

  --Cleaning up.
  box.sql.execute("DROP TABLE t1;")
diff --git a/test/sql/upgrade.result b/test/sql/upgrade.result
index 5e7d851..9326209 100644
--- a/test/sql/upgrade.result
+++ b/test/sql/upgrade.result
@@ -30,14 +30,15 @@ box.space._space.index['name']:get('_trigger')
  ...
  box.space._space.index['name']:get('_sql_stat1')
  ---
-- [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-      'type': 'string'}, {'name': 'stat', 'type': 'string'}]]
+- [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'space_id', 'type': 
'unsigned'},
+    {'name': 'index_id', 'type': 'unsigned'}, {'name': 'stat', 'type': 
'string'}]]
  ...
  box.space._space.index['name']:get('_sql_stat4')
  ---
-- [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-      'type': 'string'}, {'name': 'neq', 'type': 'string'}, {'name': 
'nlt', 'type': 'string'},
-    {'name': 'ndlt', 'type': 'string'}, {'name': 'sample', 'type': 
'scalar'}]]
+- [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'space_id', 'type': 
'unsigned'},
+    {'name': 'index_id', 'type': 'unsigned'}, {'name': 'neq', 'type': 
'string'}, {
+      'name': 'nlt', 'type': 'string'}, {'name': 'ndlt', 'type': 
'string'}, {'name': 'sample',
+      'type': 'scalar'}]]
  ...
  box.space._index:get({box.space._space.index['name']:get('_trigger').id, 0})
  ---
@@ -45,12 +46,12 @@ 
box.space._index:get({box.space._space.index['name']:get('_trigger').id, 0})
  ...
  box.space._index:get({box.space._space.index['name']:get('_sql_stat1').id, 0})
  ---
-- [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string']]]
+- [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned']]]
  ...
  box.space._index:get({box.space._space.index['name']:get('_sql_stat4').id, 0})
  ---
-- [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string'], [5,
-      'scalar']]]
+- [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned'],
+    [5, 'scalar']]]
  ...
  box.space._schema:format()
  ---

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

* [tarantool-patches] Re: [PATCH v2 1/1] sql: hold in stat tables space/index id instead of name
  2018-09-07 16:49   ` Imeev Mergen
@ 2018-09-30 23:40     ` n.pettik
  2018-10-01 11:20       ` n.pettik
  2018-10-02 16:42       ` [tarantool-patches] Re[2]: [tarantool-patches] " Мерген Имеев
  0 siblings, 2 replies; 17+ messages in thread
From: n.pettik @ 2018-09-30 23:40 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen

[-- Attachment #1: Type: text/plain, Size: 6700 bytes --]

Sorry for quite late respond, I sincerely regret it.

Patch LGTM (except for several minor nits).

> diff --git a/src/box/sql.c b/src/box/sql.c
> index b158c50..87f2088 100644
> --- a/src/box/sql.c
> +++ b/src/box/sql.c
> @@ -1217,21 +1217,21 @@ void tarantoolSqlite3LoadSchema(struct init_data *init)
>      sql_init_callback(init, TARANTOOL_SYS_SQL_STAT1_NAME,
>                BOX_SQL_STAT1_ID, 0,
>                "CREATE TABLE \""TARANTOOL_SYS_SQL_STAT1_NAME
> -                   "\"(\"tbl\" text,"
> -                   "\"idx\" text,"
> +                   "\"(\"space_id\" INT,"
> +                   "\"index_id\" INT,"
>                     "\"stat\" not null,"
> -                   "PRIMARY KEY(\"tbl\", \"idx\"))");
> +                   "PRIMARY KEY(\"space_id\", \"index_id\"))");
> 
>      sql_init_callback(init, TARANTOOL_SYS_SQL_STAT4_NAME,
>                BOX_SQL_STAT4_ID, 0,
>                "CREATE TABLE \""TARANTOOL_SYS_SQL_STAT4_NAME
> -                   "\"(\"tbl\" text,"
> -                   "\"idx\" text,"
> +                   "\"(\"space_id\" INT,"
> +                   "\"index_id\" INT,"
>                     "\"neq\" text,"
>                     "\"nlt\" text,"
>                     "\"ndlt\" text,"
>                     "\"sample\","
> -                   "PRIMARY KEY(\"tbl\", \"idx\", \"sample\"))");
> +                   "PRIMARY KEY(\"space_id\", \"index_id\", \"sample\"))”);

Note: after you rebase on fresh 2.0, these fixes will disappear, since
we don’t add system spaces to separate hash anymore.

> diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
> index 76ae153..6938dcb 100644
> --- a/src/box/sql/analyze.c
> +++ b/src/box/sql/analyze.c
> @@ -38,8 +38,8 @@
>   *
>   * The following system tables are or have been supported:
>   *
> - *    CREATE TABLE _sql_stat1(tbl, idx, stat);
> - *    CREATE TABLE _sql_stat4(tbl, idx, nEq, nLt, nDLt, sample);
> + *    CREATE TABLE _sql_stat1(space_id, index_id, stat);
> + *    CREATE TABLE _sql_stat4(space_id, index_id, nEq, nLt, nDLt, sample);
>   *
>   * For most applications, _sql_stat1 provides all the statistics required
>   * for the query planner to make good choices.
> @@ -47,7 +47,7 @@
>   * Format of _sql_stat1:
>   *
>   * There is normally one row per index, with the index identified by the
> - * name in the idx column.  The tbl column is the name of the table to
> + * name in the index_id column.  The space_id column is the name of the table to

Nit:  * id in the index_id column. The space_id column is the id of the space to

> @@ -1410,27 +1405,14 @@ load_stat_from_space(struct sqlite3 *db, const char *sql_select_prepare,
>          goto finalize;
>      uint32_t current_idx_count = 0;
>      while (sqlite3_step(stmt) == SQLITE_ROW) {
> -        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
> -        if (space_name == NULL)
> -            continue;
> -        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
> -        if (index_name == NULL)
> -            continue;
> -        uint32_t sample_count = sqlite3_column_int(stmt, 2);
> -        uint32_t space_id = box_space_id_by_name(space_name,
> -                             strlen(space_name));
> -        assert(space_id != BOX_ID_NIL);
> +        uint32_t space_id = sqlite3_column_int(stmt, 0);
> +        assert(space_id != BOX_ID_NIL && space_id != 0);

Above you have mentioned that space_id now can’t be equal to BOX_ID_NIL:

>>> @@ -1246,24 +1246,14 @@ analysis_loader(void *data, int argc, char **argv, char **unused)
>>> 	struct analysis_index_info *info = (struct analysis_index_info *) data;
>>> 	assert(info->stats != NULL);
>>> 	struct index_stat *stat = &info->stats[info->index_count++];
>>> -	uint32_t space_id = box_space_id_by_name(argv[0], strlen(argv[0]));
>>> +	uint32_t space_id = atoll(argv[0]);
>> I would add assertion like:
>> assert(space_id != 0);
>> Since atoll in case of fail returns 0 and BOX_ID_NIL != 0.
> Actually now we cannot get BOX_ID_NIL, but we can get 0. Changed "if"
> that placed next line after "atoll”.

So, why do you check within assertion that condition? I mean there is no crime,
but does it make sense? The same for other similar checks.

> --- a/test/sql/sql-statN-index-drop.test.lua
> +++ b/test/sql/sql-statN-index-drop.test.lua
> @@ -14,15 +14,15 @@ box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
>  box.sql.execute("ANALYZE;")
> 
>  -- Checking the data.
> -box.sql.execute("SELECT * FROM \"_sql_stat4\";")
> -box.sql.execute("SELECT * FROM \"_sql_stat1\";")
> +box.sql.execute('SELECT "name","index_id","neq","nlt","ndlt","sample" FROM "_sql_stat4" join "_space" on "_sql_stat4"."space_id" = "_space"."id";')
> +box.sql.execute('SELECT "name","index_id","stat" FROM "_sql_stat1" join "_space" on "_sql_stat1"."space_id" = "_space"."id";’)

Nit: I don’t think that in this test (and the rest below) we need to fetch
name of space instead of its id. Lets leave this test as it was before.

> 
>  -- 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\";")
> +box.sql.execute('SELECT "name","index_id","neq","nlt","ndlt","sample" FROM "_sql_stat4" join "_space" on "_sql_stat4"."space_id" = "_space"."id";')
> +box.sql.execute('SELECT "name","index_id","stat" FROM "_sql_stat1" join "_space" on "_sql_stat1"."space_id" = "_space"."id";')
> 
>  --Cleaning up.
>  box.sql.execute("DROP TABLE t1;")
> @@ -41,15 +41,15 @@ box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
>  box.sql.execute("ANALYZE;")
> 
>  -- Checking the data.
> -box.sql.execute("SELECT * FROM \"_sql_stat4\";")
> -box.sql.execute("SELECT * FROM \"_sql_stat1\";")
> +box.sql.execute('SELECT "name","index_id","neq","nlt","ndlt","sample" FROM "_sql_stat4" join "_space" on "_sql_stat4"."space_id" = "_space"."id";')
> +box.sql.execute('SELECT "name","index_id","stat" FROM "_sql_stat1" join "_space" on "_sql_stat1"."space_id" = "_space"."id";')
> 
>  -- 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\";")
> +box.sql.execute('SELECT "name","index_id","neq","nlt","ndlt","sample" FROM "_sql_stat4" join "_space" on "_sql_stat4"."space_id" = "_space"."id";')
> +box.sql.execute('SELECT "name","index_id","stat" FROM "_sql_stat1" join "_space" on "_sql_stat1"."space_id" = "_space"."id";')
> 


[-- Attachment #2: Type: text/html, Size: 72555 bytes --]

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

* [tarantool-patches] Re: [PATCH v2 1/1] sql: hold in stat tables space/index id instead of name
  2018-09-30 23:40     ` n.pettik
@ 2018-10-01 11:20       ` n.pettik
  2018-10-02 16:42       ` [tarantool-patches] Re[2]: [tarantool-patches] " Мерген Имеев
  1 sibling, 0 replies; 17+ messages in thread
From: n.pettik @ 2018-10-01 11:20 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen

Also this patch will automatically close #2962 issue.
Hence, please refer to it in commit message to close it.

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

* [tarantool-patches] Re[2]: [tarantool-patches] [PATCH v2 1/1] sql: hold in stat tables space/index id instead of name
  2018-09-30 23:40     ` n.pettik
  2018-10-01 11:20       ` n.pettik
@ 2018-10-02 16:42       ` Мерген Имеев
  2018-10-09 12:36         ` [tarantool-patches] " n.pettik
  1 sibling, 1 reply; 17+ messages in thread
From: Мерген Имеев @ 2018-10-02 16:42 UTC (permalink / raw)
  To: n.pettik; +Cc: tarantool-patches

[-- Attachment #1: Type: text/plain, Size: 89225 bytes --]


Hello! Thank you for review! New patch at the end of the letter.
On 10/01/2018 02:40 AM, n.pettik wrote: 
>Sorry for quite late respond, I sincerely regret it.
> 
>Patch LGTM (except for several minor nits).
>
>>diff --git a/src/box/sql.c b/src/box/sql.c
>>index b158c50..87f2088 100644
>>--- a/src/box/sql.c
>>+++ b/src/box/sql.c
>>@@ -1217,21 +1217,21 @@ void tarantoolSqlite3LoadSchema(struct init_data *init)
>>     sql_init_callback(init, TARANTOOL_SYS_SQL_STAT1_NAME,
>>               BOX_SQL_STAT1_ID, 0,
>>               "CREATE TABLE \""TARANTOOL_SYS_SQL_STAT1_NAME
>>-                   "\"(\"tbl\" text,"
>>-                   "\"idx\" text,"
>>+                   "\"(\"space_id\" INT,"
>>+                   "\"index_id\" INT,"
>>                    "\"stat\" not null,"
>>-                   "PRIMARY KEY(\"tbl\", \"idx\"))");
>>+                   "PRIMARY KEY(\"space_id\", \"index_id\"))");
>>
>>     sql_init_callback(init, TARANTOOL_SYS_SQL_STAT4_NAME,
>>               BOX_SQL_STAT4_ID, 0,
>>               "CREATE TABLE \""TARANTOOL_SYS_SQL_STAT4_NAME
>>-                   "\"(\"tbl\" text,"
>>-                   "\"idx\" text,"
>>+                   "\"(\"space_id\" INT,"
>>+                   "\"index_id\" INT,"
>>                    "\"neq\" text,"
>>                    "\"nlt\" text,"
>>                    "\"ndlt\" text,"
>>                    "\"sample\","
>>-                   "PRIMARY KEY(\"tbl\", \"idx\", \"sample\"))");
>>+                   "PRIMARY KEY(\"space_id\", \"index_id\", \"sample\"))”);
> 
>Note: after you rebase on fresh 2.0, these fixes will disappear, since
>we don’t add system spaces to separate hash anymore. 
Rebased, fixed.
>
>>diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
>>index 76ae153..6938dcb 100644
>>--- a/src/box/sql/analyze.c
>>+++ b/src/box/sql/analyze.c
>>@@ -38,8 +38,8 @@
>>  *
>>  * The following system tables are or have been supported:
>>  *
>>- *    CREATE TABLE _sql_stat1(tbl, idx, stat);
>>- *    CREATE TABLE _sql_stat4(tbl, idx, nEq, nLt, nDLt, sample);
>>+ *    CREATE TABLE _sql_stat1(space_id, index_id, stat);
>>+ *    CREATE TABLE _sql_stat4(space_id, index_id, nEq, nLt, nDLt, sample);
>>  *
>>  * For most applications, _sql_stat1 provides all the statistics required
>>  * for the query planner to make good choices.
>>@@ -47,7 +47,7 @@
>>  * Format of _sql_stat1:
>>  *
>>  * There is normally one row per index, with the index identified by the
>>- * name in the idx column.  The tbl column is the name of the table to
>>+ * name in the index_id column.  The space_id column is the name of the table to
> 
>Nit:  * id in the index_id column. The space_id column is the id of the space to 
Fixed.
> 
>>@@ -1410,27 +1405,14 @@ load_stat_from_space(struct sqlite3 *db, const char *sql_select_prepare,
>>         goto finalize;
>>     uint32_t current_idx_count = 0;
>>     while (sqlite3_step(stmt) == SQLITE_ROW) {
>>-        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
>>-        if (space_name == NULL)
>>-            continue;
>>-        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
>>-        if (index_name == NULL)
>>-            continue;
>>-        uint32_t sample_count = sqlite3_column_int(stmt, 2);
>>-        uint32_t space_id = box_space_id_by_name(space_name,
>>-                             strlen(space_name));
>>-        assert(space_id != BOX_ID_NIL);
>>+        uint32_t space_id = sqlite3_column_int(stmt, 0);
>>+        assert(space_id != BOX_ID_NIL && space_id != 0);
> 
>Above you have mentioned that space_id now can’t be equal to BOX_ID_NIL:
> 
>>>>@@ -1246,24 +1246,14 @@ analysis_loader(void *data, int argc, char **argv, char **unused)
>>>>struct analysis_index_info *info = (struct analysis_index_info *) data;
>>>>assert(info->stats != NULL);
>>>>struct index_stat *stat = &info->stats[info->index_count++];
>>>>-uint32_t space_id = box_space_id_by_name(argv[0], strlen(argv[0]));
>>>>+uint32_t space_id = atoll(argv[0]); 
>>>I would add assertion like:
>>>assert(space_id != 0);
>>>Since atoll in case of fail returns 0 and BOX_ID_NIL != 0. 
>>Actually now we cannot get BOX_ID_NIL, but we can get 0. Changed "if"
>>that placed next line after "atoll”.
>So, why do you check within assertion that condition? I mean there is no crime,
>but does it make sense? The same for other similar checks. 
Fixed.
>
>>--- a/test/sql/sql-statN-index-drop.test.lua
>>+++ b/test/sql/sql-statN-index-drop.test.lua
>>@@ -14,15 +14,15 @@ box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
>> box.sql.execute("ANALYZE;")
>>
>> -- Checking the data.
>>-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
>>-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
>>+box.sql.execute('SELECT "name","index_id","neq","nlt","ndlt","sample" FROM "_sql_stat4" join "_space" on "_sql_stat4"."space_id" = "_space"."id";')
>>+box.sql.execute('SELECT "name","index_id","stat" FROM "_sql_stat1" join "_space" on "_sql_stat1"."space_id" = "_space"."id";’)
> 
>Nit: I don’t think that in this test (and the rest below) we need to fetch
>name of space instead of its id. Lets leave this test as it was before. 
Removed space id/name from result of executed queries.
>
>>
>> -- 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\";")
>>+box.sql.execute('SELECT "name","index_id","neq","nlt","ndlt","sample" FROM "_sql_stat4" join "_space" on "_sql_stat4"."space_id" = "_space"."id";')
>>+box.sql.execute('SELECT "name","index_id","stat" FROM "_sql_stat1" join "_space" on "_sql_stat1"."space_id" = "_space"."id";')
>>
>> --Cleaning up.
>> box.sql.execute("DROP TABLE t1;")
>>@@ -41,15 +41,15 @@ box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
>> box.sql.execute("ANALYZE;")
>>
>> -- Checking the data.
>>-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
>>-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
>>+box.sql.execute('SELECT "name","index_id","neq","nlt","ndlt","sample" FROM "_sql_stat4" join "_space" on "_sql_stat4"."space_id" = "_space"."id";')
>>+box.sql.execute('SELECT "name","index_id","stat" FROM "_sql_stat1" join "_space" on "_sql_stat1"."space_id" = "_space"."id";')
>>
>> -- 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\";")
>>+box.sql.execute('SELECT "name","index_id","neq","nlt","ndlt","sample" FROM "_sql_stat4" join "_space" on "_sql_stat4"."space_id" = "_space"."id";')
>>+box.sql.execute('SELECT "name","index_id","stat" FROM "_sql_stat1" join "_space" on "_sql_stat1"."space_id" = "_space"."id";')
>>

New patch:
commit dd448807244e22a07c642bceda923d35865e406f
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Fri Aug 31 12:37:48 2018 +0300

    sql: hold in stat tables space/index id instead of name
    
    To avoid problems with table and index renaming it is good idea
    to save ids of tables and indexes instead of their names. Ids of
    tables and indexes are fixed values.
    
    Closes #3242
    Closes #2962

diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap
index 6573938..a30f72e 100644
Binary files a/src/box/bootstrap.snap and b/src/box/bootstrap.snap differ
diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
index d9c2ae4..bc18b49 100644
--- a/src/box/lua/upgrade.lua
+++ b/src/box/lua/upgrade.lua
@@ -520,11 +520,11 @@ local function upgrade_to_2_1_0()
     _index:insert{_trigger.id, 1, 'space_id', 'tree', { unique = false },
                   {{1, 'unsigned'}}}
 
-    local stat1_ft = {{name='tbl', type='string'},
-                      {name='idx', type='string'},
+    local stat1_ft = {{name='space_id', type='unsigned'},
+                      {name='index_id', type='unsigned'},
                       {name='stat', type='string'}}
-    local stat4_ft = {{name='tbl', type='string'},
-                      {name='idx', type='string'},
+    local stat4_ft = {{name='space_id', type='unsigned'},
+                      {name='index_id', type='unsigned'},
                       {name='neq', type='string'},
                       {name='nlt', type='string'},
                       {name='ndlt', type='string'},
@@ -536,7 +536,7 @@ local function upgrade_to_2_1_0()
 
     log.info("create index primary on _sql_stat1")
     _index:insert{box.schema.SQL_STAT1_ID, 0, 'primary', 'tree',
-                  {unique = true}, {{0, 'string'}, {1, 'string'}}}
+                  {unique = true}, {{0, 'unsigned'}, {1, 'unsigned'}}}
 
     log.info("create space _sql_stat4")
     _space:insert{box.schema.SQL_STAT4_ID, ADMIN, '_sql_stat4', 'memtx', 0,
@@ -544,7 +544,7 @@ local function upgrade_to_2_1_0()
 
     log.info("create index primary on _sql_stat4")
     _index:insert{box.schema.SQL_STAT4_ID, 0, 'primary', 'tree',
-                  {unique = true}, {{0, 'string'}, {1, 'string'},
+                  {unique = true}, {{0, 'unsigned'}, {1, 'unsigned'},
                                     {5, 'scalar'}}}
 
     local fk_constr_ft = {{name='name', type='string'},
diff --git a/src/box/schema.cc b/src/box/schema.cc
index 2d2be26..e2c59fb 100644
--- a/src/box/schema.cc
+++ b/src/box/schema.cc
@@ -380,17 +380,17 @@ schema_init()
              &alter_space_on_replace_index, &on_stmt_begin_index);
 
     /* _sql_stat1 - a simpler statistics on space, seen in SQL. */
-    key_parts[0].fieldno = 0; /* space name */
-    key_parts[0].type = FIELD_TYPE_STRING;
-    key_parts[1].fieldno = 1; /* index name */
-    key_parts[1].type = FIELD_TYPE_STRING;
+    key_parts[0].fieldno = 0; /* space id */
+    key_parts[0].type = FIELD_TYPE_UNSIGNED;
+    key_parts[1].fieldno = 1; /* index id */
+    key_parts[1].type = FIELD_TYPE_UNSIGNED;
     sc_space_new(BOX_SQL_STAT1_ID, "_sql_stat1", key_parts, 2, NULL, NULL);
 
     /* _sql_stat4 - extensive statistics on space, seen in SQL. */
-    key_parts[0].fieldno = 0; /* space name */
-    key_parts[0].type = FIELD_TYPE_STRING;
-    key_parts[1].fieldno = 1; /* index name */
-    key_parts[1].type = FIELD_TYPE_STRING;
+    key_parts[0].fieldno = 0; /* space id */
+    key_parts[0].type = FIELD_TYPE_UNSIGNED;
+    key_parts[1].fieldno = 1; /* index id */
+    key_parts[1].type = FIELD_TYPE_UNSIGNED;
     key_parts[2].fieldno = 5; /* sample */
     key_parts[2].type = FIELD_TYPE_SCALAR;
     sc_space_new(BOX_SQL_STAT4_ID, "_sql_stat4", key_parts, 3, NULL, NULL);
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index 01e2ad1..7fc7f89 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -38,8 +38,8 @@
  *
  * The following system tables are or have been supported:
  *
- *    CREATE TABLE _sql_stat1(tbl, idx, stat);
- *    CREATE TABLE _sql_stat4(tbl, idx, nEq, nLt, nDLt, sample);
+ *    CREATE TABLE _sql_stat1(space_id, index_id, stat);
+ *    CREATE TABLE _sql_stat4(space_id, index_id, nEq, nLt, nDLt, sample);
  *
  * For most applications, _sql_stat1 provides all the statistics required
  * for the query planner to make good choices.
@@ -47,7 +47,7 @@
  * Format of _sql_stat1:
  *
  * There is normally one row per index, with the index identified by the
- * name in the idx column.  The tbl column is the name of the table to
+ * id in the index_id column.  The space_id column is the id of the space to
  * which the index belongs.  In each such row, the stat column will be
  * a string consisting of a list of integers.  The first integer in this
  * list is the number of rows in the index.  (This is the same as the
@@ -66,9 +66,9 @@
  * "unordered" keyword is present, then the query planner assumes that
  * the index is unordered and will not use the index for a range query.
  *
- * If the _sql_stat1.idx column is NULL, then the _sql_stat1.stat
+ * If the _sql_stat1.index_id column is NULL, then the _sql_stat1.stat
  * column contains a single integer which is the (estimated) number of
- * rows in the table identified by _sql_stat1.tbl.
+ * rows in the table identified by _sql_stat1.space_id.
  *
  * Format for _sql_stat4:
  *
@@ -78,9 +78,9 @@
  * queries.
  *
  * The _sql_stat4 table contains multiple entries for each index.
- * The idx column names the index and the tbl column is the table of the
- * index.  If the idx and tbl columns are the same, then the sample is
- * of the INTEGER PRIMARY KEY.  The sample column is a blob which is the
+ * The index_id column names the index and the space_id column is the space of
+ * the index. If the index_id is equal to 0, then the sample
+ * is of the INTEGER PRIMARY KEY.  The sample column is a blob which is the
  * binary encoding of a key from the index.  The nEq column is a
  * list of integers.  The first integer is the approximate number
  * of entries in the index whose left-most column exactly matches
@@ -124,11 +124,12 @@
  * @param parse Parsing context.
  * @param stat_cursor Open the _sql_stat1 table on this cursor.
  *        you should allocate |stat_names| cursors before call.
- * @param table_name Delete records of this table if specified.
+ * @param space_id Delete records of this table if id is not
+ *        BOX_ID_NIL.
  */
 static void
 vdbe_emit_stat_space_open(struct Parse *parse, int stat_cursor,
-              const char *table_name)
+              uint32_t space_id)
 {
     const char *stat_names[] = {"_sql_stat1", "_sql_stat4"};
     const uint32_t stat_ids[] = {BOX_SQL_STAT1_ID, BOX_SQL_STAT4_ID};
@@ -136,10 +137,9 @@ vdbe_emit_stat_space_open(struct Parse *parse, int stat_cursor,
     assert(v != NULL);
     assert(sqlite3VdbeDb(v) == parse->db);
     for (uint i = 0; i < lengthof(stat_names); ++i) {
-        const char *space_name = stat_names[i];
-        if (table_name != NULL) {
-            vdbe_emit_stat_space_clear(parse, space_name, NULL,
-                           table_name);
+        if (space_id != BOX_ID_NIL) {
+            vdbe_emit_stat_space_clear(parse, stat_names[i],
+                           space_id, BOX_ID_NIL);
         } else {
             sqlite3VdbeAddOp1(v, OP_Clear, stat_ids[i]);
         }
@@ -787,10 +787,10 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
     int key_reg = ++parse->nMem;
     /* Temporary use register. */
     int tmp_reg = ++parse->nMem;
-    /* Register containing table name. */
-    int tab_name_reg = ++parse->nMem;
-    /* Register containing index name. */
-    int idx_name_reg = ++parse->nMem;
+    /* Register containing space id. */
+    int space_id_reg = ++parse->nMem;
+    /* Register containing index id. */
+    int index_id_reg = ++parse->nMem;
     /* Value for the stat column of _sql_stat1. */
     int stat1_reg = ++parse->nMem;
     /* MUST BE LAST (see below). */
@@ -810,7 +810,7 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
     const char *tab_name = space_name(space);
     sqlite3VdbeAddOp4(v, OP_OpenRead, tab_cursor, 0, 0, (void *) space,
               P4_SPACEPTR);
-    sqlite3VdbeLoadString(v, tab_name_reg, space->def->name);
+    sqlite3VdbeAddOp2(v, OP_Integer, space->def->id, space_id_reg);
     for (uint32_t j = 0; j < space->index_count; ++j) {
         struct index *idx = space->index[j];
         const char *idx_name;
@@ -824,8 +824,8 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
         else
             idx_name = idx->def->name;
         int part_count = idx->def->key_def->part_count;
-        /* Populate the register containing the index name. */
-        sqlite3VdbeLoadString(v, idx_name_reg, idx_name);
+        /* Populate the register containing the index id. */
+        sqlite3VdbeAddOp2(v, OP_Integer, idx->def->iid, index_id_reg);
         VdbeComment((v, "Analysis for %s.%s", tab_name, idx_name));
         /*
          * Pseudo-code for loop that calls stat_push():
@@ -1000,9 +1000,8 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
         sqlite3VdbeAddOp2(v, OP_Next, idx_cursor, next_row_addr);
         /* Add the entry to the stat1 table. */
         callStatGet(v, stat4_reg, STAT_GET_STAT1, stat1_reg);
-        assert("BBB"[0] == AFFINITY_TEXT);
-        sqlite3VdbeAddOp4(v, OP_MakeRecord, tab_name_reg, 3, tmp_reg,
-                  "BBB", 0);
+        sqlite3VdbeAddOp4(v, OP_MakeRecord, space_id_reg, 3, tmp_reg,
+                  "DDB", 0);
         sqlite3VdbeAddOp2(v, OP_IdxInsert, stat_cursor, tmp_reg);
         /* Add the entries to the stat4 table. */
         int eq_reg = stat1_reg;
@@ -1035,7 +1034,7 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
         }
         sqlite3VdbeAddOp3(v, OP_MakeRecord, col_reg, part_count,
                   sample_reg);
-        sqlite3VdbeAddOp3(v, OP_MakeRecord, tab_name_reg, 6, tmp_reg);
+        sqlite3VdbeAddOp3(v, OP_MakeRecord, space_id_reg, 6, tmp_reg);
         sqlite3VdbeAddOp2(v, OP_IdxReplace, stat_cursor + 1, tmp_reg);
         /* P1==1 for end-of-loop. */
         sqlite3VdbeAddOp2(v, OP_Goto, 1, next_addr);
@@ -1092,7 +1091,7 @@ sql_analyze_database(struct Parse *parser)
     sql_set_multi_write(parser, false);
     int stat_cursor = parser->nTab;
     parser->nTab += 2;
-    vdbe_emit_stat_space_open(parser, stat_cursor, NULL);
+    vdbe_emit_stat_space_open(parser, stat_cursor, BOX_ID_NIL);
     struct analyze_data anal_data = { parser, stat_cursor };
     space_foreach(sql_space_foreach_analyze, (void *) &anal_data);
     loadAnalysis(parser);
@@ -1116,7 +1115,7 @@ vdbe_emit_analyze_table(struct Parse *parse, struct space *space)
      */
     int stat_cursor = parse->nTab;
     parse->nTab += 2;
-    vdbe_emit_stat_space_open(parse, stat_cursor, space->def->name);
+    vdbe_emit_stat_space_open(parse, stat_cursor, space->def->id);
     vdbe_emit_analyze_space(parse, space, stat_cursor);
     loadAnalysis(parse);
 }
@@ -1252,24 +1251,14 @@ analysis_loader(void *data, int argc, char **argv, char **unused)
     struct analysis_index_info *info = (struct analysis_index_info *) data;
     assert(info->stats != NULL);
     struct index_stat *stat = &info->stats[info->index_count++];
-    uint32_t space_id = box_space_id_by_name(argv[0], strlen(argv[0]));
-    if (space_id == BOX_ID_NIL)
+    uint32_t space_id = atoll(argv[0]);
+    if (space_id == 0)
         return -1;
     struct space *space = space_by_id(space_id);
     assert(space != NULL);
     struct index *index;
-    uint32_t iid = box_index_id_by_name(space_id, argv[1], strlen(argv[1]));
-    /*
-     * Convention is if index's name matches with space's
-     * one, then it is primary index.
-     */
-    if (iid != BOX_ID_NIL) {
-        index = space_index(space, iid);
-    } else {
-        if (sqlite3_stricmp(argv[0], argv[1]) != 0)
-            return -1;
-        index = space_index(space, 0);
-    }
+    uint32_t iid = atoll(argv[1]);
+    index = space_index(space, iid);
     assert(index != NULL);
     /*
      * Additional field is used to describe total
@@ -1383,8 +1372,12 @@ sample_compare(const void *a, const void *b, void *arg)
  * Arguments must point to SQL statements that return
  * data equivalent to the following:
  *
- *    prepare: SELECT tbl,idx,count(*) FROM _sql_stat4 GROUP BY tbl,idx;
- *    load: SELECT tbl,idx,neq,nlt,ndlt,sample FROM _sql_stat4;
+ * prepare:
+ *     SELECT space_id,index_id,count(*) FROM _sql_stat4
+ *     GROUP BY space_id,index_id;
+ * load:
+ *     SELECT space_id,index_id,neq,nlt,ndlt,sample
+ *     FROM _sql_stat4;
  *
  * 'prepare' statement is used to allocate enough memory for
  * statistics (i.e. arrays lt, dt, dlt and avg_eq). 'load' query
@@ -1416,27 +1409,14 @@ load_stat_from_space(struct sqlite3 *db, const char *sql_select_prepare,
         goto finalize;
     uint32_t current_idx_count = 0;
     while (sqlite3_step(stmt) == SQLITE_ROW) {
-        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
-        if (space_name == NULL)
-            continue;
-        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
-        if (index_name == NULL)
-            continue;
-        uint32_t sample_count = sqlite3_column_int(stmt, 2);
-        uint32_t space_id = box_space_id_by_name(space_name,
-                             strlen(space_name));
-        assert(space_id != BOX_ID_NIL);
+        uint32_t space_id = sqlite3_column_int(stmt, 0);
+        assert(space_id != 0);
         struct space *space = space_by_id(space_id);
         assert(space != NULL);
-        struct index *index;
-        uint32_t iid = box_index_id_by_name(space_id, index_name,
-                            strlen(index_name));
-        if (sqlite3_stricmp(space_name, index_name) == 0 &&
-            iid == BOX_ID_NIL)
-            index = space_index(space, 0);
-        else
-            index = space_index(space, iid);
+        uint32_t iid = sqlite3_column_int(stmt, 1);
+        struct index *index = space_index(space, iid);
         assert(index != NULL);
+        uint32_t sample_count = sqlite3_column_int(stmt, 2);
         uint32_t column_count = index->def->key_def->part_count;
         struct index_stat *stat = &stats[current_idx_count];
         stat->sample_field_count = column_count;
@@ -1488,27 +1468,12 @@ load_stat_from_space(struct sqlite3 *db, const char *sql_select_prepare,
     struct index *prev_index = NULL;
     current_idx_count = 0;
     while (sqlite3_step(stmt) == SQLITE_ROW) {
-        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
-        if (space_name == NULL)
-            continue;
-        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
-        if (index_name == NULL)
-            continue;
-        uint32_t space_id = box_space_id_by_name(space_name,
-                             strlen(space_name));
-        assert(space_id != BOX_ID_NIL);
+        uint32_t space_id = sqlite3_column_int(stmt, 0);
+        assert(space_id != 0);
         struct space *space = space_by_id(space_id);
         assert(space != NULL);
-        struct index *index;
-        uint32_t iid = box_index_id_by_name(space_id, index_name,
-                            strlen(index_name));
-        if (iid != BOX_ID_NIL) {
-            index = space_index(space, iid);
-        } else {
-            if (sqlite3_stricmp(space_name, index_name) != 0)
-                return -1;
-            index = space_index(space, 0);
-        }
+        uint32_t iid = sqlite3_column_int(stmt, 1);
+        struct index *index = space_index(space, iid);
         assert(index != NULL);
         uint32_t column_count = index->def->key_def->part_count;
         if (index != prev_index) {
@@ -1572,28 +1537,13 @@ load_stat_to_index(struct sqlite3 *db, const char *sql_select_load,
         return -1;
     uint32_t current_idx_count = 0;
     while (sqlite3_step(stmt) == SQLITE_ROW) {
-        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
-        if (space_name == NULL)
-            continue;
-        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
-        if (index_name == NULL)
-            continue;
-        uint32_t space_id = box_space_id_by_name(space_name,
-                             strlen(space_name));
-        if (space_id == BOX_ID_NIL)
+        uint32_t space_id = sqlite3_column_int(stmt, 0);
+        if (space_id == 0)
             return -1;
         struct space *space = space_by_id(space_id);
         assert(space != NULL);
-        struct index *index;
-        uint32_t iid = box_index_id_by_name(space_id, index_name,
-                            strlen(index_name));
-        if (iid != BOX_ID_NIL) {
-            index = space_index(space, iid);
-        } else {
-            if (sqlite3_stricmp(space_name, index_name) != 0)
-                return -1;
-            index = space_index(space, 0);
-        }
+        uint32_t iid = sqlite3_column_int(stmt, 1);
+        struct index *index = space_index(space, iid);
         assert(index != NULL);
         free(index->def->opts.stat);
         index->def->opts.stat = stats[current_idx_count++];
@@ -1736,7 +1686,7 @@ sql_analysis_load(struct sqlite3 *db)
     info.stats = stats;
     info.index_count = 0;
     const char *load_stat1 =
-        "SELECT \"tbl\",\"idx\",\"stat\" FROM \"_sql_stat1\"";
+        "SELECT \"space_id\",\"index_id\",\"stat\" FROM \"_sql_stat1\"";
     /* Load new statistics out of the _sql_stat1 table. */
     if (sqlite3_exec(db, load_stat1, analysis_loader, &info, 0) != 0)
         goto fail;
@@ -1749,10 +1699,10 @@ sql_analysis_load(struct sqlite3 *db)
      * statistics. Result rows are given in a form:
      * <table name>, <index name>, <count of samples>
      */
-    const char *init_query = "SELECT \"tbl\",\"idx\",count(*) FROM "
-                 "\"_sql_stat4\" GROUP BY \"tbl\",\"idx\"";
+    const char *init_query = "SELECT \"space_id\",\"index_id\",count(*) FROM "
+                 "\"_sql_stat4\" GROUP BY \"space_id\",\"index_id\"";
     /* Query for loading statistics into in-memory structs. */
-    const char *load_query = "SELECT \"tbl\",\"idx\",\"neq\",\"nlt\","
+    const char *load_query = "SELECT \"space_id\",\"index_id\",\"neq\",\"nlt\","
                  "\"ndlt\",\"sample\" FROM \"_sql_stat4\"";
     /* Load the statistics from the _sql_stat4 table. */
     if (load_stat_from_space(db, init_query, load_query, stats) != 0)
@@ -1796,8 +1746,8 @@ sql_analysis_load(struct sqlite3 *db)
      * Ordered query is needed to be sure that indexes come
      * in the same order as in previous SELECTs.
      */
-    const char *order_query = "SELECT \"tbl\",\"idx\" FROM "
-                  "\"_sql_stat4\" GROUP BY \"tbl\",\"idx\"";
+    const char *order_query = "SELECT \"space_id\",\"index_id\" FROM "
+                  "\"_sql_stat4\" GROUP BY \"space_id\",\"index_id\"";
     if (load_stat_to_index(db, order_query, heap_stats) != 0)
         goto fail;
     if (box_txn_commit() != 0)
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 43be777..c5a98e8 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -1629,75 +1629,43 @@ sql_store_select(struct Parse *parse_context, struct Select *select)
     parse_context->parsed_ast.select = select_copy;
 }
 
-/**
- * Create expression record "@col_name = '@col_value'".
- *
- * @param parse The parsing context.
- * @param col_name Name of column.
- * @param col_value Name of row.
- * @retval not NULL on success.
- * @retval NULL on failure.
- */
-static struct Expr *
-sql_id_eq_str_expr(struct Parse *parse, const char *col_name,
-           const char *col_value)
-{
-    struct sqlite3 *db = parse->db;
-
-    struct Expr *col_name_expr = sqlite3Expr(db, TK_ID, col_name);
-    if (col_name_expr == NULL)
-        return NULL;
-    struct Expr *col_value_expr = sqlite3Expr(db, TK_STRING, col_value);
-    if (col_value_expr == NULL) {
-        sql_expr_delete(db, col_name_expr, false);
-        return NULL;
-    }
-    return sqlite3PExpr(parse, TK_EQ, col_name_expr, col_value_expr);
-}
-
 void
 vdbe_emit_stat_space_clear(struct Parse *parse, const char *stat_table_name,
-               const char *idx_name, const char *table_name)
+               uint32_t space_id, uint32_t index_id)
 {
-    assert(idx_name != NULL || table_name != NULL);
     struct sqlite3 *db = parse->db;
     assert(!db->mallocFailed);
     struct SrcList *src_list = sql_alloc_src_list(db);
     if (src_list != NULL)
         src_list->a[0].zName = sqlite3DbStrDup(db, stat_table_name);
-    struct Expr *where = NULL;
-    if (idx_name != NULL) {
-        struct Expr *expr = sql_id_eq_str_expr(parse, "idx", idx_name);
-        if (expr != NULL)
-            where = sqlite3ExprAnd(db, expr, where);
-    }
-    if (table_name != NULL) {
-        struct Expr *expr = sql_id_eq_str_expr(parse, "tbl", table_name);
-        if (expr != NULL)
-            where = sqlite3ExprAnd(db, expr, where);
-    }
-    /**
-     * On memory allocation error sql_table delete_from
-     * releases memory for its own.
-     */
-    sql_table_delete_from(parse, src_list, where);
+    struct Expr *expr = NULL;
+    struct Expr *col = sqlite3Expr(db, TK_ID, "space_id");
+    struct Expr *val = sqlite3ExprInteger(db, space_id);
+    if (col != NULL && val != NULL)
+        expr = sqlite3PExpr(parse, TK_EQ, col, val);
+    if (index_id != BOX_ID_NIL && expr != NULL) {
+        col = sqlite3Expr(db, TK_ID, "index_id");
+        val = sqlite3ExprInteger(db, index_id);
+        expr = sqlite3ExprAnd(db, sqlite3PExpr(parse, TK_EQ, col, val),
+                      expr);
+    }
+    sql_table_delete_from(parse, src_list, expr);
 }
 
 /**
  * Remove entries from the _sql_stat1 and _sql_stat4
  * system spaces after a DROP INDEX or DROP TABLE command.
  *
- * @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.
+ * @param parse The parsing context.
+ * @param space_id Id of table to be dropped or table that
+ *        contains index to be dropped.
+ * @param index_id Id of index to be dropped.
  */
 static void
-sql_clear_stat_spaces(struct Parse *parse, const char *table_name,
-              const char *idx_name)
+sql_clear_stat_spaces(struct Parse *parse, uint32_t space_id, uint32_t index_id)
 {
-    vdbe_emit_stat_space_clear(parse, "_sql_stat4", idx_name, table_name);
-    vdbe_emit_stat_space_clear(parse, "_sql_stat1", idx_name, table_name);
+    vdbe_emit_stat_space_clear(parse, "_sql_stat4", space_id, index_id);
+    vdbe_emit_stat_space_clear(parse, "_sql_stat1", space_id, index_id);
 }
 
 /**
@@ -1915,7 +1883,7 @@ sql_drop_table(struct Parse *parse_context, struct SrcList *table_name_list,
             goto exit_drop_table;
         }
     }
-    sql_clear_stat_spaces(parse_context, space_name, NULL);
+    sql_clear_stat_spaces(parse_context, space->def->id, BOX_ID_NIL);
     sql_code_drop_table(parse_context, space, is_view);
 
  exit_drop_table:
@@ -2776,7 +2744,8 @@ sql_drop_index(struct Parse *parse_context, struct SrcList *index_name_list,
      * But firstly, delete statistics since schema
      * changes after DDL.
      */
-    sql_clear_stat_spaces(parse_context, table_name, index->def->name);
+    sql_clear_stat_spaces(parse_context, space->def->id, index->def->iid);
+
     int record_reg = ++parse_context->nMem;
     int space_id_reg = ++parse_context->nMem;
     sqlite3VdbeAddOp2(v, OP_Integer, space_id, space_id_reg);
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index e8078b4..453b7d4 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -4858,11 +4858,13 @@ vdbe_emit_halt_with_presence_test(struct Parse *parser, int space_id,
  *
  * @param parse The parsing context.
  * @param stat_table_name System stat table name.
- * @param idx_name Index name.
- * @param table_name Table name.
+ * @param table_id Id of table of which analysis will be deleted.
+ * @param index_id Id of index of which analysis will be deleted.
+ *        If index_id is BOX_ID_NIL than analysis of all indexes
+ *        of selected table will be deleted.
  */
 void
 vdbe_emit_stat_space_clear(struct Parse *parse, const char *stat_table_name,
-               const char *idx_name, const char *table_name);
+               uint32_t table_id, uint32_t index_id);
 
 #endif                /* SQLITEINT_H */
diff --git a/test/box-py/bootstrap.result b/test/box-py/bootstrap.result
index 506aca3..b5850c2 100644
--- a/test/box-py/bootstrap.result
+++ b/test/box-py/bootstrap.result
@@ -74,11 +74,12 @@ box.space._space:select{}
         'type': 'unsigned'}]]
   - [340, 1, '_space_sequence', 'memtx', 0, {}, [{'name': 'id', 'type': 'unsigned'},
       {'name': 'sequence_id', 'type': 'unsigned'}, {'name': 'is_generated', 'type': 'boolean'}]]
-  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'tbl', 'type': 'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'stat', 'type': 'string'}]]
-  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'tbl', 'type': 'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'neq', 'type': 'string'}, {'name': 'nlt', 'type': 'string'},
-      {'name': 'ndlt', 'type': 'string'}, {'name': 'sample', 'type': 'scalar'}]]
+  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'space_id', 'type': 'unsigned'},
+      {'name': 'index_id', 'type': 'unsigned'}, {'name': 'stat', 'type': 'string'}]]
+  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'space_id', 'type': 'unsigned'},
+      {'name': 'index_id', 'type': 'unsigned'}, {'name': 'neq', 'type': 'string'},
+      {'name': 'nlt', 'type': 'string'}, {'name': 'ndlt', 'type': 'string'}, {'name': 'sample',
+        'type': 'scalar'}]]
   - [356, 1, '_fk_constraint', 'memtx', 0, {}, [{'name': 'name', 'type': 'string'},
       {'name': 'child_id', 'type': 'unsigned'}, {'name': 'parent_id', 'type': 'unsigned'},
       {'name': 'is_deferred', 'type': 'boolean'}, {'name': 'match', 'type': 'string'},
@@ -134,9 +135,9 @@ box.space._index:select{}
   - [330, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
   - [340, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
   - [340, 1, 'sequence', 'tree', {'unique': false}, [[1, 'unsigned']]]
-  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 'string']]]
-  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 'string'], [
-        5, 'scalar']]]
+  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 'unsigned']]]
+  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 'unsigned'],
+      [5, 'scalar']]]
   - [356, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 'unsigned']]]
   - [356, 1, 'child_id', 'tree', {'unique': false}, [[1, 'unsigned']]]
 ...
diff --git a/test/box/access_misc.result b/test/box/access_misc.result
index 4ffeb38..83e9d3e 100644
--- a/test/box/access_misc.result
+++ b/test/box/access_misc.result
@@ -813,11 +813,12 @@ box.space._space:select()
         'type': 'unsigned'}]]
   - [340, 1, '_space_sequence', 'memtx', 0, {}, [{'name': 'id', 'type': 'unsigned'},
       {'name': 'sequence_id', 'type': 'unsigned'}, {'name': 'is_generated', 'type': 'boolean'}]]
-  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'tbl', 'type': 'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'stat', 'type': 'string'}]]
-  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'tbl', 'type': 'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'neq', 'type': 'string'}, {'name': 'nlt', 'type': 'string'},
-      {'name': 'ndlt', 'type': 'string'}, {'name': 'sample', 'type': 'scalar'}]]
+  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'space_id', 'type': 'unsigned'},
+      {'name': 'index_id', 'type': 'unsigned'}, {'name': 'stat', 'type': 'string'}]]
+  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'space_id', 'type': 'unsigned'},
+      {'name': 'index_id', 'type': 'unsigned'}, {'name': 'neq', 'type': 'string'},
+      {'name': 'nlt', 'type': 'string'}, {'name': 'ndlt', 'type': 'string'}, {'name': 'sample',
+        'type': 'scalar'}]]
   - [356, 1, '_fk_constraint', 'memtx', 0, {}, [{'name': 'name', 'type': 'string'},
       {'name': 'child_id', 'type': 'unsigned'}, {'name': 'parent_id', 'type': 'unsigned'},
       {'name': 'is_deferred', 'type': 'boolean'}, {'name': 'match', 'type': 'string'},
diff --git a/test/box/alter.result b/test/box/alter.result
index 9a1086e..86e8c3a 100644
--- a/test/box/alter.result
+++ b/test/box/alter.result
@@ -228,9 +228,9 @@ _index:select{}
   - [330, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
   - [340, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
   - [340, 1, 'sequence', 'tree', {'unique': false}, [[1, 'unsigned']]]
-  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 'string']]]
-  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 'string'], [
-        5, 'scalar']]]
+  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 'unsigned']]]
+  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 'unsigned'],
+      [5, 'scalar']]]
   - [356, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 'unsigned']]]
   - [356, 1, 'child_id', 'tree', {'unique': false}, [[1, 'unsigned']]]
 ...
diff --git a/test/sql-tap/analyze1.test.lua b/test/sql-tap/analyze1.test.lua
index 2d8eed9..df433de 100755
--- a/test/sql-tap/analyze1.test.lua
+++ b/test/sql-tap/analyze1.test.lua
@@ -65,7 +65,7 @@ test:do_execsql_test(
 test:do_execsql_test(
     "analyze-1.7",
     [[
-        SELECT * FROM "_sql_stat1" WHERE "idx" IS NOT NULL
+        SELECT * FROM "_sql_stat1" WHERE "index_id" IS NOT NULL
     ]], {
         -- <analyze-1.7>
         -- </analyze-1.7>
@@ -84,7 +84,7 @@ test:do_catchsql_test(
 test:do_execsql_test(
     "analyze-1.9",
     [[
-        SELECT * FROM "_sql_stat1" WHERE "idx" IS NOT NULL
+        SELECT * FROM "_sql_stat1" WHERE "index_id" IS NOT NULL
     ]], {
         -- <analyze-1.9>
         -- </analyze-1.9>
@@ -119,7 +119,7 @@ test:do_execsql_test(
     [[
         CREATE INDEX t1i1 ON t1(a);
         ANALYZE t1;
-        SELECT * FROM "_sql_stat1" ORDER BY "idx";
+        SELECT * FROM "_sql_stat1" ORDER BY "index_id";
     ]], {
         -- <analyze-2.1>
         -- </analyze-2.1>
@@ -130,7 +130,7 @@ test:do_execsql_test(
     [[
         CREATE INDEX t1i2 ON t1(b);
         ANALYZE t1;
-        SELECT * FROM "_sql_stat1" ORDER BY "idx";
+        SELECT * FROM "_sql_stat1" ORDER BY "index_id";
     ]], {
         -- <analyze-2.2>
         -- </analyze-2.2>
@@ -141,7 +141,7 @@ test:do_execsql_test(
     [[
         CREATE INDEX t1i3 ON t1(a,b);
         ANALYZE;
-        SELECT * FROM "_sql_stat1" ORDER BY "idx";
+        SELECT * FROM "_sql_stat1" ORDER BY "index_id";
     ]], {
         -- <analyze-2.3>
         -- </analyze-2.3>
@@ -157,10 +157,10 @@ test:do_execsql_test(
         INSERT INTO t1 VALUES(1, 1,2);
         INSERT INTO t1 VALUES(2, 1,3);
         ANALYZE t1;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
     ]], {
         -- <analyze-3.1>
-        "T1", "2 1", "T1I1", "2 2", "T1I2", "2 1", "T1I3", "2 2 1"
+        0, "2 1", 1, "2 2", 2, "2 1", 3, "2 2 1"
         -- </analyze-3.1>
     })
 
@@ -170,10 +170,10 @@ test:do_execsql_test(
         INSERT INTO t1 VALUES(3, 1,4);
         INSERT INTO t1 VALUES(4, 1,5);
         ANALYZE t1;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
     ]], {
         -- <analyze-3.2>
-        "T1", "4 1", "T1I1", "4 4", "T1I2", "4 1", "T1I3", "4 4 1"
+        0, "4 1", 1, "4 4", 2, "4 1", 3, "4 4 1"
         -- </analyze-3.2>
     })
 
@@ -182,10 +182,10 @@ test:do_execsql_test(
     [[
         INSERT INTO t1 (a,b) VALUES(2,5);
         ANALYZE;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
     ]], {
         -- <analyze-3.3>
-        "T1","5 1", "T1I1", "5 3", "T1I2", "5 2", "T1I3", "5 3 1"
+        0,"5 1", 1, "5 3", 2, "5 2", 3, "5 3 1"
         -- </analyze-3.3>
     })
 
@@ -198,10 +198,10 @@ test:do_execsql_test(
         CREATE INDEX t2i2 ON t2(b);
         CREATE INDEX t2i3 ON t2(a,b);
         ANALYZE;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
     ]], {
         -- <analyze-3.4>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 1","T2I1","5 3","T2I2","5 2","T2I3","5 3 1"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",2,"5 2",3,"5 3 1",3,"5 3 1"
         -- </analyze-3.4>
     })
 
@@ -210,10 +210,10 @@ test:do_execsql_test(
     [[
         DROP INDEX t2i3 ON t2;;
         ANALYZE t1;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
     ]], {
         -- <analyze-3.5>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 1","T2I1","5 3","T2I2","5 2"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",2,"5 2",3,"5 3 1"
         -- </analyze-3.5>
     })
 
@@ -221,10 +221,10 @@ test:do_execsql_test(
     "analyze-3.6",
     [[
         ANALYZE t2;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
     ]], {
         -- <analyze-3.6>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 1","T2I1","5 3","T2I2","5 2"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",2,"5 2",3,"5 3 1"
         -- </analyze-3.6>
     })
 
@@ -233,10 +233,10 @@ test:do_execsql_test(
     [[
         DROP INDEX t2i2 ON t2;
         ANALYZE t2;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
     ]], {
         -- <analyze-3.7>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 1","T2I1","5 3"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",3,"5 3 1"
         -- </analyze-3.7>
     })
 
@@ -250,7 +250,7 @@ test:do_execsql_test(
         CREATE INDEX t3i3 ON t3(d,b,c,a);
         DROP TABLE t1;
         DROP TABLE t2;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
     ]], {
         -- <analyze-3.8>      
         -- </analyze-3.8>
@@ -260,10 +260,10 @@ test:do_execsql_test(
     "analyze-3.9",
     [[
         ANALYZE;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
     ]], {
         -- <analyze-3.9>
-        "T3","5 1","T3I1","5 3","T3I2","5 3 1 1 1","T3I3","5 5 2 1 1"
+        0,"5 1",1,"5 3",2,"5 3 1 1 1",3,"5 5 2 1 1"
         -- </analyze-3.9>
     })
 
@@ -277,7 +277,7 @@ test:do_execsql_test(
 --         INSERT INTO [silly " name] (a,b,c) VALUES(1, 2, 3);
 --         INSERT INTO [silly " name] (a,b,c) VALUES(4, 5, 6);
 --         ANALYZE;
---         SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+--         SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
 --     ]], {
 --         -- <analyze-3.10>
 --         "another foolish ' name", "2 1", "foolish ' name", "2 1 1", "t3i1", "5 3", "t3i2", "5 3 1 1 1", "t3i3", "5 5 2 1 1"
@@ -288,7 +288,7 @@ test:do_execsql_test(
 --     "analyze-3.11",
 --     [[
 --         DROP INDEX "foolish ' name";
---         SELECT "idx", "stat" FROM sqlite_stat1 ORDER BY "idx";
+--         SELECT "index_id", "stat" FROM sqlite_stat1 ORDER BY "index_id";
 --     ]], {
 --         -- <analyze-3.11>
 --         "another foolish ' name", "2 1", "t3i1", "5 3", "t3i2", "5 3 1 1 1", "t3i3", "5 5 2 1 1"
@@ -299,7 +299,7 @@ test:do_execsql_test(
 --     "analyze-3.11",
 --     [[
 --         DROP TABLE "silly "" name";
---         SELECT "idx", "stat" FROM sqlite_stat1 ORDER BY "idx";
+--         SELECT "index_id", "stat" FROM sqlite_stat1 ORDER BY "index_id";
 --     ]], {
 --         -- <analyze-3.11>
 --         "t3i1", "5 3", "t3i2", "5 3 1 1 1", "t3i3", "5 5 2 1 1"
@@ -317,31 +317,33 @@ test:do_execsql_test(
         CREATE INDEX t4i2 ON t4(y);
         INSERT INTO t4 SELECT id,a,b,c FROM t3;
         ANALYZE;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
     ]], {
         -- <analyze-4.0>
-        "T3", "5 1", "T3I1", "5 3", "T3I2", "5 3 1 1 1", "T3I3", "5 5 2 1 1", "T4", "5 1", "T4I1", "5 3", "T4I2", "5 2"
+        0, "5 1", 0, "5 1", 1, "5 3", 1, "5 3", 2, "5 3 1 1 1", 2, "5 2", 3, "5 5 2 1 1"
         -- </analyze-4.0>
     })
 
+t4 = box.space.T4
+
 test:do_execsql_test(
     "analyze-4.1",
-    [[
+    string.format([[
         DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1" VALUES('t4', 't4i1', 'nonsense');
-        INSERT INTO "_sql_stat1" VALUES('t4', 't4i2', '432653287412874653284129847632');
+        INSERT INTO "_sql_stat1" VALUES(%i, %i, 'nonsense');
+        INSERT INTO "_sql_stat1" VALUES(%i, %i, '432653287412874653284129847632');
         SELECT * FROM t4 WHERE x = 1234;
-    ]], {
+    ]], t4.id, t4.index['T4I1'].id, t4.id, t4.index['T4I2'].id), {
         -- <analyze-4.1>
         -- </analyze-4.1>
     })
 
 test:do_execsql_test(
     "analyze-4.2",
-    [[
-        INSERT INTO "_sql_stat1" VALUES('t4', 'xyzzy', '0 1 2 3');
+    string.format([[
+        INSERT INTO "_sql_stat1" VALUES(%i, 12345, '0 1 2 3');
         SELECT * FROM t4 WHERE x = 1234;
-    ]], {
+    ]], t4.id), {
         -- <analyze-4.2>
         -- </analyze-4.2>
     })
@@ -367,20 +369,20 @@ test:do_execsql_test(
         INSERT INTO t3 (a,b,c,d) SELECT a+64, b+64, c+64, d+64 FROM t3;
         INSERT INTO t4 (x,y,z) SELECT a, b, c FROM t3;
         ANALYZE;
-        SELECT DISTINCT "tbl" FROM "_sql_stat1" ORDER BY 1;
+        SELECT COUNT(DISTINCT "space_id") FROM "_sql_stat1" ORDER BY 1;
     ]], {
         -- <analyze-5.0>
-        "T3", "T4"
+        2
         -- </analyze-5.0>
     })
 
 test:do_execsql_test(
     "analyze-5.0.1",
     [[
-        SELECT DISTINCT "idx" FROM "_sql_stat1" ORDER BY 1;
+        SELECT "index_id" FROM "_sql_stat1" ORDER BY 1;
     ]], {
         -- <analyze-5.0>
-        "T3", "T3I1", "T3I2", "T3I3", "T4", "T4I1", "T4I2"
+        0, 0, 1, 1, 2, 2, 3
         -- </analyze-5.0>
     })
 
@@ -389,20 +391,20 @@ stat = "_sql_stat4"
 test:do_execsql_test(
     "analyze-5.1",
     string.format([[
-            SELECT DISTINCT "idx" FROM "%s" ORDER BY 1;
-        ]], stat, stat), {
+            SELECT DISTINCT "index_id" FROM "%s" ORDER BY 1;
+        ]], stat), {
         -- <analyze-5.1>
-        "T3", "T3I1", "T3I2", "T3I3", "T4", "T4I1", "T4I2"
+        0, 1, 2, 3
         -- </analyze-5.1>
     })
 
 test:do_execsql_test(
     "analyze-5.1.1",
     string.format([[
-            SELECT DISTINCT "tbl" FROM "%s" ORDER BY 1;
-        ]], stat, stat), {
+            SELECT COUNT(DISTINCT "space_id") FROM "%s" ORDER BY 1;
+        ]], stat), {
         -- <analyze-5.1>
-        "T3", "T4"
+        2
         -- </analyze-5.1>
     })
 
@@ -411,40 +413,40 @@ test:do_execsql_test(
     [[
         DROP INDEX t3i2 ON t3;
         ANALYZE;
-        SELECT DISTINCT "idx" FROM "_sql_stat1" ORDER BY 1;
+        SELECT "index_id" FROM "_sql_stat1" ORDER BY 1;
     ]], {
         -- <analyze-5.2>
-        "T3", "T3I1", "T3I3", "T4", "T4I1", "T4I2"
+        0, 0, 1, 1, 2, 3
         -- </analyze-5.2>
     })
 
 test:do_execsql_test(
     "analyze-5.2.1",
     [[
-        SELECT DISTINCT "tbl" FROM "_sql_stat1" ORDER BY 1;
+        SELECT COUNT(DISTINCT "space_id") FROM "_sql_stat1" ORDER BY 1;
     ]], {
         -- <analyze-5.2>
-        "T3", "T4"
+        2
         -- </analyze-5.2>
     })
 
 test:do_execsql_test(
     "analyze-5.3",
     string.format([[
-            SELECT DISTINCT "idx" FROM "%s" ORDER BY 1;
+            SELECT DISTINCT "index_id" FROM "%s" ORDER BY 1;
         ]], stat, stat), {
         -- <analyze-5.3>
-        "T3", "T3I1", "T3I3", "T4", "T4I1", "T4I2"
+        0, 1, 2, 3
         -- </analyze-5.3>
     })
 
 test:do_execsql_test(
     "analyze-5.3.1",
     string.format([[
-            SELECT DISTINCT "tbl" FROM "%s" ORDER BY 1;
-        ]], stat, stat), {
+            SELECT COUNT(DISTINCT "space_id") FROM "%s" ORDER BY 1;
+        ]], stat, stat, stat), {
         -- <analyze-5.3>
-        "T3", "T4"
+        2
         -- </analyze-5.3>
     })
 
@@ -453,40 +455,40 @@ test:do_execsql_test(
     [[
         DROP TABLE IF EXISTS t3;
         ANALYZE;
-        SELECT DISTINCT "idx" FROM "_sql_stat1" ORDER BY 1;
+        SELECT DISTINCT "index_id" FROM "_sql_stat1" ORDER BY 1;
     ]], {
         -- <analyze-5.4>
-        "T4", "T4I1", "T4I2"
+        0, 1, 2
         -- </analyze-5.4>
     })
 
 test:do_execsql_test(
     "analyze-5.4.1",
     [[
-        SELECT DISTINCT "tbl" FROM "_sql_stat1" ORDER BY 1;
+        SELECT COUNT(DISTINCT "space_id") FROM "_sql_stat1" ORDER BY 1;
     ]], {
         -- <analyze-5.4>
-        "T4"
+        1
         -- </analyze-5.4>
     })
 
 test:do_execsql_test(
     "analyze-5.5",
     string.format([[
-            SELECT DISTINCT "idx" FROM "%s" ORDER BY 1;
+            SELECT DISTINCT "index_id" FROM "%s" ORDER BY 1;
         ]], stat), {
         -- <analyze-5.5>
-        "T4", "T4I1", "T4I2"
+        0, 1, 2
         -- </analyze-5.5>
     })
 
 test:do_execsql_test(
     "analyze-5.5.1",
     string.format([[
-            SELECT DISTINCT "tbl" FROM "%s" ORDER BY 1;
-        ]], stat), {
+            SELECT COUNT(DISTINCT "space_id") FROM "%s" ORDER BY 1;
+        ]], stat, stat), {
         -- <analyze-5.5>
-        "T4"
+        1
         -- </analyze-5.5>
     })
 
@@ -516,33 +518,35 @@ test:do_test(
     -- </analyze-6.1.1>
 })
 
+t1 = box.space.T1
+
 test:do_execsql_test(
     "analyze-6.1.2",
-    [[
-            SELECT * FROM "_sql_stat1" where "tbl"='T1' and "idx"='I1' LIMIT 1;
-    ]], {
+    string.format([[
+            SELECT "index_id", "stat" FROM "_sql_stat1" where "space_id"=%i and "index_id"=%i LIMIT 1;
+    ]], t1.id, t1.index['I1'].id), {
     -- <analyze-6.1.2>
-    "T1", "I1", "221 221 221 221 2"
+    1, "221 221 221 221 2"
     -- </analyze-6.1.2>
 })
 
 test:do_execsql_test(
     "analyze-6.1.3",
-    [[
-            SELECT "tbl", "idx", "neq", "nlt", "ndlt" FROM "_sql_stat4" where "tbl"='T1' and "idx"='I1' ORDER BY "nlt" LIMIT 1;
-    ]], {
+    string.format([[
+            SELECT "index_id", "neq", "nlt", "ndlt" FROM "_sql_stat4" where "space_id"=%i and "index_id"=%i ORDER BY "nlt" LIMIT 1;
+    ]], t1.id, t1.index['I1'].id), {
     -- <analyze-6.1.3>
-    "T1", "I1", "221 221 221 1", "0 0 0 10", "0 0 0 10"
+    1, "221 221 221 1", "0 0 0 10", "0 0 0 10"
     -- </analyze-6.1.3>
 })
 
 test:do_execsql_test(
     "analyze-6.1.4",
-    [[
-            SELECT "tbl", "idx", "neq", "nlt", "ndlt" FROM "_sql_stat4" where "tbl"='T1' and "idx"='I1' ORDER BY "nlt" DESC LIMIT 1;
-    ]], {
+    string.format([[
+            SELECT "index_id", "neq", "nlt", "ndlt" FROM "_sql_stat4" where "space_id"=%i and "index_id"=%i ORDER BY "nlt" DESC LIMIT 1;
+    ]], t1.id, t1.index['I1'].id), {
     -- <analyze-6.1.4>
-    "T1", "I1", "221 221 221 1", "0 0 0 99", "0 0 0 99"
+    1, "221 221 221 1", "0 0 0 99", "0 0 0 99"
     -- </analyze-6.1.4>
 })
 
diff --git a/test/sql-tap/analyze4.test.lua b/test/sql-tap/analyze4.test.lua
index c2cc190..d64cba8 100755
--- a/test/sql-tap/analyze4.test.lua
+++ b/test/sql-tap/analyze4.test.lua
@@ -49,15 +49,19 @@ test:do_test(
         -- </analyze4-1.0>
     })
 
+t1 = box.space.T1
+
 -- Verify that the t1b index shows that it does not narrow down the
 -- search any at all.
 --
 test:do_execsql_test(
     "analyze4-1.1",
-    [[ SELECT "idx", "stat" FROM "_sql_stat1" WHERE "tbl"='T1' ORDER BY "idx"; ]],
+    string.format([[
+        SELECT "index_id", "stat" FROM "_sql_stat1" WHERE "space_id"=%i ORDER BY "index_id";
+    ]], t1.id),
     {
         -- <analyze4-1.1>
-        "T1","128 1", "T1A", "128 1", "T1B", "128 128"
+        0,"128 1", 1, "128 1", 2, "128 128"
         -- </analyze4-1.1>
     })
 
@@ -68,16 +72,16 @@ test:do_execsql_test(
 test:do_test(
     "analyze4-1.2",
     function()
-        return test:execsql([[
-            UPDATE t1 SET b='x' WHERE a%2;
+        return test:execsql(string.format([[
+            UPDATE t1 SET b='x' WHERE a%%2;
 -- pragma vdbe_debug=1;
             ANALYZE;
 -- pragma vdbe_debug=0;
-            SELECT "idx", "stat" FROM "_sql_stat1" WHERE "tbl"='T1' ORDER BY "idx";
-        ]])
+            SELECT "index_id", "stat" FROM "_sql_stat1" WHERE "space_id"=%i ORDER BY "index_id";
+        ]], t1.id))
     end, {
         -- <analyze4-1.2>
-        "T1", "128 1", "T1A", "128 1", "T1B", "128 64"
+        0, "128 1", 1, "128 1", 2, "128 64"
         -- </analyze4-1.2>
     })
 
@@ -85,37 +89,42 @@ test:do_test(
 -- Create a multi-column indices using t1.b and verify that ANALYZE 
 -- processes them correctly.
 --
+
+test:execsql([[
+    -- Tarantool doesn't suppoort ALTER stmt yet.
+    -- UPDATE t1 SET b=NULL;
+    --ALTER TABLE t1 ADD COLUMN c;
+    --ALTER TABLE t1 ADD COLUMN d;
+    -- So, re-create the table and its contents
+    DROP TABLE t1;
+    CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a,b,c DEFAULT NULL,d DEFAULT NULL);
+    CREATE INDEX t1a ON t1(a);
+    CREATE INDEX t1b ON t1(b);
+    INSERT INTO t1 (a,b) VALUES(1,NULL);
+    INSERT INTO t1 (a,b) SELECT a+1, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+2, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+4, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+8, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+16, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+32, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+64, b FROM t1;
+
+    UPDATE t1 SET c=a/4, d=a/2;
+    CREATE INDEX t1bcd ON t1(b,c,d);
+    CREATE INDEX t1cdb ON t1(c,d,b);
+    CREATE INDEX t1cbd ON t1(c,b,d);
+    ANALYZE;
+]])
+
+t1 = box.space.T1
+
 test:do_execsql_test(
     "analyze4-1.3",
-    [[
-            -- Tarantool doesn't suppoort ALTER stmt yet.
-            -- UPDATE t1 SET b=NULL;
-            --ALTER TABLE t1 ADD COLUMN c;
-            --ALTER TABLE t1 ADD COLUMN d;
-            -- So, re-create the table and its contents
-            DROP TABLE t1;
-            CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a,b,c DEFAULT NULL,d DEFAULT NULL);
-            CREATE INDEX t1a ON t1(a);
-            CREATE INDEX t1b ON t1(b);
-            INSERT INTO t1 (a,b) VALUES(1,NULL);
-            INSERT INTO t1 (a,b) SELECT a+1, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+2, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+4, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+8, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+16, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+32, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+64, b FROM t1;
-
-            UPDATE t1 SET c=a/4, d=a/2;
-            CREATE INDEX t1bcd ON t1(b,c,d);
-            CREATE INDEX t1cdb ON t1(c,d,b);
-            CREATE INDEX t1cbd ON t1(c,b,d);
-            ANALYZE;
-            SELECT "idx", "stat" FROM "_sql_stat1" WHERE "tbl"='T1' ORDER BY "idx";
-    ]]
-    , {
+    string.format([[
+            SELECT "index_id", "stat" FROM "_sql_stat1" WHERE "space_id"=%i ORDER BY "index_id";
+    ]], t1.id), {
         -- <analyze4-1.3>
-        "T1","128 1", "T1A", "128 1", "T1B", "128 128", "T1BCD", "128 128 4 2", "T1CBD", "128 4 4 2", "T1CDB", "128 4 2 2"
+        0,"128 1", 1, "128 1", 2, "128 128", 3, "128 128 4 2", 4, "128 4 2 2", 5, "128 4 4 2"
         -- </analyze4-1.3>
     })
 
diff --git a/test/sql-tap/analyze5.test.lua b/test/sql-tap/analyze5.test.lua
index d68bd3c..ece26e4 100755
--- a/test/sql-tap/analyze5.test.lua
+++ b/test/sql-tap/analyze5.test.lua
@@ -114,10 +114,11 @@ test:do_test(
 
         -- DISTINCT idx, sample -- lindex(test_decode(sample),0)
         -- WHERE idx='t1u' ORDER BY nlt;
-        return test:execsql([[ SELECT DISTINCT msgpack_decode("sample")
-                                 FROM "_sql_stat4"
-                                 WHERE "idx"='T1U'
-                                 ORDER BY "nlt"]])
+        return test:execsql(string.format([[
+            SELECT DISTINCT msgpack_decode("sample")
+            FROM "_sql_stat4"
+            WHERE "index_id"= %i
+            ORDER BY "nlt"]], box.space.T1.index['T1U'].id))
     end, {
         -- <analyze5-1.0>
         "alpha", "bravo", "charlie", "delta"
@@ -143,10 +144,10 @@ test:do_test(
 test:do_test(
     "analyze5-1.2",
     function()
-        return test:execsql([[SELECT "idx", count(*) FROM "_sql_stat4" GROUP BY 1 ORDER BY 1]])
+        return test:execsql([[SELECT "index_id", count(*) FROM "_sql_stat4" GROUP BY 1 ORDER BY 1]])
     end, {
         -- <analyze5-1.2>
-        "T1",24,"T1T",4,"T1U",4,"T1V",1,"T1W",4,"T1X",4,"T1Y",2,"T1Z",4
+        0,24,1,4,2,4,3,1,4,4,5,4,6,2,7,4
         -- </analyze5-1.2>
     })
 
diff --git a/test/sql-tap/analyze9.test.lua b/test/sql-tap/analyze9.test.lua
index 05ed501..075cc69 100755
--- a/test/sql-tap/analyze9.test.lua
+++ b/test/sql-tap/analyze9.test.lua
@@ -1,6 +1,6 @@
 #!/usr/bin/env tarantool
 test = require("sqltester")
-test:plan(121)
+test:plan(120)
 
 testprefix = "analyze9"
 
@@ -62,29 +62,29 @@ msgpack_decode_sample = function(txt)
 end
 
 box.internal.sql_create_function("msgpack_decode_sample", msgpack_decode_sample)
+t1 = box.space.T1
 
 test:do_execsql_test(
     1.2,
-    [[
-        SELECT "tbl","idx","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM "_sql_stat4" where "idx" = 'I1';
-    ]], {
+    string.format([[
+        SELECT "index_id","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM "_sql_stat4" where "index_id" = %i;
+    ]], t1.index['I1'].id), {
         -- <1.2>
-        "T1", "I1", "1 1", "0 0", "0 0", "(0) (0)", "T1", "I1", "1 1", "1 1", "1 1", "(1) (1)", 
-        "T1", "I1", "1 1", "2 2", "2 2", "(2) (2)", "T1", "I1", "1 1", "3 3", "3 3", "(3) (3)", 
-        "T1", "I1", "1 1", "4 4", "4 4", "(4) (4)"
+        1, "1 1", "0 0", "0 0", "(0) (0)", 1, "1 1", "1 1", "1 1", "(1) (1)",
+        1, "1 1", "2 2", "2 2", "(2) (2)", 1, "1 1", "3 3", "3 3", "(3) (3)",
+        1, "1 1", "4 4", "4 4", "(4) (4)"
         -- </1.2>
     })
 
 test:do_execsql_test(
     1.3,
-    [[
-        SELECT "tbl","idx","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM "_sql_stat4" where "idx" = 'T1';
-
-    ]], {
+    string.format([[
+        SELECT "index_id","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM "_sql_stat4" where "index_id" = %i;
+    ]], t1.index[0].id), {
         -- <1.3>
-        'T1', 'T1', '1', '0', '0', '(0)', 'T1', 'T1', '1', '1', '1', '(1)', 
-        'T1', 'T1', '1', '2', '2', '(2)', 'T1', 'T1', '1', '3', '3', '(3)', 
-        'T1', 'T1', '1', '4', '4', '(4)'
+        0, '1', '0', '0', '(0)', 0, '1', '1', '1', '(1)',
+        0, '1', '2', '2', '(2)', 0, '1', '3', '3', '(3)',
+        0, '1', '4', '4', '(4)'
         -- </1.3>
     })
 
@@ -101,10 +101,10 @@ test:do_execsql_test(
         INSERT INTO t1 VALUES(22.0, 'some text');
         CREATE INDEX i1 ON t1(a, b);
         ANALYZE;
-        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4";
+        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4" ORDER BY 1;
     ]], {
         -- <2.1>
-        "some text 14", "22 some text", "some text", 22
+        22, "22 some text", "some text", "some text 14"
         -- </2.1>
     })
 
@@ -116,6 +116,7 @@ test:do_execsql_test(
         CREATE TABLE t2(id INTEGER PRIMARY KEY AUTOINCREMENT, a, b);
         CREATE INDEX i2 ON t2(a, b);
     ]])
+t2 = box.space.T2
 
 test:do_test(
     3.2,
@@ -184,10 +185,10 @@ test:do_execsql_test(
 --      
 test:do_execsql_test(
     "3.3.2",
-    [[
+    string.format([[
         ANALYZE;
-        SELECT lrange("neq", 1, 1) FROM "_sql_stat4" WHERE "idx" = 'I2';
-    ]], generate_tens_str(24))
+        SELECT lrange("neq", 1, 1) FROM "_sql_stat4" WHERE "space_id" = %i and "index_id" = %i;
+    ]], t2.id, t2.index['I2'].id) , generate_tens_str(24))
 
 ---------------------------------------------------------------------------
 -- 
@@ -232,6 +233,7 @@ test:do_execsql_test(
         CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a, b, c);
         CREATE INDEX i1 ON t1(c, b, a);
     ]])
+t1 = box.space.T1
 
 insert_filler_rows_n = function(iStart, nCopy, nVal)
     for i = 0, nVal-1 do
@@ -282,10 +284,10 @@ test:do_execsql_test(
 
 test:do_execsql_test(
     4.3,
-    [[
+    string.format([[
         SELECT "neq", lrange("nlt", 1, 3), lrange("ndlt", 1, 3), lrange(msgpack_decode_sample("sample"), 1, 3) 
-            FROM "_sql_stat4" WHERE "idx" = 'I1' ORDER BY "sample" LIMIT 16;
-    ]], {
+            FROM "_sql_stat4" WHERE "space_id" = %i and "index_id" = %i ORDER BY "sample" LIMIT 16;
+    ]], t1.id, t1.index['I1'].id), {
         -- <4.3>
         "10 10 10","0 0 0","0 0 0","0 0 0","10 10 10","10 10 10","1 1 1","1 1 1","10 10 10","20 20 20",
         "2 2 2","2 2 2","10 10 10","30 30 30","3 3 3","3 3 3","10 10 10","40 40 40","4 4 4","4 4 4",
@@ -299,10 +301,10 @@ test:do_execsql_test(
 
 test:do_execsql_test(
     4.4,
-    [[
+    string.format([[
         SELECT "neq", lrange("nlt", 1, 3), lrange("ndlt", 1, 3), lrange(msgpack_decode_sample("sample"), 1, 3) 
-        FROM "_sql_stat4" WHERE "idx" = 'I1' ORDER BY "sample" DESC LIMIT 2;
-    ]], {
+        FROM "_sql_stat4" WHERE "space_id" = %i and "index_id" = %i ORDER BY "sample" DESC LIMIT 2;
+    ]], t1.id, t1.index['I1'].id), {
         -- <4.4>
         "2 1 1","295 296 296","120 122 125","201 4 h","5 3 1","290 290 291","119 119 120","200 1 b"
         -- </4.4>
@@ -364,11 +366,12 @@ test:do_test(
 test:do_execsql_test(
     4.9,
     [[
-        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4";
+        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4" ORDER BY 1;
     ]], {
         -- <4.9>
-        "x", 1110, 2230, 2750, 3350, 4090, 4470, 4980, 5240, 5280, 5290, 5590, 5920, 
-        5930, 6220, 6710, 7000, 7710, 7830, 7970, 8890, 8950, 9240, 9250, 9680
+        1110, 2230, 2750, 3350, 4090, 4470, 4980, 5240, 5280, 5290, 5590, 5920,
+        5930, 6220, 6710, 7000, 7710, 7830, 7970, 8890, 8950, 9240, 9250, 9680,
+        "x"
         -- </4.9>
     })
 
@@ -394,6 +397,8 @@ test:do_execsql_test(
         INSERT INTO "_sql_stat4" SELECT * FROM x1;
         ANALYZE;
     ]])
+t1 = box.space.T1
+x1 = box.space.X1
 
 test:do_execsql_test(
     6.2,
@@ -407,7 +412,7 @@ test:do_execsql_test(
 --
 test:do_execsql_test(
     7.1,
-    [[
+    string.format([[
         DROP TABLE IF EXISTS t1;
         CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a, b);
         CREATE INDEX i1 ON t1(a, b);
@@ -418,9 +423,9 @@ test:do_execsql_test(
         INSERT INTO t1 VALUES(null, 5, 5);
         ANALYZE;
         UPDATE "_sql_stat4" SET "sample" = '' WHERE "sample" =
-            (SELECT "sample" FROM "_sql_stat4" WHERE "tbl" = 't1' AND "idx" = 'i1' LIMIT 1);
+            (SELECT "sample" FROM "_sql_stat4" WHERE "space_id" = %i AND "index_id" = %i LIMIT 1);
         ANALYZE;
-    ]])
+    ]], t1.id, t1.index['I1'].id))
 
 -- Doesn't work due to the fact that in Tarantool rowid has been removed,
 -- and tbl, idx and sample have been united into primary key.
@@ -1014,15 +1019,15 @@ test:do_execsql_test(
 --
 test:do_execsql_test(
     15.1,
-    [[
+    string.format([[
         DROP TABLE IF EXISTS x1;
         CREATE TABLE x1(a PRIMARY KEY, b, UNIQUE(a, b));
         INSERT INTO x1 VALUES(1, 2);
         INSERT INTO x1 VALUES(3, 4);
         INSERT INTO x1 VALUES(5, 6);
         ANALYZE;
-        INSERT INTO "_sql_stat4" VALUES('x1', 'abc', '', '', '', '');
-    ]])
+        INSERT INTO "_sql_stat4" VALUES(%i, 12345, '', '', '', '');
+    ]], x1.id))
 
 test:do_execsql_test(
     15.2,
@@ -1037,7 +1042,7 @@ test:do_execsql_test(
 test:do_execsql_test(
     15.3,
     [[
-        INSERT INTO "_sql_stat4" VALUES('42', '42', '42', '42', '42', 42);
+        INSERT INTO "_sql_stat4" VALUES(42, 42, '42', '42', '42', 42);
     ]])
 
 test:do_execsql_test(
@@ -1054,7 +1059,7 @@ test:do_execsql_test(
     15.7,
     [[
         ANALYZE;
-        UPDATE "_sql_stat1" SET "tbl" = 'no such tbl';
+        UPDATE "_sql_stat1" SET "space_id" = "space_id" + 123456;
     ]])
 
 test:do_execsql_test(
@@ -1067,25 +1072,6 @@ test:do_execsql_test(
         -- </15.8>
     })
 
--- Tarantool: this test seems to be useless. There's no reason
--- for these fields to be nullable.
--- test:do_execsql_test(
---    15.9,
---    [[
---        ANALYZE;
---        UPDATE "_sql_stat4" SET "neq" = NULL, "nlt" = NULL, "ndlt" = NULL;
---    ]])
-
-test:do_execsql_test(
-    15.10,
-    [[
-        SELECT * FROM x1;
-    ]], {
-        -- <15.10>
-        1, 2, 3, 4, 5, 6
-        -- </15.10>
-    })
-
 -- This is just for coverage....
 test:do_execsql_test(
     15.11,
@@ -1135,6 +1121,7 @@ test:do_test(
         -- <17.1>
         -- </17.1>
     })
+t1 = box.space.T1
 
 test:do_execsql_test(
     17.2,
@@ -1197,7 +1184,7 @@ test:do_test(
             test:execsql(string.format("INSERT INTO t1 VALUES(%s, 0);", i))
         end
         test:execsql("ANALYZE")
-        return test:execsql([[ SELECT count(*) FROM "_sql_stat4" WHERE "idx" = 'I1'; ]])
+        return test:execsql(string.format('SELECT count(*) FROM "_sql_stat4" WHERE "space_id" = %i AND "index_id" = %i;', box.space.T1.id, box.space.T1.index['I1'].id))
     end, {
         -- <18.1>
         9
@@ -1239,12 +1226,13 @@ test:do_execsql_test(
         -- </20.2>
     })
 
+t1 = box.space.T1
 for i = 0, 15 do
     test:do_test(
         "20.3."..i,
         function()
             return test:execsql(string.format(
-                [[SELECT count(*) FROM "_sql_stat4" WHERE "idx" = 'I1' AND lrange(msgpack_decode_sample("sample"), 1, 1) = '%s']], i))
+                [[SELECT count(*) FROM "_sql_stat4" WHERE "space_id" = %i AND "index_id" = %i AND lrange(msgpack_decode_sample("sample"), 1, 1) = '%s']], t1.id, t1.index['I1'].id, i))
         end, {
             1
         })
diff --git a/test/sql-tap/analyzeC.test.lua b/test/sql-tap/analyzeC.test.lua
index a3cea70..e772d71 100755
--- a/test/sql-tap/analyzeC.test.lua
+++ b/test/sql-tap/analyzeC.test.lua
@@ -30,9 +30,7 @@ testprefix = "analyzeC"
 -- Baseline case.  Range queries work OK.  Indexes can be used for
 -- ORDER BY.
 
-test:do_execsql_test(
-    1.0,
-    [[
+test:execsql([[
         DROP TABLE IF EXISTS t1;
         CREATE TABLE t1(a PRIMARY KEY, b, c, d);
         INSERT INTO t1(a,b,c,d) VALUES(1,1,2,3),(2,7,8,9),(3,4,5,6),(4,10,11,12),(5,4,8,12),(6,1,11,111);
@@ -40,10 +38,17 @@ test:do_execsql_test(
         CREATE INDEX t1c ON t1(c);
         ANALYZE;
         DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") VALUES('t1','t1b','12345 2'),('t1','t1c','12345 4');
+    ]]
+)
+t1 = box.space.T1
+
+test:do_execsql_test(
+    1.0,
+    string.format([[
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat") VALUES(%i,1,'12345 2'),(%i,2,'12345 4');
         ANALYZE;
         SELECT b,c,d, '#' FROM t1 WHERE b BETWEEN 3 AND 8 ORDER BY d;
-    ]], {
+    ]], t1.id, t1.id), {
         -- <1.0>
         4, 5, 6, "#", 7, 8, 9, "#", 4, 8, 12, "#"
         -- </1.0>
@@ -85,11 +90,12 @@ test:do_execsql_test(
 --
 test:do_execsql_test(
     2.0,
+    string.format(
     [[
-        UPDATE "_sql_stat1" SET "stat"='12345 2 unordered' WHERE "idx"='t1b';
+        UPDATE "_sql_stat1" SET "stat"='12345 2 unordered' WHERE "index_id"=%i;
         ANALYZE;
         SELECT b, c, d, '#' FROM t1 WHERE b BETWEEN 3 AND 8 ORDER BY d;
-    ]], {
+    ]], box.space.T1.index['T1B'].id), {
         -- <2.0>
         4, 5, 6, "#", 7, 8, 9, "#", 4, 8, 12, "#"
         -- </2.0>
@@ -130,11 +136,12 @@ test:do_execsql_test(
 --
 test:do_execsql_test(
     3.0,
+    string.format(
     [[
-        UPDATE "_sql_stat1" SET "stat"='12345 2 whatever=5 unordered xyzzy=11' WHERE "idx"='t1b';
+        UPDATE "_sql_stat1" SET "stat"='12345 2 whatever=5 unordered xyzzy=11' WHERE "index_id"=%i;
         ANALYZE;
         SELECT b, c, d, '#' FROM t1 WHERE b BETWEEN 3 AND 8 ORDER BY d;
-    ]], {
+    ]], box.space.T1.index['T1B'].id), {
         -- <3.0>
         4, 5, 6, "#", 7, 8, 9, "#", 4, 8, 12, "#"
         -- </3.0>
@@ -173,17 +180,22 @@ test:do_execsql_test(
 
 -- The sz=NNN parameter determines which index to scan
 --
-test:do_execsql_test(
-    4.0,
-    [[
+
+test:execsql([[
         DROP INDEX t1b ON t1;
         CREATE INDEX t1bc ON t1(b,c);
         CREATE INDEX t1db ON t1(d,b);
         DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") VALUES('t1','t1bc','12345 3 2 sz=10'),('t1','t1db','12345 3 2 sz=20');
+    ]]
+)
+
+test:do_execsql_test(
+    4.0,
+    string.format([[
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat") VALUES(%i,%i,'12345 3 2 sz=10'),(%i,%i,'12345 3 2 sz=20');
         ANALYZE;
         SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
         -- <4.0>
         6
         -- </4.0>
@@ -201,12 +213,13 @@ test:do_execsql_test(
 
 test:do_execsql_test(
     4.2,
+    string.format(
     [[
         DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") VALUES('t1','t1bc','12345 3 2 sz=20'),('t1','t1db','12345 3 2 sz=10');
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat") VALUES(%i,%i,'12345 3 2 sz=20'),(%i,%i,'12345 3 2 sz=10');
         ANALYZE;
         SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
         -- <4.2>
         6
         -- </4.2>
@@ -227,14 +240,14 @@ test:do_execsql_test(
 --
 test:do_execsql_test(
     5.0,
-    [[
+    string.format([[
         DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat")
-          VALUES('t1','t1bc','12345 3 2 x=5 sz=10 y=10'),
-                ('t1','t1db','12345 3 2 whatever sz=20 junk');
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat")
+          VALUES(%i,%i,'12345 3 2 x=5 sz=10 y=10'),
+                (%i,%i,'12345 3 2 whatever sz=20 junk');
         ANALYZE;
         SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
         -- <5.0>
         6
         -- </5.0>
@@ -253,12 +266,12 @@ test:do_execsql_test(
 
 test:do_execsql_test(
     5.2,
-    [[
+    string.format([[
         DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") VALUES('t1','t1db','12345 3 2 x=5 sz=10 y=10'), ('t1','t1bc','12345 3 2 whatever sz=20 junk');
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat") VALUES(%i,%i,'12345 3 2 x=5 sz=10 y=10'), (%i,%i,'12345 3 2 whatever sz=20 junk');
         ANALYZE;
         SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
         -- <5.2>
         6
         -- </5.2>
diff --git a/test/sql-tap/analyzeD.test.lua b/test/sql-tap/analyzeD.test.lua
index ef6aced..9d89933 100755
--- a/test/sql-tap/analyzeD.test.lua
+++ b/test/sql-tap/analyzeD.test.lua
@@ -153,9 +153,9 @@ test:do_catchsql_test(
 
 test:do_execsql_test(
     "analyzeD-1.10",
-    [[
-        SELECT * FROM "_sql_stat4" WHERE "tbl" = 'v';
-    ]], {
+    string.format([[
+        SELECT * FROM "_sql_stat4" WHERE "space_id" = %i;
+    ]], box.space.V.id), {
         -- <analyzeD-1.10>
         
         -- <analyzeD-1.10>
@@ -163,9 +163,9 @@ test:do_execsql_test(
 
 test:do_execsql_test(
     "analyzeD-1.11",
-    [[
-        SELECT * FROM "_sql_stat1" WHERE "tbl" = 'v';
-    ]], {
+    string.format([[
+        SELECT * FROM "_sql_stat1" WHERE "space_id" = %i;
+    ]], box.space.V.id), {
         -- <analyzeD-1.11>
         
         -- <analyzeD-1.11>
@@ -183,9 +183,9 @@ test:do_catchsql_test(
 
 test:do_execsql_test(
     "analyzeD-1.13",
-    [[
-        SELECT * FROM "_sql_stat4" WHERE "tbl" = 'v';
-    ]], {
+    string.format([[
+        SELECT * FROM "_sql_stat4" WHERE "space_id" = %i;
+    ]], box.space.V.id), {
         -- <analyzeD-1.13>
         
         -- <analyzeD-1.13>
@@ -193,9 +193,9 @@ test:do_execsql_test(
 
 test:do_execsql_test(
     "analyzeD-1.14",
-    [[
-        SELECT * FROM "_sql_stat1" WHERE "tbl" = 'v';
-    ]], {
+    string.format([[
+        SELECT * FROM "_sql_stat1" WHERE "space_id" = %i;
+    ]], box.space.V.id), {
         -- <analyzeD-1.14>
         
         -- <analyzeD-1.14>
diff --git a/test/sql-tap/gh-3350-skip-scan.test.lua b/test/sql-tap/gh-3350-skip-scan.test.lua
index 301b04a..917c74c 100755
--- a/test/sql-tap/gh-3350-skip-scan.test.lua
+++ b/test/sql-tap/gh-3350-skip-scan.test.lua
@@ -77,9 +77,7 @@ test:do_execsql_test(
         }
 )
 
-test:do_execsql_test(
-        "skip-scan-1.4",
-        [[
+test:execsql([[
             DROP TABLE IF EXISTS t1;
             CREATE TABLE t1(id INTEGER PRIMARY KEY, a TEXT, b INT, c INT, d INT);
             CREATE INDEX t1abc ON t1(a,b,c);
@@ -96,10 +94,17 @@ test:do_execsql_test(
             ANALYZE;
             DELETE FROM "_sql_stat1";
             DELETE FROM "_sql_stat4";
-            INSERT INTO "_sql_stat1" VALUES('T1','T1ABC','10000 5000 2000 10');
+        ]]
+)
+
+test:do_execsql_test(
+        "skip-scan-1.4",
+        string.format(
+        [[
+            INSERT INTO "_sql_stat1" VALUES(%i,%i,'10000 5000 2000 10');
             ANALYZE t2;
             SELECT a,b,c,d FROM t1 WHERE b=345;
-        ]], {
+        ]], box.space.T1.id, box.space.T1.index['T1ABC'].id), {
             "abc", 345, 7, 8, "def", 345, 9, 10
         }
 )
diff --git a/test/sql/sql-statN-index-drop.result b/test/sql/sql-statN-index-drop.result
index a751eca..8e8a1b2 100644
--- a/test/sql/sql-statN-index-drop.result
+++ b/test/sql/sql-statN-index-drop.result
@@ -31,36 +31,36 @@ box.sql.execute("ANALYZE;")
 ---
 ...
 -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM "_sql_stat4";')
 ---
-- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
+  - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
 ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')
 ---
-- - ['T1', 'I1', '1 1']
-  - ['T1', 'T1', '1 1']
-  - ['T2', 'I1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - [0, '1 1']
+  - [1, '1 1']
+  - [0, '1 1']
+  - [1, '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\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM "_sql_stat4";')
 ---
-- - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - [0, '1', '0', '0', !!binary kQE=]
+  - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
 ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')
 ---
-- - ['T1', 'T1', '1 1']
-  - ['T2', 'I1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - [0, '1 1']
+  - [0, '1 1']
+  - [1, '1 1']
 ...
 --Cleaning up.
 box.sql.execute("DROP TABLE t1;")
@@ -93,36 +93,36 @@ box.sql.execute("ANALYZE;")
 ---
 ...
 -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM "_sql_stat4";')
 ---
-- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
+  - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
 ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')
 ---
-- - ['T1', 'I1', '1 1']
-  - ['T1', 'T1', '1 1']
-  - ['T2', 'I1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - [0, '1 1']
+  - [1, '1 1']
+  - [0, '1 1']
+  - [1, '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\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM "_sql_stat4";')
 ---
-- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
+  - [0, '1', '0', '0', !!binary kQE=]
 ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')
 ---
-- - ['T1', 'I1', '1 1']
-  - ['T1', 'T1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - [0, '1 1']
+  - [1, '1 1']
+  - [0, '1 1']
 ...
 --Cleaning up.
 box.sql.execute("DROP TABLE t1;")
diff --git a/test/sql/sql-statN-index-drop.test.lua b/test/sql/sql-statN-index-drop.test.lua
index fe7e15b..af13c8c 100644
--- a/test/sql/sql-statN-index-drop.test.lua
+++ b/test/sql/sql-statN-index-drop.test.lua
@@ -14,15 +14,15 @@ box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
 box.sql.execute("ANALYZE;")
 
 -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM "_sql_stat4";')
+box.sql.execute('SELECT "index_id","stat" 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\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM "_sql_stat4";')
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')
 
 --Cleaning up.
 box.sql.execute("DROP TABLE t1;")
@@ -41,15 +41,15 @@ box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
 box.sql.execute("ANALYZE;")
 
 -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM "_sql_stat4";')
+box.sql.execute('SELECT "index_id","stat" 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\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM "_sql_stat4";')
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')
 
 --Cleaning up.
 box.sql.execute("DROP TABLE t1;")
diff --git a/test/sql/upgrade.result b/test/sql/upgrade.result
index 5e7d851..9326209 100644
--- a/test/sql/upgrade.result
+++ b/test/sql/upgrade.result
@@ -30,14 +30,15 @@ box.space._space.index['name']:get('_trigger')
 ...
 box.space._space.index['name']:get('_sql_stat1')
 ---
-- [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'tbl', 'type': 'string'}, {'name': 'idx',
-      'type': 'string'}, {'name': 'stat', 'type': 'string'}]]
+- [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'space_id', 'type': 'unsigned'},
+    {'name': 'index_id', 'type': 'unsigned'}, {'name': 'stat', 'type': 'string'}]]
 ...
 box.space._space.index['name']:get('_sql_stat4')
 ---
-- [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'tbl', 'type': 'string'}, {'name': 'idx',
-      'type': 'string'}, {'name': 'neq', 'type': 'string'}, {'name': 'nlt', 'type': 'string'},
-    {'name': 'ndlt', 'type': 'string'}, {'name': 'sample', 'type': 'scalar'}]]
+- [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'space_id', 'type': 'unsigned'},
+    {'name': 'index_id', 'type': 'unsigned'}, {'name': 'neq', 'type': 'string'}, {
+      'name': 'nlt', 'type': 'string'}, {'name': 'ndlt', 'type': 'string'}, {'name': 'sample',
+      'type': 'scalar'}]]
 ...
 box.space._index:get({box.space._space.index['name']:get('_trigger').id, 0})
 ---
@@ -45,12 +46,12 @@ box.space._index:get({box.space._space.index['name']:get('_trigger').id, 0})
 ...
 box.space._index:get({box.space._space.index['name']:get('_sql_stat1').id, 0})
 ---
-- [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 'string']]]
+- [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 'unsigned']]]
 ...
 box.space._index:get({box.space._space.index['name']:get('_sql_stat4').id, 0})
 ---
-- [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 'string'], [5,
-      'scalar']]]
+- [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 'unsigned'],
+    [5, 'scalar']]]
 ...
 box.space._schema:format()
 ---

[-- Attachment #2: Type: text/html, Size: 252970 bytes --]

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

* [tarantool-patches] Re: [PATCH v2 1/1] sql: hold in stat tables space/index id instead of name
  2018-10-02 16:42       ` [tarantool-patches] Re[2]: [tarantool-patches] " Мерген Имеев
@ 2018-10-09 12:36         ` n.pettik
  2018-10-10 17:17           ` Imeev Mergen
  0 siblings, 1 reply; 17+ messages in thread
From: n.pettik @ 2018-10-09 12:36 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen

Please, next time attach intermediate diff (i.e. diff between two versions) or
inline each hunk as answer to comment. It is quite complicated to review
full patch (especially when it comes to patches containing hundreds of lines)
each time.

https://travis-ci.org/tarantool/tarantool/jobs/436232731

box/sql.test.lua fails on Travis. Please, fix it.

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

* [tarantool-patches] Re: [PATCH v2 1/1] sql: hold in stat tables space/index id instead of name
  2018-10-09 12:36         ` [tarantool-patches] " n.pettik
@ 2018-10-10 17:17           ` Imeev Mergen
  2018-10-11 11:41             ` n.pettik
  0 siblings, 1 reply; 17+ messages in thread
From: Imeev Mergen @ 2018-10-10 17:17 UTC (permalink / raw)
  To: n.pettik, tarantool-patches

[-- Attachment #1: Type: text/plain, Size: 84400 bytes --]

Hello! Thank you for review! New patch and diff

between two last versions below.


On 10/09/2018 03:36 PM, n.pettik wrote:
> Please, next time attach intermediate diff (i.e. diff between two versions) or
> inline each hunk as answer to comment. It is quite complicated to review
> full patch (especially when it comes to patches containing hundreds of lines)
> each time.
>
> https://travis-ci.org/tarantool/tarantool/jobs/436232731
>
> box/sql.test.lua fails on Travis. Please, fix it.
>
Now it is good on travis:
https://travis-ci.org/tarantool/tarantool/builds/439704198*

Diff between two last versions:*

diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index 95c516a..a886d8a 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -797,13 +797,13 @@ vdbe_emit_analyze_space(struct Parse *parse, 
struct space *space)
      assert(space->index_count != 0);
      struct Vdbe *v = sqlite3GetVdbe(parse);
      assert(v != NULL);
-    const char *tab_name = space_name(space);
+    MAYBE_UNUSED const char *tab_name = space_name(space);
      sqlite3VdbeAddOp4(v, OP_IteratorOpen, tab_cursor, 0, 0, (void *) 
space,
                P4_SPACEPTR);
      sqlite3VdbeAddOp2(v, OP_Integer, space->def->id, space_id_reg);
      for (uint32_t j = 0; j < space->index_count; ++j) {
          struct index *idx = space->index[j];
-        const char *idx_name;
+        MAYBE_UNUSED const char *idx_name;
          /*
           * Primary indexes feature automatically generated
           * names. Thus, for the sake of clarity, use


*New patch:*

commit d841ade371df0381af64eeaec84555eb932d4a33
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Fri Aug 31 12:37:48 2018 +0300

     sql: hold in stat tables space/index id instead of name

     To avoid problems with table and index renaming it is good idea
     to save ids of tables and indexes instead of their names. Ids of
     tables and indexes are fixed values.

     Closes #3242
     Closes #2962

diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap
index 6573938..a30f72e 100644
Binary files a/src/box/bootstrap.snap and b/src/box/bootstrap.snap differ
diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
index d9c2ae4..bc18b49 100644
--- a/src/box/lua/upgrade.lua
+++ b/src/box/lua/upgrade.lua
@@ -520,11 +520,11 @@ local function upgrade_to_2_1_0()
      _index:insert{_trigger.id, 1, 'space_id', 'tree', { unique = false },
                    {{1, 'unsigned'}}}

-    local stat1_ft = {{name='tbl', type='string'},
-                      {name='idx', type='string'},
+    local stat1_ft = {{name='space_id', type='unsigned'},
+                      {name='index_id', type='unsigned'},
                        {name='stat', type='string'}}
-    local stat4_ft = {{name='tbl', type='string'},
-                      {name='idx', type='string'},
+    local stat4_ft = {{name='space_id', type='unsigned'},
+                      {name='index_id', type='unsigned'},
                        {name='neq', type='string'},
                        {name='nlt', type='string'},
                        {name='ndlt', type='string'},
@@ -536,7 +536,7 @@ local function upgrade_to_2_1_0()

      log.info("create index primary on _sql_stat1")
      _index:insert{box.schema.SQL_STAT1_ID, 0, 'primary', 'tree',
-                  {unique = true}, {{0, 'string'}, {1, 'string'}}}
+                  {unique = true}, {{0, 'unsigned'}, {1, 'unsigned'}}}

      log.info("create space _sql_stat4")
      _space:insert{box.schema.SQL_STAT4_ID, ADMIN, '_sql_stat4', 
'memtx', 0,
@@ -544,7 +544,7 @@ local function upgrade_to_2_1_0()

      log.info("create index primary on _sql_stat4")
      _index:insert{box.schema.SQL_STAT4_ID, 0, 'primary', 'tree',
-                  {unique = true}, {{0, 'string'}, {1, 'string'},
+                  {unique = true}, {{0, 'unsigned'}, {1, 'unsigned'},
                                      {5, 'scalar'}}}

      local fk_constr_ft = {{name='name', type='string'},
diff --git a/src/box/schema.cc b/src/box/schema.cc
index 2d2be26..e2c59fb 100644
--- a/src/box/schema.cc
+++ b/src/box/schema.cc
@@ -380,17 +380,17 @@ schema_init()
               &alter_space_on_replace_index, &on_stmt_begin_index);

      /* _sql_stat1 - a simpler statistics on space, seen in SQL. */
-    key_parts[0].fieldno = 0; /* space name */
-    key_parts[0].type = FIELD_TYPE_STRING;
-    key_parts[1].fieldno = 1; /* index name */
-    key_parts[1].type = FIELD_TYPE_STRING;
+    key_parts[0].fieldno = 0; /* space id */
+    key_parts[0].type = FIELD_TYPE_UNSIGNED;
+    key_parts[1].fieldno = 1; /* index id */
+    key_parts[1].type = FIELD_TYPE_UNSIGNED;
      sc_space_new(BOX_SQL_STAT1_ID, "_sql_stat1", key_parts, 2, NULL, 
NULL);

      /* _sql_stat4 - extensive statistics on space, seen in SQL. */
-    key_parts[0].fieldno = 0; /* space name */
-    key_parts[0].type = FIELD_TYPE_STRING;
-    key_parts[1].fieldno = 1; /* index name */
-    key_parts[1].type = FIELD_TYPE_STRING;
+    key_parts[0].fieldno = 0; /* space id */
+    key_parts[0].type = FIELD_TYPE_UNSIGNED;
+    key_parts[1].fieldno = 1; /* index id */
+    key_parts[1].type = FIELD_TYPE_UNSIGNED;
      key_parts[2].fieldno = 5; /* sample */
      key_parts[2].type = FIELD_TYPE_SCALAR;
      sc_space_new(BOX_SQL_STAT4_ID, "_sql_stat4", key_parts, 3, NULL, 
NULL);
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index 674d53d..a886d8a 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -38,8 +38,8 @@
   *
   * The following system tables are or have been supported:
   *
- *    CREATE TABLE _sql_stat1(tbl, idx, stat);
- *    CREATE TABLE _sql_stat4(tbl, idx, nEq, nLt, nDLt, sample);
+ *    CREATE TABLE _sql_stat1(space_id, index_id, stat);
+ *    CREATE TABLE _sql_stat4(space_id, index_id, nEq, nLt, nDLt, sample);
   *
   * For most applications, _sql_stat1 provides all the statistics required
   * for the query planner to make good choices.
@@ -47,7 +47,7 @@
   * Format of _sql_stat1:
   *
   * There is normally one row per index, with the index identified by the
- * name in the idx column.  The tbl column is the name of the table to
+ * id in the index_id column.  The space_id column is the id of the 
space to
   * which the index belongs.  In each such row, the stat column will be
   * a string consisting of a list of integers.  The first integer in this
   * list is the number of rows in the index.  (This is the same as the
@@ -66,9 +66,9 @@
   * "unordered" keyword is present, then the query planner assumes that
   * the index is unordered and will not use the index for a range query.
   *
- * If the _sql_stat1.idx column is NULL, then the _sql_stat1.stat
+ * If the _sql_stat1.index_id column is NULL, then the _sql_stat1.stat
   * column contains a single integer which is the (estimated) number of
- * rows in the table identified by _sql_stat1.tbl.
+ * rows in the table identified by _sql_stat1.space_id.
   *
   * Format for _sql_stat4:
   *
@@ -78,9 +78,9 @@
   * queries.
   *
   * The _sql_stat4 table contains multiple entries for each index.
- * The idx column names the index and the tbl column is the table of the
- * index.  If the idx and tbl columns are the same, then the sample is
- * of the INTEGER PRIMARY KEY.  The sample column is a blob which is the
+ * The index_id column names the index and the space_id column is the 
space of
+ * the index. If the index_id is equal to 0, then the sample
+ * is of the INTEGER PRIMARY KEY.  The sample column is a blob which is the
   * binary encoding of a key from the index.  The nEq column is a
   * list of integers.  The first integer is the approximate number
   * of entries in the index whose left-most column exactly matches
@@ -122,10 +122,11 @@
   * created.
   *
   * @param parse Parsing context.
- * @param table_name Delete records of this table if specified.
+ * @param space_id Delete records of this table if id is not
+ *        BOX_ID_NIL.
   */
  static void
-vdbe_emit_stat_space_open(struct Parse *parse, const char *table_name)
+vdbe_emit_stat_space_open(struct Parse *parse, uint32_t space_id)
  {
      const char *stat_names[] = {"_sql_stat1", "_sql_stat4"};
      const uint32_t stat_ids[] = {BOX_SQL_STAT1_ID, BOX_SQL_STAT4_ID};
@@ -133,10 +134,9 @@ vdbe_emit_stat_space_open(struct Parse *parse, 
const char *table_name)
      assert(v != NULL);
      assert(sqlite3VdbeDb(v) == parse->db);
      for (uint i = 0; i < lengthof(stat_names); ++i) {
-        const char *space_name = stat_names[i];
-        if (table_name != NULL) {
-            vdbe_emit_stat_space_clear(parse, space_name, NULL,
-                           table_name);
+        if (space_id != BOX_ID_NIL) {
+            vdbe_emit_stat_space_clear(parse, stat_names[i],
+                           space_id, BOX_ID_NIL);
          } else {
              sqlite3VdbeAddOp1(v, OP_Clear, stat_ids[i]);
          }
@@ -777,10 +777,10 @@ vdbe_emit_analyze_space(struct Parse *parse, 
struct space *space)
      int key_reg = ++parse->nMem;
      /* Temporary use register. */
      int tmp_reg = ++parse->nMem;
-    /* Register containing table name. */
-    int tab_name_reg = ++parse->nMem;
-    /* Register containing index name. */
-    int idx_name_reg = ++parse->nMem;
+    /* Register containing space id. */
+    int space_id_reg = ++parse->nMem;
+    /* Register containing index id. */
+    int index_id_reg = ++parse->nMem;
      /* Value for the stat column of _sql_stat1. */
      int stat1_reg = ++parse->nMem;
      /* MUST BE LAST (see below). */
@@ -797,13 +797,13 @@ vdbe_emit_analyze_space(struct Parse *parse, 
struct space *space)
      assert(space->index_count != 0);
      struct Vdbe *v = sqlite3GetVdbe(parse);
      assert(v != NULL);
-    const char *tab_name = space_name(space);
+    MAYBE_UNUSED const char *tab_name = space_name(space);
      sqlite3VdbeAddOp4(v, OP_IteratorOpen, tab_cursor, 0, 0, (void *) 
space,
                P4_SPACEPTR);
-    sqlite3VdbeLoadString(v, tab_name_reg, space->def->name);
+    sqlite3VdbeAddOp2(v, OP_Integer, space->def->id, space_id_reg);
      for (uint32_t j = 0; j < space->index_count; ++j) {
          struct index *idx = space->index[j];
-        const char *idx_name;
+        MAYBE_UNUSED const char *idx_name;
          /*
           * Primary indexes feature automatically generated
           * names. Thus, for the sake of clarity, use
@@ -814,8 +814,8 @@ vdbe_emit_analyze_space(struct Parse *parse, struct 
space *space)
          else
              idx_name = idx->def->name;
          int part_count = idx->def->key_def->part_count;
-        /* Populate the register containing the index name. */
-        sqlite3VdbeLoadString(v, idx_name_reg, idx_name);
+        /* Populate the register containing the index id. */
+        sqlite3VdbeAddOp2(v, OP_Integer, idx->def->iid, index_id_reg);
          VdbeComment((v, "Analysis for %s.%s", tab_name, idx_name));
          /*
           * Pseudo-code for loop that calls stat_push():
@@ -990,9 +990,8 @@ vdbe_emit_analyze_space(struct Parse *parse, struct 
space *space)
          sqlite3VdbeAddOp2(v, OP_Next, idx_cursor, next_row_addr);
          /* Add the entry to the stat1 table. */
          callStatGet(v, stat4_reg, STAT_GET_STAT1, stat1_reg);
-        assert("BBB"[0] == AFFINITY_TEXT);
-        sqlite3VdbeAddOp4(v, OP_MakeRecord, tab_name_reg, 3, tmp_reg,
-                  "BBB", 0);
+        sqlite3VdbeAddOp4(v, OP_MakeRecord, space_id_reg, 3, tmp_reg,
+                  "DDB", 0);
          sqlite3VdbeAddOp4(v, OP_IdxInsert, tmp_reg, 0, 0,
                    (char *)stat1, P4_SPACEPTR);
          /* Add the entries to the stat4 table. */
@@ -1026,7 +1025,7 @@ vdbe_emit_analyze_space(struct Parse *parse, 
struct space *space)
          }
          sqlite3VdbeAddOp3(v, OP_MakeRecord, col_reg, part_count,
                    sample_reg);
-        sqlite3VdbeAddOp3(v, OP_MakeRecord, tab_name_reg, 6, tmp_reg);
+        sqlite3VdbeAddOp3(v, OP_MakeRecord, space_id_reg, 6, tmp_reg);
          sqlite3VdbeAddOp4(v, OP_IdxReplace, tmp_reg, 0, 0,
                    (char *)stat4, P4_SPACEPTR);
          /* P1==1 for end-of-loop. */
@@ -1067,7 +1066,7 @@ static void
  sql_analyze_database(struct Parse *parser)
  {
      sql_set_multi_write(parser, false);
-    vdbe_emit_stat_space_open(parser, NULL);
+    vdbe_emit_stat_space_open(parser, BOX_ID_NIL);
      space_foreach(sql_space_foreach_analyze, (void *)parser);
      loadAnalysis(parser);
  }
@@ -1088,7 +1087,7 @@ vdbe_emit_analyze_table(struct Parse *parse, 
struct space *space)
       * There are two system spaces for statistics: _sql_stat1
       * and _sql_stat4.
       */
-    vdbe_emit_stat_space_open(parse, space->def->name);
+    vdbe_emit_stat_space_open(parse, space->def->id);
      vdbe_emit_analyze_space(parse, space);
      loadAnalysis(parse);
  }
@@ -1224,24 +1223,14 @@ analysis_loader(void *data, int argc, char 
**argv, char **unused)
      struct analysis_index_info *info = (struct analysis_index_info *) 
data;
      assert(info->stats != NULL);
      struct index_stat *stat = &info->stats[info->index_count++];
-    uint32_t space_id = box_space_id_by_name(argv[0], strlen(argv[0]));
-    if (space_id == BOX_ID_NIL)
+    uint32_t space_id = atoll(argv[0]);
+    if (space_id == 0)
          return -1;
      struct space *space = space_by_id(space_id);
      assert(space != NULL);
      struct index *index;
-    uint32_t iid = box_index_id_by_name(space_id, argv[1], 
strlen(argv[1]));
-    /*
-     * Convention is if index's name matches with space's
-     * one, then it is primary index.
-     */
-    if (iid != BOX_ID_NIL) {
-        index = space_index(space, iid);
-    } else {
-        if (sqlite3_stricmp(argv[0], argv[1]) != 0)
-            return -1;
-        index = space_index(space, 0);
-    }
+    uint32_t iid = atoll(argv[1]);
+    index = space_index(space, iid);
      assert(index != NULL);
      /*
       * Additional field is used to describe total
@@ -1355,8 +1344,12 @@ sample_compare(const void *a, const void *b, void 
*arg)
   * Arguments must point to SQL statements that return
   * data equivalent to the following:
   *
- *    prepare: SELECT tbl,idx,count(*) FROM _sql_stat4 GROUP BY tbl,idx;
- *    load: SELECT tbl,idx,neq,nlt,ndlt,sample FROM _sql_stat4;
+ * prepare:
+ *     SELECT space_id,index_id,count(*) FROM _sql_stat4
+ *     GROUP BY space_id,index_id;
+ * load:
+ *     SELECT space_id,index_id,neq,nlt,ndlt,sample
+ *     FROM _sql_stat4;
   *
   * 'prepare' statement is used to allocate enough memory for
   * statistics (i.e. arrays lt, dt, dlt and avg_eq). 'load' query
@@ -1388,27 +1381,14 @@ load_stat_from_space(struct sqlite3 *db, const 
char *sql_select_prepare,
          goto finalize;
      uint32_t current_idx_count = 0;
      while (sqlite3_step(stmt) == SQLITE_ROW) {
-        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
-        if (space_name == NULL)
-            continue;
-        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
-        if (index_name == NULL)
-            continue;
-        uint32_t sample_count = sqlite3_column_int(stmt, 2);
-        uint32_t space_id = box_space_id_by_name(space_name,
-                             strlen(space_name));
-        assert(space_id != BOX_ID_NIL);
+        uint32_t space_id = sqlite3_column_int(stmt, 0);
+        assert(space_id != 0);
          struct space *space = space_by_id(space_id);
          assert(space != NULL);
-        struct index *index;
-        uint32_t iid = box_index_id_by_name(space_id, index_name,
-                            strlen(index_name));
-        if (sqlite3_stricmp(space_name, index_name) == 0 &&
-            iid == BOX_ID_NIL)
-            index = space_index(space, 0);
-        else
-            index = space_index(space, iid);
+        uint32_t iid = sqlite3_column_int(stmt, 1);
+        struct index *index = space_index(space, iid);
          assert(index != NULL);
+        uint32_t sample_count = sqlite3_column_int(stmt, 2);
          uint32_t column_count = index->def->key_def->part_count;
          struct index_stat *stat = &stats[current_idx_count];
          stat->sample_field_count = column_count;
@@ -1460,27 +1440,12 @@ load_stat_from_space(struct sqlite3 *db, const 
char *sql_select_prepare,
      struct index *prev_index = NULL;
      current_idx_count = 0;
      while (sqlite3_step(stmt) == SQLITE_ROW) {
-        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
-        if (space_name == NULL)
-            continue;
-        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
-        if (index_name == NULL)
-            continue;
-        uint32_t space_id = box_space_id_by_name(space_name,
-                             strlen(space_name));
-        assert(space_id != BOX_ID_NIL);
+        uint32_t space_id = sqlite3_column_int(stmt, 0);
+        assert(space_id != 0);
          struct space *space = space_by_id(space_id);
          assert(space != NULL);
-        struct index *index;
-        uint32_t iid = box_index_id_by_name(space_id, index_name,
-                            strlen(index_name));
-        if (iid != BOX_ID_NIL) {
-            index = space_index(space, iid);
-        } else {
-            if (sqlite3_stricmp(space_name, index_name) != 0)
-                return -1;
-            index = space_index(space, 0);
-        }
+        uint32_t iid = sqlite3_column_int(stmt, 1);
+        struct index *index = space_index(space, iid);
          assert(index != NULL);
          uint32_t column_count = index->def->key_def->part_count;
          if (index != prev_index) {
@@ -1544,28 +1509,13 @@ load_stat_to_index(struct sqlite3 *db, const 
char *sql_select_load,
          return -1;
      uint32_t current_idx_count = 0;
      while (sqlite3_step(stmt) == SQLITE_ROW) {
-        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
-        if (space_name == NULL)
-            continue;
-        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
-        if (index_name == NULL)
-            continue;
-        uint32_t space_id = box_space_id_by_name(space_name,
-                             strlen(space_name));
-        if (space_id == BOX_ID_NIL)
+        uint32_t space_id = sqlite3_column_int(stmt, 0);
+        if (space_id == 0)
              return -1;
          struct space *space = space_by_id(space_id);
          assert(space != NULL);
-        struct index *index;
-        uint32_t iid = box_index_id_by_name(space_id, index_name,
-                            strlen(index_name));
-        if (iid != BOX_ID_NIL) {
-            index = space_index(space, iid);
-        } else {
-            if (sqlite3_stricmp(space_name, index_name) != 0)
-                return -1;
-            index = space_index(space, 0);
-        }
+        uint32_t iid = sqlite3_column_int(stmt, 1);
+        struct index *index = space_index(space, iid);
          assert(index != NULL);
          free(index->def->opts.stat);
          index->def->opts.stat = stats[current_idx_count++];
@@ -1708,7 +1658,7 @@ sql_analysis_load(struct sqlite3 *db)
      info.stats = stats;
      info.index_count = 0;
      const char *load_stat1 =
-        "SELECT \"tbl\",\"idx\",\"stat\" FROM \"_sql_stat1\"";
+        "SELECT \"space_id\",\"index_id\",\"stat\" FROM \"_sql_stat1\"";
      /* Load new statistics out of the _sql_stat1 table. */
      if (sqlite3_exec(db, load_stat1, analysis_loader, &info, 0) != 0)
          goto fail;
@@ -1721,10 +1671,10 @@ sql_analysis_load(struct sqlite3 *db)
       * statistics. Result rows are given in a form:
       * <table name>, <index name>, <count of samples>
       */
-    const char *init_query = "SELECT \"tbl\",\"idx\",count(*) FROM "
-                 "\"_sql_stat4\" GROUP BY \"tbl\",\"idx\"";
+    const char *init_query = "SELECT \"space_id\",\"index_id\",count(*) 
FROM "
+                 "\"_sql_stat4\" GROUP BY \"space_id\",\"index_id\"";
      /* Query for loading statistics into in-memory structs. */
-    const char *load_query = "SELECT \"tbl\",\"idx\",\"neq\",\"nlt\","
+    const char *load_query = "SELECT 
\"space_id\",\"index_id\",\"neq\",\"nlt\","
                   "\"ndlt\",\"sample\" FROM \"_sql_stat4\"";
      /* Load the statistics from the _sql_stat4 table. */
      if (load_stat_from_space(db, init_query, load_query, stats) != 0)
@@ -1768,8 +1718,8 @@ sql_analysis_load(struct sqlite3 *db)
       * Ordered query is needed to be sure that indexes come
       * in the same order as in previous SELECTs.
       */
-    const char *order_query = "SELECT \"tbl\",\"idx\" FROM "
-                  "\"_sql_stat4\" GROUP BY \"tbl\",\"idx\"";
+    const char *order_query = "SELECT \"space_id\",\"index_id\" FROM "
+                  "\"_sql_stat4\" GROUP BY \"space_id\",\"index_id\"";
      if (load_stat_to_index(db, order_query, heap_stats) != 0)
          goto fail;
      if (box_txn_commit() != 0)
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index a806fb4..8f21c55 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -1629,75 +1629,43 @@ sql_store_select(struct Parse *parse_context, 
struct Select *select)
      parse_context->parsed_ast.select = select_copy;
  }

-/**
- * Create expression record "@col_name = '@col_value'".
- *
- * @param parse The parsing context.
- * @param col_name Name of column.
- * @param col_value Name of row.
- * @retval not NULL on success.
- * @retval NULL on failure.
- */
-static struct Expr *
-sql_id_eq_str_expr(struct Parse *parse, const char *col_name,
-           const char *col_value)
-{
-    struct sqlite3 *db = parse->db;
-
-    struct Expr *col_name_expr = sqlite3Expr(db, TK_ID, col_name);
-    if (col_name_expr == NULL)
-        return NULL;
-    struct Expr *col_value_expr = sqlite3Expr(db, TK_STRING, col_value);
-    if (col_value_expr == NULL) {
-        sql_expr_delete(db, col_name_expr, false);
-        return NULL;
-    }
-    return sqlite3PExpr(parse, TK_EQ, col_name_expr, col_value_expr);
-}
-
  void
  vdbe_emit_stat_space_clear(struct Parse *parse, const char 
*stat_table_name,
-               const char *idx_name, const char *table_name)
+               uint32_t space_id, uint32_t index_id)
  {
-    assert(idx_name != NULL || table_name != NULL);
      struct sqlite3 *db = parse->db;
      assert(!db->mallocFailed);
      struct SrcList *src_list = sql_alloc_src_list(db);
      if (src_list != NULL)
          src_list->a[0].zName = sqlite3DbStrDup(db, stat_table_name);
-    struct Expr *where = NULL;
-    if (idx_name != NULL) {
-        struct Expr *expr = sql_id_eq_str_expr(parse, "idx", idx_name);
-        if (expr != NULL)
-            where = sqlite3ExprAnd(db, expr, where);
-    }
-    if (table_name != NULL) {
-        struct Expr *expr = sql_id_eq_str_expr(parse, "tbl", table_name);
-        if (expr != NULL)
-            where = sqlite3ExprAnd(db, expr, where);
-    }
-    /**
-     * On memory allocation error sql_table delete_from
-     * releases memory for its own.
-     */
-    sql_table_delete_from(parse, src_list, where);
+    struct Expr *expr = NULL;
+    struct Expr *col = sqlite3Expr(db, TK_ID, "space_id");
+    struct Expr *val = sqlite3ExprInteger(db, space_id);
+    if (col != NULL && val != NULL)
+        expr = sqlite3PExpr(parse, TK_EQ, col, val);
+    if (index_id != BOX_ID_NIL && expr != NULL) {
+        col = sqlite3Expr(db, TK_ID, "index_id");
+        val = sqlite3ExprInteger(db, index_id);
+        expr = sqlite3ExprAnd(db, sqlite3PExpr(parse, TK_EQ, col, val),
+                      expr);
+    }
+    sql_table_delete_from(parse, src_list, expr);
  }

  /**
   * Remove entries from the _sql_stat1 and _sql_stat4
   * system spaces after a DROP INDEX or DROP TABLE command.
   *
- * @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.
+ * @param parse The parsing context.
+ * @param space_id Id of table to be dropped or table that
+ *        contains index to be dropped.
+ * @param index_id Id of index to be dropped.
   */
  static void
-sql_clear_stat_spaces(struct Parse *parse, const char *table_name,
-              const char *idx_name)
+sql_clear_stat_spaces(struct Parse *parse, uint32_t space_id, uint32_t 
index_id)
  {
-    vdbe_emit_stat_space_clear(parse, "_sql_stat4", idx_name, table_name);
-    vdbe_emit_stat_space_clear(parse, "_sql_stat1", idx_name, table_name);
+    vdbe_emit_stat_space_clear(parse, "_sql_stat4", space_id, index_id);
+    vdbe_emit_stat_space_clear(parse, "_sql_stat1", space_id, index_id);
  }

  /**
@@ -1915,7 +1883,7 @@ sql_drop_table(struct Parse *parse_context, struct 
SrcList *table_name_list,
              goto exit_drop_table;
          }
      }
-    sql_clear_stat_spaces(parse_context, space_name, NULL);
+    sql_clear_stat_spaces(parse_context, space->def->id, BOX_ID_NIL);
      sql_code_drop_table(parse_context, space, is_view);

   exit_drop_table:
@@ -2776,7 +2744,8 @@ sql_drop_index(struct Parse *parse_context, struct 
SrcList *index_name_list,
       * But firstly, delete statistics since schema
       * changes after DDL.
       */
-    sql_clear_stat_spaces(parse_context, table_name, index->def->name);
+    sql_clear_stat_spaces(parse_context, space->def->id, index->def->iid);
+
      int record_reg = ++parse_context->nMem;
      int space_id_reg = ++parse_context->nMem;
      sqlite3VdbeAddOp2(v, OP_Integer, space_id, space_id_reg);
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 744b660..dc83f1b 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -4843,11 +4843,13 @@ vdbe_emit_halt_with_presence_test(struct Parse 
*parser, int space_id,
   *
   * @param parse The parsing context.
   * @param stat_table_name System stat table name.
- * @param idx_name Index name.
- * @param table_name Table name.
+ * @param table_id Id of table of which analysis will be deleted.
+ * @param index_id Id of index of which analysis will be deleted.
+ *        If index_id is BOX_ID_NIL than analysis of all indexes
+ *        of selected table will be deleted.
   */
  void
  vdbe_emit_stat_space_clear(struct Parse *parse, const char 
*stat_table_name,
-               const char *idx_name, const char *table_name);
+               uint32_t table_id, uint32_t index_id);

  #endif                /* SQLITEINT_H */
diff --git a/test/box-py/bootstrap.result b/test/box-py/bootstrap.result
index 506aca3..b5850c2 100644
--- a/test/box-py/bootstrap.result
+++ b/test/box-py/bootstrap.result
@@ -74,11 +74,12 @@ box.space._space:select{}
          'type': 'unsigned'}]]
    - [340, 1, '_space_sequence', 'memtx', 0, {}, [{'name': 'id', 
'type': 'unsigned'},
        {'name': 'sequence_id', 'type': 'unsigned'}, {'name': 
'is_generated', 'type': 'boolean'}]]
-  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'stat', 'type': 'string'}]]
-  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'neq', 'type': 'string'}, {'name': 
'nlt', 'type': 'string'},
-      {'name': 'ndlt', 'type': 'string'}, {'name': 'sample', 'type': 
'scalar'}]]
+  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'space_id', 
'type': 'unsigned'},
+      {'name': 'index_id', 'type': 'unsigned'}, {'name': 'stat', 
'type': 'string'}]]
+  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'space_id', 
'type': 'unsigned'},
+      {'name': 'index_id', 'type': 'unsigned'}, {'name': 'neq', 'type': 
'string'},
+      {'name': 'nlt', 'type': 'string'}, {'name': 'ndlt', 'type': 
'string'}, {'name': 'sample',
+        'type': 'scalar'}]]
    - [356, 1, '_fk_constraint', 'memtx', 0, {}, [{'name': 'name', 
'type': 'string'},
        {'name': 'child_id', 'type': 'unsigned'}, {'name': 'parent_id', 
'type': 'unsigned'},
        {'name': 'is_deferred', 'type': 'boolean'}, {'name': 'match', 
'type': 'string'},
@@ -134,9 +135,9 @@ box.space._index:select{}
    - [330, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
    - [340, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
    - [340, 1, 'sequence', 'tree', {'unique': false}, [[1, 'unsigned']]]
-  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string']]]
-  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string'], [
-        5, 'scalar']]]
+  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned']]]
+  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned'],
+      [5, 'scalar']]]
    - [356, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'unsigned']]]
    - [356, 1, 'child_id', 'tree', {'unique': false}, [[1, 'unsigned']]]
  ...
diff --git a/test/box/access_misc.result b/test/box/access_misc.result
index 4ffeb38..83e9d3e 100644
--- a/test/box/access_misc.result
+++ b/test/box/access_misc.result
@@ -813,11 +813,12 @@ box.space._space:select()
          'type': 'unsigned'}]]
    - [340, 1, '_space_sequence', 'memtx', 0, {}, [{'name': 'id', 
'type': 'unsigned'},
        {'name': 'sequence_id', 'type': 'unsigned'}, {'name': 
'is_generated', 'type': 'boolean'}]]
-  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'stat', 'type': 'string'}]]
-  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'neq', 'type': 'string'}, {'name': 
'nlt', 'type': 'string'},
-      {'name': 'ndlt', 'type': 'string'}, {'name': 'sample', 'type': 
'scalar'}]]
+  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'space_id', 
'type': 'unsigned'},
+      {'name': 'index_id', 'type': 'unsigned'}, {'name': 'stat', 
'type': 'string'}]]
+  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'space_id', 
'type': 'unsigned'},
+      {'name': 'index_id', 'type': 'unsigned'}, {'name': 'neq', 'type': 
'string'},
+      {'name': 'nlt', 'type': 'string'}, {'name': 'ndlt', 'type': 
'string'}, {'name': 'sample',
+        'type': 'scalar'}]]
    - [356, 1, '_fk_constraint', 'memtx', 0, {}, [{'name': 'name', 
'type': 'string'},
        {'name': 'child_id', 'type': 'unsigned'}, {'name': 'parent_id', 
'type': 'unsigned'},
        {'name': 'is_deferred', 'type': 'boolean'}, {'name': 'match', 
'type': 'string'},
diff --git a/test/box/alter.result b/test/box/alter.result
index 9a1086e..86e8c3a 100644
--- a/test/box/alter.result
+++ b/test/box/alter.result
@@ -228,9 +228,9 @@ _index:select{}
    - [330, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
    - [340, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
    - [340, 1, 'sequence', 'tree', {'unique': false}, [[1, 'unsigned']]]
-  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string']]]
-  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string'], [
-        5, 'scalar']]]
+  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned']]]
+  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned'],
+      [5, 'scalar']]]
    - [356, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'unsigned']]]
    - [356, 1, 'child_id', 'tree', {'unique': false}, [[1, 'unsigned']]]
  ...
diff --git a/test/sql-tap/analyze1.test.lua b/test/sql-tap/analyze1.test.lua
index 2d8eed9..df433de 100755
--- a/test/sql-tap/analyze1.test.lua
+++ b/test/sql-tap/analyze1.test.lua
@@ -65,7 +65,7 @@ test:do_execsql_test(
  test:do_execsql_test(
      "analyze-1.7",
      [[
-        SELECT * FROM "_sql_stat1" WHERE "idx" IS NOT NULL
+        SELECT * FROM "_sql_stat1" WHERE "index_id" IS NOT NULL
      ]], {
          -- <analyze-1.7>
          -- </analyze-1.7>
@@ -84,7 +84,7 @@ test:do_catchsql_test(
  test:do_execsql_test(
      "analyze-1.9",
      [[
-        SELECT * FROM "_sql_stat1" WHERE "idx" IS NOT NULL
+        SELECT * FROM "_sql_stat1" WHERE "index_id" IS NOT NULL
      ]], {
          -- <analyze-1.9>
          -- </analyze-1.9>
@@ -119,7 +119,7 @@ test:do_execsql_test(
      [[
          CREATE INDEX t1i1 ON t1(a);
          ANALYZE t1;
-        SELECT * FROM "_sql_stat1" ORDER BY "idx";
+        SELECT * FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-2.1>
          -- </analyze-2.1>
@@ -130,7 +130,7 @@ test:do_execsql_test(
      [[
          CREATE INDEX t1i2 ON t1(b);
          ANALYZE t1;
-        SELECT * FROM "_sql_stat1" ORDER BY "idx";
+        SELECT * FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-2.2>
          -- </analyze-2.2>
@@ -141,7 +141,7 @@ test:do_execsql_test(
      [[
          CREATE INDEX t1i3 ON t1(a,b);
          ANALYZE;
-        SELECT * FROM "_sql_stat1" ORDER BY "idx";
+        SELECT * FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-2.3>
          -- </analyze-2.3>
@@ -157,10 +157,10 @@ test:do_execsql_test(
          INSERT INTO t1 VALUES(1, 1,2);
          INSERT INTO t1 VALUES(2, 1,3);
          ANALYZE t1;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.1>
-        "T1", "2 1", "T1I1", "2 2", "T1I2", "2 1", "T1I3", "2 2 1"
+        0, "2 1", 1, "2 2", 2, "2 1", 3, "2 2 1"
          -- </analyze-3.1>
      })

@@ -170,10 +170,10 @@ test:do_execsql_test(
          INSERT INTO t1 VALUES(3, 1,4);
          INSERT INTO t1 VALUES(4, 1,5);
          ANALYZE t1;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.2>
-        "T1", "4 1", "T1I1", "4 4", "T1I2", "4 1", "T1I3", "4 4 1"
+        0, "4 1", 1, "4 4", 2, "4 1", 3, "4 4 1"
          -- </analyze-3.2>
      })

@@ -182,10 +182,10 @@ test:do_execsql_test(
      [[
          INSERT INTO t1 (a,b) VALUES(2,5);
          ANALYZE;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.3>
-        "T1","5 1", "T1I1", "5 3", "T1I2", "5 2", "T1I3", "5 3 1"
+        0,"5 1", 1, "5 3", 2, "5 2", 3, "5 3 1"
          -- </analyze-3.3>
      })

@@ -198,10 +198,10 @@ test:do_execsql_test(
          CREATE INDEX t2i2 ON t2(b);
          CREATE INDEX t2i3 ON t2(a,b);
          ANALYZE;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.4>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 
1","T2I1","5 3","T2I2","5 2","T2I3","5 3 1"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",2,"5 2",3,"5 3 1",3,"5 3 1"
          -- </analyze-3.4>
      })

@@ -210,10 +210,10 @@ test:do_execsql_test(
      [[
          DROP INDEX t2i3 ON t2;;
          ANALYZE t1;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.5>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 
1","T2I1","5 3","T2I2","5 2"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",2,"5 2",3,"5 3 1"
          -- </analyze-3.5>
      })

@@ -221,10 +221,10 @@ test:do_execsql_test(
      "analyze-3.6",
      [[
          ANALYZE t2;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.6>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 
1","T2I1","5 3","T2I2","5 2"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",2,"5 2",3,"5 3 1"
          -- </analyze-3.6>
      })

@@ -233,10 +233,10 @@ test:do_execsql_test(
      [[
          DROP INDEX t2i2 ON t2;
          ANALYZE t2;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.7>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 
1","T2I1","5 3"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",3,"5 3 1"
          -- </analyze-3.7>
      })

@@ -250,7 +250,7 @@ test:do_execsql_test(
          CREATE INDEX t3i3 ON t3(d,b,c,a);
          DROP TABLE t1;
          DROP TABLE t2;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.8>
          -- </analyze-3.8>
@@ -260,10 +260,10 @@ test:do_execsql_test(
      "analyze-3.9",
      [[
          ANALYZE;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.9>
-        "T3","5 1","T3I1","5 3","T3I2","5 3 1 1 1","T3I3","5 5 2 1 1"
+        0,"5 1",1,"5 3",2,"5 3 1 1 1",3,"5 5 2 1 1"
          -- </analyze-3.9>
      })

@@ -277,7 +277,7 @@ test:do_execsql_test(
  --         INSERT INTO [silly " name] (a,b,c) VALUES(1, 2, 3);
  --         INSERT INTO [silly " name] (a,b,c) VALUES(4, 5, 6);
  --         ANALYZE;
---         SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+--         SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
  --     ]], {
  --         -- <analyze-3.10>
  --         "another foolish ' name", "2 1", "foolish ' name", "2 1 1", 
"t3i1", "5 3", "t3i2", "5 3 1 1 1", "t3i3", "5 5 2 1 1"
@@ -288,7 +288,7 @@ test:do_execsql_test(
  --     "analyze-3.11",
  --     [[
  --         DROP INDEX "foolish ' name";
---         SELECT "idx", "stat" FROM sqlite_stat1 ORDER BY "idx";
+--         SELECT "index_id", "stat" FROM sqlite_stat1 ORDER BY "index_id";
  --     ]], {
  --         -- <analyze-3.11>
  --         "another foolish ' name", "2 1", "t3i1", "5 3", "t3i2", "5 
3 1 1 1", "t3i3", "5 5 2 1 1"
@@ -299,7 +299,7 @@ test:do_execsql_test(
  --     "analyze-3.11",
  --     [[
  --         DROP TABLE "silly "" name";
---         SELECT "idx", "stat" FROM sqlite_stat1 ORDER BY "idx";
+--         SELECT "index_id", "stat" FROM sqlite_stat1 ORDER BY "index_id";
  --     ]], {
  --         -- <analyze-3.11>
  --         "t3i1", "5 3", "t3i2", "5 3 1 1 1", "t3i3", "5 5 2 1 1"
@@ -317,31 +317,33 @@ test:do_execsql_test(
          CREATE INDEX t4i2 ON t4(y);
          INSERT INTO t4 SELECT id,a,b,c FROM t3;
          ANALYZE;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-4.0>
-        "T3", "5 1", "T3I1", "5 3", "T3I2", "5 3 1 1 1", "T3I3", "5 5 2 
1 1", "T4", "5 1", "T4I1", "5 3", "T4I2", "5 2"
+        0, "5 1", 0, "5 1", 1, "5 3", 1, "5 3", 2, "5 3 1 1 1", 2, "5 
2", 3, "5 5 2 1 1"
          -- </analyze-4.0>
      })

+t4 = box.space.T4
+
  test:do_execsql_test(
      "analyze-4.1",
-    [[
+    string.format([[
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1" VALUES('t4', 't4i1', 'nonsense');
-        INSERT INTO "_sql_stat1" VALUES('t4', 't4i2', 
'432653287412874653284129847632');
+        INSERT INTO "_sql_stat1" VALUES(%i, %i, 'nonsense');
+        INSERT INTO "_sql_stat1" VALUES(%i, %i, 
'432653287412874653284129847632');
          SELECT * FROM t4 WHERE x = 1234;
-    ]], {
+    ]], t4.id, t4.index['T4I1'].id, t4.id, t4.index['T4I2'].id), {
          -- <analyze-4.1>
          -- </analyze-4.1>
      })

  test:do_execsql_test(
      "analyze-4.2",
-    [[
-        INSERT INTO "_sql_stat1" VALUES('t4', 'xyzzy', '0 1 2 3');
+    string.format([[
+        INSERT INTO "_sql_stat1" VALUES(%i, 12345, '0 1 2 3');
          SELECT * FROM t4 WHERE x = 1234;
-    ]], {
+    ]], t4.id), {
          -- <analyze-4.2>
          -- </analyze-4.2>
      })
@@ -367,20 +369,20 @@ test:do_execsql_test(
          INSERT INTO t3 (a,b,c,d) SELECT a+64, b+64, c+64, d+64 FROM t3;
          INSERT INTO t4 (x,y,z) SELECT a, b, c FROM t3;
          ANALYZE;
-        SELECT DISTINCT "tbl" FROM "_sql_stat1" ORDER BY 1;
+        SELECT COUNT(DISTINCT "space_id") FROM "_sql_stat1" ORDER BY 1;
      ]], {
          -- <analyze-5.0>
-        "T3", "T4"
+        2
          -- </analyze-5.0>
      })

  test:do_execsql_test(
      "analyze-5.0.1",
      [[
-        SELECT DISTINCT "idx" FROM "_sql_stat1" ORDER BY 1;
+        SELECT "index_id" FROM "_sql_stat1" ORDER BY 1;
      ]], {
          -- <analyze-5.0>
-        "T3", "T3I1", "T3I2", "T3I3", "T4", "T4I1", "T4I2"
+        0, 0, 1, 1, 2, 2, 3
          -- </analyze-5.0>
      })

@@ -389,20 +391,20 @@ stat = "_sql_stat4"
  test:do_execsql_test(
      "analyze-5.1",
      string.format([[
-            SELECT DISTINCT "idx" FROM "%s" ORDER BY 1;
-        ]], stat, stat), {
+            SELECT DISTINCT "index_id" FROM "%s" ORDER BY 1;
+        ]], stat), {
          -- <analyze-5.1>
-        "T3", "T3I1", "T3I2", "T3I3", "T4", "T4I1", "T4I2"
+        0, 1, 2, 3
          -- </analyze-5.1>
      })

  test:do_execsql_test(
      "analyze-5.1.1",
      string.format([[
-            SELECT DISTINCT "tbl" FROM "%s" ORDER BY 1;
-        ]], stat, stat), {
+            SELECT COUNT(DISTINCT "space_id") FROM "%s" ORDER BY 1;
+        ]], stat), {
          -- <analyze-5.1>
-        "T3", "T4"
+        2
          -- </analyze-5.1>
      })

@@ -411,40 +413,40 @@ test:do_execsql_test(
      [[
          DROP INDEX t3i2 ON t3;
          ANALYZE;
-        SELECT DISTINCT "idx" FROM "_sql_stat1" ORDER BY 1;
+        SELECT "index_id" FROM "_sql_stat1" ORDER BY 1;
      ]], {
          -- <analyze-5.2>
-        "T3", "T3I1", "T3I3", "T4", "T4I1", "T4I2"
+        0, 0, 1, 1, 2, 3
          -- </analyze-5.2>
      })

  test:do_execsql_test(
      "analyze-5.2.1",
      [[
-        SELECT DISTINCT "tbl" FROM "_sql_stat1" ORDER BY 1;
+        SELECT COUNT(DISTINCT "space_id") FROM "_sql_stat1" ORDER BY 1;
      ]], {
          -- <analyze-5.2>
-        "T3", "T4"
+        2
          -- </analyze-5.2>
      })

  test:do_execsql_test(
      "analyze-5.3",
      string.format([[
-            SELECT DISTINCT "idx" FROM "%s" ORDER BY 1;
+            SELECT DISTINCT "index_id" FROM "%s" ORDER BY 1;
          ]], stat, stat), {
          -- <analyze-5.3>
-        "T3", "T3I1", "T3I3", "T4", "T4I1", "T4I2"
+        0, 1, 2, 3
          -- </analyze-5.3>
      })

  test:do_execsql_test(
      "analyze-5.3.1",
      string.format([[
-            SELECT DISTINCT "tbl" FROM "%s" ORDER BY 1;
-        ]], stat, stat), {
+            SELECT COUNT(DISTINCT "space_id") FROM "%s" ORDER BY 1;
+        ]], stat, stat, stat), {
          -- <analyze-5.3>
-        "T3", "T4"
+        2
          -- </analyze-5.3>
      })

@@ -453,40 +455,40 @@ test:do_execsql_test(
      [[
          DROP TABLE IF EXISTS t3;
          ANALYZE;
-        SELECT DISTINCT "idx" FROM "_sql_stat1" ORDER BY 1;
+        SELECT DISTINCT "index_id" FROM "_sql_stat1" ORDER BY 1;
      ]], {
          -- <analyze-5.4>
-        "T4", "T4I1", "T4I2"
+        0, 1, 2
          -- </analyze-5.4>
      })

  test:do_execsql_test(
      "analyze-5.4.1",
      [[
-        SELECT DISTINCT "tbl" FROM "_sql_stat1" ORDER BY 1;
+        SELECT COUNT(DISTINCT "space_id") FROM "_sql_stat1" ORDER BY 1;
      ]], {
          -- <analyze-5.4>
-        "T4"
+        1
          -- </analyze-5.4>
      })

  test:do_execsql_test(
      "analyze-5.5",
      string.format([[
-            SELECT DISTINCT "idx" FROM "%s" ORDER BY 1;
+            SELECT DISTINCT "index_id" FROM "%s" ORDER BY 1;
          ]], stat), {
          -- <analyze-5.5>
-        "T4", "T4I1", "T4I2"
+        0, 1, 2
          -- </analyze-5.5>
      })

  test:do_execsql_test(
      "analyze-5.5.1",
      string.format([[
-            SELECT DISTINCT "tbl" FROM "%s" ORDER BY 1;
-        ]], stat), {
+            SELECT COUNT(DISTINCT "space_id") FROM "%s" ORDER BY 1;
+        ]], stat, stat), {
          -- <analyze-5.5>
-        "T4"
+        1
          -- </analyze-5.5>
      })

@@ -516,33 +518,35 @@ test:do_test(
      -- </analyze-6.1.1>
  })

+t1 = box.space.T1
+
  test:do_execsql_test(
      "analyze-6.1.2",
-    [[
-            SELECT * FROM "_sql_stat1" where "tbl"='T1' and "idx"='I1' 
LIMIT 1;
-    ]], {
+    string.format([[
+            SELECT "index_id", "stat" FROM "_sql_stat1" where 
"space_id"=%i and "index_id"=%i LIMIT 1;
+    ]], t1.id, t1.index['I1'].id), {
      -- <analyze-6.1.2>
-    "T1", "I1", "221 221 221 221 2"
+    1, "221 221 221 221 2"
      -- </analyze-6.1.2>
  })

  test:do_execsql_test(
      "analyze-6.1.3",
-    [[
-            SELECT "tbl", "idx", "neq", "nlt", "ndlt" FROM "_sql_stat4" 
where "tbl"='T1' and "idx"='I1' ORDER BY "nlt" LIMIT 1;
-    ]], {
+    string.format([[
+            SELECT "index_id", "neq", "nlt", "ndlt" FROM "_sql_stat4" 
where "space_id"=%i and "index_id"=%i ORDER BY "nlt" LIMIT 1;
+    ]], t1.id, t1.index['I1'].id), {
      -- <analyze-6.1.3>
-    "T1", "I1", "221 221 221 1", "0 0 0 10", "0 0 0 10"
+    1, "221 221 221 1", "0 0 0 10", "0 0 0 10"
      -- </analyze-6.1.3>
  })

  test:do_execsql_test(
      "analyze-6.1.4",
-    [[
-            SELECT "tbl", "idx", "neq", "nlt", "ndlt" FROM "_sql_stat4" 
where "tbl"='T1' and "idx"='I1' ORDER BY "nlt" DESC LIMIT 1;
-    ]], {
+    string.format([[
+            SELECT "index_id", "neq", "nlt", "ndlt" FROM "_sql_stat4" 
where "space_id"=%i and "index_id"=%i ORDER BY "nlt" DESC LIMIT 1;
+    ]], t1.id, t1.index['I1'].id), {
      -- <analyze-6.1.4>
-    "T1", "I1", "221 221 221 1", "0 0 0 99", "0 0 0 99"
+    1, "221 221 221 1", "0 0 0 99", "0 0 0 99"
      -- </analyze-6.1.4>
  })

diff --git a/test/sql-tap/analyze4.test.lua b/test/sql-tap/analyze4.test.lua
index c2cc190..d64cba8 100755
--- a/test/sql-tap/analyze4.test.lua
+++ b/test/sql-tap/analyze4.test.lua
@@ -49,15 +49,19 @@ test:do_test(
          -- </analyze4-1.0>
      })

+t1 = box.space.T1
+
  -- Verify that the t1b index shows that it does not narrow down the
  -- search any at all.
  --
  test:do_execsql_test(
      "analyze4-1.1",
-    [[ SELECT "idx", "stat" FROM "_sql_stat1" WHERE "tbl"='T1' ORDER BY 
"idx"; ]],
+    string.format([[
+        SELECT "index_id", "stat" FROM "_sql_stat1" WHERE "space_id"=%i 
ORDER BY "index_id";
+    ]], t1.id),
      {
          -- <analyze4-1.1>
-        "T1","128 1", "T1A", "128 1", "T1B", "128 128"
+        0,"128 1", 1, "128 1", 2, "128 128"
          -- </analyze4-1.1>
      })

@@ -68,16 +72,16 @@ test:do_execsql_test(
  test:do_test(
      "analyze4-1.2",
      function()
-        return test:execsql([[
-            UPDATE t1 SET b='x' WHERE a%2;
+        return test:execsql(string.format([[
+            UPDATE t1 SET b='x' WHERE a%%2;
  -- pragma vdbe_debug=1;
              ANALYZE;
  -- pragma vdbe_debug=0;
-            SELECT "idx", "stat" FROM "_sql_stat1" WHERE "tbl"='T1' 
ORDER BY "idx";
-        ]])
+            SELECT "index_id", "stat" FROM "_sql_stat1" WHERE 
"space_id"=%i ORDER BY "index_id";
+        ]], t1.id))
      end, {
          -- <analyze4-1.2>
-        "T1", "128 1", "T1A", "128 1", "T1B", "128 64"
+        0, "128 1", 1, "128 1", 2, "128 64"
          -- </analyze4-1.2>
      })

@@ -85,37 +89,42 @@ test:do_test(
  -- Create a multi-column indices using t1.b and verify that ANALYZE
  -- processes them correctly.
  --
+
+test:execsql([[
+    -- Tarantool doesn't suppoort ALTER stmt yet.
+    -- UPDATE t1 SET b=NULL;
+    --ALTER TABLE t1 ADD COLUMN c;
+    --ALTER TABLE t1 ADD COLUMN d;
+    -- So, re-create the table and its contents
+    DROP TABLE t1;
+    CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a,b,c DEFAULT 
NULL,d DEFAULT NULL);
+    CREATE INDEX t1a ON t1(a);
+    CREATE INDEX t1b ON t1(b);
+    INSERT INTO t1 (a,b) VALUES(1,NULL);
+    INSERT INTO t1 (a,b) SELECT a+1, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+2, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+4, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+8, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+16, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+32, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+64, b FROM t1;
+
+    UPDATE t1 SET c=a/4, d=a/2;
+    CREATE INDEX t1bcd ON t1(b,c,d);
+    CREATE INDEX t1cdb ON t1(c,d,b);
+    CREATE INDEX t1cbd ON t1(c,b,d);
+    ANALYZE;
+]])
+
+t1 = box.space.T1
+
  test:do_execsql_test(
      "analyze4-1.3",
-    [[
-            -- Tarantool doesn't suppoort ALTER stmt yet.
-            -- UPDATE t1 SET b=NULL;
-            --ALTER TABLE t1 ADD COLUMN c;
-            --ALTER TABLE t1 ADD COLUMN d;
-            -- So, re-create the table and its contents
-            DROP TABLE t1;
-            CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a,b,c 
DEFAULT NULL,d DEFAULT NULL);
-            CREATE INDEX t1a ON t1(a);
-            CREATE INDEX t1b ON t1(b);
-            INSERT INTO t1 (a,b) VALUES(1,NULL);
-            INSERT INTO t1 (a,b) SELECT a+1, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+2, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+4, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+8, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+16, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+32, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+64, b FROM t1;
-
-            UPDATE t1 SET c=a/4, d=a/2;
-            CREATE INDEX t1bcd ON t1(b,c,d);
-            CREATE INDEX t1cdb ON t1(c,d,b);
-            CREATE INDEX t1cbd ON t1(c,b,d);
-            ANALYZE;
-            SELECT "idx", "stat" FROM "_sql_stat1" WHERE "tbl"='T1' 
ORDER BY "idx";
-    ]]
-    , {
+    string.format([[
+            SELECT "index_id", "stat" FROM "_sql_stat1" WHERE 
"space_id"=%i ORDER BY "index_id";
+    ]], t1.id), {
          -- <analyze4-1.3>
-        "T1","128 1", "T1A", "128 1", "T1B", "128 128", "T1BCD", "128 
128 4 2", "T1CBD", "128 4 4 2", "T1CDB", "128 4 2 2"
+        0,"128 1", 1, "128 1", 2, "128 128", 3, "128 128 4 2", 4, "128 
4 2 2", 5, "128 4 4 2"
          -- </analyze4-1.3>
      })

diff --git a/test/sql-tap/analyze5.test.lua b/test/sql-tap/analyze5.test.lua
index d68bd3c..ece26e4 100755
--- a/test/sql-tap/analyze5.test.lua
+++ b/test/sql-tap/analyze5.test.lua
@@ -114,10 +114,11 @@ test:do_test(

          -- DISTINCT idx, sample -- lindex(test_decode(sample),0)
          -- WHERE idx='t1u' ORDER BY nlt;
-        return test:execsql([[ SELECT DISTINCT msgpack_decode("sample")
-                                 FROM "_sql_stat4"
-                                 WHERE "idx"='T1U'
-                                 ORDER BY "nlt"]])
+        return test:execsql(string.format([[
+            SELECT DISTINCT msgpack_decode("sample")
+            FROM "_sql_stat4"
+            WHERE "index_id"= %i
+            ORDER BY "nlt"]], box.space.T1.index['T1U'].id))
      end, {
          -- <analyze5-1.0>
          "alpha", "bravo", "charlie", "delta"
@@ -143,10 +144,10 @@ test:do_test(
  test:do_test(
      "analyze5-1.2",
      function()
-        return test:execsql([[SELECT "idx", count(*) FROM "_sql_stat4" 
GROUP BY 1 ORDER BY 1]])
+        return test:execsql([[SELECT "index_id", count(*) FROM 
"_sql_stat4" GROUP BY 1 ORDER BY 1]])
      end, {
          -- <analyze5-1.2>
- "T1",24,"T1T",4,"T1U",4,"T1V",1,"T1W",4,"T1X",4,"T1Y",2,"T1Z",4
+        0,24,1,4,2,4,3,1,4,4,5,4,6,2,7,4
          -- </analyze5-1.2>
      })

diff --git a/test/sql-tap/analyze9.test.lua b/test/sql-tap/analyze9.test.lua
index 05ed501..075cc69 100755
--- a/test/sql-tap/analyze9.test.lua
+++ b/test/sql-tap/analyze9.test.lua
@@ -1,6 +1,6 @@
  #!/usr/bin/env tarantool
  test = require("sqltester")
-test:plan(121)
+test:plan(120)

  testprefix = "analyze9"

@@ -62,29 +62,29 @@ msgpack_decode_sample = function(txt)
  end

  box.internal.sql_create_function("msgpack_decode_sample", 
msgpack_decode_sample)
+t1 = box.space.T1

  test:do_execsql_test(
      1.2,
-    [[
-        SELECT 
"tbl","idx","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM 
"_sql_stat4" where "idx" = 'I1';
-    ]], {
+    string.format([[
+        SELECT 
"index_id","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM 
"_sql_stat4" where "index_id" = %i;
+    ]], t1.index['I1'].id), {
          -- <1.2>
-        "T1", "I1", "1 1", "0 0", "0 0", "(0) (0)", "T1", "I1", "1 1", 
"1 1", "1 1", "(1) (1)",
-        "T1", "I1", "1 1", "2 2", "2 2", "(2) (2)", "T1", "I1", "1 1", 
"3 3", "3 3", "(3) (3)",
-        "T1", "I1", "1 1", "4 4", "4 4", "(4) (4)"
+        1, "1 1", "0 0", "0 0", "(0) (0)", 1, "1 1", "1 1", "1 1", "(1) 
(1)",
+        1, "1 1", "2 2", "2 2", "(2) (2)", 1, "1 1", "3 3", "3 3", "(3) 
(3)",
+        1, "1 1", "4 4", "4 4", "(4) (4)"
          -- </1.2>
      })

  test:do_execsql_test(
      1.3,
-    [[
-        SELECT 
"tbl","idx","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM 
"_sql_stat4" where "idx" = 'T1';
-
-    ]], {
+    string.format([[
+        SELECT 
"index_id","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM 
"_sql_stat4" where "index_id" = %i;
+    ]], t1.index[0].id), {
          -- <1.3>
-        'T1', 'T1', '1', '0', '0', '(0)', 'T1', 'T1', '1', '1', '1', 
'(1)',
-        'T1', 'T1', '1', '2', '2', '(2)', 'T1', 'T1', '1', '3', '3', 
'(3)',
-        'T1', 'T1', '1', '4', '4', '(4)'
+        0, '1', '0', '0', '(0)', 0, '1', '1', '1', '(1)',
+        0, '1', '2', '2', '(2)', 0, '1', '3', '3', '(3)',
+        0, '1', '4', '4', '(4)'
          -- </1.3>
      })

@@ -101,10 +101,10 @@ test:do_execsql_test(
          INSERT INTO t1 VALUES(22.0, 'some text');
          CREATE INDEX i1 ON t1(a, b);
          ANALYZE;
-        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4";
+        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4" ORDER 
BY 1;
      ]], {
          -- <2.1>
-        "some text 14", "22 some text", "some text", 22
+        22, "22 some text", "some text", "some text 14"
          -- </2.1>
      })

@@ -116,6 +116,7 @@ test:do_execsql_test(
          CREATE TABLE t2(id INTEGER PRIMARY KEY AUTOINCREMENT, a, b);
          CREATE INDEX i2 ON t2(a, b);
      ]])
+t2 = box.space.T2

  test:do_test(
      3.2,
@@ -184,10 +185,10 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      "3.3.2",
-    [[
+    string.format([[
          ANALYZE;
-        SELECT lrange("neq", 1, 1) FROM "_sql_stat4" WHERE "idx" = 'I2';
-    ]], generate_tens_str(24))
+        SELECT lrange("neq", 1, 1) FROM "_sql_stat4" WHERE "space_id" = 
%i and "index_id" = %i;
+    ]], t2.id, t2.index['I2'].id) , generate_tens_str(24))

  ---------------------------------------------------------------------------
  --
@@ -232,6 +233,7 @@ test:do_execsql_test(
          CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a, b, c);
          CREATE INDEX i1 ON t1(c, b, a);
      ]])
+t1 = box.space.T1

  insert_filler_rows_n = function(iStart, nCopy, nVal)
      for i = 0, nVal-1 do
@@ -282,10 +284,10 @@ test:do_execsql_test(

  test:do_execsql_test(
      4.3,
-    [[
+    string.format([[
          SELECT "neq", lrange("nlt", 1, 3), lrange("ndlt", 1, 3), 
lrange(msgpack_decode_sample("sample"), 1, 3)
-            FROM "_sql_stat4" WHERE "idx" = 'I1' ORDER BY "sample" 
LIMIT 16;
-    ]], {
+            FROM "_sql_stat4" WHERE "space_id" = %i and "index_id" = %i 
ORDER BY "sample" LIMIT 16;
+    ]], t1.id, t1.index['I1'].id), {
          -- <4.3>
          "10 10 10","0 0 0","0 0 0","0 0 0","10 10 10","10 10 10","1 1 
1","1 1 1","10 10 10","20 20 20",
          "2 2 2","2 2 2","10 10 10","30 30 30","3 3 3","3 3 3","10 10 
10","40 40 40","4 4 4","4 4 4",
@@ -299,10 +301,10 @@ test:do_execsql_test(

  test:do_execsql_test(
      4.4,
-    [[
+    string.format([[
          SELECT "neq", lrange("nlt", 1, 3), lrange("ndlt", 1, 3), 
lrange(msgpack_decode_sample("sample"), 1, 3)
-        FROM "_sql_stat4" WHERE "idx" = 'I1' ORDER BY "sample" DESC 
LIMIT 2;
-    ]], {
+        FROM "_sql_stat4" WHERE "space_id" = %i and "index_id" = %i 
ORDER BY "sample" DESC LIMIT 2;
+    ]], t1.id, t1.index['I1'].id), {
          -- <4.4>
          "2 1 1","295 296 296","120 122 125","201 4 h","5 3 1","290 290 
291","119 119 120","200 1 b"
          -- </4.4>
@@ -364,11 +366,12 @@ test:do_test(
  test:do_execsql_test(
      4.9,
      [[
-        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4";
+        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4" ORDER 
BY 1;
      ]], {
          -- <4.9>
-        "x", 1110, 2230, 2750, 3350, 4090, 4470, 4980, 5240, 5280, 
5290, 5590, 5920,
-        5930, 6220, 6710, 7000, 7710, 7830, 7970, 8890, 8950, 9240, 
9250, 9680
+        1110, 2230, 2750, 3350, 4090, 4470, 4980, 5240, 5280, 5290, 
5590, 5920,
+        5930, 6220, 6710, 7000, 7710, 7830, 7970, 8890, 8950, 9240, 
9250, 9680,
+        "x"
          -- </4.9>
      })

@@ -394,6 +397,8 @@ test:do_execsql_test(
          INSERT INTO "_sql_stat4" SELECT * FROM x1;
          ANALYZE;
      ]])
+t1 = box.space.T1
+x1 = box.space.X1

  test:do_execsql_test(
      6.2,
@@ -407,7 +412,7 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      7.1,
-    [[
+    string.format([[
          DROP TABLE IF EXISTS t1;
          CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a, b);
          CREATE INDEX i1 ON t1(a, b);
@@ -418,9 +423,9 @@ test:do_execsql_test(
          INSERT INTO t1 VALUES(null, 5, 5);
          ANALYZE;
          UPDATE "_sql_stat4" SET "sample" = '' WHERE "sample" =
-            (SELECT "sample" FROM "_sql_stat4" WHERE "tbl" = 't1' AND 
"idx" = 'i1' LIMIT 1);
+            (SELECT "sample" FROM "_sql_stat4" WHERE "space_id" = %i 
AND "index_id" = %i LIMIT 1);
          ANALYZE;
-    ]])
+    ]], t1.id, t1.index['I1'].id))

  -- Doesn't work due to the fact that in Tarantool rowid has been removed,
  -- and tbl, idx and sample have been united into primary key.
@@ -1014,15 +1019,15 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      15.1,
-    [[
+    string.format([[
          DROP TABLE IF EXISTS x1;
          CREATE TABLE x1(a PRIMARY KEY, b, UNIQUE(a, b));
          INSERT INTO x1 VALUES(1, 2);
          INSERT INTO x1 VALUES(3, 4);
          INSERT INTO x1 VALUES(5, 6);
          ANALYZE;
-        INSERT INTO "_sql_stat4" VALUES('x1', 'abc', '', '', '', '');
-    ]])
+        INSERT INTO "_sql_stat4" VALUES(%i, 12345, '', '', '', '');
+    ]], x1.id))

  test:do_execsql_test(
      15.2,
@@ -1037,7 +1042,7 @@ test:do_execsql_test(
  test:do_execsql_test(
      15.3,
      [[
-        INSERT INTO "_sql_stat4" VALUES('42', '42', '42', '42', '42', 42);
+        INSERT INTO "_sql_stat4" VALUES(42, 42, '42', '42', '42', 42);
      ]])

  test:do_execsql_test(
@@ -1054,7 +1059,7 @@ test:do_execsql_test(
      15.7,
      [[
          ANALYZE;
-        UPDATE "_sql_stat1" SET "tbl" = 'no such tbl';
+        UPDATE "_sql_stat1" SET "space_id" = "space_id" + 123456;
      ]])

  test:do_execsql_test(
@@ -1067,25 +1072,6 @@ test:do_execsql_test(
          -- </15.8>
      })

--- Tarantool: this test seems to be useless. There's no reason
--- for these fields to be nullable.
--- test:do_execsql_test(
---    15.9,
---    [[
---        ANALYZE;
---        UPDATE "_sql_stat4" SET "neq" = NULL, "nlt" = NULL, "ndlt" = 
NULL;
---    ]])
-
-test:do_execsql_test(
-    15.10,
-    [[
-        SELECT * FROM x1;
-    ]], {
-        -- <15.10>
-        1, 2, 3, 4, 5, 6
-        -- </15.10>
-    })
-
  -- This is just for coverage....
  test:do_execsql_test(
      15.11,
@@ -1135,6 +1121,7 @@ test:do_test(
          -- <17.1>
          -- </17.1>
      })
+t1 = box.space.T1

  test:do_execsql_test(
      17.2,
@@ -1197,7 +1184,7 @@ test:do_test(
              test:execsql(string.format("INSERT INTO t1 VALUES(%s, 
0);", i))
          end
          test:execsql("ANALYZE")
-        return test:execsql([[ SELECT count(*) FROM "_sql_stat4" WHERE 
"idx" = 'I1'; ]])
+        return test:execsql(string.format('SELECT count(*) FROM 
"_sql_stat4" WHERE "space_id" = %i AND "index_id" = %i;', 
box.space.T1.id, box.space.T1.index['I1'].id))
      end, {
          -- <18.1>
          9
@@ -1239,12 +1226,13 @@ test:do_execsql_test(
          -- </20.2>
      })

+t1 = box.space.T1
  for i = 0, 15 do
      test:do_test(
          "20.3."..i,
          function()
              return test:execsql(string.format(
-                [[SELECT count(*) FROM "_sql_stat4" WHERE "idx" = 'I1' 
AND lrange(msgpack_decode_sample("sample"), 1, 1) = '%s']], i))
+                [[SELECT count(*) FROM "_sql_stat4" WHERE "space_id" = 
%i AND "index_id" = %i AND lrange(msgpack_decode_sample("sample"), 1, 1) 
= '%s']], t1.id, t1.index['I1'].id, i))
          end, {
              1
          })
diff --git a/test/sql-tap/analyzeC.test.lua b/test/sql-tap/analyzeC.test.lua
index a3cea70..e772d71 100755
--- a/test/sql-tap/analyzeC.test.lua
+++ b/test/sql-tap/analyzeC.test.lua
@@ -30,9 +30,7 @@ testprefix = "analyzeC"
  -- Baseline case.  Range queries work OK.  Indexes can be used for
  -- ORDER BY.

-test:do_execsql_test(
-    1.0,
-    [[
+test:execsql([[
          DROP TABLE IF EXISTS t1;
          CREATE TABLE t1(a PRIMARY KEY, b, c, d);
          INSERT INTO t1(a,b,c,d) 
VALUES(1,1,2,3),(2,7,8,9),(3,4,5,6),(4,10,11,12),(5,4,8,12),(6,1,11,111);
@@ -40,10 +38,17 @@ test:do_execsql_test(
          CREATE INDEX t1c ON t1(c);
          ANALYZE;
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") 
VALUES('t1','t1b','12345 2'),('t1','t1c','12345 4');
+    ]]
+)
+t1 = box.space.T1
+
+test:do_execsql_test(
+    1.0,
+    string.format([[
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat") 
VALUES(%i,1,'12345 2'),(%i,2,'12345 4');
          ANALYZE;
          SELECT b,c,d, '#' FROM t1 WHERE b BETWEEN 3 AND 8 ORDER BY d;
-    ]], {
+    ]], t1.id, t1.id), {
          -- <1.0>
          4, 5, 6, "#", 7, 8, 9, "#", 4, 8, 12, "#"
          -- </1.0>
@@ -85,11 +90,12 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      2.0,
+    string.format(
      [[
-        UPDATE "_sql_stat1" SET "stat"='12345 2 unordered' WHERE 
"idx"='t1b';
+        UPDATE "_sql_stat1" SET "stat"='12345 2 unordered' WHERE 
"index_id"=%i;
          ANALYZE;
          SELECT b, c, d, '#' FROM t1 WHERE b BETWEEN 3 AND 8 ORDER BY d;
-    ]], {
+    ]], box.space.T1.index['T1B'].id), {
          -- <2.0>
          4, 5, 6, "#", 7, 8, 9, "#", 4, 8, 12, "#"
          -- </2.0>
@@ -130,11 +136,12 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      3.0,
+    string.format(
      [[
-        UPDATE "_sql_stat1" SET "stat"='12345 2 whatever=5 unordered 
xyzzy=11' WHERE "idx"='t1b';
+        UPDATE "_sql_stat1" SET "stat"='12345 2 whatever=5 unordered 
xyzzy=11' WHERE "index_id"=%i;
          ANALYZE;
          SELECT b, c, d, '#' FROM t1 WHERE b BETWEEN 3 AND 8 ORDER BY d;
-    ]], {
+    ]], box.space.T1.index['T1B'].id), {
          -- <3.0>
          4, 5, 6, "#", 7, 8, 9, "#", 4, 8, 12, "#"
          -- </3.0>
@@ -173,17 +180,22 @@ test:do_execsql_test(

  -- The sz=NNN parameter determines which index to scan
  --
-test:do_execsql_test(
-    4.0,
-    [[
+
+test:execsql([[
          DROP INDEX t1b ON t1;
          CREATE INDEX t1bc ON t1(b,c);
          CREATE INDEX t1db ON t1(d,b);
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") 
VALUES('t1','t1bc','12345 3 2 sz=10'),('t1','t1db','12345 3 2 sz=20');
+    ]]
+)
+
+test:do_execsql_test(
+    4.0,
+    string.format([[
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat") 
VALUES(%i,%i,'12345 3 2 sz=10'),(%i,%i,'12345 3 2 sz=20');
          ANALYZE;
          SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
          -- <4.0>
          6
          -- </4.0>
@@ -201,12 +213,13 @@ test:do_execsql_test(

  test:do_execsql_test(
      4.2,
+    string.format(
      [[
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") 
VALUES('t1','t1bc','12345 3 2 sz=20'),('t1','t1db','12345 3 2 sz=10');
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat") 
VALUES(%i,%i,'12345 3 2 sz=20'),(%i,%i,'12345 3 2 sz=10');
          ANALYZE;
          SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
          -- <4.2>
          6
          -- </4.2>
@@ -227,14 +240,14 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      5.0,
-    [[
+    string.format([[
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat")
-          VALUES('t1','t1bc','12345 3 2 x=5 sz=10 y=10'),
-                ('t1','t1db','12345 3 2 whatever sz=20 junk');
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat")
+          VALUES(%i,%i,'12345 3 2 x=5 sz=10 y=10'),
+                (%i,%i,'12345 3 2 whatever sz=20 junk');
          ANALYZE;
          SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
          -- <5.0>
          6
          -- </5.0>
@@ -253,12 +266,12 @@ test:do_execsql_test(

  test:do_execsql_test(
      5.2,
-    [[
+    string.format([[
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") 
VALUES('t1','t1db','12345 3 2 x=5 sz=10 y=10'), ('t1','t1bc','12345 3 2 
whatever sz=20 junk');
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat") 
VALUES(%i,%i,'12345 3 2 x=5 sz=10 y=10'), (%i,%i,'12345 3 2 whatever 
sz=20 junk');
          ANALYZE;
          SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
          -- <5.2>
          6
          -- </5.2>
diff --git a/test/sql-tap/analyzeD.test.lua b/test/sql-tap/analyzeD.test.lua
index ef6aced..9d89933 100755
--- a/test/sql-tap/analyzeD.test.lua
+++ b/test/sql-tap/analyzeD.test.lua
@@ -153,9 +153,9 @@ test:do_catchsql_test(

  test:do_execsql_test(
      "analyzeD-1.10",
-    [[
-        SELECT * FROM "_sql_stat4" WHERE "tbl" = 'v';
-    ]], {
+    string.format([[
+        SELECT * FROM "_sql_stat4" WHERE "space_id" = %i;
+    ]], box.space.V.id), {
          -- <analyzeD-1.10>

          -- <analyzeD-1.10>
@@ -163,9 +163,9 @@ test:do_execsql_test(

  test:do_execsql_test(
      "analyzeD-1.11",
-    [[
-        SELECT * FROM "_sql_stat1" WHERE "tbl" = 'v';
-    ]], {
+    string.format([[
+        SELECT * FROM "_sql_stat1" WHERE "space_id" = %i;
+    ]], box.space.V.id), {
          -- <analyzeD-1.11>

          -- <analyzeD-1.11>
@@ -183,9 +183,9 @@ test:do_catchsql_test(

  test:do_execsql_test(
      "analyzeD-1.13",
-    [[
-        SELECT * FROM "_sql_stat4" WHERE "tbl" = 'v';
-    ]], {
+    string.format([[
+        SELECT * FROM "_sql_stat4" WHERE "space_id" = %i;
+    ]], box.space.V.id), {
          -- <analyzeD-1.13>

          -- <analyzeD-1.13>
@@ -193,9 +193,9 @@ test:do_execsql_test(

  test:do_execsql_test(
      "analyzeD-1.14",
-    [[
-        SELECT * FROM "_sql_stat1" WHERE "tbl" = 'v';
-    ]], {
+    string.format([[
+        SELECT * FROM "_sql_stat1" WHERE "space_id" = %i;
+    ]], box.space.V.id), {
          -- <analyzeD-1.14>

          -- <analyzeD-1.14>
diff --git a/test/sql-tap/gh-3350-skip-scan.test.lua 
b/test/sql-tap/gh-3350-skip-scan.test.lua
index 301b04a..917c74c 100755
--- a/test/sql-tap/gh-3350-skip-scan.test.lua
+++ b/test/sql-tap/gh-3350-skip-scan.test.lua
@@ -77,9 +77,7 @@ test:do_execsql_test(
          }
  )

-test:do_execsql_test(
-        "skip-scan-1.4",
-        [[
+test:execsql([[
              DROP TABLE IF EXISTS t1;
              CREATE TABLE t1(id INTEGER PRIMARY KEY, a TEXT, b INT, c 
INT, d INT);
              CREATE INDEX t1abc ON t1(a,b,c);
@@ -96,10 +94,17 @@ test:do_execsql_test(
              ANALYZE;
              DELETE FROM "_sql_stat1";
              DELETE FROM "_sql_stat4";
-            INSERT INTO "_sql_stat1" VALUES('T1','T1ABC','10000 5000 
2000 10');
+        ]]
+)
+
+test:do_execsql_test(
+        "skip-scan-1.4",
+        string.format(
+        [[
+            INSERT INTO "_sql_stat1" VALUES(%i,%i,'10000 5000 2000 10');
              ANALYZE t2;
              SELECT a,b,c,d FROM t1 WHERE b=345;
-        ]], {
+        ]], box.space.T1.id, box.space.T1.index['T1ABC'].id), {
              "abc", 345, 7, 8, "def", 345, 9, 10
          }
  )
diff --git a/test/sql/sql-statN-index-drop.result 
b/test/sql/sql-statN-index-drop.result
index a751eca..8e8a1b2 100644
--- a/test/sql/sql-statN-index-drop.result
+++ b/test/sql/sql-statN-index-drop.result
@@ -31,36 +31,36 @@ box.sql.execute("ANALYZE;")
  ---
  ...
  -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
  ---
-- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
+  - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
  ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')
  ---
-- - ['T1', 'I1', '1 1']
-  - ['T1', 'T1', '1 1']
-  - ['T2', 'I1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - [0, '1 1']
+  - [1, '1 1']
+  - [0, '1 1']
+  - [1, '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\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
  ---
-- - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - [0, '1', '0', '0', !!binary kQE=]
+  - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
  ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')
  ---
-- - ['T1', 'T1', '1 1']
-  - ['T2', 'I1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - [0, '1 1']
+  - [0, '1 1']
+  - [1, '1 1']
  ...
  --Cleaning up.
  box.sql.execute("DROP TABLE t1;")
@@ -93,36 +93,36 @@ box.sql.execute("ANALYZE;")
  ---
  ...
  -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
  ---
-- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
+  - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
  ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')
  ---
-- - ['T1', 'I1', '1 1']
-  - ['T1', 'T1', '1 1']
-  - ['T2', 'I1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - [0, '1 1']
+  - [1, '1 1']
+  - [0, '1 1']
+  - [1, '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\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
  ---
-- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
+  - [0, '1', '0', '0', !!binary kQE=]
  ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')
  ---
-- - ['T1', 'I1', '1 1']
-  - ['T1', 'T1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - [0, '1 1']
+  - [1, '1 1']
+  - [0, '1 1']
  ...
  --Cleaning up.
  box.sql.execute("DROP TABLE t1;")
diff --git a/test/sql/sql-statN-index-drop.test.lua 
b/test/sql/sql-statN-index-drop.test.lua
index fe7e15b..af13c8c 100644
--- a/test/sql/sql-statN-index-drop.test.lua
+++ b/test/sql/sql-statN-index-drop.test.lua
@@ -14,15 +14,15 @@ box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
  box.sql.execute("ANALYZE;")

  -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
+box.sql.execute('SELECT "index_id","stat" 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\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')

  --Cleaning up.
  box.sql.execute("DROP TABLE t1;")
@@ -41,15 +41,15 @@ box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
  box.sql.execute("ANALYZE;")

  -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
+box.sql.execute('SELECT "index_id","stat" 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\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')

  --Cleaning up.
  box.sql.execute("DROP TABLE t1;")
diff --git a/test/sql/upgrade.result b/test/sql/upgrade.result
index 5e7d851..9326209 100644
--- a/test/sql/upgrade.result
+++ b/test/sql/upgrade.result
@@ -30,14 +30,15 @@ box.space._space.index['name']:get('_trigger')
  ...
  box.space._space.index['name']:get('_sql_stat1')
  ---
-- [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-      'type': 'string'}, {'name': 'stat', 'type': 'string'}]]
+- [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'space_id', 'type': 
'unsigned'},
+    {'name': 'index_id', 'type': 'unsigned'}, {'name': 'stat', 'type': 
'string'}]]
  ...
  box.space._space.index['name']:get('_sql_stat4')
  ---
-- [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-      'type': 'string'}, {'name': 'neq', 'type': 'string'}, {'name': 
'nlt', 'type': 'string'},
-    {'name': 'ndlt', 'type': 'string'}, {'name': 'sample', 'type': 
'scalar'}]]
+- [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'space_id', 'type': 
'unsigned'},
+    {'name': 'index_id', 'type': 'unsigned'}, {'name': 'neq', 'type': 
'string'}, {
+      'name': 'nlt', 'type': 'string'}, {'name': 'ndlt', 'type': 
'string'}, {'name': 'sample',
+      'type': 'scalar'}]]
  ...
  box.space._index:get({box.space._space.index['name']:get('_trigger').id, 0})
  ---
@@ -45,12 +46,12 @@ 
box.space._index:get({box.space._space.index['name']:get('_trigger').id, 0})
  ...
  box.space._index:get({box.space._space.index['name']:get('_sql_stat1').id, 0})
  ---
-- [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string']]]
+- [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned']]]
  ...
  box.space._index:get({box.space._space.index['name']:get('_sql_stat4').id, 0})
  ---
-- [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string'], [5,
-      'scalar']]]
+- [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned'],
+    [5, 'scalar']]]
  ...
  box.space._schema:format()
  ---


[-- Attachment #2: Type: text/html, Size: 102109 bytes --]

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

* [tarantool-patches] Re: [PATCH v2 1/1] sql: hold in stat tables space/index id instead of name
  2018-10-10 17:17           ` Imeev Mergen
@ 2018-10-11 11:41             ` n.pettik
  2018-10-11 14:56               ` Imeev Mergen
  0 siblings, 1 reply; 17+ messages in thread
From: n.pettik @ 2018-10-11 11:41 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen

[-- Attachment #1: Type: text/plain, Size: 2043 bytes --]


>> Please, next time attach intermediate diff (i.e. diff between two versions) or
>> inline each hunk as answer to comment. It is quite complicated to review
>> full patch (especially when it comes to patches containing hundreds of lines)
>> each time.
>> 
>> https://travis-ci.org/tarantool/tarantool/jobs/436232731 <https://travis-ci.org/tarantool/tarantool/jobs/436232731>
>> 
>> box/sql.test.lua fails on Travis. Please, fix it.
>> 
> Now it is good on travis:
> https://travis-ci.org/tarantool/tarantool/builds/439704198 <https://travis-ci.org/tarantool/tarantool/builds/439704198>
> 
> Diff between two last versions:
> 
> diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
> index 95c516a..a886d8a 100644
> --- a/src/box/sql/analyze.c
> +++ b/src/box/sql/analyze.c
> @@ -797,13 +797,13 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space)
>      assert(space->index_count != 0);
>      struct Vdbe *v = sqlite3GetVdbe(parse);
>      assert(v != NULL);
> -    const char *tab_name = space_name(space);
> +    MAYBE_UNUSED const char *tab_name = space_name(space);
>      sqlite3VdbeAddOp4(v, OP_IteratorOpen, tab_cursor, 0, 0, (void *) space,
>                P4_SPACEPTR);
>      sqlite3VdbeAddOp2(v, OP_Integer, space->def->id, space_id_reg);
>      for (uint32_t j = 0; j < space->index_count; ++j) {
>          struct index *idx = space->index[j];
> -        const char *idx_name;
> +        MAYBE_UNUSED const char *idx_name;
>          /*
>           * Primary indexes feature automatically generated
>           * names. Thus, for the sake of clarity, use

Actually, this diff doesn’t look like fix of that failed test.
I guess it is simply flaky, so this time you get lucky and it is passed.
Did you checked that test-trace from Travis fails on fresh 2.0 as well?
Did you manage to understand the reason of failure? Otherwise there is
no guarantee that you patch is innocent in this situation.
Without any investigation I can give my approval on this patch.


[-- Attachment #2: Type: text/html, Size: 3545 bytes --]

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

* [tarantool-patches] Re: [PATCH v2 1/1] sql: hold in stat tables space/index id instead of name
  2018-10-11 11:41             ` n.pettik
@ 2018-10-11 14:56               ` Imeev Mergen
  2018-10-11 15:00                 ` n.pettik
  0 siblings, 1 reply; 17+ messages in thread
From: Imeev Mergen @ 2018-10-11 14:56 UTC (permalink / raw)
  To: n.pettik, tarantool-patches, Vladislav Shpilevoy

[-- Attachment #1: Type: text/plain, Size: 2283 bytes --]

Hello! Thank you for review! My answer below.


On 10/11/2018 02:41 PM, n.pettik wrote:
>
>>> Please, next time attach intermediate diff (i.e. diff between two versions) or
>>> inline each hunk as answer to comment. It is quite complicated to review
>>> full patch (especially when it comes to patches containing hundreds of lines)
>>> each time.
>>>
>>> https://travis-ci.org/tarantool/tarantool/jobs/436232731
>>>
>>> box/sql.test.lua fails on Travis. Please, fix it.
>>>
>> Now it is good on travis:
>> https://travis-ci.org/tarantool/tarantool/builds/439704198*
>>
>> Diff between two last versions:*
>>
>> diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
>> index 95c516a..a886d8a 100644
>> --- a/src/box/sql/analyze.c
>> +++ b/src/box/sql/analyze.c
>> @@ -797,13 +797,13 @@ vdbe_emit_analyze_space(struct Parse *parse, 
>> struct space *space)
>>      assert(space->index_count != 0);
>>      struct Vdbe *v = sqlite3GetVdbe(parse);
>>      assert(v != NULL);
>> -    const char *tab_name = space_name(space);
>> +    MAYBE_UNUSED const char *tab_name = space_name(space);
>>      sqlite3VdbeAddOp4(v, OP_IteratorOpen, tab_cursor, 0, 0, (void *) 
>> space,
>>                P4_SPACEPTR);
>>      sqlite3VdbeAddOp2(v, OP_Integer, space->def->id, space_id_reg);
>>      for (uint32_t j = 0; j < space->index_count; ++j) {
>>          struct index *idx = space->index[j];
>> -        const char *idx_name;
>> +        MAYBE_UNUSED const char *idx_name;
>>          /*
>>           * Primary indexes feature automatically generated
>>           * names. Thus, for the sake of clarity, use
>
> Actually, this diff doesn’t look like fix of that failed test.
> I guess it is simply flaky, so this time you get lucky and it is passed.
> Did you checked that test-trace from Travis fails on fresh 2.0 as well?
> Did you manage to understand the reason of failure? Otherwise there is
> no guarantee that you patch is innocent in this situation.
> Without any investigation I can give my approval on this patch.
>
I was able to reproduce this failure on current 2.0:
https://github.com/tarantool/tarantool/issues/3737

I think my patch do not affect this test in the way it showed in
error.


[-- Attachment #2: Type: text/html, Size: 4504 bytes --]

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

* [tarantool-patches] Re: [PATCH v2 1/1] sql: hold in stat tables space/index id instead of name
  2018-10-11 14:56               ` Imeev Mergen
@ 2018-10-11 15:00                 ` n.pettik
  2018-11-17 14:09                   ` Imeev Mergen
  0 siblings, 1 reply; 17+ messages in thread
From: n.pettik @ 2018-10-11 15:00 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen, Kirill Yukhin


>> Actually, this diff doesn’t look like fix of that failed test.
>> I guess it is simply flaky, so this time you get lucky and it is passed.
>> Did you checked that test-trace from Travis fails on fresh 2.0 as well?
>> Did you manage to understand the reason of failure? Otherwise there is
>> no guarantee that you patch is innocent in this situation.
>> Without any investigation I can give my approval on this patch.
>> 
> I was able to reproduce this failure on current 2.0:
> https://github.com/tarantool/tarantool/issues/3737
> 
> I think my patch do not affect this test in the way it showed in
> error.

Ok, now that's what I'm talking about. LGTM.

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

* [tarantool-patches] Re: [PATCH v2 1/1] sql: hold in stat tables space/index id instead of name
  2018-10-11 15:00                 ` n.pettik
@ 2018-11-17 14:09                   ` Imeev Mergen
  2018-11-19 10:27                     ` Vladislav Shpilevoy
  0 siblings, 1 reply; 17+ messages in thread
From: Imeev Mergen @ 2018-11-17 14:09 UTC (permalink / raw)
  To: tarantool-patches, Kirill Yukhin; +Cc: n.pettik, Vladislav Shpilevoy

[-- Attachment #1: Type: text/plain, Size: 83919 bytes --]

Hi! Rebased patch to current 2.1 branch.

https://github.com/tarantool/tarantool/issues/3242

https://github.com/tarantool/tarantool/tree/imeevma/gh-3242-in-stat-tables-hold-ids

On 10/11/18 6:00 PM, n.pettik wrote:
>>> Actually, this diff doesn’t look like fix of that failed test.
>>> I guess it is simply flaky, so this time you get lucky and it is passed.
>>> Did you checked that test-trace from Travis fails on fresh 2.0 as well?
>>> Did you manage to understand the reason of failure? Otherwise there is
>>> no guarantee that you patch is innocent in this situation.
>>> Without any investigation I can give my approval on this patch.
>>>
>> I was able to reproduce this failure on current 2.0:
>> https://github.com/tarantool/tarantool/issues/3737
>>
>> I think my patch do not affect this test in the way it showed in
>> error.
> Ok, now that's what I'm talking about. LGTM.
>

*Patch:*

commit b9792dfc183d7d0851f3492c8ba9e2f7c46fe385
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Fri Aug 31 12:37:48 2018 +0300

     sql: hold in stat tables space/index id instead of name

     To avoid problems with table and index renaming it is good idea
     to save ids of tables and indexes instead of their names. Ids of
     tables and indexes are fixed values.

     Closes #3242
     Closes #2962

diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap
index b7789d6..538e635 100644
Binary files a/src/box/bootstrap.snap and b/src/box/bootstrap.snap differ
diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
index 3d9acc9..3ce586b 100644
--- a/src/box/lua/upgrade.lua
+++ b/src/box/lua/upgrade.lua
@@ -550,11 +550,11 @@ local function upgrade_to_2_1_0()
      _index:insert{_trigger.id, 1, 'space_id', 'tree', { unique = false },
                    {{1, 'unsigned'}}}

-    local stat1_ft = {{name='tbl', type='string'},
-                      {name='idx', type='string'},
+    local stat1_ft = {{name='space_id', type='unsigned'},
+                      {name='index_id', type='unsigned'},
                        {name='stat', type='string'}}
-    local stat4_ft = {{name='tbl', type='string'},
-                      {name='idx', type='string'},
+    local stat4_ft = {{name='space_id', type='unsigned'},
+                      {name='index_id', type='unsigned'},
                        {name='neq', type='string'},
                        {name='nlt', type='string'},
                        {name='ndlt', type='string'},
@@ -566,7 +566,7 @@ local function upgrade_to_2_1_0()

      log.info("create index primary on _sql_stat1")
      _index:insert{box.schema.SQL_STAT1_ID, 0, 'primary', 'tree',
-                  {unique = true}, {{0, 'string'}, {1, 'string'}}}
+                  {unique = true}, {{0, 'unsigned'}, {1, 'unsigned'}}}

      log.info("create space _sql_stat4")
      _space:insert{box.schema.SQL_STAT4_ID, ADMIN, '_sql_stat4', 
'memtx', 0,
@@ -574,7 +574,7 @@ local function upgrade_to_2_1_0()

      log.info("create index primary on _sql_stat4")
      _index:insert{box.schema.SQL_STAT4_ID, 0, 'primary', 'tree',
-                  {unique = true}, {{0, 'string'}, {1, 'string'},
+                  {unique = true}, {{0, 'unsigned'}, {1, 'unsigned'},
                                      {5, 'scalar'}}}

      local fk_constr_ft = {{name='name', type='string'},
diff --git a/src/box/schema.cc b/src/box/schema.cc
index 8625d92..6472466 100644
--- a/src/box/schema.cc
+++ b/src/box/schema.cc
@@ -445,17 +445,17 @@ schema_init()
               &alter_space_on_replace_index, &on_stmt_begin_index);

      /* _sql_stat1 - a simpler statistics on space, seen in SQL. */
-    key_parts[0].fieldno = 0; /* space name */
-    key_parts[0].type = FIELD_TYPE_STRING;
-    key_parts[1].fieldno = 1; /* index name */
-    key_parts[1].type = FIELD_TYPE_STRING;
+    key_parts[0].fieldno = 0; /* space id */
+    key_parts[0].type = FIELD_TYPE_UNSIGNED;
+    key_parts[1].fieldno = 1; /* index id */
+    key_parts[1].type = FIELD_TYPE_UNSIGNED;
      sc_space_new(BOX_SQL_STAT1_ID, "_sql_stat1", key_parts, 2, NULL, 
NULL);

      /* _sql_stat4 - extensive statistics on space, seen in SQL. */
-    key_parts[0].fieldno = 0; /* space name */
-    key_parts[0].type = FIELD_TYPE_STRING;
-    key_parts[1].fieldno = 1; /* index name */
-    key_parts[1].type = FIELD_TYPE_STRING;
+    key_parts[0].fieldno = 0; /* space id */
+    key_parts[0].type = FIELD_TYPE_UNSIGNED;
+    key_parts[1].fieldno = 1; /* index id */
+    key_parts[1].type = FIELD_TYPE_UNSIGNED;
      key_parts[2].fieldno = 5; /* sample */
      key_parts[2].type = FIELD_TYPE_SCALAR;
      sc_space_new(BOX_SQL_STAT4_ID, "_sql_stat4", key_parts, 3, NULL, 
NULL);
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index e786c90..aa5d349 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -38,8 +38,8 @@
   *
   * The following system tables are or have been supported:
   *
- *    CREATE TABLE _sql_stat1(tbl, idx, stat);
- *    CREATE TABLE _sql_stat4(tbl, idx, nEq, nLt, nDLt, sample);
+ *    CREATE TABLE _sql_stat1(space_id, index_id, stat);
+ *    CREATE TABLE _sql_stat4(space_id, index_id, nEq, nLt, nDLt, sample);
   *
   * For most applications, _sql_stat1 provides all the statistics required
   * for the query planner to make good choices.
@@ -47,7 +47,7 @@
   * Format of _sql_stat1:
   *
   * There is normally one row per index, with the index identified by the
- * name in the idx column.  The tbl column is the name of the table to
+ * id in the index_id column.  The space_id column is the id of the 
space to
   * which the index belongs.  In each such row, the stat column will be
   * a string consisting of a list of integers.  The first integer in this
   * list is the number of rows in the index.  (This is the same as the
@@ -66,9 +66,9 @@
   * "unordered" keyword is present, then the query planner assumes that
   * the index is unordered and will not use the index for a range query.
   *
- * If the _sql_stat1.idx column is NULL, then the _sql_stat1.stat
+ * If the _sql_stat1.index_id column is NULL, then the _sql_stat1.stat
   * column contains a single integer which is the (estimated) number of
- * rows in the table identified by _sql_stat1.tbl.
+ * rows in the table identified by _sql_stat1.space_id.
   *
   * Format for _sql_stat4:
   *
@@ -78,9 +78,9 @@
   * queries.
   *
   * The _sql_stat4 table contains multiple entries for each index.
- * The idx column names the index and the tbl column is the table of the
- * index.  If the idx and tbl columns are the same, then the sample is
- * of the INTEGER PRIMARY KEY.  The sample column is a blob which is the
+ * The index_id column names the index and the space_id column is the 
space of
+ * the index. If the index_id is equal to 0, then the sample
+ * is of the INTEGER PRIMARY KEY.  The sample column is a blob which is the
   * binary encoding of a key from the index.  The nEq column is a
   * list of integers.  The first integer is the approximate number
   * of entries in the index whose left-most column exactly matches
@@ -122,10 +122,11 @@
   * created.
   *
   * @param parse Parsing context.
- * @param table_name Delete records of this table if specified.
+ * @param space_id Delete records of this table if id is not
+ *        BOX_ID_NIL.
   */
  static void
-vdbe_emit_stat_space_open(struct Parse *parse, const char *table_name)
+vdbe_emit_stat_space_open(struct Parse *parse, uint32_t space_id)
  {
      const char *stat_names[] = {"_sql_stat1", "_sql_stat4"};
      const uint32_t stat_ids[] = {BOX_SQL_STAT1_ID, BOX_SQL_STAT4_ID};
@@ -133,10 +134,9 @@ vdbe_emit_stat_space_open(struct Parse *parse, 
const char *table_name)
      assert(v != NULL);
      assert(sqlite3VdbeDb(v) == parse->db);
      for (uint i = 0; i < lengthof(stat_names); ++i) {
-        const char *space_name = stat_names[i];
-        if (table_name != NULL) {
-            vdbe_emit_stat_space_clear(parse, space_name, NULL,
-                           table_name);
+        if (space_id != BOX_ID_NIL) {
+            vdbe_emit_stat_space_clear(parse, stat_names[i],
+                           space_id, BOX_ID_NIL);
          } else {
              sqlite3VdbeAddOp1(v, OP_Clear, stat_ids[i]);
          }
@@ -780,10 +780,10 @@ vdbe_emit_analyze_space(struct Parse *parse, 
struct space *space)
      int key_reg = ++parse->nMem;
      /* Temporary use register. */
      int tmp_reg = ++parse->nMem;
-    /* Register containing table name. */
-    int tab_name_reg = ++parse->nMem;
-    /* Register containing index name. */
-    int idx_name_reg = ++parse->nMem;
+    /* Register containing space id. */
+    int space_id_reg = ++parse->nMem;
+    /* Register containing index id. */
+    int index_id_reg = ++parse->nMem;
      /* Value for the stat column of _sql_stat1. */
      int stat1_reg = ++parse->nMem;
      /* MUST BE LAST (see below). */
@@ -800,13 +800,13 @@ vdbe_emit_analyze_space(struct Parse *parse, 
struct space *space)
      assert(space->index_count != 0);
      struct Vdbe *v = sqlite3GetVdbe(parse);
      assert(v != NULL);
-    const char *tab_name = space_name(space);
+    MAYBE_UNUSED const char *tab_name = space_name(space);
      sqlite3VdbeAddOp4(v, OP_IteratorOpen, tab_cursor, 0, 0, (void *) 
space,
                P4_SPACEPTR);
-    sqlite3VdbeLoadString(v, tab_name_reg, space->def->name);
+    sqlite3VdbeAddOp2(v, OP_Integer, space->def->id, space_id_reg);
      for (uint32_t j = 0; j < space->index_count; ++j) {
          struct index *idx = space->index[j];
-        const char *idx_name;
+        MAYBE_UNUSED const char *idx_name;
          /*
           * Primary indexes feature automatically generated
           * names. Thus, for the sake of clarity, use
@@ -817,8 +817,8 @@ vdbe_emit_analyze_space(struct Parse *parse, struct 
space *space)
          else
              idx_name = idx->def->name;
          int part_count = idx->def->key_def->part_count;
-        /* Populate the register containing the index name. */
-        sqlite3VdbeLoadString(v, idx_name_reg, idx_name);
+        /* Populate the register containing the index id. */
+        sqlite3VdbeAddOp2(v, OP_Integer, idx->def->iid, index_id_reg);
          VdbeComment((v, "Analysis for %s.%s", tab_name, idx_name));
          /*
           * Pseudo-code for loop that calls stat_push():
@@ -993,9 +993,8 @@ vdbe_emit_analyze_space(struct Parse *parse, struct 
space *space)
          sqlite3VdbeAddOp2(v, OP_Next, idx_cursor, next_row_addr);
          /* Add the entry to the stat1 table. */
          callStatGet(v, stat4_reg, STAT_GET_STAT1, stat1_reg);
-        assert("BBB"[0] == AFFINITY_TEXT);
-        sqlite3VdbeAddOp4(v, OP_MakeRecord, tab_name_reg, 3, tmp_reg,
-                  "BBB", 0);
+        sqlite3VdbeAddOp4(v, OP_MakeRecord, space_id_reg, 3, tmp_reg,
+                  "DDB", 0);
          sqlite3VdbeAddOp4(v, OP_IdxInsert, tmp_reg, 0, 0,
                    (char *)stat1, P4_SPACEPTR);
          /* Add the entries to the stat4 table. */
@@ -1029,7 +1028,7 @@ vdbe_emit_analyze_space(struct Parse *parse, 
struct space *space)
          }
          sqlite3VdbeAddOp3(v, OP_MakeRecord, col_reg, part_count,
                    sample_reg);
-        sqlite3VdbeAddOp3(v, OP_MakeRecord, tab_name_reg, 6, tmp_reg);
+        sqlite3VdbeAddOp3(v, OP_MakeRecord, space_id_reg, 6, tmp_reg);
          sqlite3VdbeAddOp4(v, OP_IdxReplace, tmp_reg, 0, 0,
                    (char *)stat4, P4_SPACEPTR);
          /* P1==1 for end-of-loop. */
@@ -1070,7 +1069,7 @@ static void
  sql_analyze_database(struct Parse *parser)
  {
      sql_set_multi_write(parser, false);
-    vdbe_emit_stat_space_open(parser, NULL);
+    vdbe_emit_stat_space_open(parser, BOX_ID_NIL);
      space_foreach(sql_space_foreach_analyze, (void *)parser);
      loadAnalysis(parser);
  }
@@ -1091,7 +1090,7 @@ vdbe_emit_analyze_table(struct Parse *parse, 
struct space *space)
       * There are two system spaces for statistics: _sql_stat1
       * and _sql_stat4.
       */
-    vdbe_emit_stat_space_open(parse, space->def->name);
+    vdbe_emit_stat_space_open(parse, space->def->id);
      vdbe_emit_analyze_space(parse, space);
      loadAnalysis(parse);
  }
@@ -1225,23 +1224,14 @@ analysis_loader(void *data, int argc, char 
**argv, char **unused)
      struct analysis_index_info *info = (struct analysis_index_info *) 
data;
      assert(info->stats != NULL);
      struct index_stat *stat = &info->stats[info->index_count++];
-    struct space *space = space_by_name(argv[0]);
-    if (space == NULL)
+    uint32_t space_id = atoll(argv[0]);
+    if (space_id == 0)
          return -1;
+    struct space *space = space_by_id(space_id);
+    assert(space != NULL);
      struct index *index;
-    uint32_t iid = box_index_id_by_name(space->def->id, argv[1],
-                        strlen(argv[1]));
-    /*
-     * Convention is if index's name matches with space's
-     * one, then it is primary index.
-     */
-    if (iid != BOX_ID_NIL) {
-        index = space_index(space, iid);
-    } else {
-        if (sqlite3_stricmp(argv[0], argv[1]) != 0)
-            return -1;
-        index = space_index(space, 0);
-    }
+    uint32_t iid = atoll(argv[1]);
+    index = space_index(space, iid);
      assert(index != NULL);
      /*
       * Additional field is used to describe total
@@ -1354,8 +1344,12 @@ sample_compare(const void *a, const void *b, void 
*arg)
   * Arguments must point to SQL statements that return
   * data equivalent to the following:
   *
- *    prepare: SELECT tbl,idx,count(*) FROM _sql_stat4 GROUP BY tbl,idx;
- *    load: SELECT tbl,idx,neq,nlt,ndlt,sample FROM _sql_stat4;
+ * prepare:
+ *     SELECT space_id,index_id,count(*) FROM _sql_stat4
+ *     GROUP BY space_id,index_id;
+ * load:
+ *     SELECT space_id,index_id,neq,nlt,ndlt,sample
+ *     FROM _sql_stat4;
   *
   * 'prepare' statement is used to allocate enough memory for
   * statistics (i.e. arrays lt, dt, dlt and avg_eq). 'load' query
@@ -1387,24 +1381,14 @@ load_stat_from_space(struct sqlite3 *db, const 
char *sql_select_prepare,
          goto finalize;
      uint32_t current_idx_count = 0;
      while (sqlite3_step(stmt) == SQLITE_ROW) {
-        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
-        if (space_name == NULL)
-            continue;
-        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
-        if (index_name == NULL)
-            continue;
-        uint32_t sample_count = sqlite3_column_int(stmt, 2);
-        struct space *space = space_by_name(space_name);
+        uint32_t space_id = sqlite3_column_int(stmt, 0);
+        assert(space_id != 0);
+        struct space *space = space_by_id(space_id);
          assert(space != NULL);
-        struct index *index;
-        uint32_t iid = box_index_id_by_name(space->def->id, index_name,
-                            strlen(index_name));
-        if (sqlite3_stricmp(space_name, index_name) == 0 &&
-            iid == BOX_ID_NIL)
-            index = space_index(space, 0);
-        else
-            index = space_index(space, iid);
+        uint32_t iid = sqlite3_column_int(stmt, 1);
+        struct index *index = space_index(space, iid);
          assert(index != NULL);
+        uint32_t sample_count = sqlite3_column_int(stmt, 2);
          uint32_t column_count = index->def->key_def->part_count;
          struct index_stat *stat = &stats[current_idx_count];
          stat->sample_field_count = column_count;
@@ -1456,24 +1440,12 @@ load_stat_from_space(struct sqlite3 *db, const 
char *sql_select_prepare,
      struct index *prev_index = NULL;
      current_idx_count = 0;
      while (sqlite3_step(stmt) == SQLITE_ROW) {
-        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
-        if (space_name == NULL)
-            continue;
-        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
-        if (index_name == NULL)
-            continue;
-        struct space *space = space_by_name(space_name);
+        uint32_t space_id = sqlite3_column_int(stmt, 0);
+        assert(space_id != 0);
+        struct space *space = space_by_id(space_id);
          assert(space != NULL);
-        struct index *index;
-        uint32_t iid = box_index_id_by_name(space->def->id, index_name,
-                            strlen(index_name));
-        if (iid != BOX_ID_NIL) {
-            index = space_index(space, iid);
-        } else {
-            if (sqlite3_stricmp(space_name, index_name) != 0)
-                return -1;
-            index = space_index(space, 0);
-        }
+        uint32_t iid = sqlite3_column_int(stmt, 1);
+        struct index *index = space_index(space, iid);
          assert(index != NULL);
          uint32_t column_count = index->def->key_def->part_count;
          if (index != prev_index) {
@@ -1537,24 +1509,13 @@ load_stat_to_index(struct sqlite3 *db, const 
char *sql_select_load,
          return -1;
      uint32_t current_idx_count = 0;
      while (sqlite3_step(stmt) == SQLITE_ROW) {
-        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
-        if (space_name == NULL)
-            continue;
-        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
-        if (index_name == NULL)
-            continue;
-        struct space *space = space_by_name(space_name);
+        uint32_t space_id = sqlite3_column_int(stmt, 0);
+        if (space_id == 0)
+            return -1;
+        struct space *space = space_by_id(space_id);
          assert(space != NULL);
-        struct index *index;
-        uint32_t iid = box_index_id_by_name(space->def->id, index_name,
-                            strlen(index_name));
-        if (iid != BOX_ID_NIL) {
-            index = space_index(space, iid);
-        } else {
-            if (sqlite3_stricmp(space_name, index_name) != 0)
-                return -1;
-            index = space_index(space, 0);
-        }
+        uint32_t iid = sqlite3_column_int(stmt, 1);
+        struct index *index = space_index(space, iid);
          assert(index != NULL);
          free(index->def->opts.stat);
          index->def->opts.stat = stats[current_idx_count++];
@@ -1697,7 +1658,7 @@ sql_analysis_load(struct sqlite3 *db)
      info.stats = stats;
      info.index_count = 0;
      const char *load_stat1 =
-        "SELECT \"tbl\",\"idx\",\"stat\" FROM \"_sql_stat1\"";
+        "SELECT \"space_id\",\"index_id\",\"stat\" FROM \"_sql_stat1\"";
      /* Load new statistics out of the _sql_stat1 table. */
      if (sqlite3_exec(db, load_stat1, analysis_loader, &info, 0) != 0)
          goto fail;
@@ -1710,10 +1671,10 @@ sql_analysis_load(struct sqlite3 *db)
       * statistics. Result rows are given in a form:
       * <table name>, <index name>, <count of samples>
       */
-    const char *init_query = "SELECT \"tbl\",\"idx\",count(*) FROM "
-                 "\"_sql_stat4\" GROUP BY \"tbl\",\"idx\"";
+    const char *init_query = "SELECT \"space_id\",\"index_id\",count(*) 
FROM "
+                 "\"_sql_stat4\" GROUP BY \"space_id\",\"index_id\"";
      /* Query for loading statistics into in-memory structs. */
-    const char *load_query = "SELECT \"tbl\",\"idx\",\"neq\",\"nlt\","
+    const char *load_query = "SELECT 
\"space_id\",\"index_id\",\"neq\",\"nlt\","
                   "\"ndlt\",\"sample\" FROM \"_sql_stat4\"";
      /* Load the statistics from the _sql_stat4 table. */
      if (load_stat_from_space(db, init_query, load_query, stats) != 0)
@@ -1757,8 +1718,8 @@ sql_analysis_load(struct sqlite3 *db)
       * Ordered query is needed to be sure that indexes come
       * in the same order as in previous SELECTs.
       */
-    const char *order_query = "SELECT \"tbl\",\"idx\" FROM "
-                  "\"_sql_stat4\" GROUP BY \"tbl\",\"idx\"";
+    const char *order_query = "SELECT \"space_id\",\"index_id\" FROM "
+                  "\"_sql_stat4\" GROUP BY \"space_id\",\"index_id\"";
      if (load_stat_to_index(db, order_query, heap_stats) != 0)
          goto fail;
      if (box_txn_commit() != 0)
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 5df7f0b..5a86801 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -1707,75 +1707,43 @@ sql_store_select(struct Parse *parse_context, 
struct Select *select)
      parse_context->parsed_ast.select = select_copy;
  }

-/**
- * Create expression record "@col_name = '@col_value'".
- *
- * @param parse The parsing context.
- * @param col_name Name of column.
- * @param col_value Name of row.
- * @retval not NULL on success.
- * @retval NULL on failure.
- */
-static struct Expr *
-sql_id_eq_str_expr(struct Parse *parse, const char *col_name,
-           const char *col_value)
-{
-    struct sqlite3 *db = parse->db;
-
-    struct Expr *col_name_expr = sqlite3Expr(db, TK_ID, col_name);
-    if (col_name_expr == NULL)
-        return NULL;
-    struct Expr *col_value_expr = sqlite3Expr(db, TK_STRING, col_value);
-    if (col_value_expr == NULL) {
-        sql_expr_delete(db, col_name_expr, false);
-        return NULL;
-    }
-    return sqlite3PExpr(parse, TK_EQ, col_name_expr, col_value_expr);
-}
-
  void
  vdbe_emit_stat_space_clear(struct Parse *parse, const char 
*stat_table_name,
-               const char *idx_name, const char *table_name)
+               uint32_t space_id, uint32_t index_id)
  {
-    assert(idx_name != NULL || table_name != NULL);
      struct sqlite3 *db = parse->db;
      assert(!db->mallocFailed);
      struct SrcList *src_list = sql_alloc_src_list(db);
      if (src_list != NULL)
          src_list->a[0].zName = sqlite3DbStrDup(db, stat_table_name);
-    struct Expr *where = NULL;
-    if (idx_name != NULL) {
-        struct Expr *expr = sql_id_eq_str_expr(parse, "idx", idx_name);
-        if (expr != NULL)
-            where = sqlite3ExprAnd(db, expr, where);
-    }
-    if (table_name != NULL) {
-        struct Expr *expr = sql_id_eq_str_expr(parse, "tbl", table_name);
-        if (expr != NULL)
-            where = sqlite3ExprAnd(db, expr, where);
-    }
-    /**
-     * On memory allocation error sql_table delete_from
-     * releases memory for its own.
-     */
-    sql_table_delete_from(parse, src_list, where);
+    struct Expr *expr = NULL;
+    struct Expr *col = sqlite3Expr(db, TK_ID, "space_id");
+    struct Expr *val = sqlite3ExprInteger(db, space_id);
+    if (col != NULL && val != NULL)
+        expr = sqlite3PExpr(parse, TK_EQ, col, val);
+    if (index_id != BOX_ID_NIL && expr != NULL) {
+        col = sqlite3Expr(db, TK_ID, "index_id");
+        val = sqlite3ExprInteger(db, index_id);
+        expr = sqlite3ExprAnd(db, sqlite3PExpr(parse, TK_EQ, col, val),
+                      expr);
+    }
+    sql_table_delete_from(parse, src_list, expr);
  }

  /**
   * Remove entries from the _sql_stat1 and _sql_stat4
   * system spaces after a DROP INDEX or DROP TABLE command.
   *
- * @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.
+ * @param parse The parsing context.
+ * @param space_id Id of table to be dropped or table that
+ *        contains index to be dropped.
+ * @param index_id Id of index to be dropped.
   */
  static void
-sql_clear_stat_spaces(struct Parse *parse, const char *table_name,
-              const char *idx_name)
+sql_clear_stat_spaces(struct Parse *parse, uint32_t space_id, uint32_t 
index_id)
  {
-    vdbe_emit_stat_space_clear(parse, "_sql_stat4", idx_name, table_name);
-    vdbe_emit_stat_space_clear(parse, "_sql_stat1", idx_name, table_name);
+    vdbe_emit_stat_space_clear(parse, "_sql_stat4", space_id, index_id);
+    vdbe_emit_stat_space_clear(parse, "_sql_stat1", space_id, index_id);
  }

  /**
@@ -1996,7 +1964,7 @@ sql_drop_table(struct Parse *parse_context, struct 
SrcList *table_name_list,
              goto exit_drop_table;
          }
      }
-    sql_clear_stat_spaces(parse_context, space_name, NULL);
+    sql_clear_stat_spaces(parse_context, space->def->id, BOX_ID_NIL);
      sql_code_drop_table(parse_context, space, is_view);

   exit_drop_table:
@@ -2845,7 +2813,8 @@ sql_drop_index(struct Parse *parse_context, struct 
SrcList *index_name_list,
       * But firstly, delete statistics since schema
       * changes after DDL.
       */
-    sql_clear_stat_spaces(parse_context, table_name, index->def->name);
+    sql_clear_stat_spaces(parse_context, space->def->id, index->def->iid);
+
      int record_reg = ++parse_context->nMem;
      int space_id_reg = ++parse_context->nMem;
      sqlite3VdbeAddOp2(v, OP_Integer, space->def->id, space_id_reg);
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index dbf58d9..2958a68 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -4926,11 +4926,13 @@ vdbe_emit_halt_with_presence_test(struct Parse 
*parser, int space_id,
   *
   * @param parse The parsing context.
   * @param stat_table_name System stat table name.
- * @param idx_name Index name.
- * @param table_name Table name.
+ * @param table_id Id of table of which analysis will be deleted.
+ * @param index_id Id of index of which analysis will be deleted.
+ *        If index_id is BOX_ID_NIL than analysis of all indexes
+ *        of selected table will be deleted.
   */
  void
  vdbe_emit_stat_space_clear(struct Parse *parse, const char 
*stat_table_name,
-               const char *idx_name, const char *table_name);
+               uint32_t table_id, uint32_t index_id);

  #endif                /* SQLITEINT_H */
diff --git a/test/box-py/bootstrap.result b/test/box-py/bootstrap.result
index 506aca3..b5850c2 100644
--- a/test/box-py/bootstrap.result
+++ b/test/box-py/bootstrap.result
@@ -74,11 +74,12 @@ box.space._space:select{}
          'type': 'unsigned'}]]
    - [340, 1, '_space_sequence', 'memtx', 0, {}, [{'name': 'id', 
'type': 'unsigned'},
        {'name': 'sequence_id', 'type': 'unsigned'}, {'name': 
'is_generated', 'type': 'boolean'}]]
-  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'stat', 'type': 'string'}]]
-  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'neq', 'type': 'string'}, {'name': 
'nlt', 'type': 'string'},
-      {'name': 'ndlt', 'type': 'string'}, {'name': 'sample', 'type': 
'scalar'}]]
+  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'space_id', 
'type': 'unsigned'},
+      {'name': 'index_id', 'type': 'unsigned'}, {'name': 'stat', 
'type': 'string'}]]
+  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'space_id', 
'type': 'unsigned'},
+      {'name': 'index_id', 'type': 'unsigned'}, {'name': 'neq', 'type': 
'string'},
+      {'name': 'nlt', 'type': 'string'}, {'name': 'ndlt', 'type': 
'string'}, {'name': 'sample',
+        'type': 'scalar'}]]
    - [356, 1, '_fk_constraint', 'memtx', 0, {}, [{'name': 'name', 
'type': 'string'},
        {'name': 'child_id', 'type': 'unsigned'}, {'name': 'parent_id', 
'type': 'unsigned'},
        {'name': 'is_deferred', 'type': 'boolean'}, {'name': 'match', 
'type': 'string'},
@@ -134,9 +135,9 @@ box.space._index:select{}
    - [330, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
    - [340, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
    - [340, 1, 'sequence', 'tree', {'unique': false}, [[1, 'unsigned']]]
-  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string']]]
-  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string'], [
-        5, 'scalar']]]
+  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned']]]
+  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned'],
+      [5, 'scalar']]]
    - [356, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'unsigned']]]
    - [356, 1, 'child_id', 'tree', {'unique': false}, [[1, 'unsigned']]]
  ...
diff --git a/test/box/access_misc.result b/test/box/access_misc.result
index 4ffeb38..83e9d3e 100644
--- a/test/box/access_misc.result
+++ b/test/box/access_misc.result
@@ -813,11 +813,12 @@ box.space._space:select()
          'type': 'unsigned'}]]
    - [340, 1, '_space_sequence', 'memtx', 0, {}, [{'name': 'id', 
'type': 'unsigned'},
        {'name': 'sequence_id', 'type': 'unsigned'}, {'name': 
'is_generated', 'type': 'boolean'}]]
-  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'stat', 'type': 'string'}]]
-  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'neq', 'type': 'string'}, {'name': 
'nlt', 'type': 'string'},
-      {'name': 'ndlt', 'type': 'string'}, {'name': 'sample', 'type': 
'scalar'}]]
+  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'space_id', 
'type': 'unsigned'},
+      {'name': 'index_id', 'type': 'unsigned'}, {'name': 'stat', 
'type': 'string'}]]
+  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'space_id', 
'type': 'unsigned'},
+      {'name': 'index_id', 'type': 'unsigned'}, {'name': 'neq', 'type': 
'string'},
+      {'name': 'nlt', 'type': 'string'}, {'name': 'ndlt', 'type': 
'string'}, {'name': 'sample',
+        'type': 'scalar'}]]
    - [356, 1, '_fk_constraint', 'memtx', 0, {}, [{'name': 'name', 
'type': 'string'},
        {'name': 'child_id', 'type': 'unsigned'}, {'name': 'parent_id', 
'type': 'unsigned'},
        {'name': 'is_deferred', 'type': 'boolean'}, {'name': 'match', 
'type': 'string'},
diff --git a/test/box/alter.result b/test/box/alter.result
index 9a1086e..86e8c3a 100644
--- a/test/box/alter.result
+++ b/test/box/alter.result
@@ -228,9 +228,9 @@ _index:select{}
    - [330, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
    - [340, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
    - [340, 1, 'sequence', 'tree', {'unique': false}, [[1, 'unsigned']]]
-  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string']]]
-  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string'], [
-        5, 'scalar']]]
+  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned']]]
+  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned'],
+      [5, 'scalar']]]
    - [356, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'unsigned']]]
    - [356, 1, 'child_id', 'tree', {'unique': false}, [[1, 'unsigned']]]
  ...
diff --git a/test/sql-tap/analyze1.test.lua b/test/sql-tap/analyze1.test.lua
index ea414e9..0eb4129 100755
--- a/test/sql-tap/analyze1.test.lua
+++ b/test/sql-tap/analyze1.test.lua
@@ -65,7 +65,7 @@ test:do_execsql_test(
  test:do_execsql_test(
      "analyze-1.7",
      [[
-        SELECT * FROM "_sql_stat1" WHERE "idx" IS NOT NULL
+        SELECT * FROM "_sql_stat1" WHERE "index_id" IS NOT NULL
      ]], {
          -- <analyze-1.7>
          -- </analyze-1.7>
@@ -84,7 +84,7 @@ test:do_catchsql_test(
  test:do_execsql_test(
      "analyze-1.9",
      [[
-        SELECT * FROM "_sql_stat1" WHERE "idx" IS NOT NULL
+        SELECT * FROM "_sql_stat1" WHERE "index_id" IS NOT NULL
      ]], {
          -- <analyze-1.9>
          -- </analyze-1.9>
@@ -119,7 +119,7 @@ test:do_execsql_test(
      [[
          CREATE INDEX t1i1 ON t1(a);
          ANALYZE t1;
-        SELECT * FROM "_sql_stat1" ORDER BY "idx";
+        SELECT * FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-2.1>
          -- </analyze-2.1>
@@ -130,7 +130,7 @@ test:do_execsql_test(
      [[
          CREATE INDEX t1i2 ON t1(b);
          ANALYZE t1;
-        SELECT * FROM "_sql_stat1" ORDER BY "idx";
+        SELECT * FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-2.2>
          -- </analyze-2.2>
@@ -141,7 +141,7 @@ test:do_execsql_test(
      [[
          CREATE INDEX t1i3 ON t1(a,b);
          ANALYZE;
-        SELECT * FROM "_sql_stat1" ORDER BY "idx";
+        SELECT * FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-2.3>
          -- </analyze-2.3>
@@ -157,10 +157,10 @@ test:do_execsql_test(
          INSERT INTO t1 VALUES(1, 1,2);
          INSERT INTO t1 VALUES(2, 1,3);
          ANALYZE t1;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.1>
-        "T1", "2 1", "T1I1", "2 2", "T1I2", "2 1", "T1I3", "2 2 1"
+        0, "2 1", 1, "2 2", 2, "2 1", 3, "2 2 1"
          -- </analyze-3.1>
      })

@@ -170,10 +170,10 @@ test:do_execsql_test(
          INSERT INTO t1 VALUES(3, 1,4);
          INSERT INTO t1 VALUES(4, 1,5);
          ANALYZE t1;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.2>
-        "T1", "4 1", "T1I1", "4 4", "T1I2", "4 1", "T1I3", "4 4 1"
+        0, "4 1", 1, "4 4", 2, "4 1", 3, "4 4 1"
          -- </analyze-3.2>
      })

@@ -182,10 +182,10 @@ test:do_execsql_test(
      [[
          INSERT INTO t1 (a,b) VALUES(2,5);
          ANALYZE;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.3>
-        "T1","5 1", "T1I1", "5 3", "T1I2", "5 2", "T1I3", "5 3 1"
+        0,"5 1", 1, "5 3", 2, "5 2", 3, "5 3 1"
          -- </analyze-3.3>
      })

@@ -198,10 +198,10 @@ test:do_execsql_test(
          CREATE INDEX t2i2 ON t2(b);
          CREATE INDEX t2i3 ON t2(a,b);
          ANALYZE;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.4>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 
1","T2I1","5 3","T2I2","5 2","T2I3","5 3 1"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",2,"5 2",3,"5 3 1",3,"5 3 1"
          -- </analyze-3.4>
      })

@@ -210,10 +210,10 @@ test:do_execsql_test(
      [[
          DROP INDEX t2i3 ON t2;;
          ANALYZE t1;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.5>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 
1","T2I1","5 3","T2I2","5 2"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",2,"5 2",3,"5 3 1"
          -- </analyze-3.5>
      })

@@ -221,10 +221,10 @@ test:do_execsql_test(
      "analyze-3.6",
      [[
          ANALYZE t2;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.6>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 
1","T2I1","5 3","T2I2","5 2"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",2,"5 2",3,"5 3 1"
          -- </analyze-3.6>
      })

@@ -233,10 +233,10 @@ test:do_execsql_test(
      [[
          DROP INDEX t2i2 ON t2;
          ANALYZE t2;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.7>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 
1","T2I1","5 3"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",3,"5 3 1"
          -- </analyze-3.7>
      })

@@ -250,7 +250,7 @@ test:do_execsql_test(
          CREATE INDEX t3i3 ON t3(d,b,c,a);
          DROP TABLE t1;
          DROP TABLE t2;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.8>
          -- </analyze-3.8>
@@ -260,10 +260,10 @@ test:do_execsql_test(
      "analyze-3.9",
      [[
          ANALYZE;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.9>
-        "T3","5 1","T3I1","5 3","T3I2","5 3 1 1 1","T3I3","5 5 2 1 1"
+        0,"5 1",1,"5 3",2,"5 3 1 1 1",3,"5 5 2 1 1"
          -- </analyze-3.9>
      })

@@ -277,7 +277,7 @@ test:do_execsql_test(
  --         INSERT INTO [silly " name] (a,b,c) VALUES(1, 2, 3);
  --         INSERT INTO [silly " name] (a,b,c) VALUES(4, 5, 6);
  --         ANALYZE;
---         SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+--         SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
  --     ]], {
  --         -- <analyze-3.10>
  --         "another foolish ' name", "2 1", "foolish ' name", "2 1 1", 
"t3i1", "5 3", "t3i2", "5 3 1 1 1", "t3i3", "5 5 2 1 1"
@@ -288,7 +288,7 @@ test:do_execsql_test(
  --     "analyze-3.11",
  --     [[
  --         DROP INDEX "foolish ' name";
---         SELECT "idx", "stat" FROM sqlite_stat1 ORDER BY "idx";
+--         SELECT "index_id", "stat" FROM sqlite_stat1 ORDER BY "index_id";
  --     ]], {
  --         -- <analyze-3.11>
  --         "another foolish ' name", "2 1", "t3i1", "5 3", "t3i2", "5 
3 1 1 1", "t3i3", "5 5 2 1 1"
@@ -299,7 +299,7 @@ test:do_execsql_test(
  --     "analyze-3.11",
  --     [[
  --         DROP TABLE "silly "" name";
---         SELECT "idx", "stat" FROM sqlite_stat1 ORDER BY "idx";
+--         SELECT "index_id", "stat" FROM sqlite_stat1 ORDER BY "index_id";
  --     ]], {
  --         -- <analyze-3.11>
  --         "t3i1", "5 3", "t3i2", "5 3 1 1 1", "t3i3", "5 5 2 1 1"
@@ -317,31 +317,33 @@ test:do_execsql_test(
          CREATE INDEX t4i2 ON t4(y);
          INSERT INTO t4 SELECT id,a,b,c FROM t3;
          ANALYZE;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-4.0>
-        "T3", "5 1", "T3I1", "5 3", "T3I2", "5 3 1 1 1", "T3I3", "5 5 2 
1 1", "T4", "5 1", "T4I1", "5 3", "T4I2", "5 2"
+        0, "5 1", 0, "5 1", 1, "5 3", 1, "5 3", 2, "5 3 1 1 1", 2, "5 
2", 3, "5 5 2 1 1"
          -- </analyze-4.0>
      })

+t4 = box.space.T4
+
  test:do_execsql_test(
      "analyze-4.1",
-    [[
+    string.format([[
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1" VALUES('t4', 't4i1', 'nonsense');
-        INSERT INTO "_sql_stat1" VALUES('t4', 't4i2', 
'432653287412874653284129847632');
+        INSERT INTO "_sql_stat1" VALUES(%i, %i, 'nonsense');
+        INSERT INTO "_sql_stat1" VALUES(%i, %i, 
'432653287412874653284129847632');
          SELECT * FROM t4 WHERE x = 1234;
-    ]], {
+    ]], t4.id, t4.index['T4I1'].id, t4.id, t4.index['T4I2'].id), {
          -- <analyze-4.1>
          -- </analyze-4.1>
      })

  test:do_execsql_test(
      "analyze-4.2",
-    [[
-        INSERT INTO "_sql_stat1" VALUES('t4', 'xyzzy', '0 1 2 3');
+    string.format([[
+        INSERT INTO "_sql_stat1" VALUES(%i, 12345, '0 1 2 3');
          SELECT * FROM t4 WHERE x = 1234;
-    ]], {
+    ]], t4.id), {
          -- <analyze-4.2>
          -- </analyze-4.2>
      })
@@ -367,20 +369,20 @@ test:do_execsql_test(
          INSERT INTO t3 (a,b,c,d) SELECT a+64, b+64, c+64, d+64 FROM t3;
          INSERT INTO t4 (x,y,z) SELECT a, b, c FROM t3;
          ANALYZE;
-        SELECT DISTINCT "tbl" FROM "_sql_stat1" ORDER BY 1;
+        SELECT COUNT(DISTINCT "space_id") FROM "_sql_stat1" ORDER BY 1;
      ]], {
          -- <analyze-5.0>
-        "T3", "T4"
+        2
          -- </analyze-5.0>
      })

  test:do_execsql_test(
      "analyze-5.0.1",
      [[
-        SELECT DISTINCT "idx" FROM "_sql_stat1" ORDER BY 1;
+        SELECT "index_id" FROM "_sql_stat1" ORDER BY 1;
      ]], {
          -- <analyze-5.0>
-        "T3", "T3I1", "T3I2", "T3I3", "T4", "T4I1", "T4I2"
+        0, 0, 1, 1, 2, 2, 3
          -- </analyze-5.0>
      })

@@ -389,20 +391,20 @@ stat = "_sql_stat4"
  test:do_execsql_test(
      "analyze-5.1",
      string.format([[
-            SELECT DISTINCT "idx" FROM "%s" ORDER BY 1;
-        ]], stat, stat), {
+            SELECT DISTINCT "index_id" FROM "%s" ORDER BY 1;
+        ]], stat), {
          -- <analyze-5.1>
-        "T3", "T3I1", "T3I2", "T3I3", "T4", "T4I1", "T4I2"
+        0, 1, 2, 3
          -- </analyze-5.1>
      })

  test:do_execsql_test(
      "analyze-5.1.1",
      string.format([[
-            SELECT DISTINCT "tbl" FROM "%s" ORDER BY 1;
-        ]], stat, stat), {
+            SELECT COUNT(DISTINCT "space_id") FROM "%s" ORDER BY 1;
+        ]], stat), {
          -- <analyze-5.1>
-        "T3", "T4"
+        2
          -- </analyze-5.1>
      })

@@ -411,40 +413,40 @@ test:do_execsql_test(
      [[
          DROP INDEX t3i2 ON t3;
          ANALYZE;
-        SELECT DISTINCT "idx" FROM "_sql_stat1" ORDER BY 1;
+        SELECT "index_id" FROM "_sql_stat1" ORDER BY 1;
      ]], {
          -- <analyze-5.2>
-        "T3", "T3I1", "T3I3", "T4", "T4I1", "T4I2"
+        0, 0, 1, 1, 2, 3
          -- </analyze-5.2>
      })

  test:do_execsql_test(
      "analyze-5.2.1",
      [[
-        SELECT DISTINCT "tbl" FROM "_sql_stat1" ORDER BY 1;
+        SELECT COUNT(DISTINCT "space_id") FROM "_sql_stat1" ORDER BY 1;
      ]], {
          -- <analyze-5.2>
-        "T3", "T4"
+        2
          -- </analyze-5.2>
      })

  test:do_execsql_test(
      "analyze-5.3",
      string.format([[
-            SELECT DISTINCT "idx" FROM "%s" ORDER BY 1;
+            SELECT DISTINCT "index_id" FROM "%s" ORDER BY 1;
          ]], stat, stat), {
          -- <analyze-5.3>
-        "T3", "T3I1", "T3I3", "T4", "T4I1", "T4I2"
+        0, 1, 2, 3
          -- </analyze-5.3>
      })

  test:do_execsql_test(
      "analyze-5.3.1",
      string.format([[
-            SELECT DISTINCT "tbl" FROM "%s" ORDER BY 1;
-        ]], stat, stat), {
+            SELECT COUNT(DISTINCT "space_id") FROM "%s" ORDER BY 1;
+        ]], stat, stat, stat), {
          -- <analyze-5.3>
-        "T3", "T4"
+        2
          -- </analyze-5.3>
      })

@@ -453,40 +455,40 @@ test:do_execsql_test(
      [[
          DROP TABLE IF EXISTS t3;
          ANALYZE;
-        SELECT DISTINCT "idx" FROM "_sql_stat1" ORDER BY 1;
+        SELECT DISTINCT "index_id" FROM "_sql_stat1" ORDER BY 1;
      ]], {
          -- <analyze-5.4>
-        "T4", "T4I1", "T4I2"
+        0, 1, 2
          -- </analyze-5.4>
      })

  test:do_execsql_test(
      "analyze-5.4.1",
      [[
-        SELECT DISTINCT "tbl" FROM "_sql_stat1" ORDER BY 1;
+        SELECT COUNT(DISTINCT "space_id") FROM "_sql_stat1" ORDER BY 1;
      ]], {
          -- <analyze-5.4>
-        "T4"
+        1
          -- </analyze-5.4>
      })

  test:do_execsql_test(
      "analyze-5.5",
      string.format([[
-            SELECT DISTINCT "idx" FROM "%s" ORDER BY 1;
+            SELECT DISTINCT "index_id" FROM "%s" ORDER BY 1;
          ]], stat), {
          -- <analyze-5.5>
-        "T4", "T4I1", "T4I2"
+        0, 1, 2
          -- </analyze-5.5>
      })

  test:do_execsql_test(
      "analyze-5.5.1",
      string.format([[
-            SELECT DISTINCT "tbl" FROM "%s" ORDER BY 1;
-        ]], stat), {
+            SELECT COUNT(DISTINCT "space_id") FROM "%s" ORDER BY 1;
+        ]], stat, stat), {
          -- <analyze-5.5>
-        "T4"
+        1
          -- </analyze-5.5>
      })

@@ -516,33 +518,35 @@ test:do_test(
      -- </analyze-6.1.1>
  })

+t1 = box.space.T1
+
  test:do_execsql_test(
      "analyze-6.1.2",
-    [[
-            SELECT * FROM "_sql_stat1" where "tbl"='T1' and "idx"='I1' 
LIMIT 1;
-    ]], {
+    string.format([[
+            SELECT "index_id", "stat" FROM "_sql_stat1" where 
"space_id"=%i and "index_id"=%i LIMIT 1;
+    ]], t1.id, t1.index['I1'].id), {
      -- <analyze-6.1.2>
-    "T1", "I1", "221 221 221 221 2"
+    1, "221 221 221 221 2"
      -- </analyze-6.1.2>
  })

  test:do_execsql_test(
      "analyze-6.1.3",
-    [[
-            SELECT "tbl", "idx", "neq", "nlt", "ndlt" FROM "_sql_stat4" 
where "tbl"='T1' and "idx"='I1' ORDER BY "nlt" LIMIT 1;
-    ]], {
+    string.format([[
+            SELECT "index_id", "neq", "nlt", "ndlt" FROM "_sql_stat4" 
where "space_id"=%i and "index_id"=%i ORDER BY "nlt" LIMIT 1;
+    ]], t1.id, t1.index['I1'].id), {
      -- <analyze-6.1.3>
-    "T1", "I1", "221 221 221 1", "0 0 0 10", "0 0 0 10"
+    1, "221 221 221 1", "0 0 0 10", "0 0 0 10"
      -- </analyze-6.1.3>
  })

  test:do_execsql_test(
      "analyze-6.1.4",
-    [[
-            SELECT "tbl", "idx", "neq", "nlt", "ndlt" FROM "_sql_stat4" 
where "tbl"='T1' and "idx"='I1' ORDER BY "nlt" DESC LIMIT 1;
-    ]], {
+    string.format([[
+            SELECT "index_id", "neq", "nlt", "ndlt" FROM "_sql_stat4" 
where "space_id"=%i and "index_id"=%i ORDER BY "nlt" DESC LIMIT 1;
+    ]], t1.id, t1.index['I1'].id), {
      -- <analyze-6.1.4>
-    "T1", "I1", "221 221 221 1", "0 0 0 99", "0 0 0 99"
+    1, "221 221 221 1", "0 0 0 99", "0 0 0 99"
      -- </analyze-6.1.4>
  })

diff --git a/test/sql-tap/analyze4.test.lua b/test/sql-tap/analyze4.test.lua
index f734423..4cb3ad2 100755
--- a/test/sql-tap/analyze4.test.lua
+++ b/test/sql-tap/analyze4.test.lua
@@ -49,15 +49,19 @@ test:do_test(
          -- </analyze4-1.0>
      })

+t1 = box.space.T1
+
  -- Verify that the t1b index shows that it does not narrow down the
  -- search any at all.
  --
  test:do_execsql_test(
      "analyze4-1.1",
-    [[ SELECT "idx", "stat" FROM "_sql_stat1" WHERE "tbl"='T1' ORDER BY 
"idx"; ]],
+    string.format([[
+        SELECT "index_id", "stat" FROM "_sql_stat1" WHERE "space_id"=%i 
ORDER BY "index_id";
+    ]], t1.id),
      {
          -- <analyze4-1.1>
-        "T1","128 1", "T1A", "128 1", "T1B", "128 128"
+        0,"128 1", 1, "128 1", 2, "128 128"
          -- </analyze4-1.1>
      })

@@ -68,16 +72,16 @@ test:do_execsql_test(
  test:do_test(
      "analyze4-1.2",
      function()
-        return test:execsql([[
-            UPDATE t1 SET b='x' WHERE a%2;
+        return test:execsql(string.format([[
+            UPDATE t1 SET b='x' WHERE a%%2;
  -- pragma vdbe_debug=1;
              ANALYZE;
  -- pragma vdbe_debug=0;
-            SELECT "idx", "stat" FROM "_sql_stat1" WHERE "tbl"='T1' 
ORDER BY "idx";
-        ]])
+            SELECT "index_id", "stat" FROM "_sql_stat1" WHERE 
"space_id"=%i ORDER BY "index_id";
+        ]], t1.id))
      end, {
          -- <analyze4-1.2>
-        "T1", "128 1", "T1A", "128 1", "T1B", "128 64"
+        0, "128 1", 1, "128 1", 2, "128 64"
          -- </analyze4-1.2>
      })

@@ -85,37 +89,42 @@ test:do_test(
  -- Create a multi-column indices using t1.b and verify that ANALYZE
  -- processes them correctly.
  --
+
+test:execsql([[
+    -- Tarantool doesn't suppoort ALTER stmt yet.
+    -- UPDATE t1 SET b=NULL;
+    --ALTER TABLE t1 ADD COLUMN c;
+    --ALTER TABLE t1 ADD COLUMN d;
+    -- So, re-create the table and its contents
+    DROP TABLE t1;
+    CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a INT ,b INT 
,c  INT DEFAULT NULL,d  INT DEFAULT NULL);
+    CREATE INDEX t1a ON t1(a);
+    CREATE INDEX t1b ON t1(b);
+    INSERT INTO t1 (a,b) VALUES(1,NULL);
+    INSERT INTO t1 (a,b) SELECT a+1, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+2, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+4, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+8, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+16, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+32, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+64, b FROM t1;
+
+    UPDATE t1 SET c=a/4, d=a/2;
+    CREATE INDEX t1bcd ON t1(b,c,d);
+    CREATE INDEX t1cdb ON t1(c,d,b);
+    CREATE INDEX t1cbd ON t1(c,b,d);
+    ANALYZE;
+]])
+
+t1 = box.space.T1
+
  test:do_execsql_test(
      "analyze4-1.3",
-    [[
-            -- Tarantool doesn't suppoort ALTER stmt yet.
-            -- UPDATE t1 SET b=NULL;
-            --ALTER TABLE t1 ADD COLUMN c;
-            --ALTER TABLE t1 ADD COLUMN d;
-            -- So, re-create the table and its contents
-            DROP TABLE t1;
-            CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a INT 
,b INT ,c  INT DEFAULT NULL,d  INT DEFAULT NULL);
-            CREATE INDEX t1a ON t1(a);
-            CREATE INDEX t1b ON t1(b);
-            INSERT INTO t1 (a,b) VALUES(1,NULL);
-            INSERT INTO t1 (a,b) SELECT a+1, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+2, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+4, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+8, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+16, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+32, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+64, b FROM t1;
-
-            UPDATE t1 SET c=a/4, d=a/2;
-            CREATE INDEX t1bcd ON t1(b,c,d);
-            CREATE INDEX t1cdb ON t1(c,d,b);
-            CREATE INDEX t1cbd ON t1(c,b,d);
-            ANALYZE;
-            SELECT "idx", "stat" FROM "_sql_stat1" WHERE "tbl"='T1' 
ORDER BY "idx";
-    ]]
-    , {
+    string.format([[
+            SELECT "index_id", "stat" FROM "_sql_stat1" WHERE 
"space_id"=%i ORDER BY "index_id";
+    ]], t1.id), {
          -- <analyze4-1.3>
-        "T1","128 1", "T1A", "128 1", "T1B", "128 128", "T1BCD", "128 
128 4 2", "T1CBD", "128 4 4 2", "T1CDB", "128 4 2 2"
+        0,"128 1", 1, "128 1", 2, "128 128", 3, "128 128 4 2", 4, "128 
4 2 2", 5, "128 4 4 2"
          -- </analyze4-1.3>
      })

diff --git a/test/sql-tap/analyze5.test.lua b/test/sql-tap/analyze5.test.lua
index e4a6d5e..a55ef48 100755
--- a/test/sql-tap/analyze5.test.lua
+++ b/test/sql-tap/analyze5.test.lua
@@ -115,10 +115,11 @@ test:do_test(

          -- DISTINCT idx, sample -- lindex(test_decode(sample),0)
          -- WHERE idx='t1u' ORDER BY nlt;
-        return test:execsql([[ SELECT DISTINCT msgpack_decode("sample")
-                                 FROM "_sql_stat4"
-                                 WHERE "idx"='T1U'
-                                 ORDER BY "nlt"]])
+        return test:execsql(string.format([[
+            SELECT DISTINCT msgpack_decode("sample")
+            FROM "_sql_stat4"
+            WHERE "index_id"= %i
+            ORDER BY "nlt"]], box.space.T1.index['T1U'].id))
      end, {
          -- <analyze5-1.0>
          "alpha", "bravo", "charlie", "delta"
@@ -144,10 +145,10 @@ test:do_test(
  test:do_test(
      "analyze5-1.2",
      function()
-        return test:execsql([[SELECT "idx", count(*) FROM "_sql_stat4" 
GROUP BY 1 ORDER BY 1]])
+        return test:execsql([[SELECT "index_id", count(*) FROM 
"_sql_stat4" GROUP BY 1 ORDER BY 1]])
      end, {
          -- <analyze5-1.2>
- "T1",24,"T1T",4,"T1U",4,"T1V",1,"T1W",4,"T1X",4,"T1Y",2,"T1Z",4
+        0,24,1,4,2,4,3,1,4,4,5,4,6,2,7,4
          -- </analyze5-1.2>
      })

diff --git a/test/sql-tap/analyze9.test.lua b/test/sql-tap/analyze9.test.lua
index 585e918..55b8d8c 100755
--- a/test/sql-tap/analyze9.test.lua
+++ b/test/sql-tap/analyze9.test.lua
@@ -1,6 +1,6 @@
  #!/usr/bin/env tarantool
  test = require("sqltester")
-test:plan(121)
+test:plan(120)

  testprefix = "analyze9"

@@ -62,29 +62,29 @@ msgpack_decode_sample = function(txt)
  end

  box.internal.sql_create_function("msgpack_decode_sample", "TEXT", 
msgpack_decode_sample)
+t1 = box.space.T1

  test:do_execsql_test(
      1.2,
-    [[
-        SELECT 
"tbl","idx","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM 
"_sql_stat4" where "idx" = 'I1';
-    ]], {
+    string.format([[
+        SELECT 
"index_id","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM 
"_sql_stat4" where "index_id" = %i;
+    ]], t1.index['I1'].id), {
          -- <1.2>
-        "T1", "I1", "1 1", "0 0", "0 0", "(0) (0)", "T1", "I1", "1 1", 
"1 1", "1 1", "(1) (1)",
-        "T1", "I1", "1 1", "2 2", "2 2", "(2) (2)", "T1", "I1", "1 1", 
"3 3", "3 3", "(3) (3)",
-        "T1", "I1", "1 1", "4 4", "4 4", "(4) (4)"
+        1, "1 1", "0 0", "0 0", "(0) (0)", 1, "1 1", "1 1", "1 1", "(1) 
(1)",
+        1, "1 1", "2 2", "2 2", "(2) (2)", 1, "1 1", "3 3", "3 3", "(3) 
(3)",
+        1, "1 1", "4 4", "4 4", "(4) (4)"
          -- </1.2>
      })

  test:do_execsql_test(
      1.3,
-    [[
-        SELECT 
"tbl","idx","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM 
"_sql_stat4" where "idx" = 'T1';
-
-    ]], {
+    string.format([[
+        SELECT 
"index_id","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM 
"_sql_stat4" where "index_id" = %i;
+    ]], t1.index[0].id), {
          -- <1.3>
-        'T1', 'T1', '1', '0', '0', '(0)', 'T1', 'T1', '1', '1', '1', 
'(1)',
-        'T1', 'T1', '1', '2', '2', '(2)', 'T1', 'T1', '1', '3', '3', 
'(3)',
-        'T1', 'T1', '1', '4', '4', '(4)'
+        0, '1', '0', '0', '(0)', 0, '1', '1', '1', '(1)',
+        0, '1', '2', '2', '(2)', 0, '1', '3', '3', '(3)',
+        0, '1', '4', '4', '(4)'
          -- </1.3>
      })

@@ -101,10 +101,10 @@ test:do_execsql_test(
          INSERT INTO t1 VALUES('text', 12);
          CREATE INDEX i1 ON t1(a, b);
          ANALYZE;
-        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4";
+        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4" ORDER 
BY 1;
      ]], {
          -- <2.1>
-        "text 12","some text 14","text","some text"
+        "some text","some text 14","text","text 12"
          -- </2.1>
      })

@@ -116,6 +116,7 @@ test:do_execsql_test(
          CREATE TABLE t2(id INTEGER PRIMARY KEY AUTOINCREMENT, a INT , 
b INT );
          CREATE INDEX i2 ON t2(a, b);
      ]])
+t2 = box.space.T2

  test:do_test(
      3.2,
@@ -184,10 +185,10 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      "3.3.2",
-    [[
+    string.format([[
          ANALYZE;
-        SELECT lrange("neq", 1, 1) FROM "_sql_stat4" WHERE "idx" = 'I2';
-    ]], generate_tens_str(24))
+        SELECT lrange("neq", 1, 1) FROM "_sql_stat4" WHERE "space_id" = 
%i and "index_id" = %i;
+    ]], t2.id, t2.index['I2'].id) , generate_tens_str(24))

  ---------------------------------------------------------------------------
  --
@@ -232,6 +233,7 @@ test:do_execsql_test(
          CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a TEXT , 
b INT , c INT);
          CREATE INDEX i1 ON t1(c, b, a);
      ]])
+t1 = box.space.T1

  insert_filler_rows_n = function(iStart, nCopy, nVal)
      for i = 0, nVal-1 do
@@ -282,10 +284,10 @@ test:do_execsql_test(

  test:do_execsql_test(
      4.3,
-    [[
+    string.format([[
          SELECT "neq", lrange("nlt", 1, 3), lrange("ndlt", 1, 3), 
lrange(msgpack_decode_sample("sample"), 1, 3)
-            FROM "_sql_stat4" WHERE "idx" = 'I1' ORDER BY "sample" 
LIMIT 16;
-    ]], {
+            FROM "_sql_stat4" WHERE "space_id" = %i and "index_id" = %i 
ORDER BY "sample" LIMIT 16;
+    ]], t1.id, t1.index['I1'].id), {
          -- <4.3>
          "10 10 10","0 0 0","0 0 0","0 0 0","10 10 10","10 10 10","1 1 
1","1 1 1","10 10 10","20 20 20",
          "2 2 2","2 2 2","10 10 10","30 30 30","3 3 3","3 3 3","10 10 
10","40 40 40","4 4 4","4 4 4",
@@ -299,10 +301,10 @@ test:do_execsql_test(

  test:do_execsql_test(
      4.4,
-    [[
+    string.format([[
          SELECT "neq", lrange("nlt", 1, 3), lrange("ndlt", 1, 3), 
lrange(msgpack_decode_sample("sample"), 1, 3)
-        FROM "_sql_stat4" WHERE "idx" = 'I1' ORDER BY "sample" DESC 
LIMIT 2;
-    ]], {
+        FROM "_sql_stat4" WHERE "space_id" = %i and "index_id" = %i 
ORDER BY "sample" DESC LIMIT 2;
+    ]], t1.id, t1.index['I1'].id), {
          -- <4.4>
          "2 1 1","295 296 296","120 122 125","201 4 h","5 3 1","290 290 
291","119 119 120","200 1 b"
          -- </4.4>
@@ -364,11 +366,12 @@ test:do_test(
  test:do_execsql_test(
      4.9,
      [[
-        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4";
+        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4" ORDER 
BY 1;
      ]], {
          -- <4.9>
-        "x", 1110, 2230, 2750, 3350, 4090, 4470, 4980, 5240, 5280, 
5290, 5590, 5920,
-        5930, 6220, 6710, 7000, 7710, 7830, 7970, 8890, 8950, 9240, 
9250, 9680
+        1110, 2230, 2750, 3350, 4090, 4470, 4980, 5240, 5280, 5290, 
5590, 5920,
+        5930, 6220, 6710, 7000, 7710, 7830, 7970, 8890, 8950, 9240, 
9250, 9680,
+        "x"
          -- </4.9>
      })

@@ -388,12 +391,14 @@ test:do_execsql_test(
          INSERT INTO t1 VALUES(null, 4, 4);
          INSERT INTO t1 VALUES(null, 5, 5);
          ANALYZE;
-        CREATE TABLE x1(tbl TEXT, idx TEXT , neq TEXT, nlt TEXT, ndlt 
TEXT, sample BLOB, PRIMARY KEY(tbl, idx, sample));
+        CREATE TABLE x1(tbl INT, idx INT , neq TEXT, nlt TEXT, ndlt 
TEXT, sample BLOB, PRIMARY KEY(tbl, idx, sample));
          INSERT INTO x1 SELECT * FROM "_sql_stat4";
          DELETE FROM "_sql_stat4";
          INSERT INTO "_sql_stat4" SELECT * FROM x1;
          ANALYZE;
      ]])
+t1 = box.space.T1
+x1 = box.space.X1

  test:do_execsql_test(
      6.2,
@@ -407,7 +412,7 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      7.1,
-    [[
+    string.format([[
          DROP TABLE IF EXISTS t1;
          CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a INT , 
b INT );
          CREATE INDEX i1 ON t1(a, b);
@@ -418,9 +423,9 @@ test:do_execsql_test(
          INSERT INTO t1 VALUES(null, 5, 5);
          ANALYZE;
          UPDATE "_sql_stat4" SET "sample" = '' WHERE "sample" =
-            (SELECT "sample" FROM "_sql_stat4" WHERE "tbl" = 't1' AND 
"idx" = 'i1' LIMIT 1);
+            (SELECT "sample" FROM "_sql_stat4" WHERE "space_id" = %i 
AND "index_id" = %i LIMIT 1);
          ANALYZE;
-    ]])
+    ]], t1.id, t1.index['I1'].id))

  -- Doesn't work due to the fact that in Tarantool rowid has been removed,
  -- and tbl, idx and sample have been united into primary key.
@@ -1014,15 +1019,15 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      15.1,
-    [[
+    string.format([[
          DROP TABLE IF EXISTS x1;
          CREATE TABLE x1(a  INT PRIMARY KEY, b INT , UNIQUE(a, b));
          INSERT INTO x1 VALUES(1, 2);
          INSERT INTO x1 VALUES(3, 4);
          INSERT INTO x1 VALUES(5, 6);
          ANALYZE;
-        INSERT INTO "_sql_stat4" VALUES('x1', 'abc', '', '', '', '');
-    ]])
+        INSERT INTO "_sql_stat4" VALUES(%i, 12345, '', '', '', '');
+    ]], x1.id))

  test:do_execsql_test(
      15.2,
@@ -1037,7 +1042,7 @@ test:do_execsql_test(
  test:do_execsql_test(
      15.3,
      [[
-        INSERT INTO "_sql_stat4" VALUES('42', '42', '42', '42', '42', 
'42');
+        INSERT INTO "_sql_stat4" VALUES(42, 42, '42', '42', '42', 42);
      ]])

  test:do_execsql_test(
@@ -1054,7 +1059,7 @@ test:do_execsql_test(
      15.7,
      [[
          ANALYZE;
-        UPDATE "_sql_stat1" SET "tbl" = 'no such tbl';
+        UPDATE "_sql_stat1" SET "space_id" = "space_id" + 123456;
      ]])

  test:do_execsql_test(
@@ -1067,25 +1072,6 @@ test:do_execsql_test(
          -- </15.8>
      })

--- Tarantool: this test seems to be useless. There's no reason
--- for these fields to be nullable.
--- test:do_execsql_test(
---    15.9,
---    [[
---        ANALYZE;
---        UPDATE "_sql_stat4" SET "neq" = NULL, "nlt" = NULL, "ndlt" = 
NULL;
---    ]])
-
-test:do_execsql_test(
-    15.10,
-    [[
-        SELECT * FROM x1;
-    ]], {
-        -- <15.10>
-        1, 2, 3, 4, 5, 6
-        -- </15.10>
-    })
-
  -- This is just for coverage....
  test:do_execsql_test(
      15.11,
@@ -1135,6 +1121,7 @@ test:do_test(
          -- <17.1>
          -- </17.1>
      })
+t1 = box.space.T1

  test:do_execsql_test(
      17.2,
@@ -1197,7 +1184,7 @@ test:do_test(
              test:execsql(string.format("INSERT INTO t1 VALUES(%s, 
0);", i))
          end
          test:execsql("ANALYZE")
-        return test:execsql([[ SELECT count(*) FROM "_sql_stat4" WHERE 
"idx" = 'I1'; ]])
+        return test:execsql(string.format('SELECT count(*) FROM 
"_sql_stat4" WHERE "space_id" = %i AND "index_id" = %i;', 
box.space.T1.id, box.space.T1.index['I1'].id))
      end, {
          -- <18.1>
          9
@@ -1239,12 +1226,13 @@ test:do_execsql_test(
          -- </20.2>
      })

+t1 = box.space.T1
  for i = 0, 15 do
      test:do_test(
          "20.3."..i,
          function()
              return test:execsql(string.format(
-                [[SELECT count(*) FROM "_sql_stat4" WHERE "idx" = 'I1' 
AND lrange(msgpack_decode_sample("sample"), 1, 1) = '%s']], i))
+                [[SELECT count(*) FROM "_sql_stat4" WHERE "space_id" = 
%i AND "index_id" = %i AND lrange(msgpack_decode_sample("sample"), 1, 1) 
= '%s']], t1.id, t1.index['I1'].id, i))
          end, {
              1
          })
diff --git a/test/sql-tap/analyzeC.test.lua b/test/sql-tap/analyzeC.test.lua
index 266e37e..31392c8 100755
--- a/test/sql-tap/analyzeC.test.lua
+++ b/test/sql-tap/analyzeC.test.lua
@@ -30,9 +30,7 @@ testprefix = "analyzeC"
  -- Baseline case.  Range queries work OK.  Indexes can be used for
  -- ORDER BY.

-test:do_execsql_test(
-    1.0,
-    [[
+test:execsql([[
          DROP TABLE IF EXISTS t1;
          CREATE TABLE t1(a  INT PRIMARY KEY, b INT , c INT , d INT );
          INSERT INTO t1(a,b,c,d) 
VALUES(1,1,2,3),(2,7,8,9),(3,4,5,6),(4,10,11,12),(5,4,8,12),(6,1,11,111);
@@ -40,10 +38,17 @@ test:do_execsql_test(
          CREATE INDEX t1c ON t1(c);
          ANALYZE;
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") 
VALUES('t1','t1b','12345 2'),('t1','t1c','12345 4');
+    ]]
+)
+t1 = box.space.T1
+
+test:do_execsql_test(
+    1.0,
+    string.format([[
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat") 
VALUES(%i,1,'12345 2'),(%i,2,'12345 4');
          ANALYZE;
          SELECT b,c,d, '#' FROM t1 WHERE b BETWEEN 3 AND 8 ORDER BY d;
-    ]], {
+    ]], t1.id, t1.id), {
          -- <1.0>
          4, 5, 6, "#", 7, 8, 9, "#", 4, 8, 12, "#"
          -- </1.0>
@@ -85,11 +90,12 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      2.0,
+    string.format(
      [[
-        UPDATE "_sql_stat1" SET "stat"='12345 2 unordered' WHERE 
"idx"='t1b';
+        UPDATE "_sql_stat1" SET "stat"='12345 2 unordered' WHERE 
"index_id"=%i;
          ANALYZE;
          SELECT b, c, d, '#' FROM t1 WHERE b BETWEEN 3 AND 8 ORDER BY d;
-    ]], {
+    ]], box.space.T1.index['T1B'].id), {
          -- <2.0>
          4, 5, 6, "#", 7, 8, 9, "#", 4, 8, 12, "#"
          -- </2.0>
@@ -130,11 +136,12 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      3.0,
+    string.format(
      [[
-        UPDATE "_sql_stat1" SET "stat"='12345 2 whatever=5 unordered 
xyzzy=11' WHERE "idx"='t1b';
+        UPDATE "_sql_stat1" SET "stat"='12345 2 whatever=5 unordered 
xyzzy=11' WHERE "index_id"=%i;
          ANALYZE;
          SELECT b, c, d, '#' FROM t1 WHERE b BETWEEN 3 AND 8 ORDER BY d;
-    ]], {
+    ]], box.space.T1.index['T1B'].id), {
          -- <3.0>
          4, 5, 6, "#", 7, 8, 9, "#", 4, 8, 12, "#"
          -- </3.0>
@@ -173,17 +180,22 @@ test:do_execsql_test(

  -- The sz=NNN parameter determines which index to scan
  --
-test:do_execsql_test(
-    4.0,
-    [[
+
+test:execsql([[
          DROP INDEX t1b ON t1;
          CREATE INDEX t1bc ON t1(b,c);
          CREATE INDEX t1db ON t1(d,b);
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") 
VALUES('t1','t1bc','12345 3 2 sz=10'),('t1','t1db','12345 3 2 sz=20');
+    ]]
+)
+
+test:do_execsql_test(
+    4.0,
+    string.format([[
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat") 
VALUES(%i,%i,'12345 3 2 sz=10'),(%i,%i,'12345 3 2 sz=20');
          ANALYZE;
          SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
          -- <4.0>
          6
          -- </4.0>
@@ -201,12 +213,13 @@ test:do_execsql_test(

  test:do_execsql_test(
      4.2,
+    string.format(
      [[
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") 
VALUES('t1','t1bc','12345 3 2 sz=20'),('t1','t1db','12345 3 2 sz=10');
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat") 
VALUES(%i,%i,'12345 3 2 sz=20'),(%i,%i,'12345 3 2 sz=10');
          ANALYZE;
          SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
          -- <4.2>
          6
          -- </4.2>
@@ -227,14 +240,14 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      5.0,
-    [[
+    string.format([[
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat")
-          VALUES('t1','t1bc','12345 3 2 x=5 sz=10 y=10'),
-                ('t1','t1db','12345 3 2 whatever sz=20 junk');
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat")
+          VALUES(%i,%i,'12345 3 2 x=5 sz=10 y=10'),
+                (%i,%i,'12345 3 2 whatever sz=20 junk');
          ANALYZE;
          SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
          -- <5.0>
          6
          -- </5.0>
@@ -253,12 +266,12 @@ test:do_execsql_test(

  test:do_execsql_test(
      5.2,
-    [[
+    string.format([[
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") 
VALUES('t1','t1db','12345 3 2 x=5 sz=10 y=10'), ('t1','t1bc','12345 3 2 
whatever sz=20 junk');
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat") 
VALUES(%i,%i,'12345 3 2 x=5 sz=10 y=10'), (%i,%i,'12345 3 2 whatever 
sz=20 junk');
          ANALYZE;
          SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
          -- <5.2>
          6
          -- </5.2>
diff --git a/test/sql-tap/analyzeD.test.lua b/test/sql-tap/analyzeD.test.lua
index 4bce88b..55f6e9d 100755
--- a/test/sql-tap/analyzeD.test.lua
+++ b/test/sql-tap/analyzeD.test.lua
@@ -153,9 +153,9 @@ test:do_catchsql_test(

  test:do_execsql_test(
      "analyzeD-1.10",
-    [[
-        SELECT * FROM "_sql_stat4" WHERE "tbl" = 'v';
-    ]], {
+    string.format([[
+        SELECT * FROM "_sql_stat4" WHERE "space_id" = %i;
+    ]], box.space.V.id), {
          -- <analyzeD-1.10>

          -- <analyzeD-1.10>
@@ -163,9 +163,9 @@ test:do_execsql_test(

  test:do_execsql_test(
      "analyzeD-1.11",
-    [[
-        SELECT * FROM "_sql_stat1" WHERE "tbl" = 'v';
-    ]], {
+    string.format([[
+        SELECT * FROM "_sql_stat1" WHERE "space_id" = %i;
+    ]], box.space.V.id), {
          -- <analyzeD-1.11>

          -- <analyzeD-1.11>
@@ -183,9 +183,9 @@ test:do_catchsql_test(

  test:do_execsql_test(
      "analyzeD-1.13",
-    [[
-        SELECT * FROM "_sql_stat4" WHERE "tbl" = 'v';
-    ]], {
+    string.format([[
+        SELECT * FROM "_sql_stat4" WHERE "space_id" = %i;
+    ]], box.space.V.id), {
          -- <analyzeD-1.13>

          -- <analyzeD-1.13>
@@ -193,9 +193,9 @@ test:do_execsql_test(

  test:do_execsql_test(
      "analyzeD-1.14",
-    [[
-        SELECT * FROM "_sql_stat1" WHERE "tbl" = 'v';
-    ]], {
+    string.format([[
+        SELECT * FROM "_sql_stat1" WHERE "space_id" = %i;
+    ]], box.space.V.id), {
          -- <analyzeD-1.14>

          -- <analyzeD-1.14>
diff --git a/test/sql-tap/gh-3350-skip-scan.test.lua 
b/test/sql-tap/gh-3350-skip-scan.test.lua
index 4cecfe0..640075e 100755
--- a/test/sql-tap/gh-3350-skip-scan.test.lua
+++ b/test/sql-tap/gh-3350-skip-scan.test.lua
@@ -77,9 +77,7 @@ test:do_execsql_test(
          }
  )

-test:do_execsql_test(
-        "skip-scan-1.4",
-        [[
+test:execsql([[
              DROP TABLE IF EXISTS t1;
              CREATE TABLE t1(id INTEGER PRIMARY KEY, a TEXT, b INT, c 
INT, d INT);
              CREATE INDEX t1abc ON t1(a,b,c);
@@ -96,10 +94,17 @@ test:do_execsql_test(
              ANALYZE;
              DELETE FROM "_sql_stat1";
              DELETE FROM "_sql_stat4";
-            INSERT INTO "_sql_stat1" VALUES('T1','T1ABC','10000 5000 
2000 10');
+        ]]
+)
+
+test:do_execsql_test(
+        "skip-scan-1.4",
+        string.format(
+        [[
+            INSERT INTO "_sql_stat1" VALUES(%i,%i,'10000 5000 2000 10');
              ANALYZE t2;
              SELECT a,b,c,d FROM t1 WHERE b=345;
-        ]], {
+        ]], box.space.T1.id, box.space.T1.index['T1ABC'].id), {
              "abc", 345, 7, 8, "def", 345, 9, 10
          }
  )
diff --git a/test/sql/sql-statN-index-drop.result 
b/test/sql/sql-statN-index-drop.result
index 7605951..b31dc81 100644
--- a/test/sql/sql-statN-index-drop.result
+++ b/test/sql/sql-statN-index-drop.result
@@ -31,36 +31,36 @@ box.sql.execute("ANALYZE;")
  ---
  ...
  -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
  ---
-- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
+  - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
  ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')
  ---
-- - ['T1', 'I1', '1 1']
-  - ['T1', 'T1', '1 1']
-  - ['T2', 'I1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - [0, '1 1']
+  - [1, '1 1']
+  - [0, '1 1']
+  - [1, '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\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
  ---
-- - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - [0, '1', '0', '0', !!binary kQE=]
+  - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
  ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')
  ---
-- - ['T1', 'T1', '1 1']
-  - ['T2', 'I1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - [0, '1 1']
+  - [0, '1 1']
+  - [1, '1 1']
  ...
  --Cleaning up.
  box.sql.execute("DROP TABLE t1;")
@@ -93,36 +93,36 @@ box.sql.execute("ANALYZE;")
  ---
  ...
  -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
  ---
-- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
+  - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
  ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')
  ---
-- - ['T1', 'I1', '1 1']
-  - ['T1', 'T1', '1 1']
-  - ['T2', 'I1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - [0, '1 1']
+  - [1, '1 1']
+  - [0, '1 1']
+  - [1, '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\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
  ---
-- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
+  - [0, '1', '0', '0', !!binary kQE=]
  ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')
  ---
-- - ['T1', 'I1', '1 1']
-  - ['T1', 'T1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - [0, '1 1']
+  - [1, '1 1']
+  - [0, '1 1']
  ...
  --Cleaning up.
  box.sql.execute("DROP TABLE t1;")
diff --git a/test/sql/sql-statN-index-drop.test.lua 
b/test/sql/sql-statN-index-drop.test.lua
index 35f2291..9ce77a5 100644
--- a/test/sql/sql-statN-index-drop.test.lua
+++ b/test/sql/sql-statN-index-drop.test.lua
@@ -14,15 +14,15 @@ box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
  box.sql.execute("ANALYZE;")

  -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
+box.sql.execute('SELECT "index_id","stat" 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\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')

  --Cleaning up.
  box.sql.execute("DROP TABLE t1;")
@@ -41,15 +41,15 @@ box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
  box.sql.execute("ANALYZE;")

  -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
+box.sql.execute('SELECT "index_id","stat" 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\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')

  --Cleaning up.
  box.sql.execute("DROP TABLE t1;")
diff --git a/test/sql/upgrade.result b/test/sql/upgrade.result
index 5e7d851..9326209 100644
--- a/test/sql/upgrade.result
+++ b/test/sql/upgrade.result
@@ -30,14 +30,15 @@ box.space._space.index['name']:get('_trigger')
  ...
  box.space._space.index['name']:get('_sql_stat1')
  ---
-- [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-      'type': 'string'}, {'name': 'stat', 'type': 'string'}]]
+- [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'space_id', 'type': 
'unsigned'},
+    {'name': 'index_id', 'type': 'unsigned'}, {'name': 'stat', 'type': 
'string'}]]
  ...
  box.space._space.index['name']:get('_sql_stat4')
  ---
-- [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-      'type': 'string'}, {'name': 'neq', 'type': 'string'}, {'name': 
'nlt', 'type': 'string'},
-    {'name': 'ndlt', 'type': 'string'}, {'name': 'sample', 'type': 
'scalar'}]]
+- [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'space_id', 'type': 
'unsigned'},
+    {'name': 'index_id', 'type': 'unsigned'}, {'name': 'neq', 'type': 
'string'}, {
+      'name': 'nlt', 'type': 'string'}, {'name': 'ndlt', 'type': 
'string'}, {'name': 'sample',
+      'type': 'scalar'}]]
  ...
  box.space._index:get({box.space._space.index['name']:get('_trigger').id, 0})
  ---
@@ -45,12 +46,12 @@ 
box.space._index:get({box.space._space.index['name']:get('_trigger').id, 0})
  ...
  box.space._index:get({box.space._space.index['name']:get('_sql_stat1').id, 0})
  ---
-- [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string']]]
+- [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned']]]
  ...
  box.space._index:get({box.space._space.index['name']:get('_sql_stat4').id, 0})
  ---
-- [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string'], [5,
-      'scalar']]]
+- [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned'],
+    [5, 'scalar']]]
  ...
  box.space._schema:format()
  ---


[-- Attachment #2: Type: text/html, Size: 106481 bytes --]

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

* [tarantool-patches] Re: [PATCH v2 1/1] sql: hold in stat tables space/index id instead of name
  2018-11-17 14:09                   ` Imeev Mergen
@ 2018-11-19 10:27                     ` Vladislav Shpilevoy
  2018-11-19 10:38                       ` Vladislav Shpilevoy
                                         ` (2 more replies)
  0 siblings, 3 replies; 17+ messages in thread
From: Vladislav Shpilevoy @ 2018-11-19 10:27 UTC (permalink / raw)
  To: Imeev Mergen, tarantool-patches, Kirill Yukhin; +Cc: n.pettik

Hi! Thanks for the rebase! See 4 comments below.

> commit b9792dfc183d7d0851f3492c8ba9e2f7c46fe385
> Author: Mergen Imeev <imeevma@gmail.com>
> Date:   Fri Aug 31 12:37:48 2018 +0300
> 
>      sql: hold in stat tables space/index id instead of name
> 
>      To avoid problems with table and index renaming it is good idea
>      to save ids of tables and indexes instead of their names. Ids of
>      tables and indexes are fixed values.
> 
>      Closes #3242
>      Closes #2962
> 
> diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap
> index b7789d6..538e635 100644
> Binary files a/src/box/bootstrap.snap and b/src/box/bootstrap.snap differ
> diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
> index 3d9acc9..3ce586b 100644
> --- a/src/box/lua/upgrade.lua
> +++ b/src/box/lua/upgrade.lua
> @@ -550,11 +550,11 @@ local function upgrade_to_2_1_0()
>       _index:insert{_trigger.id, 1, 'space_id', 'tree', { unique = false },
>                     {{1, 'unsigned'}}}
> 
> -    local stat1_ft = {{name='tbl', type='string'},
> -                      {name='idx', type='string'},
> +    local stat1_ft = {{name='space_id', type='unsigned'},
> +                      {name='index_id', type='unsigned'},
>                         {name='stat', type='string'}}
> -    local stat4_ft = {{name='tbl', type='string'},
> -                      {name='idx', type='string'},
> +    local stat4_ft = {{name='space_id', type='unsigned'},
> +                      {name='index_id', type='unsigned'},
>                         {name='neq', type='string'},
>                         {name='nlt', type='string'},
>                         {name='ndlt', type='string'}

1. Unfortunately, as I said earlier, you can not change the
past. 2.1.0 is already released, so you should create upgrade_to_2_2_0()
function and here update stat spaces format.

> diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
> index e786c90..aa5d349 100644
> --- a/src/box/sql/analyze.c
> +++ b/src/box/sql/analyze.c
> @@ -800,13 +800,13 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space)
>       assert(space->index_count != 0);
>       struct Vdbe *v = sqlite3GetVdbe(parse);
>       assert(v != NULL);
> -    const char *tab_name = space_name(space);
> +    MAYBE_UNUSED const char *tab_name = space_name(space);

2. Why unused?

>       sqlite3VdbeAddOp4(v, OP_IteratorOpen, tab_cursor, 0, 0, (void *) space,
>                 P4_SPACEPTR);
> -    sqlite3VdbeLoadString(v, tab_name_reg, space->def->name);
> +    sqlite3VdbeAddOp2(v, OP_Integer, space->def->id, space_id_reg);
>       for (uint32_t j = 0; j < space->index_count; ++j) {
>           struct index *idx = space->index[j];
> -        const char *idx_name;
> +        MAYBE_UNUSED const char *idx_name;

3. Why?

>           /*
>            * Primary indexes feature automatically generated
>            * names. Thus, for the sake of clarity, use
> @@ -1225,23 +1224,14 @@ analysis_loader(void *data, int argc, char **argv, char **unused)
>       struct analysis_index_info *info = (struct analysis_index_info *) data;
>       assert(info->stats != NULL);
>       struct index_stat *stat = &info->stats[info->index_count++];
> -    struct space *space = space_by_name(argv[0]);
> -    if (space == NULL)
> +    uint32_t space_id = atoll(argv[0]);
> +    if (space_id == 0)
>           return -1;
> +    struct space *space = space_by_id(space_id);
> +    assert(space != NULL);
>       struct index *index;
> -    uint32_t iid = box_index_id_by_name(space->def->id, argv[1],
> -                        strlen(argv[1]));
> -    /*
> -     * Convention is if index's name matches with space's
> -     * one, then it is primary index.
> -     */
> -    if (iid != BOX_ID_NIL) {
> -        index = space_index(space, iid);
> -    } else {
> -        if (sqlite3_stricmp(argv[0], argv[1]) != 0)
> -            return -1;
> -        index = space_index(space, 0);
> -    }
> +    uint32_t iid = atoll(argv[1]);
> +    index = space_index(space, iid);

4.1. Why in some places d oyou check result of
atoll and sqlite3_column_int, but in others do not?

>       assert(index != NULL);
>       /*
>        * Additional field is used to describe total
> @@ -1387,24 +1381,14 @@ load_stat_from_space(struct sqlite3 *db, const char *sql_select_prepare,
>           goto finalize;
>       uint32_t current_idx_count = 0;
>       while (sqlite3_step(stmt) == SQLITE_ROW) {
> -        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
> -        if (space_name == NULL)
> -            continue;
> -        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
> -        if (index_name == NULL)
> -            continue;
> -        uint32_t sample_count = sqlite3_column_int(stmt, 2);
> -        struct space *space = space_by_name(space_name);
> +        uint32_t space_id = sqlite3_column_int(stmt, 0);
> +        assert(space_id != 0);
> +        struct space *space = space_by_id(space_id);

4.2. Here you do not check, but ...

>           assert(space != NULL);
> -        struct index *index;
> @@ -1537,24 +1509,13 @@ load_stat_to_index(struct sqlite3 *db, const char *sql_select_load,
>           return -1;
>       uint32_t current_idx_count = 0;
>       while (sqlite3_step(stmt) == SQLITE_ROW) {
> -        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
> -        if (space_name == NULL)
> -            continue;
> -        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
> -        if (index_name == NULL)
> -            continue;
> -        struct space *space = space_by_name(space_name);
> +        uint32_t space_id = sqlite3_column_int(stmt, 0);
> +        if (space_id == 0)
> +            return -1;

4.3. ... here you check again.

> +        struct space *space = space_by_id(space_id);
>           assert(space != NULL);
> -        struct index *index;
> -        uint32_t iid = box_index_id_by_name(space->def->id, index_name,
> -                            strlen(index_name));
> -        if (iid != BOX_ID_NIL) {
> -            index = space_index(space, iid);
> -        } else {
> -            if (sqlite3_stricmp(space_name, index_name) != 0)
> -                return -1;
> -            index = space_index(space, 0);
> -        }
> +        uint32_t iid = sqlite3_column_int(stmt, 1);

4.4. And here you don't ...

> +        struct index *index = space_index(space, iid);
>           assert(index != NULL);
>           free(index->def->opts.stat);
>           index->def->opts.stat = stats[current_idx_count++];

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

* [tarantool-patches] Re: [PATCH v2 1/1] sql: hold in stat tables space/index id instead of name
  2018-11-19 10:27                     ` Vladislav Shpilevoy
@ 2018-11-19 10:38                       ` Vladislav Shpilevoy
  2018-11-21 16:00                       ` Konstantin Osipov
  2018-12-08 12:39                       ` Imeev Mergen
  2 siblings, 0 replies; 17+ messages in thread
From: Vladislav Shpilevoy @ 2018-11-19 10:38 UTC (permalink / raw)
  To: tarantool-patches



On 19/11/2018 13:27, Vladislav Shpilevoy wrote:
> Hi! Thanks for the rebase! See 4 comments below.
> 
>> commit b9792dfc183d7d0851f3492c8ba9e2f7c46fe385
>> Author: Mergen Imeev <imeevma@gmail.com>
>> Date:   Fri Aug 31 12:37:48 2018 +0300
>>
>>      sql: hold in stat tables space/index id instead of name
>>
>>      To avoid problems with table and index renaming it is good idea
>>      to save ids of tables and indexes instead of their names. Ids of
>>      tables and indexes are fixed values.
>>
>>      Closes #3242
>>      Closes #2962
>>
>> diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap
>> index b7789d6..538e635 100644
>> Binary files a/src/box/bootstrap.snap and b/src/box/bootstrap.snap differ
>> diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
>> index 3d9acc9..3ce586b 100644
>> --- a/src/box/lua/upgrade.lua
>> +++ b/src/box/lua/upgrade.lua
>> @@ -550,11 +550,11 @@ local function upgrade_to_2_1_0()
>>       _index:insert{_trigger.id, 1, 'space_id', 'tree', { unique = false },
>>                     {{1, 'unsigned'}}}
>>
>> -    local stat1_ft = {{name='tbl', type='string'},
>> -                      {name='idx', type='string'},
>> +    local stat1_ft = {{name='space_id', type='unsigned'},
>> +                      {name='index_id', type='unsigned'},
>>                         {name='stat', type='string'}}
>> -    local stat4_ft = {{name='tbl', type='string'},
>> -                      {name='idx', type='string'},
>> +    local stat4_ft = {{name='space_id', type='unsigned'},
>> +                      {name='index_id', type='unsigned'},
>>                         {name='neq', type='string'},
>>                         {name='nlt', type='string'},
>>                         {name='ndlt', type='string'}
> 
> 1. Unfortunately, as I said earlier, you can not change the
> past. 2.1.0 is already released, so you should create upgrade_to_2_2_0()
> function and here update stat spaces format.

Sorry, 2.1.1, not 2.2.0.

> 
>> diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
>> index e786c90..aa5d349 100644
>> --- a/src/box/sql/analyze.c
>> +++ b/src/box/sql/analyze.c
>> @@ -800,13 +800,13 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space)
>>       assert(space->index_count != 0);
>>       struct Vdbe *v = sqlite3GetVdbe(parse);
>>       assert(v != NULL);
>> -    const char *tab_name = space_name(space);
>> +    MAYBE_UNUSED const char *tab_name = space_name(space);
> 
> 2. Why unused?
> 
>>       sqlite3VdbeAddOp4(v, OP_IteratorOpen, tab_cursor, 0, 0, (void *) space,
>>                 P4_SPACEPTR);
>> -    sqlite3VdbeLoadString(v, tab_name_reg, space->def->name);
>> +    sqlite3VdbeAddOp2(v, OP_Integer, space->def->id, space_id_reg);
>>       for (uint32_t j = 0; j < space->index_count; ++j) {
>>           struct index *idx = space->index[j];
>> -        const char *idx_name;
>> +        MAYBE_UNUSED const char *idx_name;
> 
> 3. Why?
> 
>>           /*
>>            * Primary indexes feature automatically generated
>>            * names. Thus, for the sake of clarity, use
>> @@ -1225,23 +1224,14 @@ analysis_loader(void *data, int argc, char **argv, char **unused)
>>       struct analysis_index_info *info = (struct analysis_index_info *) data;
>>       assert(info->stats != NULL);
>>       struct index_stat *stat = &info->stats[info->index_count++];
>> -    struct space *space = space_by_name(argv[0]);
>> -    if (space == NULL)
>> +    uint32_t space_id = atoll(argv[0]);
>> +    if (space_id == 0)
>>           return -1;
>> +    struct space *space = space_by_id(space_id);
>> +    assert(space != NULL);
>>       struct index *index;
>> -    uint32_t iid = box_index_id_by_name(space->def->id, argv[1],
>> -                        strlen(argv[1]));
>> -    /*
>> -     * Convention is if index's name matches with space's
>> -     * one, then it is primary index.
>> -     */
>> -    if (iid != BOX_ID_NIL) {
>> -        index = space_index(space, iid);
>> -    } else {
>> -        if (sqlite3_stricmp(argv[0], argv[1]) != 0)
>> -            return -1;
>> -        index = space_index(space, 0);
>> -    }
>> +    uint32_t iid = atoll(argv[1]);
>> +    index = space_index(space, iid);
> 
> 4.1. Why in some places d oyou check result of
> atoll and sqlite3_column_int, but in others do not?
> 
>>       assert(index != NULL);
>>       /*
>>        * Additional field is used to describe total
>> @@ -1387,24 +1381,14 @@ load_stat_from_space(struct sqlite3 *db, const char *sql_select_prepare,
>>           goto finalize;
>>       uint32_t current_idx_count = 0;
>>       while (sqlite3_step(stmt) == SQLITE_ROW) {
>> -        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
>> -        if (space_name == NULL)
>> -            continue;
>> -        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
>> -        if (index_name == NULL)
>> -            continue;
>> -        uint32_t sample_count = sqlite3_column_int(stmt, 2);
>> -        struct space *space = space_by_name(space_name);
>> +        uint32_t space_id = sqlite3_column_int(stmt, 0);
>> +        assert(space_id != 0);
>> +        struct space *space = space_by_id(space_id);
> 
> 4.2. Here you do not check, but ...
> 
>>           assert(space != NULL);
>> -        struct index *index;
>> @@ -1537,24 +1509,13 @@ load_stat_to_index(struct sqlite3 *db, const char *sql_select_load,
>>           return -1;
>>       uint32_t current_idx_count = 0;
>>       while (sqlite3_step(stmt) == SQLITE_ROW) {
>> -        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
>> -        if (space_name == NULL)
>> -            continue;
>> -        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
>> -        if (index_name == NULL)
>> -            continue;
>> -        struct space *space = space_by_name(space_name);
>> +        uint32_t space_id = sqlite3_column_int(stmt, 0);
>> +        if (space_id == 0)
>> +            return -1;
> 
> 4.3. ... here you check again.
> 
>> +        struct space *space = space_by_id(space_id);
>>           assert(space != NULL);
>> -        struct index *index;
>> -        uint32_t iid = box_index_id_by_name(space->def->id, index_name,
>> -                            strlen(index_name));
>> -        if (iid != BOX_ID_NIL) {
>> -            index = space_index(space, iid);
>> -        } else {
>> -            if (sqlite3_stricmp(space_name, index_name) != 0)
>> -                return -1;
>> -            index = space_index(space, 0);
>> -        }
>> +        uint32_t iid = sqlite3_column_int(stmt, 1);
> 
> 4.4. And here you don't ...
> 
>> +        struct index *index = space_index(space, iid);
>>           assert(index != NULL);
>>           free(index->def->opts.stat);
>>           index->def->opts.stat = stats[current_idx_count++];
> 

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

* [tarantool-patches] Re: [PATCH v2 1/1] sql: hold in stat tables space/index id instead of name
  2018-11-19 10:27                     ` Vladislav Shpilevoy
  2018-11-19 10:38                       ` Vladislav Shpilevoy
@ 2018-11-21 16:00                       ` Konstantin Osipov
  2018-11-21 16:11                         ` Vladislav Shpilevoy
  2018-12-08 12:39                       ` Imeev Mergen
  2 siblings, 1 reply; 17+ messages in thread
From: Konstantin Osipov @ 2018-11-21 16:00 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Imeev Mergen, Kirill Yukhin, n.pettik

* Vladislav Shpilevoy <v.shpilevoy@tarantool.org> [18/11/19 14:56]:
> 1. Unfortunately, as I said earlier, you can not change the
> past. 2.1.0 is already released, so you should create upgrade_to_2_2_0()
> function and here update stat spaces format.

Please don't use 2.2.0 id yet, let's use 2.1.1 or 2.1.2

> 

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

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

* [tarantool-patches] Re: [PATCH v2 1/1] sql: hold in stat tables space/index id instead of name
  2018-11-21 16:00                       ` Konstantin Osipov
@ 2018-11-21 16:11                         ` Vladislav Shpilevoy
  0 siblings, 0 replies; 17+ messages in thread
From: Vladislav Shpilevoy @ 2018-11-21 16:11 UTC (permalink / raw)
  To: tarantool-patches, Konstantin Osipov
  Cc: Imeev Mergen, Kirill Yukhin, n.pettik



On 21/11/2018 19:00, Konstantin Osipov wrote:
> * Vladislav Shpilevoy <v.shpilevoy@tarantool.org> [18/11/19 14:56]:
>> 1. Unfortunately, as I said earlier, you can not change the
>> past. 2.1.0 is already released, so you should create upgrade_to_2_2_0()
>> function and here update stat spaces format.
> 
> Please don't use 2.2.0 id yet, let's use 2.1.1 or 2.1.2
> 
>>
> 
That is what I said in the next email.

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

* [tarantool-patches] Re: [PATCH v2 1/1] sql: hold in stat tables space/index id instead of name
  2018-11-19 10:27                     ` Vladislav Shpilevoy
  2018-11-19 10:38                       ` Vladislav Shpilevoy
  2018-11-21 16:00                       ` Konstantin Osipov
@ 2018-12-08 12:39                       ` Imeev Mergen
  2 siblings, 0 replies; 17+ messages in thread
From: Imeev Mergen @ 2018-12-08 12:39 UTC (permalink / raw)
  To: Vladislav Shpilevoy, tarantool-patches, n.pettik

[-- Attachment #1: Type: text/plain, Size: 91517 bytes --]

Hi! Thank you for review! New version and my answers below.

On 11/19/18 1:27 PM, Vladislav Shpilevoy wrote:
> Hi! Thanks for the rebase! See 4 comments below.
>
>> commit b9792dfc183d7d0851f3492c8ba9e2f7c46fe385
>> Author: Mergen Imeev <imeevma@gmail.com>
>> Date:   Fri Aug 31 12:37:48 2018 +0300
>>
>>      sql: hold in stat tables space/index id instead of name
>>
>>      To avoid problems with table and index renaming it is good idea
>>      to save ids of tables and indexes instead of their names. Ids of
>>      tables and indexes are fixed values.
>>
>>      Closes #3242
>>      Closes #2962
>>
>> diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap
>> index b7789d6..538e635 100644
>> Binary files a/src/box/bootstrap.snap and b/src/box/bootstrap.snap 
>> differ
>> diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
>> index 3d9acc9..3ce586b 100644
>> --- a/src/box/lua/upgrade.lua
>> +++ b/src/box/lua/upgrade.lua
>> @@ -550,11 +550,11 @@ local function upgrade_to_2_1_0()
>>       _index:insert{_trigger.id, 1, 'space_id', 'tree', { unique = 
>> false },
>>                     {{1, 'unsigned'}}}
>>
>> -    local stat1_ft = {{name='tbl', type='string'},
>> -                      {name='idx', type='string'},
>> +    local stat1_ft = {{name='space_id', type='unsigned'},
>> +                      {name='index_id', type='unsigned'},
>>                         {name='stat', type='string'}}
>> -    local stat4_ft = {{name='tbl', type='string'},
>> -                      {name='idx', type='string'},
>> +    local stat4_ft = {{name='space_id', type='unsigned'},
>> +                      {name='index_id', type='unsigned'},
>>                         {name='neq', type='string'},
>>                         {name='nlt', type='string'},
>>                         {name='ndlt', type='string'}
>
> 1. Unfortunately, as I said earlier, you can not change the
> past. 2.1.0 is already released, so you should create upgrade_to_2_2_0()
> function and here update stat spaces format.
Done.
>
>> diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
>> index e786c90..aa5d349 100644
>> --- a/src/box/sql/analyze.c
>> +++ b/src/box/sql/analyze.c
>> @@ -800,13 +800,13 @@ vdbe_emit_analyze_space(struct Parse *parse, 
>> struct space *space)
>>       assert(space->index_count != 0);
>>       struct Vdbe *v = sqlite3GetVdbe(parse);
>>       assert(v != NULL);
>> -    const char *tab_name = space_name(space);
>> +    MAYBE_UNUSED const char *tab_name = space_name(space);
>
> 2. Why unused?
It is used only in VdbeComment, so it will not be used in
RelWithDebInfoWError build.
>
>>       sqlite3VdbeAddOp4(v, OP_IteratorOpen, tab_cursor, 0, 0, (void 
>> *) space,
>>                 P4_SPACEPTR);
>> -    sqlite3VdbeLoadString(v, tab_name_reg, space->def->name);
>> +    sqlite3VdbeAddOp2(v, OP_Integer, space->def->id, space_id_reg);
>>       for (uint32_t j = 0; j < space->index_count; ++j) {
>>           struct index *idx = space->index[j];
>> -        const char *idx_name;
>> +        MAYBE_UNUSED const char *idx_name;
>
> 3. Why?
Same as previous.
>
>>           /*
>>            * Primary indexes feature automatically generated
>>            * names. Thus, for the sake of clarity, use
>> @@ -1225,23 +1224,14 @@ analysis_loader(void *data, int argc, char 
>> **argv, char **unused)
>>       struct analysis_index_info *info = (struct analysis_index_info 
>> *) data;
>>       assert(info->stats != NULL);
>>       struct index_stat *stat = &info->stats[info->index_count++];
>> -    struct space *space = space_by_name(argv[0]);
>> -    if (space == NULL)
>> +    uint32_t space_id = atoll(argv[0]);
>> +    if (space_id == 0)
>>           return -1;
>> +    struct space *space = space_by_id(space_id);
>> +    assert(space != NULL);
>>       struct index *index;
>> -    uint32_t iid = box_index_id_by_name(space->def->id, argv[1],
>> -                        strlen(argv[1]));
>> -    /*
>> -     * Convention is if index's name matches with space's
>> -     * one, then it is primary index.
>> -     */
>> -    if (iid != BOX_ID_NIL) {
>> -        index = space_index(space, iid);
>> -    } else {
>> -        if (sqlite3_stricmp(argv[0], argv[1]) != 0)
>> -            return -1;
>> -        index = space_index(space, 0);
>> -    }
>> +    uint32_t iid = atoll(argv[1]);
>> +    index = space_index(space, iid);
>
> 4.1. Why in some places d oyou check result of
> atoll and sqlite3_column_int, but in others do not?
Fixed. Added all checks.
>
>>       assert(index != NULL);
>>       /*
>>        * Additional field is used to describe total
>> @@ -1387,24 +1381,14 @@ load_stat_from_space(struct sqlite3 *db, 
>> const char *sql_select_prepare,
>>           goto finalize;
>>       uint32_t current_idx_count = 0;
>>       while (sqlite3_step(stmt) == SQLITE_ROW) {
>> -        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
>> -        if (space_name == NULL)
>> -            continue;
>> -        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
>> -        if (index_name == NULL)
>> -            continue;
>> -        uint32_t sample_count = sqlite3_column_int(stmt, 2);
>> -        struct space *space = space_by_name(space_name);
>> +        uint32_t space_id = sqlite3_column_int(stmt, 0);
>> +        assert(space_id != 0);
>> +        struct space *space = space_by_id(space_id);
>
> 4.2. Here you do not check, but ...
Fixed.
>
>>           assert(space != NULL);
>> -        struct index *index;
>> @@ -1537,24 +1509,13 @@ load_stat_to_index(struct sqlite3 *db, const 
>> char *sql_select_load,
>>           return -1;
>>       uint32_t current_idx_count = 0;
>>       while (sqlite3_step(stmt) == SQLITE_ROW) {
>> -        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
>> -        if (space_name == NULL)
>> -            continue;
>> -        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
>> -        if (index_name == NULL)
>> -            continue;
>> -        struct space *space = space_by_name(space_name);
>> +        uint32_t space_id = sqlite3_column_int(stmt, 0);
>> +        if (space_id == 0)
>> +            return -1;
>
> 4.3. ... here you check again.
Fixed.
>
>> +        struct space *space = space_by_id(space_id);
>>           assert(space != NULL);
>> -        struct index *index;
>> -        uint32_t iid = box_index_id_by_name(space->def->id, index_name,
>> -                            strlen(index_name));
>> -        if (iid != BOX_ID_NIL) {
>> -            index = space_index(space, iid);
>> -        } else {
>> -            if (sqlite3_stricmp(space_name, index_name) != 0)
>> -                return -1;
>> -            index = space_index(space, 0);
>> -        }
>> +        uint32_t iid = sqlite3_column_int(stmt, 1);
>
> 4.4. And here you don't ...
Fixed.
>
>> +        struct index *index = space_index(space, iid);
>>           assert(index != NULL);
>>           free(index->def->opts.stat);
>>           index->def->opts.stat = stats[current_idx_count++];


*New version:*

commit 5e1eb513b33ee8ab31901e8e0bf8d8f5a9442b9f
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Sun Dec 2 17:57:19 2018 +0300

     sql: hold in stat tables space/index id instead of name

     To avoid problems with table and index renaming it is good idea
     to save ids of tables and indexes instead of their names. Ids of
     tables and indexes are fixed values.

     Closes #3242
     Closes #2962

diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap
index d6cc821..22208c6 100644
Binary files a/src/box/bootstrap.snap and b/src/box/bootstrap.snap differ
diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
index 3d9acc9..2767f9b 100644
--- a/src/box/lua/upgrade.lua
+++ b/src/box/lua/upgrade.lua
@@ -613,6 +613,31 @@ local function upgrade_to_2_1_0()
      upgrade_priv_to_2_1_0()
  end

+--------------------------------------------------------------------------------
+-- Tarantool 2.1.1
+--------------------------------------------------------------------------------
+
+local function upgrade_sql_stat_to_2_1_1(space_id, parts)
+    local _sql_stat = box.space[space_id]
+
+    local stats = _sql_stat:select()
+    for _, v in pairs(stats) do _sql_stat:delete{v.tbl, v.idx} end
+
+    local format = _sql_stat:format()
+    format[1].name = 'space_id'
+    format[1].type = 'unsigned'
+    format[2].name = 'index_id'
+    format[2].type = 'unsigned'
+    _sql_stat.index.primary:alter{parts={3, 'string'}}
+    _sql_stat:format(format)
+    _sql_stat.index.primary:alter{parts=parts}
+end
+
+local function upgrade_to_2_1_1()
+    upgrade_sql_stat_to_2_1_1(box.schema.SQL_STAT1_ID, {1, 'unsigned', 
2, 'unsigned'})
+    upgrade_sql_stat_to_2_1_1(box.schema.SQL_STAT4_ID, {1, 'unsigned', 
2, 'unsigned', 6, 'scalar'})
+end
+
  local function get_version()
      local version = box.space._schema:get{'version'}
      if version == nil then
@@ -640,7 +665,8 @@ local function upgrade(options)
          {version = mkversion(1, 7, 7), func = upgrade_to_1_7_7, auto = 
true},
          {version = mkversion(1, 10, 0), func = upgrade_to_1_10_0, auto 
= true},
          {version = mkversion(1, 10, 2), func = upgrade_to_1_10_2, auto 
= true},
-        {version = mkversion(2, 1, 0), func = upgrade_to_2_1_0, auto = 
true}
+        {version = mkversion(2, 1, 0), func = upgrade_to_2_1_0, auto = 
true},
+        {version = mkversion(2, 1, 1), func = upgrade_to_2_1_1, auto = 
true}
      }

      for _, handler in ipairs(handlers) do
diff --git a/src/box/schema.cc b/src/box/schema.cc
index 8625d92..844b6c3 100644
--- a/src/box/schema.cc
+++ b/src/box/schema.cc
@@ -444,21 +444,19 @@ schema_init()
      sc_space_new(BOX_INDEX_ID, "_index", key_parts, 2,
               &alter_space_on_replace_index, &on_stmt_begin_index);

-    /* _sql_stat1 - a simpler statistics on space, seen in SQL. */
-    key_parts[0].fieldno = 0; /* space name */
+    /*
+     *_sql_stat1 - a simpler statistics on space, seen in SQL.
+     * The real index is defined in the snapshot.
+     */
+    key_parts[0].fieldno = 2;
      key_parts[0].type = FIELD_TYPE_STRING;
-    key_parts[1].fieldno = 1; /* index name */
-    key_parts[1].type = FIELD_TYPE_STRING;
-    sc_space_new(BOX_SQL_STAT1_ID, "_sql_stat1", key_parts, 2, NULL, NULL);
+    sc_space_new(BOX_SQL_STAT1_ID, "_sql_stat1", key_parts, 1, NULL, NULL);

-    /* _sql_stat4 - extensive statistics on space, seen in SQL. */
-    key_parts[0].fieldno = 0; /* space name */
-    key_parts[0].type = FIELD_TYPE_STRING;
-    key_parts[1].fieldno = 1; /* index name */
-    key_parts[1].type = FIELD_TYPE_STRING;
-    key_parts[2].fieldno = 5; /* sample */
-    key_parts[2].type = FIELD_TYPE_SCALAR;
-    sc_space_new(BOX_SQL_STAT4_ID, "_sql_stat4", key_parts, 3, NULL, NULL);
+    /*
+     * _sql_stat4 - extensive statistics on space, seen in SQL.
+     * The real index is defined in the snapshot.
+     */
+    sc_space_new(BOX_SQL_STAT4_ID, "_sql_stat4", key_parts, 1, NULL, NULL);

      /* _fk_сonstraint - foreign keys constraints. */
      key_parts[0].fieldno = 0; /* constraint name */
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index 3f49280..426fe73 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -38,8 +38,8 @@
   *
   * The following system tables are or have been supported:
   *
- *    CREATE TABLE _sql_stat1(tbl, idx, stat);
- *    CREATE TABLE _sql_stat4(tbl, idx, nEq, nLt, nDLt, sample);
+ *    CREATE TABLE _sql_stat1(space_id, index_id, stat);
+ *    CREATE TABLE _sql_stat4(space_id, index_id, nEq, nLt, nDLt, sample);
   *
   * For most applications, _sql_stat1 provides all the statistics required
   * for the query planner to make good choices.
@@ -47,7 +47,7 @@
   * Format of _sql_stat1:
   *
   * There is normally one row per index, with the index identified by the
- * name in the idx column.  The tbl column is the name of the table to
+ * id in the index_id column.  The space_id column is the id of the 
space to
   * which the index belongs.  In each such row, the stat column will be
   * a string consisting of a list of integers.  The first integer in this
   * list is the number of rows in the index.  (This is the same as the
@@ -66,9 +66,9 @@
   * "unordered" keyword is present, then the query planner assumes that
   * the index is unordered and will not use the index for a range query.
   *
- * If the _sql_stat1.idx column is NULL, then the _sql_stat1.stat
+ * If the _sql_stat1.index_id column is NULL, then the _sql_stat1.stat
   * column contains a single integer which is the (estimated) number of
- * rows in the table identified by _sql_stat1.tbl.
+ * rows in the table identified by _sql_stat1.space_id.
   *
   * Format for _sql_stat4:
   *
@@ -78,9 +78,9 @@
   * queries.
   *
   * The _sql_stat4 table contains multiple entries for each index.
- * The idx column names the index and the tbl column is the table of the
- * index.  If the idx and tbl columns are the same, then the sample is
- * of the INTEGER PRIMARY KEY.  The sample column is a blob which is the
+ * The index_id column names the index and the space_id column is the 
space of
+ * the index. If the index_id is equal to 0, then the sample
+ * is of the INTEGER PRIMARY KEY.  The sample column is a blob which is the
   * binary encoding of a key from the index.  The nEq column is a
   * list of integers.  The first integer is the approximate number
   * of entries in the index whose left-most column exactly matches
@@ -122,10 +122,11 @@
   * created.
   *
   * @param parse Parsing context.
- * @param table_name Delete records of this table if specified.
+ * @param space_id Delete records of this table if id is not
+ *        BOX_ID_NIL.
   */
  static void
-vdbe_emit_stat_space_open(struct Parse *parse, const char *table_name)
+vdbe_emit_stat_space_open(struct Parse *parse, uint32_t space_id)
  {
      const char *stat_names[] = {"_sql_stat1", "_sql_stat4"};
      const uint32_t stat_ids[] = {BOX_SQL_STAT1_ID, BOX_SQL_STAT4_ID};
@@ -133,10 +134,9 @@ vdbe_emit_stat_space_open(struct Parse *parse, 
const char *table_name)
      assert(v != NULL);
      assert(sqlite3VdbeDb(v) == parse->db);
      for (uint i = 0; i < lengthof(stat_names); ++i) {
-        const char *space_name = stat_names[i];
-        if (table_name != NULL) {
-            vdbe_emit_stat_space_clear(parse, space_name, NULL,
-                           table_name);
+        if (space_id != BOX_ID_NIL) {
+            vdbe_emit_stat_space_clear(parse, stat_names[i],
+                           space_id, BOX_ID_NIL);
          } else {
              sqlite3VdbeAddOp1(v, OP_Clear, stat_ids[i]);
          }
@@ -780,10 +780,10 @@ vdbe_emit_analyze_space(struct Parse *parse, 
struct space *space)
      int key_reg = ++parse->nMem;
      /* Temporary use register. */
      int tmp_reg = ++parse->nMem;
-    /* Register containing table name. */
-    int tab_name_reg = ++parse->nMem;
-    /* Register containing index name. */
-    int idx_name_reg = ++parse->nMem;
+    /* Register containing space id. */
+    int space_id_reg = ++parse->nMem;
+    /* Register containing index id. */
+    int index_id_reg = ++parse->nMem;
      /* Value for the stat column of _sql_stat1. */
      int stat1_reg = ++parse->nMem;
      /* MUST BE LAST (see below). */
@@ -800,13 +800,13 @@ vdbe_emit_analyze_space(struct Parse *parse, 
struct space *space)
      assert(space->index_count != 0);
      struct Vdbe *v = sqlite3GetVdbe(parse);
      assert(v != NULL);
-    const char *tab_name = space_name(space);
+    MAYBE_UNUSED const char *tab_name = space_name(space);
      sqlite3VdbeAddOp4(v, OP_IteratorOpen, tab_cursor, 0, 0, (void *) 
space,
                P4_SPACEPTR);
-    sqlite3VdbeLoadString(v, tab_name_reg, space->def->name);
+    sqlite3VdbeAddOp2(v, OP_Integer, space->def->id, space_id_reg);
      for (uint32_t j = 0; j < space->index_count; ++j) {
          struct index *idx = space->index[j];
-        const char *idx_name;
+        MAYBE_UNUSED const char *idx_name;
          /*
           * Primary indexes feature automatically generated
           * names. Thus, for the sake of clarity, use
@@ -817,8 +817,8 @@ vdbe_emit_analyze_space(struct Parse *parse, struct 
space *space)
          else
              idx_name = idx->def->name;
          int part_count = idx->def->key_def->part_count;
-        /* Populate the register containing the index name. */
-        sqlite3VdbeLoadString(v, idx_name_reg, idx_name);
+        /* Populate the register containing the index id. */
+        sqlite3VdbeAddOp2(v, OP_Integer, idx->def->iid, index_id_reg);
          VdbeComment((v, "Analysis for %s.%s", tab_name, idx_name));
          /*
           * Pseudo-code for loop that calls stat_push():
@@ -993,9 +993,8 @@ vdbe_emit_analyze_space(struct Parse *parse, struct 
space *space)
          sqlite3VdbeAddOp2(v, OP_Next, idx_cursor, next_row_addr);
          /* Add the entry to the stat1 table. */
          callStatGet(v, stat4_reg, STAT_GET_STAT1, stat1_reg);
-        assert("BBB"[0] == AFFINITY_TEXT);
-        sqlite3VdbeAddOp4(v, OP_MakeRecord, tab_name_reg, 3, tmp_reg,
-                  "BBB", 0);
+        sqlite3VdbeAddOp4(v, OP_MakeRecord, space_id_reg, 3, tmp_reg,
+                  "DDB", 0);
          sqlite3VdbeAddOp4(v, OP_IdxInsert, tmp_reg, 0, 0,
                    (char *)stat1, P4_SPACEPTR);
          /* Add the entries to the stat4 table. */
@@ -1029,7 +1028,7 @@ vdbe_emit_analyze_space(struct Parse *parse, 
struct space *space)
          }
          sqlite3VdbeAddOp3(v, OP_MakeRecord, col_reg, part_count,
                    sample_reg);
-        sqlite3VdbeAddOp3(v, OP_MakeRecord, tab_name_reg, 6, tmp_reg);
+        sqlite3VdbeAddOp3(v, OP_MakeRecord, space_id_reg, 6, tmp_reg);
          sqlite3VdbeAddOp4(v, OP_IdxReplace, tmp_reg, 0, 0,
                    (char *)stat4, P4_SPACEPTR);
          /* P1==1 for end-of-loop. */
@@ -1070,7 +1069,7 @@ static void
  sql_analyze_database(struct Parse *parser)
  {
      sql_set_multi_write(parser, false);
-    vdbe_emit_stat_space_open(parser, NULL);
+    vdbe_emit_stat_space_open(parser, BOX_ID_NIL);
      space_foreach(sql_space_foreach_analyze, (void *)parser);
      loadAnalysis(parser);
  }
@@ -1091,7 +1090,7 @@ vdbe_emit_analyze_table(struct Parse *parse, 
struct space *space)
       * There are two system spaces for statistics: _sql_stat1
       * and _sql_stat4.
       */
-    vdbe_emit_stat_space_open(parse, space->def->name);
+    vdbe_emit_stat_space_open(parse, space->def->id);
      vdbe_emit_analyze_space(parse, space);
      loadAnalysis(parse);
  }
@@ -1225,24 +1224,17 @@ analysis_loader(void *data, int argc, char 
**argv, char **unused)
      struct analysis_index_info *info = (struct analysis_index_info *) 
data;
      assert(info->stats != NULL);
      struct index_stat *stat = &info->stats[info->index_count++];
-    struct space *space = space_by_name(argv[0]);
+    uint32_t space_id = atoll(argv[0]);
+    if (space_id == 0)
+        return -1;
+    struct space *space = space_by_id(space_id);
      if (space == NULL)
          return -1;
      struct index *index;
-    uint32_t iid = box_index_id_by_name(space->def->id, argv[1],
-                        strlen(argv[1]));
-    /*
-     * Convention is if index's name matches with space's
-     * one, then it is primary index.
-     */
-    if (iid != BOX_ID_NIL) {
-        index = space_index(space, iid);
-    } else {
-        if (sqlite3_stricmp(argv[0], argv[1]) != 0)
-            return -1;
-        index = space_index(space, 0);
-    }
-    assert(index != NULL);
+    uint32_t iid = atoll(argv[1]);
+    index = space_index(space, iid);
+    if (index == NULL)
+        return -1;
      /*
       * Additional field is used to describe total
       * count of tuples in index. Although now all
@@ -1354,8 +1346,12 @@ sample_compare(const void *a, const void *b, void 
*arg)
   * Arguments must point to SQL statements that return
   * data equivalent to the following:
   *
- *    prepare: SELECT tbl,idx,count(*) FROM _sql_stat4 GROUP BY tbl,idx;
- *    load: SELECT tbl,idx,neq,nlt,ndlt,sample FROM _sql_stat4;
+ * prepare:
+ *     SELECT space_id,index_id,count(*) FROM _sql_stat4
+ *     GROUP BY space_id,index_id;
+ * load:
+ *     SELECT space_id,index_id,neq,nlt,ndlt,sample
+ *     FROM _sql_stat4;
   *
   * 'prepare' statement is used to allocate enough memory for
   * statistics (i.e. arrays lt, dt, dlt and avg_eq). 'load' query
@@ -1387,24 +1383,17 @@ load_stat_from_space(struct sqlite3 *db, const 
char *sql_select_prepare,
          goto finalize;
      uint32_t current_idx_count = 0;
      while (sqlite3_step(stmt) == SQLITE_ROW) {
-        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
-        if (space_name == NULL)
+        uint32_t space_id = sqlite3_column_int(stmt, 0);
+        if (space_id == 0)
              continue;
-        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
-        if (index_name == NULL)
+        struct space *space = space_by_id(space_id);
+        if (space == NULL)
+            continue;
+        uint32_t iid = sqlite3_column_int(stmt, 1);
+        struct index *index = space_index(space, iid);
+        if (index == NULL)
              continue;
          uint32_t sample_count = sqlite3_column_int(stmt, 2);
-        struct space *space = space_by_name(space_name);
-        assert(space != NULL);
-        struct index *index;
-        uint32_t iid = box_index_id_by_name(space->def->id, index_name,
-                            strlen(index_name));
-        if (sqlite3_stricmp(space_name, index_name) == 0 &&
-            iid == BOX_ID_NIL)
-            index = space_index(space, 0);
-        else
-            index = space_index(space, iid);
-        assert(index != NULL);
          uint32_t column_count = index->def->key_def->part_count;
          struct index_stat *stat = &stats[current_idx_count];
          stat->sample_field_count = column_count;
@@ -1456,25 +1445,16 @@ load_stat_from_space(struct sqlite3 *db, const 
char *sql_select_prepare,
      struct index *prev_index = NULL;
      current_idx_count = 0;
      while (sqlite3_step(stmt) == SQLITE_ROW) {
-        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
-        if (space_name == NULL)
+        uint32_t space_id = sqlite3_column_int(stmt, 0);
+        if (space_id == 0)
              continue;
-        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
-        if (index_name == NULL)
+        struct space *space = space_by_id(space_id);
+        if (space == NULL)
+            continue;
+        uint32_t iid = sqlite3_column_int(stmt, 1);
+        struct index *index = space_index(space, iid);
+        if (index == NULL)
              continue;
-        struct space *space = space_by_name(space_name);
-        assert(space != NULL);
-        struct index *index;
-        uint32_t iid = box_index_id_by_name(space->def->id, index_name,
-                            strlen(index_name));
-        if (iid != BOX_ID_NIL) {
-            index = space_index(space, iid);
-        } else {
-            if (sqlite3_stricmp(space_name, index_name) != 0)
-                return -1;
-            index = space_index(space, 0);
-        }
-        assert(index != NULL);
          uint32_t column_count = index->def->key_def->part_count;
          if (index != prev_index) {
              if (prev_index != NULL) {
@@ -1537,25 +1517,16 @@ load_stat_to_index(struct sqlite3 *db, const 
char *sql_select_load,
          return -1;
      uint32_t current_idx_count = 0;
      while (sqlite3_step(stmt) == SQLITE_ROW) {
-        const char *space_name = (char *)sqlite3_column_text(stmt, 0);
-        if (space_name == NULL)
+        uint32_t space_id = sqlite3_column_int(stmt, 0);
+        if (space_id == 0)
              continue;
-        const char *index_name = (char *)sqlite3_column_text(stmt, 1);
-        if (index_name == NULL)
+        struct space *space = space_by_id(space_id);
+        if (space == NULL)
+            continue;
+        uint32_t iid = sqlite3_column_int(stmt, 1);
+        struct index *index = space_index(space, iid);
+        if (index == NULL)
              continue;
-        struct space *space = space_by_name(space_name);
-        assert(space != NULL);
-        struct index *index;
-        uint32_t iid = box_index_id_by_name(space->def->id, index_name,
-                            strlen(index_name));
-        if (iid != BOX_ID_NIL) {
-            index = space_index(space, iid);
-        } else {
-            if (sqlite3_stricmp(space_name, index_name) != 0)
-                return -1;
-            index = space_index(space, 0);
-        }
-        assert(index != NULL);
          free(index->def->opts.stat);
          index->def->opts.stat = stats[current_idx_count++];
      }
@@ -1699,7 +1670,7 @@ sql_analysis_load(struct sqlite3 *db)
      info.stats = stats;
      info.index_count = 0;
      const char *load_stat1 =
-        "SELECT \"tbl\",\"idx\",\"stat\" FROM \"_sql_stat1\"";
+        "SELECT \"space_id\",\"index_id\",\"stat\" FROM \"_sql_stat1\"";
      /* Load new statistics out of the _sql_stat1 table. */
      if (sqlite3_exec(db, load_stat1, analysis_loader, &info, 0) != 0)
          goto fail;
@@ -1712,10 +1683,10 @@ sql_analysis_load(struct sqlite3 *db)
       * statistics. Result rows are given in a form:
       * <table name>, <index name>, <count of samples>
       */
-    const char *init_query = "SELECT \"tbl\",\"idx\",count(*) FROM "
-                 "\"_sql_stat4\" GROUP BY \"tbl\",\"idx\"";
+    const char *init_query = "SELECT \"space_id\",\"index_id\",count(*) 
FROM "
+                 "\"_sql_stat4\" GROUP BY \"space_id\",\"index_id\"";
      /* Query for loading statistics into in-memory structs. */
-    const char *load_query = "SELECT \"tbl\",\"idx\",\"neq\",\"nlt\","
+    const char *load_query = "SELECT 
\"space_id\",\"index_id\",\"neq\",\"nlt\","
                   "\"ndlt\",\"sample\" FROM \"_sql_stat4\"";
      /* Load the statistics from the _sql_stat4 table. */
      if (load_stat_from_space(db, init_query, load_query, stats) != 0)
@@ -1759,8 +1730,8 @@ sql_analysis_load(struct sqlite3 *db)
       * Ordered query is needed to be sure that indexes come
       * in the same order as in previous SELECTs.
       */
-    const char *order_query = "SELECT \"tbl\",\"idx\" FROM "
-                  "\"_sql_stat4\" GROUP BY \"tbl\",\"idx\"";
+    const char *order_query = "SELECT \"space_id\",\"index_id\" FROM "
+                  "\"_sql_stat4\" GROUP BY \"space_id\",\"index_id\"";
      if (load_stat_to_index(db, order_query, heap_stats) != 0)
          goto fail;
      if (box_txn_commit() != 0)
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 52f0bde..67d9627 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -1707,75 +1707,43 @@ sql_store_select(struct Parse *parse_context, 
struct Select *select)
      parse_context->parsed_ast.select = select_copy;
  }

-/**
- * Create expression record "@col_name = '@col_value'".
- *
- * @param parse The parsing context.
- * @param col_name Name of column.
- * @param col_value Name of row.
- * @retval not NULL on success.
- * @retval NULL on failure.
- */
-static struct Expr *
-sql_id_eq_str_expr(struct Parse *parse, const char *col_name,
-           const char *col_value)
-{
-    struct sqlite3 *db = parse->db;
-
-    struct Expr *col_name_expr = sqlite3Expr(db, TK_ID, col_name);
-    if (col_name_expr == NULL)
-        return NULL;
-    struct Expr *col_value_expr = sqlite3Expr(db, TK_STRING, col_value);
-    if (col_value_expr == NULL) {
-        sql_expr_delete(db, col_name_expr, false);
-        return NULL;
-    }
-    return sqlite3PExpr(parse, TK_EQ, col_name_expr, col_value_expr);
-}
-
  void
  vdbe_emit_stat_space_clear(struct Parse *parse, const char 
*stat_table_name,
-               const char *idx_name, const char *table_name)
+               uint32_t space_id, uint32_t index_id)
  {
-    assert(idx_name != NULL || table_name != NULL);
      struct sqlite3 *db = parse->db;
      assert(!db->mallocFailed);
      struct SrcList *src_list = sql_alloc_src_list(db);
      if (src_list != NULL)
          src_list->a[0].zName = sqlite3DbStrDup(db, stat_table_name);
-    struct Expr *where = NULL;
-    if (idx_name != NULL) {
-        struct Expr *expr = sql_id_eq_str_expr(parse, "idx", idx_name);
-        if (expr != NULL)
-            where = sqlite3ExprAnd(db, expr, where);
-    }
-    if (table_name != NULL) {
-        struct Expr *expr = sql_id_eq_str_expr(parse, "tbl", table_name);
-        if (expr != NULL)
-            where = sqlite3ExprAnd(db, expr, where);
-    }
-    /**
-     * On memory allocation error sql_table delete_from
-     * releases memory for its own.
-     */
-    sql_table_delete_from(parse, src_list, where);
+    struct Expr *expr = NULL;
+    struct Expr *col = sqlite3Expr(db, TK_ID, "space_id");
+    struct Expr *val = sqlite3ExprInteger(db, space_id);
+    if (col != NULL && val != NULL)
+        expr = sqlite3PExpr(parse, TK_EQ, col, val);
+    if (index_id != BOX_ID_NIL && expr != NULL) {
+        col = sqlite3Expr(db, TK_ID, "index_id");
+        val = sqlite3ExprInteger(db, index_id);
+        expr = sqlite3ExprAnd(db, sqlite3PExpr(parse, TK_EQ, col, val),
+                      expr);
+    }
+    sql_table_delete_from(parse, src_list, expr);
  }

  /**
   * Remove entries from the _sql_stat1 and _sql_stat4
   * system spaces after a DROP INDEX or DROP TABLE command.
   *
- * @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.
+ * @param parse The parsing context.
+ * @param space_id Id of table to be dropped or table that
+ *        contains index to be dropped.
+ * @param index_id Id of index to be dropped.
   */
  static void
-sql_clear_stat_spaces(struct Parse *parse, const char *table_name,
-              const char *idx_name)
+sql_clear_stat_spaces(struct Parse *parse, uint32_t space_id, uint32_t 
index_id)
  {
-    vdbe_emit_stat_space_clear(parse, "_sql_stat4", idx_name, table_name);
-    vdbe_emit_stat_space_clear(parse, "_sql_stat1", idx_name, table_name);
+    vdbe_emit_stat_space_clear(parse, "_sql_stat4", space_id, index_id);
+    vdbe_emit_stat_space_clear(parse, "_sql_stat1", space_id, index_id);
  }

  /**
@@ -1996,7 +1964,7 @@ sql_drop_table(struct Parse *parse_context, struct 
SrcList *table_name_list,
              goto exit_drop_table;
          }
      }
-    sql_clear_stat_spaces(parse_context, space_name, NULL);
+    sql_clear_stat_spaces(parse_context, space->def->id, BOX_ID_NIL);
      sql_code_drop_table(parse_context, space, is_view);

   exit_drop_table:
@@ -2845,7 +2813,8 @@ sql_drop_index(struct Parse *parse_context, struct 
SrcList *index_name_list,
       * But firstly, delete statistics since schema
       * changes after DDL.
       */
-    sql_clear_stat_spaces(parse_context, table_name, index->def->name);
+    sql_clear_stat_spaces(parse_context, space->def->id, index->def->iid);
+
      int record_reg = ++parse_context->nMem;
      int space_id_reg = ++parse_context->nMem;
      sqlite3VdbeAddOp2(v, OP_Integer, space->def->id, space_id_reg);
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index dbf58d9..2958a68 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -4926,11 +4926,13 @@ vdbe_emit_halt_with_presence_test(struct Parse 
*parser, int space_id,
   *
   * @param parse The parsing context.
   * @param stat_table_name System stat table name.
- * @param idx_name Index name.
- * @param table_name Table name.
+ * @param table_id Id of table of which analysis will be deleted.
+ * @param index_id Id of index of which analysis will be deleted.
+ *        If index_id is BOX_ID_NIL than analysis of all indexes
+ *        of selected table will be deleted.
   */
  void
  vdbe_emit_stat_space_clear(struct Parse *parse, const char 
*stat_table_name,
-               const char *idx_name, const char *table_name);
+               uint32_t table_id, uint32_t index_id);

  #endif                /* SQLITEINT_H */
diff --git a/test/box-py/bootstrap.result b/test/box-py/bootstrap.result
index 506aca3..6dc13b4 100644
--- a/test/box-py/bootstrap.result
+++ b/test/box-py/bootstrap.result
@@ -5,7 +5,7 @@ box.space._schema:select{}
  ---
  - - ['cluster', '<cluster uuid>']
    - ['max_id', 511]
-  - ['version', 2, 1, 0]
+  - ['version', 2, 1, 1]
  ...
  box.space._cluster:select{}
  ---
@@ -74,11 +74,12 @@ box.space._space:select{}
          'type': 'unsigned'}]]
    - [340, 1, '_space_sequence', 'memtx', 0, {}, [{'name': 'id', 
'type': 'unsigned'},
        {'name': 'sequence_id', 'type': 'unsigned'}, {'name': 
'is_generated', 'type': 'boolean'}]]
-  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'stat', 'type': 'string'}]]
-  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'neq', 'type': 'string'}, {'name': 
'nlt', 'type': 'string'},
-      {'name': 'ndlt', 'type': 'string'}, {'name': 'sample', 'type': 
'scalar'}]]
+  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'space_id', 
'type': 'unsigned'},
+      {'name': 'index_id', 'type': 'unsigned'}, {'name': 'stat', 
'type': 'string'}]]
+  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'space_id', 
'type': 'unsigned'},
+      {'name': 'index_id', 'type': 'unsigned'}, {'name': 'neq', 'type': 
'string'},
+      {'name': 'nlt', 'type': 'string'}, {'name': 'ndlt', 'type': 
'string'}, {'name': 'sample',
+        'type': 'scalar'}]]
    - [356, 1, '_fk_constraint', 'memtx', 0, {}, [{'name': 'name', 
'type': 'string'},
        {'name': 'child_id', 'type': 'unsigned'}, {'name': 'parent_id', 
'type': 'unsigned'},
        {'name': 'is_deferred', 'type': 'boolean'}, {'name': 'match', 
'type': 'string'},
@@ -134,9 +135,9 @@ box.space._index:select{}
    - [330, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
    - [340, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
    - [340, 1, 'sequence', 'tree', {'unique': false}, [[1, 'unsigned']]]
-  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string']]]
-  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string'], [
-        5, 'scalar']]]
+  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned']]]
+  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned'],
+      [5, 'scalar']]]
    - [356, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'unsigned']]]
    - [356, 1, 'child_id', 'tree', {'unique': false}, [[1, 'unsigned']]]
  ...
diff --git a/test/box/access_misc.result b/test/box/access_misc.result
index 4ffeb38..83e9d3e 100644
--- a/test/box/access_misc.result
+++ b/test/box/access_misc.result
@@ -813,11 +813,12 @@ box.space._space:select()
          'type': 'unsigned'}]]
    - [340, 1, '_space_sequence', 'memtx', 0, {}, [{'name': 'id', 
'type': 'unsigned'},
        {'name': 'sequence_id', 'type': 'unsigned'}, {'name': 
'is_generated', 'type': 'boolean'}]]
-  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'stat', 'type': 'string'}]]
-  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-        'type': 'string'}, {'name': 'neq', 'type': 'string'}, {'name': 
'nlt', 'type': 'string'},
-      {'name': 'ndlt', 'type': 'string'}, {'name': 'sample', 'type': 
'scalar'}]]
+  - [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'space_id', 
'type': 'unsigned'},
+      {'name': 'index_id', 'type': 'unsigned'}, {'name': 'stat', 
'type': 'string'}]]
+  - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'space_id', 
'type': 'unsigned'},
+      {'name': 'index_id', 'type': 'unsigned'}, {'name': 'neq', 'type': 
'string'},
+      {'name': 'nlt', 'type': 'string'}, {'name': 'ndlt', 'type': 
'string'}, {'name': 'sample',
+        'type': 'scalar'}]]
    - [356, 1, '_fk_constraint', 'memtx', 0, {}, [{'name': 'name', 
'type': 'string'},
        {'name': 'child_id', 'type': 'unsigned'}, {'name': 'parent_id', 
'type': 'unsigned'},
        {'name': 'is_deferred', 'type': 'boolean'}, {'name': 'match', 
'type': 'string'},
diff --git a/test/box/alter.result b/test/box/alter.result
index 9a1086e..86e8c3a 100644
--- a/test/box/alter.result
+++ b/test/box/alter.result
@@ -228,9 +228,9 @@ _index:select{}
    - [330, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
    - [340, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
    - [340, 1, 'sequence', 'tree', {'unique': false}, [[1, 'unsigned']]]
-  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string']]]
-  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string'], [
-        5, 'scalar']]]
+  - [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned']]]
+  - [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned'],
+      [5, 'scalar']]]
    - [356, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'unsigned']]]
    - [356, 1, 'child_id', 'tree', {'unique': false}, [[1, 'unsigned']]]
  ...
diff --git a/test/sql-tap/analyze1.test.lua b/test/sql-tap/analyze1.test.lua
index ea414e9..0eb4129 100755
--- a/test/sql-tap/analyze1.test.lua
+++ b/test/sql-tap/analyze1.test.lua
@@ -65,7 +65,7 @@ test:do_execsql_test(
  test:do_execsql_test(
      "analyze-1.7",
      [[
-        SELECT * FROM "_sql_stat1" WHERE "idx" IS NOT NULL
+        SELECT * FROM "_sql_stat1" WHERE "index_id" IS NOT NULL
      ]], {
          -- <analyze-1.7>
          -- </analyze-1.7>
@@ -84,7 +84,7 @@ test:do_catchsql_test(
  test:do_execsql_test(
      "analyze-1.9",
      [[
-        SELECT * FROM "_sql_stat1" WHERE "idx" IS NOT NULL
+        SELECT * FROM "_sql_stat1" WHERE "index_id" IS NOT NULL
      ]], {
          -- <analyze-1.9>
          -- </analyze-1.9>
@@ -119,7 +119,7 @@ test:do_execsql_test(
      [[
          CREATE INDEX t1i1 ON t1(a);
          ANALYZE t1;
-        SELECT * FROM "_sql_stat1" ORDER BY "idx";
+        SELECT * FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-2.1>
          -- </analyze-2.1>
@@ -130,7 +130,7 @@ test:do_execsql_test(
      [[
          CREATE INDEX t1i2 ON t1(b);
          ANALYZE t1;
-        SELECT * FROM "_sql_stat1" ORDER BY "idx";
+        SELECT * FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-2.2>
          -- </analyze-2.2>
@@ -141,7 +141,7 @@ test:do_execsql_test(
      [[
          CREATE INDEX t1i3 ON t1(a,b);
          ANALYZE;
-        SELECT * FROM "_sql_stat1" ORDER BY "idx";
+        SELECT * FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-2.3>
          -- </analyze-2.3>
@@ -157,10 +157,10 @@ test:do_execsql_test(
          INSERT INTO t1 VALUES(1, 1,2);
          INSERT INTO t1 VALUES(2, 1,3);
          ANALYZE t1;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.1>
-        "T1", "2 1", "T1I1", "2 2", "T1I2", "2 1", "T1I3", "2 2 1"
+        0, "2 1", 1, "2 2", 2, "2 1", 3, "2 2 1"
          -- </analyze-3.1>
      })

@@ -170,10 +170,10 @@ test:do_execsql_test(
          INSERT INTO t1 VALUES(3, 1,4);
          INSERT INTO t1 VALUES(4, 1,5);
          ANALYZE t1;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.2>
-        "T1", "4 1", "T1I1", "4 4", "T1I2", "4 1", "T1I3", "4 4 1"
+        0, "4 1", 1, "4 4", 2, "4 1", 3, "4 4 1"
          -- </analyze-3.2>
      })

@@ -182,10 +182,10 @@ test:do_execsql_test(
      [[
          INSERT INTO t1 (a,b) VALUES(2,5);
          ANALYZE;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.3>
-        "T1","5 1", "T1I1", "5 3", "T1I2", "5 2", "T1I3", "5 3 1"
+        0,"5 1", 1, "5 3", 2, "5 2", 3, "5 3 1"
          -- </analyze-3.3>
      })

@@ -198,10 +198,10 @@ test:do_execsql_test(
          CREATE INDEX t2i2 ON t2(b);
          CREATE INDEX t2i3 ON t2(a,b);
          ANALYZE;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.4>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 
1","T2I1","5 3","T2I2","5 2","T2I3","5 3 1"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",2,"5 2",3,"5 3 1",3,"5 3 1"
          -- </analyze-3.4>
      })

@@ -210,10 +210,10 @@ test:do_execsql_test(
      [[
          DROP INDEX t2i3 ON t2;;
          ANALYZE t1;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.5>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 
1","T2I1","5 3","T2I2","5 2"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",2,"5 2",3,"5 3 1"
          -- </analyze-3.5>
      })

@@ -221,10 +221,10 @@ test:do_execsql_test(
      "analyze-3.6",
      [[
          ANALYZE t2;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.6>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 
1","T2I1","5 3","T2I2","5 2"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",2,"5 2",3,"5 3 1"
          -- </analyze-3.6>
      })

@@ -233,10 +233,10 @@ test:do_execsql_test(
      [[
          DROP INDEX t2i2 ON t2;
          ANALYZE t2;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.7>
-        "T1","5 1","T1I1","5 3","T1I2","5 2","T1I3","5 3 1","T2","5 
1","T2I1","5 3"
+        0,"5 1",0,"5 1",1,"5 3",1,"5 3",2,"5 2",3,"5 3 1"
          -- </analyze-3.7>
      })

@@ -250,7 +250,7 @@ test:do_execsql_test(
          CREATE INDEX t3i3 ON t3(d,b,c,a);
          DROP TABLE t1;
          DROP TABLE t2;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.8>
          -- </analyze-3.8>
@@ -260,10 +260,10 @@ test:do_execsql_test(
      "analyze-3.9",
      [[
          ANALYZE;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-3.9>
-        "T3","5 1","T3I1","5 3","T3I2","5 3 1 1 1","T3I3","5 5 2 1 1"
+        0,"5 1",1,"5 3",2,"5 3 1 1 1",3,"5 5 2 1 1"
          -- </analyze-3.9>
      })

@@ -277,7 +277,7 @@ test:do_execsql_test(
  --         INSERT INTO [silly " name] (a,b,c) VALUES(1, 2, 3);
  --         INSERT INTO [silly " name] (a,b,c) VALUES(4, 5, 6);
  --         ANALYZE;
---         SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+--         SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
  --     ]], {
  --         -- <analyze-3.10>
  --         "another foolish ' name", "2 1", "foolish ' name", "2 1 1", 
"t3i1", "5 3", "t3i2", "5 3 1 1 1", "t3i3", "5 5 2 1 1"
@@ -288,7 +288,7 @@ test:do_execsql_test(
  --     "analyze-3.11",
  --     [[
  --         DROP INDEX "foolish ' name";
---         SELECT "idx", "stat" FROM sqlite_stat1 ORDER BY "idx";
+--         SELECT "index_id", "stat" FROM sqlite_stat1 ORDER BY "index_id";
  --     ]], {
  --         -- <analyze-3.11>
  --         "another foolish ' name", "2 1", "t3i1", "5 3", "t3i2", "5 
3 1 1 1", "t3i3", "5 5 2 1 1"
@@ -299,7 +299,7 @@ test:do_execsql_test(
  --     "analyze-3.11",
  --     [[
  --         DROP TABLE "silly "" name";
---         SELECT "idx", "stat" FROM sqlite_stat1 ORDER BY "idx";
+--         SELECT "index_id", "stat" FROM sqlite_stat1 ORDER BY "index_id";
  --     ]], {
  --         -- <analyze-3.11>
  --         "t3i1", "5 3", "t3i2", "5 3 1 1 1", "t3i3", "5 5 2 1 1"
@@ -317,31 +317,33 @@ test:do_execsql_test(
          CREATE INDEX t4i2 ON t4(y);
          INSERT INTO t4 SELECT id,a,b,c FROM t3;
          ANALYZE;
-        SELECT "idx", "stat" FROM "_sql_stat1" ORDER BY "idx";
+        SELECT "index_id", "stat" FROM "_sql_stat1" ORDER BY "index_id";
      ]], {
          -- <analyze-4.0>
-        "T3", "5 1", "T3I1", "5 3", "T3I2", "5 3 1 1 1", "T3I3", "5 5 2 
1 1", "T4", "5 1", "T4I1", "5 3", "T4I2", "5 2"
+        0, "5 1", 0, "5 1", 1, "5 3", 1, "5 3", 2, "5 3 1 1 1", 2, "5 
2", 3, "5 5 2 1 1"
          -- </analyze-4.0>
      })

+t4 = box.space.T4
+
  test:do_execsql_test(
      "analyze-4.1",
-    [[
+    string.format([[
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1" VALUES('t4', 't4i1', 'nonsense');
-        INSERT INTO "_sql_stat1" VALUES('t4', 't4i2', 
'432653287412874653284129847632');
+        INSERT INTO "_sql_stat1" VALUES(%i, %i, 'nonsense');
+        INSERT INTO "_sql_stat1" VALUES(%i, %i, 
'432653287412874653284129847632');
          SELECT * FROM t4 WHERE x = 1234;
-    ]], {
+    ]], t4.id, t4.index['T4I1'].id, t4.id, t4.index['T4I2'].id), {
          -- <analyze-4.1>
          -- </analyze-4.1>
      })

  test:do_execsql_test(
      "analyze-4.2",
-    [[
-        INSERT INTO "_sql_stat1" VALUES('t4', 'xyzzy', '0 1 2 3');
+    string.format([[
+        INSERT INTO "_sql_stat1" VALUES(%i, 12345, '0 1 2 3');
          SELECT * FROM t4 WHERE x = 1234;
-    ]], {
+    ]], t4.id), {
          -- <analyze-4.2>
          -- </analyze-4.2>
      })
@@ -367,20 +369,20 @@ test:do_execsql_test(
          INSERT INTO t3 (a,b,c,d) SELECT a+64, b+64, c+64, d+64 FROM t3;
          INSERT INTO t4 (x,y,z) SELECT a, b, c FROM t3;
          ANALYZE;
-        SELECT DISTINCT "tbl" FROM "_sql_stat1" ORDER BY 1;
+        SELECT COUNT(DISTINCT "space_id") FROM "_sql_stat1" ORDER BY 1;
      ]], {
          -- <analyze-5.0>
-        "T3", "T4"
+        2
          -- </analyze-5.0>
      })

  test:do_execsql_test(
      "analyze-5.0.1",
      [[
-        SELECT DISTINCT "idx" FROM "_sql_stat1" ORDER BY 1;
+        SELECT "index_id" FROM "_sql_stat1" ORDER BY 1;
      ]], {
          -- <analyze-5.0>
-        "T3", "T3I1", "T3I2", "T3I3", "T4", "T4I1", "T4I2"
+        0, 0, 1, 1, 2, 2, 3
          -- </analyze-5.0>
      })

@@ -389,20 +391,20 @@ stat = "_sql_stat4"
  test:do_execsql_test(
      "analyze-5.1",
      string.format([[
-            SELECT DISTINCT "idx" FROM "%s" ORDER BY 1;
-        ]], stat, stat), {
+            SELECT DISTINCT "index_id" FROM "%s" ORDER BY 1;
+        ]], stat), {
          -- <analyze-5.1>
-        "T3", "T3I1", "T3I2", "T3I3", "T4", "T4I1", "T4I2"
+        0, 1, 2, 3
          -- </analyze-5.1>
      })

  test:do_execsql_test(
      "analyze-5.1.1",
      string.format([[
-            SELECT DISTINCT "tbl" FROM "%s" ORDER BY 1;
-        ]], stat, stat), {
+            SELECT COUNT(DISTINCT "space_id") FROM "%s" ORDER BY 1;
+        ]], stat), {
          -- <analyze-5.1>
-        "T3", "T4"
+        2
          -- </analyze-5.1>
      })

@@ -411,40 +413,40 @@ test:do_execsql_test(
      [[
          DROP INDEX t3i2 ON t3;
          ANALYZE;
-        SELECT DISTINCT "idx" FROM "_sql_stat1" ORDER BY 1;
+        SELECT "index_id" FROM "_sql_stat1" ORDER BY 1;
      ]], {
          -- <analyze-5.2>
-        "T3", "T3I1", "T3I3", "T4", "T4I1", "T4I2"
+        0, 0, 1, 1, 2, 3
          -- </analyze-5.2>
      })

  test:do_execsql_test(
      "analyze-5.2.1",
      [[
-        SELECT DISTINCT "tbl" FROM "_sql_stat1" ORDER BY 1;
+        SELECT COUNT(DISTINCT "space_id") FROM "_sql_stat1" ORDER BY 1;
      ]], {
          -- <analyze-5.2>
-        "T3", "T4"
+        2
          -- </analyze-5.2>
      })

  test:do_execsql_test(
      "analyze-5.3",
      string.format([[
-            SELECT DISTINCT "idx" FROM "%s" ORDER BY 1;
+            SELECT DISTINCT "index_id" FROM "%s" ORDER BY 1;
          ]], stat, stat), {
          -- <analyze-5.3>
-        "T3", "T3I1", "T3I3", "T4", "T4I1", "T4I2"
+        0, 1, 2, 3
          -- </analyze-5.3>
      })

  test:do_execsql_test(
      "analyze-5.3.1",
      string.format([[
-            SELECT DISTINCT "tbl" FROM "%s" ORDER BY 1;
-        ]], stat, stat), {
+            SELECT COUNT(DISTINCT "space_id") FROM "%s" ORDER BY 1;
+        ]], stat, stat, stat), {
          -- <analyze-5.3>
-        "T3", "T4"
+        2
          -- </analyze-5.3>
      })

@@ -453,40 +455,40 @@ test:do_execsql_test(
      [[
          DROP TABLE IF EXISTS t3;
          ANALYZE;
-        SELECT DISTINCT "idx" FROM "_sql_stat1" ORDER BY 1;
+        SELECT DISTINCT "index_id" FROM "_sql_stat1" ORDER BY 1;
      ]], {
          -- <analyze-5.4>
-        "T4", "T4I1", "T4I2"
+        0, 1, 2
          -- </analyze-5.4>
      })

  test:do_execsql_test(
      "analyze-5.4.1",
      [[
-        SELECT DISTINCT "tbl" FROM "_sql_stat1" ORDER BY 1;
+        SELECT COUNT(DISTINCT "space_id") FROM "_sql_stat1" ORDER BY 1;
      ]], {
          -- <analyze-5.4>
-        "T4"
+        1
          -- </analyze-5.4>
      })

  test:do_execsql_test(
      "analyze-5.5",
      string.format([[
-            SELECT DISTINCT "idx" FROM "%s" ORDER BY 1;
+            SELECT DISTINCT "index_id" FROM "%s" ORDER BY 1;
          ]], stat), {
          -- <analyze-5.5>
-        "T4", "T4I1", "T4I2"
+        0, 1, 2
          -- </analyze-5.5>
      })

  test:do_execsql_test(
      "analyze-5.5.1",
      string.format([[
-            SELECT DISTINCT "tbl" FROM "%s" ORDER BY 1;
-        ]], stat), {
+            SELECT COUNT(DISTINCT "space_id") FROM "%s" ORDER BY 1;
+        ]], stat, stat), {
          -- <analyze-5.5>
-        "T4"
+        1
          -- </analyze-5.5>
      })

@@ -516,33 +518,35 @@ test:do_test(
      -- </analyze-6.1.1>
  })

+t1 = box.space.T1
+
  test:do_execsql_test(
      "analyze-6.1.2",
-    [[
-            SELECT * FROM "_sql_stat1" where "tbl"='T1' and "idx"='I1' 
LIMIT 1;
-    ]], {
+    string.format([[
+            SELECT "index_id", "stat" FROM "_sql_stat1" where 
"space_id"=%i and "index_id"=%i LIMIT 1;
+    ]], t1.id, t1.index['I1'].id), {
      -- <analyze-6.1.2>
-    "T1", "I1", "221 221 221 221 2"
+    1, "221 221 221 221 2"
      -- </analyze-6.1.2>
  })

  test:do_execsql_test(
      "analyze-6.1.3",
-    [[
-            SELECT "tbl", "idx", "neq", "nlt", "ndlt" FROM "_sql_stat4" 
where "tbl"='T1' and "idx"='I1' ORDER BY "nlt" LIMIT 1;
-    ]], {
+    string.format([[
+            SELECT "index_id", "neq", "nlt", "ndlt" FROM "_sql_stat4" 
where "space_id"=%i and "index_id"=%i ORDER BY "nlt" LIMIT 1;
+    ]], t1.id, t1.index['I1'].id), {
      -- <analyze-6.1.3>
-    "T1", "I1", "221 221 221 1", "0 0 0 10", "0 0 0 10"
+    1, "221 221 221 1", "0 0 0 10", "0 0 0 10"
      -- </analyze-6.1.3>
  })

  test:do_execsql_test(
      "analyze-6.1.4",
-    [[
-            SELECT "tbl", "idx", "neq", "nlt", "ndlt" FROM "_sql_stat4" 
where "tbl"='T1' and "idx"='I1' ORDER BY "nlt" DESC LIMIT 1;
-    ]], {
+    string.format([[
+            SELECT "index_id", "neq", "nlt", "ndlt" FROM "_sql_stat4" 
where "space_id"=%i and "index_id"=%i ORDER BY "nlt" DESC LIMIT 1;
+    ]], t1.id, t1.index['I1'].id), {
      -- <analyze-6.1.4>
-    "T1", "I1", "221 221 221 1", "0 0 0 99", "0 0 0 99"
+    1, "221 221 221 1", "0 0 0 99", "0 0 0 99"
      -- </analyze-6.1.4>
  })

diff --git a/test/sql-tap/analyze4.test.lua b/test/sql-tap/analyze4.test.lua
index f734423..4cb3ad2 100755
--- a/test/sql-tap/analyze4.test.lua
+++ b/test/sql-tap/analyze4.test.lua
@@ -49,15 +49,19 @@ test:do_test(
          -- </analyze4-1.0>
      })

+t1 = box.space.T1
+
  -- Verify that the t1b index shows that it does not narrow down the
  -- search any at all.
  --
  test:do_execsql_test(
      "analyze4-1.1",
-    [[ SELECT "idx", "stat" FROM "_sql_stat1" WHERE "tbl"='T1' ORDER BY 
"idx"; ]],
+    string.format([[
+        SELECT "index_id", "stat" FROM "_sql_stat1" WHERE "space_id"=%i 
ORDER BY "index_id";
+    ]], t1.id),
      {
          -- <analyze4-1.1>
-        "T1","128 1", "T1A", "128 1", "T1B", "128 128"
+        0,"128 1", 1, "128 1", 2, "128 128"
          -- </analyze4-1.1>
      })

@@ -68,16 +72,16 @@ test:do_execsql_test(
  test:do_test(
      "analyze4-1.2",
      function()
-        return test:execsql([[
-            UPDATE t1 SET b='x' WHERE a%2;
+        return test:execsql(string.format([[
+            UPDATE t1 SET b='x' WHERE a%%2;
  -- pragma vdbe_debug=1;
              ANALYZE;
  -- pragma vdbe_debug=0;
-            SELECT "idx", "stat" FROM "_sql_stat1" WHERE "tbl"='T1' 
ORDER BY "idx";
-        ]])
+            SELECT "index_id", "stat" FROM "_sql_stat1" WHERE 
"space_id"=%i ORDER BY "index_id";
+        ]], t1.id))
      end, {
          -- <analyze4-1.2>
-        "T1", "128 1", "T1A", "128 1", "T1B", "128 64"
+        0, "128 1", 1, "128 1", 2, "128 64"
          -- </analyze4-1.2>
      })

@@ -85,37 +89,42 @@ test:do_test(
  -- Create a multi-column indices using t1.b and verify that ANALYZE
  -- processes them correctly.
  --
+
+test:execsql([[
+    -- Tarantool doesn't suppoort ALTER stmt yet.
+    -- UPDATE t1 SET b=NULL;
+    --ALTER TABLE t1 ADD COLUMN c;
+    --ALTER TABLE t1 ADD COLUMN d;
+    -- So, re-create the table and its contents
+    DROP TABLE t1;
+    CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a INT ,b INT 
,c  INT DEFAULT NULL,d  INT DEFAULT NULL);
+    CREATE INDEX t1a ON t1(a);
+    CREATE INDEX t1b ON t1(b);
+    INSERT INTO t1 (a,b) VALUES(1,NULL);
+    INSERT INTO t1 (a,b) SELECT a+1, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+2, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+4, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+8, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+16, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+32, b FROM t1;
+    INSERT INTO t1 (a,b) SELECT a+64, b FROM t1;
+
+    UPDATE t1 SET c=a/4, d=a/2;
+    CREATE INDEX t1bcd ON t1(b,c,d);
+    CREATE INDEX t1cdb ON t1(c,d,b);
+    CREATE INDEX t1cbd ON t1(c,b,d);
+    ANALYZE;
+]])
+
+t1 = box.space.T1
+
  test:do_execsql_test(
      "analyze4-1.3",
-    [[
-            -- Tarantool doesn't suppoort ALTER stmt yet.
-            -- UPDATE t1 SET b=NULL;
-            --ALTER TABLE t1 ADD COLUMN c;
-            --ALTER TABLE t1 ADD COLUMN d;
-            -- So, re-create the table and its contents
-            DROP TABLE t1;
-            CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a INT 
,b INT ,c  INT DEFAULT NULL,d  INT DEFAULT NULL);
-            CREATE INDEX t1a ON t1(a);
-            CREATE INDEX t1b ON t1(b);
-            INSERT INTO t1 (a,b) VALUES(1,NULL);
-            INSERT INTO t1 (a,b) SELECT a+1, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+2, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+4, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+8, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+16, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+32, b FROM t1;
-            INSERT INTO t1 (a,b) SELECT a+64, b FROM t1;
-
-            UPDATE t1 SET c=a/4, d=a/2;
-            CREATE INDEX t1bcd ON t1(b,c,d);
-            CREATE INDEX t1cdb ON t1(c,d,b);
-            CREATE INDEX t1cbd ON t1(c,b,d);
-            ANALYZE;
-            SELECT "idx", "stat" FROM "_sql_stat1" WHERE "tbl"='T1' 
ORDER BY "idx";
-    ]]
-    , {
+    string.format([[
+            SELECT "index_id", "stat" FROM "_sql_stat1" WHERE 
"space_id"=%i ORDER BY "index_id";
+    ]], t1.id), {
          -- <analyze4-1.3>
-        "T1","128 1", "T1A", "128 1", "T1B", "128 128", "T1BCD", "128 
128 4 2", "T1CBD", "128 4 4 2", "T1CDB", "128 4 2 2"
+        0,"128 1", 1, "128 1", 2, "128 128", 3, "128 128 4 2", 4, "128 
4 2 2", 5, "128 4 4 2"
          -- </analyze4-1.3>
      })

diff --git a/test/sql-tap/analyze5.test.lua b/test/sql-tap/analyze5.test.lua
index e4a6d5e..a55ef48 100755
--- a/test/sql-tap/analyze5.test.lua
+++ b/test/sql-tap/analyze5.test.lua
@@ -115,10 +115,11 @@ test:do_test(

          -- DISTINCT idx, sample -- lindex(test_decode(sample),0)
          -- WHERE idx='t1u' ORDER BY nlt;
-        return test:execsql([[ SELECT DISTINCT msgpack_decode("sample")
-                                 FROM "_sql_stat4"
-                                 WHERE "idx"='T1U'
-                                 ORDER BY "nlt"]])
+        return test:execsql(string.format([[
+            SELECT DISTINCT msgpack_decode("sample")
+            FROM "_sql_stat4"
+            WHERE "index_id"= %i
+            ORDER BY "nlt"]], box.space.T1.index['T1U'].id))
      end, {
          -- <analyze5-1.0>
          "alpha", "bravo", "charlie", "delta"
@@ -144,10 +145,10 @@ test:do_test(
  test:do_test(
      "analyze5-1.2",
      function()
-        return test:execsql([[SELECT "idx", count(*) FROM "_sql_stat4" 
GROUP BY 1 ORDER BY 1]])
+        return test:execsql([[SELECT "index_id", count(*) FROM 
"_sql_stat4" GROUP BY 1 ORDER BY 1]])
      end, {
          -- <analyze5-1.2>
- "T1",24,"T1T",4,"T1U",4,"T1V",1,"T1W",4,"T1X",4,"T1Y",2,"T1Z",4
+        0,24,1,4,2,4,3,1,4,4,5,4,6,2,7,4
          -- </analyze5-1.2>
      })

diff --git a/test/sql-tap/analyze9.test.lua b/test/sql-tap/analyze9.test.lua
index 585e918..55b8d8c 100755
--- a/test/sql-tap/analyze9.test.lua
+++ b/test/sql-tap/analyze9.test.lua
@@ -1,6 +1,6 @@
  #!/usr/bin/env tarantool
  test = require("sqltester")
-test:plan(121)
+test:plan(120)

  testprefix = "analyze9"

@@ -62,29 +62,29 @@ msgpack_decode_sample = function(txt)
  end

  box.internal.sql_create_function("msgpack_decode_sample", "TEXT", 
msgpack_decode_sample)
+t1 = box.space.T1

  test:do_execsql_test(
      1.2,
-    [[
-        SELECT 
"tbl","idx","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM 
"_sql_stat4" where "idx" = 'I1';
-    ]], {
+    string.format([[
+        SELECT 
"index_id","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM 
"_sql_stat4" where "index_id" = %i;
+    ]], t1.index['I1'].id), {
          -- <1.2>
-        "T1", "I1", "1 1", "0 0", "0 0", "(0) (0)", "T1", "I1", "1 1", 
"1 1", "1 1", "(1) (1)",
-        "T1", "I1", "1 1", "2 2", "2 2", "(2) (2)", "T1", "I1", "1 1", 
"3 3", "3 3", "(3) (3)",
-        "T1", "I1", "1 1", "4 4", "4 4", "(4) (4)"
+        1, "1 1", "0 0", "0 0", "(0) (0)", 1, "1 1", "1 1", "1 1", "(1) 
(1)",
+        1, "1 1", "2 2", "2 2", "(2) (2)", 1, "1 1", "3 3", "3 3", "(3) 
(3)",
+        1, "1 1", "4 4", "4 4", "(4) (4)"
          -- </1.2>
      })

  test:do_execsql_test(
      1.3,
-    [[
-        SELECT 
"tbl","idx","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM 
"_sql_stat4" where "idx" = 'T1';
-
-    ]], {
+    string.format([[
+        SELECT 
"index_id","neq","nlt","ndlt",msgpack_decode_sample("sample") FROM 
"_sql_stat4" where "index_id" = %i;
+    ]], t1.index[0].id), {
          -- <1.3>
-        'T1', 'T1', '1', '0', '0', '(0)', 'T1', 'T1', '1', '1', '1', 
'(1)',
-        'T1', 'T1', '1', '2', '2', '(2)', 'T1', 'T1', '1', '3', '3', 
'(3)',
-        'T1', 'T1', '1', '4', '4', '(4)'
+        0, '1', '0', '0', '(0)', 0, '1', '1', '1', '(1)',
+        0, '1', '2', '2', '(2)', 0, '1', '3', '3', '(3)',
+        0, '1', '4', '4', '(4)'
          -- </1.3>
      })

@@ -101,10 +101,10 @@ test:do_execsql_test(
          INSERT INTO t1 VALUES('text', 12);
          CREATE INDEX i1 ON t1(a, b);
          ANALYZE;
-        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4";
+        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4" ORDER 
BY 1;
      ]], {
          -- <2.1>
-        "text 12","some text 14","text","some text"
+        "some text","some text 14","text","text 12"
          -- </2.1>
      })

@@ -116,6 +116,7 @@ test:do_execsql_test(
          CREATE TABLE t2(id INTEGER PRIMARY KEY AUTOINCREMENT, a INT , 
b INT );
          CREATE INDEX i2 ON t2(a, b);
      ]])
+t2 = box.space.T2

  test:do_test(
      3.2,
@@ -184,10 +185,10 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      "3.3.2",
-    [[
+    string.format([[
          ANALYZE;
-        SELECT lrange("neq", 1, 1) FROM "_sql_stat4" WHERE "idx" = 'I2';
-    ]], generate_tens_str(24))
+        SELECT lrange("neq", 1, 1) FROM "_sql_stat4" WHERE "space_id" = 
%i and "index_id" = %i;
+    ]], t2.id, t2.index['I2'].id) , generate_tens_str(24))

  ---------------------------------------------------------------------------
  --
@@ -232,6 +233,7 @@ test:do_execsql_test(
          CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a TEXT , 
b INT , c INT);
          CREATE INDEX i1 ON t1(c, b, a);
      ]])
+t1 = box.space.T1

  insert_filler_rows_n = function(iStart, nCopy, nVal)
      for i = 0, nVal-1 do
@@ -282,10 +284,10 @@ test:do_execsql_test(

  test:do_execsql_test(
      4.3,
-    [[
+    string.format([[
          SELECT "neq", lrange("nlt", 1, 3), lrange("ndlt", 1, 3), 
lrange(msgpack_decode_sample("sample"), 1, 3)
-            FROM "_sql_stat4" WHERE "idx" = 'I1' ORDER BY "sample" 
LIMIT 16;
-    ]], {
+            FROM "_sql_stat4" WHERE "space_id" = %i and "index_id" = %i 
ORDER BY "sample" LIMIT 16;
+    ]], t1.id, t1.index['I1'].id), {
          -- <4.3>
          "10 10 10","0 0 0","0 0 0","0 0 0","10 10 10","10 10 10","1 1 
1","1 1 1","10 10 10","20 20 20",
          "2 2 2","2 2 2","10 10 10","30 30 30","3 3 3","3 3 3","10 10 
10","40 40 40","4 4 4","4 4 4",
@@ -299,10 +301,10 @@ test:do_execsql_test(

  test:do_execsql_test(
      4.4,
-    [[
+    string.format([[
          SELECT "neq", lrange("nlt", 1, 3), lrange("ndlt", 1, 3), 
lrange(msgpack_decode_sample("sample"), 1, 3)
-        FROM "_sql_stat4" WHERE "idx" = 'I1' ORDER BY "sample" DESC 
LIMIT 2;
-    ]], {
+        FROM "_sql_stat4" WHERE "space_id" = %i and "index_id" = %i 
ORDER BY "sample" DESC LIMIT 2;
+    ]], t1.id, t1.index['I1'].id), {
          -- <4.4>
          "2 1 1","295 296 296","120 122 125","201 4 h","5 3 1","290 290 
291","119 119 120","200 1 b"
          -- </4.4>
@@ -364,11 +366,12 @@ test:do_test(
  test:do_execsql_test(
      4.9,
      [[
-        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4";
+        SELECT msgpack_decode_sample("sample") FROM "_sql_stat4" ORDER 
BY 1;
      ]], {
          -- <4.9>
-        "x", 1110, 2230, 2750, 3350, 4090, 4470, 4980, 5240, 5280, 
5290, 5590, 5920,
-        5930, 6220, 6710, 7000, 7710, 7830, 7970, 8890, 8950, 9240, 
9250, 9680
+        1110, 2230, 2750, 3350, 4090, 4470, 4980, 5240, 5280, 5290, 
5590, 5920,
+        5930, 6220, 6710, 7000, 7710, 7830, 7970, 8890, 8950, 9240, 
9250, 9680,
+        "x"
          -- </4.9>
      })

@@ -388,12 +391,14 @@ test:do_execsql_test(
          INSERT INTO t1 VALUES(null, 4, 4);
          INSERT INTO t1 VALUES(null, 5, 5);
          ANALYZE;
-        CREATE TABLE x1(tbl TEXT, idx TEXT , neq TEXT, nlt TEXT, ndlt 
TEXT, sample BLOB, PRIMARY KEY(tbl, idx, sample));
+        CREATE TABLE x1(tbl INT, idx INT , neq TEXT, nlt TEXT, ndlt 
TEXT, sample BLOB, PRIMARY KEY(tbl, idx, sample));
          INSERT INTO x1 SELECT * FROM "_sql_stat4";
          DELETE FROM "_sql_stat4";
          INSERT INTO "_sql_stat4" SELECT * FROM x1;
          ANALYZE;
      ]])
+t1 = box.space.T1
+x1 = box.space.X1

  test:do_execsql_test(
      6.2,
@@ -407,7 +412,7 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      7.1,
-    [[
+    string.format([[
          DROP TABLE IF EXISTS t1;
          CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, a INT , 
b INT );
          CREATE INDEX i1 ON t1(a, b);
@@ -418,9 +423,9 @@ test:do_execsql_test(
          INSERT INTO t1 VALUES(null, 5, 5);
          ANALYZE;
          UPDATE "_sql_stat4" SET "sample" = '' WHERE "sample" =
-            (SELECT "sample" FROM "_sql_stat4" WHERE "tbl" = 't1' AND 
"idx" = 'i1' LIMIT 1);
+            (SELECT "sample" FROM "_sql_stat4" WHERE "space_id" = %i 
AND "index_id" = %i LIMIT 1);
          ANALYZE;
-    ]])
+    ]], t1.id, t1.index['I1'].id))

  -- Doesn't work due to the fact that in Tarantool rowid has been removed,
  -- and tbl, idx and sample have been united into primary key.
@@ -1014,15 +1019,15 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      15.1,
-    [[
+    string.format([[
          DROP TABLE IF EXISTS x1;
          CREATE TABLE x1(a  INT PRIMARY KEY, b INT , UNIQUE(a, b));
          INSERT INTO x1 VALUES(1, 2);
          INSERT INTO x1 VALUES(3, 4);
          INSERT INTO x1 VALUES(5, 6);
          ANALYZE;
-        INSERT INTO "_sql_stat4" VALUES('x1', 'abc', '', '', '', '');
-    ]])
+        INSERT INTO "_sql_stat4" VALUES(%i, 12345, '', '', '', '');
+    ]], x1.id))

  test:do_execsql_test(
      15.2,
@@ -1037,7 +1042,7 @@ test:do_execsql_test(
  test:do_execsql_test(
      15.3,
      [[
-        INSERT INTO "_sql_stat4" VALUES('42', '42', '42', '42', '42', 
'42');
+        INSERT INTO "_sql_stat4" VALUES(42, 42, '42', '42', '42', 42);
      ]])

  test:do_execsql_test(
@@ -1054,7 +1059,7 @@ test:do_execsql_test(
      15.7,
      [[
          ANALYZE;
-        UPDATE "_sql_stat1" SET "tbl" = 'no such tbl';
+        UPDATE "_sql_stat1" SET "space_id" = "space_id" + 123456;
      ]])

  test:do_execsql_test(
@@ -1067,25 +1072,6 @@ test:do_execsql_test(
          -- </15.8>
      })

--- Tarantool: this test seems to be useless. There's no reason
--- for these fields to be nullable.
--- test:do_execsql_test(
---    15.9,
---    [[
---        ANALYZE;
---        UPDATE "_sql_stat4" SET "neq" = NULL, "nlt" = NULL, "ndlt" = 
NULL;
---    ]])
-
-test:do_execsql_test(
-    15.10,
-    [[
-        SELECT * FROM x1;
-    ]], {
-        -- <15.10>
-        1, 2, 3, 4, 5, 6
-        -- </15.10>
-    })
-
  -- This is just for coverage....
  test:do_execsql_test(
      15.11,
@@ -1135,6 +1121,7 @@ test:do_test(
          -- <17.1>
          -- </17.1>
      })
+t1 = box.space.T1

  test:do_execsql_test(
      17.2,
@@ -1197,7 +1184,7 @@ test:do_test(
              test:execsql(string.format("INSERT INTO t1 VALUES(%s, 
0);", i))
          end
          test:execsql("ANALYZE")
-        return test:execsql([[ SELECT count(*) FROM "_sql_stat4" WHERE 
"idx" = 'I1'; ]])
+        return test:execsql(string.format('SELECT count(*) FROM 
"_sql_stat4" WHERE "space_id" = %i AND "index_id" = %i;', 
box.space.T1.id, box.space.T1.index['I1'].id))
      end, {
          -- <18.1>
          9
@@ -1239,12 +1226,13 @@ test:do_execsql_test(
          -- </20.2>
      })

+t1 = box.space.T1
  for i = 0, 15 do
      test:do_test(
          "20.3."..i,
          function()
              return test:execsql(string.format(
-                [[SELECT count(*) FROM "_sql_stat4" WHERE "idx" = 'I1' 
AND lrange(msgpack_decode_sample("sample"), 1, 1) = '%s']], i))
+                [[SELECT count(*) FROM "_sql_stat4" WHERE "space_id" = 
%i AND "index_id" = %i AND lrange(msgpack_decode_sample("sample"), 1, 1) 
= '%s']], t1.id, t1.index['I1'].id, i))
          end, {
              1
          })
diff --git a/test/sql-tap/analyzeC.test.lua b/test/sql-tap/analyzeC.test.lua
index 266e37e..31392c8 100755
--- a/test/sql-tap/analyzeC.test.lua
+++ b/test/sql-tap/analyzeC.test.lua
@@ -30,9 +30,7 @@ testprefix = "analyzeC"
  -- Baseline case.  Range queries work OK.  Indexes can be used for
  -- ORDER BY.

-test:do_execsql_test(
-    1.0,
-    [[
+test:execsql([[
          DROP TABLE IF EXISTS t1;
          CREATE TABLE t1(a  INT PRIMARY KEY, b INT , c INT , d INT );
          INSERT INTO t1(a,b,c,d) 
VALUES(1,1,2,3),(2,7,8,9),(3,4,5,6),(4,10,11,12),(5,4,8,12),(6,1,11,111);
@@ -40,10 +38,17 @@ test:do_execsql_test(
          CREATE INDEX t1c ON t1(c);
          ANALYZE;
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") 
VALUES('t1','t1b','12345 2'),('t1','t1c','12345 4');
+    ]]
+)
+t1 = box.space.T1
+
+test:do_execsql_test(
+    1.0,
+    string.format([[
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat") 
VALUES(%i,1,'12345 2'),(%i,2,'12345 4');
          ANALYZE;
          SELECT b,c,d, '#' FROM t1 WHERE b BETWEEN 3 AND 8 ORDER BY d;
-    ]], {
+    ]], t1.id, t1.id), {
          -- <1.0>
          4, 5, 6, "#", 7, 8, 9, "#", 4, 8, 12, "#"
          -- </1.0>
@@ -85,11 +90,12 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      2.0,
+    string.format(
      [[
-        UPDATE "_sql_stat1" SET "stat"='12345 2 unordered' WHERE 
"idx"='t1b';
+        UPDATE "_sql_stat1" SET "stat"='12345 2 unordered' WHERE 
"index_id"=%i;
          ANALYZE;
          SELECT b, c, d, '#' FROM t1 WHERE b BETWEEN 3 AND 8 ORDER BY d;
-    ]], {
+    ]], box.space.T1.index['T1B'].id), {
          -- <2.0>
          4, 5, 6, "#", 7, 8, 9, "#", 4, 8, 12, "#"
          -- </2.0>
@@ -130,11 +136,12 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      3.0,
+    string.format(
      [[
-        UPDATE "_sql_stat1" SET "stat"='12345 2 whatever=5 unordered 
xyzzy=11' WHERE "idx"='t1b';
+        UPDATE "_sql_stat1" SET "stat"='12345 2 whatever=5 unordered 
xyzzy=11' WHERE "index_id"=%i;
          ANALYZE;
          SELECT b, c, d, '#' FROM t1 WHERE b BETWEEN 3 AND 8 ORDER BY d;
-    ]], {
+    ]], box.space.T1.index['T1B'].id), {
          -- <3.0>
          4, 5, 6, "#", 7, 8, 9, "#", 4, 8, 12, "#"
          -- </3.0>
@@ -173,17 +180,22 @@ test:do_execsql_test(

  -- The sz=NNN parameter determines which index to scan
  --
-test:do_execsql_test(
-    4.0,
-    [[
+
+test:execsql([[
          DROP INDEX t1b ON t1;
          CREATE INDEX t1bc ON t1(b,c);
          CREATE INDEX t1db ON t1(d,b);
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") 
VALUES('t1','t1bc','12345 3 2 sz=10'),('t1','t1db','12345 3 2 sz=20');
+    ]]
+)
+
+test:do_execsql_test(
+    4.0,
+    string.format([[
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat") 
VALUES(%i,%i,'12345 3 2 sz=10'),(%i,%i,'12345 3 2 sz=20');
          ANALYZE;
          SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
          -- <4.0>
          6
          -- </4.0>
@@ -201,12 +213,13 @@ test:do_execsql_test(

  test:do_execsql_test(
      4.2,
+    string.format(
      [[
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") 
VALUES('t1','t1bc','12345 3 2 sz=20'),('t1','t1db','12345 3 2 sz=10');
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat") 
VALUES(%i,%i,'12345 3 2 sz=20'),(%i,%i,'12345 3 2 sz=10');
          ANALYZE;
          SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
          -- <4.2>
          6
          -- </4.2>
@@ -227,14 +240,14 @@ test:do_execsql_test(
  --
  test:do_execsql_test(
      5.0,
-    [[
+    string.format([[
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat")
-          VALUES('t1','t1bc','12345 3 2 x=5 sz=10 y=10'),
-                ('t1','t1db','12345 3 2 whatever sz=20 junk');
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat")
+          VALUES(%i,%i,'12345 3 2 x=5 sz=10 y=10'),
+                (%i,%i,'12345 3 2 whatever sz=20 junk');
          ANALYZE;
          SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
          -- <5.0>
          6
          -- </5.0>
@@ -253,12 +266,12 @@ test:do_execsql_test(

  test:do_execsql_test(
      5.2,
-    [[
+    string.format([[
          DELETE FROM "_sql_stat1";
-        INSERT INTO "_sql_stat1"("tbl","idx","stat") 
VALUES('t1','t1db','12345 3 2 x=5 sz=10 y=10'), ('t1','t1bc','12345 3 2 
whatever sz=20 junk');
+        INSERT INTO "_sql_stat1"("space_id","index_id","stat") 
VALUES(%i,%i,'12345 3 2 x=5 sz=10 y=10'), (%i,%i,'12345 3 2 whatever 
sz=20 junk');
          ANALYZE;
          SELECT count(b) FROM t1;
-    ]], {
+    ]], t1.id, t1.index['T1BC'].id, t1.id, t1.index['T1DB'].id), {
          -- <5.2>
          6
          -- </5.2>
diff --git a/test/sql-tap/analyzeD.test.lua b/test/sql-tap/analyzeD.test.lua
index 4bce88b..55f6e9d 100755
--- a/test/sql-tap/analyzeD.test.lua
+++ b/test/sql-tap/analyzeD.test.lua
@@ -153,9 +153,9 @@ test:do_catchsql_test(

  test:do_execsql_test(
      "analyzeD-1.10",
-    [[
-        SELECT * FROM "_sql_stat4" WHERE "tbl" = 'v';
-    ]], {
+    string.format([[
+        SELECT * FROM "_sql_stat4" WHERE "space_id" = %i;
+    ]], box.space.V.id), {
          -- <analyzeD-1.10>

          -- <analyzeD-1.10>
@@ -163,9 +163,9 @@ test:do_execsql_test(

  test:do_execsql_test(
      "analyzeD-1.11",
-    [[
-        SELECT * FROM "_sql_stat1" WHERE "tbl" = 'v';
-    ]], {
+    string.format([[
+        SELECT * FROM "_sql_stat1" WHERE "space_id" = %i;
+    ]], box.space.V.id), {
          -- <analyzeD-1.11>

          -- <analyzeD-1.11>
@@ -183,9 +183,9 @@ test:do_catchsql_test(

  test:do_execsql_test(
      "analyzeD-1.13",
-    [[
-        SELECT * FROM "_sql_stat4" WHERE "tbl" = 'v';
-    ]], {
+    string.format([[
+        SELECT * FROM "_sql_stat4" WHERE "space_id" = %i;
+    ]], box.space.V.id), {
          -- <analyzeD-1.13>

          -- <analyzeD-1.13>
@@ -193,9 +193,9 @@ test:do_execsql_test(

  test:do_execsql_test(
      "analyzeD-1.14",
-    [[
-        SELECT * FROM "_sql_stat1" WHERE "tbl" = 'v';
-    ]], {
+    string.format([[
+        SELECT * FROM "_sql_stat1" WHERE "space_id" = %i;
+    ]], box.space.V.id), {
          -- <analyzeD-1.14>

          -- <analyzeD-1.14>
diff --git a/test/sql-tap/gh-3350-skip-scan.test.lua 
b/test/sql-tap/gh-3350-skip-scan.test.lua
index 4cecfe0..640075e 100755
--- a/test/sql-tap/gh-3350-skip-scan.test.lua
+++ b/test/sql-tap/gh-3350-skip-scan.test.lua
@@ -77,9 +77,7 @@ test:do_execsql_test(
          }
  )

-test:do_execsql_test(
-        "skip-scan-1.4",
-        [[
+test:execsql([[
              DROP TABLE IF EXISTS t1;
              CREATE TABLE t1(id INTEGER PRIMARY KEY, a TEXT, b INT, c 
INT, d INT);
              CREATE INDEX t1abc ON t1(a,b,c);
@@ -96,10 +94,17 @@ test:do_execsql_test(
              ANALYZE;
              DELETE FROM "_sql_stat1";
              DELETE FROM "_sql_stat4";
-            INSERT INTO "_sql_stat1" VALUES('T1','T1ABC','10000 5000 
2000 10');
+        ]]
+)
+
+test:do_execsql_test(
+        "skip-scan-1.4",
+        string.format(
+        [[
+            INSERT INTO "_sql_stat1" VALUES(%i,%i,'10000 5000 2000 10');
              ANALYZE t2;
              SELECT a,b,c,d FROM t1 WHERE b=345;
-        ]], {
+        ]], box.space.T1.id, box.space.T1.index['T1ABC'].id), {
              "abc", 345, 7, 8, "def", 345, 9, 10
          }
  )
diff --git a/test/sql/sql-statN-index-drop.result 
b/test/sql/sql-statN-index-drop.result
index 7605951..b31dc81 100644
--- a/test/sql/sql-statN-index-drop.result
+++ b/test/sql/sql-statN-index-drop.result
@@ -31,36 +31,36 @@ box.sql.execute("ANALYZE;")
  ---
  ...
  -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
  ---
-- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
+  - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
  ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')
  ---
-- - ['T1', 'I1', '1 1']
-  - ['T1', 'T1', '1 1']
-  - ['T2', 'I1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - [0, '1 1']
+  - [1, '1 1']
+  - [0, '1 1']
+  - [1, '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\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
  ---
-- - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - [0, '1', '0', '0', !!binary kQE=]
+  - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
  ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')
  ---
-- - ['T1', 'T1', '1 1']
-  - ['T2', 'I1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - [0, '1 1']
+  - [0, '1 1']
+  - [1, '1 1']
  ...
  --Cleaning up.
  box.sql.execute("DROP TABLE t1;")
@@ -93,36 +93,36 @@ box.sql.execute("ANALYZE;")
  ---
  ...
  -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
  ---
-- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
+  - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
  ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')
  ---
-- - ['T1', 'I1', '1 1']
-  - ['T1', 'T1', '1 1']
-  - ['T2', 'I1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - [0, '1 1']
+  - [1, '1 1']
+  - [0, '1 1']
+  - [1, '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\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
  ---
-- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
-  - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
-  - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
+- - [0, '1', '0', '0', !!binary kQE=]
+  - [1, '1', '0', '0', !!binary kQI=]
+  - [0, '1', '0', '0', !!binary kQE=]
  ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')
  ---
-- - ['T1', 'I1', '1 1']
-  - ['T1', 'T1', '1 1']
-  - ['T2', 'T2', '1 1']
+- - [0, '1 1']
+  - [1, '1 1']
+  - [0, '1 1']
  ...
  --Cleaning up.
  box.sql.execute("DROP TABLE t1;")
diff --git a/test/sql/sql-statN-index-drop.test.lua 
b/test/sql/sql-statN-index-drop.test.lua
index 35f2291..9ce77a5 100644
--- a/test/sql/sql-statN-index-drop.test.lua
+++ b/test/sql/sql-statN-index-drop.test.lua
@@ -14,15 +14,15 @@ box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
  box.sql.execute("ANALYZE;")

  -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
+box.sql.execute('SELECT "index_id","stat" 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\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')

  --Cleaning up.
  box.sql.execute("DROP TABLE t1;")
@@ -41,15 +41,15 @@ box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
  box.sql.execute("ANALYZE;")

  -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
+box.sql.execute('SELECT "index_id","stat" 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\";")
+box.sql.execute('SELECT "index_id","neq","nlt","ndlt","sample" FROM 
"_sql_stat4";')
+box.sql.execute('SELECT "index_id","stat" FROM "_sql_stat1";')

  --Cleaning up.
  box.sql.execute("DROP TABLE t1;")
diff --git a/test/sql/upgrade.result b/test/sql/upgrade.result
index 5e7d851..9326209 100644
--- a/test/sql/upgrade.result
+++ b/test/sql/upgrade.result
@@ -30,14 +30,15 @@ box.space._space.index['name']:get('_trigger')
  ...
  box.space._space.index['name']:get('_sql_stat1')
  ---
-- [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-      'type': 'string'}, {'name': 'stat', 'type': 'string'}]]
+- [348, 1, '_sql_stat1', 'memtx', 0, {}, [{'name': 'space_id', 'type': 
'unsigned'},
+    {'name': 'index_id', 'type': 'unsigned'}, {'name': 'stat', 'type': 
'string'}]]
  ...
  box.space._space.index['name']:get('_sql_stat4')
  ---
-- [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'tbl', 'type': 
'string'}, {'name': 'idx',
-      'type': 'string'}, {'name': 'neq', 'type': 'string'}, {'name': 
'nlt', 'type': 'string'},
-    {'name': 'ndlt', 'type': 'string'}, {'name': 'sample', 'type': 
'scalar'}]]
+- [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'space_id', 'type': 
'unsigned'},
+    {'name': 'index_id', 'type': 'unsigned'}, {'name': 'neq', 'type': 
'string'}, {
+      'name': 'nlt', 'type': 'string'}, {'name': 'ndlt', 'type': 
'string'}, {'name': 'sample',
+      'type': 'scalar'}]]
  ...
  box.space._index:get({box.space._space.index['name']:get('_trigger').id, 0})
  ---
@@ -45,12 +46,12 @@ 
box.space._index:get({box.space._space.index['name']:get('_trigger').id, 0})
  ...
  box.space._index:get({box.space._space.index['name']:get('_sql_stat1').id, 0})
  ---
-- [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string']]]
+- [348, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned']]]
  ...
  box.space._index:get({box.space._space.index['name']:get('_sql_stat4').id, 0})
  ---
-- [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'string'], [1, 
'string'], [5,
-      'scalar']]]
+- [349, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 
'unsigned'],
+    [5, 'scalar']]]
  ...
  box.space._schema:format()
  ---


[-- Attachment #2: Type: text/html, Size: 118048 bytes --]

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

end of thread, other threads:[~2018-12-08 12:40 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-08-31 10:39 [tarantool-patches] [PATCH v2 1/1] sql: hold in stat tables space/index id instead of name imeevma
2018-09-05 23:13 ` [tarantool-patches] " n.pettik
2018-09-07 16:49   ` Imeev Mergen
2018-09-30 23:40     ` n.pettik
2018-10-01 11:20       ` n.pettik
2018-10-02 16:42       ` [tarantool-patches] Re[2]: [tarantool-patches] " Мерген Имеев
2018-10-09 12:36         ` [tarantool-patches] " n.pettik
2018-10-10 17:17           ` Imeev Mergen
2018-10-11 11:41             ` n.pettik
2018-10-11 14:56               ` Imeev Mergen
2018-10-11 15:00                 ` n.pettik
2018-11-17 14:09                   ` Imeev Mergen
2018-11-19 10:27                     ` Vladislav Shpilevoy
2018-11-19 10:38                       ` Vladislav Shpilevoy
2018-11-21 16:00                       ` Konstantin Osipov
2018-11-21 16:11                         ` Vladislav Shpilevoy
2018-12-08 12:39                       ` Imeev Mergen

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