diff --git a/manifest b/manifest index ec6bc382cf..201500ef2f 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index 5099e8f445..2293825c06 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -dd96bda2a85c1d94fb4a0bf5f27e2977f7f7e42e \ No newline at end of file +ded9dec6459baf21e01f63250db5ace57f390e7a \ No newline at end of file diff --git a/src/analyze.c b/src/analyze.c index e8c7c37171..c6c279f278 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -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; izName, 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; izName, 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); - } - } - 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; - } - case SQLITE_INTEGER: - case SQLITE_FLOAT: - pSample[iCol-1].u.r = sqlite3_column_double(pStmt, iCol); - break; - case SQLITE_NULL: - break; - } - } - } - } - if( rc==SQLITE_NOMEM ){ - sqlite3_finalize(pStmt); - }else{ - rc = sqlite3_finalize(pStmt); - } - } - (void)sqlite3SafetyOn(db); - sqlite3DbFree(db, zSql); - }else{ - rc = SQLITE_NOMEM; + 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=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; + } + } + + 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; + } + pSample->nByte = n; + pSample->u.z = sqlite3DbMallocRaw(db, n); + if( pSample->u.z ){ + memcpy(pSample->u.z, z, n); + }else{ + break; + } + } + } + } + } + } + rc = sqlite3_finalize(pStmt); + } + (void)sqlite3SafetyOn(db); } - if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; + if( rc==SQLITE_NOMEM ){ + db->mallocFailed = 1; + } return rc; } diff --git a/src/build.c b/src/build.c index 8cf9ec7530..3e9b0ec96c 100644 --- a/src/build.c +++ b/src/build.c @@ -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; iaSample[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); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 3912715e52..56dd912ac7 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -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 diff --git a/src/vdbe.c b/src/vdbe.c index 9bae940846..44d12f68d7 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -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 * ** diff --git a/src/where.c b/src/where.c index d96578f540..16d89606c7 100644 --- a/src/where.c +++ b/src/where.c @@ -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; ienc==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 index 0000000000..829cef9627 --- /dev/null +++ b/test/analyze2.test @@ -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