From 85c165cacad3b54b418be2299aff6439a422aaa0 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 19 Aug 2009 14:34:54 +0000 Subject: [PATCH] Clean up sqlite_stat2 related code. Add test cases. FossilOrigin-Name: aa728e06ce456fa42e68687bff6c7424460c31ef --- manifest | 20 ++-- manifest.uuid | 2 +- src/analyze.c | 118 +++++++++++-------- src/build.c | 11 +- src/sqliteInt.h | 1 + test/analyze2.test | 280 +++++++++++++++++++++++++++++++++++---------- test/malloc.test | 27 +++++ 7 files changed, 330 insertions(+), 129 deletions(-) diff --git a/manifest b/manifest index 805197d53f..dbfa3aaf14 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sproblem\swhere\sa\sbuffer\sallocated\sfrom\sa\slookaside\spool\swas\sbeing\sreleased\susing\sthe\ssystem\sfree(). -D 2009-08-19T09:09:38 +C Clean\sup\ssqlite_stat2\srelated\scode.\sAdd\stest\scases. +D 2009-08-19T14:34:55 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in 0f7761c5d1c62ae7a841e3393ffaff1fa0f5c00a F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -100,7 +100,7 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad F src/alter.c 8b42cace4f8e312de596807ba2685179da64fec4 -F src/analyze.c 3213d61ee5fbcf8a54ccfc6c07667c595316c559 +F src/analyze.c 985949131d55e3d9551cf00bff48409660a36a46 F src/attach.c 13995348fc5a26cdd136a50806faf292aabc173f F src/auth.c 802a9439dfa0b8c208b10055cba400e82ef18025 F src/backup.c 6f1c2d9862c8a3feb7739dfcca02c1f5352e37f3 @@ -109,7 +109,7 @@ F src/btmutex.c 0f43a75bb5b8147b386e8e1c3e71ba734e3863b7 F src/btree.c 49212ddaee8d7d12b4f1e17b9de62f7ea91ca59d F src/btree.h 577448a890c2ab9b21e6ab74f073526184bceebe F src/btreeInt.h 1c86297e69380f6577e7ae67452597dd8d5c2705 -F src/build.c 979b2aa9238531407ef7e64a56eed05d4c02b88d +F src/build.c ace6b5d99f724f102077ab6b0883ce1059c75271 F src/callback.c cb68b21b0d4ae7d11ae0e487933bce3323784dcf F src/complete.c 5ad5c6cd4548211867c204c41a126d73a9fbcea0 F src/date.c ab5f7137656652a48434d64f96bdcdc823bb23b3 @@ -163,7 +163,7 @@ F src/select.c 67b0778c9585905c8aa75aaa469e76ef3c1d315a F src/shell.c db2643650b9268df89a4bedca3f1c6d9e786f1bb F src/sqlite.h.in a6850e9034df1336e8139c4d6964d7d2f0f52337 F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17 -F src/sqliteInt.h 4422daf1c74034094eee966cbce357232767b308 +F src/sqliteInt.h 6bf0a232dc66ef02c4f1bedacc63322282081757 F src/sqliteLimit.h ffe93f5a0c4e7bd13e70cd7bf84cfb5c3465f45d F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76 F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d @@ -223,7 +223,7 @@ F test/alter3.test 25b95a136708f22b87184fa6a4309eea03d65153 F test/alter4.test 9386ffd1e9c7245f43eca412b2058d747509cc1f F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc F test/analyze.test ad5329098fe4de4a96852231d53e3e9e6283ad4b -F test/analyze2.test f58acc55f582996dc68ec9d28ede4ebb68afd11f +F test/analyze2.test eb66cbd9486460a9a74876d2c6e0a49a08a44a87 F test/async.test 8c75d31b8330f8b70cf2571b014d4476a063efdb F test/async2.test bf5e2ca2c96763b4cba3d016249ad7259a5603b6 F test/async3.test 93edaa9122f498e56ea98c36c72abc407f4fb11e @@ -448,7 +448,7 @@ F test/lock6.test 862aa71e97b288d6b3f92ba3313f51bd0b003776 F test/lookaside.test 1dd350dc6dff015c47c07fcc5a727a72fc5bae02 F test/main.test 347ab987f16167858781383427476b33dc69fdb7 F test/make-where7.tcl 05c16b5d4f5d6512881dfec560cb793915932ef9 -F test/malloc.test 7d7e1f04e6c2f338965e4220f2653f8b34ad3ee5 +F test/malloc.test d23580e15c33ee0353717129421b077541e910dc F test/malloc3.test 4bc57f850b212f706f3e1b37c4eced1d5a727cd1 F test/malloc4.test 957337613002b7058a85116493a262f679f3a261 F test/malloc5.test 4d16d1bb26d2deddd7c4f480deec341f9b2d0e22 @@ -744,7 +744,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl 672f81d693a03f80f5ae60bfefacd8a349e76746 -P 362665e89c21fd603d9f8ad6c0ead590e885af7c -R 0f4157c0cca678abe78a1dd4148f5f03 +P 67207a15bd7302ffeb2f342532b57b4852838d83 +R fc67b3175e5d16a0dc57c70ae312c646 U dan -Z 51de1621e1a30aec3aaa94297a00c42c +Z b80db5caa38d8f59e436115740e81d97 diff --git a/manifest.uuid b/manifest.uuid index cb60c31367..9b8e4fba22 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -67207a15bd7302ffeb2f342532b57b4852838d83 \ No newline at end of file +aa728e06ce456fa42e68687bff6c7424460c31ef \ No newline at end of file diff --git a/src/analyze.c b/src/analyze.c index 42645b7643..31acbc3815 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -116,16 +116,13 @@ static void analyzeOneTable( int endOfLoop; /* The end of the loop */ int addr; /* The address of an instruction */ int iDb; /* Index of database containing pTab */ - int regTabname = iMem++; /* Register containing table name */ int regIdxname = iMem++; /* Register containing index name */ int regSampleno = iMem++; /* Register containing next sample number */ int regCol = iMem++; /* Content of a column analyzed table */ - int regRec = iMem++; /* Register holding completed record */ int regTemp = iMem++; /* Temporary use register */ int regRowid = iMem++; /* Rowid for the inserted record */ - #ifdef SQLITE_ENABLE_STAT2 int regTemp2 = iMem++; /* Temporary use register */ int regSamplerecno = iMem++; /* Next sample index record number */ @@ -195,21 +192,21 @@ static void analyzeOneTable( sqlite3VdbeAddOp2(v, OP_Integer, 0, regSamplerecno); #endif - /* Memory cells are used as follows. All memory cell addresses are - ** offset by iMem. That is, cell 0 below is actually cell iMem, cell - ** 1 is cell 1+iMem, etc. + /* The block of memory cells initialized here is used as follows. ** - ** 0: The total number of rows in the table. + ** iMem: + ** The total number of rows in the table. ** - ** 1..nCol: Number of distinct entries in index considering the - ** left-most N columns, where N is the same as the - ** memory cell number. + ** iMem+1 .. iMem+nCol: + ** Number of distinct entries in index considering the + ** left-most N columns only, where N is between 1 and nCol, + ** inclusive. ** - ** nCol+1..2*nCol: Previous value of indexed columns, from left to - ** right. + ** 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 NULL. + ** Cells iMem through iMem+nCol are initialized to 0. The others are + ** initialized to contain an SQL NULL. */ for(i=0; i<=nCol; i++){ sqlite3VdbeAddOp2(v, OP_Integer, 0, iMem+i); @@ -257,23 +254,15 @@ static void analyzeOneTable( sqlite3VdbeJumpHere(v, ne); sqlite3VdbeAddOp2(v, OP_AddImm, regRecno, 1); } - assert( sqlite3VdbeCurrentAddr(v)==(topOfLoop+14+2*i) ); -#else - assert( sqlite3VdbeCurrentAddr(v)==(topOfLoop+2+2*i) ); #endif sqlite3VdbeAddOp3(v, OP_Ne, regCol, 0, iMem+nCol+i+1); - /**** TODO: add collating sequence *****/ sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL); } sqlite3VdbeAddOp2(v, OP_Goto, 0, endOfLoop); for(i=0; iaSample ){ + int j; + sqlite3 *dbMem = pIdx->pTable->dbMem; + for(j=0; jaSample[j]; + if( p->eType==SQLITE_TEXT || p->eType==SQLITE_BLOB ){ + sqlite3DbFree(pIdx->pTable->dbMem, p->u.z); + } + } + sqlite3DbFree(dbMem, pIdx->aSample); + pIdx->aSample = 0; + } +#endif +} + +/* +** Load the content of the sqlite_stat1 and sqlite_stat2 tables. The +** contents of sqlite_stat1 are used to populate the Index.aiRowEst[] +** arrays. The contents of sqlite_stat2 are used to populate the +** Index.aSample[] arrays. +** +** If the sqlite_stat1 table is not present in the database, SQLITE_ERROR +** is returned. In this case, even if SQLITE_ENABLE_STAT2 was defined +** during compilation and the sqlite_stat2 table is present, no data is +** read from it. +** +** If SQLITE_ENABLE_STAT2 was defined during compilation and the +** sqlite_stat2 table is not present in the database, SQLITE_ERROR is +** returned. However, in this case, data is read from the sqlite_stat1 +** table (if it is present) before returning. +** +** If an OOM error occurs, this function always sets db->mallocFailed. +** This means if the caller does not care about other errors, the return +** code may be ignored. */ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ analysisInfo sInfo; @@ -503,18 +529,19 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){ Index *pIdx = sqliteHashData(i); sqlite3DefaultRowEst(pIdx); + sqlite3DeleteIndexSamples(pIdx); } - /* Check to make sure the sqlite_stat1 table existss */ + /* Check to make sure the sqlite_stat1 table exists */ sInfo.db = db; sInfo.zDatabase = db->aDb[iDb].zName; if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)==0 ){ - return SQLITE_ERROR; + return SQLITE_ERROR; } /* Load new statistics out of the sqlite_stat1 table */ - zSql = sqlite3MPrintf(db, "SELECT idx, stat FROM %Q.sqlite_stat1", - sInfo.zDatabase); + zSql = sqlite3MPrintf(db, + "SELECT idx, stat FROM %Q.sqlite_stat1", sInfo.zDatabase); if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ @@ -524,33 +551,35 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ sqlite3DbFree(db, zSql); } + /* Load the statistics from the sqlite_stat2 table. */ #ifdef SQLITE_ENABLE_STAT2 + if( rc==SQLITE_OK && !sqlite3FindTable(db, "sqlite_stat2", sInfo.zDatabase) ){ + rc = SQLITE_ERROR; + } if( rc==SQLITE_OK ){ sqlite3_stmt *pStmt = 0; zSql = sqlite3MPrintf(db, - "SELECT idx,sampleno,sample FROM %Q.sqlite_stat2", sInfo.zDatabase - ); + "SELECT idx,sampleno,sample FROM %Q.sqlite_stat2", sInfo.zDatabase); if( !zSql ){ - return SQLITE_NOMEM; + rc = SQLITE_NOMEM; + }else{ + (void)sqlite3SafetyOff(db); + rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); + (void)sqlite3SafetyOn(db); + sqlite3DbFree(db, zSql); } - (void)sqlite3SafetyOff(db); - rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); - assert( rc!=SQLITE_MISUSE ); - (void)sqlite3SafetyOn(db); - sqlite3DbFree(db, zSql); - (void)sqlite3SafetyOff(db); - if( rc==SQLITE_OK ){ + (void)sqlite3SafetyOff(db); while( sqlite3_step(pStmt)==SQLITE_ROW ){ char *zIndex = (char *)sqlite3_column_text(pStmt, 0); Index *pIdx = sqlite3FindIndex(db, zIndex, sInfo.zDatabase); if( pIdx ){ int iSample = sqlite3_column_int(pStmt, 1); sqlite3 *dbMem = pIdx->pTable->dbMem; - assert( dbMem==db || dbMem==0 ); + assert( dbMem==db || dbMem==0 ); if( iSample=0 ){ int eType = sqlite3_column_type(pStmt, 2); @@ -558,16 +587,13 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ static const int sz = sizeof(IndexSample)*SQLITE_INDEX_SAMPLES; pIdx->aSample = (IndexSample *)sqlite3DbMallocZero(dbMem, sz); if( pIdx->aSample==0 ){ - db->mallocFailed = 1; + db->mallocFailed = 1; break; } } if( pIdx->aSample ){ IndexSample *pSample = &pIdx->aSample[iSample]; - if( pSample->eType==SQLITE_TEXT || pSample->eType==SQLITE_BLOB ){ - sqlite3DbFree(dbMem, pSample->u.z); - } pSample->eType = eType; if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ pSample->u.r = sqlite3_column_double(pStmt, 2); @@ -586,7 +612,7 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ if( pSample->u.z ){ memcpy(pSample->u.z, z, n); }else{ - db->mallocFailed = 1; + db->mallocFailed = 1; break; } } @@ -595,8 +621,8 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ } } rc = sqlite3_finalize(pStmt); + (void)sqlite3SafetyOn(db); } - (void)sqlite3SafetyOn(db); } #endif diff --git a/src/build.c b/src/build.c index 3e9b0ec96c..e47aaed08d 100644 --- a/src/build.c +++ b/src/build.c @@ -343,16 +343,7 @@ Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){ static void freeIndex(Index *p){ sqlite3 *db = p->pTable->dbMem; /* testcase( db==0 ); */ - if( p->aSample ){ - int i; - for(i=0; iaSample[i].eType; - if( e==SQLITE_BLOB || e==SQLITE_TEXT ){ - sqlite3DbFree(db, p->aSample[i].u.z); - } - } - } - sqlite3DbFree(db, p->aSample); + sqlite3DeleteIndexSamples(p); sqlite3DbFree(db, p->zColAff); sqlite3DbFree(db, p); } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 277ffb4529..da14167a03 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2833,6 +2833,7 @@ int sqlite3InvokeBusyHandler(BusyHandler*); int sqlite3FindDb(sqlite3*, Token*); int sqlite3FindDbName(sqlite3 *, const char *); int sqlite3AnalysisLoad(sqlite3*,int iDB); +void sqlite3DeleteIndexSamples(Index*); void sqlite3DefaultRowEst(Index*); void sqlite3RegisterLikeFunctions(sqlite3*, int); int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*); diff --git a/test/analyze2.test b/test/analyze2.test index 7c78882b05..d4716181de 100644 --- a/test/analyze2.test +++ b/test/analyze2.test @@ -19,7 +19,34 @@ ifcapable !stat2 { return } -proc eqp sql { +#-------------------------------------------------------------------- +# Test organization: +# +# analyze2-1.*: Tests to verify that ANALYZE creates and populates the +# sqlite_stat2 table as expected. +# +# analyze2-2.*: Test that when a table has two indexes on it and either +# index may be used for the scan, the index suggested by +# the contents of sqlite_stat2 table is prefered. +# +# analyze2-3.*: Similar to the previous block of tests, but using tables +# that contain a mixture of NULL, numeric, text and blob +# values. +# +# analyze2-4.*: Check that when an indexed column uses a collation other +# than BINARY, the collation is taken into account when +# using the contents of sqlite_stat2 to estimate the cost +# of a range scan. +# +# analyze2-5.*: Check that collation sequences are used as described above +# even when the only available version of the collation +# function require UTF-16 encoded arguments. +# +# analyze2-6.*: Check that the library behaves correctly when one of the +# sqlite_stat2 or sqlite_stat1 tables are missing. +# + +proc eqp {sql} { uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] } @@ -43,7 +70,6 @@ do_test analyze2-1.1 { t1 sqlite_autoindex_t1_1 8 888 \ t1 sqlite_autoindex_t1_1 9 999 \ ] - do_test analyze2-1.2 { execsql { DELETE FROM t1 WHERe x>9; @@ -51,7 +77,6 @@ do_test analyze2-1.2 { SELECT tbl, idx, group_concat(sample, ' ') FROM sqlite_stat2; } } {t1 sqlite_autoindex_t1_1 {0 1 2 3 4 5 6 7 8 9}} - do_test analyze2-1.3 { execsql { DELETE FROM t1 WHERE x>5; @@ -59,7 +84,6 @@ do_test analyze2-1.3 { SELECT * FROM sqlite_stat2; } } {} - do_test analyze2-1.4 { execsql { DELETE FROM t1; @@ -68,62 +92,62 @@ do_test analyze2-1.4 { } } {} -do_test analyze2-1.1 { + +do_test analyze2-2.1 { execsql { + BEGIN; DROP TABLE t1; CREATE TABLE t1(x, y); CREATE INDEX t1_x ON t1(x); CREATE INDEX t1_y ON t1(y); } - for {set i 0} {$i < 1000} {incr i} { execsql { INSERT INTO t1 VALUES($i, $i) } } + execsql COMMIT execsql ANALYZE } {} -do_test analyze2-1.2 { - execsql { EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE x>500 AND y>700 } -} {0 0 {TABLE t1 WITH INDEX t1_y}} - -do_test analyze2-1.3 { - execsql { EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE x>700 AND y>500 } -} {0 0 {TABLE t1 WITH INDEX t1_x}} - -do_test analyze2-1.3 { - execsql { EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE y>700 AND x>500 } -} {0 0 {TABLE t1 WITH INDEX t1_y}} -do_test analyze2-1.4 { - execsql { EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE y>500 AND x>700 } -} {0 0 {TABLE t1 WITH INDEX t1_x}} - -do_test analyze2-2.1 { - eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 200 AND y BETWEEN 400 AND 700" -} {0 0 {TABLE t1 WITH INDEX t1_x}} do_test analyze2-2.2 { - eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 500 AND y BETWEEN 400 AND 700" + eqp "SELECT * FROM t1 WHERE x>500 AND y>700" } {0 0 {TABLE t1 WITH INDEX t1_y}} do_test analyze2-2.3 { + eqp "SELECT * FROM t1 WHERE x>700 AND y>500" +} {0 0 {TABLE t1 WITH INDEX t1_x}} +do_test analyze2-2.3 { + eqp "SELECT * FROM t1 WHERE y>700 AND x>500" +} {0 0 {TABLE t1 WITH INDEX t1_y}} +do_test analyze2-2.4 { + eqp "SELECT * FROM t1 WHERE y>500 AND x>700" +} {0 0 {TABLE t1 WITH INDEX t1_x}} +do_test analyze2-2.5 { + eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 200 AND y BETWEEN 400 AND 700" +} {0 0 {TABLE t1 WITH INDEX t1_x}} +do_test analyze2-2.6 { + eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 500 AND y BETWEEN 400 AND 700" +} {0 0 {TABLE t1 WITH INDEX t1_y}} +do_test analyze2-2.7 { eqp "SELECT * FROM t1 WHERE x BETWEEN -400 AND -300 AND y BETWEEN 100 AND 300" } {0 0 {TABLE t1 WITH INDEX t1_x}} -do_test analyze2-2.4 { +do_test analyze2-2.8 { eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 300 AND y BETWEEN -400 AND -300" } {0 0 {TABLE t1 WITH INDEX t1_y}} - -do_test analyze2-3.1 { +do_test analyze2-2.9 { eqp "SELECT * FROM t1 WHERE x BETWEEN 500 AND 100 AND y BETWEEN 100 AND 300" } {0 0 {TABLE t1 WITH INDEX t1_x}} -do_test analyze2-3.2 { +do_test analyze2-2.10 { eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 300 AND y BETWEEN 500 AND 100" } {0 0 {TABLE t1 WITH INDEX t1_y}} -do_test analyze2-4.1 { +do_test analyze2-3.1 { set alphabet [list a b c d e f g h i j] + execsql BEGIN for {set i 0} {$i < 1000} {incr i} { set str [lindex $alphabet [expr ($i/100)%10]] append str [lindex $alphabet [expr ($i/ 10)%10]] append str [lindex $alphabet [expr ($i/ 1)%10]] execsql { INSERT INTO t1 VALUES($str, $str) } } + execsql COMMIT execsql ANALYZE execsql { SELECT tbl,idx,group_concat(sample,' ') @@ -132,7 +156,7 @@ do_test analyze2-4.1 { GROUP BY tbl,idx } } {t1 t1_x {0 222 444 666 888 bba ddc ffe hhg jjj}} -do_test analyze2-4.2 { +do_test analyze2-3.2 { execsql { SELECT tbl,idx,group_concat(sample,' ') FROM sqlite_stat2 @@ -141,36 +165,38 @@ do_test analyze2-4.2 { } } {t1 t1_y {0 222 444 666 888 bba ddc ffe hhg jjj}} -do_test analyze2-4.3 { +do_test analyze2-3.3 { eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 500 AND y BETWEEN 'a' AND 'b'" } {0 0 {TABLE t1 WITH INDEX t1_y}} -do_test analyze2-4.4 { +do_test analyze2-3.4 { eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 400 AND y BETWEEN 'a' AND 'h'" } {0 0 {TABLE t1 WITH INDEX t1_x}} -do_test analyze2-4.5 { +do_test analyze2-3.5 { eqp "SELECT * FROM t1 WHERE x<'a' AND y>'h'" } {0 0 {TABLE t1 WITH INDEX t1_y}} -do_test analyze2-4.6 { +do_test analyze2-3.6 { eqp "SELECT * FROM t1 WHERE x<444 AND y>'h'" } {0 0 {TABLE t1 WITH INDEX t1_y}} -do_test analyze2-4.7 { +do_test analyze2-3.7 { eqp "SELECT * FROM t1 WHERE x<221 AND y>'h'" } {0 0 {TABLE t1 WITH INDEX t1_x}} -do_test analyze2-5.1 { +do_test analyze2-4.1 { execsql { CREATE TABLE t3(a COLLATE nocase, b) } execsql { CREATE INDEX t3a ON t3(a) } execsql { CREATE INDEX t3b ON t3(b) } set alphabet [list A b C d E f G h I j] + execsql BEGIN for {set i 0} {$i < 1000} {incr i} { set str [lindex $alphabet [expr ($i/100)%10]] append str [lindex $alphabet [expr ($i/ 10)%10]] append str [lindex $alphabet [expr ($i/ 1)%10]] execsql { INSERT INTO t3 VALUES($str, $str) } } + execsql COMMIT execsql ANALYZE } {} -do_test analyze2-5.2 { +do_test analyze2-4.2 { execsql { SELECT tbl,idx,group_concat(sample,' ') FROM sqlite_stat2 @@ -178,7 +204,7 @@ do_test analyze2-5.2 { GROUP BY tbl,idx } } {t3 t3a {AAA bbb CCC ddd EEE fff GGG hhh III jjj}} -do_test analyze2-5.3 { +do_test analyze2-4.3 { execsql { SELECT tbl,idx,group_concat(sample,' ') FROM sqlite_stat2 @@ -187,10 +213,10 @@ do_test analyze2-5.3 { } } {t3 t3b {AAA CCC EEE GGG III bbb ddd fff hhh jjj}} -do_test analyze2-5.4 { +do_test analyze2-4.4 { eqp "SELECT * FROM t3 WHERE a > 'A' AND a < 'C' AND b > 'A' AND b < 'C'" } {0 0 {TABLE t3 WITH INDEX t3b}} -do_test analyze2-5.5 { +do_test analyze2-4.5 { eqp "SELECT * FROM t3 WHERE a > 'A' AND a < 'c' AND b > 'A' AND b < 'c'" } {0 0 {TABLE t3 WITH INDEX t3a}} @@ -198,21 +224,22 @@ proc test_collate {enc lhs rhs} { # puts $enc return [string compare $lhs $rhs] } - -do_test analyze2-6.1 { +do_test analyze2-5.1 { add_test_collate db 0 0 1 execsql { CREATE TABLE t4(x COLLATE test_collate) } execsql { CREATE INDEX t4x ON t4(x) } set alphabet [list a b c d e f g h i j] + execsql BEGIN for {set i 0} {$i < 1000} {incr i} { set str [lindex $alphabet [expr ($i/100)%10]] append str [lindex $alphabet [expr ($i/ 10)%10]] append str [lindex $alphabet [expr ($i/ 1)%10]] execsql { INSERT INTO t4 VALUES($str) } } + execsql COMMIT execsql ANALYZE } {} -do_test analyze2-6.2 { +do_test analyze2-5.2 { execsql { SELECT tbl,idx,group_concat(sample,' ') FROM sqlite_stat2 @@ -220,31 +247,160 @@ do_test analyze2-6.2 { GROUP BY tbl,idx } } {t4 t4x {aaa bbb ccc ddd eee fff ggg hhh iii jjj}} -do_test analyze2-6.3 { +do_test analyze2-5.3 { eqp "SELECT * FROM t4 WHERE x>'ccc'" } {0 0 {TABLE t4 WITH INDEX t4x}} -do_test analyze2-6.4 { +do_test analyze2-5.4 { eqp "SELECT * FROM t4 AS t41, t4 AS t42 WHERE t41.x>'ccc' AND t42.x>'ggg'" } {0 1 {TABLE t4 AS t42 WITH INDEX t4x} 1 0 {TABLE t4 AS t41 WITH INDEX t4x}} -do_test analyze2-6.5 { +do_test analyze2-5.5 { eqp "SELECT * FROM t4 AS t41, t4 AS t42 WHERE t41.x>'ddd' AND t42.x>'ccc'" } {0 0 {TABLE t4 AS t41 WITH INDEX t4x} 1 1 {TABLE t4 AS t42 WITH INDEX t4x}} -ifcapable memdebug { - execsql { DELETE FROM t4 } - db close - source $testdir/malloc_common.tcl - file copy -force test.db bak.db - - do_malloc_test analyze2-oom -tclprep { - db close - file copy -force bak.db test.db - sqlite3 db test.db - sqlite3_db_config_lookaside db 0 0 0 - add_test_collate db 0 0 1 - } -sqlbody { - SELECT * FROM t4 AS t41, t4 AS t42 WHERE t41.x>'ddd' AND t42.x>'ccc' +#-------------------------------------------------------------------- +# These tests, analyze2-6.*, verify that the library behaves correctly +# when one of the sqlite_stat1 and sqlite_stat2 tables is missing. +# +# If the sqlite_stat1 table is not present, then the sqlite_stat2 +# table is not read. However, if it is the sqlite_stat2 table that +# is missing, the data in the sqlite_stat1 table is still used. +# +# Tests analyze2-6.1.* test the libary when the sqlite_stat2 table +# is missing. Tests analyze2-6.2.* test the library when sqlite_stat1 +# is not present. +# +do_test analyze2-6.0 { + execsql { + DROP TABLE t4; + CREATE TABLE t5(a, b); CREATE INDEX t5i ON t5(a, b); + CREATE TABLE t6(a, b); CREATE INDEX t6i ON t6(a, b); } -} + for {set ii 0} {$ii < 20} {incr ii} { + execsql { + INSERT INTO t5 VALUES($ii, $ii); + INSERT INTO t6 VALUES($ii/10, $ii/10); + } + } + execsql { + CREATE TABLE master AS + SELECT * FROM sqlite_master WHERE name LIKE 'sqlite_stat%' + } +} {} + +do_test analyze2-6.1.1 { + eqp {SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a = 1 AND + t6.a = 1 AND t6.b = 1 + } +} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} +do_test analyze2-6.1.2 { + db cache flush + execsql ANALYZE + eqp {SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a = 1 AND + t6.a = 1 AND t6.b = 1 + } +} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} +do_test analyze2-6.1.3 { + sqlite3 db test.db + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a = 1 AND + t6.a = 1 AND t6.b = 1 + } +} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} +do_test analyze2-6.1.4 { + execsql { + PRAGMA writable_schema = 1; + DELETE FROM sqlite_master WHERE tbl_name = 'sqlite_stat2'; + } + sqlite3 db test.db + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a = 1 AND + t6.a = 1 AND t6.b = 1 + } +} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} +do_test analyze2-6.1.5 { + execsql { + PRAGMA writable_schema = 1; + DELETE FROM sqlite_master WHERE tbl_name = 'sqlite_stat1'; + } + sqlite3 db test.db + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a = 1 AND + t6.a = 1 AND t6.b = 1 + } +} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} +do_test analyze2-6.1.6 { + execsql { + PRAGMA writable_schema = 1; + INSERT INTO sqlite_master SELECT * FROM master; + } + sqlite3 db test.db + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a = 1 AND + t6.a = 1 AND t6.b = 1 + } +} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} + +do_test analyze2-6.2.1 { + execsql { + DELETE FROM sqlite_stat1; + DELETE FROM sqlite_stat2; + } + sqlite3 db test.db + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a>1 AND t5.a<15 AND + t6.a>1 + } +} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} +do_test analyze2-6.2.2 { + db cache flush + execsql ANALYZE + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a>1 AND t5.a<15 AND + t6.a>1 + } +} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} +do_test analyze2-6.2.3 { + sqlite3 db test.db + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a>1 AND t5.a<15 AND + t6.a>1 + } +} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} +do_test analyze2-6.2.4 { + execsql { + PRAGMA writable_schema = 1; + DELETE FROM sqlite_master WHERE tbl_name = 'sqlite_stat1'; + } + sqlite3 db test.db + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a>1 AND t5.a<15 AND + t6.a>1 + } +} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} +do_test analyze2-6.2.5 { + execsql { + PRAGMA writable_schema = 1; + DELETE FROM sqlite_master WHERE tbl_name = 'sqlite_stat2'; + } + sqlite3 db test.db + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a>1 AND t5.a<15 AND + t6.a>1 + } +} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} +do_test analyze2-6.2.6 { + execsql { + PRAGMA writable_schema = 1; + INSERT INTO sqlite_master SELECT * FROM master; + } + sqlite3 db test.db + execsql ANALYZE + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a>1 AND t5.a<15 AND + t6.a>1 + } +} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} finish_test diff --git a/test/malloc.test b/test/malloc.test index e88bc5071a..8459b280a2 100644 --- a/test/malloc.test +++ b/test/malloc.test @@ -866,6 +866,33 @@ if {[db eval {PRAGMA locking_mode}]!="exclusive"} { catch { db2 close } } +ifcapable stat2 { + do_malloc_test 38 -tclprep { + add_test_collate db 0 0 1 + execsql { + ANALYZE; + CREATE TABLE t4(x COLLATE test_collate); + CREATE INDEX t4x ON t4(x); + INSERT INTO sqlite_stat2 VALUES('t4', 't4x', 0, 'aaa'); + INSERT INTO sqlite_stat2 VALUES('t4', 't4x', 1, 'aaa'); + INSERT INTO sqlite_stat2 VALUES('t4', 't4x', 2, 'aaa'); + INSERT INTO sqlite_stat2 VALUES('t4', 't4x', 3, 'aaa'); + INSERT INTO sqlite_stat2 VALUES('t4', 't4x', 4, 'aaa'); + INSERT INTO sqlite_stat2 VALUES('t4', 't4x', 5, 'aaa'); + INSERT INTO sqlite_stat2 VALUES('t4', 't4x', 6, 'aaa'); + INSERT INTO sqlite_stat2 VALUES('t4', 't4x', 7, 'aaa'); + INSERT INTO sqlite_stat2 VALUES('t4', 't4x', 8, 'aaa'); + INSERT INTO sqlite_stat2 VALUES('t4', 't4x', 9, 'aaa'); + } + db close + sqlite3 db test.db + sqlite3_db_config_lookaside db 0 0 0 + add_test_collate db 0 0 1 + } -sqlbody { + SELECT * FROM t4 AS t41, t4 AS t42 WHERE t41.x>'ddd' AND t42.x>'ccc' + } +} + # Ensure that no file descriptors were leaked. do_test malloc-99.X { catch {db close}