]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add the ability to use bind parameters in the CLI. The new ".set KEY=VALUE"
authordrh <drh@noemail.net>
Fri, 27 Apr 2018 17:39:22 +0000 (17:39 +0000)
committerdrh <drh@noemail.net>
Fri, 27 Apr 2018 17:39:22 +0000 (17:39 +0000)
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
Makefile.msc
ext/misc/bindvtab.c [new file with mode: 0644]
main.mk
manifest
manifest.uuid
src/shell.c.in

index a3ec869643c50aac67c39b499e1a35b9a85855f6..1c2bed97278788bee2ad55e73864d4e00b7ff50b 100644 (file)
@@ -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 \
index 50119d836110bab1799a65e31cc4e86f12de1124..faa7206f3e3a8c32d539c438b863641028b514e8 100644 (file)
@@ -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 (file)
index 0000000..b031ddf
--- /dev/null
@@ -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 <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+/* 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 b590b79cb1bb0a4ff185b5dc3f570fbd4a6ed835..68e209975c5a4b14eb11353901ec7ba41c2a2f24 100644 (file)
--- 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 \
index cb3e447750eee499b0be302c5cf6b58742218612..241aefa036b90e8e4c98e33da1e1d568375849f5 100644 (file)
--- 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
index 839311ed430fdba57480ad611aa7d56a15d382f2..487874bf116d91da8b9763334ae9e38b4077f46f 100644 (file)
@@ -1 +1 @@
-05f6278a02e5cde89f76ced5af7d508e26576d7291dad7ee9e06b1a3be516cb0
\ No newline at end of file
+1f2944d1d64d36b729450685974c260295cf1e32c204b71e27d20ebc4d65f9e8
\ No newline at end of file
index c0e5c7a75c96675e6438e3e4311c6f9dda9a9e59..c6ab3fd18c633df1bd45b8a546c1c28752589534 100644 (file)
@@ -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);