1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-05 04:30:38 +03:00

Add support for tab-completion (using the ext/misc/completion.c virtual table)

to the command-line shell.

FossilOrigin-Name: 95cd1d9f8baa6be305c9a8bfa26fef2a403f2d5b3b5c9c55382ec04f0bc98d40
This commit is contained in:
drh
2017-07-11 13:59:07 +00:00
parent 2ce15c3f4c
commit 56eb09bc23
5 changed files with 663 additions and 17 deletions

View File

@@ -33,10 +33,7 @@
** faster than any human can type. ** faster than any human can type.
** **
*/ */
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_COMPLETIONVTAB)
#if !defined(SQLITEINT_H)
#include "sqlite3ext.h" #include "sqlite3ext.h"
#endif
SQLITE_EXTENSION_INIT1 SQLITE_EXTENSION_INIT1
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
@@ -495,7 +492,6 @@ int sqlite3CompletionVtabInit(sqlite3 *db){
return rc; return rc;
} }
#ifndef SQLITE_CORE
#ifdef _WIN32 #ifdef _WIN32
__declspec(dllexport) __declspec(dllexport)
#endif #endif
@@ -511,5 +507,3 @@ int sqlite3_completion_init(
#endif #endif
return rc; return rc;
} }
#endif /* SQLITE_CORE */
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_COMPLETIONVTAB) */

View File

@@ -1,5 +1,5 @@
C Change\sthe\ssrc/shell.c\sfile\sso\sthat\sit\sis\sgenerated\sfrom\sa\snew\sscript\sat\ntool/mkshellc.tcl\sand\sthe\stemplate\sfile\ssrc/shell.c.in,\sand\sautomatically\nincludes\sthe\sextensions\sit\sneeds\sout\sof\sext/misc. C Add\ssupport\sfor\stab-completion\s(using\sthe\sext/misc/completion.c\svirtual\stable)\nto\sthe\scommand-line\sshell.
D 2017-07-11T13:34:40.462 D 2017-07-11T13:59:07.400
F Makefile.in 081e48dfe7f995d57ce1a88ddf4d2917b4349158648a6cd45b42beae30de3a12 F Makefile.in 081e48dfe7f995d57ce1a88ddf4d2917b4349158648a6cd45b42beae30de3a12
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc 20850e3e8d4d4791e0531955852d768eb06f24138214870d543abb1a47346fba F Makefile.msc 20850e3e8d4d4791e0531955852d768eb06f24138214870d543abb1a47346fba
@@ -256,7 +256,7 @@ F ext/misc/amatch.c 6db4607cb17c54b853a2d7c7c36046d004853f65b9b733e6f019d543d5df
F ext/misc/anycollseq.c 5ffdfde9829eeac52219136ad6aa7cd9a4edb3b15f4f2532de52f4a22525eddb F ext/misc/anycollseq.c 5ffdfde9829eeac52219136ad6aa7cd9a4edb3b15f4f2532de52f4a22525eddb
F ext/misc/carray.c 40c27641010a4dc67e3690bdb7c9d36ca58b3c2d F ext/misc/carray.c 40c27641010a4dc67e3690bdb7c9d36ca58b3c2d
F ext/misc/closure.c 0d2a038df8fbae7f19de42e7c7d71f2e4dc88704 F ext/misc/closure.c 0d2a038df8fbae7f19de42e7c7d71f2e4dc88704
F ext/misc/completion.c 637c016977809c2c3174472e7a416c9dbefc89dadb0efbd33f1516114b43900e F ext/misc/completion.c 821141b02a0313ce7dbe72f0260ef755f81d4b57eacb799864e4c40ad5ceb3f3
F ext/misc/compress.c 122faa92d25033d6c3f07c39231de074ab3d2e83 F ext/misc/compress.c 122faa92d25033d6c3f07c39231de074ab3d2e83
F ext/misc/csv.c d91c0388445b08f6e373dd0e8fc024d4551b1fcaf64e876a1c3f4fac8a63adc2 F ext/misc/csv.c d91c0388445b08f6e373dd0e8fc024d4551b1fcaf64e876a1c3f4fac8a63adc2
F ext/misc/dbdump.c 3509fa6b8932d04e932d6b6b827b6a82ca362781b8e8f3c77336f416793e215e F ext/misc/dbdump.c 3509fa6b8932d04e932d6b6b827b6a82ca362781b8e8f3c77336f416793e215e
@@ -450,8 +450,8 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
F src/resolve.c 6aa1fb1212e601f65b983ee1215d69a591986c8f97a3805c425c625a53839539 F src/resolve.c 6aa1fb1212e601f65b983ee1215d69a591986c8f97a3805c425c625a53839539
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
F src/select.c 6aa7e8ee53dcb9d0b77b05670bb44a1076d6747bc9b2c1f12a365b553ab6c828 F src/select.c 6aa7e8ee53dcb9d0b77b05670bb44a1076d6747bc9b2c1f12a365b553ab6c828
F src/shell.c 71a338b3a29502a4a7290fa646bc4547d911777aba55ebc040b5940786435833 F src/shell.c 19726a6f28f66aa0488f5e9c7aacc399f2bbcd934bffc3a92a5de6d365389eee
F src/shell.c.in 9d75c83c13fb1b96e60c56cce96146d84c4c8b3d18d11bd85e0759ebf744bcb2 F src/shell.c.in 18510e3fdd9216a6183ff93a48cac7d8d253d39f17750af597d90d3c5f27fc34
F src/sqlite.h.in 03a422ba13da1dfef7f1aaa1ba344acf18dc867112620b1fdb2a1426cabba634 F src/sqlite.h.in 03a422ba13da1dfef7f1aaa1ba344acf18dc867112620b1fdb2a1426cabba634
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 58fd0676d3111d02e62e5a35992a7d3da5d3f88753acc174f2d37b774fbbdd28 F src/sqlite3ext.h 58fd0676d3111d02e62e5a35992a7d3da5d3f88753acc174f2d37b774fbbdd28
@@ -1631,7 +1631,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 724819b456ca7a5ca6d68415b3b3554741c3ddbb5c416cb6b6b5fc54177f426e P 17e0bb12d82b510b86b6886b9fd0faf39b60b1374027344f89d7b89a32b842b9
R 60a762ef308a5ae7a380cb2ce07b0a0c R 951f8f3a9284f3001be7f39a25c50055
U drh U drh
Z 1a54bd7eb9eb8ff312901d65383e16cf Z 55996074e61f063450435131cf82f298

View File

@@ -1 +1 @@
17e0bb12d82b510b86b6886b9fd0faf39b60b1374027344f89d7b89a32b842b9 95cd1d9f8baa6be305c9a8bfa26fef2a403f2d5b3b5c9c55382ec04f0bc98d40

View File

@@ -1629,6 +1629,517 @@ int sqlite3_fileio_init(
} }
/************************* End ../ext/misc/fileio.c ********************/ /************************* End ../ext/misc/fileio.c ********************/
/************************* Begin ../ext/misc/completion.c ******************/
/*
** 2017-07-10
**
** 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.
**
*************************************************************************
**
** This file implements an eponymous virtual table that returns suggested
** completions for a partial SQL input.
**
** Suggested usage:
**
** SELECT DISTINCT candidate COLLATE nocase
** FROM completion($prefix,$wholeline)
** ORDER BY 1;
**
** The two query parameters are optional. $prefix is the text of the
** current word being typed and that is to be completed. $wholeline is
** the complete input line, used for context.
**
** The raw completion() table might return the same candidate multiple
** times, for example if the same column name is used to two or more
** tables. And the candidates are returned in an arbitrary order. Hence,
** the DISTINCT and ORDER BY are recommended.
**
** This virtual table operates at the speed of human typing, and so there
** is no attempt to make it fast. Even a slow implementation will be much
** faster than any human can type.
**
*/
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
#include <ctype.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* completion_vtab is a subclass of sqlite3_vtab which will
** serve as the underlying representation of a completion virtual table
*/
typedef struct completion_vtab completion_vtab;
struct completion_vtab {
sqlite3_vtab base; /* Base class - must be first */
sqlite3 *db; /* Database connection for this completion vtab */
};
/* completion_cursor is a subclass of sqlite3_vtab_cursor which will
** serve as the underlying representation of a cursor that scans
** over rows of the result
*/
typedef struct completion_cursor completion_cursor;
struct completion_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
sqlite3 *db; /* Database connection for this cursor */
int nPrefix, nLine; /* Number of bytes in zPrefix and zLine */
char *zPrefix; /* The prefix for the word we want to complete */
char *zLine; /* The whole that we want to complete */
const char *zCurrentRow; /* Current output row */
sqlite3_stmt *pStmt; /* Current statement */
sqlite3_int64 iRowid; /* The rowid */
int ePhase; /* Current phase */
int j; /* inter-phase counter */
};
/* Values for ePhase:
*/
#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
/*
** The completionConnect() method is invoked to create a new
** completion_vtab that describes the completion virtual table.
**
** Think of this routine as the constructor for completion_vtab objects.
**
** All this routine needs to do is:
**
** (1) Allocate the completion_vtab object and initialize all fields.
**
** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
** result set of queries against completion will look like.
*/
static int completionConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
completion_vtab *pNew;
int rc;
/* Column numbers */
#define COMPLETION_COLUMN_CANDIDATE 0 /* Suggested completion of the input */
#define COMPLETION_COLUMN_PREFIX 1 /* Prefix of the word to be completed */
#define COMPLETION_COLUMN_WHOLELINE 2 /* Entire line seen so far */
#define COMPLETION_COLUMN_PHASE 3 /* ePhase - used for debugging only */
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x("
" candidate TEXT,"
" prefix TEXT HIDDEN,"
" wholeline TEXT HIDDEN,"
" phase INT HIDDEN" /* Used for debugging only */
")");
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;
}
/*
** This method is the destructor for completion_cursor objects.
*/
static int completionDisconnect(sqlite3_vtab *pVtab){
sqlite3_free(pVtab);
return SQLITE_OK;
}
/*
** Constructor for a new completion_cursor object.
*/
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;
}
/*
** Reset the completion_cursor.
*/
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;
}
/*
** Destructor for a completion_cursor.
*/
static int completionClose(sqlite3_vtab_cursor *cur){
completionCursorReset((completion_cursor*)cur);
sqlite3_free(cur);
return SQLITE_OK;
}
/*
** All SQL keywords understood by SQLite
*/
static const char *completionKwrds[] = {
"ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ANALYZE", "AND", "AS",
"ASC", "ATTACH", "AUTOINCREMENT", "BEFORE", "BEGIN", "BETWEEN", "BY",
"CASCADE", "CASE", "CAST", "CHECK", "COLLATE", "COLUMN", "COMMIT",
"CONFLICT", "CONSTRAINT", "CREATE", "CROSS", "CURRENT_DATE",
"CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT", "DEFERRABLE",
"DEFERRED", "DELETE", "DESC", "DETACH", "DISTINCT", "DROP", "EACH",
"ELSE", "END", "ESCAPE", "EXCEPT", "EXCLUSIVE", "EXISTS", "EXPLAIN",
"FAIL", "FOR", "FOREIGN", "FROM", "FULL", "GLOB", "GROUP", "HAVING", "IF",
"IGNORE", "IMMEDIATE", "IN", "INDEX", "INDEXED", "INITIALLY", "INNER",
"INSERT", "INSTEAD", "INTERSECT", "INTO", "IS", "ISNULL", "JOIN", "KEY",
"LEFT", "LIKE", "LIMIT", "MATCH", "NATURAL", "NO", "NOT", "NOTNULL",
"NULL", "OF", "OFFSET", "ON", "OR", "ORDER", "OUTER", "PLAN", "PRAGMA",
"PRIMARY", "QUERY", "RAISE", "RECURSIVE", "REFERENCES", "REGEXP",
"REINDEX", "RELEASE", "RENAME", "REPLACE", "RESTRICT", "RIGHT",
"ROLLBACK", "ROW", "SAVEPOINT", "SELECT", "SET", "TABLE", "TEMP",
"TEMPORARY", "THEN", "TO", "TRANSACTION", "TRIGGER", "UNION", "UNIQUE",
"UPDATE", "USING", "VACUUM", "VALUES", "VIEW", "VIRTUAL", "WHEN", "WHERE",
"WITH", "WITHOUT",
};
/*
** Advance a completion_cursor to its next row of output.
**
** The ->ePhase, ->j, and ->pStmt fields of the completion_cursor object
** record the current state of the scan. This routine sets ->zCurrentRow
** to the current row of output and then returns. If no more rows remain,
** then ->ePhase is set to COMPLETION_EOF which will signal the virtual
** table that has reached the end of its scan.
**
** The current implementation just lists potential identifiers and
** keywords and filters them by zPrefix. Future enhancements should
** take zLine into account to try to restrict the set of identifiers and
** keywords based on what would be legal at the current point of input.
*/
static int completionNext(sqlite3_vtab_cursor *cur){
completion_cursor *pCur = (completion_cursor*)cur;
int eNextPhase = 0; /* Next phase to try if current phase reaches end */
int iCol = -1; /* If >=0, step pCur->pStmt and use the i-th column */
pCur->iRowid++;
while( pCur->ePhase!=COMPLETION_EOF ){
switch( pCur->ePhase ){
case COMPLETION_KEYWORDS: {
if( pCur->j >= sizeof(completionKwrds)/sizeof(completionKwrds[0]) ){
pCur->zCurrentRow = 0;
pCur->ePhase = COMPLETION_DATABASES;
}else{
pCur->zCurrentRow = completionKwrds[pCur->j++];
}
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_master"
" WHERE type='table'",
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_master AS sm"
" JOIN pragma_table_info(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 ){
/* This case is when the phase presets zCurrentRow */
if( pCur->zCurrentRow==0 ) continue;
}else{
if( sqlite3_step(pCur->pStmt)==SQLITE_ROW ){
/* Extract the next row of content */
pCur->zCurrentRow = (const char*)sqlite3_column_text(pCur->pStmt, iCol);
}else{
/* When all rows are finished, advance to the next phase */
sqlite3_finalize(pCur->pStmt);
pCur->pStmt = 0;
pCur->ePhase = eNextPhase;
continue;
}
}
if( pCur->nPrefix==0 ) break;
if( sqlite3_strnicmp(pCur->zPrefix, pCur->zCurrentRow, pCur->nPrefix)==0 ){
break;
}
}
return SQLITE_OK;
}
/*
** Return values of columns for the row at which the completion_cursor
** is currently pointing.
*/
static int completionColumn(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
int i /* Which column to return */
){
completion_cursor *pCur = (completion_cursor*)cur;
switch( i ){
case COMPLETION_COLUMN_CANDIDATE: {
sqlite3_result_text(ctx, pCur->zCurrentRow, -1, 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;
}
/*
** Return the rowid for the current row. In this implementation, the
** rowid is the same as the output value.
*/
static int completionRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
completion_cursor *pCur = (completion_cursor*)cur;
*pRowid = pCur->iRowid;
return SQLITE_OK;
}
/*
** Return TRUE if the cursor has been moved off of the last
** row of output.
*/
static int completionEof(sqlite3_vtab_cursor *cur){
completion_cursor *pCur = (completion_cursor*)cur;
return pCur->ePhase >= COMPLETION_EOF;
}
/*
** This method is called to "rewind" the completion_cursor object back
** to the first row of output. This method is always called at least
** once prior to any call to completionColumn() or completionRowid() or
** completionEof().
*/
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;
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;
}
iArg++;
}
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;
}
iArg++;
}
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->iRowid = 0;
pCur->ePhase = COMPLETION_FIRST_PHASE;
return completionNext(pVtabCursor);
}
/*
** SQLite will invoke this method one or more times while planning a query
** that uses the completion virtual table. This routine needs to create
** a query plan for each invocation and compute an estimated cost for that
** plan.
**
** There are two hidden parameters that act as arguments to the table-valued
** function: "prefix" and "wholeline". Bit 0 of idxNum is set if "prefix"
** is available and bit 1 is set if "wholeline" is available.
*/
static int completionBestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
int i; /* Loop over constraints */
int idxNum = 0; /* The query plan bitmask */
int prefixIdx = -1; /* Index of the start= constraint, or -1 if none */
int wholelineIdx = -1; /* Index of the stop= constraint, or -1 if none */
int nArg = 0; /* Number of arguments that completeFilter() expects */
const struct sqlite3_index_constraint *pConstraint;
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;
}
/*
** This following structure defines all the methods for the
** completion virtual table.
*/
static sqlite3_module completionModule = {
0, /* iVersion */
0, /* xCreate */
completionConnect, /* xConnect */
completionBestIndex, /* xBestIndex */
completionDisconnect, /* xDisconnect */
0, /* xDestroy */
completionOpen, /* xOpen - open a cursor */
completionClose, /* xClose - close a cursor */
completionFilter, /* xFilter - configure scan constraints */
completionNext, /* xNext - advance a cursor */
completionEof, /* xEof - check for end of scan */
completionColumn, /* xColumn - read data */
completionRowid, /* xRowid - read data */
0, /* xUpdate */
0, /* xBegin */
0, /* xSync */
0, /* xCommit */
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
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);
#ifndef SQLITE_OMIT_VIRTUALTABLE
rc = sqlite3CompletionVtabInit(db);
#endif
return rc;
}
/************************* End ../ext/misc/completion.c ********************/
#if defined(SQLITE_ENABLE_SESSION) #if defined(SQLITE_ENABLE_SESSION)
/* /*
@@ -3724,12 +4235,77 @@ static void open_db(ShellState *p, int keepAlive){
#endif #endif
sqlite3_fileio_init(p->db, 0, 0); sqlite3_fileio_init(p->db, 0, 0);
sqlite3_shathree_init(p->db, 0, 0); sqlite3_shathree_init(p->db, 0, 0);
sqlite3_completion_init(p->db, 0, 0);
sqlite3_create_function(p->db, "shell_add_schema", 2, SQLITE_UTF8, 0, sqlite3_create_function(p->db, "shell_add_schema", 2, SQLITE_UTF8, 0,
shellAddSchemaName, 0, 0); shellAddSchemaName, 0, 0);
} }
} }
#if HAVE_READLINE || HAVE_EDITLINE
/*
** Readline completion callbacks
*/
static char *readline_completion_generator(const char *text, int state){
static sqlite3_stmt *pStmt = 0;
char *zRet;
if( state==0 ){
char *zSql;
int rc;
sqlite3_finalize(pStmt);
zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase"
" FROM completion(%Q) ORDER BY 1", text);
sqlite3_prepare_v2(globalDb, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
}
if( sqlite3_step(pStmt)==SQLITE_ROW ){
zRet = strdup(sqlite3_column_text(pStmt, 0));
}else{
sqlite3_finalize(pStmt);
pStmt = 0;
zRet = 0;
}
return zRet;
}
static char **readline_completion(const char *zText, int iStart, int iEnd){
rl_attempted_completion_over = 1;
return rl_completion_matches(zText, readline_completion_generator);
}
#elif HAVE_LINENOISE
/*
** Linenoise completion callback
*/
static void linenoise_completion(const char *zLine, linenoiseCompletions *lc){
int nLine = (int)strlen(zLine);
int i, iStart;
sqlite3_stmt *pStmt = 0;
char *zSql;
char zBuf[1000];
if( nLine>sizeof(zBuf)-30 ) return;
if( zLine[0]=='.' ) return;
for(i=nLine-1; i>=0 && (isalnum(zLine[i]) || zLine[i]=='_'); i--){}
if( i==nLine-1 ) return;
iStart = i+1;
memcpy(zBuf, zLine, iStart);
zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase"
" FROM completion(%Q,%Q) ORDER BY 1",
&zLine[iStart], zLine);
sqlite3_prepare_v2(globalDb, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
sqlite3_exec(globalDb, "PRAGMA page_count", 0, 0, 0); /* Load the schema */
while( sqlite3_step(pStmt)==SQLITE_ROW ){
const char *zCompletion = (const char*)sqlite3_column_text(pStmt, 0);
int nCompletion = sqlite3_column_bytes(pStmt, 0);
if( iStart+nCompletion < sizeof(zBuf)-1 ){
memcpy(zBuf+iStart, zCompletion, nCompletion+1);
linenoiseAddCompletion(lc, zBuf);
}
}
sqlite3_finalize(pStmt);
}
#endif
/* /*
** Do C-language style dequoting. ** Do C-language style dequoting.
** **
@@ -7777,6 +8353,11 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
} }
} }
if( zHistory ){ shell_read_history(zHistory); } if( zHistory ){ shell_read_history(zHistory); }
#if HAVE_READLINE || HAVE_EDITLINE
rl_attempted_completion_function = readline_completion;
#elif HAVE_LINENOISE
linenoiseSetCompletionCallback(linenoise_completion);
#endif
rc = process_input(&data, 0); rc = process_input(&data, 0);
if( zHistory ){ if( zHistory ){
shell_stifle_history(100); shell_stifle_history(100);

View File

@@ -795,6 +795,7 @@ static void shellAddSchemaName(
INCLUDE ../ext/misc/shathree.c INCLUDE ../ext/misc/shathree.c
INCLUDE ../ext/misc/fileio.c INCLUDE ../ext/misc/fileio.c
INCLUDE ../ext/misc/completion.c
#if defined(SQLITE_ENABLE_SESSION) #if defined(SQLITE_ENABLE_SESSION)
/* /*
@@ -2890,12 +2891,77 @@ static void open_db(ShellState *p, int keepAlive){
#endif #endif
sqlite3_fileio_init(p->db, 0, 0); sqlite3_fileio_init(p->db, 0, 0);
sqlite3_shathree_init(p->db, 0, 0); sqlite3_shathree_init(p->db, 0, 0);
sqlite3_completion_init(p->db, 0, 0);
sqlite3_create_function(p->db, "shell_add_schema", 2, SQLITE_UTF8, 0, sqlite3_create_function(p->db, "shell_add_schema", 2, SQLITE_UTF8, 0,
shellAddSchemaName, 0, 0); shellAddSchemaName, 0, 0);
} }
} }
#if HAVE_READLINE || HAVE_EDITLINE
/*
** Readline completion callbacks
*/
static char *readline_completion_generator(const char *text, int state){
static sqlite3_stmt *pStmt = 0;
char *zRet;
if( state==0 ){
char *zSql;
int rc;
sqlite3_finalize(pStmt);
zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase"
" FROM completion(%Q) ORDER BY 1", text);
sqlite3_prepare_v2(globalDb, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
}
if( sqlite3_step(pStmt)==SQLITE_ROW ){
zRet = strdup(sqlite3_column_text(pStmt, 0));
}else{
sqlite3_finalize(pStmt);
pStmt = 0;
zRet = 0;
}
return zRet;
}
static char **readline_completion(const char *zText, int iStart, int iEnd){
rl_attempted_completion_over = 1;
return rl_completion_matches(zText, readline_completion_generator);
}
#elif HAVE_LINENOISE
/*
** Linenoise completion callback
*/
static void linenoise_completion(const char *zLine, linenoiseCompletions *lc){
int nLine = (int)strlen(zLine);
int i, iStart;
sqlite3_stmt *pStmt = 0;
char *zSql;
char zBuf[1000];
if( nLine>sizeof(zBuf)-30 ) return;
if( zLine[0]=='.' ) return;
for(i=nLine-1; i>=0 && (isalnum(zLine[i]) || zLine[i]=='_'); i--){}
if( i==nLine-1 ) return;
iStart = i+1;
memcpy(zBuf, zLine, iStart);
zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase"
" FROM completion(%Q,%Q) ORDER BY 1",
&zLine[iStart], zLine);
sqlite3_prepare_v2(globalDb, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
sqlite3_exec(globalDb, "PRAGMA page_count", 0, 0, 0); /* Load the schema */
while( sqlite3_step(pStmt)==SQLITE_ROW ){
const char *zCompletion = (const char*)sqlite3_column_text(pStmt, 0);
int nCompletion = sqlite3_column_bytes(pStmt, 0);
if( iStart+nCompletion < sizeof(zBuf)-1 ){
memcpy(zBuf+iStart, zCompletion, nCompletion+1);
linenoiseAddCompletion(lc, zBuf);
}
}
sqlite3_finalize(pStmt);
}
#endif
/* /*
** Do C-language style dequoting. ** Do C-language style dequoting.
** **
@@ -6943,6 +7009,11 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
} }
} }
if( zHistory ){ shell_read_history(zHistory); } if( zHistory ){ shell_read_history(zHistory); }
#if HAVE_READLINE || HAVE_EDITLINE
rl_attempted_completion_function = readline_completion;
#elif HAVE_LINENOISE
linenoiseSetCompletionCallback(linenoise_completion);
#endif
rc = process_input(&data, 0); rc = process_input(&data, 0);
if( zHistory ){ if( zHistory ){
shell_stifle_history(100); shell_stifle_history(100);