Index: src/analyze.c ================================================================== --- src/analyze.c +++ src/analyze.c @@ -139,18 +139,21 @@ */ #ifndef SQLITE_OMIT_ANALYZE #include "sqliteInt.h" #if defined(SQLITE_ENABLE_STAT4) -# define IsStat4 1 -# define IsStat3 0 +# define IsStat4 1 +# define IsStat3 0 +# define SQLITE_ENABLE_STAT34 1 #elif defined(SQLITE_ENABLE_STAT3) -# define IsStat4 0 -# define IsStat3 1 +# define IsStat4 0 +# define IsStat3 1 +# define SQLITE_ENABLE_STAT34 1 #else -# define IsStat4 0 -# define IsStat3 0 +# define IsStat4 0 +# define IsStat3 0 +# undef SQLITE_ENABLE_STAT34 #endif /* ** This routine generates code that opens the sqlite_stat1 table for ** writing with cursor iStatCur. If the library was built with the @@ -364,10 +367,11 @@ "stat_init", /* zName */ 0, /* pHash */ 0 /* pDestructor */ }; +#ifdef SQLITE_ENABLE_STAT34 /* ** Return true if pNew is to be preferred over pOld. */ static int sampleIsBetter(Stat4Sample *pNew, Stat4Sample *pOld){ tRowcnt nEqNew = pNew->anEq[pNew->iCol]; @@ -485,42 +489,44 @@ } assert( iMin>=0 ); p->iMin = iMin; } } +#endif /* SQLITE_ENABLE_STAT34 */ /* ** Field iChng of the index being scanned has changed. So at this point ** p->current contains a sample that reflects the previous row of the ** index. The value of anEq[iChng] and subsequent anEq[] elements are ** correct at this point. */ static void samplePushPrevious(Stat4Accum *p, int iChng){ - if( IsStat4 ){ - int i; - - /* Check if any samples from the aBest[] array should be pushed - ** into IndexSample.a[] at this point. */ - for(i=(p->nCol-2); i>=iChng; i--){ - Stat4Sample *pBest = &p->aBest[i]; - if( p->nSamplemxSample - || sampleIsBetter(pBest, &p->a[p->iMin]) - ){ - sampleInsert(p, pBest, i); - } - } - - /* Update the anEq[] fields of any samples already collected. */ - for(i=p->nSample-1; i>=0; i--){ - int j; - for(j=iChng; jnCol; j++){ - if( p->a[i].anEq[j]==0 ) p->a[i].anEq[j] = p->current.anEq[j]; - } - } - } - - if( IsStat3 && iChng==0 ){ +#ifdef SQLITE_ENABLE_STAT4 + int i; + + /* Check if any samples from the aBest[] array should be pushed + ** into IndexSample.a[] at this point. */ + for(i=(p->nCol-2); i>=iChng; i--){ + Stat4Sample *pBest = &p->aBest[i]; + if( p->nSamplemxSample + || sampleIsBetter(pBest, &p->a[p->iMin]) + ){ + sampleInsert(p, pBest, i); + } + } + + /* Update the anEq[] fields of any samples already collected. */ + for(i=p->nSample-1; i>=0; i--){ + int j; + for(j=iChng; jnCol; j++){ + if( p->a[i].anEq[j]==0 ) p->a[i].anEq[j] = p->current.anEq[j]; + } + } +#endif + +#if defined(SQLITE_ENABLE_STAT3) && !defined(SQLITE_ENABLE_STAT4) + if( iChng==0 ){ tRowcnt nLt = p->current.anLt[0]; tRowcnt nEq = p->current.anEq[0]; /* Check if this is to be a periodic sample. If so, add it. */ if( (nLt/p->nPSample)!=(nLt+nEq)/p->nPSample ){ @@ -532,10 +538,11 @@ /* Or if it is a non-periodic sample. Add it in this case too. */ if( p->nSamplemxSample || sampleIsBetter(&p->current, &p->a[p->iMin]) ){ sampleInsert(p, &p->current, 0); } } +#endif } /* ** Implementation of the stat_push SQL function. ** @@ -581,11 +588,12 @@ if( IsStat4 || IsStat3 ){ p->current.iRowid = rowid; p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345; } - if( IsStat4 ){ +#ifdef SQLITE_ENABLE_STAT4 + { tRowcnt nLt = p->current.anLt[p->nCol-1]; /* Check if this is to be a periodic sample. If so, add it. */ if( (nLt/p->nPSample)!=(nLt+1)/p->nPSample ){ p->current.isPSample = 1; @@ -600,10 +608,11 @@ if( i>=iChng || sampleIsBetter(&p->current, &p->aBest[i]) ){ sampleCopy(p, &p->aBest[i], &p->current); } } } +#endif } static const FuncDef statPushFuncdef = { 3, /* nArg */ SQLITE_UTF8, /* iPrefEnc */ 0, /* flags */ @@ -622,33 +631,38 @@ #define STAT_GET_NEQ 2 /* "neq" column of stat[34] entry */ #define STAT_GET_NLT 3 /* "nlt" column of stat[34] entry */ #define STAT_GET_NDLT 4 /* "ndlt" column of stat[34] entry */ /* -** Implementation of the stat3_get(P,N,...) SQL function. This routine is -** used to query the results. Content is returned for the Nth sqlite_stat3 -** row where N is between 0 and S-1 and S is the number of samples. The -** value returned depends on the number of arguments. +** Implementation of the stat_get(P,J) SQL function. This routine is +** used to query the results. Content is returned for parameter J +** which is one of the STAT_GET_xxxx values defined above. ** -** argc==2 result: rowid -** argc==3 result: nEq -** argc==4 result: nLt -** argc==5 result: nDLt +** If neither STAT3 nor STAT4 are enabled, then J is always +** STAT_GET_STAT1 and is hence omitted and this routine becomes +** a one-parameter function, stat_get(P), that always returns the +** stat1 table entry information. */ static void statGet( sqlite3_context *context, int argc, sqlite3_value **argv ){ Stat4Accum *p = (Stat4Accum*)sqlite3_value_blob(argv[0]); +#ifdef SQLITE_ENABLE_STAT34 + /* STAT3 and STAT4 have a parameter on this routine. */ int eCall = sqlite3_value_int(argv[1]); + assert( argc==2 ); assert( eCall==STAT_GET_STAT1 || eCall==STAT_GET_NEQ || eCall==STAT_GET_ROWID || eCall==STAT_GET_NLT || eCall==STAT_GET_NDLT ); - - if( eCall==STAT_GET_STAT1 ){ + if( eCall==STAT_GET_STAT1 ) +#else + assert( argc==1 ); +#endif + { /* Return the value to store in the "stat" column of the sqlite_stat1 ** table for this index. ** ** The value is a string composed of a list of integers describing ** the index. The first integer in the list is the total number of @@ -687,11 +701,13 @@ assert( p->current.anEq[i] ); } assert( z[0]=='\0' && z>zRet ); sqlite3_result_text(context, zRet, -1, sqlite3_free); - }else if( eCall==STAT_GET_ROWID ){ + } +#ifdef SQLITE_ENABLE_STAT34 + else if( eCall==STAT_GET_ROWID ){ if( p->iGet<0 ){ samplePushPrevious(p, 0); p->iGet = 0; } if( p->iGetnSample ){ @@ -728,10 +744,11 @@ z[-1] = '\0'; sqlite3_result_text(context, zRet, -1, sqlite3_free); } } } +#endif /* SQLITE_ENABLE_STAT34 */ } static const FuncDef statGetFuncdef = { 2, /* nArg */ SQLITE_UTF8, /* iPrefEnc */ 0, /* flags */ @@ -745,14 +762,18 @@ 0 /* pDestructor */ }; static void callStatGet(Vdbe *v, int regStat4, int iParam, int regOut){ assert( regOut!=regStat4 && regOut!=regStat4+1 ); +#ifdef SQLITE_ENABLE_STAT34 sqlite3VdbeAddOp2(v, OP_Integer, iParam, regStat4+1); +#else + assert( iParam==STAT_GET_STAT1 ); +#endif sqlite3VdbeAddOp3(v, OP_Function, 0, regStat4, regOut); sqlite3VdbeChangeP4(v, -1, (char*)&statGetFuncdef, P4_FUNCDEF); - sqlite3VdbeChangeP5(v, 2); + sqlite3VdbeChangeP5(v, 1 + IsStat3 + IsStat4); } /* ** Generate code to do an analysis of all indices associated with ** a single table. @@ -1246,11 +1267,11 @@ /* ** If the Index.aSample variable is not NULL, delete the aSample[] array ** and its contents. */ void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){ -#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3) +#ifdef SQLITE_ENABLE_STAT34 if( pIdx->aSample ){ int j; for(j=0; jnSample; j++){ IndexSample *p = &pIdx->aSample[j]; sqlite3DbFree(db, p->p); @@ -1262,15 +1283,14 @@ pIdx->aSample = 0; } #else UNUSED_PARAMETER(db); UNUSED_PARAMETER(pIdx); -#endif +#endif /* SQLITE_ENABLE_STAT34 */ } -#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3) - +#ifdef SQLITE_ENABLE_STAT34 /* ** Populate the pIdx->aAvgEq[] array based on the samples currently ** stored in pIdx->aSample[]. */ static void initAvgEq(Index *pIdx){ @@ -1460,24 +1480,24 @@ ); } return rc; } -#endif /* SQLITE_ENABLE_STAT4 */ +#endif /* SQLITE_ENABLE_STAT34 */ /* -** Load the content of the sqlite_stat1 and sqlite_stat4 tables. The +** Load the content of the sqlite_stat1 and sqlite_stat3/4 tables. The ** contents of sqlite_stat1 are used to populate the Index.aiRowEst[] -** arrays. The contents of sqlite_stat4 are used to populate the +** arrays. The contents of sqlite_stat3/4 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_STAT4 was defined -** during compilation and the sqlite_stat4 table is present, no data is +** is returned. In this case, even if SQLITE_ENABLE_STAT3/4 was defined +** during compilation and the sqlite_stat3/4 table is present, no data is ** read from it. ** -** If SQLITE_ENABLE_STAT4 was defined during compilation and the +** If SQLITE_ENABLE_STAT3/4 was defined during compilation and the ** sqlite_stat4 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. @@ -1496,11 +1516,11 @@ /* Clear any prior statistics */ assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){ Index *pIdx = sqliteHashData(i); sqlite3DefaultRowEst(pIdx); -#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3) +#ifdef SQLITE_ENABLE_STAT34 sqlite3DeleteIndexSamples(db, pIdx); pIdx->aSample = 0; #endif } @@ -1521,11 +1541,11 @@ sqlite3DbFree(db, zSql); } /* Load the statistics from the sqlite_stat4 table. */ -#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3) +#ifdef SQLITE_ENABLE_STAT34 if( rc==SQLITE_OK ){ int lookasideEnabled = db->lookaside.bEnabled; db->lookaside.bEnabled = 0; rc = loadStat4(db, sInfo.zDatabase); db->lookaside.bEnabled = lookasideEnabled;