| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | #include "sqlite3ext.h" |
| | SQLITE_EXTENSION_INIT1 |
| | #include <assert.h> |
| | #include <string.h> |
| | #include <ctype.h> |
| |
|
| | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| |
|
| | #ifndef IsAlnum |
| | #define IsAlnum(X) isalnum((unsigned char)X) |
| | #endif |
| |
|
| |
|
| | |
| | |
| | |
| | typedef struct completion_vtab completion_vtab; |
| | struct completion_vtab { |
| | sqlite3_vtab base; |
| | sqlite3 *db; |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | typedef struct completion_cursor completion_cursor; |
| | struct completion_cursor { |
| | sqlite3_vtab_cursor base; |
| | sqlite3 *db; |
| | int nPrefix, nLine; |
| | char *zPrefix; |
| | char *zLine; |
| | const char *zCurrentRow; |
| | int szRow; |
| | sqlite3_stmt *pStmt; |
| | sqlite3_int64 iRowid; |
| | int ePhase; |
| | int j; |
| | }; |
| |
|
| | |
| | |
| | #define COMPLETION_FIRST_PHASE 1 |
| | #define COMPLETION_KEYWORDS 1 |
| | #define COMPLETION_PRAGMAS 2 |
| | #define COMPLETION_FUNCTIONS 3 |
| | #define COMPLETION_COLLATIONS 4 |
| | #define COMPLETION_INDEXES 5 |
| | #define COMPLETION_TRIGGERS 6 |
| | #define COMPLETION_DATABASES 7 |
| | #define COMPLETION_TABLES 8 |
| | #define COMPLETION_COLUMNS 9 |
| | #define COMPLETION_MODULES 10 |
| | #define COMPLETION_EOF 11 |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static int completionConnect( |
| | sqlite3 *db, |
| | void *pAux, |
| | int argc, const char *const*argv, |
| | sqlite3_vtab **ppVtab, |
| | char **pzErr |
| | ){ |
| | completion_vtab *pNew; |
| | int rc; |
| |
|
| | (void)(pAux); |
| | (void)(argc); |
| | (void)(argv); |
| | (void)(pzErr); |
| |
|
| | |
| | #define COMPLETION_COLUMN_CANDIDATE 0 |
| | #define COMPLETION_COLUMN_PREFIX 1 |
| | #define COMPLETION_COLUMN_WHOLELINE 2 |
| | #define COMPLETION_COLUMN_PHASE 3 |
| |
|
| | sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS); |
| | rc = sqlite3_declare_vtab(db, |
| | "CREATE TABLE x(" |
| | " candidate TEXT," |
| | " prefix TEXT HIDDEN," |
| | " wholeline TEXT HIDDEN," |
| | " phase INT HIDDEN" |
| | ")"); |
| | if( rc==SQLITE_OK ){ |
| | pNew = sqlite3_malloc( sizeof(*pNew) ); |
| | *ppVtab = (sqlite3_vtab*)pNew; |
| | if( pNew==0 ) return SQLITE_NOMEM; |
| | memset(pNew, 0, sizeof(*pNew)); |
| | pNew->db = db; |
| | } |
| | return rc; |
| | } |
| |
|
| | |
| | |
| | |
| | static int completionDisconnect(sqlite3_vtab *pVtab){ |
| | sqlite3_free(pVtab); |
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | static int completionOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ |
| | completion_cursor *pCur; |
| | pCur = sqlite3_malloc( sizeof(*pCur) ); |
| | if( pCur==0 ) return SQLITE_NOMEM; |
| | memset(pCur, 0, sizeof(*pCur)); |
| | pCur->db = ((completion_vtab*)p)->db; |
| | *ppCursor = &pCur->base; |
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | static void completionCursorReset(completion_cursor *pCur){ |
| | sqlite3_free(pCur->zPrefix); pCur->zPrefix = 0; pCur->nPrefix = 0; |
| | sqlite3_free(pCur->zLine); pCur->zLine = 0; pCur->nLine = 0; |
| | sqlite3_finalize(pCur->pStmt); pCur->pStmt = 0; |
| | pCur->j = 0; |
| | } |
| |
|
| | |
| | |
| | |
| | static int completionClose(sqlite3_vtab_cursor *cur){ |
| | completionCursorReset((completion_cursor*)cur); |
| | sqlite3_free(cur); |
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static int completionNext(sqlite3_vtab_cursor *cur){ |
| | completion_cursor *pCur = (completion_cursor*)cur; |
| | int eNextPhase = 0; |
| | int iCol = -1; |
| | pCur->iRowid++; |
| | while( pCur->ePhase!=COMPLETION_EOF ){ |
| | switch( pCur->ePhase ){ |
| | case COMPLETION_KEYWORDS: { |
| | if( pCur->j >= sqlite3_keyword_count() ){ |
| | pCur->zCurrentRow = 0; |
| | pCur->ePhase = COMPLETION_DATABASES; |
| | }else{ |
| | sqlite3_keyword_name(pCur->j++, &pCur->zCurrentRow, &pCur->szRow); |
| | } |
| | iCol = -1; |
| | break; |
| | } |
| | case COMPLETION_DATABASES: { |
| | if( pCur->pStmt==0 ){ |
| | sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, |
| | &pCur->pStmt, 0); |
| | } |
| | iCol = 1; |
| | eNextPhase = COMPLETION_TABLES; |
| | break; |
| | } |
| | case COMPLETION_TABLES: { |
| | if( pCur->pStmt==0 ){ |
| | sqlite3_stmt *pS2; |
| | char *zSql = 0; |
| | const char *zSep = ""; |
| | sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pS2, 0); |
| | while( sqlite3_step(pS2)==SQLITE_ROW ){ |
| | const char *zDb = (const char*)sqlite3_column_text(pS2, 1); |
| | zSql = sqlite3_mprintf( |
| | "%z%s" |
| | "SELECT name FROM \"%w\".sqlite_schema", |
| | zSql, zSep, zDb |
| | ); |
| | if( zSql==0 ) return SQLITE_NOMEM; |
| | zSep = " UNION "; |
| | } |
| | sqlite3_finalize(pS2); |
| | sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0); |
| | sqlite3_free(zSql); |
| | } |
| | iCol = 0; |
| | eNextPhase = COMPLETION_COLUMNS; |
| | break; |
| | } |
| | case COMPLETION_COLUMNS: { |
| | if( pCur->pStmt==0 ){ |
| | sqlite3_stmt *pS2; |
| | char *zSql = 0; |
| | const char *zSep = ""; |
| | sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pS2, 0); |
| | while( sqlite3_step(pS2)==SQLITE_ROW ){ |
| | const char *zDb = (const char*)sqlite3_column_text(pS2, 1); |
| | zSql = sqlite3_mprintf( |
| | "%z%s" |
| | "SELECT pti.name FROM \"%w\".sqlite_schema AS sm" |
| | " JOIN pragma_table_xinfo(sm.name,%Q) AS pti" |
| | " WHERE sm.type='table'", |
| | zSql, zSep, zDb, zDb |
| | ); |
| | if( zSql==0 ) return SQLITE_NOMEM; |
| | zSep = " UNION "; |
| | } |
| | sqlite3_finalize(pS2); |
| | sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0); |
| | sqlite3_free(zSql); |
| | } |
| | iCol = 0; |
| | eNextPhase = COMPLETION_EOF; |
| | break; |
| | } |
| | } |
| | if( iCol<0 ){ |
| | |
| | if( pCur->zCurrentRow==0 ) continue; |
| | }else{ |
| | if( sqlite3_step(pCur->pStmt)==SQLITE_ROW ){ |
| | |
| | pCur->zCurrentRow = (const char*)sqlite3_column_text(pCur->pStmt, iCol); |
| | pCur->szRow = sqlite3_column_bytes(pCur->pStmt, iCol); |
| | }else{ |
| | |
| | sqlite3_finalize(pCur->pStmt); |
| | pCur->pStmt = 0; |
| | pCur->ePhase = eNextPhase; |
| | continue; |
| | } |
| | } |
| | if( pCur->nPrefix==0 ) break; |
| | if( pCur->nPrefix<=pCur->szRow |
| | && sqlite3_strnicmp(pCur->zPrefix, pCur->zCurrentRow, pCur->nPrefix)==0 |
| | ){ |
| | break; |
| | } |
| | } |
| |
|
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | static int completionColumn( |
| | sqlite3_vtab_cursor *cur, |
| | sqlite3_context *ctx, |
| | int i |
| | ){ |
| | completion_cursor *pCur = (completion_cursor*)cur; |
| | switch( i ){ |
| | case COMPLETION_COLUMN_CANDIDATE: { |
| | sqlite3_result_text(ctx, pCur->zCurrentRow, pCur->szRow,SQLITE_TRANSIENT); |
| | break; |
| | } |
| | case COMPLETION_COLUMN_PREFIX: { |
| | sqlite3_result_text(ctx, pCur->zPrefix, -1, SQLITE_TRANSIENT); |
| | break; |
| | } |
| | case COMPLETION_COLUMN_WHOLELINE: { |
| | sqlite3_result_text(ctx, pCur->zLine, -1, SQLITE_TRANSIENT); |
| | break; |
| | } |
| | case COMPLETION_COLUMN_PHASE: { |
| | sqlite3_result_int(ctx, pCur->ePhase); |
| | break; |
| | } |
| | } |
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | static int completionRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ |
| | completion_cursor *pCur = (completion_cursor*)cur; |
| | *pRowid = pCur->iRowid; |
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | static int completionEof(sqlite3_vtab_cursor *cur){ |
| | completion_cursor *pCur = (completion_cursor*)cur; |
| | return pCur->ePhase >= COMPLETION_EOF; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | static int completionFilter( |
| | sqlite3_vtab_cursor *pVtabCursor, |
| | int idxNum, const char *idxStr, |
| | int argc, sqlite3_value **argv |
| | ){ |
| | completion_cursor *pCur = (completion_cursor *)pVtabCursor; |
| | int iArg = 0; |
| | (void)(idxStr); |
| | (void)(argc); |
| | completionCursorReset(pCur); |
| | if( idxNum & 1 ){ |
| | pCur->nPrefix = sqlite3_value_bytes(argv[iArg]); |
| | if( pCur->nPrefix>0 ){ |
| | pCur->zPrefix = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg])); |
| | if( pCur->zPrefix==0 ) return SQLITE_NOMEM; |
| | pCur->nPrefix = (int)strlen(pCur->zPrefix); |
| | } |
| | iArg = 1; |
| | } |
| | if( idxNum & 2 ){ |
| | pCur->nLine = sqlite3_value_bytes(argv[iArg]); |
| | if( pCur->nLine>0 ){ |
| | pCur->zLine = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg])); |
| | if( pCur->zLine==0 ) return SQLITE_NOMEM; |
| | pCur->nLine = (int)strlen(pCur->zLine); |
| | } |
| | } |
| | if( pCur->zLine!=0 && pCur->zPrefix==0 ){ |
| | int i = pCur->nLine; |
| | while( i>0 && (IsAlnum(pCur->zLine[i-1]) || pCur->zLine[i-1]=='_') ){ |
| | i--; |
| | } |
| | pCur->nPrefix = pCur->nLine - i; |
| | if( pCur->nPrefix>0 ){ |
| | pCur->zPrefix = sqlite3_mprintf("%.*s", pCur->nPrefix, pCur->zLine + i); |
| | if( pCur->zPrefix==0 ) return SQLITE_NOMEM; |
| | pCur->nPrefix = (int)strlen(pCur->zPrefix); |
| | } |
| | } |
| | pCur->iRowid = 0; |
| | pCur->ePhase = COMPLETION_FIRST_PHASE; |
| | return completionNext(pVtabCursor); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static int completionBestIndex( |
| | sqlite3_vtab *tab, |
| | sqlite3_index_info *pIdxInfo |
| | ){ |
| | int i; |
| | int idxNum = 0; |
| | int prefixIdx = -1; |
| | int wholelineIdx = -1; |
| | int nArg = 0; |
| | const struct sqlite3_index_constraint *pConstraint; |
| |
|
| | (void)(tab); |
| | pConstraint = pIdxInfo->aConstraint; |
| | for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ |
| | if( pConstraint->usable==0 ) continue; |
| | if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; |
| | switch( pConstraint->iColumn ){ |
| | case COMPLETION_COLUMN_PREFIX: |
| | prefixIdx = i; |
| | idxNum |= 1; |
| | break; |
| | case COMPLETION_COLUMN_WHOLELINE: |
| | wholelineIdx = i; |
| | idxNum |= 2; |
| | break; |
| | } |
| | } |
| | if( prefixIdx>=0 ){ |
| | pIdxInfo->aConstraintUsage[prefixIdx].argvIndex = ++nArg; |
| | pIdxInfo->aConstraintUsage[prefixIdx].omit = 1; |
| | } |
| | if( wholelineIdx>=0 ){ |
| | pIdxInfo->aConstraintUsage[wholelineIdx].argvIndex = ++nArg; |
| | pIdxInfo->aConstraintUsage[wholelineIdx].omit = 1; |
| | } |
| | pIdxInfo->idxNum = idxNum; |
| | pIdxInfo->estimatedCost = (double)5000 - 1000*nArg; |
| | pIdxInfo->estimatedRows = 500 - 100*nArg; |
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | static sqlite3_module completionModule = { |
| | 0, |
| | 0, |
| | completionConnect, |
| | completionBestIndex, |
| | completionDisconnect, |
| | 0, |
| | completionOpen, |
| | completionClose, |
| | completionFilter, |
| | completionNext, |
| | completionEof, |
| | completionColumn, |
| | completionRowid, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0 |
| | }; |
| |
|
| | #endif |
| |
|
| | int sqlite3CompletionVtabInit(sqlite3 *db){ |
| | int rc = SQLITE_OK; |
| | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| | rc = sqlite3_create_module(db, "completion", &completionModule, 0); |
| | #endif |
| | return rc; |
| | } |
| |
|
| | #ifdef _WIN32 |
| | __declspec(dllexport) |
| | #endif |
| | int sqlite3_completion_init( |
| | sqlite3 *db, |
| | char **pzErrMsg, |
| | const sqlite3_api_routines *pApi |
| | ){ |
| | int rc = SQLITE_OK; |
| | SQLITE_EXTENSION_INIT2(pApi); |
| | (void)(pzErrMsg); |
| | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| | rc = sqlite3CompletionVtabInit(db); |
| | #endif |
| | return rc; |
| | } |
| |
|