#include "sqlite3expert.h"
#include <assert.h>
#include <string.h>
#include <stdio.h>
#if !defined(SQLITE_AMALGAMATION)
#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1
#endif
#if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS)
# define ALWAYS(X) (1)
# define NEVER(X) (0)
#elif !defined(NDEBUG)
# define ALWAYS(X) ((X)?1:(assert(0),0))
# define NEVER(X) ((X)?(assert(0),1):0)
#else
# define ALWAYS(X) (X)
# define NEVER(X) (X)
#endif
#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
typedef sqlite3_int64 i64;
typedef sqlite3_uint64 u64;
typedef struct IdxColumn IdxColumn;
typedef struct IdxConstraint IdxConstraint;
typedef struct IdxScan IdxScan;
typedef struct IdxStatement IdxStatement;
typedef struct IdxTable IdxTable;
typedef struct IdxWrite IdxWrite;
#define STRLEN (int)strlen
#define UNIQUE_TABLE_NAME "t592690916721053953805701627921227776"
struct IdxConstraint {
char *zColl;
int bRange;
int iCol;
int bFlag;
int bDesc;
IdxConstraint *pNext;
IdxConstraint *pLink;
};
struct IdxScan {
IdxTable *pTab;
int iDb;
i64 covering;
IdxConstraint *pOrder;
IdxConstraint *pEq;
IdxConstraint *pRange;
IdxScan *pNextScan;
};
struct IdxColumn {
char *zName;
char *zColl;
int iPk;
};
struct IdxTable {
int nCol;
char *zName;
IdxColumn *aCol;
IdxTable *pNext;
};
struct IdxWrite {
IdxTable *pTab;
int eOp;
IdxWrite *pNext;
};
struct IdxStatement {
int iId;
char *zSql;
char *zIdx;
char *zEQP;
IdxStatement *pNext;
};
#define IDX_HASH_SIZE 1023
typedef struct IdxHashEntry IdxHashEntry;
typedef struct IdxHash IdxHash;
struct IdxHashEntry {
char *zKey;
char *zVal;
char *zVal2;
IdxHashEntry *pHashNext;
IdxHashEntry *pNext;
};
struct IdxHash {
IdxHashEntry *pFirst;
IdxHashEntry *aHash[IDX_HASH_SIZE];
};
struct sqlite3expert {
int iSample;
sqlite3 *db;
sqlite3 *dbm;
sqlite3 *dbv;
IdxTable *pTable;
IdxScan *pScan;
IdxWrite *pWrite;
IdxStatement *pStatement;
int bRun;
char **pzErrmsg;
int rc;
IdxHash hIdx;
char *zCandidates;
};
static void *idxMalloc(int *pRc, int nByte){
void *pRet;
assert( *pRc==SQLITE_OK );
assert( nByte>0 );
pRet = sqlite3_malloc(nByte);
if( pRet ){
memset(pRet, 0, nByte);
}else{
*pRc = SQLITE_NOMEM;
}
return pRet;
}
static void idxHashInit(IdxHash *pHash){
memset(pHash, 0, sizeof(IdxHash));
}
static void idxHashClear(IdxHash *pHash){
int i;
for(i=0; i<IDX_HASH_SIZE; i++){
IdxHashEntry *pEntry;
IdxHashEntry *pNext;
for(pEntry=pHash->aHash[i]; pEntry; pEntry=pNext){
pNext = pEntry->pHashNext;
sqlite3_free(pEntry->zVal2);
sqlite3_free(pEntry);
}
}
memset(pHash, 0, sizeof(IdxHash));
}
static int idxHashString(const char *z, int n){
unsigned int ret = 0;
int i;
for(i=0; i<n; i++){
ret += (ret<<3) + (unsigned char)(z[i]);
}
return (int)(ret % IDX_HASH_SIZE);
}
static int idxHashAdd(
int *pRc,
IdxHash *pHash,
const char *zKey,
const char *zVal
){
int nKey = STRLEN(zKey);
int iHash = idxHashString(zKey, nKey);
int nVal = (zVal ? STRLEN(zVal) : 0);
IdxHashEntry *pEntry;
assert( iHash>=0 );
for(pEntry=pHash->aHash[iHash]; pEntry; pEntry=pEntry->pHashNext){
if( STRLEN(pEntry->zKey)==nKey && 0==memcmp(pEntry->zKey, zKey, nKey) ){
return 1;
}
}
pEntry = idxMalloc(pRc, sizeof(IdxHashEntry) + nKey+1 + nVal+1);
if( pEntry ){
pEntry->zKey = (char*)&pEntry[1];
memcpy(pEntry->zKey, zKey, nKey);
if( zVal ){
pEntry->zVal = &pEntry->zKey[nKey+1];
memcpy(pEntry->zVal, zVal, nVal);
}
pEntry->pHashNext = pHash->aHash[iHash];
pHash->aHash[iHash] = pEntry;
pEntry->pNext = pHash->pFirst;
pHash->pFirst = pEntry;
}
return 0;
}
static IdxHashEntry *idxHashFind(IdxHash *pHash, const char *zKey, int nKey){
int iHash;
IdxHashEntry *pEntry;
if( nKey<0 ) nKey = STRLEN(zKey);
iHash = idxHashString(zKey, nKey);
assert( iHash>=0 );
for(pEntry=pHash->aHash[iHash]; pEntry; pEntry=pEntry->pHashNext){
if( STRLEN(pEntry->zKey)==nKey && 0==memcmp(pEntry->zKey, zKey, nKey) ){
return pEntry;
}
}
return 0;
}
static const char *idxHashSearch(IdxHash *pHash, const char *zKey, int nKey){
IdxHashEntry *pEntry = idxHashFind(pHash, zKey, nKey);
if( pEntry ) return pEntry->zVal;
return 0;
}
static IdxConstraint *idxNewConstraint(int *pRc, const char *zColl){
IdxConstraint *pNew;
int nColl = STRLEN(zColl);
assert( *pRc==SQLITE_OK );
pNew = (IdxConstraint*)idxMalloc(pRc, sizeof(IdxConstraint) * nColl + 1);
if( pNew ){
pNew->zColl = (char*)&pNew[1];
memcpy(pNew->zColl, zColl, nColl+1);
}
return pNew;
}
static void idxDatabaseError(
sqlite3 *db,
char **pzErrmsg
){
*pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
}
static int idxPrepareStmt(
sqlite3 *db,
sqlite3_stmt **ppStmt,
char **pzErrmsg,
const char *zSql
){
int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0);
if( rc!=SQLITE_OK ){
*ppStmt = 0;
idxDatabaseError(db, pzErrmsg);
}
return rc;
}
static int idxPrintfPrepareStmt(
sqlite3 *db,
sqlite3_stmt **ppStmt,
char **pzErrmsg,
const char *zFmt,
...
){
va_list ap;
int rc;
char *zSql;
va_start(ap, zFmt);
zSql = sqlite3_vmprintf(zFmt, ap);
if( zSql==0 ){
rc = SQLITE_NOMEM;
}else{
rc = idxPrepareStmt(db, ppStmt, pzErrmsg, zSql);
sqlite3_free(zSql);
}
va_end(ap);
return rc;
}
typedef struct ExpertVtab ExpertVtab;
struct ExpertVtab {
sqlite3_vtab base;
IdxTable *pTab;
sqlite3expert *pExpert;
};
typedef struct ExpertCsr ExpertCsr;
struct ExpertCsr {
sqlite3_vtab_cursor base;
sqlite3_stmt *pData;
};
static char *expertDequote(const char *zIn){
int n = STRLEN(zIn);
char *zRet = sqlite3_malloc(n);
assert( zIn[0]=='\'' );
assert( zIn[n-1]=='\'' );
if( zRet ){
int iOut = 0;
int iIn = 0;
for(iIn=1; iIn<(n-1); iIn++){
if( zIn[iIn]=='\'' ){
assert( zIn[iIn+1]=='\'' );
iIn++;
}
zRet[iOut++] = zIn[iIn];
}
zRet[iOut] = '\0';
}
return zRet;
}
static int expertConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
sqlite3expert *pExpert = (sqlite3expert*)pAux;
ExpertVtab *p = 0;
int rc;
if( argc!=4 ){
*pzErr = sqlite3_mprintf("internal error!");
rc = SQLITE_ERROR;
}else{
char *zCreateTable = expertDequote(argv[3]);
if( zCreateTable ){
rc = sqlite3_declare_vtab(db, zCreateTable);
if( rc==SQLITE_OK ){
p = idxMalloc(&rc, sizeof(ExpertVtab));
}
if( rc==SQLITE_OK ){
p->pExpert = pExpert;
p->pTab = pExpert->pTable;
assert( sqlite3_stricmp(p->pTab->zName, argv[2])==0 );
}
sqlite3_free(zCreateTable);
}else{
rc = SQLITE_NOMEM;
}
}
*ppVtab = (sqlite3_vtab*)p;
return rc;
}
static int expertDisconnect(sqlite3_vtab *pVtab){
ExpertVtab *p = (ExpertVtab*)pVtab;
sqlite3_free(p);
return SQLITE_OK;
}
static int expertBestIndex(sqlite3_vtab *pVtab, sqlite3_index_info *pIdxInfo){
ExpertVtab *p = (ExpertVtab*)pVtab;
int rc = SQLITE_OK;
int n = 0;
IdxScan *pScan;
const int opmask =
SQLITE_INDEX_CONSTRAINT_EQ | SQLITE_INDEX_CONSTRAINT_GT |
SQLITE_INDEX_CONSTRAINT_LT | SQLITE_INDEX_CONSTRAINT_GE |
SQLITE_INDEX_CONSTRAINT_LE;
pScan = idxMalloc(&rc, sizeof(IdxScan));
if( pScan ){
int i;
pScan->pTab = p->pTab;
pScan->pNextScan = p->pExpert->pScan;
p->pExpert->pScan = pScan;
for(i=0; i<pIdxInfo->nConstraint; i++){
struct sqlite3_index_constraint *pCons = &pIdxInfo->aConstraint[i];
if( pCons->usable
&& pCons->iColumn>=0
&& p->pTab->aCol[pCons->iColumn].iPk==0
&& (pCons->op & opmask)
){
IdxConstraint *pNew;
const char *zColl = sqlite3_vtab_collation(pIdxInfo, i);
pNew = idxNewConstraint(&rc, zColl);
if( pNew ){
pNew->iCol = pCons->iColumn;
if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){
pNew->pNext = pScan->pEq;
pScan->pEq = pNew;
}else{
pNew->bRange = 1;
pNew->pNext = pScan->pRange;
pScan->pRange = pNew;
}
}
n++;
pIdxInfo->aConstraintUsage[i].argvIndex = n;
}
}
for(i=pIdxInfo->nOrderBy-1; i>=0; i--){
int iCol = pIdxInfo->aOrderBy[i].iColumn;
if( iCol>=0 ){
IdxConstraint *pNew = idxNewConstraint(&rc, p->pTab->aCol[iCol].zColl);
if( pNew ){
pNew->iCol = iCol;
pNew->bDesc = pIdxInfo->aOrderBy[i].desc;
pNew->pNext = pScan->pOrder;
pNew->pLink = pScan->pOrder;
pScan->pOrder = pNew;
n++;
}
}
}
}
pIdxInfo->estimatedCost = 1000000.0 / (n+1);
return rc;
}
static int expertUpdate(
sqlite3_vtab *pVtab,
int nData,
sqlite3_value **azData,
sqlite_int64 *pRowid
){
(void)pVtab;
(void)nData;
(void)azData;
(void)pRowid;
return SQLITE_OK;
}
static int expertOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
int rc = SQLITE_OK;
ExpertCsr *pCsr;
(void)pVTab;
pCsr = idxMalloc(&rc, sizeof(ExpertCsr));
*ppCursor = (sqlite3_vtab_cursor*)pCsr;
return rc;
}
static int expertClose(sqlite3_vtab_cursor *cur){
ExpertCsr *pCsr = (ExpertCsr*)cur;
sqlite3_finalize(pCsr->pData);
sqlite3_free(pCsr);
return SQLITE_OK;
}
static int expertEof(sqlite3_vtab_cursor *cur){
ExpertCsr *pCsr = (ExpertCsr*)cur;
return pCsr->pData==0;
}
static int expertNext(sqlite3_vtab_cursor *cur){
ExpertCsr *pCsr = (ExpertCsr*)cur;
int rc = SQLITE_OK;
assert( pCsr->pData );
rc = sqlite3_step(pCsr->pData);
if( rc!=SQLITE_ROW ){
rc = sqlite3_finalize(pCsr->pData);
pCsr->pData = 0;
}else{
rc = SQLITE_OK;
}
return rc;
}
static int expertRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
(void)cur;
*pRowid = 0;
return SQLITE_OK;
}
static int expertColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
ExpertCsr *pCsr = (ExpertCsr*)cur;
sqlite3_value *pVal;
pVal = sqlite3_column_value(pCsr->pData, i);
if( pVal ){
sqlite3_result_value(ctx, pVal);
}
return SQLITE_OK;
}
static int expertFilter(
sqlite3_vtab_cursor *cur,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
ExpertCsr *pCsr = (ExpertCsr*)cur;
ExpertVtab *pVtab = (ExpertVtab*)(cur->pVtab);
sqlite3expert *pExpert = pVtab->pExpert;
int rc;
(void)idxNum;
(void)idxStr;
(void)argc;
(void)argv;
rc = sqlite3_finalize(pCsr->pData);
pCsr->pData = 0;
if( rc==SQLITE_OK ){
rc = idxPrintfPrepareStmt(pExpert->db, &pCsr->pData, &pVtab->base.zErrMsg,
"SELECT * FROM main.%Q WHERE sample()", pVtab->pTab->zName
);
}
if( rc==SQLITE_OK ){
rc = expertNext(cur);
}
return rc;
}
static int idxRegisterVtab(sqlite3expert *p){
static sqlite3_module expertModule = {
2,
expertConnect,
expertConnect,
expertBestIndex,
expertDisconnect,
expertDisconnect,
expertOpen,
expertClose,
expertFilter,
expertNext,
expertEof,
expertColumn,
expertRowid,
expertUpdate,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
};
return sqlite3_create_module(p->dbv, "expert", &expertModule, (void*)p);
}
static void idxFinalize(int *pRc, sqlite3_stmt *pStmt){
int rc = sqlite3_finalize(pStmt);
if( *pRc==SQLITE_OK ) *pRc = rc;
}
static int idxGetTableInfo(
sqlite3 *db,
const char *zTab,
IdxTable **ppOut,
char **pzErrmsg
){
sqlite3_stmt *p1 = 0;
int nCol = 0;
int nTab;
int nByte;
IdxTable *pNew = 0;
int rc, rc2;
char *pCsr = 0;
int nPk = 0;
*ppOut = 0;
if( zTab==0 ) return SQLITE_ERROR;
nTab = STRLEN(zTab);
nByte = sizeof(IdxTable) + nTab + 1;
rc = idxPrintfPrepareStmt(db, &p1, pzErrmsg, "PRAGMA table_xinfo=%Q", zTab);
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(p1) ){
const char *zCol = (const char*)sqlite3_column_text(p1, 1);
const char *zColSeq = 0;
if( zCol==0 ){
rc = SQLITE_ERROR;
break;
}
nByte += 1 + STRLEN(zCol);
rc = sqlite3_table_column_metadata(
db, "main", zTab, zCol, 0, &zColSeq, 0, 0, 0
);
if( zColSeq==0 ) zColSeq = "binary";
nByte += 1 + STRLEN(zColSeq);
nCol++;
nPk += (sqlite3_column_int(p1, 5)>0);
}
rc2 = sqlite3_reset(p1);
if( rc==SQLITE_OK ) rc = rc2;
nByte += sizeof(IdxColumn) * nCol;
if( rc==SQLITE_OK ){
pNew = idxMalloc(&rc, nByte);
}
if( rc==SQLITE_OK ){
pNew->aCol = (IdxColumn*)&pNew[1];
pNew->nCol = nCol;
pCsr = (char*)&pNew->aCol[nCol];
}
nCol = 0;
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(p1) ){
const char *zCol = (const char*)sqlite3_column_text(p1, 1);
const char *zColSeq = 0;
int nCopy;
if( zCol==0 ) continue;
nCopy = STRLEN(zCol) + 1;
pNew->aCol[nCol].zName = pCsr;
pNew->aCol[nCol].iPk = (sqlite3_column_int(p1, 5)==1 && nPk==1);
memcpy(pCsr, zCol, nCopy);
pCsr += nCopy;
rc = sqlite3_table_column_metadata(
db, "main", zTab, zCol, 0, &zColSeq, 0, 0, 0
);
if( rc==SQLITE_OK ){
if( zColSeq==0 ) zColSeq = "binary";
nCopy = STRLEN(zColSeq) + 1;
pNew->aCol[nCol].zColl = pCsr;
memcpy(pCsr, zColSeq, nCopy);
pCsr += nCopy;
}
nCol++;
}
idxFinalize(&rc, p1);
if( rc!=SQLITE_OK ){
sqlite3_free(pNew);
pNew = 0;
}else if( ALWAYS(pNew!=0) ){
pNew->zName = pCsr;
if( ALWAYS(pNew->zName!=0) ) memcpy(pNew->zName, zTab, nTab+1);
}
*ppOut = pNew;
return rc;
}
static char *idxAppendText(int *pRc, char *zIn, const char *zFmt, ...){
va_list ap;
char *zAppend = 0;
char *zRet = 0;
int nIn = zIn ? STRLEN(zIn) : 0;
int nAppend = 0;
va_start(ap, zFmt);
if( *pRc==SQLITE_OK ){
zAppend = sqlite3_vmprintf(zFmt, ap);
if( zAppend ){
nAppend = STRLEN(zAppend);
zRet = (char*)sqlite3_malloc(nIn + nAppend + 1);
}
if( zAppend && zRet ){
if( nIn ) memcpy(zRet, zIn, nIn);
memcpy(&zRet[nIn], zAppend, nAppend+1);
}else{
sqlite3_free(zRet);
zRet = 0;
*pRc = SQLITE_NOMEM;
}
sqlite3_free(zAppend);
sqlite3_free(zIn);
}
va_end(ap);
return zRet;
}
static int idxIdentifierRequiresQuotes(const char *zId){
int i;
int nId = STRLEN(zId);
if( sqlite3_keyword_check(zId, nId) ) return 1;
for(i=0; zId[i]; i++){
if( !(zId[i]=='_')
&& !(zId[i]>='0' && zId[i]<='9')
&& !(zId[i]>='a' && zId[i]<='z')
&& !(zId[i]>='A' && zId[i]<='Z')
){
return 1;
}
}
return 0;
}
static char *idxAppendColDefn(
int *pRc,
char *zIn,
IdxTable *pTab,
IdxConstraint *pCons
){
char *zRet = zIn;
IdxColumn *p = &pTab->aCol[pCons->iCol];
if( zRet ) zRet = idxAppendText(pRc, zRet, ", ");
if( idxIdentifierRequiresQuotes(p->zName) ){
zRet = idxAppendText(pRc, zRet, "%Q", p->zName);
}else{
zRet = idxAppendText(pRc, zRet, "%s", p->zName);
}
if( sqlite3_stricmp(p->zColl, pCons->zColl) ){
if( idxIdentifierRequiresQuotes(pCons->zColl) ){
zRet = idxAppendText(pRc, zRet, " COLLATE %Q", pCons->zColl);
}else{
zRet = idxAppendText(pRc, zRet, " COLLATE %s", pCons->zColl);
}
}
if( pCons->bDesc ){
zRet = idxAppendText(pRc, zRet, " DESC");
}
return zRet;
}
static int idxFindCompatible(
int *pRc,
sqlite3* dbm,
IdxScan *pScan,
IdxConstraint *pEq,
IdxConstraint *pTail
){
const char *zTbl = pScan->pTab->zName;
sqlite3_stmt *pIdxList = 0;
IdxConstraint *pIter;
int nEq = 0;
int rc;
for(pIter=pEq; pIter; pIter=pIter->pLink) nEq++;
rc = idxPrintfPrepareStmt(dbm, &pIdxList, 0, "PRAGMA index_list=%Q", zTbl);
while( rc==SQLITE_OK && sqlite3_step(pIdxList)==SQLITE_ROW ){
int bMatch = 1;
IdxConstraint *pT = pTail;
sqlite3_stmt *pInfo = 0;
const char *zIdx = (const char*)sqlite3_column_text(pIdxList, 1);
if( zIdx==0 ) continue;
for(pIter=pEq; pIter; pIter=pIter->pLink) pIter->bFlag = 0;
rc = idxPrintfPrepareStmt(dbm, &pInfo, 0, "PRAGMA index_xInfo=%Q", zIdx);
while( rc==SQLITE_OK && sqlite3_step(pInfo)==SQLITE_ROW ){
int iIdx = sqlite3_column_int(pInfo, 0);
int iCol = sqlite3_column_int(pInfo, 1);
const char *zColl = (const char*)sqlite3_column_text(pInfo, 4);
if( iIdx<nEq ){
for(pIter=pEq; pIter; pIter=pIter->pLink){
if( pIter->bFlag ) continue;
if( pIter->iCol!=iCol ) continue;
if( sqlite3_stricmp(pIter->zColl, zColl) ) continue;
pIter->bFlag = 1;
break;
}
if( pIter==0 ){
bMatch = 0;
break;
}
}else{
if( pT ){
if( pT->iCol!=iCol || sqlite3_stricmp(pT->zColl, zColl) ){
bMatch = 0;
break;
}
pT = pT->pLink;
}
}
}
idxFinalize(&rc, pInfo);
if( rc==SQLITE_OK && bMatch ){
sqlite3_finalize(pIdxList);
return 1;
}
}
idxFinalize(&rc, pIdxList);
*pRc = rc;
return 0;
}
static int countNonzeros(void* pCount, int nc,
char* azResults[], char* azColumns[]){
(void)azColumns;
if( nc>0 && (azResults[0][0]!='0' || azResults[0][1]!=0) ){
*((int *)pCount) += 1;
}
return 0;
}
static int idxCreateFromCons(
sqlite3expert *p,
IdxScan *pScan,
IdxConstraint *pEq,
IdxConstraint *pTail
){
sqlite3 *dbm = p->dbm;
int rc = SQLITE_OK;
if( (pEq || pTail) && 0==idxFindCompatible(&rc, dbm, pScan, pEq, pTail) ){
IdxTable *pTab = pScan->pTab;
char *zCols = 0;
char *zIdx = 0;
IdxConstraint *pCons;
unsigned int h = 0;
const char *zFmt;
for(pCons=pEq; pCons; pCons=pCons->pLink){
zCols = idxAppendColDefn(&rc, zCols, pTab, pCons);
}
for(pCons=pTail; pCons; pCons=pCons->pLink){
zCols = idxAppendColDefn(&rc, zCols, pTab, pCons);
}
if( rc==SQLITE_OK ){
const char *zTable = pScan->pTab->zName;
int quoteTable = idxIdentifierRequiresQuotes(zTable);
char *zName = 0;
int collisions = 0;
do{
int i;
char *zFind;
for(i=0; zCols[i]; i++){
h += ((h<<3) + zCols[i]);
}
sqlite3_free(zName);
zName = sqlite3_mprintf("%s_idx_%08x", zTable, h);
if( zName==0 ) break;
zFmt = "SELECT count(*) FROM sqlite_schema WHERE name=%Q"
" AND type in ('index','table','view')";
zFind = sqlite3_mprintf(zFmt, zName);
i = 0;
rc = sqlite3_exec(dbm, zFind, countNonzeros, &i, 0);
assert(rc==SQLITE_OK);
sqlite3_free(zFind);
if( i==0 ){
collisions = 0;
break;
}
++collisions;
}while( collisions<50 && zName!=0 );
if( collisions ){
rc = SQLITE_BUSY_TIMEOUT;
}else if( zName==0 ){
rc = SQLITE_NOMEM;
}else{
if( quoteTable ){
zFmt = "CREATE INDEX \"%w\" ON \"%w\"(%s)";
}else{
zFmt = "CREATE INDEX %s ON %s(%s)";
}
zIdx = sqlite3_mprintf(zFmt, zName, zTable, zCols);
if( !zIdx ){
rc = SQLITE_NOMEM;
}else{
rc = sqlite3_exec(dbm, zIdx, 0, 0, p->pzErrmsg);
if( rc!=SQLITE_OK ){
rc = SQLITE_BUSY_TIMEOUT;
}else{
idxHashAdd(&rc, &p->hIdx, zName, zIdx);
}
}
sqlite3_free(zName);
sqlite3_free(zIdx);
}
}
sqlite3_free(zCols);
}
return rc;
}
static int idxFindConstraint(IdxConstraint *pList, IdxConstraint *p){
IdxConstraint *pCmp;
for(pCmp=pList; pCmp; pCmp=pCmp->pLink){
if( p->iCol==pCmp->iCol ) return 1;
}
return 0;
}
static int idxCreateFromWhere(
sqlite3expert *p,
IdxScan *pScan,
IdxConstraint *pTail
){
IdxConstraint *p1 = 0;
IdxConstraint *pCon;
int rc;
for(pCon=pScan->pEq; pCon; pCon=pCon->pNext){
if( !idxFindConstraint(p1, pCon) && !idxFindConstraint(pTail, pCon) ){
pCon->pLink = p1;
p1 = pCon;
}
}
rc = idxCreateFromCons(p, pScan, p1, pTail);
if( pTail==0 ){
for(pCon=pScan->pRange; rc==SQLITE_OK && pCon; pCon=pCon->pNext){
assert( pCon->pLink==0 );
if( !idxFindConstraint(p1, pCon) && !idxFindConstraint(pTail, pCon) ){
rc = idxCreateFromCons(p, pScan, p1, pCon);
}
}
}
return rc;
}
static int idxCreateCandidates(sqlite3expert *p){
int rc = SQLITE_OK;
IdxScan *pIter;
for(pIter=p->pScan; pIter && rc==SQLITE_OK; pIter=pIter->pNextScan){
rc = idxCreateFromWhere(p, pIter, 0);
if( rc==SQLITE_OK && pIter->pOrder ){
rc = idxCreateFromWhere(p, pIter, pIter->pOrder);
}
}
return rc;
}
static void idxConstraintFree(IdxConstraint *pConstraint){
IdxConstraint *pNext;
IdxConstraint *p;
for(p=pConstraint; p; p=pNext){
pNext = p->pNext;
sqlite3_free(p);
}
}
static void idxScanFree(IdxScan *pScan, IdxScan *pLast){
IdxScan *p;
IdxScan *pNext;
for(p=pScan; p!=pLast; p=pNext){
pNext = p->pNextScan;
idxConstraintFree(p->pOrder);
idxConstraintFree(p->pEq);
idxConstraintFree(p->pRange);
sqlite3_free(p);
}
}
static void idxStatementFree(IdxStatement *pStatement, IdxStatement *pLast){
IdxStatement *p;
IdxStatement *pNext;
for(p=pStatement; p!=pLast; p=pNext){
pNext = p->pNext;
sqlite3_free(p->zEQP);
sqlite3_free(p->zIdx);
sqlite3_free(p);
}
}
static void idxTableFree(IdxTable *pTab){
IdxTable *pIter;
IdxTable *pNext;
for(pIter=pTab; pIter; pIter=pNext){
pNext = pIter->pNext;
sqlite3_free(pIter);
}
}
static void idxWriteFree(IdxWrite *pTab){
IdxWrite *pIter;
IdxWrite *pNext;
for(pIter=pTab; pIter; pIter=pNext){
pNext = pIter->pNext;
sqlite3_free(pIter);
}
}
static int idxFindIndexes(
sqlite3expert *p,
char **pzErr
){
IdxStatement *pStmt;
sqlite3 *dbm = p->dbm;
int rc = SQLITE_OK;
IdxHash hIdx;
idxHashInit(&hIdx);
for(pStmt=p->pStatement; rc==SQLITE_OK && pStmt; pStmt=pStmt->pNext){
IdxHashEntry *pEntry;
sqlite3_stmt *pExplain = 0;
idxHashClear(&hIdx);
rc = idxPrintfPrepareStmt(dbm, &pExplain, pzErr,
"EXPLAIN QUERY PLAN %s", pStmt->zSql
);
while( rc==SQLITE_OK && sqlite3_step(pExplain)==SQLITE_ROW ){
const char *zDetail = (const char*)sqlite3_column_text(pExplain, 3);
int nDetail;
int i;
if( !zDetail ) continue;
nDetail = STRLEN(zDetail);
for(i=0; i<nDetail; i++){
const char *zIdx = 0;
if( i+13<nDetail && memcmp(&zDetail[i], " USING INDEX ", 13)==0 ){
zIdx = &zDetail[i+13];
}else if( i+22<nDetail
&& memcmp(&zDetail[i], " USING COVERING INDEX ", 22)==0
){
zIdx = &zDetail[i+22];
}
if( zIdx ){
const char *zSql;
int nIdx = 0;
while( zIdx[nIdx]!='\0' && (zIdx[nIdx]!=' ' || zIdx[nIdx+1]!='(') ){
nIdx++;
}
zSql = idxHashSearch(&p->hIdx, zIdx, nIdx);
if( zSql ){
idxHashAdd(&rc, &hIdx, zSql, 0);
if( rc ) goto find_indexes_out;
}
break;
}
}
if( zDetail[0]!='-' ){
pStmt->zEQP = idxAppendText(&rc, pStmt->zEQP, "%s\n", zDetail);
}
}
for(pEntry=hIdx.pFirst; pEntry; pEntry=pEntry->pNext){
pStmt->zIdx = idxAppendText(&rc, pStmt->zIdx, "%s;\n", pEntry->zKey);
}
idxFinalize(&rc, pExplain);
}
find_indexes_out:
idxHashClear(&hIdx);
return rc;
}
static int idxAuthCallback(
void *pCtx,
int eOp,
const char *z3,
const char *z4,
const char *zDb,
const char *zTrigger
){
int rc = SQLITE_OK;
(void)z4;
(void)zTrigger;
if( eOp==SQLITE_INSERT || eOp==SQLITE_UPDATE || eOp==SQLITE_DELETE ){
if( sqlite3_stricmp(zDb, "main")==0 ){
sqlite3expert *p = (sqlite3expert*)pCtx;
IdxTable *pTab;
for(pTab=p->pTable; pTab; pTab=pTab->pNext){
if( 0==sqlite3_stricmp(z3, pTab->zName) ) break;
}
if( pTab ){
IdxWrite *pWrite;
for(pWrite=p->pWrite; pWrite; pWrite=pWrite->pNext){
if( pWrite->pTab==pTab && pWrite->eOp==eOp ) break;
}
if( pWrite==0 ){
pWrite = idxMalloc(&rc, sizeof(IdxWrite));
if( rc==SQLITE_OK ){
pWrite->pTab = pTab;
pWrite->eOp = eOp;
pWrite->pNext = p->pWrite;
p->pWrite = pWrite;
}
}
}
}
}
return rc;
}
static int idxProcessOneTrigger(
sqlite3expert *p,
IdxWrite *pWrite,
char **pzErr
){
static const char *zInt = UNIQUE_TABLE_NAME;
static const char *zDrop = "DROP TABLE " UNIQUE_TABLE_NAME;
IdxTable *pTab = pWrite->pTab;
const char *zTab = pTab->zName;
const char *zSql =
"SELECT 'CREATE TEMP' || substr(sql, 7) FROM sqlite_schema "
"WHERE tbl_name = %Q AND type IN ('table', 'trigger') "
"ORDER BY type;";
sqlite3_stmt *pSelect = 0;
int rc = SQLITE_OK;
char *zWrite = 0;
rc = idxPrintfPrepareStmt(p->db, &pSelect, pzErr, zSql, zTab, zTab);
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSelect) ){
const char *zCreate = (const char*)sqlite3_column_text(pSelect, 0);
if( zCreate==0 ) continue;
rc = sqlite3_exec(p->dbv, zCreate, 0, 0, pzErr);
}
idxFinalize(&rc, pSelect);
if( rc==SQLITE_OK ){
char *z = sqlite3_mprintf("ALTER TABLE temp.%Q RENAME TO %Q", zTab, zInt);
if( z==0 ){
rc = SQLITE_NOMEM;
}else{
rc = sqlite3_exec(p->dbv, z, 0, 0, pzErr);
sqlite3_free(z);
}
}
switch( pWrite->eOp ){
case SQLITE_INSERT: {
int i;
zWrite = idxAppendText(&rc, zWrite, "INSERT INTO %Q VALUES(", zInt);
for(i=0; i<pTab->nCol; i++){
zWrite = idxAppendText(&rc, zWrite, "%s?", i==0 ? "" : ", ");
}
zWrite = idxAppendText(&rc, zWrite, ")");
break;
}
case SQLITE_UPDATE: {
int i;
zWrite = idxAppendText(&rc, zWrite, "UPDATE %Q SET ", zInt);
for(i=0; i<pTab->nCol; i++){
zWrite = idxAppendText(&rc, zWrite, "%s%Q=?", i==0 ? "" : ", ",
pTab->aCol[i].zName
);
}
break;
}
default: {
assert( pWrite->eOp==SQLITE_DELETE );
if( rc==SQLITE_OK ){
zWrite = sqlite3_mprintf("DELETE FROM %Q", zInt);
if( zWrite==0 ) rc = SQLITE_NOMEM;
}
}
}
if( rc==SQLITE_OK ){
sqlite3_stmt *pX = 0;
rc = sqlite3_prepare_v2(p->dbv, zWrite, -1, &pX, 0);
idxFinalize(&rc, pX);
if( rc!=SQLITE_OK ){
idxDatabaseError(p->dbv, pzErr);
}
}
sqlite3_free(zWrite);
if( rc==SQLITE_OK ){
rc = sqlite3_exec(p->dbv, zDrop, 0, 0, pzErr);
}
return rc;
}
static int idxProcessTriggers(sqlite3expert *p, char **pzErr){
int rc = SQLITE_OK;
IdxWrite *pEnd = 0;
IdxWrite *pFirst = p->pWrite;
while( rc==SQLITE_OK && pFirst!=pEnd ){
IdxWrite *pIter;
for(pIter=pFirst; rc==SQLITE_OK && pIter!=pEnd; pIter=pIter->pNext){
rc = idxProcessOneTrigger(p, pIter, pzErr);
}
pEnd = pFirst;
pFirst = p->pWrite;
}
return rc;
}
static int idxCreateVtabSchema(sqlite3expert *p, char **pzErrmsg){
int rc = idxRegisterVtab(p);
sqlite3_stmt *pSchema = 0;
rc = idxPrepareStmt(p->db, &pSchema, pzErrmsg,
"SELECT type, name, sql, 1 FROM sqlite_schema "
"WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%%' "
" UNION ALL "
"SELECT type, name, sql, 2 FROM sqlite_schema "
"WHERE type = 'trigger'"
" AND tbl_name IN(SELECT name FROM sqlite_schema WHERE type = 'view') "
"ORDER BY 4, 1"
);
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSchema) ){
const char *zType = (const char*)sqlite3_column_text(pSchema, 0);
const char *zName = (const char*)sqlite3_column_text(pSchema, 1);
const char *zSql = (const char*)sqlite3_column_text(pSchema, 2);
if( zType==0 || zName==0 ) continue;
if( zType[0]=='v' || zType[1]=='r' ){
if( zSql ) rc = sqlite3_exec(p->dbv, zSql, 0, 0, pzErrmsg);
}else{
IdxTable *pTab;
rc = idxGetTableInfo(p->db, zName, &pTab, pzErrmsg);
if( rc==SQLITE_OK ){
int i;
char *zInner = 0;
char *zOuter = 0;
pTab->pNext = p->pTable;
p->pTable = pTab;
zInner = idxAppendText(&rc, 0, "CREATE TABLE x(");
for(i=0; i<pTab->nCol; i++){
zInner = idxAppendText(&rc, zInner, "%s%Q COLLATE %s",
(i==0 ? "" : ", "), pTab->aCol[i].zName, pTab->aCol[i].zColl
);
}
zInner = idxAppendText(&rc, zInner, ")");
zOuter = idxAppendText(&rc, 0,
"CREATE VIRTUAL TABLE %Q USING expert(%Q)", zName, zInner
);
if( rc==SQLITE_OK ){
rc = sqlite3_exec(p->dbv, zOuter, 0, 0, pzErrmsg);
}
sqlite3_free(zInner);
sqlite3_free(zOuter);
}
}
}
idxFinalize(&rc, pSchema);
return rc;
}
struct IdxSampleCtx {
int iTarget;
double target;
double nRow;
double nRet;
};
static void idxSampleFunc(
sqlite3_context *pCtx,
int argc,
sqlite3_value **argv
){
struct IdxSampleCtx *p = (struct IdxSampleCtx*)sqlite3_user_data(pCtx);
int bRet;
(void)argv;
assert( argc==0 );
if( p->nRow==0.0 ){
bRet = 1;
}else{
bRet = (p->nRet / p->nRow) <= p->target;
if( bRet==0 ){
unsigned short rnd;
sqlite3_randomness(2, (void*)&rnd);
bRet = ((int)rnd % 100) <= p->iTarget;
}
}
sqlite3_result_int(pCtx, bRet);
p->nRow += 1.0;
p->nRet += (double)bRet;
}
struct IdxRemCtx {
int nSlot;
struct IdxRemSlot {
int eType;
i64 iVal;
double rVal;
int nByte;
int n;
char *z;
} aSlot[1];
};
static void idxRemFunc(
sqlite3_context *pCtx,
int argc,
sqlite3_value **argv
){
struct IdxRemCtx *p = (struct IdxRemCtx*)sqlite3_user_data(pCtx);
struct IdxRemSlot *pSlot;
int iSlot;
assert( argc==2 );
iSlot = sqlite3_value_int(argv[0]);
assert( iSlot<=p->nSlot );
pSlot = &p->aSlot[iSlot];
switch( pSlot->eType ){
case SQLITE_NULL:
break;
case SQLITE_INTEGER:
sqlite3_result_int64(pCtx, pSlot->iVal);
break;
case SQLITE_FLOAT:
sqlite3_result_double(pCtx, pSlot->rVal);
break;
case SQLITE_BLOB:
sqlite3_result_blob(pCtx, pSlot->z, pSlot->n, SQLITE_TRANSIENT);
break;
case SQLITE_TEXT:
sqlite3_result_text(pCtx, pSlot->z, pSlot->n, SQLITE_TRANSIENT);
break;
}
pSlot->eType = sqlite3_value_type(argv[1]);
switch( pSlot->eType ){
case SQLITE_NULL:
break;
case SQLITE_INTEGER:
pSlot->iVal = sqlite3_value_int64(argv[1]);
break;
case SQLITE_FLOAT:
pSlot->rVal = sqlite3_value_double(argv[1]);
break;
case SQLITE_BLOB:
case SQLITE_TEXT: {
int nByte = sqlite3_value_bytes(argv[1]);
const void *pData = 0;
if( nByte>pSlot->nByte ){
char *zNew = (char*)sqlite3_realloc(pSlot->z, nByte*2);
if( zNew==0 ){
sqlite3_result_error_nomem(pCtx);
return;
}
pSlot->nByte = nByte*2;
pSlot->z = zNew;
}
pSlot->n = nByte;
if( pSlot->eType==SQLITE_BLOB ){
pData = sqlite3_value_blob(argv[1]);
if( pData ) memcpy(pSlot->z, pData, nByte);
}else{
pData = sqlite3_value_text(argv[1]);
memcpy(pSlot->z, pData, nByte);
}
break;
}
}
}
static int idxLargestIndex(sqlite3 *db, int *pnMax, char **pzErr){
int rc = SQLITE_OK;
const char *zMax =
"SELECT max(i.seqno) FROM "
" sqlite_schema AS s, "
" pragma_index_list(s.name) AS l, "
" pragma_index_info(l.name) AS i "
"WHERE s.type = 'table'";
sqlite3_stmt *pMax = 0;
*pnMax = 0;
rc = idxPrepareStmt(db, &pMax, pzErr, zMax);
if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pMax) ){
*pnMax = sqlite3_column_int(pMax, 0) + 1;
}
idxFinalize(&rc, pMax);
return rc;
}
static int idxPopulateOneStat1(
sqlite3expert *p,
sqlite3_stmt *pIndexXInfo,
sqlite3_stmt *pWriteStat,
const char *zTab,
const char *zIdx,
char **pzErr
){
char *zCols = 0;
char *zOrder = 0;
char *zQuery = 0;
int nCol = 0;
int i;
sqlite3_stmt *pQuery = 0;
int *aStat = 0;
int rc = SQLITE_OK;
assert( p->iSample>0 );
sqlite3_bind_text(pIndexXInfo, 1, zIdx, -1, SQLITE_STATIC);
while( SQLITE_OK==rc && SQLITE_ROW==sqlite3_step(pIndexXInfo) ){
const char *zComma = zCols==0 ? "" : ", ";
const char *zName = (const char*)sqlite3_column_text(pIndexXInfo, 0);
const char *zColl = (const char*)sqlite3_column_text(pIndexXInfo, 1);
zCols = idxAppendText(&rc, zCols,
"%sx.%Q IS rem(%d, x.%Q) COLLATE %s", zComma, zName, nCol, zName, zColl
);
zOrder = idxAppendText(&rc, zOrder, "%s%d", zComma, ++nCol);
}
sqlite3_reset(pIndexXInfo);
if( rc==SQLITE_OK ){
if( p->iSample==100 ){
zQuery = sqlite3_mprintf(
"SELECT %s FROM %Q x ORDER BY %s", zCols, zTab, zOrder
);
}else{
zQuery = sqlite3_mprintf(
"SELECT %s FROM temp."UNIQUE_TABLE_NAME" x ORDER BY %s", zCols, zOrder
);
}
}
sqlite3_free(zCols);
sqlite3_free(zOrder);
if( rc==SQLITE_OK ){
sqlite3 *dbrem = (p->iSample==100 ? p->db : p->dbv);
rc = idxPrepareStmt(dbrem, &pQuery, pzErr, zQuery);
}
sqlite3_free(zQuery);
if( rc==SQLITE_OK ){
aStat = (int*)idxMalloc(&rc, sizeof(int)*(nCol+1));
}
if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pQuery) ){
IdxHashEntry *pEntry;
char *zStat = 0;
for(i=0; i<=nCol; i++) aStat[i] = 1;
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pQuery) ){
aStat[0]++;
for(i=0; i<nCol; i++){
if( sqlite3_column_int(pQuery, i)==0 ) break;
}
for(; i<nCol; i++){
aStat[i+1]++;
}
}
if( rc==SQLITE_OK ){
int s0 = aStat[0];
zStat = sqlite3_mprintf("%d", s0);
if( zStat==0 ) rc = SQLITE_NOMEM;
for(i=1; rc==SQLITE_OK && i<=nCol; i++){
zStat = idxAppendText(&rc, zStat, " %d", (s0+aStat[i]/2) / aStat[i]);
}
}
if( rc==SQLITE_OK ){
sqlite3_bind_text(pWriteStat, 1, zTab, -1, SQLITE_STATIC);
sqlite3_bind_text(pWriteStat, 2, zIdx, -1, SQLITE_STATIC);
sqlite3_bind_text(pWriteStat, 3, zStat, -1, SQLITE_STATIC);
sqlite3_step(pWriteStat);
rc = sqlite3_reset(pWriteStat);
}
pEntry = idxHashFind(&p->hIdx, zIdx, STRLEN(zIdx));
if( pEntry ){
assert( pEntry->zVal2==0 );
pEntry->zVal2 = zStat;
}else{
sqlite3_free(zStat);
}
}
sqlite3_free(aStat);
idxFinalize(&rc, pQuery);
return rc;
}
static int idxBuildSampleTable(sqlite3expert *p, const char *zTab){
int rc;
char *zSql;
rc = sqlite3_exec(p->dbv,"DROP TABLE IF EXISTS temp."UNIQUE_TABLE_NAME,0,0,0);
if( rc!=SQLITE_OK ) return rc;
zSql = sqlite3_mprintf(
"CREATE TABLE temp." UNIQUE_TABLE_NAME " AS SELECT * FROM %Q", zTab
);
if( zSql==0 ) return SQLITE_NOMEM;
rc = sqlite3_exec(p->dbv, zSql, 0, 0, 0);
sqlite3_free(zSql);
return rc;
}
static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
int rc = SQLITE_OK;
int nMax =0;
struct IdxRemCtx *pCtx = 0;
struct IdxSampleCtx samplectx;
int i;
i64 iPrev = -100000;
sqlite3_stmt *pAllIndex = 0;
sqlite3_stmt *pIndexXInfo = 0;
sqlite3_stmt *pWrite = 0;
const char *zAllIndex =
"SELECT s.rowid, s.name, l.name FROM "
" sqlite_schema AS s, "
" pragma_index_list(s.name) AS l "
"WHERE s.type = 'table'";
const char *zIndexXInfo =
"SELECT name, coll FROM pragma_index_xinfo(?) WHERE key";
const char *zWrite = "INSERT INTO sqlite_stat1 VALUES(?, ?, ?)";
if( p->iSample==0 ) return SQLITE_OK;
rc = idxLargestIndex(p->dbm, &nMax, pzErr);
if( nMax<=0 || rc!=SQLITE_OK ) return rc;
rc = sqlite3_exec(p->dbm, "ANALYZE; PRAGMA writable_schema=1", 0, 0, 0);
if( rc==SQLITE_OK ){
int nByte = sizeof(struct IdxRemCtx) + (sizeof(struct IdxRemSlot) * nMax);
pCtx = (struct IdxRemCtx*)idxMalloc(&rc, nByte);
}
if( rc==SQLITE_OK ){
sqlite3 *dbrem = (p->iSample==100 ? p->db : p->dbv);
rc = sqlite3_create_function(
dbrem, "rem", 2, SQLITE_UTF8, (void*)pCtx, idxRemFunc, 0, 0
);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(
p->db, "sample", 0, SQLITE_UTF8, (void*)&samplectx, idxSampleFunc, 0, 0
);
}
if( rc==SQLITE_OK ){
pCtx->nSlot = nMax+1;
rc = idxPrepareStmt(p->dbm, &pAllIndex, pzErr, zAllIndex);
}
if( rc==SQLITE_OK ){
rc = idxPrepareStmt(p->dbm, &pIndexXInfo, pzErr, zIndexXInfo);
}
if( rc==SQLITE_OK ){
rc = idxPrepareStmt(p->dbm, &pWrite, pzErr, zWrite);
}
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pAllIndex) ){
i64 iRowid = sqlite3_column_int64(pAllIndex, 0);
const char *zTab = (const char*)sqlite3_column_text(pAllIndex, 1);
const char *zIdx = (const char*)sqlite3_column_text(pAllIndex, 2);
if( zTab==0 || zIdx==0 ) continue;
if( p->iSample<100 && iPrev!=iRowid ){
samplectx.target = (double)p->iSample / 100.0;
samplectx.iTarget = p->iSample;
samplectx.nRow = 0.0;
samplectx.nRet = 0.0;
rc = idxBuildSampleTable(p, zTab);
if( rc!=SQLITE_OK ) break;
}
rc = idxPopulateOneStat1(p, pIndexXInfo, pWrite, zTab, zIdx, pzErr);
iPrev = iRowid;
}
if( rc==SQLITE_OK && p->iSample<100 ){
rc = sqlite3_exec(p->dbv,
"DROP TABLE IF EXISTS temp." UNIQUE_TABLE_NAME, 0,0,0
);
}
idxFinalize(&rc, pAllIndex);
idxFinalize(&rc, pIndexXInfo);
idxFinalize(&rc, pWrite);
if( pCtx ){
for(i=0; i<pCtx->nSlot; i++){
sqlite3_free(pCtx->aSlot[i].z);
}
sqlite3_free(pCtx);
}
if( rc==SQLITE_OK ){
rc = sqlite3_exec(p->dbm, "ANALYZE sqlite_schema", 0, 0, 0);
}
sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp."UNIQUE_TABLE_NAME,0,0,0);
return rc;
}
sqlite3expert *sqlite3_expert_new(sqlite3 *db, char **pzErrmsg){
int rc = SQLITE_OK;
sqlite3expert *pNew;
pNew = (sqlite3expert*)idxMalloc(&rc, sizeof(sqlite3expert));
if( rc==SQLITE_OK ){
pNew->db = db;
pNew->iSample = 100;
rc = sqlite3_open(":memory:", &pNew->dbv);
}
if( rc==SQLITE_OK ){
rc = sqlite3_open(":memory:", &pNew->dbm);
if( rc==SQLITE_OK ){
sqlite3_db_config(pNew->dbm, SQLITE_DBCONFIG_TRIGGER_EQP, 1, (int*)0);
}
}
if( rc==SQLITE_OK ){
sqlite3_stmt *pSql = 0;
rc = idxPrintfPrepareStmt(pNew->db, &pSql, pzErrmsg,
"SELECT sql FROM sqlite_schema WHERE name NOT LIKE 'sqlite_%%'"
" AND sql NOT LIKE 'CREATE VIRTUAL %%'"
);
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
const char *zSql = (const char*)sqlite3_column_text(pSql, 0);
if( zSql ) rc = sqlite3_exec(pNew->dbm, zSql, 0, 0, pzErrmsg);
}
idxFinalize(&rc, pSql);
}
if( rc==SQLITE_OK ){
rc = idxCreateVtabSchema(pNew, pzErrmsg);
}
if( rc==SQLITE_OK ){
sqlite3_set_authorizer(pNew->dbv, idxAuthCallback, (void*)pNew);
}
if( rc!=SQLITE_OK ){
sqlite3_expert_destroy(pNew);
pNew = 0;
}
return pNew;
}
int sqlite3_expert_config(sqlite3expert *p, int op, ...){
int rc = SQLITE_OK;
va_list ap;
va_start(ap, op);
switch( op ){
case EXPERT_CONFIG_SAMPLE: {
int iVal = va_arg(ap, int);
if( iVal<0 ) iVal = 0;
if( iVal>100 ) iVal = 100;
p->iSample = iVal;
break;
}
default:
rc = SQLITE_NOTFOUND;
break;
}
va_end(ap);
return rc;
}
int sqlite3_expert_sql(
sqlite3expert *p,
const char *zSql,
char **pzErr
){
IdxScan *pScanOrig = p->pScan;
IdxStatement *pStmtOrig = p->pStatement;
int rc = SQLITE_OK;
const char *zStmt = zSql;
if( p->bRun ) return SQLITE_MISUSE;
while( rc==SQLITE_OK && zStmt && zStmt[0] ){
sqlite3_stmt *pStmt = 0;
rc = sqlite3_prepare_v2(p->dbv, zStmt, -1, &pStmt, &zStmt);
if( rc==SQLITE_OK ){
if( pStmt ){
IdxStatement *pNew;
const char *z = sqlite3_sql(pStmt);
int n = STRLEN(z);
pNew = (IdxStatement*)idxMalloc(&rc, sizeof(IdxStatement) + n+1);
if( rc==SQLITE_OK ){
pNew->zSql = (char*)&pNew[1];
memcpy(pNew->zSql, z, n+1);
pNew->pNext = p->pStatement;
if( p->pStatement ) pNew->iId = p->pStatement->iId+1;
p->pStatement = pNew;
}
sqlite3_finalize(pStmt);
}
}else{
idxDatabaseError(p->dbv, pzErr);
}
}
if( rc!=SQLITE_OK ){
idxScanFree(p->pScan, pScanOrig);
idxStatementFree(p->pStatement, pStmtOrig);
p->pScan = pScanOrig;
p->pStatement = pStmtOrig;
}
return rc;
}
int sqlite3_expert_analyze(sqlite3expert *p, char **pzErr){
int rc;
IdxHashEntry *pEntry;
rc = idxProcessTriggers(p, pzErr);
if( rc==SQLITE_OK ){
rc = idxCreateCandidates(p);
}else if ( rc==SQLITE_BUSY_TIMEOUT ){
if( pzErr )
*pzErr = sqlite3_mprintf("Cannot find a unique index name to propose.");
return rc;
}
if( rc==SQLITE_OK ){
rc = idxPopulateStat1(p, pzErr);
}
for(pEntry=p->hIdx.pFirst; pEntry; pEntry=pEntry->pNext){
p->zCandidates = idxAppendText(&rc, p->zCandidates,
"%s;%s%s\n", pEntry->zVal,
pEntry->zVal2 ? " -- stat1: " : "", pEntry->zVal2
);
}
if( rc==SQLITE_OK ){
rc = idxFindIndexes(p, pzErr);
}
if( rc==SQLITE_OK ){
p->bRun = 1;
}
return rc;
}
int sqlite3_expert_count(sqlite3expert *p){
int nRet = 0;
if( p->pStatement ) nRet = p->pStatement->iId+1;
return nRet;
}
const char *sqlite3_expert_report(sqlite3expert *p, int iStmt, int eReport){
const char *zRet = 0;
IdxStatement *pStmt;
if( p->bRun==0 ) return 0;
for(pStmt=p->pStatement; pStmt && pStmt->iId!=iStmt; pStmt=pStmt->pNext);
switch( eReport ){
case EXPERT_REPORT_SQL:
if( pStmt ) zRet = pStmt->zSql;
break;
case EXPERT_REPORT_INDEXES:
if( pStmt ) zRet = pStmt->zIdx;
break;
case EXPERT_REPORT_PLAN:
if( pStmt ) zRet = pStmt->zEQP;
break;
case EXPERT_REPORT_CANDIDATES:
zRet = p->zCandidates;
break;
}
return zRet;
}
void sqlite3_expert_destroy(sqlite3expert *p){
if( p ){
sqlite3_close(p->dbm);
sqlite3_close(p->dbv);
idxScanFree(p->pScan, 0);
idxStatementFree(p->pStatement, 0);
idxTableFree(p->pTable);
idxWriteFree(p->pWrite);
idxHashClear(&p->hIdx);
sqlite3_free(p->zCandidates);
sqlite3_free(p);
}
}
#endif