Clean up sqlite_stat2 related code. Add test cases.
FossilOrigin-Name: aa728e06ce456fa42e68687bff6c7424460c31ef
diff --git a/src/analyze.c b/src/analyze.c
index 42645b7..31acbc3 100644
--- a/src/analyze.c
+++ b/src/analyze.c
@@ -116,16 +116,13 @@
int endOfLoop; /* The end of the loop */
int addr; /* The address of an instruction */
int iDb; /* Index of database containing pTab */
-
int regTabname = iMem++; /* Register containing table name */
int regIdxname = iMem++; /* Register containing index name */
int regSampleno = iMem++; /* Register containing next sample number */
int regCol = iMem++; /* Content of a column analyzed table */
-
int regRec = iMem++; /* Register holding completed record */
int regTemp = iMem++; /* Temporary use register */
int regRowid = iMem++; /* Rowid for the inserted record */
-
#ifdef SQLITE_ENABLE_STAT2
int regTemp2 = iMem++; /* Temporary use register */
int regSamplerecno = iMem++; /* Next sample index record number */
@@ -195,21 +192,21 @@
sqlite3VdbeAddOp2(v, OP_Integer, 0, regSamplerecno);
#endif
- /* Memory cells are used as follows. All memory cell addresses are
- ** offset by iMem. That is, cell 0 below is actually cell iMem, cell
- ** 1 is cell 1+iMem, etc.
+ /* The block of memory cells initialized here is used as follows.
**
- ** 0: The total number of rows in the table.
+ ** iMem:
+ ** The total number of rows in the table.
**
- ** 1..nCol: Number of distinct entries in index considering the
- ** left-most N columns, where N is the same as the
- ** memory cell number.
+ ** iMem+1 .. iMem+nCol:
+ ** Number of distinct entries in index considering the
+ ** left-most N columns only, where N is between 1 and nCol,
+ ** inclusive.
**
- ** nCol+1..2*nCol: Previous value of indexed columns, from left to
- ** right.
+ ** iMem+nCol+1 .. Mem+2*nCol:
+ ** Previous value of indexed columns, from left to right.
**
- ** Cells iMem through iMem+nCol are initialized to 0. The others
- ** are initialized to NULL.
+ ** Cells iMem through iMem+nCol are initialized to 0. The others are
+ ** initialized to contain an SQL NULL.
*/
for(i=0; i<=nCol; i++){
sqlite3VdbeAddOp2(v, OP_Integer, 0, iMem+i);
@@ -257,23 +254,15 @@
sqlite3VdbeJumpHere(v, ne);
sqlite3VdbeAddOp2(v, OP_AddImm, regRecno, 1);
}
- assert( sqlite3VdbeCurrentAddr(v)==(topOfLoop+14+2*i) );
-#else
- assert( sqlite3VdbeCurrentAddr(v)==(topOfLoop+2+2*i) );
#endif
sqlite3VdbeAddOp3(v, OP_Ne, regCol, 0, iMem+nCol+i+1);
-
/**** TODO: add collating sequence *****/
sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL);
}
sqlite3VdbeAddOp2(v, OP_Goto, 0, endOfLoop);
for(i=0; i<nCol; i++){
-#ifdef SQLITE_ENABLE_STAT2
- sqlite3VdbeJumpHere(v, topOfLoop+14+2*i);
-#else
- sqlite3VdbeJumpHere(v, topOfLoop+2+2*i);
-#endif
+ sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-(nCol*2));
sqlite3VdbeAddOp2(v, OP_AddImm, iMem+i+1, 1);
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, iMem+nCol+i+1);
}
@@ -486,8 +475,45 @@
}
/*
-** Load the content of the sqlite_stat1 and sqlite_stat2 tables into the
-** index hash tables.
+** If the Index.aSample variable is not NULL, delete the aSample[] array
+** and its contents.
+*/
+void sqlite3DeleteIndexSamples(Index *pIdx){
+#ifdef SQLITE_ENABLE_STAT2
+ if( pIdx->aSample ){
+ int j;
+ sqlite3 *dbMem = pIdx->pTable->dbMem;
+ for(j=0; j<SQLITE_INDEX_SAMPLES; j++){
+ IndexSample *p = &pIdx->aSample[j];
+ if( p->eType==SQLITE_TEXT || p->eType==SQLITE_BLOB ){
+ sqlite3DbFree(pIdx->pTable->dbMem, p->u.z);
+ }
+ }
+ sqlite3DbFree(dbMem, pIdx->aSample);
+ pIdx->aSample = 0;
+ }
+#endif
+}
+
+/*
+** Load the content of the sqlite_stat1 and sqlite_stat2 tables. The
+** contents of sqlite_stat1 are used to populate the Index.aiRowEst[]
+** arrays. The contents of sqlite_stat2 are used to populate the
+** Index.aSample[] arrays.
+**
+** If the sqlite_stat1 table is not present in the database, SQLITE_ERROR
+** is returned. In this case, even if SQLITE_ENABLE_STAT2 was defined
+** during compilation and the sqlite_stat2 table is present, no data is
+** read from it.
+**
+** If SQLITE_ENABLE_STAT2 was defined during compilation and the
+** sqlite_stat2 table is not present in the database, SQLITE_ERROR is
+** returned. However, in this case, data is read from the sqlite_stat1
+** table (if it is present) before returning.
+**
+** If an OOM error occurs, this function always sets db->mallocFailed.
+** This means if the caller does not care about other errors, the return
+** code may be ignored.
*/
int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
analysisInfo sInfo;
@@ -503,18 +529,19 @@
for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){
Index *pIdx = sqliteHashData(i);
sqlite3DefaultRowEst(pIdx);
+ sqlite3DeleteIndexSamples(pIdx);
}
- /* Check to make sure the sqlite_stat1 table existss */
+ /* Check to make sure the sqlite_stat1 table exists */
sInfo.db = db;
sInfo.zDatabase = db->aDb[iDb].zName;
if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)==0 ){
- return SQLITE_ERROR;
+ return SQLITE_ERROR;
}
/* Load new statistics out of the sqlite_stat1 table */
- zSql = sqlite3MPrintf(db, "SELECT idx, stat FROM %Q.sqlite_stat1",
- sInfo.zDatabase);
+ zSql = sqlite3MPrintf(db,
+ "SELECT idx, stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
if( zSql==0 ){
rc = SQLITE_NOMEM;
}else{
@@ -524,33 +551,35 @@
sqlite3DbFree(db, zSql);
}
+
/* Load the statistics from the sqlite_stat2 table. */
#ifdef SQLITE_ENABLE_STAT2
+ if( rc==SQLITE_OK && !sqlite3FindTable(db, "sqlite_stat2", sInfo.zDatabase) ){
+ rc = SQLITE_ERROR;
+ }
if( rc==SQLITE_OK ){
sqlite3_stmt *pStmt = 0;
zSql = sqlite3MPrintf(db,
- "SELECT idx,sampleno,sample FROM %Q.sqlite_stat2", sInfo.zDatabase
- );
+ "SELECT idx,sampleno,sample FROM %Q.sqlite_stat2", sInfo.zDatabase);
if( !zSql ){
- return SQLITE_NOMEM;
+ rc = SQLITE_NOMEM;
+ }else{
+ (void)sqlite3SafetyOff(db);
+ rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
+ (void)sqlite3SafetyOn(db);
+ sqlite3DbFree(db, zSql);
}
- (void)sqlite3SafetyOff(db);
- rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
- assert( rc!=SQLITE_MISUSE );
- (void)sqlite3SafetyOn(db);
- sqlite3DbFree(db, zSql);
- (void)sqlite3SafetyOff(db);
-
if( rc==SQLITE_OK ){
+ (void)sqlite3SafetyOff(db);
while( sqlite3_step(pStmt)==SQLITE_ROW ){
char *zIndex = (char *)sqlite3_column_text(pStmt, 0);
Index *pIdx = sqlite3FindIndex(db, zIndex, sInfo.zDatabase);
if( pIdx ){
int iSample = sqlite3_column_int(pStmt, 1);
sqlite3 *dbMem = pIdx->pTable->dbMem;
- assert( dbMem==db || dbMem==0 );
+ assert( dbMem==db || dbMem==0 );
if( iSample<SQLITE_INDEX_SAMPLES && iSample>=0 ){
int eType = sqlite3_column_type(pStmt, 2);
@@ -558,16 +587,13 @@
static const int sz = sizeof(IndexSample)*SQLITE_INDEX_SAMPLES;
pIdx->aSample = (IndexSample *)sqlite3DbMallocZero(dbMem, sz);
if( pIdx->aSample==0 ){
- db->mallocFailed = 1;
+ db->mallocFailed = 1;
break;
}
}
if( pIdx->aSample ){
IndexSample *pSample = &pIdx->aSample[iSample];
- if( pSample->eType==SQLITE_TEXT || pSample->eType==SQLITE_BLOB ){
- sqlite3DbFree(dbMem, pSample->u.z);
- }
pSample->eType = eType;
if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
pSample->u.r = sqlite3_column_double(pStmt, 2);
@@ -586,7 +612,7 @@
if( pSample->u.z ){
memcpy(pSample->u.z, z, n);
}else{
- db->mallocFailed = 1;
+ db->mallocFailed = 1;
break;
}
}
@@ -595,8 +621,8 @@
}
}
rc = sqlite3_finalize(pStmt);
+ (void)sqlite3SafetyOn(db);
}
- (void)sqlite3SafetyOn(db);
}
#endif
diff --git a/src/build.c b/src/build.c
index 3e9b0ec..e47aaed 100644
--- a/src/build.c
+++ b/src/build.c
@@ -343,16 +343,7 @@
static void freeIndex(Index *p){
sqlite3 *db = p->pTable->dbMem;
/* testcase( db==0 ); */
- if( p->aSample ){
- int i;
- for(i=0; i<SQLITE_INDEX_SAMPLES; i++){
- int e = p->aSample[i].eType;
- if( e==SQLITE_BLOB || e==SQLITE_TEXT ){
- sqlite3DbFree(db, p->aSample[i].u.z);
- }
- }
- }
- sqlite3DbFree(db, p->aSample);
+ sqlite3DeleteIndexSamples(p);
sqlite3DbFree(db, p->zColAff);
sqlite3DbFree(db, p);
}
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 277ffb4..da14167 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -2833,6 +2833,7 @@
int sqlite3FindDb(sqlite3*, Token*);
int sqlite3FindDbName(sqlite3 *, const char *);
int sqlite3AnalysisLoad(sqlite3*,int iDB);
+void sqlite3DeleteIndexSamples(Index*);
void sqlite3DefaultRowEst(Index*);
void sqlite3RegisterLikeFunctions(sqlite3*, int);
int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*);