Use N separate cursors when scanning an index with N columns to collect sqlite_stat4 data. This fixes a problem with collecting incorrect nEq values from multi-column indexes.
FossilOrigin-Name: 3a71afe67418ce00097cd9714c395fe9ff16f23b
This commit is contained in:
parent
c55521a60b
commit
e043201d3d
16
manifest
16
manifest
@ -1,5 +1,5 @@
|
||||
C Fix\sa\scouple\sof\sproblems\sin\scode\srelated\sto\ssqlite_stat4.
|
||||
D 2013-08-05T05:34:30.270
|
||||
C Use\sN\sseparate\scursors\swhen\sscanning\san\sindex\swith\sN\scolumns\sto\scollect\ssqlite_stat4\sdata.\sThis\sfixes\sa\sproblem\swith\scollecting\sincorrect\snEq\svalues\sfrom\smulti-column\sindexes.
|
||||
D 2013-08-05T18:00:56.397
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 5e41da95d92656a5004b03d3576e8b226858a28e
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@ -157,7 +157,7 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
|
||||
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
|
||||
F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad
|
||||
F src/alter.c f8db986c03eb0bfb221523fc9bbb9d0b70de3168
|
||||
F src/analyze.c b9fb000c4dcac4f7f3dbd1a8832ffdacf17a4d35
|
||||
F src/analyze.c a0979f7fdc8cd724f8e646ba9ef6ca1e56fa7491
|
||||
F src/attach.c 1816f5a9eea8d2010fc2b22b44f0f63eb3a62704
|
||||
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
|
||||
F src/backup.c 43b348822db3e4cef48b2ae5a445fbeb6c73a165
|
||||
@ -243,7 +243,7 @@ F src/test_config.c 636ecd15a6ba18bf97a590b5a21f47573c8c2b65
|
||||
F src/test_demovfs.c 20a4975127993f4959890016ae9ce5535a880094
|
||||
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
|
||||
F src/test_fs.c 8f786bfd0ad48030cf2a06fb1f050e9c60a150d7
|
||||
F src/test_func.c 3a8dd37c08ab43b76d38eea2836e34a3897bf170
|
||||
F src/test_func.c fcd238feb694332d5962ee08578ef30ff4ac6559
|
||||
F src/test_hexio.c abfdecb6fa58c354623978efceb088ca18e379cd
|
||||
F src/test_init.c 3cbad7ce525aec925f8fda2192d576d47f0d478a
|
||||
F src/test_intarray.c 87847c71c3c36889c0bcc9c4baf9d31881665d61
|
||||
@ -308,7 +308,7 @@ F test/analyze5.test e3eece09761c935ec0b85dc4ed70dbf6cac1ed77
|
||||
F test/analyze6.test 3c01e084309706a1033f850330ea24f6f7846297
|
||||
F test/analyze7.test c0af22c5e0140e2e4ac556a21c2b6fff58229c98
|
||||
F test/analyze8.test 092425439c12f62f9d5c3127e2b4f6e7b3e170cc
|
||||
F test/analyze9.test 2ffe8f627b8f0309a72c2ff390effa430b1ef2d8
|
||||
F test/analyze9.test 30479ec9ac395e77cfced52496845500768d45f9
|
||||
F test/async.test 1d0e056ba1bb9729283a0f22718d3a25e82c277b
|
||||
F test/async2.test c0a9bd20816d7d6a2ceca7b8c03d3d69c28ffb8b
|
||||
F test/async3.test d73a062002376d7edc1fe3edff493edbec1fc2f7
|
||||
@ -1106,7 +1106,7 @@ F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
|
||||
F tool/wherecosttest.c f407dc4c79786982a475261866a161cd007947ae
|
||||
F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
|
||||
P 2beea303a1d609cd2ff252412c50b966b9e5e8f1
|
||||
R d4282412612019a7b27b2fb043ed04d3
|
||||
P badd24d987240db5528b37d1c177431617079f9b
|
||||
R 2711e68fa0a11591cfe5a22173f05afc
|
||||
U dan
|
||||
Z 13683b9d54d5fd3b76b2568a74c36c12
|
||||
Z ccb7ab66bd998be3fb0311148b3ca62c
|
||||
|
@ -1 +1 @@
|
||||
badd24d987240db5528b37d1c177431617079f9b
|
||||
3a71afe67418ce00097cd9714c395fe9ff16f23b
|
366
src/analyze.c
366
src/analyze.c
@ -391,7 +391,8 @@ static void stat4Push(
|
||||
for(i=0; i<p->nCol; i++){
|
||||
pSample->anEq[i] = sqlite3_value_int64(aEq[i]);
|
||||
pSample->anLt[i] = sqlite3_value_int64(aLt[i]);
|
||||
pSample->anDLt[i] = sqlite3_value_int64(aDLt[i]);
|
||||
pSample->anDLt[i] = sqlite3_value_int64(aDLt[i])-1;
|
||||
assert( sqlite3_value_int64(aDLt[i])>0 );
|
||||
}
|
||||
|
||||
/* Find the new minimum */
|
||||
@ -564,11 +565,13 @@ static void analyzeOneTable(
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Establish a read-lock on the table at the shared-cache level. */
|
||||
/* Establish a read-lock on the table at the shared-cache level.
|
||||
** Also open a read-only cursor on the table. */
|
||||
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
|
||||
|
||||
iIdxCur = pParse->nTab++;
|
||||
iTabCur = pParse->nTab++;
|
||||
sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0);
|
||||
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
int nCol; /* Number of columns indexed by pIdx */
|
||||
KeyInfo *pKey; /* KeyInfo structure for pIdx */
|
||||
@ -577,12 +580,12 @@ static void analyzeOneTable(
|
||||
|
||||
int regRowid; /* Register for rowid of current row */
|
||||
int regPrev; /* First in array of previous values */
|
||||
int regDLt; /* First in array of nDlt registers */
|
||||
int regDLte; /* First in array of nDlt registers */
|
||||
int regLt; /* First in array of nLt registers */
|
||||
int regEq; /* First in array of nEq registers */
|
||||
int regCnt; /* Number of index entries */
|
||||
|
||||
int addrGoto;
|
||||
int regEof; /* True once cursors are all at EOF */
|
||||
int endOfScan; /* Label to jump to once scan is finished */
|
||||
|
||||
if( pOnlyIdx && pOnlyIdx!=pIdx ) continue;
|
||||
if( pIdx->pPartIdxWhere==0 ) needTableCnt = 0;
|
||||
@ -592,21 +595,99 @@ static void analyzeOneTable(
|
||||
if( aChngAddr==0 ) continue;
|
||||
pKey = sqlite3IndexKeyinfo(pParse, pIdx);
|
||||
|
||||
/* Open a cursor to the index to be analyzed. */
|
||||
assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) );
|
||||
sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb,
|
||||
(char *)pKey, P4_KEYINFO_HANDOFF);
|
||||
VdbeComment((v, "%s", pIdx->zName));
|
||||
|
||||
/* Populate the register containing the index name. */
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, pIdx->zName, 0);
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
if( once ){
|
||||
once = 0;
|
||||
sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead);
|
||||
/*
|
||||
** The following pseudo-code demonstrates the way the VM scans an index
|
||||
** to call stat4_push() and collect the values for the sqlite_stat1
|
||||
** entry. The code below is for an index with 2 columns. The actual
|
||||
** VM code generated may be for any number of columns.
|
||||
**
|
||||
** One cursor is opened for each column in the index (nCol). All cursors
|
||||
** scan concurrently the index from start to end. All variables used in
|
||||
** the pseudo-code are initialized to zero.
|
||||
**
|
||||
** Rewind csr(0)
|
||||
** Rewind csr(1)
|
||||
**
|
||||
** next_0:
|
||||
** regPrev(0) = csr(0)[0]
|
||||
** regDLte(0) += 1
|
||||
** regLt(0) += regEq(0)
|
||||
** regEq(0) = 0
|
||||
** do {
|
||||
** regEq(0) += 1
|
||||
** Next csr(0)
|
||||
** }while ( csr(0)[0] == regPrev(0) )
|
||||
**
|
||||
** next_1:
|
||||
** regPrev(1) = csr(1)[1]
|
||||
** regDLte(1) += 1
|
||||
** regLt(1) += regEq(1)
|
||||
** regEq(1) = 0
|
||||
** regRowid = csr(1)[rowid] // innermost cursor only
|
||||
** do {
|
||||
** regEq(1) += 1
|
||||
** regCnt += 1 // innermost cursor only
|
||||
** Next csr(1)
|
||||
** }while ( csr(1)[0..1] == regPrev(0..1) )
|
||||
**
|
||||
** stat4_push(regRowid, regEq, regLt, regDLte);
|
||||
**
|
||||
** if( eof( csr(1) ) ) goto endOfScan
|
||||
** if( csr(1)[0] != regPrev(0) ) goto next_0
|
||||
** goto next_1
|
||||
**
|
||||
** endOfScan:
|
||||
** // done!
|
||||
**
|
||||
** The last two lines above modify the contents of the regDLte array
|
||||
** so that each element contains the number of distinct key prefixes
|
||||
** of the corresponding length. As required to calculate the contents
|
||||
** of the sqlite_stat1 entry.
|
||||
**
|
||||
** Currently, the last memory cell allocated (that with the largest
|
||||
** integer identifier) is regStat4. Immediately following regStat4
|
||||
** we allocate the following:
|
||||
**
|
||||
** regRowid - 1 register
|
||||
** regEq - nCol registers
|
||||
** regLt - nCol registers
|
||||
** regDLte - nCol registers
|
||||
** regCnt - 1 register
|
||||
** regPrev - nCol registers
|
||||
** regEof - 1 register
|
||||
**
|
||||
** The regRowid, regEq, regLt and regDLte registers must be positioned in
|
||||
** that order immediately following regStat4 so that they can be passed
|
||||
** to the stat4_push() function.
|
||||
**
|
||||
** All of the above are initialized to contain integer value 0.
|
||||
*/
|
||||
regRowid = regStat4+1; /* Rowid argument */
|
||||
regEq = regRowid+1; /* First in array of nEq value registers */
|
||||
regLt = regEq+nCol; /* First in array of nLt value registers */
|
||||
regDLte = regLt+nCol; /* First in array of nDLt value registers */
|
||||
regCnt = regDLte+nCol; /* Row counter */
|
||||
regPrev = regCnt+1; /* First in array of prev. value registers */
|
||||
regEof = regPrev+nCol; /* True once last row read from index */
|
||||
if( regEof+1>pParse->nMem ){
|
||||
pParse->nMem = regPrev+nCol;
|
||||
}
|
||||
|
||||
/* Open a read-only cursor for each column of the index. */
|
||||
assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) );
|
||||
iIdxCur = pParse->nTab++;
|
||||
pParse->nTab += (nCol-1);
|
||||
for(i=0; i<nCol; i++){
|
||||
int iMode = (i==0 ? P4_KEYINFO_HANDOFF : P4_KEYINFO);
|
||||
sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur+i, pIdx->tnum, iDb);
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)pKey, iMode);
|
||||
VdbeComment((v, "%s", pIdx->zName));
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
/* Invoke the stat4_init() function. The arguments are:
|
||||
**
|
||||
** * the number of rows in the index,
|
||||
@ -621,181 +702,98 @@ static void analyzeOneTable(
|
||||
sqlite3VdbeChangeP5(v, 3);
|
||||
#endif /* SQLITE_ENABLE_STAT4 */
|
||||
|
||||
/* The block of (1 + 4*nCol) memory cells initialized here is used
|
||||
** as follows:
|
||||
**
|
||||
** TODO: Update this comment:
|
||||
**
|
||||
** iMem:
|
||||
** Loop counter. The number of rows visited so far, including
|
||||
** the current row (i.e. this register is set to 1 for the
|
||||
** first iteration of the loop).
|
||||
**
|
||||
** iMem+1 .. iMem+nCol:
|
||||
** Number of distinct index entries seen so far, considering
|
||||
** the left-most N columns only, where N is between 1 and nCol,
|
||||
** inclusive.
|
||||
**
|
||||
** iMem+nCol+1 .. Mem+2*nCol:
|
||||
** Previous value of indexed columns, from left to right.
|
||||
**
|
||||
** Cells iMem through iMem+nCol are initialized to 0. The others are
|
||||
** initialized to contain an SQL NULL.
|
||||
*/
|
||||
regRowid = regStat4+1; /* Rowid argument */
|
||||
regEq = regRowid+1; /* First in array of nEq value registers */
|
||||
regLt = regEq+nCol; /* First in array of nLt value registers */
|
||||
regDLt = regLt+nCol; /* First in array of nDLt value registers */
|
||||
regCnt = regDLt+nCol; /* Row counter */
|
||||
regPrev = regCnt+1; /* First in array of prev. value registers */
|
||||
|
||||
if( regPrev+1>pParse->nMem ){
|
||||
pParse->nMem = regPrev+1;
|
||||
}
|
||||
for(i=0; i<2+nCol*4; i++){
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowid+i);
|
||||
/* Initialize all the memory registers allocated above to 0. */
|
||||
for(i=regRowid; i<=regEof; i++){
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, i);
|
||||
}
|
||||
|
||||
/*
|
||||
** Loop through all entries in the b-tree index. Pseudo-code for the
|
||||
** body of the loop is as follows:
|
||||
**
|
||||
** foreach i IN index {
|
||||
** regCnt += 1
|
||||
**
|
||||
** if( regEq(0)==0 ) goto ne_0;
|
||||
**
|
||||
** if i(0) != regPrev(0) {
|
||||
** stat4_push(regRowid, regEq, regLt, regDLt);
|
||||
** goto ne_0;
|
||||
** }
|
||||
** regEq(0) += 1
|
||||
**
|
||||
** if i(1) != regPrev(1){
|
||||
** stat4_push(regRowid, regEq, regLt, regDLt);
|
||||
** goto ne_1;
|
||||
** }
|
||||
** regEq(1) += 1
|
||||
**
|
||||
** goto all_eq;
|
||||
**
|
||||
** ne_0:
|
||||
** regPrev(0) = i(0)
|
||||
** if( regEq(0) != 0 ) regDLt(0) += 1
|
||||
** regLt(0) += regEq(0)
|
||||
** regEq(0) = 1
|
||||
**
|
||||
** ne_1:
|
||||
** regPrev(1) = $i(1)
|
||||
** if( regEq(1) != 0 ) regDLt(1) += 1
|
||||
** regLt(1) += regEq(1)
|
||||
** regEq(1) = 1
|
||||
**
|
||||
** all_eq:
|
||||
** regRowid = i(rowid)
|
||||
** }
|
||||
**
|
||||
** stat4_push(regRowid, regEq, regLt, regDLt);
|
||||
**
|
||||
** if( regEq(0) != 0 ) regDLt(0) += 1
|
||||
** if( regEq(1) != 0 ) regDLt(1) += 1
|
||||
**
|
||||
** The last two lines above modify the contents of the regDLt array
|
||||
** so that each element contains the number of distinct key prefixes
|
||||
** of the corresponding length. As required to calculate the contents
|
||||
** of the sqlite_stat1 entry.
|
||||
**
|
||||
** Note: if regEq(0)==0, stat4_push() is a no-op.
|
||||
*/
|
||||
endOfLoop = sqlite3VdbeMakeLabel(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, iIdxCur, endOfLoop);
|
||||
topOfLoop = sqlite3VdbeCurrentAddr(v);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regCnt, 1); /* Increment row counter */
|
||||
|
||||
/* This jump is taken for the first iteration of the loop only.
|
||||
**
|
||||
** if( regEq(0)==0 ) goto ne_0;
|
||||
*/
|
||||
addrIfNot = sqlite3VdbeAddOp1(v, OP_IfNot, regEq);
|
||||
|
||||
/* Code these bits:
|
||||
**
|
||||
** if i(N) != regPrev(N) {
|
||||
** stat4_push(regRowid, regEq, regLt, regDLt);
|
||||
** goto ne_N;
|
||||
** }
|
||||
** regEq(N) += 1
|
||||
*/
|
||||
/* Rewind all cursors open on the index. If the table is entry, this
|
||||
** will cause control to jump to address endOfScan immediately. */
|
||||
endOfScan = sqlite3VdbeMakeLabel(v);
|
||||
for(i=0; i<nCol; i++){
|
||||
char *pColl; /* Pointer to CollSeq cast to (char*) */
|
||||
assert( pIdx->azColl && pIdx->azColl[i]!=0 );
|
||||
pColl = (char*)sqlite3LocateCollSeq(pParse, pIdx->azColl[i]);
|
||||
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regCol);
|
||||
sqlite3VdbeAddOp4(v, OP_Eq, regCol, 0, regPrev+i, pColl, P4_COLLSEQ);
|
||||
sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
|
||||
|
||||
sqlite3VdbeAddOp3(v, OP_Function, 1, regStat4, regTemp2);
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)&stat4PushFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 2 + 3*nCol);
|
||||
|
||||
aChngAddr[i] = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
VdbeComment((v, "jump if column %d changed", i));
|
||||
|
||||
sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-3);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regEq+i, 1);
|
||||
VdbeComment((v, "incr repeat count"));
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, iIdxCur+i, endOfScan);
|
||||
}
|
||||
|
||||
/* Code the "continue" */
|
||||
addrGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, endOfLoop);
|
||||
|
||||
/* And now these:
|
||||
**
|
||||
** ne_N:
|
||||
** regPrev(N) = i(N)
|
||||
** if( regEq(N) != N ) regDLt(N) += 1
|
||||
** regLt(N) += regEq(N)
|
||||
** regEq(N) = 1
|
||||
*/
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3VdbeJumpHere(v, aChngAddr[i]); /* Set jump dest for the OP_Ne */
|
||||
if( i==0 ){
|
||||
sqlite3VdbeJumpHere(v, addrIfNot); /* Jump dest for OP_IfNot */
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regPrev+i);
|
||||
sqlite3VdbeAddOp2(v, OP_IfNot, regEq+i, sqlite3VdbeCurrentAddr(v)+2);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regDLt+i, 1);
|
||||
char *pColl = (char*)sqlite3LocateCollSeq(pParse, pIdx->azColl[i]);
|
||||
int iCsr = iIdxCur+i;
|
||||
int iDo;
|
||||
int iNe; /* Jump here to exit do{...}while loop */
|
||||
int j;
|
||||
int bInner = (i==(nCol-1)); /* True for innermost cursor */
|
||||
|
||||
/* Implementation of the following pseudo-code:
|
||||
**
|
||||
** regPrev(i) = csr(i)[i]
|
||||
** regDLte(i) += 1
|
||||
** regLt(i) += regEq(i)
|
||||
** regEq(i) = 0
|
||||
** regRowid = csr(i)[rowid] // innermost cursor only
|
||||
*/
|
||||
aChngAddr[i] = sqlite3VdbeAddOp3(v, OP_Column, iCsr, i, regPrev+i);
|
||||
VdbeComment((v, "regPrev(%d) = csr(%d)(%d)", i, i, i));
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regDLte+i, 1);
|
||||
VdbeComment((v, "regDLte(%d) += 1", i));
|
||||
sqlite3VdbeAddOp3(v, OP_Add, regEq+i, regLt+i, regLt+i);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 1, regEq+i);
|
||||
VdbeComment((v, "regLt(%d) += regEq(%d)", i, i));
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regEq+i);
|
||||
VdbeComment((v, "regEq(%d) = 0", i));
|
||||
if( bInner ) sqlite3VdbeAddOp2(v, OP_IdxRowid, iCsr, regRowid);
|
||||
|
||||
/* This bit:
|
||||
**
|
||||
** do {
|
||||
** regEq(i) += 1
|
||||
** regCnt += 1 // innermost cursor only
|
||||
** Next csr(i)
|
||||
** if( Eof csr(i) ){
|
||||
** regEof = 1 // innermost cursor only
|
||||
** break
|
||||
** }
|
||||
** }while ( csr(i)[0..i] == regPrev(0..i) )
|
||||
*/
|
||||
iDo = sqlite3VdbeAddOp2(v, OP_AddImm, regEq+i, 1);
|
||||
VdbeComment((v, "regEq(%d) += 1", i));
|
||||
if( bInner ){
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regCnt, 1);
|
||||
VdbeComment((v, "regCnt += 1"));
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iCsr, sqlite3VdbeCurrentAddr(v)+2+bInner);
|
||||
if( bInner ) sqlite3VdbeAddOp2(v, OP_Integer, 1, regEof);
|
||||
iNe = sqlite3VdbeMakeLabel(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, iNe);
|
||||
for(j=0; j<=i; j++){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iCsr, j, regCol);
|
||||
sqlite3VdbeAddOp4(v, OP_Ne, regCol, iNe, regPrev+j, pColl, P4_COLLSEQ);
|
||||
sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
|
||||
VdbeComment((v, "if( regPrev(%d) != csr(%d)(%d) )", j, i, j));
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, iDo);
|
||||
sqlite3VdbeResolveLabel(v, iNe);
|
||||
}
|
||||
sqlite3DbFree(db, aChngAddr);
|
||||
|
||||
/*
|
||||
** all_eq:
|
||||
** regRowid = i(rowid)
|
||||
*/
|
||||
sqlite3VdbeJumpHere(v, addrGoto);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid);
|
||||
|
||||
/* The end of the loop that iterates through all index entries. Always
|
||||
** jump here after updating the iMem+1...iMem+1+nCol counters. */
|
||||
sqlite3VdbeResolveLabel(v, endOfLoop);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, topOfLoop);
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iIdxCur);
|
||||
|
||||
/* Final invocation of stat4_push() */
|
||||
/* Invoke stat4_push() */
|
||||
sqlite3VdbeAddOp3(v, OP_Function, 1, regStat4, regTemp2);
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)&stat4PushFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 2 + 3*nCol);
|
||||
|
||||
/* Finally:
|
||||
**
|
||||
** if( regEq(0) != 0 ) regDLt(0) += 1
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_If, regEof, endOfScan);
|
||||
for(i=0; i<nCol-1; i++){
|
||||
char *pColl = (char*)sqlite3LocateCollSeq(pParse, pIdx->azColl[i]);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur+nCol-1, i, regCol);
|
||||
sqlite3VdbeAddOp3(v, OP_Ne, regCol, aChngAddr[i], regPrev+i);
|
||||
sqlite3VdbeChangeP4(v, -1, pColl, P4_COLLSEQ);
|
||||
sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, aChngAddr[nCol-1]);
|
||||
sqlite3DbFree(db, aChngAddr);
|
||||
|
||||
sqlite3VdbeResolveLabel(v, endOfScan);
|
||||
|
||||
/* Close all the cursors */
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3VdbeAddOp2(v, OP_IfNot, regEq+i, sqlite3VdbeCurrentAddr(v)+2);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regDLt+i, 1);
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iIdxCur+i);
|
||||
VdbeComment((v, "close index cursor %d", i));
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
@ -858,9 +856,9 @@ static void analyzeOneTable(
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, regTemp, 0, " ", 0);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regStat1, regStat1);
|
||||
sqlite3VdbeAddOp3(v, OP_Add, regCnt, regDLt+i, regTemp);
|
||||
sqlite3VdbeAddOp3(v, OP_Add, regCnt, regDLte+i, regTemp);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regTemp, -1);
|
||||
sqlite3VdbeAddOp3(v, OP_Divide, regDLt+i, regTemp, regTemp);
|
||||
sqlite3VdbeAddOp3(v, OP_Divide, regDLte+i, regTemp, regTemp);
|
||||
sqlite3VdbeAddOp1(v, OP_ToInt, regTemp);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regStat1, regStat1);
|
||||
}
|
||||
@ -876,10 +874,8 @@ static void analyzeOneTable(
|
||||
** name and the row count as the content.
|
||||
*/
|
||||
if( pOnlyIdx==0 && needTableCnt ){
|
||||
sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pTab->tnum, iDb);
|
||||
VdbeComment((v, "%s", pTab->zName));
|
||||
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat1);
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iIdxCur);
|
||||
sqlite3VdbeAddOp2(v, OP_Count, iTabCur, regStat1);
|
||||
jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1);
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname);
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0);
|
||||
@ -888,6 +884,10 @@ static void analyzeOneTable(
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
|
||||
sqlite3VdbeJumpHere(v, jZeroRows);
|
||||
}
|
||||
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iTabCur);
|
||||
|
||||
/* TODO: Not sure about this... */
|
||||
if( pParse->nMem<regRec ) pParse->nMem = regRec;
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,9 @@
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
|
||||
|
||||
/*
|
||||
** Allocate nByte bytes of space using sqlite3_malloc(). If the
|
||||
@ -458,6 +461,99 @@ static void real2hex(
|
||||
sqlite3_result_text(context, zOut, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: test_decode(record)
|
||||
**
|
||||
** This function implements an SQL user-function that accepts a blob
|
||||
** containing a formatted database record as its only argument. It returns
|
||||
** a tcl list (type SQLITE_TEXT) containing each of the values stored
|
||||
** in the record.
|
||||
*/
|
||||
static void test_decode(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
u8 *pRec;
|
||||
u8 *pEndHdr; /* Points to one byte past record header */
|
||||
u8 *pHdr; /* Current point in record header */
|
||||
u8 *pBody; /* Current point in record data */
|
||||
u64 nHdr; /* Bytes in record header */
|
||||
Tcl_Obj *pRet; /* Return value */
|
||||
|
||||
pRet = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(pRet);
|
||||
|
||||
assert( argc==1 );
|
||||
pRec = (u8*)sqlite3_value_blob(argv[0]);
|
||||
|
||||
pHdr = pRec + sqlite3GetVarint(pRec, &nHdr);
|
||||
pBody = pEndHdr = &pRec[nHdr];
|
||||
while( pHdr<pEndHdr ){
|
||||
Tcl_Obj *pVal = 0;
|
||||
u64 iSerialType;
|
||||
Mem mem;
|
||||
|
||||
memset(&mem, 0, sizeof(mem));
|
||||
mem.db = db;
|
||||
mem.enc = SQLITE_UTF8;
|
||||
pHdr += sqlite3GetVarint(pHdr, &iSerialType);
|
||||
pBody += sqlite3VdbeSerialGet(pBody, (u32)iSerialType, &mem);
|
||||
|
||||
sqlite3VdbeMemStoreType(&mem);
|
||||
switch( sqlite3_value_type(&mem) ){
|
||||
case SQLITE_TEXT:
|
||||
pVal = Tcl_NewStringObj((const char*)sqlite3_value_text(&mem), -1);
|
||||
break;
|
||||
|
||||
case SQLITE_BLOB: {
|
||||
char hexdigit[] = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
|
||||
};
|
||||
int n = sqlite3_value_bytes(&mem);
|
||||
u8 *z = (u8*)sqlite3_value_blob(&mem);
|
||||
int i;
|
||||
pVal = Tcl_NewStringObj("x'", -1);
|
||||
for(i=0; i<n; i++){
|
||||
char hex[3];
|
||||
hex[0] = hexdigit[((z[i] >> 4) & 0x0F)];
|
||||
hex[1] = hexdigit[(z[i] & 0x0F)];
|
||||
hex[2] = '\0';
|
||||
Tcl_AppendStringsToObj(pVal, hex, 0);
|
||||
}
|
||||
Tcl_AppendStringsToObj(pVal, "'", 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case SQLITE_FLOAT:
|
||||
pVal = Tcl_NewDoubleObj(sqlite3_value_double(&mem));
|
||||
break;
|
||||
|
||||
case SQLITE_INTEGER:
|
||||
pVal = Tcl_NewWideIntObj(sqlite3_value_int64(&mem));
|
||||
break;
|
||||
|
||||
case SQLITE_NULL:
|
||||
pVal = Tcl_NewStringObj("NULL", -1);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert( 0 );
|
||||
}
|
||||
|
||||
Tcl_ListObjAppendElement(0, pRet, pVal);
|
||||
|
||||
if( mem.zMalloc ){
|
||||
sqlite3DbFree(db, mem.zMalloc);
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_result_text(context, Tcl_GetString(pRet), -1, SQLITE_TRANSIENT);
|
||||
Tcl_DecrRefCount(pRet);
|
||||
}
|
||||
|
||||
|
||||
static int registerTestFunctions(sqlite3 *db){
|
||||
static const struct {
|
||||
@ -482,6 +578,7 @@ static int registerTestFunctions(sqlite3 *db){
|
||||
{ "test_isolation", 2, SQLITE_UTF8, test_isolation},
|
||||
{ "test_counter", 1, SQLITE_UTF8, counterFunc},
|
||||
{ "real2hex", 1, SQLITE_UTF8, real2hex},
|
||||
{ "test_decode", 1, SQLITE_UTF8, test_decode},
|
||||
};
|
||||
int i;
|
||||
|
||||
|
@ -37,10 +37,21 @@ do_test 1.0 {
|
||||
execsql { CREATE INDEX i1 ON t1(a, b) }
|
||||
} {}
|
||||
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
ANALYZE;
|
||||
} {}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
SELECT tbl,idx,nEq,nLt,nDLt,test_decode(sample) FROM sqlite_stat4;
|
||||
} {
|
||||
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)}
|
||||
}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
SELECT tbl,idx,nEq,nLt,nDLt,s(sample) FROM sqlite_stat4;
|
||||
} {
|
||||
@ -52,5 +63,55 @@ do_execsql_test 1.2 {
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# This is really just to test SQL user function "test_decode".
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 2.1 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
INSERT INTO t1 VALUES('some text', 14, NULL);
|
||||
INSERT INTO t1 VALUES(22.0, NULL, x'656667');
|
||||
CREATE INDEX i1 ON t1(a, b, c);
|
||||
ANALYZE;
|
||||
SELECT test_decode(sample) FROM sqlite_stat4;
|
||||
} {
|
||||
{22.0 NULL x'656667'}
|
||||
{{some text} 14 NULL}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 3.1 {
|
||||
CREATE TABLE t2(a, b);
|
||||
CREATE INDEX i2 ON t2(a, b);
|
||||
BEGIN;
|
||||
}
|
||||
|
||||
do_test 3.2 {
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
set a [expr $i / 10]
|
||||
set b [expr int(rand() * 15.0)]
|
||||
execsql { INSERT INTO t2 VALUES($a, $b) }
|
||||
}
|
||||
execsql COMMIT
|
||||
} {}
|
||||
|
||||
db func lindex lindex
|
||||
|
||||
# Each value of "a" occurs exactly 10 times in the table.
|
||||
#
|
||||
do_execsql_test 3.3.1 {
|
||||
SELECT count(*) FROM t2 GROUP BY a;
|
||||
} [lrange [string repeat "10 " 100] 0 99]
|
||||
|
||||
# The first element in the "nEq" list of all samples should therefore be 10.
|
||||
#
|
||||
do_execsql_test 3.3.2 {
|
||||
ANALYZE;
|
||||
SELECT lindex(nEq, 0) FROM sqlite_stat4;
|
||||
} [lrange [string repeat "10 " 100] 0 23]
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user