From 43085d742548e19d3a61c26c547d4dc3cf42bf36 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 3 Oct 2014 19:16:53 +0000 Subject: [PATCH 1/4] Improve the accuracy of the estimates used when searching an index for values not present in any stat4 samples under some circumstances. FossilOrigin-Name: e6f7f97dbc677c9f01b23142928c3fa7307c2fba --- manifest | 19 +++++++++-------- manifest.uuid | 2 +- src/analyze.c | 54 ++++++++++++++++++++++++++++++++++++------------- src/sqliteInt.h | 1 + 4 files changed, 53 insertions(+), 23 deletions(-) diff --git a/manifest b/manifest index 1c0365b036..f180b21817 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\srequirements\smarks\son\sthe\ssqlite3_db_status()\sinterface\simplementation.\nFix\sa\stypo\sin\sthe\sdocumentation.\s\sFix\sthe\snew\ssqlite3_result_text64()\sroutine\nso\sthat\sit\sworks\scorrectly\swith\san\sencoding\sparameter\sof\sSQLITE_UTF16. -D 2014-10-03T16:00:51.115 +C Improve\sthe\saccuracy\sof\sthe\sestimates\sused\swhen\ssearching\san\sindex\sfor\svalues\snot\spresent\sin\sany\sstat4\ssamples\sunder\ssome\scircumstances. +D 2014-10-03T19:16:53.018 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in cf57f673d77606ab0f2d9627ca52a9ba1464146a F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -166,7 +166,7 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 3d8b83c91651f53472ca17599dae3457b8b89494 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a F src/alter.c ba266a779bc7ce10e52e59e7d3dc79fa342e8fdb -F src/analyze.c 6290a109be876daaa242cd7216f97240f5401776 +F src/analyze.c 418c2fc20cd36f1acc82456b5bd9baae77dbda78 F src/attach.c f4e94df2d1826feda65eb0939f7f6f5f923a0ad9 F src/auth.c d8abcde53426275dab6243b441256fcd8ccbebb2 F src/backup.c a31809c65623cc41849b94d368917f8bb66e6a7e @@ -232,7 +232,7 @@ F src/shell.c 38f627b0885191357f55902a3ac199de90d79715 F src/sqlite.h.in a0b09ea5f73f3629c20b9788e0cde2a70f1703f5 F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad F src/sqlite3ext.h 17d487c3c91b0b8c584a32fbeb393f6f795eea7d -F src/sqliteInt.h 5a430c5443717d7c5e2c224f9dcc2534348dc3f6 +F src/sqliteInt.h 3e4bd1b2288528b6a7f2d52709618572b422ab7e F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 961d5926e5a8fda611d385ec22c226b8635cd1cb F src/table.c 2e99ef7ef16187e17033d9398dc962ce22dab5cb @@ -1201,7 +1201,10 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 440705b98a3429b830ea85e71cc1e414bc6d8058 -R 8a35df4f3d17b4f437ae0dc791626bd3 -U drh -Z 3c9270c816943439d7ab9b669a742153 +P d2fc322728331ae2d147c8496129df5e3c655eb5 +R 57c99795ca217ceed92eda9a33e89730 +T *branch * stat4-avgeq +T *sym-stat4-avgeq * +T -sym-trunk * +U dan +Z 343ac493fd77cb5d6f7bcecf2a2a97e2 diff --git a/manifest.uuid b/manifest.uuid index 4d17ba50dd..ea230c321e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d2fc322728331ae2d147c8496129df5e3c655eb5 \ No newline at end of file +e6f7f97dbc677c9f01b23142928c3fa7307c2fba \ No newline at end of file diff --git a/src/analyze.c b/src/analyze.c index aec1f021ea..154033d067 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -1448,12 +1448,12 @@ static void decodeIntArray( #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 if( aOut ){ aOut[i] = v; - }else + } #else assert( aOut==0 ); UNUSED_PARAMETER(aOut); #endif - { + if( aLog ){ aLog[i] = sqlite3LogEst(v); } if( *z==' ' ) z++; @@ -1516,8 +1516,16 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){ z = argv[2]; if( pIndex ){ + int nCol = pIndex->nKeyCol+1; +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + tRowcnt * const aiRowEst = pIndex->aiRowEst = (tRowcnt*)sqlite3DbMallocZero( + pInfo->db, sizeof(tRowcnt) * nCol + ); +#else + tRowcnt * const aiRowEst = 0; +#endif pIndex->bUnordered = 0; - decodeIntArray((char*)z, pIndex->nKeyCol+1, 0, pIndex->aiRowLogEst, pIndex); + decodeIntArray((char*)z, nCol, aiRowEst, pIndex->aiRowLogEst, pIndex); if( pIndex->pPartIdxWhere==0 ) pTable->nRowLogEst = pIndex->aiRowLogEst[0]; }else{ Index fakeIdx; @@ -1576,25 +1584,38 @@ static void initAvgEq(Index *pIdx){ pIdx->aAvgEq[nCol] = 1; } for(iCol=0; iColnSample; int i; /* Used to iterate through samples */ tRowcnt sumEq = 0; /* Sum of the nEq values */ - tRowcnt nSum = 0; /* Number of terms contributing to sumEq */ tRowcnt avgEq = 0; - tRowcnt nDLt = pFinal->anDLt[iCol]; + tRowcnt nRow; /* Number of rows in index */ + i64 nSum100 = 0; /* Number of terms contributing to sumEq */ + i64 nDist100; /* Number of distinct values in index */ + + if( pIdx->aiRowEst==0 ){ + nRow = pFinal->anLt[iCol]; + nDist100 = (i64)100 * pFinal->anDLt[iCol]; + nSample--; + }else{ + nRow = pIdx->aiRowEst[0]; + nDist100 = ((i64)100 * pIdx->aiRowEst[0]) / pIdx->aiRowEst[iCol+1]; + } /* Set nSum to the number of distinct (iCol+1) field prefixes that - ** occur in the stat4 table for this index before pFinal. Set - ** sumEq to the sum of the nEq values for column iCol for the same - ** set (adding the value only once where there exist duplicate - ** prefixes). */ - for(i=0; i<(pIdx->nSample-1); i++){ - if( aSample[i].anDLt[iCol]!=aSample[i+1].anDLt[iCol] ){ + ** occur in the stat4 table for this index. Set sumEq to the sum of + ** the nEq values for column iCol for the same set (adding the value + ** only once where there exist duplicate prefixes). */ + for(i=0; inSample-1) + || aSample[i].anDLt[iCol]!=aSample[i+1].anDLt[iCol] + ){ sumEq += aSample[i].anEq[iCol]; - nSum++; + nSum100 += 100; } } - if( nDLt>nSum ){ - avgEq = (pFinal->anLt[iCol] - sumEq)/(nDLt - nSum); + + if( nDist100>nSum100 ){ + avgEq = ((i64)100 * (nRow - sumEq))/(nDist100 - nSum100); } if( avgEq==0 ) avgEq = 1; pIdx->aAvgEq[iCol] = avgEq; @@ -1846,6 +1867,11 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ rc = loadStat4(db, sInfo.zDatabase); db->lookaside.bEnabled = lookasideEnabled; } + for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){ + Index *pIdx = sqliteHashData(i); + sqlite3DbFree(db, pIdx->aiRowEst); + pIdx->aiRowEst = 0; + } #endif if( rc==SQLITE_NOMEM ){ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 695b63d753..74c36c8db5 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1801,6 +1801,7 @@ struct Index { int nSampleCol; /* Size of IndexSample.anEq[] and so on */ tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */ IndexSample *aSample; /* Samples of the left-most key */ + tRowcnt *aiRowEst; /* Non-logarithmic stat1 data for this table */ #endif }; From 0c1a18b2944ef0a3dedfb192a90927dc69b32068 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 3 Oct 2014 19:29:39 +0000 Subject: [PATCH 2/4] Fix a division-by-zero error that might occur if the sqlite_stat1 table is corrupt. FossilOrigin-Name: f9c053b23ece877a7fdbe82204a10592f2d24a2d --- manifest | 15 ++++++--------- manifest.uuid | 2 +- src/analyze.c | 2 +- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index f180b21817..db1dca9628 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improve\sthe\saccuracy\sof\sthe\sestimates\sused\swhen\ssearching\san\sindex\sfor\svalues\snot\spresent\sin\sany\sstat4\ssamples\sunder\ssome\scircumstances. -D 2014-10-03T19:16:53.018 +C Fix\sa\sdivision-by-zero\serror\sthat\smight\soccur\sif\sthe\ssqlite_stat1\stable\sis\scorrupt. +D 2014-10-03T19:29:39.807 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in cf57f673d77606ab0f2d9627ca52a9ba1464146a F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -166,7 +166,7 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 3d8b83c91651f53472ca17599dae3457b8b89494 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a F src/alter.c ba266a779bc7ce10e52e59e7d3dc79fa342e8fdb -F src/analyze.c 418c2fc20cd36f1acc82456b5bd9baae77dbda78 +F src/analyze.c 8d5a138936dab3436e67ca3a0f6466ad2f18d86b F src/attach.c f4e94df2d1826feda65eb0939f7f6f5f923a0ad9 F src/auth.c d8abcde53426275dab6243b441256fcd8ccbebb2 F src/backup.c a31809c65623cc41849b94d368917f8bb66e6a7e @@ -1201,10 +1201,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P d2fc322728331ae2d147c8496129df5e3c655eb5 -R 57c99795ca217ceed92eda9a33e89730 -T *branch * stat4-avgeq -T *sym-stat4-avgeq * -T -sym-trunk * +P e6f7f97dbc677c9f01b23142928c3fa7307c2fba +R d767aa2120b870307998a73a73f29d86 U dan -Z 343ac493fd77cb5d6f7bcecf2a2a97e2 +Z 9bc979da9e7cfcf210fc63943ac10f56 diff --git a/manifest.uuid b/manifest.uuid index ea230c321e..662bc404af 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e6f7f97dbc677c9f01b23142928c3fa7307c2fba \ No newline at end of file +f9c053b23ece877a7fdbe82204a10592f2d24a2d \ No newline at end of file diff --git a/src/analyze.c b/src/analyze.c index 154033d067..2f65fe3d3e 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -1592,7 +1592,7 @@ static void initAvgEq(Index *pIdx){ i64 nSum100 = 0; /* Number of terms contributing to sumEq */ i64 nDist100; /* Number of distinct values in index */ - if( pIdx->aiRowEst==0 ){ + if( pIdx->aiRowEst==0 || pIdx->aiRowEst[iCol+1]==0 ){ nRow = pFinal->anLt[iCol]; nDist100 = (i64)100 * pFinal->anDLt[iCol]; nSample--; From 75b170b16431b1b38aaf1bf64e29b8de5aec6325 Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 4 Oct 2014 00:07:44 +0000 Subject: [PATCH 3/4] Avoid leaking Index.aiRowEst memory if an OOM causes a rollback which deletes the index before the aiRowEst deletion code in sqlite3AnalysisLoad() routine has a chance to run. Since the aiRowEst now might be deleted from freeIndex() which does not always have a db pointer, make sure the aiRowEst memory is not held in lookaside. FossilOrigin-Name: efd87ba142723ba131fcc985db6eb45c5a3c637b --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/analyze.c | 7 ++++--- src/build.c | 3 +++ 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index db1dca9628..2b01d7bbcc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sdivision-by-zero\serror\sthat\smight\soccur\sif\sthe\ssqlite_stat1\stable\sis\scorrupt. -D 2014-10-03T19:29:39.807 +C Avoid\sleaking\sIndex.aiRowEst\smemory\sif\san\sOOM\scauses\sa\srollback\swhich\sdeletes\nthe\sindex\sbefore\sthe\saiRowEst\sdeletion\scode\sin\ssqlite3AnalysisLoad()\sroutine\nhas\sa\schance\sto\srun.\s\sSince\sthe\saiRowEst\snow\smight\sbe\sdeleted\sfrom\sfreeIndex()\nwhich\sdoes\snot\salways\shave\sa\sdb\spointer,\smake\ssure\sthe\saiRowEst\smemory\sis\nnot\sheld\sin\slookaside. +D 2014-10-04T00:07:44.206 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in cf57f673d77606ab0f2d9627ca52a9ba1464146a F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -166,7 +166,7 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 3d8b83c91651f53472ca17599dae3457b8b89494 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a F src/alter.c ba266a779bc7ce10e52e59e7d3dc79fa342e8fdb -F src/analyze.c 8d5a138936dab3436e67ca3a0f6466ad2f18d86b +F src/analyze.c ee85c504829aea05489ed0c67cbcd68d6a1ea7dd F src/attach.c f4e94df2d1826feda65eb0939f7f6f5f923a0ad9 F src/auth.c d8abcde53426275dab6243b441256fcd8ccbebb2 F src/backup.c a31809c65623cc41849b94d368917f8bb66e6a7e @@ -175,7 +175,7 @@ F src/btmutex.c 49ca66250c7dfa844a4d4cb8272b87420d27d3a5 F src/btree.c fa00618117fb6bb46c243452c56997c0d22d4fc9 F src/btree.h a79aa6a71e7f1055f01052b7f821bd1c2dce95c8 F src/btreeInt.h 1bd7957161a1346a914f1f09231610e777a8e58d -F src/build.c bde83dd5cf812e310a7e5ad2846790a14745bef4 +F src/build.c 9e5205db9a0c8a1a4ce7379d60a2a34cb0b7339c F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0 F src/complete.c 535183afb3c75628b78ce82612931ac7cdf26f14 F src/ctime.c bb434068b5308a857b181c2d204a320ff0d6c638 @@ -1201,7 +1201,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P e6f7f97dbc677c9f01b23142928c3fa7307c2fba -R d767aa2120b870307998a73a73f29d86 -U dan -Z 9bc979da9e7cfcf210fc63943ac10f56 +P f9c053b23ece877a7fdbe82204a10592f2d24a2d +R f53222c51c5cd542b15f6fc746109d5c +U drh +Z 12ba20164b8e53ca4d39b40be557570a diff --git a/manifest.uuid b/manifest.uuid index 662bc404af..e923963045 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f9c053b23ece877a7fdbe82204a10592f2d24a2d \ No newline at end of file +efd87ba142723ba131fcc985db6eb45c5a3c637b \ No newline at end of file diff --git a/src/analyze.c b/src/analyze.c index 2f65fe3d3e..6b244dd9a7 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -1518,9 +1518,10 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){ if( pIndex ){ int nCol = pIndex->nKeyCol+1; #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 - tRowcnt * const aiRowEst = pIndex->aiRowEst = (tRowcnt*)sqlite3DbMallocZero( - pInfo->db, sizeof(tRowcnt) * nCol + tRowcnt * const aiRowEst = pIndex->aiRowEst = (tRowcnt*)sqlite3MallocZero( + sizeof(tRowcnt) * nCol ); + if( aiRowEst==0 ) pInfo->db->mallocFailed = 1; #else tRowcnt * const aiRowEst = 0; #endif @@ -1869,7 +1870,7 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ } for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){ Index *pIdx = sqliteHashData(i); - sqlite3DbFree(db, pIdx->aiRowEst); + sqlite3_free(pIdx->aiRowEst); pIdx->aiRowEst = 0; } #endif diff --git a/src/build.c b/src/build.c index 777831aab5..14d8aab587 100644 --- a/src/build.c +++ b/src/build.c @@ -435,6 +435,9 @@ static void freeIndex(sqlite3 *db, Index *p){ sqlite3ExprDelete(db, p->pPartIdxWhere); sqlite3DbFree(db, p->zColAff); if( p->isResized ) sqlite3DbFree(db, p->azColl); +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + sqlite3_free(p->aiRowEst); +#endif sqlite3DbFree(db, p); } From 4ee3eb0ad482c160527260af9210f410581e6436 Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 4 Oct 2014 10:22:01 +0000 Subject: [PATCH 4/4] Add a test to show that the change on this branch is effective. FossilOrigin-Name: fc619be057975b8be6d0958024c5d436edbdf084 --- manifest | 13 ++--- manifest.uuid | 2 +- test/analyzeD.test | 117 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 test/analyzeD.test diff --git a/manifest b/manifest index 2b01d7bbcc..7ac24e1526 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Avoid\sleaking\sIndex.aiRowEst\smemory\sif\san\sOOM\scauses\sa\srollback\swhich\sdeletes\nthe\sindex\sbefore\sthe\saiRowEst\sdeletion\scode\sin\ssqlite3AnalysisLoad()\sroutine\nhas\sa\schance\sto\srun.\s\sSince\sthe\saiRowEst\snow\smight\sbe\sdeleted\sfrom\sfreeIndex()\nwhich\sdoes\snot\salways\shave\sa\sdb\spointer,\smake\ssure\sthe\saiRowEst\smemory\sis\nnot\sheld\sin\slookaside. -D 2014-10-04T00:07:44.206 +C Add\sa\stest\sto\sshow\sthat\sthe\schange\son\sthis\sbranch\sis\seffective. +D 2014-10-04T10:22:01.856 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in cf57f673d77606ab0f2d9627ca52a9ba1464146a F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -326,6 +326,7 @@ F test/analyze9.test 72795c8113604b5dcd47a1498a61d6d7fb5d041a F test/analyzeA.test 3335697f6700c7052295cfd0067fc5b2aacddf9a F test/analyzeB.test 8bf35ee0a548aea831bf56762cb8e7fdb1db083d F test/analyzeC.test 555a6cc388b9818b6eda6df816f01ce0a75d3a93 +F test/analyzeD.test 08f9d0bee4e118a66fff3a32d02dbe0ee0a2b594 F test/async.test 1d0e056ba1bb9729283a0f22718d3a25e82c277b F test/async2.test c0a9bd20816d7d6a2ceca7b8c03d3d69c28ffb8b F test/async3.test d73a062002376d7edc1fe3edff493edbec1fc2f7 @@ -1201,7 +1202,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P f9c053b23ece877a7fdbe82204a10592f2d24a2d -R f53222c51c5cd542b15f6fc746109d5c -U drh -Z 12ba20164b8e53ca4d39b40be557570a +P efd87ba142723ba131fcc985db6eb45c5a3c637b +R 4172251d18757975548e0803ec5466b1 +U dan +Z d36e743f41577a93de7b943cc5387e20 diff --git a/manifest.uuid b/manifest.uuid index e923963045..cfe8277192 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -efd87ba142723ba131fcc985db6eb45c5a3c637b \ No newline at end of file +fc619be057975b8be6d0958024c5d436edbdf084 \ No newline at end of file diff --git a/test/analyzeD.test b/test/analyzeD.test new file mode 100644 index 0000000000..4d46be6c64 --- /dev/null +++ b/test/analyzeD.test @@ -0,0 +1,117 @@ +# 2005 July 22 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# This file implements tests for the ANALYZE command. +# +# $Id: analyze.test,v 1.9 2008/08/11 18:44:58 drh Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix analyzeD + +ifcapable {!stat4} { + finish_test + return +} + + +# Set up a table with the following properties: +# +# * Contains 1000 rows. +# * Column a contains even integers between 0 and 18, inclusive (so that +# a=? for any such integer matches 100 rows). +# * Column b contains integers between 0 and 9, inclusive. +# * Column c contains integers between 0 and 199, inclusive (so that +# for any such integer, c=? matches 5 rows). +# * Then add 7 rows with a new value for "a" - 3001. The stat4 table will +# not contain any samples with a=3001. +# +do_execsql_test 1.0 { + CREATE TABLE t1(a, b, c); +} +do_test 1.1 { + for {set i 1} {$i < 1000} {incr i} { + set c [expr $i % 200] + execsql { INSERT INTO t1(a, b, c) VALUES( 2*($i/100), $i%10, $c ) } + } + + execsql { + INSERT INTO t1 VALUES(3001, 3001, 3001); + INSERT INTO t1 VALUES(3001, 3001, 3002); + INSERT INTO t1 VALUES(3001, 3001, 3003); + INSERT INTO t1 VALUES(3001, 3001, 3004); + INSERT INTO t1 VALUES(3001, 3001, 3005); + INSERT INTO t1 VALUES(3001, 3001, 3006); + INSERT INTO t1 VALUES(3001, 3001, 3007); + + CREATE INDEX t1_ab ON t1(a, b); + CREATE INDEX t1_c ON t1(c); + + ANALYZE; + } +} {} + +# With full ANALYZE data, SQLite sees that c=150 (5 rows) is better than +# a=3001 (7 rows). +# +do_eqp_test 1.2 { + SELECT * FROM t1 WHERE a=3001 AND c=150; +} { + 0 0 0 {SEARCH TABLE t1 USING INDEX t1_c (c=?)} +} + +do_test 1.3 { + execsql { DELETE FROM sqlite_stat1 } + db close + sqlite3 db test.db +} {} + +# Without stat1, because 3001 is larger than all samples in the stat4 +# table, SQLite things that a=3001 matches just 1 row. So it (incorrectly) +# chooses it over the c=150 index (5 rows). Even with stat1 data, things +# worked this way before commit [e6f7f97dbc]. +# +do_eqp_test 1.4 { + SELECT * FROM t1 WHERE a=3001 AND c=150; +} { + 0 0 0 {SEARCH TABLE t1 USING INDEX t1_ab (a=?)} +} + +do_test 1.5 { + execsql { + UPDATE t1 SET a=13 WHERE a = 3001; + ANALYZE; + } +} {} + +do_eqp_test 1.6 { + SELECT * FROM t1 WHERE a=13 AND c=150; +} { + 0 0 0 {SEARCH TABLE t1 USING INDEX t1_c (c=?)} +} + +do_test 1.7 { + execsql { DELETE FROM sqlite_stat1 } + db close + sqlite3 db test.db +} {} + +# Same test as 1.4, except this time the 7 rows that match the a=? condition +# do not feature larger values than all rows in the stat4 table. So SQLite +# gets this right, even without stat1 data. +do_eqp_test 1.8 { + SELECT * FROM t1 WHERE a=13 AND c=150; +} { + 0 0 0 {SEARCH TABLE t1 USING INDEX t1_c (c=?)} +} + +finish_test +