From ec814a8992ad9ed6a62c6d2ca7137f1da7856267 Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 27 Apr 2018 17:39:22 +0000 Subject: [PATCH] Add the ability to use bind parameters in the CLI. The new ".set KEY=VALUE" dot-command works to set bindings. Or use the "-Dkey=value" command-line option. Or use the built-in shell_bindings(k,v) virtual table to set, delete, or changing bindings. FossilOrigin-Name: 1f2944d1d64d36b729450685974c260295cf1e32c204b71e27d20ebc4d65f9e8 --- Makefile.in | 1 + Makefile.msc | 1 + ext/misc/bindvtab.c | 444 ++++++++++++++++++++++++++++++++++++++++++++ main.mk | 1 + manifest | 22 ++- manifest.uuid | 2 +- src/shell.c.in | 28 +++ 7 files changed, 489 insertions(+), 10 deletions(-) create mode 100644 ext/misc/bindvtab.c diff --git a/Makefile.in b/Makefile.in index a3ec869643..1c2bed9727 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1012,6 +1012,7 @@ SHELL_SRC = \ $(TOP)/ext/misc/shathree.c \ $(TOP)/ext/misc/fileio.c \ $(TOP)/ext/misc/completion.c \ + $(TOP)/ext/misc/bindvtab.c \ $(TOP)/ext/misc/sqlar.c \ $(TOP)/ext/expert/sqlite3expert.c \ $(TOP)/ext/expert/sqlite3expert.h \ diff --git a/Makefile.msc b/Makefile.msc index 50119d8361..faa7206f3e 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -2099,6 +2099,7 @@ SHELL_SRC = \ $(TOP)\ext\misc\shathree.c \ $(TOP)\ext\misc\fileio.c \ $(TOP)\ext\misc\completion.c \ + $(TOP)\ext\misc\bindvtab.c \ $(TOP)\ext\expert\sqlite3expert.c \ $(TOP)\ext\expert\sqlite3expert.h \ $(TOP)\src\test_windirent.c diff --git a/ext/misc/bindvtab.c b/ext/misc/bindvtab.c new file mode 100644 index 0000000000..b031ddfdc8 --- /dev/null +++ b/ext/misc/bindvtab.c @@ -0,0 +1,444 @@ +/* +** 2018-04-27 +** +** 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 a simple key/value store used to hold bind +** parameters for SQLite. The key/value store is a singleton - there +** is exactly one per process. The store can be accessed and controlled +** from SQLite using an eponymous virtual table. +*/ +#if !defined(SQLITEINT_H) +#include "sqlite3ext.h" +#endif +SQLITE_EXTENSION_INIT1 +#include +#include +#include + +/* Each entry in the key/value store */ +typedef struct BindingEntry BindingEntry; +struct BindingEntry { + char *zKey; /* Key */ + BindingEntry *pNext; /* Next entry in the list */ + BindingEntry *pPrev; /* Previous entry in the list */ + int eType; /* SQLITE_INTEGER, _FLOAT, _TEXT, or _BLOB */ + int len; /* Length for SQLITE_BLOB values */ + union { + sqlite3_int64 i; /* Integer value */ + double r; /* Real value */ + char *z; /* Text value */ + unsigned char *b; /* Blob value */ + } u; +}; + +/* Global list of all entries */ +static BindingEntry *global_pAll = 0; + +/* Locate any entry with the given key. Return NULL if not found. +*/ +static BindingEntry *shellBindingFind(const char *zKey){ + BindingEntry *p; + for(p=global_pAll; p && strcmp(p->zKey,zKey)!=0; p = p->pNext){} + return p; +} + +/* Delete any entry with the given key, if it exists. +*/ +static void shellBindingDelete(const char *zKey){ + BindingEntry *p; + p = shellBindingFind(zKey); + if( p ){ + if( p->pNext ){ + p->pNext->pPrev = p->pPrev; + } + if( p->pPrev ){ + p->pPrev->pNext = p->pNext; + }else{ + global_pAll = p->pNext; + } + free(p); + } +} + +/* Insert a new shell binding */ +static void shellBindingInsert(BindingEntry *p){ + p->pNext = global_pAll; + if( global_pAll ) global_pAll->pPrev = p; + global_pAll = p; + p->pPrev = 0; +} + +/* +** True if c is a valid ID character. +*/ +static int shellBindIdChar(char c){ + if( c>='a' && c<='z' ) return 1; + if( c>='A' && c<='Z' ) return 1; + if( c=='_' ) return 1; + if( c>='0' && c<='9' ) return 2; + return 0; +} + +/* Create a new binding given a string of the form "KEY=VALUE". Return +** values: +** +** 0: success +** 1: out of memory +** 2: Argument is not a valid KEY=VALUE string +** +** The type of VALUE is TEXT. +*/ +int shell_bindings_new_text(const char *z){ + int i; + int nKey; + int nData; + BindingEntry *p; + for(i=0; shellBindIdChar(z[i]); i++){} + if( i==0 ) return 2; + if( shellBindIdChar(z[0])==2 ) return 2; + nKey = i; + if( z[i]!='=' ) return 2; + for(nData=0; z[nKey+1+nData]; nData++){} + p = malloc( sizeof(*p) + nKey + nData + 2 ); + if( p==0 ) return 1; + memset(p, 0, sizeof(*p)); + p->zKey = (char*)&p[1]; + memcpy(p->zKey, z, nKey); + p->zKey[nKey] = 0; + p->u.z = &p->zKey[nKey+1]; + p->len = nData; + p->eType = SQLITE_TEXT; + memcpy(p->u.z, &z[nKey+1], nData+1); + shellBindingDelete(p->zKey); + shellBindingInsert(p); + return 0; +} + +/* +** Delete all shell bindings +*/ +void shell_bindings_clear(void){ + BindingEntry *pNext; + while( global_pAll ){ + pNext = global_pAll->pNext; + free(global_pAll); + global_pAll = pNext; + } +} + +/* Given a prepared statement, apply all bindings for which there are +** known values in the k-v store +*/ +void shell_bindings_apply(sqlite3_stmt *pStmt){ + int n = sqlite3_bind_parameter_count(pStmt); + int i; + BindingEntry *p; + for(i=1; i<=n; i++){ + const char *zKey = sqlite3_bind_parameter_name(pStmt, i); + if( zKey==0 || zKey[0]==0 ) continue; + zKey++; + p = shellBindingFind(zKey); + if( p==0 ) continue; + switch( p->eType ){ + case SQLITE_INTEGER: + sqlite3_bind_int64(pStmt, i, p->u.i); + break; + case SQLITE_FLOAT: + sqlite3_bind_double(pStmt, i, p->u.r); + break; + case SQLITE_TEXT: + sqlite3_bind_text(pStmt, i, p->u.z, p->len, SQLITE_TRANSIENT); + break; + case SQLITE_BLOB: + sqlite3_bind_blob(pStmt, i, p->u.b, p->len, SQLITE_TRANSIENT); + break; + } + } +} + +/* bindvtab_vtab is a subclass of sqlite3_vtab which is +** underlying representation of the virtual table +*/ +typedef struct bindvtab_vtab bindvtab_vtab; +struct bindvtab_vtab { + sqlite3_vtab base; /* Base class - must be first */ +}; + +/* bindvtab_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 bindvtab_cursor bindvtab_cursor; +struct bindvtab_cursor { + sqlite3_vtab_cursor base; /* Base class - must be first */ + BindingEntry *p; /* Current entry in the scan */ +}; + +/* +** The bindvtabConnect() method is invoked to create a new +** template virtual table. +** +** Think of this routine as the constructor for bindvtab_vtab objects. +** +** All this routine needs to do is: +** +** (1) Allocate the bindvtab_vtab object and initialize all fields. +** +** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the +** result set of queries against the virtual table will look like. +*/ +static int bindvtabConnect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + bindvtab_vtab *pNew; + int rc; + + rc = sqlite3_declare_vtab(db, + "CREATE TABLE shell_bindings(k TEXT PRIMARY KEY,v)" + " WITHOUT ROWID" + ); + /* For convenience, define symbolic names for the index to each column. */ +#define BINDVTAB_KEY 0 +#define BINDVTAB_VALUE 1 + if( rc==SQLITE_OK ){ + pNew = sqlite3_malloc( sizeof(*pNew) ); + *ppVtab = (sqlite3_vtab*)pNew; + if( pNew==0 ) return SQLITE_NOMEM; + memset(pNew, 0, sizeof(*pNew)); + } + return rc; +} + +/* +** This method is the destructor for bindvtab_vtab objects. +*/ +static int bindvtabDisconnect(sqlite3_vtab *pVtab){ + bindvtab_vtab *p = (bindvtab_vtab*)pVtab; + sqlite3_free(p); + return SQLITE_OK; +} + +/* +** Constructor for a new bindvtab_cursor object. +*/ +static int bindvtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ + bindvtab_cursor *pCur; + pCur = sqlite3_malloc( sizeof(*pCur) ); + if( pCur==0 ) return SQLITE_NOMEM; + memset(pCur, 0, sizeof(*pCur)); + *ppCursor = &pCur->base; + return SQLITE_OK; +} + +/* +** Destructor for a bindvtab_cursor. +*/ +static int bindvtabClose(sqlite3_vtab_cursor *cur){ + bindvtab_cursor *pCur = (bindvtab_cursor*)cur; + sqlite3_free(pCur); + return SQLITE_OK; +} + + +/* +** Advance a bindvtab_cursor to its next row of output. +*/ +static int bindvtabNext(sqlite3_vtab_cursor *cur){ + bindvtab_cursor *pCur = (bindvtab_cursor*)cur; + pCur->p = pCur->p->pNext; + return SQLITE_OK; +} + +/* +** Return values of columns for the row at which the bindvtab_cursor +** is currently pointing. +*/ +static int bindvtabColumn( + sqlite3_vtab_cursor *cur, /* The cursor */ + sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ + int i /* Which column to return */ +){ + bindvtab_cursor *pCur = (bindvtab_cursor*)cur; + BindingEntry *p = pCur->p; + if( i==BINDVTAB_KEY ){ + sqlite3_result_text(ctx, p->zKey, -1, SQLITE_TRANSIENT); + }else{ + assert( i==BINDVTAB_VALUE ); + switch( p->eType ){ + case SQLITE_INTEGER: + sqlite3_result_int(ctx, p->u.i); + break; + case SQLITE_FLOAT: + sqlite3_result_double(ctx, p->u.r); + break; + case SQLITE_TEXT: + sqlite3_result_text(ctx, p->u.z, p->len, SQLITE_TRANSIENT); + break; + case SQLITE_BLOB: + sqlite3_result_blob(ctx, p->u.b, p->len, SQLITE_TRANSIENT); + 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 bindvtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + return SQLITE_OK; +} + +/* +** Return TRUE if the cursor has been moved off of the last +** row of output. +*/ +static int bindvtabEof(sqlite3_vtab_cursor *cur){ + bindvtab_cursor *pCur = (bindvtab_cursor*)cur; + return pCur->p==0; +} + +/* +** This method is called to "rewind" the bindvtab_cursor object back +** to the first row of output. This method is always called at least +** once prior to any call to bindvtabColumn() or bindvtabRowid() or +** bindvtabEof(). +*/ +static int bindvtabFilter( + sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + bindvtab_cursor *pCur = (bindvtab_cursor *)pVtabCursor; + pCur->p = global_pAll; + return SQLITE_OK; +} + +/* +** SQLite will invoke this method one or more times while planning a query +** that uses the virtual table. This routine needs to create +** a query plan for each invocation and compute an estimated cost for that +** plan. +*/ +static int bindvtabBestIndex( + sqlite3_vtab *tab, + sqlite3_index_info *pIdxInfo +){ + pIdxInfo->estimatedCost = (double)10; + pIdxInfo->estimatedRows = 10; + return SQLITE_OK; +} + +/* +** Called to make changes to the shell bindings +*/ +static int bindvtabUpdate( + sqlite3_vtab *pVTab, + int argc, + sqlite3_value **argv, + sqlite_int64 *pRowid +){ + const char *zKey; + BindingEntry *p; + int nKey; + int len; + int eType; + if( sqlite3_value_type(argv[0])!=SQLITE_NULL ){ + zKey = (const char*)sqlite3_value_text(argv[0]); + if( zKey ) shellBindingDelete(zKey); + } + if( argc==1 ) return SQLITE_OK; + eType = sqlite3_value_type(argv[3]); + if( eType==SQLITE_NULL ) return SQLITE_OK; + zKey = (const char*)sqlite3_value_text(argv[2]); + if( zKey==0 ) return SQLITE_OK; + nKey = sqlite3_value_bytes(argv[2]); + shellBindingDelete(zKey); + if( eType==SQLITE_BLOB || eType==SQLITE_TEXT ){ + len = sqlite3_value_bytes(argv[3]); + }else{ + len = 0; + } + p = malloc( sizeof(*p) + nKey + len + 2 ); + if( p==0 ) return SQLITE_NOMEM; + memset(p, 0, sizeof(*p)); + p->zKey = (char*)&p[1]; + memcpy(p->zKey, zKey, nKey+1); + p->eType = eType; + switch( eType ){ + case SQLITE_INTEGER: + p->u.i = sqlite3_value_int64(argv[3]); + break; + case SQLITE_FLOAT: + p->u.r = sqlite3_value_double(argv[3]); + break; + case SQLITE_TEXT: + p->u.z = &p->zKey[nKey+1]; + memcpy(p->u.z, sqlite3_value_text(argv[3]), len); + break; + case SQLITE_BLOB: + p->u.b = (unsigned char*)&p->zKey[nKey+1]; + memcpy(p->u.b, sqlite3_value_blob(argv[3]), len); + break; + } + shellBindingInsert(p); + return SQLITE_OK; +} + +/* +** This following structure defines all the methods for the +** virtual table. +*/ +static sqlite3_module bindvtabModule = { + /* iVersion */ 0, + /* xCreate */ 0, + /* xConnect */ bindvtabConnect, + /* xBestIndex */ bindvtabBestIndex, + /* xDisconnect */ bindvtabDisconnect, + /* xDestroy */ 0, + /* xOpen */ bindvtabOpen, + /* xClose */ bindvtabClose, + /* xFilter */ bindvtabFilter, + /* xNext */ bindvtabNext, + /* xEof */ bindvtabEof, + /* xColumn */ bindvtabColumn, + /* xRowid */ bindvtabRowid, + /* xUpdate */ bindvtabUpdate, + /* xBegin */ 0, + /* xSync */ 0, + /* xCommit */ 0, + /* xRollback */ 0, + /* xFindMethod */ 0, + /* xRename */ 0, + /* xSavepoint */ 0, + /* xRelease */ 0, + /* xRollbackTo */ 0 +}; + + +#ifdef _WIN32 +__declspec(dllexport) +#endif +int sqlite3_bindvtab_init( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pApi +){ + int rc = SQLITE_OK; + SQLITE_EXTENSION_INIT2(pApi); + rc = sqlite3_create_module(db, "shell_bindings", &bindvtabModule, 0); + return rc; +} diff --git a/main.mk b/main.mk index b590b79cb1..68e209975c 100644 --- a/main.mk +++ b/main.mk @@ -707,6 +707,7 @@ SHELL_SRC = \ $(TOP)/ext/misc/shathree.c \ $(TOP)/ext/misc/fileio.c \ $(TOP)/ext/misc/completion.c \ + $(TOP)/ext/misc/bindvtab.c \ $(TOP)/ext/misc/sqlar.c \ $(TOP)/ext/expert/sqlite3expert.c \ $(TOP)/ext/expert/sqlite3expert.h \ diff --git a/manifest b/manifest index cb3e447750..241aefa036 100644 --- a/manifest +++ b/manifest @@ -1,10 +1,10 @@ -C Enhance\sthe\scomments\sin\sthe\stemplatevtab.c\simplementation. -D 2018-04-27T15:17:08.924 +C Add\sthe\sability\sto\suse\sbind\sparameters\sin\sthe\sCLI.\s\sThe\snew\s".set\sKEY=VALUE"\ndot-command\sworks\sto\sset\sbindings.\s\sOr\suse\sthe\s"-Dkey=value"\scommand-line\noption.\s\sOr\suse\sthe\sbuilt-in\sshell_bindings(k,v)\svirtual\stable\sto\sset,\ndelete,\sor\schanging\sbindings. +D 2018-04-27T17:39:22.740 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea -F Makefile.in 5ce9343cba9c189046f1afe6d2bcc1f68079439febc05267b98aec6ecc752439 +F Makefile.in da02d4d3544992af522b4ece652debef3255a5d45377ebb1b5ab8a9a5cd16859 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 -F Makefile.msc 59179295f6a9b433e3f59a6dc2fcf6db6fcac35d92015294beb5d27f2924ebb9 +F Makefile.msc a885344e4f1277b2dce6e9cfb69bc6eceb9494203fbc08b4b7a106268a1a7808 F README.md 7764d56778d567913ef11c82da9ab94aefa0826f7c243351e4e2d7adaef6f373 F VERSION b7c9d1d11cb70ef8e90cfcf3c944aa58a9f801cc2ad487eebb0a110c16dfc2df F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 @@ -269,6 +269,7 @@ F ext/misc/README.md d6dd0fe1d8af77040216798a6a2b0c46c73054d2f0ea544fbbcdccf6f23 F ext/misc/amatch.c 6db4607cb17c54b853a2d7c7c36046d004853f65b9b733e6f019d543d5dfae87 F ext/misc/anycollseq.c 5ffdfde9829eeac52219136ad6aa7cd9a4edb3b15f4f2532de52f4a22525eddb F ext/misc/appendvfs.c 3777f22ec1057dc4e5fd89f2fbddcc7a29fbeef1ad038c736c54411bb1967af7 +F ext/misc/bindvtab.c e21871461a0bb4646da111a7f32f709946f76a4013d523dc183192f0879c0cb3 F ext/misc/btreeinfo.c 78c8c57d325185ccc04b7679e5b020e34a4d9c87453e6b7ac943d0a26cee3256 F ext/misc/carray.c ed96c218ea940b85c9a274c4d9c59fe9491c299147a38a8bba537687bd6c6005 F ext/misc/closure.c 0d2a038df8fbae7f19de42e7c7d71f2e4dc88704 @@ -414,7 +415,7 @@ F ext/userauth/userauth.c 3410be31283abba70255d71fd24734e017a4497f F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 -F main.mk 068618fe288bf9d93091b808be73e26a93fb2db8435328cc5760f9dd35ba168b +F main.mk fb46a40f6573a36b1a97901eefafb287aa31e494e8737e772cc0722580025607 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504 @@ -493,7 +494,7 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384 F src/resolve.c 6415381a0e9d22c0e7cba33ca4a53f81474190862f5d4838190f5eb5b0b47bc9 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac F src/select.c daf07d8defce3311f9e69f1280a874d78bc1d16c305f6aa689640f7afa02842f -F src/shell.c.in d63f06c870ec1761ea98bd1cae651ff0ea6beadf8be892105dabd913f94cb3da +F src/shell.c.in 3bac9ab3c0ec1b8641b9ed25c0e2cf242b8bb186202d577b56fb16408dcacf24 F src/sqlite.h.in 8e70752a57597c08f64f3d49fc1fc46926b862d2e23b038b0d23b9cc748d88ea F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 83a3c4ce93d650bedfd1aa558cb85a516bd6d094445ee989740827d0d944368d @@ -1725,7 +1726,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 9fd0faf517993587d2f54212638545fc85fbbc84a031bcfae8c1e5894825d83b -R 408ce2695c1a501e1640fcd4bee57806 +P 05f6278a02e5cde89f76ced5af7d508e26576d7291dad7ee9e06b1a3be516cb0 +R 2fe85036f0f3b01feae669c6ee5a9387 +T *branch * shell-bindings +T *sym-shell-bindings * +T -sym-trunk * U drh -Z 4384bff1ecbee30802e6ddd37b479122 +Z 275e393030ab15b790049a2a3b9245cb diff --git a/manifest.uuid b/manifest.uuid index 839311ed43..487874bf11 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -05f6278a02e5cde89f76ced5af7d508e26576d7291dad7ee9e06b1a3be516cb0 \ No newline at end of file +1f2944d1d64d36b729450685974c260295cf1e32c204b71e27d20ebc4d65f9e8 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index c0e5c7a75c..c6ab3fd18c 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -940,6 +940,7 @@ INCLUDE ../ext/misc/shathree.c INCLUDE ../ext/misc/fileio.c INCLUDE ../ext/misc/completion.c INCLUDE ../ext/misc/appendvfs.c +INCLUDE ../ext/misc/bindvtab.c #ifdef SQLITE_HAVE_ZLIB INCLUDE ../ext/misc/zipfile.c INCLUDE ../ext/misc/sqlar.c @@ -3007,6 +3008,7 @@ static int shell_exec( } } + shell_bindings_apply(pStmt); exec_prepared_stmt(pArg, pStmt); explain_data_delete(pArg); eqp_render(pArg); @@ -3406,6 +3408,7 @@ static char zHelp[] = #if defined(SQLITE_ENABLE_SESSION) ".session CMD ... Create or control sessions\n" #endif + ".set KEY=VALUE Set bind parameter KEY to be string VALUE\n" ".sha3sum ?OPTIONS...? Compute a SHA3 hash of database content\n" #ifndef SQLITE_NOHAVE_SYSTEM ".shell CMD ARGS... Run CMD ARGS... in a system shell\n" @@ -3618,6 +3621,7 @@ static void open_db(ShellState *p, int keepAlive){ sqlite3_fileio_init(p->db, 0, 0); sqlite3_shathree_init(p->db, 0, 0); sqlite3_completion_init(p->db, 0, 0); + sqlite3_bindvtab_init(p->db, 0, 0); #ifdef SQLITE_HAVE_ZLIB sqlite3_zipfile_init(p->db, 0, 0); sqlite3_sqlar_init(p->db, 0, 0); @@ -7148,6 +7152,20 @@ static int do_meta_command(char *zLine, ShellState *p){ } }else + if( c=='s' && n==3 && strncmp(azArg[0],"set",3)==0 ){ + int x; + if( nArg<2 ){ + raw_printf(stderr, "Usage: .set KEY=VALUE\n"); + rc = 1; + goto meta_command_exit; + } + x = shell_bindings_new_text(azArg[1]); + if( x ){ + utf8_printf(stderr, "Error: bad setting: %s\n", azArg[1]); + } + }else + + if( c=='s' && n>=4 && strncmp(azArg[0],"sha3sum",n)==0 ){ const char *zLike = 0; /* Which table to checksum. 0 means everything */ int i; /* Loop counter */ @@ -8116,6 +8134,7 @@ static const char zOptions[] = " -column set output mode to 'column'\n" " -cmd COMMAND run \"COMMAND\" before reading stdin\n" " -csv set output mode to 'csv'\n" + " -Dkey=value set shell binding variable \"key\" to \"value\"\n" " -echo print commands before execution\n" " -init FILENAME read/process named file\n" " -[no]header turn headers on or off\n" @@ -8419,6 +8438,12 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ ** command, so ignore them */ break; #endif + }else if( strncmp(z, "-D",2)==0 ){ + int x; + x = shell_bindings_new_text(z+2); + if( x ){ + utf8_printf(stderr, "Error: bad binding: %s\n", z); + } } } verify_uninitialized(); @@ -8615,6 +8640,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ readStdin = 0; break; #endif + }else if( strncmp(z, "-D", 2)==0 ){ + /* Noop */ }else{ utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); raw_printf(stderr,"Use -help for a list of options.\n"); @@ -8693,6 +8720,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ } sqlite3_free(data.zFreeOnClose); find_home_dir(1); + shell_bindings_clear(); output_reset(&data); data.doXdgOpen = 0; clearTempFile(&data); -- 2.47.2