| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | #ifndef SQLITE_OMIT_ANALYZE |
| | #include "sqliteInt.h" |
| |
|
| | #if defined(SQLITE_ENABLE_STAT4) |
| | # define IsStat4 1 |
| | #else |
| | # define IsStat4 0 |
| | # undef SQLITE_STAT4_SAMPLES |
| | # define SQLITE_STAT4_SAMPLES 1 |
| | #endif |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static void openStatTable( |
| | Parse *pParse, |
| | int iDb, |
| | int iStatCur, |
| | const char *zWhere, |
| | const char *zWhereType |
| | ){ |
| | static const struct { |
| | const char *zName; |
| | const char *zCols; |
| | } aTable[] = { |
| | { "sqlite_stat1", "tbl,idx,stat" }, |
| | #if defined(SQLITE_ENABLE_STAT4) |
| | { "sqlite_stat4", "tbl,idx,neq,nlt,ndlt,sample" }, |
| | #else |
| | { "sqlite_stat4", 0 }, |
| | #endif |
| | { "sqlite_stat3", 0 }, |
| | }; |
| | int i; |
| | sqlite3 *db = pParse->db; |
| | Db *pDb; |
| | Vdbe *v = sqlite3GetVdbe(pParse); |
| | u32 aRoot[ArraySize(aTable)]; |
| | u8 aCreateTbl[ArraySize(aTable)]; |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | const int nToOpen = OptimizationEnabled(db,SQLITE_Stat4) ? 2 : 1; |
| | #else |
| | const int nToOpen = 1; |
| | #endif |
| |
|
| | if( v==0 ) return; |
| | assert( sqlite3BtreeHoldsAllMutexes(db) ); |
| | assert( sqlite3VdbeDb(v)==db ); |
| | pDb = &db->aDb[iDb]; |
| |
|
| | |
| | |
| | |
| | for(i=0; i<ArraySize(aTable); i++){ |
| | const char *zTab = aTable[i].zName; |
| | Table *pStat; |
| | aCreateTbl[i] = 0; |
| | if( (pStat = sqlite3FindTable(db, zTab, pDb->zDbSName))==0 ){ |
| | if( i<nToOpen ){ |
| | |
| | |
| | |
| | |
| | sqlite3NestedParse(pParse, |
| | "CREATE TABLE %Q.%s(%s)", pDb->zDbSName, zTab, aTable[i].zCols |
| | ); |
| | assert( pParse->isCreate || pParse->nErr ); |
| | aRoot[i] = (u32)pParse->u1.cr.regRoot; |
| | aCreateTbl[i] = OPFLAG_P2ISREG; |
| | } |
| | }else{ |
| | |
| | |
| | |
| | aRoot[i] = pStat->tnum; |
| | sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab); |
| | if( zWhere ){ |
| | sqlite3NestedParse(pParse, |
| | "DELETE FROM %Q.%s WHERE %s=%Q", |
| | pDb->zDbSName, zTab, zWhereType, zWhere |
| | ); |
| | #ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
| | }else if( db->xPreUpdateCallback ){ |
| | sqlite3NestedParse(pParse, "DELETE FROM %Q.%s", pDb->zDbSName, zTab); |
| | #endif |
| | }else{ |
| | |
| | sqlite3VdbeAddOp2(v, OP_Clear, (int)aRoot[i], iDb); |
| | } |
| | } |
| | } |
| |
|
| | |
| | for(i=0; i<nToOpen; i++){ |
| | assert( i<ArraySize(aTable) ); |
| | sqlite3VdbeAddOp4Int(v, OP_OpenWrite, iStatCur+i, (int)aRoot[i], iDb, 3); |
| | sqlite3VdbeChangeP5(v, aCreateTbl[i]); |
| | VdbeComment((v, aTable[i].zName)); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | #ifndef SQLITE_STAT4_SAMPLES |
| | # define SQLITE_STAT4_SAMPLES 24 |
| | #endif |
| |
|
| | |
| | |
| | |
| | |
| | |
| | typedef struct StatAccum StatAccum; |
| | typedef struct StatSample StatSample; |
| | struct StatSample { |
| | tRowcnt *anDLt; |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | tRowcnt *anEq; |
| | tRowcnt *anLt; |
| | union { |
| | i64 iRowid; |
| | u8 *aRowid; |
| | } u; |
| | u32 nRowid; |
| | u8 isPSample; |
| | int iCol; |
| | u32 iHash; |
| | #endif |
| | }; |
| | struct StatAccum { |
| | sqlite3 *db; |
| | tRowcnt nEst; |
| | tRowcnt nRow; |
| | int nLimit; |
| | int nCol; |
| | int nKeyCol; |
| | u8 nSkipAhead; |
| | StatSample current; |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | tRowcnt nPSample; |
| | int mxSample; |
| | u32 iPrn; |
| | StatSample *aBest; |
| | int iMin; |
| | int nSample; |
| | int nMaxEqZero; |
| | int iGet; |
| | StatSample *a; |
| | #endif |
| | }; |
| |
|
| | |
| | |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | static void sampleClear(sqlite3 *db, StatSample *p){ |
| | assert( db!=0 ); |
| | if( p->nRowid ){ |
| | sqlite3DbFree(db, p->u.aRowid); |
| | p->nRowid = 0; |
| | } |
| | } |
| | #endif |
| |
|
| | |
| | |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | static void sampleSetRowid(sqlite3 *db, StatSample *p, int n, const u8 *pData){ |
| | assert( db!=0 ); |
| | if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid); |
| | p->u.aRowid = sqlite3DbMallocRawNN(db, n); |
| | if( p->u.aRowid ){ |
| | p->nRowid = n; |
| | memcpy(p->u.aRowid, pData, n); |
| | }else{ |
| | p->nRowid = 0; |
| | } |
| | } |
| | #endif |
| |
|
| | |
| | |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | static void sampleSetRowidInt64(sqlite3 *db, StatSample *p, i64 iRowid){ |
| | assert( db!=0 ); |
| | if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid); |
| | p->nRowid = 0; |
| | p->u.iRowid = iRowid; |
| | } |
| | #endif |
| |
|
| |
|
| | |
| | |
| | |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | static void sampleCopy(StatAccum *p, StatSample *pTo, StatSample *pFrom){ |
| | pTo->isPSample = pFrom->isPSample; |
| | pTo->iCol = pFrom->iCol; |
| | pTo->iHash = pFrom->iHash; |
| | memcpy(pTo->anEq, pFrom->anEq, sizeof(tRowcnt)*p->nCol); |
| | memcpy(pTo->anLt, pFrom->anLt, sizeof(tRowcnt)*p->nCol); |
| | memcpy(pTo->anDLt, pFrom->anDLt, sizeof(tRowcnt)*p->nCol); |
| | if( pFrom->nRowid ){ |
| | sampleSetRowid(p->db, pTo, pFrom->nRowid, pFrom->u.aRowid); |
| | }else{ |
| | sampleSetRowidInt64(p->db, pTo, pFrom->u.iRowid); |
| | } |
| | } |
| | #endif |
| |
|
| | |
| | |
| | |
| | static void statAccumDestructor(void *pOld){ |
| | StatAccum *p = (StatAccum*)pOld; |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | if( p->mxSample ){ |
| | int i; |
| | for(i=0; i<p->nCol; i++) sampleClear(p->db, p->aBest+i); |
| | for(i=0; i<p->mxSample; i++) sampleClear(p->db, p->a+i); |
| | sampleClear(p->db, &p->current); |
| | } |
| | #endif |
| | sqlite3DbFree(p->db, p); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static void statInit( |
| | sqlite3_context *context, |
| | int argc, |
| | sqlite3_value **argv |
| | ){ |
| | StatAccum *p; |
| | int nCol; |
| | int nKeyCol; |
| | int nColUp; |
| | i64 n; |
| | sqlite3 *db = sqlite3_context_db_handle(context); |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | |
| | int mxSample = OptimizationEnabled(db,SQLITE_Stat4) ?SQLITE_STAT4_SAMPLES :0; |
| | #endif |
| |
|
| | |
| | UNUSED_PARAMETER(argc); |
| | nCol = sqlite3_value_int(argv[0]); |
| | assert( nCol>0 ); |
| | nColUp = sizeof(tRowcnt)<8 ? (nCol+1)&~1 : nCol; |
| | nKeyCol = sqlite3_value_int(argv[1]); |
| | assert( nKeyCol<=nCol ); |
| | assert( nKeyCol>0 ); |
| |
|
| | |
| | n = sizeof(*p) |
| | + sizeof(tRowcnt)*nColUp; |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | n += sizeof(tRowcnt)*nColUp; |
| | if( mxSample ){ |
| | n += sizeof(tRowcnt)*nColUp |
| | + sizeof(StatSample)*(nCol+mxSample) |
| | + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample); |
| | } |
| | #endif |
| | p = sqlite3DbMallocZero(db, n); |
| | if( p==0 ){ |
| | sqlite3_result_error_nomem(context); |
| | return; |
| | } |
| |
|
| | p->db = db; |
| | p->nEst = sqlite3_value_int64(argv[2]); |
| | p->nRow = 0; |
| | p->nLimit = sqlite3_value_int(argv[3]); |
| | p->nCol = nCol; |
| | p->nKeyCol = nKeyCol; |
| | p->nSkipAhead = 0; |
| | p->current.anDLt = (tRowcnt*)&p[1]; |
| |
|
| | #ifdef SQLITE_ENABLE_STAT4 |
| | p->current.anEq = &p->current.anDLt[nColUp]; |
| | p->mxSample = p->nLimit==0 ? mxSample : 0; |
| | if( mxSample ){ |
| | u8 *pSpace; |
| | int i; |
| |
|
| | p->iGet = -1; |
| | p->nPSample = (tRowcnt)(p->nEst/(mxSample/3+1) + 1); |
| | p->current.anLt = &p->current.anEq[nColUp]; |
| | p->iPrn = 0x689e962d*(u32)nCol ^ 0xd0944565*(u32)sqlite3_value_int(argv[2]); |
| | |
| | |
| | p->a = (struct StatSample*)&p->current.anLt[nColUp]; |
| | p->aBest = &p->a[mxSample]; |
| | pSpace = (u8*)(&p->a[mxSample+nCol]); |
| | for(i=0; i<(mxSample+nCol); i++){ |
| | p->a[i].anEq = (tRowcnt *)pSpace; pSpace += (sizeof(tRowcnt) * nColUp); |
| | p->a[i].anLt = (tRowcnt *)pSpace; pSpace += (sizeof(tRowcnt) * nColUp); |
| | p->a[i].anDLt = (tRowcnt *)pSpace; pSpace += (sizeof(tRowcnt) * nColUp); |
| | } |
| | assert( (pSpace - (u8*)p)==n ); |
| | |
| | for(i=0; i<nCol; i++){ |
| | p->aBest[i].iCol = i; |
| | } |
| | } |
| | #endif |
| |
|
| | |
| | |
| | |
| | |
| | sqlite3_result_blob(context, p, sizeof(*p), statAccumDestructor); |
| | } |
| | static const FuncDef statInitFuncdef = { |
| | 4, |
| | SQLITE_UTF8, |
| | 0, |
| | 0, |
| | statInit, |
| | 0, |
| | 0, 0, |
| | "stat_init", |
| | {0} |
| | }; |
| |
|
| | #ifdef SQLITE_ENABLE_STAT4 |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static int sampleIsBetterPost( |
| | StatAccum *pAccum, |
| | StatSample *pNew, |
| | StatSample *pOld |
| | ){ |
| | int nCol = pAccum->nCol; |
| | int i; |
| | assert( pNew->iCol==pOld->iCol ); |
| | for(i=pNew->iCol+1; i<nCol; i++){ |
| | if( pNew->anEq[i]>pOld->anEq[i] ) return 1; |
| | if( pNew->anEq[i]<pOld->anEq[i] ) return 0; |
| | } |
| | if( pNew->iHash>pOld->iHash ) return 1; |
| | return 0; |
| | } |
| | #endif |
| |
|
| | #ifdef SQLITE_ENABLE_STAT4 |
| | |
| | |
| | |
| | |
| | |
| | |
| | static int sampleIsBetter( |
| | StatAccum *pAccum, |
| | StatSample *pNew, |
| | StatSample *pOld |
| | ){ |
| | tRowcnt nEqNew = pNew->anEq[pNew->iCol]; |
| | tRowcnt nEqOld = pOld->anEq[pOld->iCol]; |
| |
|
| | assert( pOld->isPSample==0 && pNew->isPSample==0 ); |
| | assert( IsStat4 || (pNew->iCol==0 && pOld->iCol==0) ); |
| |
|
| | if( (nEqNew>nEqOld) ) return 1; |
| | if( nEqNew==nEqOld ){ |
| | if( pNew->iCol<pOld->iCol ) return 1; |
| | return (pNew->iCol==pOld->iCol && sampleIsBetterPost(pAccum, pNew, pOld)); |
| | } |
| | return 0; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | static void sampleInsert(StatAccum *p, StatSample *pNew, int nEqZero){ |
| | StatSample *pSample = 0; |
| | int i; |
| |
|
| | assert( IsStat4 || nEqZero==0 ); |
| |
|
| | |
| | |
| | |
| | |
| | if( nEqZero>p->nMaxEqZero ){ |
| | p->nMaxEqZero = nEqZero; |
| | } |
| | if( pNew->isPSample==0 ){ |
| | StatSample *pUpgrade = 0; |
| | assert( pNew->anEq[pNew->iCol]>0 ); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | for(i=p->nSample-1; i>=0; i--){ |
| | StatSample *pOld = &p->a[i]; |
| | if( pOld->anEq[pNew->iCol]==0 ){ |
| | if( pOld->isPSample ) return; |
| | assert( pOld->iCol>pNew->iCol ); |
| | assert( sampleIsBetter(p, pNew, pOld) ); |
| | if( pUpgrade==0 || sampleIsBetter(p, pOld, pUpgrade) ){ |
| | pUpgrade = pOld; |
| | } |
| | } |
| | } |
| | if( pUpgrade ){ |
| | pUpgrade->iCol = pNew->iCol; |
| | pUpgrade->anEq[pUpgrade->iCol] = pNew->anEq[pUpgrade->iCol]; |
| | goto find_new_min; |
| | } |
| | } |
| |
|
| | |
| | if( p->nSample>=p->mxSample ){ |
| | StatSample *pMin = &p->a[p->iMin]; |
| | tRowcnt *anEq = pMin->anEq; |
| | tRowcnt *anLt = pMin->anLt; |
| | tRowcnt *anDLt = pMin->anDLt; |
| | sampleClear(p->db, pMin); |
| | memmove(pMin, &pMin[1], sizeof(p->a[0])*(p->nSample-p->iMin-1)); |
| | pSample = &p->a[p->nSample-1]; |
| | pSample->nRowid = 0; |
| | pSample->anEq = anEq; |
| | pSample->anDLt = anDLt; |
| | pSample->anLt = anLt; |
| | p->nSample = p->mxSample-1; |
| | } |
| |
|
| | |
| | |
| | |
| | assert( p->nSample==0 |
| | || pNew->anLt[p->nCol-1] > p->a[p->nSample-1].anLt[p->nCol-1] ); |
| |
|
| | |
| | pSample = &p->a[p->nSample]; |
| | sampleCopy(p, pSample, pNew); |
| | p->nSample++; |
| |
|
| | |
| | memset(pSample->anEq, 0, sizeof(tRowcnt)*nEqZero); |
| |
|
| | find_new_min: |
| | if( p->nSample>=p->mxSample ){ |
| | int iMin = -1; |
| | for(i=0; i<p->mxSample; i++){ |
| | if( p->a[i].isPSample ) continue; |
| | if( iMin<0 || sampleIsBetter(p, &p->a[iMin], &p->a[i]) ){ |
| | iMin = i; |
| | } |
| | } |
| | assert( iMin>=0 ); |
| | p->iMin = iMin; |
| | } |
| | } |
| | #endif |
| |
|
| | #ifdef SQLITE_ENABLE_STAT4 |
| | |
| | |
| | |
| | |
| | |
| | |
| | static void samplePushPrevious(StatAccum *p, int iChng){ |
| | int i; |
| |
|
| | |
| | |
| | for(i=(p->nCol-2); i>=iChng; i--){ |
| | StatSample *pBest = &p->aBest[i]; |
| | pBest->anEq[i] = p->current.anEq[i]; |
| | if( p->nSample<p->mxSample || sampleIsBetter(p, pBest, &p->a[p->iMin]) ){ |
| | sampleInsert(p, pBest, i); |
| | } |
| | } |
| |
|
| | |
| | |
| | for(i=p->nSample-1; i>=0; i--){ |
| | int j; |
| | for(j=p->nMaxEqZero; j<p->nCol; j++) assert( p->a[i].anEq[j]>0 ); |
| | } |
| |
|
| | |
| | if( iChng<p->nMaxEqZero ){ |
| | for(i=p->nSample-1; i>=0; i--){ |
| | int j; |
| | for(j=iChng; j<p->nCol; j++){ |
| | if( p->a[i].anEq[j]==0 ) p->a[i].anEq[j] = p->current.anEq[j]; |
| | } |
| | } |
| | p->nMaxEqZero = iChng; |
| | } |
| | } |
| | #endif |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static void statPush( |
| | sqlite3_context *context, |
| | int argc, |
| | sqlite3_value **argv |
| | ){ |
| | int i; |
| |
|
| | |
| | StatAccum *p = (StatAccum*)sqlite3_value_blob(argv[0]); |
| | int iChng = sqlite3_value_int(argv[1]); |
| |
|
| | UNUSED_PARAMETER( argc ); |
| | UNUSED_PARAMETER( context ); |
| | assert( p->nCol>0 ); |
| | assert( iChng<p->nCol ); |
| |
|
| | if( p->nRow==0 ){ |
| | |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | for(i=0; i<p->nCol; i++) p->current.anEq[i] = 1; |
| | #endif |
| | }else{ |
| | |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | if( p->mxSample ) samplePushPrevious(p, iChng); |
| | #endif |
| |
|
| | |
| | |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | for(i=0; i<iChng; i++){ |
| | p->current.anEq[i]++; |
| | } |
| | #endif |
| | for(i=iChng; i<p->nCol; i++){ |
| | p->current.anDLt[i]++; |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | if( p->mxSample ) p->current.anLt[i] += p->current.anEq[i]; |
| | p->current.anEq[i] = 1; |
| | #endif |
| | } |
| | } |
| |
|
| | p->nRow++; |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | if( p->mxSample ){ |
| | tRowcnt nLt; |
| | if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){ |
| | sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2])); |
| | }else{ |
| | sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]), |
| | sqlite3_value_blob(argv[2])); |
| | } |
| | p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345; |
| |
|
| | nLt = p->current.anLt[p->nCol-1]; |
| | |
| | if( (nLt/p->nPSample)!=(nLt+1)/p->nPSample ){ |
| | p->current.isPSample = 1; |
| | p->current.iCol = 0; |
| | sampleInsert(p, &p->current, p->nCol-1); |
| | p->current.isPSample = 0; |
| | } |
| |
|
| | |
| | for(i=0; i<(p->nCol-1); i++){ |
| | p->current.iCol = i; |
| | if( i>=iChng || sampleIsBetterPost(p, &p->current, &p->aBest[i]) ){ |
| | sampleCopy(p, &p->aBest[i], &p->current); |
| | } |
| | } |
| | }else |
| | #endif |
| | if( p->nLimit && p->nRow>(tRowcnt)p->nLimit*(p->nSkipAhead+1) ){ |
| | p->nSkipAhead++; |
| | sqlite3_result_int(context, p->current.anDLt[0]>0); |
| | } |
| | } |
| |
|
| | static const FuncDef statPushFuncdef = { |
| | 2+IsStat4, |
| | SQLITE_UTF8, |
| | 0, |
| | 0, |
| | statPush, |
| | 0, |
| | 0, 0, |
| | "stat_push", |
| | {0} |
| | }; |
| |
|
| | #define STAT_GET_STAT1 0 |
| | #define STAT_GET_ROWID 1 |
| | #define STAT_GET_NEQ 2 |
| | #define STAT_GET_NLT 3 |
| | #define STAT_GET_NDLT 4 |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static void statGet( |
| | sqlite3_context *context, |
| | int argc, |
| | sqlite3_value **argv |
| | ){ |
| | StatAccum *p = (StatAccum*)sqlite3_value_blob(argv[0]); |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | |
| | 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 |
| | ); |
| | assert( eCall==STAT_GET_STAT1 || p->mxSample ); |
| | if( eCall==STAT_GET_STAT1 ) |
| | #else |
| | assert( argc==1 ); |
| | #endif |
| | { |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | sqlite3_str sStat; |
| | int i; |
| |
|
| | sqlite3StrAccumInit(&sStat, 0, 0, 0, (p->nKeyCol+1)*100); |
| | sqlite3_str_appendf(&sStat, "%llu", |
| | p->nSkipAhead ? (u64)p->nEst : (u64)p->nRow); |
| | for(i=0; i<p->nKeyCol; i++){ |
| | u64 nDistinct = p->current.anDLt[i] + 1; |
| | u64 iVal = (p->nRow + nDistinct - 1) / nDistinct; |
| | if( iVal==2 && p->nRow*10 <= nDistinct*11 ) iVal = 1; |
| | sqlite3_str_appendf(&sStat, " %llu", iVal); |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | assert( p->current.anEq[i] || p->nRow==0 ); |
| | #endif |
| | } |
| | sqlite3ResultStrAccum(context, &sStat); |
| | } |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | else if( eCall==STAT_GET_ROWID ){ |
| | if( p->iGet<0 ){ |
| | samplePushPrevious(p, 0); |
| | p->iGet = 0; |
| | } |
| | if( p->iGet<p->nSample ){ |
| | StatSample *pS = p->a + p->iGet; |
| | if( pS->nRowid==0 ){ |
| | sqlite3_result_int64(context, pS->u.iRowid); |
| | }else{ |
| | sqlite3_result_blob(context, pS->u.aRowid, pS->nRowid, |
| | SQLITE_TRANSIENT); |
| | } |
| | } |
| | }else{ |
| | tRowcnt *aCnt = 0; |
| | sqlite3_str sStat; |
| | int i; |
| |
|
| | assert( p->iGet<p->nSample ); |
| | switch( eCall ){ |
| | case STAT_GET_NEQ: aCnt = p->a[p->iGet].anEq; break; |
| | case STAT_GET_NLT: aCnt = p->a[p->iGet].anLt; break; |
| | default: { |
| | aCnt = p->a[p->iGet].anDLt; |
| | p->iGet++; |
| | break; |
| | } |
| | } |
| | sqlite3StrAccumInit(&sStat, 0, 0, 0, p->nCol*100); |
| | for(i=0; i<p->nCol; i++){ |
| | sqlite3_str_appendf(&sStat, "%llu ", (u64)aCnt[i]); |
| | } |
| | if( sStat.nChar ) sStat.nChar--; |
| | sqlite3ResultStrAccum(context, &sStat); |
| | } |
| | #endif |
| | #ifndef SQLITE_DEBUG |
| | UNUSED_PARAMETER( argc ); |
| | #endif |
| | } |
| | static const FuncDef statGetFuncdef = { |
| | 1+IsStat4, |
| | SQLITE_UTF8, |
| | 0, |
| | 0, |
| | statGet, |
| | 0, |
| | 0, 0, |
| | "stat_get", |
| | {0} |
| | }; |
| |
|
| | static void callStatGet(Parse *pParse, int regStat, int iParam, int regOut){ |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | sqlite3VdbeAddOp2(pParse->pVdbe, OP_Integer, iParam, regStat+1); |
| | #elif SQLITE_DEBUG |
| | assert( iParam==STAT_GET_STAT1 ); |
| | #else |
| | UNUSED_PARAMETER( iParam ); |
| | #endif |
| | assert( regOut!=regStat && regOut!=regStat+1 ); |
| | sqlite3VdbeAddFunctionCall(pParse, 0, regStat, regOut, 1+IsStat4, |
| | &statGetFuncdef, 0); |
| | } |
| |
|
| | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| | |
| | |
| | |
| | static void analyzeVdbeCommentIndexWithColumnName( |
| | Vdbe *v, |
| | Index *pIdx, |
| | int k |
| | ){ |
| | int i; |
| | assert( k>=0 && k<pIdx->nColumn ); |
| | i = pIdx->aiColumn[k]; |
| | if( NEVER(i==XN_ROWID) ){ |
| | VdbeComment((v,"%s.rowid",pIdx->zName)); |
| | }else if( i==XN_EXPR ){ |
| | assert( pIdx->bHasExpr ); |
| | VdbeComment((v,"%s.expr(%d)",pIdx->zName, k)); |
| | }else{ |
| | VdbeComment((v,"%s.%s", pIdx->zName, pIdx->pTable->aCol[i].zCnName)); |
| | } |
| | } |
| | #else |
| | # define analyzeVdbeCommentIndexWithColumnName(a,b,c) |
| | #endif |
| |
|
| | |
| | |
| | |
| | |
| | static void analyzeOneTable( |
| | Parse *pParse, |
| | Table *pTab, |
| | Index *pOnlyIdx, |
| | int iStatCur, |
| | int iMem, |
| | int iTab |
| | ){ |
| | sqlite3 *db = pParse->db; |
| | Index *pIdx; |
| | int iIdxCur; |
| | int iTabCur; |
| | Vdbe *v; |
| | int i; |
| | int jZeroRows = -1; |
| | int iDb; |
| | u8 needTableCnt = 1; |
| | int regNewRowid = iMem++; |
| | int regStat = iMem++; |
| | int regChng = iMem++; |
| | int regRowid = iMem++; |
| | int regTemp = iMem++; |
| | int regTemp2 = iMem++; |
| | int regTabname = iMem++; |
| | int regIdxname = iMem++; |
| | int regStat1 = iMem++; |
| | int regPrev = iMem; |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | int doOnce = 1; |
| | #endif |
| | #ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
| | Table *pStat1 = 0; |
| | #endif |
| |
|
| | sqlite3TouchRegister(pParse, iMem); |
| | assert( sqlite3NoTempsInRange(pParse, regNewRowid, iMem) ); |
| | v = sqlite3GetVdbe(pParse); |
| | if( v==0 || NEVER(pTab==0) ){ |
| | return; |
| | } |
| | if( !IsOrdinaryTable(pTab) ){ |
| | |
| | return; |
| | } |
| | if( sqlite3_strlike("sqlite\\_%", pTab->zName, '\\')==0 ){ |
| | |
| | return; |
| | } |
| | assert( sqlite3BtreeHoldsAllMutexes(db) ); |
| | iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| | assert( iDb>=0 ); |
| | assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); |
| | #ifndef SQLITE_OMIT_AUTHORIZATION |
| | if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0, |
| | db->aDb[iDb].zDbSName ) ){ |
| | return; |
| | } |
| | #endif |
| |
|
| | #ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
| | if( db->xPreUpdateCallback ){ |
| | pStat1 = (Table*)sqlite3DbMallocZero(db, sizeof(Table) + 13); |
| | if( pStat1==0 ) return; |
| | pStat1->zName = (char*)&pStat1[1]; |
| | memcpy(pStat1->zName, "sqlite_stat1", 13); |
| | pStat1->nCol = 3; |
| | pStat1->iPKey = -1; |
| | sqlite3VdbeAddOp4(pParse->pVdbe, OP_Noop, 0, 0, 0,(char*)pStat1,P4_DYNAMIC); |
| | } |
| | #endif |
| |
|
| | |
| | |
| | |
| | |
| | sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); |
| | iTabCur = iTab++; |
| | iIdxCur = iTab++; |
| | pParse->nTab = MAX(pParse->nTab, iTab); |
| | sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead); |
| | sqlite3VdbeLoadString(v, regTabname, pTab->zName); |
| |
|
| | for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ |
| | int nCol; |
| | int addrGotoEnd; |
| | int addrNextRow; |
| | const char *zIdxName; |
| | int nColTest; |
| |
|
| | if( pOnlyIdx && pOnlyIdx!=pIdx ) continue; |
| | if( pIdx->pPartIdxWhere==0 ) needTableCnt = 0; |
| | if( !HasRowid(pTab) && IsPrimaryKeyIndex(pIdx) ){ |
| | nCol = pIdx->nKeyCol; |
| | zIdxName = pTab->zName; |
| | nColTest = nCol - 1; |
| | }else{ |
| | nCol = pIdx->nColumn; |
| | zIdxName = pIdx->zName; |
| | nColTest = pIdx->uniqNotNull ? pIdx->nKeyCol-1 : nCol-1; |
| | } |
| |
|
| | |
| | sqlite3VdbeLoadString(v, regIdxname, zIdxName); |
| | VdbeComment((v, "Analysis for %s.%s", pTab->zName, zIdxName)); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | |
| | |
| | |
| | sqlite3TouchRegister(pParse, regPrev+nColTest); |
| |
|
| | |
| | assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) ); |
| | sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb); |
| | sqlite3VdbeSetP4KeyInfo(pParse, pIdx); |
| | VdbeComment((v, "%s", pIdx->zName)); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | assert( regTemp2==regStat+4 ); |
| | sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat+1); |
| | assert( regRowid==regStat+2 ); |
| | sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regRowid); |
| | sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, |
| | OptimizationDisabled(db, SQLITE_Stat4)); |
| | sqlite3VdbeAddFunctionCall(pParse, 0, regStat+1, regStat, 4, |
| | &statInitFuncdef, 0); |
| | addrGotoEnd = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); |
| | VdbeCoverage(v); |
| |
|
| | sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng); |
| | addrNextRow = sqlite3VdbeCurrentAddr(v); |
| |
|
| | if( nColTest>0 ){ |
| | int endDistinctTest = sqlite3VdbeMakeLabel(pParse); |
| | int *aGotoChng; |
| | aGotoChng = sqlite3DbMallocRawNN(db, sizeof(int)*nColTest); |
| | if( aGotoChng==0 ) continue; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | sqlite3VdbeAddOp0(v, OP_Goto); |
| | addrNextRow = sqlite3VdbeCurrentAddr(v); |
| | if( nColTest==1 && pIdx->nKeyCol==1 && IsUniqueIndex(pIdx) ){ |
| | |
| | |
| | |
| | sqlite3VdbeAddOp2(v, OP_NotNull, regPrev, endDistinctTest); |
| | VdbeCoverage(v); |
| | } |
| | for(i=0; i<nColTest; i++){ |
| | char *pColl = (char*)sqlite3LocateCollSeq(pParse, pIdx->azColl[i]); |
| | sqlite3VdbeAddOp2(v, OP_Integer, i, regChng); |
| | sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regTemp); |
| | analyzeVdbeCommentIndexWithColumnName(v,pIdx,i); |
| | aGotoChng[i] = |
| | sqlite3VdbeAddOp4(v, OP_Ne, regTemp, 0, regPrev+i, pColl, P4_COLLSEQ); |
| | sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); |
| | VdbeCoverage(v); |
| | } |
| | sqlite3VdbeAddOp2(v, OP_Integer, nColTest, regChng); |
| | sqlite3VdbeGoto(v, endDistinctTest); |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | sqlite3VdbeJumpHere(v, addrNextRow-1); |
| | for(i=0; i<nColTest; i++){ |
| | sqlite3VdbeJumpHere(v, aGotoChng[i]); |
| | sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regPrev+i); |
| | analyzeVdbeCommentIndexWithColumnName(v,pIdx,i); |
| | } |
| | sqlite3VdbeResolveLabel(v, endDistinctTest); |
| | sqlite3DbFree(db, aGotoChng); |
| | } |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | if( OptimizationEnabled(db, SQLITE_Stat4) ){ |
| | assert( regRowid==(regStat+2) ); |
| | if( HasRowid(pTab) ){ |
| | sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid); |
| | }else{ |
| | Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable); |
| | int j, k, regKey; |
| | regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol); |
| | for(j=0; j<pPk->nKeyCol; j++){ |
| | k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[j]); |
| | assert( k>=0 && k<pIdx->nColumn ); |
| | sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j); |
| | analyzeVdbeCommentIndexWithColumnName(v,pIdx,k); |
| | } |
| | sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid); |
| | sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol); |
| | } |
| | } |
| | #endif |
| | assert( regChng==(regStat+1) ); |
| | { |
| | sqlite3VdbeAddFunctionCall(pParse, 1, regStat, regTemp, 2+IsStat4, |
| | &statPushFuncdef, 0); |
| | if( db->nAnalysisLimit ){ |
| | int j1, j2, j3; |
| | j1 = sqlite3VdbeAddOp1(v, OP_IsNull, regTemp); VdbeCoverage(v); |
| | j2 = sqlite3VdbeAddOp1(v, OP_If, regTemp); VdbeCoverage(v); |
| | j3 = sqlite3VdbeAddOp4Int(v, OP_SeekGT, iIdxCur, 0, regPrev, 1); |
| | VdbeCoverage(v); |
| | sqlite3VdbeJumpHere(v, j1); |
| | sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v); |
| | sqlite3VdbeJumpHere(v, j2); |
| | sqlite3VdbeJumpHere(v, j3); |
| | }else{ |
| | sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v); |
| | } |
| | } |
| |
|
| | |
| | if( pIdx->pPartIdxWhere ){ |
| | |
| | |
| | sqlite3VdbeJumpHere(v, addrGotoEnd); |
| | addrGotoEnd = 0; |
| | } |
| | callStatGet(pParse, regStat, STAT_GET_STAT1, regStat1); |
| | assert( "BBB"[0]==SQLITE_AFF_TEXT ); |
| | sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0); |
| | sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid); |
| | sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid); |
| | #ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
| | sqlite3VdbeChangeP4(v, -1, (char*)pStat1, P4_TABLE); |
| | #endif |
| | sqlite3VdbeChangeP5(v, OPFLAG_APPEND); |
| |
|
| | |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | if( OptimizationEnabled(db, SQLITE_Stat4) && db->nAnalysisLimit==0 ){ |
| | int regEq = regStat1; |
| | int regLt = regStat1+1; |
| | int regDLt = regStat1+2; |
| | int regSample = regStat1+3; |
| | int regCol = regStat1+4; |
| | int regSampleRowid = regCol + nCol; |
| | int addrNext; |
| | int addrIsNull; |
| | u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound; |
| |
|
| | |
| | if( addrGotoEnd==0 ){ |
| | sqlite3VdbeAddOp2(v, OP_Cast, regStat1, SQLITE_AFF_INTEGER); |
| | addrGotoEnd = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1); |
| | VdbeCoverage(v); |
| | } |
| |
|
| | if( doOnce ){ |
| | int mxCol = nCol; |
| | Index *pX; |
| |
|
| | |
| | for(pX=pTab->pIndex; pX; pX=pX->pNext){ |
| | int nColX; |
| | if( !HasRowid(pTab) && IsPrimaryKeyIndex(pX) ){ |
| | nColX = pX->nKeyCol; |
| | }else{ |
| | nColX = pX->nColumn; |
| | } |
| | if( nColX>mxCol ) mxCol = nColX; |
| | } |
| |
|
| | |
| | sqlite3TouchRegister(pParse, regCol+mxCol); |
| | doOnce = 0; |
| | #ifdef SQLITE_DEBUG |
| | |
| | |
| | |
| | |
| | testcase( !sqlite3NoTempsInRange(pParse, regEq, regCol+mxCol) ); |
| | #endif |
| | sqlite3ClearTempRegCache(pParse); |
| | assert( sqlite3NoTempsInRange(pParse, regEq, regCol+mxCol) ); |
| | } |
| | assert( sqlite3NoTempsInRange(pParse, regEq, regCol+nCol) ); |
| |
|
| | addrNext = sqlite3VdbeCurrentAddr(v); |
| | callStatGet(pParse, regStat, STAT_GET_ROWID, regSampleRowid); |
| | addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regSampleRowid); |
| | VdbeCoverage(v); |
| | callStatGet(pParse, regStat, STAT_GET_NEQ, regEq); |
| | callStatGet(pParse, regStat, STAT_GET_NLT, regLt); |
| | callStatGet(pParse, regStat, STAT_GET_NDLT, regDLt); |
| | sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0); |
| | VdbeCoverage(v); |
| | for(i=0; i<nCol; i++){ |
| | sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, i, regCol+i); |
| | } |
| | sqlite3VdbeAddOp3(v, OP_MakeRecord, regCol, nCol, regSample); |
| | sqlite3VdbeAddOp3(v, OP_MakeRecord, regTabname, 6, regTemp); |
| | sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regNewRowid); |
| | sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regTemp, regNewRowid); |
| | sqlite3VdbeAddOp2(v, OP_Goto, 1, addrNext); |
| | sqlite3VdbeJumpHere(v, addrIsNull); |
| | } |
| | #endif |
| |
|
| | |
| | if( addrGotoEnd ) sqlite3VdbeJumpHere(v, addrGotoEnd); |
| | } |
| |
|
| |
|
| | |
| | |
| | |
| | if( pOnlyIdx==0 && needTableCnt ){ |
| | VdbeComment((v, "%s", pTab->zName)); |
| | sqlite3VdbeAddOp2(v, OP_Count, iTabCur, regStat1); |
| | jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1); VdbeCoverage(v); |
| | sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname); |
| | assert( "BBB"[0]==SQLITE_AFF_TEXT ); |
| | sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0); |
| | sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid); |
| | sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid); |
| | sqlite3VdbeChangeP5(v, OPFLAG_APPEND); |
| | #ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
| | sqlite3VdbeChangeP4(v, -1, (char*)pStat1, P4_TABLE); |
| | #endif |
| | sqlite3VdbeJumpHere(v, jZeroRows); |
| | } |
| | } |
| |
|
| |
|
| | |
| | |
| | |
| | |
| | static void loadAnalysis(Parse *pParse, int iDb){ |
| | Vdbe *v = sqlite3GetVdbe(pParse); |
| | if( v ){ |
| | sqlite3VdbeAddOp1(v, OP_LoadAnalysis, iDb); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | static void analyzeDatabase(Parse *pParse, int iDb){ |
| | sqlite3 *db = pParse->db; |
| | Schema *pSchema = db->aDb[iDb].pSchema; |
| | HashElem *k; |
| | int iStatCur; |
| | int iMem; |
| | int iTab; |
| |
|
| | sqlite3BeginWriteOperation(pParse, 0, iDb); |
| | iStatCur = pParse->nTab; |
| | pParse->nTab += 3; |
| | openStatTable(pParse, iDb, iStatCur, 0, 0); |
| | iMem = pParse->nMem+1; |
| | iTab = pParse->nTab; |
| | assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); |
| | for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ |
| | Table *pTab = (Table*)sqliteHashData(k); |
| | analyzeOneTable(pParse, pTab, 0, iStatCur, iMem, iTab); |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | iMem = sqlite3FirstAvailableRegister(pParse, iMem); |
| | #else |
| | assert( iMem==sqlite3FirstAvailableRegister(pParse,iMem) ); |
| | #endif |
| | } |
| | loadAnalysis(pParse, iDb); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | static void analyzeTable(Parse *pParse, Table *pTab, Index *pOnlyIdx){ |
| | int iDb; |
| | int iStatCur; |
| |
|
| | assert( pTab!=0 ); |
| | assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); |
| | iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); |
| | sqlite3BeginWriteOperation(pParse, 0, iDb); |
| | iStatCur = pParse->nTab; |
| | pParse->nTab += 3; |
| | if( pOnlyIdx ){ |
| | openStatTable(pParse, iDb, iStatCur, pOnlyIdx->zName, "idx"); |
| | }else{ |
| | openStatTable(pParse, iDb, iStatCur, pTab->zName, "tbl"); |
| | } |
| | analyzeOneTable(pParse, pTab, pOnlyIdx, iStatCur,pParse->nMem+1,pParse->nTab); |
| | loadAnalysis(pParse, iDb); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){ |
| | sqlite3 *db = pParse->db; |
| | int iDb; |
| | int i; |
| | char *z, *zDb; |
| | Table *pTab; |
| | Index *pIdx; |
| | Token *pTableName; |
| | Vdbe *v; |
| |
|
| | |
| | |
| | assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); |
| | if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ |
| | return; |
| | } |
| |
|
| | assert( pName2!=0 || pName1==0 ); |
| | if( pName1==0 ){ |
| | |
| | for(i=0; i<db->nDb; i++){ |
| | if( i==1 ) continue; |
| | analyzeDatabase(pParse, i); |
| | } |
| | }else if( pName2->n==0 && (iDb = sqlite3FindDb(db, pName1))>=0 ){ |
| | |
| | analyzeDatabase(pParse, iDb); |
| | }else{ |
| | |
| | iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pTableName); |
| | if( iDb>=0 ){ |
| | zDb = pName2->n ? db->aDb[iDb].zDbSName : 0; |
| | z = sqlite3NameFromToken(db, pTableName); |
| | if( z ){ |
| | if( (pIdx = sqlite3FindIndex(db, z, zDb))!=0 ){ |
| | analyzeTable(pParse, pIdx->pTable, pIdx); |
| | }else if( (pTab = sqlite3LocateTable(pParse, 0, z, zDb))!=0 ){ |
| | analyzeTable(pParse, pTab, 0); |
| | } |
| | sqlite3DbFree(db, z); |
| | } |
| | } |
| | } |
| | if( db->nSqlExec==0 && (v = sqlite3GetVdbe(pParse))!=0 ){ |
| | sqlite3VdbeAddOp0(v, OP_Expire); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | typedef struct analysisInfo analysisInfo; |
| | struct analysisInfo { |
| | sqlite3 *db; |
| | const char *zDatabase; |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | static void decodeIntArray( |
| | char *zIntArray, |
| | int nOut, |
| | tRowcnt *aOut, |
| | LogEst *aLog, |
| | Index *pIndex |
| | ){ |
| | char *z = zIntArray; |
| | int c; |
| | int i; |
| | tRowcnt v; |
| |
|
| | #ifdef SQLITE_ENABLE_STAT4 |
| | if( z==0 ) z = ""; |
| | #else |
| | assert( z!=0 ); |
| | #endif |
| | for(i=0; *z && i<nOut; i++){ |
| | v = 0; |
| | while( (c=z[0])>='0' && c<='9' ){ |
| | v = v*10 + c - '0'; |
| | z++; |
| | } |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | if( aOut ) aOut[i] = v; |
| | if( aLog ) aLog[i] = sqlite3LogEst(v); |
| | #else |
| | assert( aOut==0 ); |
| | UNUSED_PARAMETER(aOut); |
| | assert( aLog!=0 ); |
| | aLog[i] = sqlite3LogEst(v); |
| | #endif |
| | if( *z==' ' ) z++; |
| | } |
| | #ifndef SQLITE_ENABLE_STAT4 |
| | assert( pIndex!=0 ); { |
| | #else |
| | if( pIndex ){ |
| | #endif |
| | pIndex->bUnordered = 0; |
| | pIndex->noSkipScan = 0; |
| | while( z[0] ){ |
| | if( sqlite3_strglob("unordered*", z)==0 ){ |
| | pIndex->bUnordered = 1; |
| | }else if( sqlite3_strglob("sz=[0-9]*", z)==0 ){ |
| | int sz = sqlite3Atoi(z+3); |
| | if( sz<2 ) sz = 2; |
| | pIndex->szIdxRow = sqlite3LogEst(sz); |
| | }else if( sqlite3_strglob("noskipscan*", z)==0 ){ |
| | pIndex->noSkipScan = 1; |
| | } |
| | #ifdef SQLITE_ENABLE_COSTMULT |
| | else if( sqlite3_strglob("costmult=[0-9]*",z)==0 ){ |
| | pIndex->pTable->costMult = sqlite3LogEst(sqlite3Atoi(z+9)); |
| | } |
| | #endif |
| | while( z[0]!=0 && z[0]!=' ' ) z++; |
| | while( z[0]==' ' ) z++; |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){ |
| | analysisInfo *pInfo = (analysisInfo*)pData; |
| | Index *pIndex; |
| | Table *pTable; |
| | const char *z; |
| |
|
| | assert( argc==3 ); |
| | UNUSED_PARAMETER2(NotUsed, argc); |
| |
|
| | if( argv==0 || argv[0]==0 || argv[2]==0 ){ |
| | return 0; |
| | } |
| | pTable = sqlite3FindTable(pInfo->db, argv[0], pInfo->zDatabase); |
| | if( pTable==0 ){ |
| | return 0; |
| | } |
| | if( argv[1]==0 ){ |
| | pIndex = 0; |
| | }else if( sqlite3_stricmp(argv[0],argv[1])==0 ){ |
| | pIndex = sqlite3PrimaryKeyIndex(pTable); |
| | }else{ |
| | pIndex = sqlite3FindIndex(pInfo->db, argv[1], pInfo->zDatabase); |
| | } |
| | z = argv[2]; |
| |
|
| | if( pIndex ){ |
| | tRowcnt *aiRowEst = 0; |
| | int nCol = pIndex->nKeyCol+1; |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | |
| | |
| | |
| | if( pIndex->aiRowEst==0 ){ |
| | pIndex->aiRowEst = (tRowcnt*)sqlite3MallocZero(sizeof(tRowcnt) * nCol); |
| | if( pIndex->aiRowEst==0 ) sqlite3OomFault(pInfo->db); |
| | } |
| | aiRowEst = pIndex->aiRowEst; |
| | #endif |
| | pIndex->bUnordered = 0; |
| | decodeIntArray((char*)z, nCol, aiRowEst, pIndex->aiRowLogEst, pIndex); |
| | pIndex->hasStat1 = 1; |
| | if( pIndex->pPartIdxWhere==0 ){ |
| | pTable->nRowLogEst = pIndex->aiRowLogEst[0]; |
| | pTable->tabFlags |= TF_HasStat1; |
| | } |
| | }else{ |
| | Index fakeIdx; |
| | fakeIdx.szIdxRow = pTable->szTabRow; |
| | #ifdef SQLITE_ENABLE_COSTMULT |
| | fakeIdx.pTable = pTable; |
| | #endif |
| | decodeIntArray((char*)z, 1, 0, &pTable->nRowLogEst, &fakeIdx); |
| | pTable->szTabRow = fakeIdx.szIdxRow; |
| | pTable->tabFlags |= TF_HasStat1; |
| | } |
| |
|
| | return 0; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){ |
| | assert( db!=0 ); |
| | assert( pIdx!=0 ); |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | if( pIdx->aSample ){ |
| | int j; |
| | for(j=0; j<pIdx->nSample; j++){ |
| | IndexSample *p = &pIdx->aSample[j]; |
| | sqlite3DbFree(db, p->p); |
| | } |
| | sqlite3DbFree(db, pIdx->aSample); |
| | } |
| | if( db->pnBytesFreed==0 ){ |
| | pIdx->nSample = 0; |
| | pIdx->aSample = 0; |
| | } |
| | #else |
| | UNUSED_PARAMETER(db); |
| | UNUSED_PARAMETER(pIdx); |
| | #endif |
| | } |
| |
|
| | #ifdef SQLITE_ENABLE_STAT4 |
| | |
| | |
| | |
| | |
| | static void initAvgEq(Index *pIdx){ |
| | if( pIdx ){ |
| | IndexSample *aSample = pIdx->aSample; |
| | IndexSample *pFinal = &aSample[pIdx->nSample-1]; |
| | int iCol; |
| | int nCol = 1; |
| | if( pIdx->nSampleCol>1 ){ |
| | |
| | |
| | |
| | |
| | nCol = pIdx->nSampleCol-1; |
| | pIdx->aAvgEq[nCol] = 1; |
| | } |
| | for(iCol=0; iCol<nCol; iCol++){ |
| | int nSample = pIdx->nSample; |
| | int i; |
| | tRowcnt sumEq = 0; |
| | tRowcnt avgEq = 0; |
| | tRowcnt nRow; |
| | i64 nSum100 = 0; |
| | i64 nDist100; |
| |
|
| | if( !pIdx->aiRowEst || iCol>=pIdx->nKeyCol || pIdx->aiRowEst[iCol+1]==0 ){ |
| | nRow = pFinal->anLt[iCol]; |
| | nDist100 = (i64)100 * pFinal->anDLt[iCol]; |
| | nSample--; |
| | }else{ |
| | nRow = pIdx->aiRowEst[0]; |
| | nDist100 = ((i64)100 * pIdx->aiRowEst[0]) / pIdx->aiRowEst[iCol+1]; |
| | } |
| | pIdx->nRowEst0 = nRow; |
| |
|
| | |
| | |
| | |
| | |
| | for(i=0; i<nSample; i++){ |
| | if( i==(pIdx->nSample-1) |
| | || aSample[i].anDLt[iCol]!=aSample[i+1].anDLt[iCol] |
| | ){ |
| | sumEq += aSample[i].anEq[iCol]; |
| | nSum100 += 100; |
| | } |
| | } |
| |
|
| | if( nDist100>nSum100 && sumEq<nRow ){ |
| | avgEq = ((i64)100 * (nRow - sumEq))/(nDist100 - nSum100); |
| | } |
| | if( avgEq==0 ) avgEq = 1; |
| | pIdx->aAvgEq[iCol] = avgEq; |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | static Index *findIndexOrPrimaryKey( |
| | sqlite3 *db, |
| | const char *zName, |
| | const char *zDb |
| | ){ |
| | Index *pIdx = sqlite3FindIndex(db, zName, zDb); |
| | if( pIdx==0 ){ |
| | Table *pTab = sqlite3FindTable(db, zName, zDb); |
| | if( pTab && !HasRowid(pTab) ) pIdx = sqlite3PrimaryKeyIndex(pTab); |
| | } |
| | return pIdx; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static int loadStatTbl( |
| | sqlite3 *db, |
| | const char *zSql1, |
| | const char *zSql2, |
| | const char *zDb |
| | ){ |
| | int rc; |
| | sqlite3_stmt *pStmt = 0; |
| | char *zSql; |
| | Index *pPrevIdx = 0; |
| | IndexSample *pSample; |
| |
|
| | assert( db->lookaside.bDisable ); |
| | zSql = sqlite3MPrintf(db, zSql1, zDb); |
| | if( !zSql ){ |
| | return SQLITE_NOMEM_BKPT; |
| | } |
| | rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); |
| | sqlite3DbFree(db, zSql); |
| | if( rc ) return rc; |
| |
|
| | while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| | int nIdxCol = 1; |
| |
|
| | char *zIndex; |
| | Index *pIdx; |
| | int nSample; |
| | i64 nByte; |
| | i64 i; |
| | tRowcnt *pSpace; |
| | u8 *pPtr; |
| |
|
| | zIndex = (char *)sqlite3_column_text(pStmt, 0); |
| | if( zIndex==0 ) continue; |
| | nSample = sqlite3_column_int(pStmt, 1); |
| | pIdx = findIndexOrPrimaryKey(db, zIndex, zDb); |
| | assert( pIdx==0 || pIdx->nSample==0 ); |
| | if( pIdx==0 ) continue; |
| | if( pIdx->aSample!=0 ){ |
| | |
| | continue; |
| | } |
| | assert( !HasRowid(pIdx->pTable) || pIdx->nColumn==pIdx->nKeyCol+1 ); |
| | if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){ |
| | nIdxCol = pIdx->nKeyCol; |
| | }else{ |
| | nIdxCol = pIdx->nColumn; |
| | } |
| | pIdx->nSampleCol = nIdxCol; |
| | pIdx->mxSample = nSample; |
| | nByte = ROUND8(sizeof(IndexSample) * nSample); |
| | nByte += sizeof(tRowcnt) * nIdxCol * 3 * nSample; |
| | nByte += nIdxCol * sizeof(tRowcnt); |
| |
|
| | pIdx->aSample = sqlite3DbMallocZero(db, nByte); |
| | if( pIdx->aSample==0 ){ |
| | sqlite3_finalize(pStmt); |
| | return SQLITE_NOMEM_BKPT; |
| | } |
| | pPtr = (u8*)pIdx->aSample; |
| | pPtr += ROUND8(nSample*sizeof(pIdx->aSample[0])); |
| | pSpace = (tRowcnt*)pPtr; |
| | assert( EIGHT_BYTE_ALIGNMENT( pSpace ) ); |
| | pIdx->aAvgEq = pSpace; pSpace += nIdxCol; |
| | pIdx->pTable->tabFlags |= TF_HasStat4; |
| | for(i=0; i<nSample; i++){ |
| | 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, zSql2, zDb); |
| | if( !zSql ){ |
| | return SQLITE_NOMEM_BKPT; |
| | } |
| | rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); |
| | sqlite3DbFree(db, zSql); |
| | if( rc ) return rc; |
| |
|
| | while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| | char *zIndex; |
| | Index *pIdx; |
| | int nCol = 1; |
| |
|
| | zIndex = (char *)sqlite3_column_text(pStmt, 0); |
| | if( zIndex==0 ) continue; |
| | pIdx = findIndexOrPrimaryKey(db, zIndex, zDb); |
| | if( pIdx==0 ) continue; |
| | if( pIdx->nSample>=pIdx->mxSample ){ |
| | |
| | |
| | continue; |
| | } |
| | |
| | |
| | nCol = pIdx->nSampleCol; |
| | if( pIdx!=pPrevIdx ){ |
| | initAvgEq(pPrevIdx); |
| | pPrevIdx = pIdx; |
| | } |
| | pSample = &pIdx->aSample[pIdx->nSample]; |
| | decodeIntArray((char*)sqlite3_column_text(pStmt,1),nCol,pSample->anEq,0,0); |
| | decodeIntArray((char*)sqlite3_column_text(pStmt,2),nCol,pSample->anLt,0,0); |
| | decodeIntArray((char*)sqlite3_column_text(pStmt,3),nCol,pSample->anDLt,0,0); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | pSample->n = sqlite3_column_bytes(pStmt, 4); |
| | pSample->p = sqlite3DbMallocZero(db, pSample->n + 8); |
| | if( pSample->p==0 ){ |
| | sqlite3_finalize(pStmt); |
| | return SQLITE_NOMEM_BKPT; |
| | } |
| | if( pSample->n ){ |
| | memcpy(pSample->p, sqlite3_column_blob(pStmt, 4), pSample->n); |
| | } |
| | pIdx->nSample++; |
| | } |
| | rc = sqlite3_finalize(pStmt); |
| | if( rc==SQLITE_OK ) initAvgEq(pPrevIdx); |
| | return rc; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | static int loadStat4(sqlite3 *db, const char *zDb){ |
| | int rc = SQLITE_OK; |
| | const Table *pStat4; |
| |
|
| | assert( db->lookaside.bDisable ); |
| | if( OptimizationEnabled(db, SQLITE_Stat4) |
| | && (pStat4 = sqlite3FindTable(db, "sqlite_stat4", zDb))!=0 |
| | && IsOrdinaryTable(pStat4) |
| | ){ |
| | rc = loadStatTbl(db, |
| | "SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx COLLATE nocase", |
| | "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4", |
| | zDb |
| | ); |
| | } |
| | return rc; |
| | } |
| | #endif |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ |
| | analysisInfo sInfo; |
| | HashElem *i; |
| | char *zSql; |
| | int rc = SQLITE_OK; |
| | Schema *pSchema = db->aDb[iDb].pSchema; |
| | const Table *pStat1; |
| |
|
| | assert( iDb>=0 && iDb<db->nDb ); |
| | assert( db->aDb[iDb].pBt!=0 ); |
| |
|
| | |
| | assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); |
| | for(i=sqliteHashFirst(&pSchema->tblHash); i; i=sqliteHashNext(i)){ |
| | Table *pTab = sqliteHashData(i); |
| | pTab->tabFlags &= ~TF_HasStat1; |
| | } |
| | for(i=sqliteHashFirst(&pSchema->idxHash); i; i=sqliteHashNext(i)){ |
| | Index *pIdx = sqliteHashData(i); |
| | pIdx->hasStat1 = 0; |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | sqlite3DeleteIndexSamples(db, pIdx); |
| | pIdx->aSample = 0; |
| | #endif |
| | } |
| |
|
| | |
| | sInfo.db = db; |
| | sInfo.zDatabase = db->aDb[iDb].zDbSName; |
| | if( (pStat1 = sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)) |
| | && IsOrdinaryTable(pStat1) |
| | ){ |
| | zSql = sqlite3MPrintf(db, |
| | "SELECT tbl,idx,stat FROM %Q.sqlite_stat1", sInfo.zDatabase); |
| | if( zSql==0 ){ |
| | rc = SQLITE_NOMEM_BKPT; |
| | }else{ |
| | rc = sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0); |
| | sqlite3DbFree(db, zSql); |
| | } |
| | } |
| |
|
| | |
| | assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); |
| | for(i=sqliteHashFirst(&pSchema->idxHash); i; i=sqliteHashNext(i)){ |
| | Index *pIdx = sqliteHashData(i); |
| | if( !pIdx->hasStat1 ) sqlite3DefaultRowEst(pIdx); |
| | } |
| |
|
| | |
| | #ifdef SQLITE_ENABLE_STAT4 |
| | if( rc==SQLITE_OK ){ |
| | DisableLookaside; |
| | rc = loadStat4(db, sInfo.zDatabase); |
| | EnableLookaside; |
| | } |
| | for(i=sqliteHashFirst(&pSchema->idxHash); i; i=sqliteHashNext(i)){ |
| | Index *pIdx = sqliteHashData(i); |
| | sqlite3_free(pIdx->aiRowEst); |
| | pIdx->aiRowEst = 0; |
| | } |
| | #endif |
| |
|
| | if( rc==SQLITE_NOMEM ){ |
| | sqlite3OomFault(db); |
| | } |
| | return rc; |
| | } |
| |
|
| |
|
| | #endif |
| |
|