Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c81e845340 | |||
| ec814a8992 |
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,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;
|
||||
}
|
||||
@@ -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 \
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
C Enhance\sthe\scomments\sin\sthe\stemplatevtab.c\simplementation.
|
||||
D 2018-04-27T15:17:08.924
|
||||
C Better\scomments\son\sthe\sbindvtab.c\simplementation.\s\sAll\sthe\stwo-argument\nversion\sof\sthe\s.set\scommand.\s\sAll\sbindings\sfrom\s.set\sand\s-D\sare\sstill\nstring.
|
||||
D 2018-04-27T20:49:13.739
|
||||
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 70ca40ff9bec3cc6da53e09bf13e0af858dc210e68e41b7915d4ff71c363819c
|
||||
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 d751c9e8aa9c73a700d4033d2bfcfccad90c476b1fcb0d406e3f6b140e0f02c0
|
||||
F src/sqlite.h.in 8e70752a57597c08f64f3d49fc1fc46926b862d2e23b038b0d23b9cc748d88ea
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 83a3c4ce93d650bedfd1aa558cb85a516bd6d094445ee989740827d0d944368d
|
||||
@@ -1725,7 +1726,7 @@ 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 1f2944d1d64d36b729450685974c260295cf1e32c204b71e27d20ebc4d65f9e8
|
||||
R 8b8e4c122ecc932439216f69c14e3e66
|
||||
U drh
|
||||
Z 4384bff1ecbee30802e6ddd37b479122
|
||||
Z 42c91d81ee6385ae52b3a143c0c973f3
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
05f6278a02e5cde89f76ced5af7d508e26576d7291dad7ee9e06b1a3be516cb0
|
||||
7b2a65a65475b80669af4ff5ac7adb6b310cd8275a36e672a3f5c88a259693c0
|
||||
@@ -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,30 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
}
|
||||
}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 */
|
||||
@@ -8116,6 +8144,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 +8448,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 +8650,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 +8730,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);
|
||||
|
||||
Reference in New Issue
Block a user