]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Change the sqlite_stat2 schema to be more flexible.
authordan <dan@noemail.net>
Tue, 18 Aug 2009 16:24:58 +0000 (16:24 +0000)
committerdan <dan@noemail.net>
Tue, 18 Aug 2009 16:24:58 +0000 (16:24 +0000)
FossilOrigin-Name: ded9dec6459baf21e01f63250db5ace57f390e7a

manifest
manifest.uuid
src/analyze.c
src/build.c
src/sqliteInt.h
src/vdbe.c
src/where.c
test/analyze2.test [new file with mode: 0644]

index ec6bc382cf0cdcb45f843d28277ff5940c4bcefe..201500ef2fc2e8427ee3d5b169cc146f07b3e420 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C First\sversion\sof\ssqlite_stat2\s(schema\sforces\sexactly\s10\ssamples).
-D 2009-08-17T17:06:59
+C Change\sthe\ssqlite_stat2\sschema\sto\sbe\smore\sflexible.
+D 2009-08-18T16:24:59
 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 c3f1ea347d5a2c90aec66a510e4c7b29d79fbca2
+F src/analyze.c 7f086e4da3db68c9fad2a4d61d90a62683084a57
 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 09389ab5d5a5997dbefa8f15fc4755f6c3243f4c
+F src/build.c 979b2aa9238531407ef7e64a56eed05d4c02b88d
 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 21722d546c8a93bf079564d49e628d5e66d3244a
+F src/sqliteInt.h a11d40fc24390e5e0665cf8b1addbe162b9f76c3
 F src/sqliteLimit.h ffe93f5a0c4e7bd13e70cd7bf84cfb5c3465f45d
 F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76
 F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d
@@ -204,7 +204,7 @@ F src/update.c 4da327f706c0d0dfedf4d92154b1b5688bdea0ac
 F src/utf.c 9b022ac1c1f306733d57daa0df0b8beb7c17e95e
 F src/util.c c2416f60ae704a8c4990e4909aa810f90cbffa07
 F src/vacuum.c 3fe0eebea6d2311c1c2ab2962887d11f7a4dcfb0
-F src/vdbe.c f2c07c6440826f69fc6d39083ac7fe5ba98fe3be
+F src/vdbe.c 464e2e30b1287554a23cdaa0b6b010a9dcb5eb29
 F src/vdbe.h 457b6c70f02885cec1f5225b5e6441d067b55d3f
 F src/vdbeInt.h 831c254a6eef237ef4664c8381a0137586567007
 F src/vdbeapi.c 0ab8ada7260b32031ca97f338caecf0812460624
@@ -213,7 +213,7 @@ F src/vdbeblob.c a3f3e0e877fc64ea50165eec2855f5ada4477611
 F src/vdbemem.c afd6ce02945e659f65642f290a37ccf4a88c4dcb
 F src/vtab.c aedd76e8670d5a5379f93804398d3ba960125547
 F src/walker.c 1edca756275f158b80f20eb6f104c8d3fcc96a04
-F src/where.c 33a3aa8bef9594002300b4bc9aa2a7b37c71345c
+F src/where.c 9d4c2f178f842c1ed18c068880f324479ea2cb38
 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
 F test/all.test 14165b3e32715b700b5f0cbf8f6e3833dda0be45
@@ -223,6 +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 30987f595f9e34b95bae81794e99b5ca2be7e46c
 F test/async.test 8c75d31b8330f8b70cf2571b014d4476a063efdb
 F test/async2.test bf5e2ca2c96763b4cba3d016249ad7259a5603b6
 F test/async3.test 93edaa9122f498e56ea98c36c72abc407f4fb11e
@@ -743,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 0d5b058717858c9cda8ca120a3d814453a94a0e6
-R 942eb5447bdff248ec9f0cbf9f54bb64
+P dd96bda2a85c1d94fb4a0bf5f27e2977f7f7e42e
+R 3ad6c851396bbe27ea036387da51f58c
 U dan
-Z d16a7c84025a55a7e2426c9a05cd868d
+Z ea071f69e4d6383649120789bd868cdf
index 5099e8f445bfa6c4df9f4a8959da7945bce83d61..2293825c0607e9902560b6fea2f2b7b65caa767a 100644 (file)
@@ -1 +1 @@
-dd96bda2a85c1d94fb4a0bf5f27e2977f7f7e42e
\ No newline at end of file
+ded9dec6459baf21e01f63250db5ace57f390e7a
\ No newline at end of file
index e8c7c37171e4b2e24cd33c252029652b5a7ea220..c6c279f278df8bd586d27927754e8d2b3c2d7513 100644 (file)
@@ -32,7 +32,7 @@ static void openStatTable(
   const char *zWhere      /* Delete entries associated with this table */
 ){
   const char *aName[] = { "sqlite_stat1", "sqlite_stat2" };
-  const char *aCols[] = { "tbl,idx,stat", "tbl,idx," SQLITE_INDEX_SAMPLE_COLS };
+  const char *aCols[] = { "tbl,idx,stat", "tbl,idx,sampleno,sample" };
   int aRoot[] = {0, 0};
   int aCreateTbl[] = {0, 0};
 
@@ -94,7 +94,6 @@ static void analyzeOneTable(
 ){
   Index *pIdx;     /* An index to being analyzed */
   int iIdxCur;     /* Index of VdbeCursor for index being analyzed */
-  int nCol;        /* Number of columns in the index */
   Vdbe *v;         /* The virtual machine being built up */
   int i;           /* Loop counter */
   int topOfLoop;   /* The top of the loop */
@@ -102,6 +101,21 @@ static void analyzeOneTable(
   int addr;        /* The address of an instruction */
   int iDb;         /* Index of database containing pTab */
 
+
+  /* Assign the required registers. */
+  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 regSamplerecno = iMem++; /* Next sample index record number */
+  int regRecno = iMem++;       /* Register next index record number */
+  int regRec = iMem++;         /* Register holding completed record */
+  int regTemp = iMem++;        /* Temporary use register */
+  int regTemp2 = iMem++;        /* Temporary use register */
+  int regRowid = iMem++;       /* Rowid for the inserted record */
+  int regCount = iMem++;       /* Total number of records in table */
+
   v = sqlite3GetVdbe(pParse);
   if( v==0 || NEVER(pTab==0) || pTab->pIndex==0 ){
     /* Do no analysis for tables that have no indices */
@@ -120,39 +134,43 @@ static void analyzeOneTable(
   /* Establish a read-lock on the table at the shared-cache level. */
   sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
 
-  iMem += 3;
   iIdxCur = pParse->nTab++;
   for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+    int nCol = pIdx->nColumn;
     KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
-    int regFields;    /* Register block for building records */
-    int regRec;       /* Register holding completed record */
-    int regTemp;      /* Temporary use register */
-    int regCol;       /* Content of a column from the table being analyzed */
-    int regRowid;     /* Rowid for the inserted record */
-    int regF2;
-    int regStat2;
-
-    /* Open a cursor to the index to be analyzed
-    */
+
+    if( iMem+1+(nCol*2)>pParse->nMem ){
+      pParse->nMem = iMem+1+(nCol*2);
+    }
+
+    /* Open a cursor to the index to be analyzed. */
     assert( iDb==sqlite3SchemaToIndex(pParse->db, pIdx->pSchema) );
-    nCol = pIdx->nColumn;
     sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb,
         (char *)pKey, P4_KEYINFO_HANDOFF);
     VdbeComment((v, "%s", pIdx->zName));
-    regStat2 = iMem+nCol*2+1;
-    regFields = regStat2+2+SQLITE_INDEX_SAMPLES;
-    regTemp = regRowid = regCol = regFields+3;
-    regRec = regCol+1;
-    if( regRec>pParse->nMem ){
-      pParse->nMem = regRec;
-    }
 
-    /* Fill in the register with the total number of rows. */
+    /* If this iteration of the loop is generating code to analyze the
+    ** first index in the pTab->pIndex list, then register regCount has
+    ** not been populated. In this case populate it now.  */
     if( pTab->pIndex==pIdx ){
-      sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, iMem-3);
+      sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regCount);
+      sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0);
     }
-    sqlite3VdbeAddOp2(v, OP_Integer, 0, iMem-2);
-    sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem-1);
+    sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, pIdx->zName, 0);
+
+    /* Zero the regSampleno and regRecno registers. */
+    sqlite3VdbeAddOp2(v, OP_Integer, 0, regSampleno);
+    sqlite3VdbeAddOp2(v, OP_Integer, 0, regRecno);
+
+    /* If there are less than INDEX_SAMPLES records in the index, then
+    ** set the contents of regSampleRecno to integer value INDEX_SAMPLES.
+    ** Otherwise, set it to zero. This is to ensure that if there are 
+    ** less than the said number of entries in the index, no samples at
+    ** all are collected.  */
+    sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES, regSamplerecno);
+    sqlite3VdbeAddOp3(v, OP_Lt, regSamplerecno, sqlite3VdbeCurrentAddr(v)+2,
+        regCount);
+    sqlite3VdbeAddOp2(v, OP_Integer, 0, regSamplerecno);
 
     /* Memory cells are used as follows. All memory cell addresses are
     ** offset by iMem. That is, cell 0 below is actually cell iMem, cell
@@ -167,8 +185,6 @@ static void analyzeOneTable(
     **    nCol+1..2*nCol:  Previous value of indexed columns, from left to
     **                     right.
     **
-    **    2*nCol+1..2*nCol+10: 10 evenly spaced samples.
-    **
     ** Cells iMem through iMem+nCol are initialized to 0.  The others
     ** are initialized to NULL.
     */
@@ -179,7 +195,7 @@ static void analyzeOneTable(
       sqlite3VdbeAddOp2(v, OP_Null, 0, iMem+nCol+i+1);
     }
 
-    /* Start the analysis loop. This loop runs through all the entries inof
+    /* Start the analysis loop. This loop runs through all the entries in
     ** the index b-tree.  */
     endOfLoop = sqlite3VdbeMakeLabel(v);
     sqlite3VdbeAddOp2(v, OP_Rewind, iIdxCur, endOfLoop);
@@ -189,15 +205,45 @@ static void analyzeOneTable(
     for(i=0; i<nCol; i++){
       sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regCol);
       if( i==0 ){
-        sqlite3VdbeAddOp3(v, OP_Sample, iMem-3, regCol, regStat2+2);
+
+        /* Check if the record that cursor iIdxCur points to contains a
+        ** value that should be stored in the sqlite_stat2 table. If so,
+        ** store it.  */
+        int ne = sqlite3VdbeAddOp3(v, OP_Ne, regRecno, 0, regSamplerecno);
+        assert( regTabname+1==regIdxname 
+             && regTabname+2==regSampleno
+             && regTabname+3==regCol
+        );
+        sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 4, regRec, "aaab", 0);
+        sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regRowid);
+        sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regRec, regRowid);
+
+        /* Calculate new values for regSamplerecno and regSampleno.
+        **
+        **   sampleno = sampleno + 1
+        **   samplerecno = samplerecno+(remaining records)/(remaining samples)
+        */
+        sqlite3VdbeAddOp2(v, OP_AddImm, regSampleno, 1);
+        sqlite3VdbeAddOp3(v, OP_Subtract, regRecno, regCount, regTemp);
+        sqlite3VdbeAddOp2(v, OP_AddImm, regTemp, -1);
+        sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES, regTemp2);
+        sqlite3VdbeAddOp3(v, OP_Subtract, regSampleno, regTemp2, regTemp2);
+        sqlite3VdbeAddOp3(v, OP_Divide, regTemp2, regTemp, regTemp);
+        sqlite3VdbeAddOp3(v, OP_Add, regSamplerecno, regTemp, regSamplerecno);
+
+        sqlite3VdbeJumpHere(v, ne);
+        sqlite3VdbeAddOp2(v, OP_AddImm, regRecno, 1);
       }
+
+      assert( sqlite3VdbeCurrentAddr(v)==(topOfLoop+14+2*i) );
       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++){
-      sqlite3VdbeJumpHere(v, topOfLoop + 1 + 2*(i + 1));
+      sqlite3VdbeJumpHere(v, topOfLoop+14+2*i);
       sqlite3VdbeAddOp2(v, OP_AddImm, iMem+i+1, 1);
       sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, iMem+nCol+i+1);
     }
@@ -226,33 +272,20 @@ static void analyzeOneTable(
     ** is never possible.
     */
     addr = sqlite3VdbeAddOp1(v, OP_IfNot, iMem);
-    sqlite3VdbeAddOp4(v, OP_String8, 0, regFields, 0, pTab->zName, 0);
-    sqlite3VdbeAddOp4(v, OP_String8, 0, regFields+1, 0, pIdx->zName, 0);
-    regF2 = regFields+2;
-    sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regF2);
+    sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regSampleno);
     for(i=0; i<nCol; i++){
       sqlite3VdbeAddOp4(v, OP_String8, 0, regTemp, 0, " ", 0);
-      sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regF2, regF2);
+      sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regSampleno, regSampleno);
       sqlite3VdbeAddOp3(v, OP_Add, iMem, iMem+i+1, regTemp);
       sqlite3VdbeAddOp2(v, OP_AddImm, regTemp, -1);
       sqlite3VdbeAddOp3(v, OP_Divide, iMem+i+1, regTemp, regTemp);
       sqlite3VdbeAddOp1(v, OP_ToInt, regTemp);
-      sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regF2, regF2);
+      sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regSampleno, regSampleno);
     }
-    sqlite3VdbeAddOp4(v, OP_MakeRecord, regFields, 3, regRec, "aaa", 0);
+    sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0);
     sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid);
     sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid);
     sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
-
-    /* Store the results in sqlite_stat2. */
-    sqlite3VdbeAddOp4(v, OP_String8, 0, regStat2, 0, pTab->zName, 0);
-    sqlite3VdbeAddOp4(v, OP_String8, 0, regStat2+1, 0, pIdx->zName, 0);
-    sqlite3VdbeAddOp4(v, OP_MakeRecord, regStat2, SQLITE_INDEX_SAMPLES+2,
-       regRec, "aabbbbbbbbbb", 0
-    );
-    sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regRowid);
-    sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regRec, regRowid);
-
     sqlite3VdbeJumpHere(v, addr);
   }
 }
@@ -461,85 +494,80 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
     sqlite3DbFree(db, zSql);
   }
 
-  /* Load the statistics from the sqlite_stat2 table */
+  /* Load the statistics from the sqlite_stat2 table. */
   if( rc==SQLITE_OK ){
+    sqlite3_stmt *pStmt = 0;
+
     zSql = sqlite3MPrintf(db, 
-       "SELECT idx," SQLITE_INDEX_SAMPLE_COLS " FROM %Q.sqlite_stat2",
-        sInfo.zDatabase
+        "SELECT idx,sampleno,sample FROM %Q.sqlite_stat2", sInfo.zDatabase
     );
-    if( zSql ){
-      sqlite3_stmt *pStmt = 0;
-      (void)sqlite3SafetyOff(db);
-      rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
-      if( rc==SQLITE_OK ){
-       while( SQLITE_ROW==sqlite3_step(pStmt) ){
-         char *zIndex = (char *)sqlite3_column_text(pStmt, 0);
-         Index *pIdx;
-          pIdx = sqlite3FindIndex(db, zIndex, sInfo.zDatabase);
-         if( pIdx ){
-           char *pSpace;
-           IndexSample *pSample;
-           int iCol;
-           int nAlloc = SQLITE_INDEX_SAMPLES * sizeof(IndexSample);
-           for(iCol=1; iCol<=SQLITE_INDEX_SAMPLES; iCol++){
-             int eType = sqlite3_column_type(pStmt, iCol);
-             if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
-               nAlloc += sqlite3_column_bytes(pStmt, iCol);
+    if( !zSql ){
+      return SQLITE_NOMEM;
+    }
+
+    (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 ){
+      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);
+          if( iSample<SQLITE_INDEX_SAMPLES && iSample>=0 ){
+            int eType = sqlite3_column_type(pStmt, 2);
+
+            if( pIdx->aSample==0 ){
+              pIdx->aSample = (IndexSample *)sqlite3DbMallocZero(db, 
+                  sizeof(IndexSample)*SQLITE_INDEX_SAMPLES
+              );
+             if( pIdx->aSample==0 ){
+               break;
              }
-           }
-           pSample = sqlite3DbMallocRaw(db, nAlloc);
-           if( !pSample ){
-             rc = SQLITE_NOMEM;
-             break;
-           }
-           sqlite3DbFree(db, pIdx->aSample);
-           pIdx->aSample = pSample;
-           pSpace = (char *)&pSample[SQLITE_INDEX_SAMPLES];
-           for(iCol=1; iCol<=SQLITE_INDEX_SAMPLES; iCol++){
-             int eType = sqlite3_column_type(pStmt, iCol);
-             pSample[iCol-1].eType = eType;
-             switch( eType ){
-                case SQLITE_BLOB:
-                case SQLITE_TEXT: {
-                  const char *z = (const char *)(
-                     (eType==SQLITE_BLOB) ?
-                      sqlite3_column_blob(pStmt, iCol):
-                      sqlite3_column_text(pStmt, iCol)
-                 );
-                  int n = sqlite3_column_bytes(pStmt, iCol);
-                 if( n>24 ){
-                   n = 24;
-                 }
-                 pSample[iCol-1].nByte = n;
-                 pSample[iCol-1].u.z = pSpace;
-                 memcpy(pSpace, z, n);
-                 pSpace += n;
-                 break;
+            }
+
+            if( pIdx->aSample ){
+              IndexSample *pSample = &pIdx->aSample[iSample];
+              if( pSample->eType==SQLITE_TEXT || pSample->eType==SQLITE_BLOB ){
+                sqlite3DbFree(db, pSample->u.z);
+              }
+             pSample->eType = eType;
+             if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
+                pSample->u.r = sqlite3_column_double(pStmt, 2);
+             }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
+                const char *z = (const char *)(
+                    (eType==SQLITE_BLOB) ?
+                    sqlite3_column_blob(pStmt, 2):
+                    sqlite3_column_text(pStmt, 2)
+                );
+                int n = sqlite3_column_bytes(pStmt, 2);
+                if( n>24 ){
+                  n = 24;
                 }
-                case SQLITE_INTEGER:
-                case SQLITE_FLOAT:
-                 pSample[iCol-1].u.r = sqlite3_column_double(pStmt, iCol);
-                 break;
-                case SQLITE_NULL:
+                pSample->nByte = n;
+                pSample->u.z = sqlite3DbMallocRaw(db, n);
+                if( pSample->u.z ){
+                  memcpy(pSample->u.z, z, n);
+                }else{
                  break;
-             }
-           }
-         }
-       }
-       if( rc==SQLITE_NOMEM ){
-         sqlite3_finalize(pStmt);
-       }else{
-         rc = sqlite3_finalize(pStmt);
-       }
+               }
+              }
+            }
+          }
+        }
       }
-      (void)sqlite3SafetyOn(db);
-      sqlite3DbFree(db, zSql);
-    }else{
-      rc = SQLITE_NOMEM;
+      rc = sqlite3_finalize(pStmt);
     }
+    (void)sqlite3SafetyOn(db);
   }
 
-  if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
+  if( rc==SQLITE_NOMEM ){
+    db->mallocFailed = 1;
+  }
   return rc;
 }
 
index 8cf9ec75309e9c3212d8b7dce6d11dd7a7fed92c..3e9b0ec96c61185358b045ebc029d60f57654db7 100644 (file)
@@ -343,6 +343,15 @@ 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);
   sqlite3DbFree(db, p->zColAff);
   sqlite3DbFree(db, p);
index 3912715e528ee26e0d196b20aaae07dd8ac69b07..56dd912ac7955ba5a4c5677b4a6dded27e36340e 100644 (file)
@@ -78,7 +78,6 @@
 #endif
 
 #define SQLITE_INDEX_SAMPLES 10
-#define SQLITE_INDEX_SAMPLE_COLS "s1,s2,s3,s4,s5,s6,s7,s8,s9,s10"
 
 /*
 ** This macro is used to "hide" some ugliness in casting an int
index 9bae9408464a6aa9f7712ce838a4248902c8fb20..44d12f68d75be388e395ddcea4ae4f3eba6f9c9d 100644 (file)
@@ -1205,9 +1205,9 @@ case OP_Concat: {           /* same as TK_CONCAT, in1, in2, out3 */
 /* Opcode: Divide P1 P2 P3 * *
 **
 ** Divide the value in register P1 by the value in register P2
-** and store the result in register P3.  If the value in register P2
-** is zero, then the result is NULL.
-** If either input is NULL, the result is NULL.
+** and store the result in register P3 (P3=P2/P1). If the value in 
+** register P1 is zero, then the result is NULL. If either input is 
+** NULL, the result is NULL.
 */
 /* Opcode: Remainder P1 P2 P3 * *
 **
@@ -4974,49 +4974,6 @@ case OP_Expire: {
   break;
 }
 
-
-/* Opcode: Sample P1 P2 P3 * *
-**
-** Register P1 contains the total number of rows in the index being 
-** analyzed. Register P1+1 contains an integer between 0 and 9, the
-** index of the next sample required. Register P1+2 contains an index
-** between 1 and *P1, the number of the next sample required. Register
-** P1+3 contains the current row index.
-**
-** If the integer in register P1+3 is the same as the integer in register
-** P1+1, then the following takes place:
-**
-**   (a) the contents of register P1+1 is incremented.
-**
-**   (b) the contents of the register identified by parameter P2 is 
-**       copied to register number (P3 + X), where X is the newly
-**       incremented value of register P1+1.
-**
-**   (c) register P1+2 is set to the index of the next sample required.
-*/
-case OP_Sample: {
-  int p1 = pOp->p1;
-  i64 iReq = p->aMem[p1+2].u.i;
-  i64 iRow = p->aMem[p1+3].u.i;
-
-  while( iReq==iRow ){
-    i64 nRow = p->aMem[p1].u.i;
-    int iSample = ++p->aMem[p1+1].u.i;
-    Mem *pReg = &p->aMem[pOp->p3 + iSample - 1];
-
-    assert( pReg<&p->aMem[p->nMem] );
-    sqlite3VdbeMemShallowCopy(pReg, &p->aMem[pOp->p2], MEM_Ephem);
-    Deephemeralize(pReg);
-    if( iSample==SQLITE_INDEX_SAMPLES ){
-      iReq = 0;
-    }else{
-      iReq = iRow + (nRow-iRow)/(SQLITE_INDEX_SAMPLES - iSample);
-      p->aMem[p1+2].u.i = iReq;
-    }
-  }
-  break;
-}
-
 #ifndef SQLITE_OMIT_SHARED_CACHE
 /* Opcode: TableLock P1 P2 P3 P4 *
 **
index d96578f5407af9aeb50711ca58db526a51d566cd..16d89606c7149ca3059bf96f5e904489d3d9d918 100644 (file)
@@ -1929,40 +1929,40 @@ static int whereRangeRegion(
       if( eType==SQLITE_BLOB ){
         z = (const u8 *)sqlite3_value_blob(pVal);
         pColl = db->pDfltColl;
-       assert( pColl->enc==SQLITE_UTF8 );
+        assert( pColl->enc==SQLITE_UTF8 );
       }else{
-       pColl = sqlite3FindCollSeq(db, SQLITE_UTF8, *pIdx->azColl, 0);
-       if( sqlite3CheckCollSeq(pParse, pColl) ){
-         return SQLITE_ERROR;
-       }
+        pColl = sqlite3FindCollSeq(db, SQLITE_UTF8, *pIdx->azColl, 0);
+        if( sqlite3CheckCollSeq(pParse, pColl) ){
+          return SQLITE_ERROR;
+        }
         z = (const u8 *)sqlite3ValueText(pVal, pColl->enc);
-       if( !z ){
-         return SQLITE_NOMEM;
-       }
+        if( !z ){
+          return SQLITE_NOMEM;
+        }
         assert( z && pColl && pColl->xCmp );
       }
       n = sqlite3ValueBytes(pVal, pColl->enc);
 
       for(i=0; i<SQLITE_INDEX_SAMPLES; i++){
-       int r;
+        int r;
         int eSampletype = aSample[i].eType;
         if( eSampletype==SQLITE_NULL || eSampletype<eType ) continue;
         if( (eSampletype!=eType) ) break;
         if( pColl->enc==SQLITE_UTF8 ){
-         r = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z);
+          r = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z);
         }else{
-         int nSample;
-         char *zSample = sqlite3Utf8to16(
+          int nSample;
+          char *zSample = sqlite3Utf8to16(
               db, pColl->enc, aSample[i].u.z, aSample[i].nByte, &nSample
           );
-         if( !zSample ){
-           assert( db->mallocFailed );
-           return SQLITE_NOMEM;
-         }
-         r = pColl->xCmp(pColl->pUser, nSample, zSample, n, z);
-         sqlite3DbFree(db, zSample);
+          if( !zSample ){
+            assert( db->mallocFailed );
+            return SQLITE_NOMEM;
+          }
+          r = pColl->xCmp(pColl->pUser, nSample, zSample, n, z);
+          sqlite3DbFree(db, zSample);
         }
-       if( r>0 ) break;
+        if( r>0 ) break;
       }
     }
 
@@ -2246,7 +2246,7 @@ static void bestBtreeIndex(
       if( findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){
         WhereTerm *pTop = findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE, pIdx);
         WhereTerm *pBtm = findTerm(pWC, iCur, j, notReady, WO_GT|WO_GE, pIdx);
-       whereRangeScanEst(pParse, pProbe, nEq, pBtm, pTop, &nBound);
+        whereRangeScanEst(pParse, pProbe, nEq, pBtm, pTop, &nBound);
         if( pTop ){
           wsFlags |= WHERE_TOP_LIMIT;
           used |= pTop->prereqRight;
diff --git a/test/analyze2.test b/test/analyze2.test
new file mode 100644 (file)
index 0000000..829cef9
--- /dev/null
@@ -0,0 +1,247 @@
+# 2009 August 06
+#
+# 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.
+#
+#***********************************************************************
+#
+# $Id: analyze.test,v 1.9 2008/08/11 18:44:58 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+sqlite3_db_config_lookaside db 0 0 0
+
+do_test analyze2-0.1 {
+  execsql { CREATE TABLE t1(x PRIMARY KEY) }
+  for {set i 0} {$i < 1000} {incr i} {
+    execsql { INSERT INTO t1 VALUES($i) }
+  }
+  execsql { 
+    ANALYZE;
+    SELECT * FROM sqlite_stat2;
+  }
+} [list t1 sqlite_autoindex_t1_1 0 0   \
+        t1 sqlite_autoindex_t1_1 1 111 \
+        t1 sqlite_autoindex_t1_1 2 222 \
+        t1 sqlite_autoindex_t1_1 3 333 \
+        t1 sqlite_autoindex_t1_1 4 444 \
+        t1 sqlite_autoindex_t1_1 5 555 \
+        t1 sqlite_autoindex_t1_1 6 666 \
+        t1 sqlite_autoindex_t1_1 7 777 \
+        t1 sqlite_autoindex_t1_1 8 888 \
+        t1 sqlite_autoindex_t1_1 9 999 \
+]
+
+do_test analyze2-0.2 {
+  execsql {
+    DELETE FROM t1 WHERe x>9;
+    ANALYZE;
+    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-0.3 {
+  execsql {
+    DELETE FROM t1 WHERE x>5;
+    ANALYZE;
+    SELECT * FROM sqlite_stat2;
+  }
+} {}
+
+do_test analyze2-0.4 {
+  execsql {
+    DELETE FROM t1;
+    ANALYZE;
+    SELECT * FROM sqlite_stat2;
+  }
+} {}
+
+proc eqp sql {
+  uplevel execsql [list "EXPLAIN QUERY PLAN $sql"]
+}
+
+do_test analyze2-1.1 {
+  execsql { 
+    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 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"
+} {0 0 {TABLE t1 WITH INDEX t1_y}}
+do_test analyze2-2.3 {
+  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 {
+  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 {
+  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 {
+  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 {
+  set alphabet [list a b c d e f g h i j]
+  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 ANALYZE
+  execsql { 
+    SELECT tbl,idx,group_concat(sample,' ') 
+    FROM sqlite_stat2 
+    WHERE idx = 't1_x' 
+    GROUP BY tbl,idx
+  }
+} {t1 t1_x {0 222 444 666 888 bba ddc ffe hhg jjj}}
+do_test analyze2-4.2 {
+  execsql { 
+    SELECT tbl,idx,group_concat(sample,' ') 
+    FROM sqlite_stat2 
+    WHERE idx = 't1_y' 
+    GROUP BY tbl,idx
+  }
+} {t1 t1_y {0 222 444 666 888 bba ddc ffe hhg jjj}}
+
+do_test analyze2-4.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 {
+  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 {
+  eqp "SELECT * FROM t1 WHERE x<'a' AND y>'h'"
+} {0 0 {TABLE t1 WITH INDEX t1_y}}
+do_test analyze2-4.6 {
+  eqp "SELECT * FROM t1 WHERE x<444 AND y>'h'"
+} {0 0 {TABLE t1 WITH INDEX t1_y}}
+do_test analyze2-4.7 {
+  eqp "SELECT * FROM t1 WHERE x<221 AND y>'h'"
+} {0 0 {TABLE t1 WITH INDEX t1_x}}
+
+do_test analyze2-5.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]
+  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 ANALYZE
+} {}
+do_test analyze2-5.2 {
+  execsql { 
+    SELECT tbl,idx,group_concat(sample,' ') 
+    FROM sqlite_stat2 
+    WHERE idx = 't3a' 
+    GROUP BY tbl,idx
+  }
+} {t3 t3a {AAA bbb CCC ddd EEE fff GGG hhh III jjj}}
+do_test analyze2-5.3 {
+  execsql { 
+    SELECT tbl,idx,group_concat(sample,' ') 
+    FROM sqlite_stat2 
+    WHERE idx = 't3b' 
+    GROUP BY tbl,idx
+  }
+} {t3 t3b {AAA CCC EEE GGG III bbb ddd fff hhh jjj}}
+
+do_test analyze2-5.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 {
+  eqp "SELECT * FROM t3 WHERE a > 'A' AND a < 'c' AND b > 'A' AND b < 'c'"
+} {0 0 {TABLE t3 WITH INDEX t3a}}
+
+proc test_collate {enc lhs rhs} {
+  # puts $enc
+  return [string compare $lhs $rhs]
+}
+
+do_test analyze2-6.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]
+  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 ANALYZE
+} {}
+do_test analyze2-6.2 {
+  execsql { 
+    SELECT tbl,idx,group_concat(sample,' ') 
+    FROM sqlite_stat2 
+    WHERE tbl = 't4' 
+    GROUP BY tbl,idx
+  }
+} {t4 t4x {aaa bbb ccc ddd eee fff ggg hhh iii jjj}}
+do_test analyze2-6.3 {
+  eqp "SELECT * FROM t4 WHERE x>'ccc'"
+} {0 0 {TABLE t4 WITH INDEX t4x}}
+do_test analyze2-6.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 {
+  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'
+  }
+}
+
+finish_test