-C Fix\sa\sbug\sin\scalculating\sthe\saverage\snumber\sof\sentries\sfor\skeys\snot\spresent\sin\sthe\ssqlite_stat4\stable.
-D 2013-08-12T11:21:10.969
+C Re-enable\sreading\sfrom\sthe\ssqlite_stat3\stable\s(as\swell\sas\ssqlite_stat4).
+D 2013-08-12T16:34:32.514
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 5e41da95d92656a5004b03d3576e8b226858a28e
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad
F src/alter.c 2af0330bb1b601af7a7789bf7229675fd772a083
-F src/analyze.c fd1bcb9bc4ca29cd36f60c620cc501c933048c28
+F src/analyze.c e5ce42f04f58003fa45908791a5ad06bdd2c2ff1
F src/attach.c 1816f5a9eea8d2010fc2b22b44f0f63eb3a62704
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
F src/backup.c 43b348822db3e4cef48b2ae5a445fbeb6c73a165
F src/expr.c 0bbb44462a19169189b2709fbbd800950521b5ae
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c 914a6bbd987d857c41ac9d244efa6641f36faadb
-F src/func.c 5c50c1ea31fd864b0fe921fe1a8d4c55acd609ef
+F src/func.c 78c371ddfb0bb2d4c4356e8d7336b582d4ca96a9
F src/global.c 5caf4deab621abb45b4c607aad1bd21c20aac759
F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4
F src/hash.h 8890a25af81fb85a9ad7790d32eedab4b994da22
F src/sqlite.h.in bd1451ba1ab681022a53bccc3c39580ba094a3ff
F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0
F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
-F src/sqliteInt.h 0ff47977058e1babf0c4265f1791c379d172b02f
+F src/sqliteInt.h 646063fc1564842fd8e54eee00f8b8b429e2eb1f
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
F src/test_demovfs.c 69b2085076654ebc18014cbc6386f04409c959a9
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
F src/test_fs.c ced436e3d4b8e4681328409b8081051ce614e28f
-F src/test_func.c fcd238feb694332d5962ee08578ef30ff4ac6559
+F src/test_func.c 338a6e5ade3560ad36280881bbcf45f28d06cb68
F src/test_hexio.c abfdecb6fa58c354623978efceb088ca18e379cd
F src/test_init.c 3cbad7ce525aec925f8fda2192d576d47f0d478a
F src/test_intarray.c 87847c71c3c36889c0bcc9c4baf9d31881665d61
F test/analyze6.test 3c01e084309706a1033f850330ea24f6f7846297
F test/analyze7.test c0af22c5e0140e2e4ac556a21c2b6fff58229c98
F test/analyze8.test 8d1f76ff1e47c4093bb7be3971ba08fa56dc470d
-F test/analyze9.test 3e1bd0209354bb987832fba580c754cf77dc6ba3
+F test/analyze9.test 1b419d03407f2a6f4f1485620d54cb3e1bab3a71
+F test/analyzeA.test 949c3344280e0ca6de0b49805e4f291cdc1daa43
F test/async.test 1d0e056ba1bb9729283a0f22718d3a25e82c277b
F test/async2.test c0a9bd20816d7d6a2ceca7b8c03d3d69c28ffb8b
F test/async3.test d73a062002376d7edc1fe3edff493edbec1fc2f7
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
F tool/wherecosttest.c f407dc4c79786982a475261866a161cd007947ae
F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
-P 088d1ff94890ada50d43e6a366a58167ec5a8e96
-R 4b6821682980828caa2dcfc6f067a6e7
+P ec3ffb174844406a6186c3dcc41b76d0331b502c
+R e94619aadc884e84bf22674359c1bbe0
U dan
-Z 150d3ee65bd4be58d3670346ad74798d
+Z c424468455b7fdc35c2aa42e6130568e
-ec3ffb174844406a6186c3dcc41b76d0331b502c
\ No newline at end of file
+6d45078e621526fc2bac0eaefbb0f9602b9a8ec5
\ No newline at end of file
const char *zCols;
} aTable[] = {
{ "sqlite_stat1", "tbl,idx,stat" },
-#ifdef SQLITE_ENABLE_STAT4
+#if defined(SQLITE_ENABLE_STAT4)
{ "sqlite_stat4", "tbl,idx,neq,nlt,ndlt,sample" },
+#elif defined(SQLITE_ENABLE_STAT3)
+ { "sqlite_stat3", "tbl,idx,neq,nlt,ndlt,sample" },
#endif
};
}
#ifdef SQLITE_ENABLE_STAT4
+
/*
-** Load content from the sqlite_stat4 table into the Index.aSample[]
-** arrays of all indices.
+** The implementation of the sqlite_record() function. This function accepts
+** a single argument of any type. The return value is a formatted database
+** record (a blob) containing the argument value.
+**
+** This is used to convert the value stored in the 'sample' column of the
+** sqlite_stat3 table to the record format SQLite uses internally.
*/
-static int loadStat4(sqlite3 *db, const char *zDb){
+static void recordFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const int file_format = 1;
+ int iSerial; /* Serial type */
+ int nSerial; /* Bytes of space for iSerial as varint */
+ int nVal; /* Bytes of space required for argv[0] */
+ int nRet;
+ sqlite3 *db;
+ u8 *aRet;
+
+ iSerial = sqlite3VdbeSerialType(argv[0], file_format);
+ nSerial = sqlite3VarintLen(iSerial);
+ nVal = sqlite3VdbeSerialTypeLen(iSerial);
+ db = sqlite3_context_db_handle(context);
+
+ nRet = 1 + nSerial + nVal;
+ aRet = sqlite3DbMallocRaw(db, nRet);
+ if( aRet==0 ){
+ sqlite3_result_error_nomem(context);
+ }else{
+ aRet[0] = nSerial+1;
+ sqlite3PutVarint(&aRet[1], iSerial);
+ sqlite3VdbeSerialPut(&aRet[1+nSerial], nVal, argv[0], file_format);
+ sqlite3_result_blob(context, aRet, nRet, SQLITE_TRANSIENT);
+ sqlite3DbFree(db, aRet);
+ }
+}
+
+/*
+** Register built-in functions used to help read ANALYZE data.
+*/
+void sqlite3AnalyzeFunctions(void){
+ static SQLITE_WSD FuncDef aAnalyzeTableFuncs[] = {
+ FUNCTION(sqlite_record, 1, 0, 0, recordFunc),
+ };
+ int i;
+ FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
+ FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aAnalyzeTableFuncs);
+ for(i=0; i<ArraySize(aAnalyzeTableFuncs); i++){
+ sqlite3FuncDefInsert(pHash, &aFunc[i]);
+ }
+}
+
+/*
+** Load the content from either the sqlite_stat4 or sqlite_stat3 table
+** into the relevant Index.aSample[] arrays.
+**
+** Arguments zSql1 and zSql2 must point to SQL statements that return
+** data equivalent to the following (statements are different for stat3,
+** see the caller of this function for details):
+**
+** zSql1: SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx
+** zSql2: SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4
+**
+** where %Q is replaced with the database name before the SQL is executed.
+*/
+static int loadStatTbl(
+ sqlite3 *db, /* Database handle */
+ int bStat3, /* Assume single column records only */
+ const char *zSql1, /* SQL statement 1 (see above) */
+ const char *zSql2, /* SQL statement 2 (see above) */
+ const char *zDb /* Database name (e.g. "main") */
+){
int rc; /* Result codes from subroutines */
sqlite3_stmt *pStmt = 0; /* An SQL statement being run */
char *zSql; /* Text of the SQL statement */
IndexSample *pSample; /* A slot in pIdx->aSample[] */
assert( db->lookaside.bEnabled==0 );
- if( !sqlite3FindTable(db, "sqlite_stat4", zDb) ){
- return SQLITE_OK;
- }
-
- zSql = sqlite3MPrintf(db,
- "SELECT idx,count(*) FROM %Q.sqlite_stat4"
- " GROUP BY idx", zDb);
+ zSql = sqlite3MPrintf(db, zSql1, zDb);
if( !zSql ){
return SQLITE_NOMEM;
}
if( rc ) return rc;
while( sqlite3_step(pStmt)==SQLITE_ROW ){
+ int nIdxCol = 1; /* Number of columns in stat4 records */
+ int nAvgCol = 1; /* Number of entries in Index.aAvgEq */
+
char *zIndex; /* Index name */
Index *pIdx; /* Pointer to the index object */
int nSample; /* Number of samples */
pIdx = sqlite3FindIndex(db, zIndex, zDb);
if( pIdx==0 ) continue;
assert( pIdx->nSample==0 );
+ if( bStat3==0 ){
+ nIdxCol = pIdx->nColumn+1;
+ nAvgCol = pIdx->nColumn;
+ }
pIdx->nSample = nSample;
nByte = sizeof(IndexSample) * nSample;
- nByte += sizeof(tRowcnt) * (pIdx->nColumn+1) * 3 * nSample;
- nByte += pIdx->nColumn * sizeof(tRowcnt); /* Space for Index.aAvgEq[] */
+ nByte += sizeof(tRowcnt) * nIdxCol * 3 * nSample;
+ nByte += nAvgCol * sizeof(tRowcnt); /* Space for Index.aAvgEq[] */
pIdx->aSample = sqlite3DbMallocZero(db, nByte);
if( pIdx->aSample==0 ){
return SQLITE_NOMEM;
}
pSpace = (tRowcnt*)&pIdx->aSample[nSample];
- pIdx->aAvgEq = pSpace; pSpace += pIdx->nColumn;
+ pIdx->aAvgEq = pSpace; pSpace += nAvgCol;
for(i=0; i<pIdx->nSample; i++){
- pIdx->aSample[i].anEq = pSpace; pSpace += pIdx->nColumn+1;
- pIdx->aSample[i].anLt = pSpace; pSpace += pIdx->nColumn+1;
- pIdx->aSample[i].anDLt = pSpace; pSpace += pIdx->nColumn+1;
+ pIdx->aSample[i].anEq = pSpace; pSpace += nIdxCol;
+ pIdx->aSample[i].anLt = pSpace; pSpace += nIdxCol;
+ pIdx->aSample[i].anDLt = pSpace; pSpace += nIdxCol;
}
assert( ((u8*)pSpace)-nByte==(u8*)(pIdx->aSample) );
}
rc = sqlite3_finalize(pStmt);
if( rc ) return rc;
- zSql = sqlite3MPrintf(db,
- "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4", zDb);
+ zSql = sqlite3MPrintf(db, zSql2, zDb);
if( !zSql ){
return SQLITE_NOMEM;
}
if( rc ) return rc;
while( sqlite3_step(pStmt)==SQLITE_ROW ){
- char *zIndex; /* Index name */
- Index *pIdx; /* Pointer to the index object */
- int i; /* Loop counter */
- int nCol; /* Number of columns in index */
+ char *zIndex; /* Index name */
+ Index *pIdx; /* Pointer to the index object */
+ int i; /* Loop counter */
+ int nCol = 1; /* Number of columns in index */
zIndex = (char *)sqlite3_column_text(pStmt, 0);
if( zIndex==0 ) continue;
assert( idx<pIdx->nSample );
pSample = &pIdx->aSample[idx];
- nCol = pIdx->nColumn+1;
+ if( bStat3==0 ){
+ nCol = pIdx->nColumn+1;
+ }
decodeIntArray((char*)sqlite3_column_text(pStmt,1), nCol, pSample->anEq, 0);
decodeIntArray((char*)sqlite3_column_text(pStmt,2), nCol, pSample->anLt, 0);
decodeIntArray((char*)sqlite3_column_text(pStmt,3), nCol, pSample->anDLt,0);
}
if( avgEq==0 ) avgEq = 1;
pIdx->aAvgEq[iCol] = avgEq;
+ if( bStat3 ) break;
}
}
}
return sqlite3_finalize(pStmt);
}
+
+/*
+** Load content from the sqlite_stat4 and sqlite_stat3 tables into
+** the Index.aSample[] arrays of all indices.
+*/
+static int loadStat4(sqlite3 *db, const char *zDb){
+ int rc = SQLITE_OK; /* Result codes from subroutines */
+
+ assert( db->lookaside.bEnabled==0 );
+ if( sqlite3FindTable(db, "sqlite_stat4", zDb) ){
+ rc = loadStatTbl(db, 0,
+ "SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx",
+ "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4",
+ zDb
+ );
+ }
+
+ if( rc==SQLITE_OK && sqlite3FindTable(db, "sqlite_stat3", zDb) ){
+ rc = loadStatTbl(db, 1,
+ "SELECT idx,count(*) FROM %Q.sqlite_stat3 GROUP BY idx",
+ "SELECT idx,neq,nlt,ndlt,sqlite_record(sample) FROM %Q.sqlite_stat3",
+ zDb
+ );
+ }
+
+ return rc;
+}
#endif /* SQLITE_ENABLE_STAT4 */
/*
#ifndef SQLITE_OMIT_ALTERTABLE
sqlite3AlterFunctions();
#endif
+#if defined(SQLITE_ENABLE_STAT3) || defined(SQLITE_ENABLE_STAT4)
+ sqlite3AnalyzeFunctions();
+#endif
}
void sqlite3RootPageMoved(sqlite3*, int, int, int);
void sqlite3Reindex(Parse*, Token*, Token*);
void sqlite3AlterFunctions(void);
+void sqlite3AnalyzeFunctions(void);
void sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
int sqlite3GetToken(const unsigned char *, int *);
void sqlite3NestedParse(Parse*, const char*, ...);
sqlite3_result_text(context, zOut, -1, SQLITE_TRANSIENT);
}
+/*
+** tclcmd: test_extract(record, field)
+**
+** This function implements an SQL user-function that accepts a blob
+** containing a formatted database record as the first argument. The
+** second argument is the index of the field within that record to
+** extract and return.
+*/
+static void test_extract(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ u8 *pRec;
+ u8 *pEndHdr; /* Points to one byte past record header */
+ u8 *pHdr; /* Current point in record header */
+ u8 *pBody; /* Current point in record data */
+ u64 nHdr; /* Bytes in record header */
+ int iIdx; /* Required field */
+ int iCurrent = 0; /* Current field */
+
+ assert( argc==2 );
+ pRec = (u8*)sqlite3_value_blob(argv[0]);
+ iIdx = sqlite3_value_int(argv[1]);
+
+ pHdr = pRec + sqlite3GetVarint(pRec, &nHdr);
+ pBody = pEndHdr = &pRec[nHdr];
+
+ for(iCurrent=0; pHdr<pEndHdr && iCurrent<=iIdx; iCurrent++){
+ u64 iSerialType;
+ Mem mem;
+
+ memset(&mem, 0, sizeof(mem));
+ mem.db = db;
+ mem.enc = SQLITE_UTF8;
+ pHdr += sqlite3GetVarint(pHdr, &iSerialType);
+ pBody += sqlite3VdbeSerialGet(pBody, (u32)iSerialType, &mem);
+ sqlite3VdbeMemStoreType(&mem);
+
+ if( iCurrent==iIdx ){
+ sqlite3_result_value(context, &mem);
+ }
+
+ sqlite3DbFree(db, mem.zMalloc);
+ }
+}
+
/*
** tclcmd: test_decode(record)
**
{ "test_counter", 1, SQLITE_UTF8, counterFunc},
{ "real2hex", 1, SQLITE_UTF8, real2hex},
{ "test_decode", 1, SQLITE_UTF8, test_decode},
+ { "test_extract", 2, SQLITE_UTF8, test_extract},
};
int i;
#
#***********************************************************************
#
+# This file contains automated tests used to verify that the sqlite_stat4
+# functionality is working.
+#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
--- /dev/null
+# 2013 August 3
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+# This file contains automated tests used to verify that the current build
+# (which must be either ENABLE_STAT3 or ENABLE_STAT4) works with both stat3
+# and stat4 data.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix analyzeA
+
+ifcapable !stat4&&!stat3 {
+ finish_test
+ return
+}
+
+proc populate_stat3 {} {
+ # Open a second connection on database "test.db" and run ANALYZE. If this
+ # is an ENABLE_STAT3 build, this is all that is required to create and
+ # populate the sqlite_stat3 table.
+ #
+ sqlite3 db2 test.db
+ execsql { ANALYZE }
+
+ # Now, if this is an ENABLE_STAT4 build, create and populate the
+ # sqlite_stat3 table based on the stat4 data gathered by the ANALYZE
+ # above. Then drop the sqlite_stat4 table.
+ #
+ ifcapable stat4 {
+ db2 func lindex lindex
+ execsql {
+ PRAGMA writable_schema = on;
+ CREATE TABLE sqlite_stat3(tbl,idx,neq,nlt,ndlt,sample);
+ INSERT INTO sqlite_stat3
+ SELECT DISTINCT tbl, idx,
+ lindex(neq,0), lindex(nlt,0), lindex(ndlt,0), test_extract(sample, 0)
+ FROM sqlite_stat4;
+ DROP TABLE sqlite_stat4;
+ PRAGMA writable_schema = off;
+ } db2
+ }
+
+ # Modify the database schema cookie to ensure that the other connection
+ # reloads the schema.
+ #
+ execsql {
+ CREATE TABLE obscure_tbl_nm(x);
+ DROP TABLE obscure_tbl_nm;
+ } db2
+ db2 close
+}
+
+
+proc populate_stat4 {} {
+ execsql { ANALYZE }
+# ifcapable stat3 {
+# execsql {
+# PRAGMA writable_schema = on;
+# CREATE TABLE sqlite_stat4(tbl,idx,neq,nlt,ndlt,sample);
+# INSERT INTO sqlite_stat4 SELECT
+# tbl, idx, nlt, neq, ndlt,
+# test_extract(sample, 1)
+# FROM sqlite_stat4;
+# DROP TABLE sqlite_stat4;
+# PRAGMA writable_schema = off;
+# ANALYZE sqlite_master;
+# }
+# }
+}
+
+foreach {tn analyze_cmd} {1 populate_stat4 2 populate_stat3} {
+ reset_db
+ do_test 1.$tn.1 {
+ execsql { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c) }
+ for {set i 0} {$i < 100} {incr i} {
+ set c [expr int(pow(1.1,$i)/100)]
+ set b [expr 125 - int(pow(1.1,99-$i))/100]
+ execsql {INSERT INTO t1 VALUES($i, $b, $c)}
+ }
+ } {}
+
+ execsql { CREATE INDEX t1b ON t1(b) }
+ execsql { CREATE INDEX t1c ON t1(c) }
+ $analyze_cmd
+
+ do_execsql_test 1.$tn.2.1 { SELECT count(*) FROM t1 WHERE b=31 } 1
+ do_execsql_test 1.$tn.2.2 { SELECT count(*) FROM t1 WHERE c=0 } 49
+ do_execsql_test 1.$tn.2.3 { SELECT count(*) FROM t1 WHERE b=125 } 49
+ do_execsql_test 1.$tn.2.4 { SELECT count(*) FROM t1 WHERE c=16 } 1
+
+ do_eqp_test 1.$tn.2.5 {
+ SELECT * FROM t1 WHERE b = 31 AND c = 0;
+ } {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)}}
+ do_eqp_test 1.$tn.2.6 {
+ SELECT * FROM t1 WHERE b = 125 AND c = 16;
+ } {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c=?)}}
+
+ do_execsql_test 1.$tn.3.1 {
+ SELECT count(*) FROM t1 WHERE b BETWEEN 0 AND 50
+ } {6}
+ do_execsql_test 1.$tn.3.2 {
+ SELECT count(*) FROM t1 WHERE c BETWEEN 0 AND 50
+ } {90}
+
+ do_execsql_test 1.$tn.3.3 {
+ SELECT count(*) FROM t1 WHERE b BETWEEN 75 AND 125
+ } {90}
+ do_execsql_test 1.$tn.3.4 {
+ SELECT count(*) FROM t1 WHERE c BETWEEN 75 AND 125
+ } {6}
+
+ do_eqp_test 1.$tn.3.5 {
+ SELECT * FROM t1 WHERE b BETWEEN 0 AND 50 AND c BETWEEN 0 AND 50
+ } {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?)}}
+
+ do_eqp_test 1.$tn.3.6 {
+ SELECT * FROM t1 WHERE b BETWEEN 75 AND 125 AND c BETWEEN 75 AND 125
+ } {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?)}}
+}
+
+
+finish_test
+