/ Changes On Branch shell-bindings
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch shell-bindings Excluding Merge-Ins

This is equivalent to a diff from 05f6278a02 to 7b2a65a654

2018-04-27
20:49
Better comments on the bindvtab.c implementation. All the two-argument version of the .set command. All bindings from .set and -D are still string. (Leaf check-in: 7b2a65a654 user: drh tags: shell-bindings)
17:39
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. (check-in: 1f2944d1d6 user: drh tags: shell-bindings)
16:35
Fix a test script error causing tests to fail in soak.test. (check-in: 462b52b121 user: dan tags: trunk)
15:17
Enhance the comments in the templatevtab.c implementation. (check-in: 05f6278a02 user: drh tags: trunk)
2018-04-26
18:34
The previous fix for ticket [d85fffd6ffe856092ed8da] in check-in [0a514e62ad1ebe5c12da8dae] did not completely address the probably in that it only worked for cases where the OP_SCopy that loaded the register was the last instruction in the sequence for the expression, which is not necessarily the case for expressions like CASE...END. This revision prevents the registered that will be recomputed from being cached in the first place. (check-in: 9fd0faf517 user: drh tags: trunk)

Changes to Makefile.in.

  1008   1008   # Source files that go into making shell.c
  1009   1009   SHELL_SRC = \
  1010   1010   	$(TOP)/src/shell.c.in \
  1011   1011           $(TOP)/ext/misc/appendvfs.c \
  1012   1012   	$(TOP)/ext/misc/shathree.c \
  1013   1013   	$(TOP)/ext/misc/fileio.c \
  1014   1014   	$(TOP)/ext/misc/completion.c \
         1015  +        $(TOP)/ext/misc/bindvtab.c \
  1015   1016   	$(TOP)/ext/misc/sqlar.c \
  1016   1017   	$(TOP)/ext/expert/sqlite3expert.c \
  1017   1018   	$(TOP)/ext/expert/sqlite3expert.h \
  1018   1019   	$(TOP)/ext/misc/zipfile.c \
  1019   1020           $(TOP)/src/test_windirent.c
  1020   1021   
  1021   1022   shell.c:	$(SHELL_SRC) $(TOP)/tool/mkshellc.tcl

Changes to Makefile.msc.

  2095   2095   # Source files that go into making shell.c
  2096   2096   SHELL_SRC = \
  2097   2097   	$(TOP)\src\shell.c.in \
  2098   2098   	$(TOP)\ext\misc\appendvfs.c \
  2099   2099   	$(TOP)\ext\misc\shathree.c \
  2100   2100   	$(TOP)\ext\misc\fileio.c \
  2101   2101   	$(TOP)\ext\misc\completion.c \
         2102  +        $(TOP)\ext\misc\bindvtab.c \
  2102   2103   	$(TOP)\ext\expert\sqlite3expert.c \
  2103   2104   	$(TOP)\ext\expert\sqlite3expert.h \
  2104   2105   	$(TOP)\src\test_windirent.c
  2105   2106   
  2106   2107   # If use of zlib is enabled, add the "zipfile.c" source file.
  2107   2108   #
  2108   2109   !IF $(USE_ZLIB)!=0

Added ext/misc/bindvtab.c.

            1  +/*
            2  +** 2018-04-27
            3  +**
            4  +** The author disclaims copyright to this source code.  In place of
            5  +** a legal notice, here is a blessing:
            6  +**
            7  +**    May you do good and not evil.
            8  +**    May you find forgiveness for yourself and forgive others.
            9  +**    May you share freely, never taking more than you give.
           10  +**
           11  +*************************************************************************
           12  +**
           13  +** This file implements a simple key/value store used to hold bind
           14  +** parameters for SQLite.  The key/value store is a singleton - there
           15  +** is exactly one per process.  The store can be accessed and controlled
           16  +** from SQLite using an eponymous virtual table.
           17  +**
           18  +** This is used to do parameter binding in the command-line shell.
           19  +**
           20  +** The ".set key=value" command and the "-Dkey=value" command-line option
           21  +** invoke shell_bindings_new_text() on the argument ("key=value") in order
           22  +** to create entries in the store.  The CLI then invokes
           23  +** shell_bindings_apply() on each prepared statement just prior to
           24  +** running it.
           25  +**
           26  +** All bindings are accessible through an eponymous-only virtual table
           27  +** named shell_bindings.  Ex:
           28  +**
           29  +**    INSERT INTO shell_bindings(k,v) VALUES('p1',12345);
           30  +**    SELECT $p1, typeof($p1);
           31  +**
           32  +** The above results in an answer of 12345,'integer'.  Bindings generated
           33  +** using the virtual table can have any type other than NULL.  But bindings
           34  +** generated by the .set command and the -D command-line option are always
           35  +** text.
           36  +**
           37  +** The CLI is single-threaded, so there is no attempt to make this code
           38  +** threadsafe.
           39  +**
           40  +** The key/value store is kept in a global list, and uses malloc/free rather
           41  +** than sqlite3_malloc64/sqlite3_free so that it can be completely independent
           42  +** of SQLite, can exist both before sqlite3_initialize() and after
           43  +** sqlite3_shutdown(), and so that it will persist across multiple
           44  +** connections created using ".open".
           45  +**
           46  +** The number of parameters is expected to be small, so they are stored
           47  +** on a simple linked list.  If this proves to be too inefficient, some other
           48  +** algorithm can be substituted in the future without changing the interface.
           49  +*/
           50  +#if !defined(SQLITEINT_H)
           51  +#include "sqlite3ext.h"
           52  +#endif
           53  +SQLITE_EXTENSION_INIT1
           54  +#include <string.h>
           55  +#include <assert.h>
           56  +#include <stdlib.h>
           57  +
           58  +/* Each entry in the key/value store */
           59  +typedef struct BindingEntry  BindingEntry;
           60  +struct BindingEntry {
           61  +  char *zKey;             /* Key */
           62  +  BindingEntry *pNext;    /* Next entry in the list */
           63  +  BindingEntry *pPrev;    /* Previous entry in the list */
           64  +  int eType;              /* SQLITE_INTEGER, _FLOAT, _TEXT, or _BLOB */
           65  +  int len;                /* Length for SQLITE_BLOB values */
           66  +  union {
           67  +    sqlite3_int64 i;         /* Integer value */
           68  +    double r;                /* Real value */
           69  +    char *z;                 /* Text value */
           70  +    unsigned char *b;        /* Blob value */
           71  +  } u;
           72  +};
           73  +
           74  +/* Global list of all entries */
           75  +static BindingEntry *global_pAll = 0;
           76  +
           77  +/* Locate any entry with the given key.  Return NULL if not found.
           78  +*/
           79  +static BindingEntry *shellBindingFind(const char *zKey){
           80  +  BindingEntry *p;
           81  +  for(p=global_pAll; p && strcmp(p->zKey,zKey)!=0; p = p->pNext){}
           82  +  return p;
           83  +}
           84  +
           85  +/* Delete any entry with the given key, if it exists.
           86  +*/
           87  +static void shellBindingDelete(const char *zKey){
           88  +  BindingEntry *p;
           89  +  p = shellBindingFind(zKey);
           90  +  if( p ){
           91  +    if( p->pNext ){
           92  +      p->pNext->pPrev = p->pPrev;
           93  +    }
           94  +    if( p->pPrev ){
           95  +      p->pPrev->pNext = p->pNext;
           96  +    }else{
           97  +      global_pAll = p->pNext;
           98  +    }
           99  +    free(p);
          100  +  }
          101  +}
          102  +
          103  +/* Insert a new shell binding */
          104  +static void shellBindingInsert(BindingEntry *p){
          105  +  p->pNext = global_pAll;
          106  +  if( global_pAll ) global_pAll->pPrev = p;
          107  +  global_pAll = p;
          108  +  p->pPrev = 0;
          109  +}
          110  +
          111  +/*
          112  +** True if c is a valid ID character.
          113  +*/
          114  +static int shellBindIdChar(char c){
          115  +  if( c>='a' && c<='z' ) return 1;
          116  +  if( c>='A' && c<='Z' ) return 1;
          117  +  if( c=='_' ) return 1;
          118  +  if( c>='0' && c<='9' ) return 2;
          119  +  return 0;
          120  +}
          121  +
          122  +/* Create a new binding given a string of the form "KEY=VALUE".  Return
          123  +** values:
          124  +**
          125  +**    0:    success
          126  +**    1:    out of memory
          127  +**    2:    Argument is not a valid KEY=VALUE string
          128  +**
          129  +** The type of VALUE is TEXT.
          130  +*/
          131  +int shell_bindings_new_text(const char *z){
          132  +  int i;
          133  +  int nKey;
          134  +  int nData;
          135  +  BindingEntry *p;
          136  +  for(i=0; shellBindIdChar(z[i]); i++){}
          137  +  if( i==0 ) return 2;
          138  +  if( shellBindIdChar(z[0])==2 ) return 2;
          139  +  nKey = i;
          140  +  if( z[i]!='=' ) return 2;
          141  +  for(nData=0; z[nKey+1+nData]; nData++){}
          142  +  p = malloc( sizeof(*p) + nKey + nData + 2 );
          143  +  if( p==0 ) return 1;
          144  +  memset(p, 0, sizeof(*p));
          145  +  p->zKey = (char*)&p[1];
          146  +  memcpy(p->zKey, z, nKey);
          147  +  p->zKey[nKey] = 0;
          148  +  p->u.z = &p->zKey[nKey+1];
          149  +  p->len = nData;
          150  +  p->eType = SQLITE_TEXT;
          151  +  memcpy(p->u.z, &z[nKey+1], nData+1);
          152  +  shellBindingDelete(p->zKey);
          153  +  shellBindingInsert(p);
          154  +  return 0;
          155  +}
          156  +
          157  +/*
          158  +** Delete all shell bindings
          159  +*/
          160  +void shell_bindings_clear(void){
          161  +  BindingEntry *pNext;
          162  +  while( global_pAll ){
          163  +    pNext = global_pAll->pNext;
          164  +    free(global_pAll);
          165  +    global_pAll = pNext;
          166  +  }
          167  +}
          168  +
          169  +/* Given a prepared statement, apply all bindings for which there are
          170  +** known values in the k-v store
          171  +*/
          172  +void shell_bindings_apply(sqlite3_stmt *pStmt){
          173  +  int n = sqlite3_bind_parameter_count(pStmt);
          174  +  int i;
          175  +  BindingEntry *p;
          176  +  for(i=1; i<=n; i++){
          177  +    const char *zKey = sqlite3_bind_parameter_name(pStmt, i);
          178  +    if( zKey==0 || zKey[0]==0 ) continue;
          179  +    zKey++;
          180  +    p = shellBindingFind(zKey);
          181  +    if( p==0 ) continue;
          182  +    switch( p->eType ){
          183  +      case SQLITE_INTEGER:
          184  +        sqlite3_bind_int64(pStmt, i, p->u.i);
          185  +        break;
          186  +      case SQLITE_FLOAT:
          187  +        sqlite3_bind_double(pStmt, i, p->u.r);
          188  +        break;
          189  +      case SQLITE_TEXT:
          190  +        sqlite3_bind_text(pStmt, i, p->u.z, p->len, SQLITE_TRANSIENT);
          191  +        break;
          192  +      case SQLITE_BLOB:
          193  +        sqlite3_bind_blob(pStmt, i, p->u.b, p->len, SQLITE_TRANSIENT);
          194  +        break;
          195  +    }
          196  +  }
          197  +}
          198  +
          199  +/* bindvtab_vtab is a subclass of sqlite3_vtab which is
          200  +** underlying representation of the virtual table
          201  +*/
          202  +typedef struct bindvtab_vtab bindvtab_vtab;
          203  +struct bindvtab_vtab {
          204  +  sqlite3_vtab base;  /* Base class - must be first */
          205  +};
          206  +
          207  +/* bindvtab_cursor is a subclass of sqlite3_vtab_cursor which will
          208  +** serve as the underlying representation of a cursor that scans
          209  +** over rows of the result
          210  +*/
          211  +typedef struct bindvtab_cursor bindvtab_cursor;
          212  +struct bindvtab_cursor {
          213  +  sqlite3_vtab_cursor base;  /* Base class - must be first */
          214  +  BindingEntry *p;           /* Current entry in the scan */
          215  +};
          216  +
          217  +/*
          218  +** The bindvtabConnect() method is invoked to create a new
          219  +** template virtual table.
          220  +**
          221  +** Think of this routine as the constructor for bindvtab_vtab objects.
          222  +**
          223  +** All this routine needs to do is:
          224  +**
          225  +**    (1) Allocate the bindvtab_vtab object and initialize all fields.
          226  +**
          227  +**    (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
          228  +**        result set of queries against the virtual table will look like.
          229  +*/
          230  +static int bindvtabConnect(
          231  +  sqlite3 *db,
          232  +  void *pAux,
          233  +  int argc, const char *const*argv,
          234  +  sqlite3_vtab **ppVtab,
          235  +  char **pzErr
          236  +){
          237  +  bindvtab_vtab *pNew;
          238  +  int rc;
          239  +
          240  +  rc = sqlite3_declare_vtab(db,
          241  +           "CREATE TABLE shell_bindings(k TEXT PRIMARY KEY,v)"
          242  +           " WITHOUT ROWID"
          243  +       );
          244  +  /* For convenience, define symbolic names for the index to each column. */
          245  +#define BINDVTAB_KEY    0
          246  +#define BINDVTAB_VALUE  1
          247  +  if( rc==SQLITE_OK ){
          248  +    pNew = sqlite3_malloc( sizeof(*pNew) );
          249  +    *ppVtab = (sqlite3_vtab*)pNew;
          250  +    if( pNew==0 ) return SQLITE_NOMEM;
          251  +    memset(pNew, 0, sizeof(*pNew));
          252  +  }
          253  +  return rc;
          254  +}
          255  +
          256  +/*
          257  +** This method is the destructor for bindvtab_vtab objects.
          258  +*/
          259  +static int bindvtabDisconnect(sqlite3_vtab *pVtab){
          260  +  bindvtab_vtab *p = (bindvtab_vtab*)pVtab;
          261  +  sqlite3_free(p);
          262  +  return SQLITE_OK;
          263  +}
          264  +
          265  +/*
          266  +** Constructor for a new bindvtab_cursor object.
          267  +*/
          268  +static int bindvtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
          269  +  bindvtab_cursor *pCur;
          270  +  pCur = sqlite3_malloc( sizeof(*pCur) );
          271  +  if( pCur==0 ) return SQLITE_NOMEM;
          272  +  memset(pCur, 0, sizeof(*pCur));
          273  +  *ppCursor = &pCur->base;
          274  +  return SQLITE_OK;
          275  +}
          276  +
          277  +/*
          278  +** Destructor for a bindvtab_cursor.
          279  +*/
          280  +static int bindvtabClose(sqlite3_vtab_cursor *cur){
          281  +  bindvtab_cursor *pCur = (bindvtab_cursor*)cur;
          282  +  sqlite3_free(pCur);
          283  +  return SQLITE_OK;
          284  +}
          285  +
          286  +
          287  +/*
          288  +** Advance a bindvtab_cursor to its next row of output.
          289  +*/
          290  +static int bindvtabNext(sqlite3_vtab_cursor *cur){
          291  +  bindvtab_cursor *pCur = (bindvtab_cursor*)cur;
          292  +  pCur->p = pCur->p->pNext;
          293  +  return SQLITE_OK;
          294  +}
          295  +
          296  +/*
          297  +** Return values of columns for the row at which the bindvtab_cursor
          298  +** is currently pointing.
          299  +*/
          300  +static int bindvtabColumn(
          301  +  sqlite3_vtab_cursor *cur,   /* The cursor */
          302  +  sqlite3_context *ctx,       /* First argument to sqlite3_result_...() */
          303  +  int i                       /* Which column to return */
          304  +){
          305  +  bindvtab_cursor *pCur = (bindvtab_cursor*)cur;
          306  +  BindingEntry *p = pCur->p;
          307  +  if( i==BINDVTAB_KEY ){
          308  +    sqlite3_result_text(ctx, p->zKey, -1, SQLITE_TRANSIENT);
          309  +  }else{
          310  +    assert( i==BINDVTAB_VALUE );
          311  +    switch( p->eType ){
          312  +      case SQLITE_INTEGER:
          313  +        sqlite3_result_int(ctx, p->u.i);
          314  +        break;
          315  +      case SQLITE_FLOAT:
          316  +        sqlite3_result_double(ctx, p->u.r);
          317  +        break;
          318  +      case SQLITE_TEXT:
          319  +        sqlite3_result_text(ctx, p->u.z, p->len, SQLITE_TRANSIENT);
          320  +        break;
          321  +      case SQLITE_BLOB:
          322  +        sqlite3_result_blob(ctx, p->u.b, p->len, SQLITE_TRANSIENT);
          323  +        break;
          324  +    }
          325  +  }
          326  +  return SQLITE_OK;
          327  +}
          328  +
          329  +/*
          330  +** Return the rowid for the current row.  In this implementation, the
          331  +** rowid is the same as the output value.
          332  +*/
          333  +static int bindvtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
          334  +  return SQLITE_OK;
          335  +}
          336  +
          337  +/*
          338  +** Return TRUE if the cursor has been moved off of the last
          339  +** row of output.
          340  +*/
          341  +static int bindvtabEof(sqlite3_vtab_cursor *cur){
          342  +  bindvtab_cursor *pCur = (bindvtab_cursor*)cur;
          343  +  return pCur->p==0;
          344  +}
          345  +
          346  +/*
          347  +** This method is called to "rewind" the bindvtab_cursor object back
          348  +** to the first row of output.  This method is always called at least
          349  +** once prior to any call to bindvtabColumn() or bindvtabRowid() or 
          350  +** bindvtabEof().
          351  +*/
          352  +static int bindvtabFilter(
          353  +  sqlite3_vtab_cursor *pVtabCursor, 
          354  +  int idxNum, const char *idxStr,
          355  +  int argc, sqlite3_value **argv
          356  +){
          357  +  bindvtab_cursor *pCur = (bindvtab_cursor *)pVtabCursor;
          358  +  pCur->p = global_pAll;
          359  +  return SQLITE_OK;
          360  +}
          361  +
          362  +/*
          363  +** SQLite will invoke this method one or more times while planning a query
          364  +** that uses the virtual table.  This routine needs to create
          365  +** a query plan for each invocation and compute an estimated cost for that
          366  +** plan.
          367  +*/
          368  +static int bindvtabBestIndex(
          369  +  sqlite3_vtab *tab,
          370  +  sqlite3_index_info *pIdxInfo
          371  +){
          372  +  pIdxInfo->estimatedCost = (double)10;
          373  +  pIdxInfo->estimatedRows = 10;
          374  +  return SQLITE_OK;
          375  +}
          376  +
          377  +/*
          378  +** Called to make changes to the shell bindings
          379  +*/
          380  +static int bindvtabUpdate(
          381  +  sqlite3_vtab *pVTab,
          382  +  int argc,
          383  +  sqlite3_value **argv,
          384  +  sqlite_int64 *pRowid
          385  +){
          386  +  const char *zKey;
          387  +  BindingEntry *p;
          388  +  int nKey;
          389  +  int len;
          390  +  int eType;
          391  +  if( sqlite3_value_type(argv[0])!=SQLITE_NULL ){
          392  +    zKey = (const char*)sqlite3_value_text(argv[0]);
          393  +    if( zKey ) shellBindingDelete(zKey);
          394  +  }
          395  +  if( argc==1 ) return SQLITE_OK;
          396  +  eType = sqlite3_value_type(argv[3]);
          397  +  if( eType==SQLITE_NULL ) return SQLITE_OK;
          398  +  zKey = (const char*)sqlite3_value_text(argv[2]);
          399  +  if( zKey==0 ) return SQLITE_OK;
          400  +  nKey = sqlite3_value_bytes(argv[2]);
          401  +  shellBindingDelete(zKey);
          402  +  if( eType==SQLITE_BLOB || eType==SQLITE_TEXT ){
          403  +    len = sqlite3_value_bytes(argv[3]);
          404  +  }else{
          405  +    len = 0;
          406  +  }
          407  +  p = malloc( sizeof(*p) + nKey + len + 2 );
          408  +  if( p==0 ) return SQLITE_NOMEM;
          409  +  memset(p, 0, sizeof(*p));
          410  +  p->zKey = (char*)&p[1];
          411  +  memcpy(p->zKey, zKey, nKey+1);
          412  +  p->eType = eType;
          413  +  switch( eType ){
          414  +    case SQLITE_INTEGER: 
          415  +       p->u.i = sqlite3_value_int64(argv[3]);
          416  +       break;
          417  +    case SQLITE_FLOAT: 
          418  +       p->u.r = sqlite3_value_double(argv[3]);
          419  +       break;
          420  +    case SQLITE_TEXT:
          421  +       p->u.z = &p->zKey[nKey+1];
          422  +       memcpy(p->u.z, sqlite3_value_text(argv[3]), len);
          423  +       break;
          424  +    case SQLITE_BLOB:
          425  +       p->u.b = (unsigned char*)&p->zKey[nKey+1];
          426  +       memcpy(p->u.b, sqlite3_value_blob(argv[3]), len);
          427  +       break;
          428  +  }
          429  +  shellBindingInsert(p);
          430  +  return SQLITE_OK;
          431  +}
          432  +
          433  +/*
          434  +** This following structure defines all the methods for the 
          435  +** virtual table.
          436  +*/
          437  +static sqlite3_module bindvtabModule = {
          438  +  /* iVersion    */ 0,
          439  +  /* xCreate     */ 0,
          440  +  /* xConnect    */ bindvtabConnect,
          441  +  /* xBestIndex  */ bindvtabBestIndex,
          442  +  /* xDisconnect */ bindvtabDisconnect,
          443  +  /* xDestroy    */ 0,
          444  +  /* xOpen       */ bindvtabOpen,
          445  +  /* xClose      */ bindvtabClose,
          446  +  /* xFilter     */ bindvtabFilter,
          447  +  /* xNext       */ bindvtabNext,
          448  +  /* xEof        */ bindvtabEof,
          449  +  /* xColumn     */ bindvtabColumn,
          450  +  /* xRowid      */ bindvtabRowid,
          451  +  /* xUpdate     */ bindvtabUpdate,
          452  +  /* xBegin      */ 0,
          453  +  /* xSync       */ 0,
          454  +  /* xCommit     */ 0,
          455  +  /* xRollback   */ 0,
          456  +  /* xFindMethod */ 0,
          457  +  /* xRename     */ 0,
          458  +  /* xSavepoint  */ 0,
          459  +  /* xRelease    */ 0,
          460  +  /* xRollbackTo */ 0
          461  +};
          462  +
          463  +
          464  +#ifdef _WIN32
          465  +__declspec(dllexport)
          466  +#endif
          467  +int sqlite3_bindvtab_init(
          468  +  sqlite3 *db, 
          469  +  char **pzErrMsg, 
          470  +  const sqlite3_api_routines *pApi
          471  +){
          472  +  int rc = SQLITE_OK;
          473  +  SQLITE_EXTENSION_INIT2(pApi);
          474  +  rc = sqlite3_create_module(db, "shell_bindings", &bindvtabModule, 0);
          475  +  return rc;
          476  +}

Changes to main.mk.

   703    703   # Source files that go into making shell.c
   704    704   SHELL_SRC = \
   705    705   	$(TOP)/src/shell.c.in \
   706    706           $(TOP)/ext/misc/appendvfs.c \
   707    707   	$(TOP)/ext/misc/shathree.c \
   708    708   	$(TOP)/ext/misc/fileio.c \
   709    709   	$(TOP)/ext/misc/completion.c \
          710  +        $(TOP)/ext/misc/bindvtab.c \
   710    711   	$(TOP)/ext/misc/sqlar.c \
   711    712   	$(TOP)/ext/expert/sqlite3expert.c \
   712    713   	$(TOP)/ext/expert/sqlite3expert.h \
   713    714   	$(TOP)/ext/misc/zipfile.c \
   714    715           $(TOP)/src/test_windirent.c
   715    716   
   716    717   shell.c:	$(SHELL_SRC) $(TOP)/tool/mkshellc.tcl

Changes to src/shell.c.in.

   936    936   INCLUDE test_windirent.c
   937    937   #define dirent DIRENT
   938    938   #endif
   939    939   INCLUDE ../ext/misc/shathree.c
   940    940   INCLUDE ../ext/misc/fileio.c
   941    941   INCLUDE ../ext/misc/completion.c
   942    942   INCLUDE ../ext/misc/appendvfs.c
          943  +INCLUDE ../ext/misc/bindvtab.c
   943    944   #ifdef SQLITE_HAVE_ZLIB
   944    945   INCLUDE ../ext/misc/zipfile.c
   945    946   INCLUDE ../ext/misc/sqlar.c
   946    947   #endif
   947    948   INCLUDE ../ext/expert/sqlite3expert.h
   948    949   INCLUDE ../ext/expert/sqlite3expert.c
   949    950   
................................................................................
  3003   3004           /* If the shell is currently in ".explain" mode, gather the extra
  3004   3005           ** data required to add indents to the output.*/
  3005   3006           if( pArg->cMode==MODE_Explain ){
  3006   3007             explain_data_prepare(pArg, pStmt);
  3007   3008           }
  3008   3009         }
  3009   3010   
         3011  +      shell_bindings_apply(pStmt);
  3010   3012         exec_prepared_stmt(pArg, pStmt);
  3011   3013         explain_data_delete(pArg);
  3012   3014         eqp_render(pArg);
  3013   3015   
  3014   3016         /* print usage stats if stats on */
  3015   3017         if( pArg && pArg->statsOn ){
  3016   3018           display_stats(db, pArg, 0);
................................................................................
  3402   3404     "                          Add --indent for pretty-printing\n"
  3403   3405     ".selftest ?--init?     Run tests defined in the SELFTEST table\n"
  3404   3406     ".separator COL ?ROW?   Change the column separator and optionally the row\n"
  3405   3407     "                         separator for both the output mode and .import\n"
  3406   3408   #if defined(SQLITE_ENABLE_SESSION)
  3407   3409     ".session CMD ...       Create or control sessions\n"
  3408   3410   #endif
         3411  +  ".set KEY=VALUE         Set bind parameter KEY to be string VALUE\n"
  3409   3412     ".sha3sum ?OPTIONS...?  Compute a SHA3 hash of database content\n"
  3410   3413   #ifndef SQLITE_NOHAVE_SYSTEM
  3411   3414     ".shell CMD ARGS...     Run CMD ARGS... in a system shell\n"
  3412   3415   #endif
  3413   3416     ".show                  Show the current values for various settings\n"
  3414   3417     ".stats ?on|off?        Show stats or turn stats on or off\n"
  3415   3418   #ifndef SQLITE_NOHAVE_SYSTEM
................................................................................
  3614   3617       }
  3615   3618   #ifndef SQLITE_OMIT_LOAD_EXTENSION
  3616   3619       sqlite3_enable_load_extension(p->db, 1);
  3617   3620   #endif
  3618   3621       sqlite3_fileio_init(p->db, 0, 0);
  3619   3622       sqlite3_shathree_init(p->db, 0, 0);
  3620   3623       sqlite3_completion_init(p->db, 0, 0);
         3624  +    sqlite3_bindvtab_init(p->db, 0, 0);
  3621   3625   #ifdef SQLITE_HAVE_ZLIB
  3622   3626       sqlite3_zipfile_init(p->db, 0, 0);
  3623   3627       sqlite3_sqlar_init(p->db, 0, 0);
  3624   3628   #endif
  3625   3629       sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0,
  3626   3630                               shellAddSchemaName, 0, 0);
  3627   3631       sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0,
................................................................................
  7143   7147                          "%.*s", (int)ArraySize(p->colSeparator)-1, azArg[1]);
  7144   7148       }
  7145   7149       if( nArg>=3 ){
  7146   7150         sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator,
  7147   7151                          "%.*s", (int)ArraySize(p->rowSeparator)-1, azArg[2]);
  7148   7152       }
  7149   7153     }else
         7154  +
         7155  +  if( c=='s' && n==3 && strncmp(azArg[0],"set",3)==0 ){
         7156  +    int x;
         7157  +    char *zKey = 0;
         7158  +    char *zToFree = 0;
         7159  +    if( nArg==2 ){
         7160  +      zKey = azArg[1];
         7161  +    }else if( nArg==3 ){
         7162  +      zKey = zToFree = sqlite3_mprintf("%s=%s",azArg[1],azArg[2]);
         7163  +    }else{
         7164  +      raw_printf(stderr,
         7165  +         "Usage: .set KEY VALUE\n   Or: .set KEY=VALUE\n"
         7166  +         "Use SQL on the \"shell_bindings\" table to query or delete keys.\n"
         7167  +      );
         7168  +      rc = 1;
         7169  +      goto meta_command_exit;
         7170  +    }
         7171  +    x = shell_bindings_new_text(zKey);
         7172  +    if( x ){
         7173  +      utf8_printf(stderr, "Error: bad setting: %s\n", zKey);
         7174  +    }
         7175  +    sqlite3_free(zToFree);
         7176  +  }else
         7177  +
  7150   7178   
  7151   7179     if( c=='s' && n>=4 && strncmp(azArg[0],"sha3sum",n)==0 ){
  7152   7180       const char *zLike = 0;   /* Which table to checksum. 0 means everything */
  7153   7181       int i;                   /* Loop counter */
  7154   7182       int bSchema = 0;         /* Also hash the schema */
  7155   7183       int bSeparate = 0;       /* Hash each table separately */
  7156   7184       int iSize = 224;         /* Hash algorithm to use */
................................................................................
  8112   8140     "   -append              append the database to the end of the file\n"
  8113   8141     "   -ascii               set output mode to 'ascii'\n"
  8114   8142     "   -bail                stop after hitting an error\n"
  8115   8143     "   -batch               force batch I/O\n"
  8116   8144     "   -column              set output mode to 'column'\n"
  8117   8145     "   -cmd COMMAND         run \"COMMAND\" before reading stdin\n"
  8118   8146     "   -csv                 set output mode to 'csv'\n"
         8147  +  "   -Dkey=value          set shell binding variable \"key\" to \"value\"\n"
  8119   8148     "   -echo                print commands before execution\n"
  8120   8149     "   -init FILENAME       read/process named file\n"
  8121   8150     "   -[no]header          turn headers on or off\n"
  8122   8151   #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
  8123   8152     "   -heap SIZE           Size of heap for memsys3 or memsys5\n"
  8124   8153   #endif
  8125   8154     "   -help                show this message\n"
................................................................................
  8415   8444         data.openMode = SHELL_OPEN_READONLY;
  8416   8445   #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
  8417   8446       }else if( strncmp(z, "-A",2)==0 ){
  8418   8447         /* All remaining command-line arguments are passed to the ".archive"
  8419   8448         ** command, so ignore them */
  8420   8449         break;
  8421   8450   #endif
         8451  +    }else if( strncmp(z, "-D",2)==0 ){
         8452  +      int x;
         8453  +      x = shell_bindings_new_text(z+2);
         8454  +      if( x ){
         8455  +        utf8_printf(stderr, "Error: bad binding: %s\n", z);
         8456  +      }
  8422   8457       }
  8423   8458     }
  8424   8459     verify_uninitialized();
  8425   8460   
  8426   8461   
  8427   8462   #ifdef SQLITE_SHELL_INIT_PROC
  8428   8463     {
................................................................................
  8611   8646           arDotCommand(&data, argv+(i-1), argc-(i-1));
  8612   8647         }else{
  8613   8648           arDotCommand(&data, argv+i, argc-i);
  8614   8649         }
  8615   8650         readStdin = 0;
  8616   8651         break;
  8617   8652   #endif
         8653  +    }else if( strncmp(z, "-D", 2)==0 ){
         8654  +      /* Noop */
  8618   8655       }else{
  8619   8656         utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
  8620   8657         raw_printf(stderr,"Use -help for a list of options.\n");
  8621   8658         return 1;
  8622   8659       }
  8623   8660       data.cMode = data.mode;
  8624   8661     }
................................................................................
  8689   8726     set_table_name(&data, 0);
  8690   8727     if( data.db ){
  8691   8728       session_close_all(&data);
  8692   8729       sqlite3_close(data.db);
  8693   8730     }
  8694   8731     sqlite3_free(data.zFreeOnClose);
  8695   8732     find_home_dir(1);
         8733  +  shell_bindings_clear();
  8696   8734     output_reset(&data);
  8697   8735     data.doXdgOpen = 0;
  8698   8736     clearTempFile(&data);
  8699   8737   #if !SQLITE_SHELL_IS_UTF8
  8700   8738     for(i=0; i<argc; i++) free(argv[i]);
  8701   8739     free(argv);
  8702   8740   #endif
  8703   8741     return rc;
  8704   8742   }