| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | #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, i64 nByte){ |
| | void *pRet; |
| | assert( *pRc==SQLITE_OK ); |
| | assert( nByte>0 ); |
| | pRet = sqlite3_malloc64(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) + (i64)nKey+1 + (i64)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){ |
| | i64 n = STRLEN(zIn); |
| | char *zRet = sqlite3_malloc64(n); |
| |
|
| | assert( zIn[0]=='\'' ); |
| | assert( zIn[n-1]=='\'' ); |
| |
|
| | if( zRet ){ |
| | i64 iOut = 0; |
| | i64 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 sqlite_expert_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, |
| | 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; |
| | i64 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; |
| | i64 nIn = zIn ? STRLEN(zIn) : 0; |
| | i64 nAppend = 0; |
| | va_start(ap, zFmt); |
| | if( *pRc==SQLITE_OK ){ |
| | zAppend = sqlite3_vmprintf(zFmt, ap); |
| | if( zAppend ){ |
| | nAppend = STRLEN(zAppend); |
| | zRet = (char*)sqlite3_malloc64(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 expertDbContainsObject( |
| | sqlite3 *db, |
| | const char *zTab, |
| | int *pbContains |
| | ){ |
| | const char *zSql = "SELECT 1 FROM sqlite_schema WHERE name = ?"; |
| | sqlite3_stmt *pSql = 0; |
| | int rc = SQLITE_OK; |
| | int ret = 0; |
| |
|
| | rc = sqlite3_prepare_v2(db, zSql, -1, &pSql, 0); |
| | if( rc==SQLITE_OK ){ |
| | sqlite3_bind_text(pSql, 1, zTab, -1, SQLITE_STATIC); |
| | if( SQLITE_ROW==sqlite3_step(pSql) ){ |
| | ret = 1; |
| | } |
| | rc = sqlite3_finalize(pSql); |
| | } |
| |
|
| | *pbContains = ret; |
| | return rc; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static int expertSchemaSql(sqlite3 *db, const char *zSql, char **pzErr){ |
| | int rc = SQLITE_OK; |
| | char *zErr = 0; |
| |
|
| | rc = sqlite3_exec(db, zSql, 0, 0, &zErr); |
| | if( rc!=SQLITE_OK && zErr ){ |
| | int nErr = STRLEN(zErr); |
| | if( nErr>=15 && memcmp(zErr, "no such module:", 15)==0 ){ |
| | sqlite3_free(zErr); |
| | rc = SQLITE_OK; |
| | zErr = 0; |
| | } |
| | } |
| |
|
| | *pzErr = zErr; |
| | 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, " |
| | " substr(sql,1,14)=='create virtual' COLLATE nocase " |
| | "FROM sqlite_schema " |
| | "WHERE type IN ('table','view') AND " |
| | " substr(name,1,7)!='sqlite_' COLLATE nocase " |
| | " UNION ALL " |
| | "SELECT type, name, sql, 2, 0 FROM sqlite_schema " |
| | "WHERE type = 'trigger'" |
| | " AND tbl_name IN(SELECT name FROM sqlite_schema WHERE type = 'view') " |
| | "ORDER BY 4, 5 DESC, 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); |
| | int bVirtual = sqlite3_column_int(pSchema, 4); |
| | int bExists = 0; |
| |
|
| | if( zType==0 || zName==0 ) continue; |
| | rc = expertDbContainsObject(p->dbv, zName, &bExists); |
| | if( rc || bExists ) continue; |
| |
|
| | if( zType[0]=='v' || zType[1]=='r' || bVirtual ){ |
| | |
| | if( zSql ) rc = expertSchemaSql(p->dbv, zSql, pzErrmsg); |
| | }else{ |
| | IdxTable *pTab; |
| | rc = idxGetTableInfo(p->db, zName, &pTab, pzErrmsg); |
| | if( rc==SQLITE_OK && ALWAYS(pTab!=0) ){ |
| | 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; |
| | i64 nByte; |
| | i64 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: |
| | assert( pSlot->n <= 0x7fffffff ); |
| | sqlite3_result_blob(pCtx, pSlot->z, (int)pSlot->n, SQLITE_TRANSIENT); |
| | break; |
| |
|
| | case SQLITE_TEXT: |
| | assert( pSlot->n <= 0x7fffffff ); |
| | sqlite3_result_text(pCtx, pSlot->z, (int)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: { |
| | i64 nByte = sqlite3_value_bytes(argv[1]); |
| | const void *pData = 0; |
| | if( nByte>pSlot->nByte ){ |
| | char *zNew = (char*)sqlite3_realloc64(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; |
| | i64 *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); |
| | if( zName==0 ){ |
| | |
| | sqlite3_free(zCols); |
| | sqlite3_free(zOrder); |
| | return sqlite3_reset(pIndexXInfo); |
| | } |
| | zCols = idxAppendText(&rc, zCols, |
| | "%sx.%Q IS sqlite_expert_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 = (i64*)idxMalloc(&rc, sizeof(i64)*(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 ){ |
| | i64 s0 = aStat[0]; |
| | zStat = sqlite3_mprintf("%lld", s0); |
| | if( zStat==0 ) rc = SQLITE_NOMEM; |
| | for(i=1; rc==SQLITE_OK && i<=nCol; i++){ |
| | zStat = idxAppendText(&rc, zStat, " %lld", (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 ){ |
| | i64 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, "sqlite_expert_rem", |
| | 2, SQLITE_UTF8, (void*)pCtx, idxRemFunc, 0, 0 |
| | ); |
| | } |
| | if( rc==SQLITE_OK ){ |
| | rc = sqlite3_create_function(p->db, "sqlite_expert_sample", |
| | 0, SQLITE_UTF8, (void*)&samplectx, idxSampleFunc, 0, 0 |
| | ); |
| | } |
| |
|
| | if( rc==SQLITE_OK ){ |
| | pCtx->nSlot = (i64)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_create_function(p->db, "sqlite_expert_rem", 2, SQLITE_UTF8, 0,0,0,0); |
| | sqlite3_create_function(p->db, "sqlite_expert_sample", 0,SQLITE_UTF8,0,0,0,0); |
| |
|
| | sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp."UNIQUE_TABLE_NAME,0,0,0); |
| | return rc; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | int dummyCompare(void *up1, int up2, const void *up3, int up4, const void *up5){ |
| | (void)up1; |
| | (void)up2; |
| | (void)up3; |
| | (void)up4; |
| | (void)up5; |
| | assert(0); |
| | return 0; |
| | } |
| | |
| | void useDummyCS(void *up1, sqlite3 *db, int etr, const char *zName){ |
| | (void)up1; |
| | sqlite3_create_collation_v2(db, zName, etr, 0, dummyCompare, 0); |
| | } |
| |
|
| | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) \ |
| | && !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS) |
| | |
| | |
| | |
| | void dummyUDF(sqlite3_context *up1, int up2, sqlite3_value **up3){ |
| | (void)up1; |
| | (void)up2; |
| | (void)up3; |
| | assert(0); |
| | } |
| | void dummyUDFvalue(sqlite3_context *up1){ |
| | (void)up1; |
| | assert(0); |
| | } |
| |
|
| | |
| | |
| | |
| | int registerUDFs(sqlite3 *dbSrc, sqlite3 *dbDst){ |
| | sqlite3_stmt *pStmt; |
| | int rc = sqlite3_prepare_v2(dbSrc, |
| | "SELECT name,type,enc,narg,flags " |
| | "FROM pragma_function_list() " |
| | "WHERE builtin==0", -1, &pStmt, 0); |
| | if( rc==SQLITE_OK ){ |
| | while( SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){ |
| | int nargs = sqlite3_column_int(pStmt,3); |
| | int flags = sqlite3_column_int(pStmt,4); |
| | const char *name = (char*)sqlite3_column_text(pStmt,0); |
| | const char *type = (char*)sqlite3_column_text(pStmt,1); |
| | const char *enc = (char*)sqlite3_column_text(pStmt,2); |
| | if( name==0 || type==0 || enc==0 ){ |
| | |
| | }else{ |
| | int ienc = SQLITE_UTF8; |
| | int rcf = SQLITE_ERROR; |
| | if( strcmp(enc,"utf16le")==0 ) ienc = SQLITE_UTF16LE; |
| | else if( strcmp(enc,"utf16be")==0 ) ienc = SQLITE_UTF16BE; |
| | ienc |= (flags & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY)); |
| | if( strcmp(type,"w")==0 ){ |
| | rcf = sqlite3_create_window_function(dbDst,name,nargs,ienc,0, |
| | dummyUDF,dummyUDFvalue,0,0,0); |
| | }else if( strcmp(type,"a")==0 ){ |
| | rcf = sqlite3_create_function(dbDst,name,nargs,ienc,0, |
| | 0,dummyUDF,dummyUDFvalue); |
| | }else if( strcmp(type,"s")==0 ){ |
| | rcf = sqlite3_create_function(dbDst,name,nargs,ienc,0, |
| | dummyUDF,0,0); |
| | } |
| | if( rcf!=SQLITE_OK ){ |
| | rc = rcf; |
| | break; |
| | } |
| | } |
| | } |
| | sqlite3_finalize(pStmt); |
| | if( rc==SQLITE_DONE ) rc = SQLITE_OK; |
| | } |
| | return rc; |
| | } |
| | #endif |
| |
|
| | |
| | |
| | |
| | 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 ) rc = sqlite3_collation_needed(pNew->dbm,0,useDummyCS); |
| | if( rc==SQLITE_OK ) rc = sqlite3_collation_needed(pNew->dbv,0,useDummyCS); |
| |
|
| | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) \ |
| | && !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS) |
| | |
| | if( rc==SQLITE_OK ){ |
| | rc = registerUDFs(pNew->db, pNew->dbm); |
| | } |
| | if( rc==SQLITE_OK ){ |
| | rc = registerUDFs(pNew->db, pNew->dbv); |
| | } |
| | #endif |
| |
|
| | |
| | if( rc==SQLITE_OK ){ |
| | sqlite3_stmt *pSql = 0; |
| | rc = idxPrintfPrepareStmt(pNew->db, &pSql, pzErrmsg, |
| | "SELECT sql, name, substr(sql,1,14)=='create virtual' COLLATE nocase" |
| | " FROM sqlite_schema WHERE substr(name,1,7)!='sqlite_' COLLATE nocase" |
| | " ORDER BY 3 DESC, rowid" |
| | ); |
| | while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ |
| | const char *zSql = (const char*)sqlite3_column_text(pSql, 0); |
| | const char *zName = (const char*)sqlite3_column_text(pSql, 1); |
| | int bExists = 0; |
| | rc = expertDbContainsObject(pNew->dbm, zName, &bExists); |
| | if( rc==SQLITE_OK && zSql && bExists==0 ){ |
| | rc = expertSchemaSql(pNew->dbm, zSql, 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 = idxPrepareStmt(p->db, &pStmt, pzErr, zStmt); |
| | if( rc!=SQLITE_OK ) break; |
| | sqlite3_finalize(pStmt); |
| | rc = sqlite3_prepare_v2(p->dbv, zStmt, -1, &pStmt, &zStmt); |
| | if( rc==SQLITE_OK ){ |
| | if( pStmt ){ |
| | IdxStatement *pNew; |
| | const char *z = sqlite3_sql(pStmt); |
| | i64 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 |
| |
|