]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Merge latest trunk changes into this branch.
authordan <dan@noemail.net>
Thu, 20 Apr 2017 17:35:46 +0000 (17:35 +0000)
committerdan <dan@noemail.net>
Thu, 20 Apr 2017 17:35:46 +0000 (17:35 +0000)
FossilOrigin-Name: b1533bc455f52f570c0f4b8aaa0da802757dc89b0e45b9a9b31aa591a44bf7bd

1  2 
ext/expert/sqlite3expert.c
manifest
manifest.uuid
src/pragma.c
src/vdbe.c
src/where.c
test/permutations.test

index 3ee74b5710383a018a26a9ff3e90679403f62b88,0000000000000000000000000000000000000000..8cacc9d706c3a02730cc012c330582677f15d4db
mode 100644,000000..100644
--- /dev/null
@@@ -1,1930 -1,0 +1,1938 @@@
 +/*
 +** 2017 April 09
 +**
 +** The author disclaims copyright to this source code.  In place of
 +** a legal notice, here is a blessing:
 +**
 +**    May you do good and not evil.
 +**    May you find forgiveness for yourself and forgive others.
 +**    May you share freely, never taking more than you give.
 +**
 +*************************************************************************
 +*/
 +
 +#if !defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHEREINFO_HOOK)
 +
 +#include "sqlite3expert.h"
 +#include <assert.h>
 +#include <string.h>
 +#include <stdio.h>
 +
 +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;
 +
++/*
++** A temp table name that we assume no user database will actually use.
++** If this assumption proves incorrect triggers on the table with the
++** conflicting name will be ignored.
++*/
 +#define UNIQUE_TABLE_NAME "t592690916721053953805701627921227776"
 +
 +/*
 +** A single constraint. Equivalent to either "col = ?" or "col < ?" (or
 +** any other type of single-ended range constraint on a column).
 +**
 +** pLink:
 +**   Used to temporarily link IdxConstraint objects into lists while
 +**   creating candidate indexes.
 +*/
 +struct IdxConstraint {
 +  char *zColl;                    /* Collation sequence */
 +  int bRange;                     /* True for range, false for eq */
 +  int iCol;                       /* Constrained table column */
 +  int bFlag;                      /* Used by idxFindCompatible() */
 +  int bDesc;                      /* True if ORDER BY <expr> DESC */
 +  IdxConstraint *pNext;           /* Next constraint in pEq or pRange list */
 +  IdxConstraint *pLink;           /* See above */
 +};
 +
 +/*
 +** A single scan of a single table.
 +*/
 +struct IdxScan {
 +  IdxTable *pTab;                 /* Associated table object */
 +  int iDb;                        /* Database containing table zTable */
 +  i64 covering;                   /* Mask of columns required for cov. index */
 +  IdxConstraint *pOrder;          /* ORDER BY columns */
 +  IdxConstraint *pEq;             /* List of == constraints */
 +  IdxConstraint *pRange;          /* List of < constraints */
 +  IdxScan *pNextScan;             /* Next IdxScan object for same analysis */
 +};
 +
 +/*
 +** Information regarding a single database table. Extracted from 
 +** "PRAGMA table_info" by function idxGetTableInfo().
 +*/
 +struct IdxColumn {
 +  char *zName;
 +  char *zColl;
 +  int iPk;
 +};
 +struct IdxTable {
 +  int nCol;
 +  char *zName;                    /* Table name */
 +  IdxColumn *aCol;
 +  IdxTable *pNext;                /* Next table in linked list of all tables */
 +};
 +
 +/*
 +** An object of the following type is created for each unique table/write-op
 +** seen. The objects are stored in a singly-linked list beginning at
 +** sqlite3expert.pWrite.
 +*/
 +struct IdxWrite {
 +  IdxTable *pTab;
 +  int eOp;                        /* SQLITE_UPDATE, DELETE or INSERT */
 +  IdxWrite *pNext;
 +};
 +
 +/*
 +** Each statement being analyzed is represented by an instance of this
 +** structure.
 +*/
 +struct IdxStatement {
 +  int iId;                        /* Statement number */
 +  char *zSql;                     /* SQL statement */
 +  char *zIdx;                     /* Indexes */
 +  char *zEQP;                     /* Plan */
 +  IdxStatement *pNext;
 +};
 +
 +
 +/*
 +** A hash table for storing strings. With space for a payload string
 +** with each entry. Methods are:
 +**
 +**   idxHashInit()
 +**   idxHashClear()
 +**   idxHashAdd()
 +**   idxHashSearch()
 +*/
 +#define IDX_HASH_SIZE 1023
 +typedef struct IdxHashEntry IdxHashEntry;
 +typedef struct IdxHash IdxHash;
 +struct IdxHashEntry {
 +  char *zKey;                     /* nul-terminated key */
 +  char *zVal;                     /* nul-terminated value string */
 +  char *zVal2;                    /* nul-terminated value string 2 */
 +  IdxHashEntry *pHashNext;        /* Next entry in same hash bucket */
 +  IdxHashEntry *pNext;            /* Next entry in hash */
 +};
 +struct IdxHash {
 +  IdxHashEntry *pFirst;
 +  IdxHashEntry *aHash[IDX_HASH_SIZE];
 +};
 +
 +/*
 +** sqlite3expert object.
 +*/
 +struct sqlite3expert {
 +  int iSample;                    /* Percentage of tables to sample for stat1 */
 +  sqlite3 *db;                    /* User database */
 +  sqlite3 *dbm;                   /* In-memory db for this analysis */
 +  sqlite3 *dbv;                   /* Vtab schema for this analysis */
 +  IdxTable *pTable;               /* List of all IdxTable objects */
 +  IdxScan *pScan;                 /* List of scan objects */
 +  IdxWrite *pWrite;               /* List of write objects */
 +  IdxStatement *pStatement;       /* List of IdxStatement objects */
 +  int bRun;                       /* True once analysis has run */
 +  char **pzErrmsg;
 +  int rc;                         /* Error code from whereinfo hook */
 +  IdxHash hIdx;                   /* Hash containing all candidate indexes */
 +  char *zCandidates;              /* For EXPERT_REPORT_CANDIDATES */
 +};
 +
 +
 +/*
 +** Allocate and return nByte bytes of zeroed memory using sqlite3_malloc(). 
 +** If the allocation fails, set *pRc to SQLITE_NOMEM and return NULL.
 +*/
 +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;
 +}
 +
 +/*
 +** Initialize an IdxHash hash table.
 +*/
 +static void idxHashInit(IdxHash *pHash){
 +  memset(pHash, 0, sizeof(IdxHash));
 +}
 +
 +/*
 +** Reset an IdxHash hash table.
 +*/
 +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));
 +}
 +
 +/*
 +** Return the index of the hash bucket that the string specified by the
 +** arguments to this function belongs.
 +*/
 +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);
 +}
 +
 +/*
 +** If zKey is already present in the hash table, return non-zero and do
 +** nothing. Otherwise, add an entry with key zKey and payload string zVal to
 +** the hash table passed as the second argument. 
 +*/
 +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;
 +}
 +
++/*
++** If zKey/nKey is present in the hash table, return a pointer to the 
++** hash-entry object.
++*/
 +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;
 +}
 +
 +/*
 +** If the hash table contains an entry with a key equal to the string
 +** passed as the final two arguments to this function, return a pointer
 +** to the payload string. Otherwise, if zKey/nKey is not present in the
 +** hash table, return NULL.
 +*/
 +static const char *idxHashSearch(IdxHash *pHash, const char *zKey, int nKey){
 +  IdxHashEntry *pEntry = idxHashFind(pHash, zKey, nKey);
 +  if( pEntry ) return pEntry->zVal;
 +  return 0;
 +}
 +
 +/*
 +** Allocate and return a new IdxConstraint object. Set the IdxConstraint.zColl
 +** variable to point to a copy of nul-terminated string zColl.
 +*/
 +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;
 +}
 +
 +/*
 +** An error associated with database handle db has just occurred. Pass
 +** the error message to callback function xOut.
 +*/
 +static void idxDatabaseError(
 +  sqlite3 *db,                    /* Database handle */
 +  char **pzErrmsg                 /* Write error here */
 +){
 +  *pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
 +}
 +
 +/*
 +** Prepare an SQL statement.
 +*/
 +static int idxPrepareStmt(
 +  sqlite3 *db,                    /* Database handle to compile against */
 +  sqlite3_stmt **ppStmt,          /* OUT: Compiled SQL statement */
 +  char **pzErrmsg,                /* OUT: sqlite3_malloc()ed error message */
 +  const char *zSql                /* SQL statement to compile */
 +){
 +  int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0);
 +  if( rc!=SQLITE_OK ){
 +    *ppStmt = 0;
 +    idxDatabaseError(db, pzErrmsg);
 +  }
 +  return rc;
 +}
 +
 +/*
 +** Prepare an SQL statement using the results of a printf() formatting.
 +*/
 +static int idxPrintfPrepareStmt(
 +  sqlite3 *db,                    /* Database handle to compile against */
 +  sqlite3_stmt **ppStmt,          /* OUT: Compiled SQL statement */
 +  char **pzErrmsg,                /* OUT: sqlite3_malloc()ed error message */
 +  const char *zFmt,               /* printf() format of SQL statement */
 +  ...                             /* Trailing printf() arguments */
 +){
 +  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;
 +}
 +
 +
 +/*************************************************************************
 +** Beginning of virtual table implementation.
 +*/
 +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;
 +}
 +
 +/* 
 +** This function is the implementation of both the xConnect and xCreate
 +** methods of the r-tree virtual table.
 +**
 +**   argv[0]   -> module name
 +**   argv[1]   -> database name
 +**   argv[2]   -> table name
 +**   argv[...] -> column names...
 +*/
 +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;
 +  sqlite3 *dbv = p->pExpert->dbv;
 +  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;
 +
 +    /* Link the new scan object into the list */
 +    pScan->pTab = p->pTab;
 +    pScan->pNextScan = p->pExpert->pScan;
 +    p->pExpert->pScan = pScan;
 +
 +    /* Add the constraints to the IdxScan object */
 +    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(dbv, 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;
 +      }
 +    }
 +
 +    /* Add the ORDER BY to the IdxScan object */
 +    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;
 +  return rc;
 +}
 +
 +static int expertUpdate(
 +  sqlite3_vtab *pVtab, 
 +  int nData, 
 +  sqlite3_value **azData, 
 +  sqlite_int64 *pRowid
 +){
 +  return SQLITE_OK;
 +}
 +
 +/* 
 +** Virtual table module xOpen method.
 +*/
 +static int expertOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
 +  int rc = SQLITE_OK;
 +  ExpertCsr *pCsr;
 +  pCsr = idxMalloc(&rc, sizeof(ExpertCsr));
 +  *ppCursor = (sqlite3_vtab_cursor*)pCsr;
 +  return rc;
 +}
 +
 +/* 
 +** Virtual table module xClose method.
 +*/
 +static int expertClose(sqlite3_vtab_cursor *cur){
 +  ExpertCsr *pCsr = (ExpertCsr*)cur;
 +  sqlite3_finalize(pCsr->pData);
 +  sqlite3_free(pCsr);
 +  return SQLITE_OK;
 +}
 +
 +/*
 +** Virtual table module xEof method.
 +**
 +** Return non-zero if the cursor does not currently point to a valid 
 +** record (i.e if the scan has finished), or zero otherwise.
 +*/
 +static int expertEof(sqlite3_vtab_cursor *cur){
 +  ExpertCsr *pCsr = (ExpertCsr*)cur;
 +  return pCsr->pData==0;
 +}
 +
 +/* 
 +** Virtual table module xNext method.
 +*/
 +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;
 +}
 +
 +/* 
 +** Virtual table module xRowid method.
 +*/
 +static int expertRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
 +  *pRowid = 0;
 +  return SQLITE_OK;
 +}
 +
 +/* 
 +** Virtual table module xColumn method.
 +*/
 +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;
 +}
 +
 +/* 
 +** Virtual table module xFilter method.
 +*/
 +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;
 +
 +  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,                            /* iVersion */
 +    expertConnect,                /* xCreate - create a table */
 +    expertConnect,                /* xConnect - connect to an existing table */
 +    expertBestIndex,              /* xBestIndex - Determine search strategy */
 +    expertDisconnect,             /* xDisconnect - Disconnect from a table */
 +    expertDisconnect,             /* xDestroy - Drop a table */
 +    expertOpen,                   /* xOpen - open a cursor */
 +    expertClose,                  /* xClose - close a cursor */
 +    expertFilter,                 /* xFilter - configure scan constraints */
 +    expertNext,                   /* xNext - advance a cursor */
 +    expertEof,                    /* xEof */
 +    expertColumn,                 /* xColumn - read data */
 +    expertRowid,                  /* xRowid - read data */
 +    expertUpdate,                 /* xUpdate - write data */
 +    0,                            /* xBegin - begin transaction */
 +    0,                            /* xSync - sync transaction */
 +    0,                            /* xCommit - commit transaction */
 +    0,                            /* xRollback - rollback transaction */
 +    0,                            /* xFindFunction - function overloading */
 +    0,                            /* xRename - rename the table */
 +    0,                            /* xSavepoint */
 +    0,                            /* xRelease */
 +    0,                            /* xRollbackTo */
 +  };
 +
 +  return sqlite3_create_module(p->dbv, "expert", &expertModule, (void*)p);
 +}
 +/*
 +** End of virtual table implementation.
 +*************************************************************************/
 +/*
 +** Finalize SQL statement pStmt. If (*pRc) is SQLITE_OK when this function
 +** is called, set it to the return value of sqlite3_finalize() before
 +** returning. Otherwise, discard the sqlite3_finalize() return value.
 +*/
 +static void idxFinalize(int *pRc, sqlite3_stmt *pStmt){
 +  int rc = sqlite3_finalize(pStmt);
 +  if( *pRc==SQLITE_OK ) *pRc = rc;
 +}
 +
 +/*
 +** Attempt to allocate an IdxTable structure corresponding to table zTab
 +** in the main database of connection db. If successful, set (*ppOut) to
 +** point to the new object and return SQLITE_OK. Otherwise, return an
 +** SQLite error code and set (*ppOut) to NULL. In this case *pzErrmsg may be
 +** set to point to an error string.
 +**
 +** It is the responsibility of the caller to eventually free either the
 +** IdxTable object or error message using sqlite3_free().
 +*/
 +static int idxGetTableInfo(
 +  sqlite3 *db,                    /* Database connection to read details from */
 +  const char *zTab,               /* Table name */
 +  IdxTable **ppOut,               /* OUT: New object (if successful) */
 +  char **pzErrmsg                 /* OUT: Error message (if not) */
 +){
 +  sqlite3_stmt *p1 = 0;
 +  int nCol = 0;
 +  int nTab = strlen(zTab);
 +  int nByte = sizeof(IdxTable) + nTab + 1;
 +  IdxTable *pNew = 0;
 +  int rc, rc2;
 +  char *pCsr = 0;
 +
 +  rc = idxPrintfPrepareStmt(db, &p1, pzErrmsg, "PRAGMA table_info=%Q", zTab);
 +  while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(p1) ){
 +    const char *zCol = (const char*)sqlite3_column_text(p1, 1);
 +    nByte += 1 + strlen(zCol);
 +    rc = sqlite3_table_column_metadata(
 +        db, "main", zTab, zCol, 0, &zCol, 0, 0, 0
 +    );
 +    nByte += 1 + strlen(zCol);
 +    nCol++;
 +  }
 +  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);
 +    int nCopy = strlen(zCol) + 1;
 +    pNew->aCol[nCol].zName = pCsr;
 +    pNew->aCol[nCol].iPk = sqlite3_column_int(p1, 5);
 +    memcpy(pCsr, zCol, nCopy);
 +    pCsr += nCopy;
 +
 +    rc = sqlite3_table_column_metadata(
 +        db, "main", zTab, zCol, 0, &zCol, 0, 0, 0
 +    );
 +    if( rc==SQLITE_OK ){
 +      nCopy = strlen(zCol) + 1;
 +      pNew->aCol[nCol].zColl = pCsr;
 +      memcpy(pCsr, zCol, nCopy);
 +      pCsr += nCopy;
 +    }
 +
 +    nCol++;
 +  }
 +  idxFinalize(&rc, p1);
 +
 +  if( rc!=SQLITE_OK ){
 +    sqlite3_free(pNew);
 +    pNew = 0;
 +  }else{
 +    pNew->zName = pCsr;
 +    memcpy(pNew->zName, zTab, nTab+1);
 +  }
 +
 +  *ppOut = pNew;
 +  return rc;
 +}
 +
 +/*
 +** This function is a no-op if *pRc is set to anything other than 
 +** SQLITE_OK when it is called.
 +**
 +** If *pRc is initially set to SQLITE_OK, then the text specified by
 +** the printf() style arguments is appended to zIn and the result returned
 +** in a buffer allocated by sqlite3_malloc(). sqlite3_free() is called on
 +** zIn before returning.
 +*/
 +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 ){
 +      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;
 +}
 +
 +/*
 +** Return true if zId must be quoted in order to use it as an SQL
 +** identifier, or false otherwise.
 +*/
 +static int idxIdentifierRequiresQuotes(const char *zId){
 +  int i;
 +  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;
 +}
 +
 +/*
 +** This function appends an index column definition suitable for constraint
 +** pCons to the string passed as zIn and returns the result.
 +*/
 +static char *idxAppendColDefn(
 +  int *pRc,                       /* IN/OUT: Error code */
 +  char *zIn,                      /* Column defn accumulated so far */
 +  IdxTable *pTab,                 /* Table index will be created on */
 +  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;
 +}
 +
 +/*
 +** Search database dbm for an index compatible with the one idxCreateFromCons()
 +** would create from arguments pScan, pEq and pTail. If no error occurs and 
 +** such an index is found, return non-zero. Or, if no such index is found,
 +** return zero.
 +**
 +** If an error occurs, set *pRc to an SQLite error code and return zero.
 +*/
 +static int idxFindCompatible(
 +  int *pRc,                       /* OUT: Error code */
 +  sqlite3* dbm,                   /* Database to search */
 +  IdxScan *pScan,                 /* Scan for table to search for index on */
 +  IdxConstraint *pEq,             /* List of == constraints */
 +  IdxConstraint *pTail            /* List of range constraints */
 +){
 +  const char *zTbl = pScan->pTab->zName;
 +  sqlite3_stmt *pIdxList = 0;
 +  IdxConstraint *pIter;
 +  int nEq = 0;                    /* Number of elements in pEq */
 +  int rc;
 +
 +  /* Count the elements in list pEq */
 +  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);
 +
 +    /* Zero the IdxConstraint.bFlag values in the pEq list */
 +    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 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;
 +    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 ){
 +      /* Hash the list of columns to come up with a name for the index */
 +      const char *zTable = pScan->pTab->zName;
 +      char *zName;                /* Index name */
 +      int i;
 +      for(i=0; zCols[i]; i++){
 +        h += ((h<<3) + zCols[i]);
 +      }
 +      zName = sqlite3_mprintf("%s_idx_%08x", zTable, h);
 +      if( zName==0 ){ 
 +        rc = SQLITE_NOMEM;
 +      }else{
 +        if( idxIdentifierRequiresQuotes(zTable) ){
 +          zFmt = "CREATE INDEX '%q' ON %Q(%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);
 +          idxHashAdd(&rc, &p->hIdx, zName, zIdx);
 +        }
 +        sqlite3_free(zName);
 +        sqlite3_free(zIdx);
 +      }
 +    }
 +
 +    sqlite3_free(zCols);
 +  }
 +  return rc;
 +}
 +
 +/*
 +** Return true if list pList (linked by IdxConstraint.pLink) contains
 +** a constraint compatible with *p. Otherwise return false.
 +*/
 +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,                 /* Create indexes for this scan */
 +  IdxConstraint *pTail            /* range/ORDER BY constraints for inclusion */
 +){
 +  IdxConstraint *p1 = 0;
 +  IdxConstraint *pCon;
 +  int rc;
 +
 +  /* Gather up all the == constraints. */
 +  for(pCon=pScan->pEq; pCon; pCon=pCon->pNext){
 +    if( !idxFindConstraint(p1, pCon) && !idxFindConstraint(pTail, pCon) ){
 +      pCon->pLink = p1;
 +      p1 = pCon;
 +    }
 +  }
 +
 +  /* Create an index using the == constraints collected above. And the
 +  ** range constraint/ORDER BY terms passed in by the caller, if any. */
 +  rc = idxCreateFromCons(p, pScan, p1, pTail);
 +
 +  /* If no range/ORDER BY passed by the caller, create a version of the
 +  ** index for each range constraint.  */
 +  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;
 +}
 +
 +/*
 +** Create candidate indexes in database [dbm] based on the data in 
 +** linked-list pScan.
 +*/
 +static int idxCreateCandidates(sqlite3expert *p, char **pzErr){
 +  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;
 +}
 +
 +/*
 +** Free all elements of the linked list starting at pConstraint.
 +*/
 +static void idxConstraintFree(IdxConstraint *pConstraint){
 +  IdxConstraint *pNext;
 +  IdxConstraint *p;
 +
 +  for(p=pConstraint; p; p=pNext){
 +    pNext = p->pNext;
 +    sqlite3_free(p);
 +  }
 +}
 +
 +/*
 +** Free all elements of the linked list starting from pScan up until pLast
 +** (pLast is not freed).
 +*/
 +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);
 +  }
 +}
 +
 +/*
 +** Free all elements of the linked list starting from pStatement up 
 +** until pLast (pLast is not freed).
 +*/
 +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);
 +  }
 +}
 +
 +/*
 +** Free the linked list of IdxTable objects starting at pTab.
 +*/
 +static void idxTableFree(IdxTable *pTab){
 +  IdxTable *pIter;
 +  IdxTable *pNext;
 +  for(pIter=pTab; pIter; pIter=pNext){
 +    pNext = pIter->pNext;
 +    sqlite3_free(pIter);
 +  }
 +}
 +
 +/*
 +** Free the linked list of IdxWrite objects starting at pTab.
 +*/
 +static void idxWriteFree(IdxWrite *pTab){
 +  IdxWrite *pIter;
 +  IdxWrite *pNext;
 +  for(pIter=pTab; pIter; pIter=pNext){
 +    pNext = pIter->pNext;
 +    sqlite3_free(pIter);
 +  }
 +}
 +
 +
 +
 +/*
 +** This function is called after candidate indexes have been created. It
 +** runs all the queries to see which indexes they prefer, and populates
 +** IdxStatement.zIdx and IdxStatement.zEQP with the results.
 +*/
 +int idxFindIndexes(
 +  sqlite3expert *p,
 +  char **pzErr                         /* OUT: Error message (sqlite3_malloc) */
 +){
 +  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 ){
 +      int iSelectid = sqlite3_column_int(pExplain, 0);
 +      int iOrder = sqlite3_column_int(pExplain, 1);
 +      int iFrom = sqlite3_column_int(pExplain, 2);
 +      const char *zDetail = (const char*)sqlite3_column_text(pExplain, 3);
 +      int nDetail = strlen(zDetail);
 +      int i;
 +
 +      for(i=0; i<nDetail; i++){
 +        const char *zIdx = 0;
 +        if( memcmp(&zDetail[i], " USING INDEX ", 13)==0 ){
 +          zIdx = &zDetail[i+13];
 +        }else if( 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;
 +        }
 +      }
 +
 +      pStmt->zEQP = idxAppendText(&rc, pStmt->zEQP, "%d|%d|%d|%s\n", 
 +          iSelectid, iOrder, iFrom, 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;
 +  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_master "
 +    "WHERE tbl_name = %Q AND type IN ('table', 'trigger') "
 +    "ORDER BY type;";
 +  sqlite3_stmt *pSelect = 0;
 +  int rc = SQLITE_OK;
 +  char *zWrite = 0;
 +
 +  /* Create the table and its triggers in the temp schema */
 +  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);
 +    rc = sqlite3_exec(p->dbv, zCreate, 0, 0, pzErr);
 +  }
 +  idxFinalize(&rc, pSelect);
 +
 +  /* Rename the table in the temp schema to zInt */
 +  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;
 +
 +  /* For each table in the main db schema:
 +  **
 +  **   1) Add an entry to the p->pTable list, and
 +  **   2) Create the equivalent virtual table in dbv.
 +  */
 +  rc = idxPrepareStmt(p->db, &pSchema, pzErrmsg,
 +      "SELECT type, name, sql, 1 FROM sqlite_master "
 +      "WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%%' "
 +      " UNION ALL "
 +      "SELECT type, name, sql, 2 FROM sqlite_master "
 +      "WHERE type = 'trigger'"
 +      "  AND tbl_name IN(SELECT name FROM sqlite_master 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]=='v' || zType[1]=='r' ){
 +      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;
 +
 +        /* The statement the vtab will pass to sqlite3_declare_vtab() */
 +        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, ")");
 +
 +        /* The CVT statement to create the vtab */
 +        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;                  /* Target nRet/nRow value */
 +  double nRow;                    /* Number of rows seen */
 +  double nRet;                    /* Number of rows returned */
 +};
 +
 +static void idxSampleFunc(
 +  sqlite3_context *pCtx,
 +  int argc,
 +  sqlite3_value **argv
 +){
 +  struct IdxSampleCtx *p = (struct IdxSampleCtx*)sqlite3_user_data(pCtx);
 +  int bRet;
 +
 +  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;                    /* SQLITE_NULL, INTEGER, REAL, TEXT, BLOB */
 +    i64 iVal;                     /* SQLITE_INTEGER value */
 +    double rVal;                  /* SQLITE_FLOAT value */
 +    int nByte;                    /* Bytes of space allocated at z */
 +    int n;                        /* Size of buffer z */
 +    char *z;                      /* SQLITE_TEXT/BLOB value */
 +  } aSlot[1];
 +};
 +
 +/*
 +** Implementation of scalar function rem().
 +*/
 +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:
 +      /* no-op */
 +      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:
 +      /* no-op */
 +      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]);
 +      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 ){
 +        memcpy(pSlot->z, sqlite3_value_blob(argv[1]), nByte);
 +      }else{
 +        memcpy(pSlot->z, sqlite3_value_text(argv[1]), nByte);
 +      }
 +      break;
 +    }
 +  }
 +}
 +
 +static int idxLargestIndex(sqlite3 *db, int *pnMax, char **pzErr){
 +  int rc = SQLITE_OK;
 +  const char *zMax = 
 +    "SELECT max(i.seqno) FROM "
 +    "  sqlite_master 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 );
 +
 +  /* Formulate the query text */
 +  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);
 +  }
 +  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);
 +
 +  /* Formulate the query text */
 +  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(/*no-op*/; 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;
 +}
 +
 +/*
 +** This function is called as part of sqlite3_expert_analyze(). Candidate
 +** indexes have already been created in database sqlite3expert.dbm, this
 +** function populates sqlite_stat1 table in the same database.
 +**
 +** The stat1 data is generated by querying the 
 +*/
 +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_master 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 iSample==0, no sqlite_stat1 data is required. */
 +  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( 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);
 +
 +  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_master", 0, 0, 0);
 +  }
 +
 +  sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp."UNIQUE_TABLE_NAME,0,0,0);
 +  return rc;
 +}
 +
 +/*
 +** Allocate a new sqlite3expert object.
 +*/
 +sqlite3expert *sqlite3_expert_new(sqlite3 *db, char **pzErrmsg){
 +  int rc = SQLITE_OK;
 +  sqlite3expert *pNew;
 +
 +  pNew = (sqlite3expert*)idxMalloc(&rc, sizeof(sqlite3expert));
 +
 +  /* Open two in-memory databases to work with. The "vtab database" (dbv)
 +  ** will contain a virtual table corresponding to each real table in
 +  ** the user database schema, and a copy of each view. It is used to
 +  ** collect information regarding the WHERE, ORDER BY and other clauses
 +  ** of the user's query.
 +  */
 +  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_FULL_EQP, 1, (int*)0);
 +    }
 +  }
 +  
 +
 +  /* Copy the entire schema of database [db] into [dbm]. */
 +  if( rc==SQLITE_OK ){
 +    sqlite3_stmt *pSql;
 +    rc = idxPrintfPrepareStmt(pNew->db, &pSql, pzErrmsg, 
 +        "SELECT sql FROM sqlite_master WHERE name NOT LIKE 'sqlite_%%'"
 +    );
 +    while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
 +      const char *zSql = (const char*)sqlite3_column_text(pSql, 0);
 +      rc = sqlite3_exec(pNew->dbm, zSql, 0, 0, pzErrmsg);
 +    }
 +    idxFinalize(&rc, pSql);
 +  }
 +
 +  /* Create the vtab schema */
 +  if( rc==SQLITE_OK ){
 +    rc = idxCreateVtabSchema(pNew, pzErrmsg);
 +  }
 +
 +  /* Register the auth callback with dbv */
 +  if( rc==SQLITE_OK ){
 +    sqlite3_set_authorizer(pNew->dbv, idxAuthCallback, (void*)pNew);
 +  }
 +
 +  /* If an error has occurred, free the new object and reutrn NULL. Otherwise,
 +  ** return the new sqlite3expert handle.  */
 +  if( rc!=SQLITE_OK ){
 +    sqlite3_expert_destroy(pNew);
 +    pNew = 0;
 +  }
 +  return pNew;
 +}
 +
 +/*
 +** Configure an sqlite3expert object.
 +*/
 +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;
 +}
 +
 +/*
 +** Add an SQL statement to the analysis.
 +*/
 +int sqlite3_expert_sql(
 +  sqlite3expert *p,               /* From sqlite3_expert_new() */
 +  const char *zSql,               /* SQL statement to add */
 +  char **pzErr                    /* OUT: Error message (if any) */
 +){
 +  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;
 +
 +  /* Do trigger processing to collect any extra IdxScan structures */
 +  rc = idxProcessTriggers(p, pzErr);
 +
 +  /* Create candidate indexes within the in-memory database file */
 +  if( rc==SQLITE_OK ){
 +    rc = idxCreateCandidates(p, pzErr);
 +  }
 +
 +  /* Generate the stat1 data */
 +  if( rc==SQLITE_OK ){
 +    rc = idxPopulateStat1(p, pzErr);
 +  }
 +
 +  /* Formulate the EXPERT_REPORT_CANDIDATES text */
 +  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
 +    );
 +  }
 +
 +  /* Figure out which of the candidate indexes are preferred by the query
 +  ** planner and report the results to the user.  */
 +  if( rc==SQLITE_OK ){
 +    rc = idxFindIndexes(p, pzErr);
 +  }
 +
 +  if( rc==SQLITE_OK ){
 +    p->bRun = 1;
 +  }
 +  return rc;
 +}
 +
 +/*
 +** Return the total number of statements that have been added to this
 +** sqlite3expert using sqlite3_expert_sql().
 +*/
 +int sqlite3_expert_count(sqlite3expert *p){
 +  int nRet = 0;
 +  if( p->pStatement ) nRet = p->pStatement->iId+1;
 +  return nRet;
 +}
 +
 +/*
 +** Return a component of the report.
 +*/
 +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;
 +}
 +
 +/*
 +** Free an sqlite3expert object.
 +*/
 +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 /* !defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHEREINFO_HOOK) */
 +
diff --cc manifest
index 81277ab60b30956ddedb2bda34312a0d01b7731d,4ba5cabb7763f5b5693f73c97af45223dd65df6f..66b5295d638fd38d0eaf2f87f617df3f5c8b8597
+++ b/manifest
@@@ -1,5 -1,5 +1,5 @@@
- C Avoid\screating\sa\stemp\stable\sin\sthe\suser\sdatabase\sin\sthe\ssqlite3_expert\scode.
- D 2017-04-20T17:03:32.652
 -C Further\simprovements\sto\scoverage\sof\sfts3\smodule.
 -D 2017-04-19T13:25:45.345
++C Merge\slatest\strunk\schanges\sinto\sthis\sbranch.
++D 2017-04-20T17:35:46.403
  F Makefile.in 1cc758ce3374a32425e4d130c2fe7b026b20de5b8843243de75f087c0a2661fb
  F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
  F Makefile.msc 6a8c838220f7c00820e1fc0ac1bccaaa8e5676067e1dbfa1bafa7a4ffecf8ae6
@@@ -40,12 -40,6 +40,12 @@@ F ext/README.md fd5f78013b0a2bc6f0067af
  F ext/async/README.txt e12275968f6fde133a80e04387d0e839b0c51f91
  F ext/async/sqlite3async.c 0f3070cc3f5ede78f2b9361fb3b629ce200d7d74
  F ext/async/sqlite3async.h f489b080af7e72aec0e1ee6f1d98ab6cf2e4dcef
- F ext/expert/sqlite3expert.c fde366d8c1b1970b2c18196ca2e64d01c2106bd9431c371a26e8d5b79f37f90b
 +F ext/expert/README.md 9f15075ec5ad772808eff55ef044c31140fd1146aa0a3c47eafd155e71851b01
 +F ext/expert/expert.c 33842ef151d84c5f8000f9c7b938998c6b999eaef7ce1f4eeb0df8ffe6739496
 +F ext/expert/expert1.test 1033e43071b69dc2f4e88fbf03fc7f18846c9865cac14f28c80f581437f09acb
++F ext/expert/sqlite3expert.c 4bc1820a70d68b478884a26a2215df8c1f2d44fb40d9cd8c47d2046c8ce0c8bc
 +F ext/expert/sqlite3expert.h af6354f8ee5c9e025024e63fec3bd640a802afcc3099a44d804752cf0791d811
 +F ext/expert/test_expert.c b01a5115f9444a9b416582c985138f5dfdb279848ce8b7452be383530be27f01
  F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
  F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b
  F ext/fts1/ft_hash.h 06df7bba40dadd19597aa400a875dbc2fed705ea
@@@ -400,19 -395,19 +401,19 @@@ F src/parse.y 0513387ce02fea97897d8caef
  F src/pcache.c 62835bed959e2914edd26afadfecce29ece0e870
  F src/pcache.h 2cedcd8407eb23017d92790b112186886e179490
  F src/pcache1.c 1195a21fe28e223e024f900b2011e80df53793f0356a24caace4188b098540dc
- F src/pragma.c 150821702fc90694b46c3432c1402fc970a4c5b8595cb13c21aeb568f9a78fc3
 -F src/pragma.c 2362670a9d28b71708aecb2b9b10b3f7be71f4c950961c07e81dc400e3ce6371
++F src/pragma.c 7fef375edafdb7ae9ba938b992aa726e18bf07b0599cfed040a088a262744b7a
  F src/pragma.h 37a1311d0388db480388d7ec09054f7103045eff20d4971f8a433b77f40b9921
 -F src/prepare.c b1140c3d0cf59bc85ace00ce363153041b424b7a
 +F src/prepare.c 7c46b5c7be9e19a1bf87777f0b7f9fb257b5ff9856c46de49f2354acfbeb4c86
  F src/printf.c 8757834f1b54dae512fb25eb1acc8e94a0d15dd2290b58f2563f65973265adb2
  F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
  F src/resolve.c 3e518b962d932a997fae373366880fc028c75706
  F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
- F src/select.c 4588dcfb0fa430012247a209ba08e17904dd32ec7690e9cb6c85e0ef012b0518
- F src/shell.c 70f4957b988572315e97c56941fdc81fd35907fee36b7b2e7be5ec4c7e9d065d
+ F src/select.c bf8ab605e49717c222136380453cfb7eda564f8e500d5ff6a01341ea59fefe80
+ F src/shell.c 21b79c0e1b93f8e35fd7b4087d6ba438326c3d7e285d0dd51dfd741475f858a1
 -F src/sqlite.h.in 40233103e3e4e10f8a63523498d0259d232e42aba478e2d3fb914799185aced6
 +F src/sqlite.h.in 900a07463a87be50b9954817f4c24a0660b4c4ddc1bfe83dedea484c6ac98425
  F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
  F src/sqlite3ext.h 58fd0676d3111d02e62e5a35992a7d3da5d3f88753acc174f2d37b774fbbdd28
 -F src/sqliteInt.h 9affb53bb405dcea1d86e85198ebaf6232a684cc2b2af6b3c181869f1c8f3e93
 +F src/sqliteInt.h 0e520ab49f019221dd5a17b6e4006523ce4f33d88b20bcf9115d11952a487c39
  F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
  F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1
  F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
@@@ -468,19 -463,19 +469,19 @@@ F src/test_windirent.h 5d67483a55442e31
  F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
  F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
  F src/tokenize.c 1003d6d90c6783206c711f0a9397656fa5b055209f4d092caa43bb3bf5215db5
- F src/treeview.c b92d57c1ac59f4a3f6b189506921a2b48098f6f4d6afd0b715bc2815ef6af092
+ F src/treeview.c 6cf8d7fe9e63fae57dad1bb57f6615e14eac0c527e43d868e805042cae8ed1f7
 -F src/trigger.c c9f0810043b265724fdb1bdd466894f984dfc182
 +F src/trigger.c 134b8e7b61317ab7b2a2dd12eb1b9aa2e23ac5bc4a05e63e35b3609b6b30a7c0
  F src/update.c c443935c652af9365e033f756550b5032d02e1b06eb2cb890ed7511ae0c051dc
  F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c
  F src/util.c ca8440ede81e155d15cff7c101654f60b55a9ae6
  F src/vacuum.c 1fe4555cd8c9b263afb85b5b4ee3a4a4181ad569
- F src/vdbe.c 314f0a70ddc29e63f131dfbe6f53c277875d3028cdf5654a709e21cab39fe0c9
 -F src/vdbe.c 1eba07ffa0e4ff5e14ceca17e366c26f58c11d12cc56e55a9a13157a93533b7e
++F src/vdbe.c 857fd5fe839b2ce4b999f4c0e17106963f0cb96f5e8ba20ebb22701267c09af2
  F src/vdbe.h f7d1456e28875c2dcb964056589b5b7149ab7edf39edeca801596a39bb3d3848
  F src/vdbeInt.h c070bc5c8b913bda0ceaa995cd4d939ded5e4fc96cf7c3c1c602d41b871f8ade
  F src/vdbeapi.c 5b08d82592bcff4470601fe78aaabebd50837860
 -F src/vdbeaux.c 6b3f6ce909e206d4c918988b13b7fa687e92b4471d137e0f2a37edac80ec60be
 +F src/vdbeaux.c 526b617ac6b5e167a6bd581e067f1ee1dbcb06e7802cff46b76fb1c02ed7d34e
  F src/vdbeblob.c 359891617358deefc85bef7bcf787fa6b77facb9
- F src/vdbemem.c 3122f5a21064198c10ee1b4686937aab27d5395712d9af905b7fa1affc47a453
+ F src/vdbemem.c 2c70f8f5de6c71fb99a22c5b83be9fab5c47cdd8e279fa44a8c00cfed06d7e89
  F src/vdbesort.c e72fe02a2121386ba767ede8942e9450878b8fc873abf3d1b6824485f092570c
  F src/vdbetrace.c 41963d5376f0349842b5fc4aaaaacd7d9cdc0834
  F src/vtab.c 007513c2ef52472fcdea6a741683d50662e82790
@@@ -488,7 -483,7 +489,7 @@@ F src/vxworks.h d2988f4e5a61a4dfe82c652
  F src/wal.c 40c543f0a2195d1b0dc88ef12142bea690009344
  F src/wal.h 06b2a0b599cc0f53ea97f497cf8c6b758c999f71
  F src/walker.c b71a992b413b3a022572eccf29ef4b4890223791
- F src/where.c d22a2ae7f823f5405e5d00c3397204ce1272d53bc3f2fc0c516315f143eb1a24
 -F src/where.c c6352f15be5031907c68bcbde96cad1a6da20e9f4051d10168a59235de9a8566
++F src/where.c a12df4e97aec78ec37e3aa92eea6d889ef3cd9ce5cfe6b682bf1243c743d897c
  F src/whereInt.h 2a4b634d63ce488b46d4b0da8f2eaa8f9aeab202bc25ef76f007de5e3fba1f20
  F src/wherecode.c 943e32e9dccd0af802e0683ae11071c8bd808364e5908a5fb66758bd404c8681
  F src/whereexpr.c e913aaa7b73ffcce66abcea5f197e2c538d48b5df78d0b7bba8ff4d73cc2e745
@@@ -1035,7 -1032,7 +1038,7 @@@ F test/parser1.test 391b9bf9a229547a129
  F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
  F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
  F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
- F test/permutations.test 9c0da2079fa37e7509957c9efbbdc282dea4ed0e732d19e6f216d53ae431a67d
 -F test/permutations.test 8aaa22a0f428a7e6b8446b97bc7691a273eaeff5dc290fb9129bf79fa9813a6e
++F test/permutations.test 1d9e247280c1e656a1f2567a263b83561a29d8c3eca6a349ae939218e82a9cfc
  F test/pragma.test 1e94755164a3a3264cd39836de4bebcb7809e5f8
  F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
  F test/pragma3.test 14c12bc5352b1e100e0b6b44f371053a81ccf8ed
@@@ -1579,7 -1575,8 +1582,7 @@@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a9
  F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
  F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
  F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
- P 4577fea5cd9d91ea241e9be82797ca1a4447f536e1e4b78a4a569aeb52e78fcb 8e57c31340dd9ffc457da63c5996fb1b573f8154f864ec2b52c15f399906ac8b
- R 73307a63b82b2606f65f28fc989fd3ae
 -P ea8a0d2ce0cb1ca3f4f18c72fb780d1c26792799acc87e6726f9eaccf2f178bf
 -Q +6417c1bbeceaea88ec1972149a1afc74fc40a677c1447160e53dbf4ba0930c42
 -R 14e4aea31c2fb4851be140b478cd2183
++P 4e366996434e63f06cc451d2011f1f1464360f03430b8260e48f78a612b4e9d6 6b21d0fdebdccfaf63590d9ca9a279c22b8baec07c1a669b9f617f25bd857384
++R 3ff876810132a5a27eb9c4a943446c06
  U dan
- Z a6caf6a2d1f5541a0691d13e2a47ea26
 -Z e5cfee329765f9813d3e00bb36cba8b5
++Z 9bcc0d98d05bcef127f4de36c5b9d364
diff --cc manifest.uuid
index 7dc88187b639aed9681ac92f4a5fb3b8ad488dcf,2d3577ec63f5cd5a11ec6e35280d3d80ec17ff4c..10e306f4491b0ba1e9c49b2f5c86770e718383e9
@@@ -1,1 -1,1 +1,1 @@@
- 4e366996434e63f06cc451d2011f1f1464360f03430b8260e48f78a612b4e9d6
 -6b21d0fdebdccfaf63590d9ca9a279c22b8baec07c1a669b9f617f25bd857384
++b1533bc455f52f570c0f4b8aaa0da802757dc89b0e45b9a9b31aa591a44bf7bd
diff --cc src/pragma.c
Simple merge
diff --cc src/vdbe.c
Simple merge
diff --cc src/where.c
Simple merge
Simple merge