]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Clean up sqlite_stat2 related code. Add test cases.
authordan <dan@noemail.net>
Wed, 19 Aug 2009 14:34:54 +0000 (14:34 +0000)
committerdan <dan@noemail.net>
Wed, 19 Aug 2009 14:34:54 +0000 (14:34 +0000)
FossilOrigin-Name: aa728e06ce456fa42e68687bff6c7424460c31ef

manifest
manifest.uuid
src/analyze.c
src/build.c
src/sqliteInt.h
test/analyze2.test
test/malloc.test

index 805197d53f72064e2ea3c19ba874a875212137e4..dbfa3aaf14a05e6c0e8b8d35be252bb98de11263 100644 (file)
--- 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
index cb60c31367a6933d5dd4f668a897a474a41f0050..9b8e4fba22fbdcf7199433fcec5ef3ec973c997c 100644 (file)
@@ -1 +1 @@
-67207a15bd7302ffeb2f342532b57b4852838d83
\ No newline at end of file
+aa728e06ce456fa42e68687bff6c7424460c31ef
\ No newline at end of file
index 42645b76436ec49fd65143971396f8ca0e2c9bf6..31acbc381547242051a76de3a2c914f65f6ac57f 100644 (file)
@@ -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; i<nCol; i++){
-#ifdef SQLITE_ENABLE_STAT2
-      sqlite3VdbeJumpHere(v, topOfLoop+14+2*i);
-#else
-      sqlite3VdbeJumpHere(v, topOfLoop+2+2*i);
-#endif
+      sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-(nCol*2));
       sqlite3VdbeAddOp2(v, OP_AddImm, iMem+i+1, 1);
       sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, iMem+nCol+i+1);
     }
@@ -486,8 +475,45 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
 }
 
 /*
-** Load the content of the sqlite_stat1 and sqlite_stat2 tables into the 
-** index hash tables.
+** If the Index.aSample variable is not NULL, delete the aSample[] array
+** and its contents.
+*/
+void sqlite3DeleteIndexSamples(Index *pIdx){
+#ifdef SQLITE_ENABLE_STAT2
+  if( pIdx->aSample ){
+    int j;
+    sqlite3 *dbMem = pIdx->pTable->dbMem;
+    for(j=0; j<SQLITE_INDEX_SAMPLES; j++){
+      IndexSample *p = &pIdx->aSample[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<SQLITE_INDEX_SAMPLES && 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
 
index 3e9b0ec96c61185358b045ebc029d60f57654db7..e47aaed08d8672586e1b5ec3fbd650d3ed28ab6b 100644 (file)
@@ -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; i<SQLITE_INDEX_SAMPLES; i++){
-      int e = p->aSample[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);
 }
index 277ffb4529654bb901c5883dd92f4d05af70f4b9..da14167a035deb6e8c82c657f1bbe2fbb74999aa 100644 (file)
@@ -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*);
index 7c78882b05751c3de6de7b0d6fe0e3eb733d844b..d4716181de3ae1a40e22bd5d293351496378c253 100644 (file)
@@ -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 }
+do_test analyze2-2.2 {
+  eqp "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 }
+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-1.3 {
-  execsql { EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE y>700 AND x>500 }
+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-1.4 {
-  execsql { EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE y>500 AND x>700 }
+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.1 {
+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.2 {
+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.3 {
+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
index e88bc5071a8492475d0572f607d318441fb4e2af..8459b280a208e50c7dd36ea8b98f009248a334b7 100644 (file)
@@ -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}