SQLite

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 05f6278a to 7b2a65a6

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: 7b2a65a6 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: 1f2944d1 user: drh tags: shell-bindings)
16:35
Fix a test script error causing tests to fail in soak.test. (check-in: 462b52b1 user: dan tags: trunk)
15:17
Enhance the comments in the templatevtab.c implementation. (check-in: 05f6278a 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: 9fd0faf5 user: drh tags: trunk)

Changes to Makefile.in.

1008
1009
1010
1011
1012
1013
1014

1015
1016
1017
1018
1019
1020
1021
# Source files that go into making shell.c
SHELL_SRC = \
	$(TOP)/src/shell.c.in \
        $(TOP)/ext/misc/appendvfs.c \
	$(TOP)/ext/misc/shathree.c \
	$(TOP)/ext/misc/fileio.c \
	$(TOP)/ext/misc/completion.c \

	$(TOP)/ext/misc/sqlar.c \
	$(TOP)/ext/expert/sqlite3expert.c \
	$(TOP)/ext/expert/sqlite3expert.h \
	$(TOP)/ext/misc/zipfile.c \
        $(TOP)/src/test_windirent.c

shell.c:	$(SHELL_SRC) $(TOP)/tool/mkshellc.tcl







>







1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
# Source files that go into making shell.c
SHELL_SRC = \
	$(TOP)/src/shell.c.in \
        $(TOP)/ext/misc/appendvfs.c \
	$(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 \
	$(TOP)/ext/misc/zipfile.c \
        $(TOP)/src/test_windirent.c

shell.c:	$(SHELL_SRC) $(TOP)/tool/mkshellc.tcl

Changes to Makefile.msc.

2095
2096
2097
2098
2099
2100
2101

2102
2103
2104
2105
2106
2107
2108
# Source files that go into making shell.c
SHELL_SRC = \
	$(TOP)\src\shell.c.in \
	$(TOP)\ext\misc\appendvfs.c \
	$(TOP)\ext\misc\shathree.c \
	$(TOP)\ext\misc\fileio.c \
	$(TOP)\ext\misc\completion.c \

	$(TOP)\ext\expert\sqlite3expert.c \
	$(TOP)\ext\expert\sqlite3expert.h \
	$(TOP)\src\test_windirent.c

# If use of zlib is enabled, add the "zipfile.c" source file.
#
!IF $(USE_ZLIB)!=0







>







2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
# Source files that go into making shell.c
SHELL_SRC = \
	$(TOP)\src\shell.c.in \
	$(TOP)\ext\misc\appendvfs.c \
	$(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

# If use of zlib is enabled, add the "zipfile.c" source file.
#
!IF $(USE_ZLIB)!=0

Added ext/misc/bindvtab.c.

























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
/*
** 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.
**
** This is used to do parameter binding in the command-line shell.
**
** The ".set key=value" command and the "-Dkey=value" command-line option
** invoke shell_bindings_new_text() on the argument ("key=value") in order
** to create entries in the store.  The CLI then invokes
** shell_bindings_apply() on each prepared statement just prior to
** running it.
**
** All bindings are accessible through an eponymous-only virtual table
** named shell_bindings.  Ex:
**
**    INSERT INTO shell_bindings(k,v) VALUES('p1',12345);
**    SELECT $p1, typeof($p1);
**
** The above results in an answer of 12345,'integer'.  Bindings generated
** using the virtual table can have any type other than NULL.  But bindings
** generated by the .set command and the -D command-line option are always
** text.
**
** The CLI is single-threaded, so there is no attempt to make this code
** threadsafe.
**
** The key/value store is kept in a global list, and uses malloc/free rather
** than sqlite3_malloc64/sqlite3_free so that it can be completely independent
** of SQLite, can exist both before sqlite3_initialize() and after
** sqlite3_shutdown(), and so that it will persist across multiple
** connections created using ".open".
**
** The number of parameters is expected to be small, so they are stored
** on a simple linked list.  If this proves to be too inefficient, some other
** algorithm can be substituted in the future without changing the interface.
*/
#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;
}

Changes to main.mk.

703
704
705
706
707
708
709

710
711
712
713
714
715
716
# Source files that go into making shell.c
SHELL_SRC = \
	$(TOP)/src/shell.c.in \
        $(TOP)/ext/misc/appendvfs.c \
	$(TOP)/ext/misc/shathree.c \
	$(TOP)/ext/misc/fileio.c \
	$(TOP)/ext/misc/completion.c \

	$(TOP)/ext/misc/sqlar.c \
	$(TOP)/ext/expert/sqlite3expert.c \
	$(TOP)/ext/expert/sqlite3expert.h \
	$(TOP)/ext/misc/zipfile.c \
        $(TOP)/src/test_windirent.c

shell.c:	$(SHELL_SRC) $(TOP)/tool/mkshellc.tcl







>







703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
# Source files that go into making shell.c
SHELL_SRC = \
	$(TOP)/src/shell.c.in \
        $(TOP)/ext/misc/appendvfs.c \
	$(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 \
	$(TOP)/ext/misc/zipfile.c \
        $(TOP)/src/test_windirent.c

shell.c:	$(SHELL_SRC) $(TOP)/tool/mkshellc.tcl

Changes to src/shell.c.in.

936
937
938
939
940
941
942

943
944
945
946
947
948
949
INCLUDE test_windirent.c
#define dirent DIRENT
#endif
INCLUDE ../ext/misc/shathree.c
INCLUDE ../ext/misc/fileio.c
INCLUDE ../ext/misc/completion.c
INCLUDE ../ext/misc/appendvfs.c

#ifdef SQLITE_HAVE_ZLIB
INCLUDE ../ext/misc/zipfile.c
INCLUDE ../ext/misc/sqlar.c
#endif
INCLUDE ../ext/expert/sqlite3expert.h
INCLUDE ../ext/expert/sqlite3expert.c








>







936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
INCLUDE test_windirent.c
#define dirent DIRENT
#endif
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
#endif
INCLUDE ../ext/expert/sqlite3expert.h
INCLUDE ../ext/expert/sqlite3expert.c

3003
3004
3005
3006
3007
3008
3009

3010
3011
3012
3013
3014
3015
3016
        /* If the shell is currently in ".explain" mode, gather the extra
        ** data required to add indents to the output.*/
        if( pArg->cMode==MODE_Explain ){
          explain_data_prepare(pArg, pStmt);
        }
      }


      exec_prepared_stmt(pArg, pStmt);
      explain_data_delete(pArg);
      eqp_render(pArg);

      /* print usage stats if stats on */
      if( pArg && pArg->statsOn ){
        display_stats(db, pArg, 0);







>







3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
        /* If the shell is currently in ".explain" mode, gather the extra
        ** data required to add indents to the output.*/
        if( pArg->cMode==MODE_Explain ){
          explain_data_prepare(pArg, pStmt);
        }
      }

      shell_bindings_apply(pStmt);
      exec_prepared_stmt(pArg, pStmt);
      explain_data_delete(pArg);
      eqp_render(pArg);

      /* print usage stats if stats on */
      if( pArg && pArg->statsOn ){
        display_stats(db, pArg, 0);
3402
3403
3404
3405
3406
3407
3408

3409
3410
3411
3412
3413
3414
3415
  "                          Add --indent for pretty-printing\n"
  ".selftest ?--init?     Run tests defined in the SELFTEST table\n"
  ".separator COL ?ROW?   Change the column separator and optionally the row\n"
  "                         separator for both the output mode and .import\n"
#if defined(SQLITE_ENABLE_SESSION)
  ".session CMD ...       Create or control sessions\n"
#endif

  ".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"
#endif
  ".show                  Show the current values for various settings\n"
  ".stats ?on|off?        Show stats or turn stats on or off\n"
#ifndef SQLITE_NOHAVE_SYSTEM







>







3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
  "                          Add --indent for pretty-printing\n"
  ".selftest ?--init?     Run tests defined in the SELFTEST table\n"
  ".separator COL ?ROW?   Change the column separator and optionally the row\n"
  "                         separator for both the output mode and .import\n"
#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"
#endif
  ".show                  Show the current values for various settings\n"
  ".stats ?on|off?        Show stats or turn stats on or off\n"
#ifndef SQLITE_NOHAVE_SYSTEM
3614
3615
3616
3617
3618
3619
3620

3621
3622
3623
3624
3625
3626
3627
    }
#ifndef SQLITE_OMIT_LOAD_EXTENSION
    sqlite3_enable_load_extension(p->db, 1);
#endif
    sqlite3_fileio_init(p->db, 0, 0);
    sqlite3_shathree_init(p->db, 0, 0);
    sqlite3_completion_init(p->db, 0, 0);

#ifdef SQLITE_HAVE_ZLIB
    sqlite3_zipfile_init(p->db, 0, 0);
    sqlite3_sqlar_init(p->db, 0, 0);
#endif
    sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0,
                            shellAddSchemaName, 0, 0);
    sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0,







>







3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
    }
#ifndef SQLITE_OMIT_LOAD_EXTENSION
    sqlite3_enable_load_extension(p->db, 1);
#endif
    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);
#endif
    sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0,
                            shellAddSchemaName, 0, 0);
    sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0,
7143
7144
7145
7146
7147
7148
7149
























7150
7151
7152
7153
7154
7155
7156
                       "%.*s", (int)ArraySize(p->colSeparator)-1, azArg[1]);
    }
    if( nArg>=3 ){
      sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator,
                       "%.*s", (int)ArraySize(p->rowSeparator)-1, azArg[2]);
    }
  }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 */
    int bSchema = 0;         /* Also hash the schema */
    int bSeparate = 0;       /* Hash each table separately */
    int iSize = 224;         /* Hash algorithm to use */







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







7147
7148
7149
7150
7151
7152
7153
7154
7155
7156
7157
7158
7159
7160
7161
7162
7163
7164
7165
7166
7167
7168
7169
7170
7171
7172
7173
7174
7175
7176
7177
7178
7179
7180
7181
7182
7183
7184
                       "%.*s", (int)ArraySize(p->colSeparator)-1, azArg[1]);
    }
    if( nArg>=3 ){
      sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator,
                       "%.*s", (int)ArraySize(p->rowSeparator)-1, azArg[2]);
    }
  }else

  if( c=='s' && n==3 && strncmp(azArg[0],"set",3)==0 ){
    int x;
    char *zKey = 0;
    char *zToFree = 0;
    if( nArg==2 ){
      zKey = azArg[1];
    }else if( nArg==3 ){
      zKey = zToFree = sqlite3_mprintf("%s=%s",azArg[1],azArg[2]);
    }else{
      raw_printf(stderr,
         "Usage: .set KEY VALUE\n   Or: .set KEY=VALUE\n"
         "Use SQL on the \"shell_bindings\" table to query or delete keys.\n"
      );
      rc = 1;
      goto meta_command_exit;
    }
    x = shell_bindings_new_text(zKey);
    if( x ){
      utf8_printf(stderr, "Error: bad setting: %s\n", zKey);
    }
    sqlite3_free(zToFree);
  }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 */
    int bSchema = 0;         /* Also hash the schema */
    int bSeparate = 0;       /* Hash each table separately */
    int iSize = 224;         /* Hash algorithm to use */
8112
8113
8114
8115
8116
8117
8118

8119
8120
8121
8122
8123
8124
8125
  "   -append              append the database to the end of the file\n"
  "   -ascii               set output mode to 'ascii'\n"
  "   -bail                stop after hitting an error\n"
  "   -batch               force batch I/O\n"
  "   -column              set output mode to 'column'\n"
  "   -cmd COMMAND         run \"COMMAND\" before reading stdin\n"
  "   -csv                 set output mode to 'csv'\n"

  "   -echo                print commands before execution\n"
  "   -init FILENAME       read/process named file\n"
  "   -[no]header          turn headers on or off\n"
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
  "   -heap SIZE           Size of heap for memsys3 or memsys5\n"
#endif
  "   -help                show this message\n"







>







8140
8141
8142
8143
8144
8145
8146
8147
8148
8149
8150
8151
8152
8153
8154
  "   -append              append the database to the end of the file\n"
  "   -ascii               set output mode to 'ascii'\n"
  "   -bail                stop after hitting an error\n"
  "   -batch               force batch I/O\n"
  "   -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"
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
  "   -heap SIZE           Size of heap for memsys3 or memsys5\n"
#endif
  "   -help                show this message\n"
8415
8416
8417
8418
8419
8420
8421






8422
8423
8424
8425
8426
8427
8428
      data.openMode = SHELL_OPEN_READONLY;
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
    }else if( strncmp(z, "-A",2)==0 ){
      /* All remaining command-line arguments are passed to the ".archive"
      ** command, so ignore them */
      break;
#endif






    }
  }
  verify_uninitialized();


#ifdef SQLITE_SHELL_INIT_PROC
  {







>
>
>
>
>
>







8444
8445
8446
8447
8448
8449
8450
8451
8452
8453
8454
8455
8456
8457
8458
8459
8460
8461
8462
8463
      data.openMode = SHELL_OPEN_READONLY;
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
    }else if( strncmp(z, "-A",2)==0 ){
      /* All remaining command-line arguments are passed to the ".archive"
      ** 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();


#ifdef SQLITE_SHELL_INIT_PROC
  {
8611
8612
8613
8614
8615
8616
8617


8618
8619
8620
8621
8622
8623
8624
        arDotCommand(&data, argv+(i-1), argc-(i-1));
      }else{
        arDotCommand(&data, argv+i, argc-i);
      }
      readStdin = 0;
      break;
#endif


    }else{
      utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
      raw_printf(stderr,"Use -help for a list of options.\n");
      return 1;
    }
    data.cMode = data.mode;
  }







>
>







8646
8647
8648
8649
8650
8651
8652
8653
8654
8655
8656
8657
8658
8659
8660
8661
        arDotCommand(&data, argv+(i-1), argc-(i-1));
      }else{
        arDotCommand(&data, argv+i, argc-i);
      }
      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");
      return 1;
    }
    data.cMode = data.mode;
  }
8689
8690
8691
8692
8693
8694
8695

8696
8697
8698
8699
8700
8701
8702
8703
8704
  set_table_name(&data, 0);
  if( data.db ){
    session_close_all(&data);
    sqlite3_close(data.db);
  }
  sqlite3_free(data.zFreeOnClose);
  find_home_dir(1);

  output_reset(&data);
  data.doXdgOpen = 0;
  clearTempFile(&data);
#if !SQLITE_SHELL_IS_UTF8
  for(i=0; i<argc; i++) free(argv[i]);
  free(argv);
#endif
  return rc;
}







>









8726
8727
8728
8729
8730
8731
8732
8733
8734
8735
8736
8737
8738
8739
8740
8741
8742
  set_table_name(&data, 0);
  if( data.db ){
    session_close_all(&data);
    sqlite3_close(data.db);
  }
  sqlite3_free(data.zFreeOnClose);
  find_home_dir(1);
  shell_bindings_clear();
  output_reset(&data);
  data.doXdgOpen = 0;
  clearTempFile(&data);
#if !SQLITE_SHELL_IS_UTF8
  for(i=0; i<argc; i++) free(argv[i]);
  free(argv);
#endif
  return rc;
}