Re-enable reading from the sqlite_stat3 table (as well as sqlite_stat4).

FossilOrigin-Name: 6d45078e621526fc2bac0eaefbb0f9602b9a8ec5
This commit is contained in:
dan 2013-08-12 16:34:32 +00:00
parent 5133c78cae
commit 0106e378f1
8 changed files with 326 additions and 35 deletions

View File

@ -1,5 +1,5 @@
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
@ -157,7 +157,7 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
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
@ -175,7 +175,7 @@ F src/delete.c 2317c814866d9aa71fea16b3faf4fdd4d6a49b94
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
@ -221,7 +221,7 @@ F src/shell.c 128eb16ccec68509a4a2f1948f2483819bf63425
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
@ -243,7 +243,7 @@ F src/test_config.c 636ecd15a6ba18bf97a590b5a21f47573c8c2b65
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
@ -308,7 +308,8 @@ F test/analyze5.test 96ac783a56142bbbedb58a7c1eebd1808b49cfae
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
@ -1106,7 +1107,7 @@ F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
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

View File

@ -1 +1 @@
ec3ffb174844406a6186c3dcc41b76d0331b502c
6d45078e621526fc2bac0eaefbb0f9602b9a8ec5

View File

@ -168,8 +168,10 @@ static void openStatTable(
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
};
@ -1206,11 +1208,81 @@ void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
}
#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 */
@ -1219,13 +1291,7 @@ static int loadStat4(sqlite3 *db, const char *zDb){
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;
}
@ -1234,6 +1300,9 @@ static int loadStat4(sqlite3 *db, const char *zDb){
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 */
@ -1247,10 +1316,14 @@ static int loadStat4(sqlite3 *db, const char *zDb){
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 ){
@ -1258,19 +1331,18 @@ static int loadStat4(sqlite3 *db, const char *zDb){
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;
}
@ -1279,10 +1351,10 @@ static int loadStat4(sqlite3 *db, const char *zDb){
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;
@ -1297,7 +1369,9 @@ static int loadStat4(sqlite3 *db, const char *zDb){
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);
@ -1327,6 +1401,7 @@ static int loadStat4(sqlite3 *db, const char *zDb){
}
if( avgEq==0 ) avgEq = 1;
pIdx->aAvgEq[iCol] = avgEq;
if( bStat3 ) break;
}
}
@ -1340,6 +1415,33 @@ static int loadStat4(sqlite3 *db, const char *zDb){
}
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 */
/*

View File

@ -1715,4 +1715,7 @@ void sqlite3RegisterGlobalFunctions(void){
#ifndef SQLITE_OMIT_ALTERTABLE
sqlite3AlterFunctions();
#endif
#if defined(SQLITE_ENABLE_STAT3) || defined(SQLITE_ENABLE_STAT4)
sqlite3AnalyzeFunctions();
#endif
}

View File

@ -3055,6 +3055,7 @@ extern int sqlite3PendingByte;
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*, ...);

View File

@ -461,6 +461,54 @@ static void real2hex(
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)
**
@ -579,6 +627,7 @@ static int registerTestFunctions(sqlite3 *db){
{ "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;

View File

@ -9,6 +9,9 @@
#
#***********************************************************************
#
# This file contains automated tests used to verify that the sqlite_stat4
# functionality is working.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl

132
test/analyzeA.test Normal file
View File

@ -0,0 +1,132 @@
# 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