summaryrefslogtreecommitdiff
path: root/upstream/shell.c
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2023-01-23 20:06:01 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2023-01-23 20:06:01 +0300
commitee398fb5539afa74875cb5fcf08428e18059a426 (patch)
tree5ce2aa43f59d9262f2a68703402cebe4b10380ca /upstream/shell.c
parentd93edb6587eead9c8207155f0a8e0694beff3686 (diff)
Upgrade to 3.39.4
Diffstat (limited to 'upstream/shell.c')
-rw-r--r--upstream/shell.c6580
1 files changed, 4414 insertions, 2166 deletions
diff --git a/upstream/shell.c b/upstream/shell.c
index 27de22a..e66ae08 100644
--- a/upstream/shell.c
+++ b/upstream/shell.c
@@ -36,6 +36,18 @@
#endif
/*
+** Optionally #include a user-defined header, whereby compilation options
+** may be set prior to where they take effect, but after platform setup.
+** If SQLITE_CUSTOM_INCLUDE=? is defined, its value names the #include
+** file. Note that this macro has a like effect on sqlite3.c compilation.
+*/
+# define SHELL_STRINGIFY_(f) #f
+# define SHELL_STRINGIFY(f) SHELL_STRINGIFY_(f)
+#ifdef SQLITE_CUSTOM_INCLUDE
+# include SHELL_STRINGIFY(SQLITE_CUSTOM_INCLUDE)
+#endif
+
+/*
** Determine if we are dealing with WinRT, which provides only a subset of
** the full Win32 API.
*/
@@ -235,6 +247,16 @@ static void setTextMode(FILE *file, int isOutput){
# define setTextMode(X,Y)
#endif
+/*
+** When compiling with emcc (a.k.a. emscripten), we're building a
+** WebAssembly (WASM) bundle and need to disable and rewire a few
+** things.
+*/
+#ifdef __EMSCRIPTEN__
+#define SQLITE_SHELL_WASM_MODE
+#else
+#undef SQLITE_SHELL_WASM_MODE
+#endif
/* True if the timer is enabled */
static int enableTimer = 0;
@@ -244,6 +266,7 @@ static sqlite3_int64 timeOfDay(void){
static sqlite3_vfs *clockVfs = 0;
sqlite3_int64 t;
if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0);
+ if( clockVfs==0 ) return 0; /* Never actually happens */
if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){
clockVfs->xCurrentTimeInt64(clockVfs, &t);
}else{
@@ -432,15 +455,6 @@ static sqlite3 *globalDb = 0;
*/
static volatile int seenInterrupt = 0;
-#ifdef SQLITE_DEBUG
-/*
-** Out-of-memory simulator variables
-*/
-static unsigned int oomCounter = 0; /* Simulate OOM when equals 1 */
-static unsigned int oomRepeat = 0; /* Number of OOMs in a row */
-static void*(*defaultMalloc)(int) = 0; /* The low-level malloc routine */
-#endif /* SQLITE_DEBUG */
-
/*
** This is the name of our program. It is set in main(), used
** in a number of other places, mostly for error messages.
@@ -492,48 +506,12 @@ static void shell_out_of_memory(void){
exit(1);
}
-#ifdef SQLITE_DEBUG
-/* This routine is called when a simulated OOM occurs. It is broken
-** out as a separate routine to make it easy to set a breakpoint on
-** the OOM
-*/
-void shellOomFault(void){
- if( oomRepeat>0 ){
- oomRepeat--;
- }else{
- oomCounter--;
- }
-}
-#endif /* SQLITE_DEBUG */
-
-#ifdef SQLITE_DEBUG
-/* This routine is a replacement malloc() that is used to simulate
-** Out-Of-Memory (OOM) errors for testing purposes.
+/* Check a pointer to see if it is NULL. If it is NULL, exit with an
+** out-of-memory error.
*/
-static void *oomMalloc(int nByte){
- if( oomCounter ){
- if( oomCounter==1 ){
- shellOomFault();
- return 0;
- }else{
- oomCounter--;
- }
- }
- return defaultMalloc(nByte);
-}
-#endif /* SQLITE_DEBUG */
-
-#ifdef SQLITE_DEBUG
-/* Register the OOM simulator. This must occur before any memory
-** allocations */
-static void registerOomSimulator(void){
- sqlite3_mem_methods mem;
- sqlite3_config(SQLITE_CONFIG_GETMALLOC, &mem);
- defaultMalloc = mem.xMalloc;
- mem.xMalloc = oomMalloc;
- sqlite3_config(SQLITE_CONFIG_MALLOC, &mem);
+static void shell_check_oom(void *p){
+ if( p==0 ) shell_out_of_memory();
}
-#endif
/*
** Write I/O traces to the following stream.
@@ -640,19 +618,38 @@ static int strlenChar(const char *z){
}
/*
-** Return true if zFile does not exist or if it is not an ordinary file.
+** Return open FILE * if zFile exists, can be opened for read
+** and is an ordinary file or a character stream source.
+** Otherwise return 0.
*/
+static FILE * openChrSource(const char *zFile){
#ifdef _WIN32
-# define notNormalFile(X) 0
+ struct _stat x = {0};
+# define STAT_CHR_SRC(mode) ((mode & (_S_IFCHR|_S_IFIFO|_S_IFREG))!=0)
+ /* On Windows, open first, then check the stream nature. This order
+ ** is necessary because _stat() and sibs, when checking a named pipe,
+ ** effectively break the pipe as its supplier sees it. */
+ FILE *rv = fopen(zFile, "rb");
+ if( rv==0 ) return 0;
+ if( _fstat(_fileno(rv), &x) != 0
+ || !STAT_CHR_SRC(x.st_mode)){
+ fclose(rv);
+ rv = 0;
+ }
+ return rv;
#else
-static int notNormalFile(const char *zFile){
- struct stat x;
- int rc;
- memset(&x, 0, sizeof(x));
- rc = stat(zFile, &x);
- return rc || !S_ISREG(x.st_mode);
-}
+ struct stat x = {0};
+ int rc = stat(zFile, &x);
+# define STAT_CHR_SRC(mode) (S_ISREG(mode)||S_ISFIFO(mode)||S_ISCHR(mode))
+ if( rc!=0 ) return 0;
+ if( STAT_CHR_SRC(x.st_mode) ){
+ return fopen(zFile, "rb");
+ }else{
+ return 0;
+ }
#endif
+#undef STAT_CHR_SRC
+}
/*
** This routine reads a line of text from FILE in, stores
@@ -671,7 +668,7 @@ static char *local_getline(char *zLine, FILE *in){
if( n+100>nLine ){
nLine = nLine*2 + 100;
zLine = realloc(zLine, nLine);
- if( zLine==0 ) shell_out_of_memory();
+ shell_check_oom(zLine);
}
if( fgets(&zLine[n], nLine - n, in)==0 ){
if( n==0 ){
@@ -698,7 +695,7 @@ static char *local_getline(char *zLine, FILE *in){
int nTrans = strlen30(zTrans)+1;
if( nTrans>nLine ){
zLine = realloc(zLine, nTrans);
- if( zLine==0 ) shell_out_of_memory();
+ shell_check_oom(zLine);
}
memcpy(zLine, zTrans, nTrans);
sqlite3_free(zTrans);
@@ -722,6 +719,7 @@ static char *local_getline(char *zLine, FILE *in){
** be freed by the caller or else passed back into this routine via the
** zPrior argument for reuse.
*/
+#ifndef SQLITE_SHELL_WASM_MODE
static char *one_input_line(FILE *in, char *zPrior, int isContinuation){
char *zPrompt;
char *zResult;
@@ -741,7 +739,7 @@ static char *one_input_line(FILE *in, char *zPrior, int isContinuation){
}
return zResult;
}
-
+#endif /* !SQLITE_SHELL_WASM_MODE */
/*
** Return the value of a hexadecimal digit. Return -1 if the input
@@ -829,7 +827,7 @@ static void freeText(ShellText *p){
** If the third argument, quote, is not '\0', then it is used as a
** quote character for zAppend.
*/
-static void appendText(ShellText *p, char const *zAppend, char quote){
+static void appendText(ShellText *p, const char *zAppend, char quote){
int len;
int i;
int nAppend = strlen30(zAppend);
@@ -842,10 +840,10 @@ static void appendText(ShellText *p, char const *zAppend, char quote){
}
}
- if( p->n+len>=p->nAlloc ){
+ if( p->z==0 || p->n+len>=p->nAlloc ){
p->nAlloc = p->nAlloc*2 + len + 20;
p->z = realloc(p->z, p->nAlloc);
- if( p->z==0 ) shell_out_of_memory();
+ shell_check_oom(p->z);
}
if( quote ){
@@ -900,6 +898,7 @@ static char *shellFakeSchema(
zSql = sqlite3_mprintf("PRAGMA \"%w\".table_info=%Q;",
zSchema ? zSchema : "main", zName);
+ shell_check_oom(zSql);
sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
initText(&s);
@@ -916,6 +915,7 @@ static char *shellFakeSchema(
nRow++;
appendText(&s, zDiv, 0);
zDiv = ",";
+ if( zCol==0 ) zCol = "";
cQuote = quoteChar(zCol);
appendText(&s, zCol, cQuote);
}
@@ -939,9 +939,11 @@ static void shellModuleSchema(
int nVal,
sqlite3_value **apVal
){
- const char *zName = (const char*)sqlite3_value_text(apVal[0]);
- char *zFake = shellFakeSchema(sqlite3_context_db_handle(pCtx), 0, zName);
+ const char *zName;
+ char *zFake;
UNUSED_PARAMETER(nVal);
+ zName = (const char*)sqlite3_value_text(apVal[0]);
+ zFake = zName ? shellFakeSchema(sqlite3_context_db_handle(pCtx), 0, zName) : 0;
if( zFake ){
sqlite3_result_text(pCtx, sqlite3_mprintf("/* %s */", zFake),
-1, sqlite3_free);
@@ -988,7 +990,7 @@ static void shellAddSchemaName(
sqlite3 *db = sqlite3_context_db_handle(pCtx);
UNUSED_PARAMETER(nVal);
if( zIn!=0 && strncmp(zIn, "CREATE ", 7)==0 ){
- for(i=0; i<(int)(sizeof(aPrefix)/sizeof(aPrefix[0])); i++){
+ for(i=0; i<ArraySize(aPrefix); i++){
int n = strlen30(aPrefix[i]);
if( strncmp(zIn+7, aPrefix[i], n)==0 && zIn[n+7]==' ' ){
char *z = 0;
@@ -1390,6 +1392,117 @@ INT closedir(
/************************* End test_windirent.c ********************/
#define dirent DIRENT
#endif
+/************************* Begin ../ext/misc/memtrace.c ******************/
+/*
+** 2019-01-21
+**
+** 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 an extension that uses the SQLITE_CONFIG_MALLOC
+** mechanism to add a tracing layer on top of SQLite. If this extension
+** is registered prior to sqlite3_initialize(), it will cause all memory
+** allocation activities to be logged on standard output, or to some other
+** FILE specified by the initializer.
+**
+** This file needs to be compiled into the application that uses it.
+**
+** This extension is used to implement the --memtrace option of the
+** command-line shell.
+*/
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+/* The original memory allocation routines */
+static sqlite3_mem_methods memtraceBase;
+static FILE *memtraceOut;
+
+/* Methods that trace memory allocations */
+static void *memtraceMalloc(int n){
+ if( memtraceOut ){
+ fprintf(memtraceOut, "MEMTRACE: allocate %d bytes\n",
+ memtraceBase.xRoundup(n));
+ }
+ return memtraceBase.xMalloc(n);
+}
+static void memtraceFree(void *p){
+ if( p==0 ) return;
+ if( memtraceOut ){
+ fprintf(memtraceOut, "MEMTRACE: free %d bytes\n", memtraceBase.xSize(p));
+ }
+ memtraceBase.xFree(p);
+}
+static void *memtraceRealloc(void *p, int n){
+ if( p==0 ) return memtraceMalloc(n);
+ if( n==0 ){
+ memtraceFree(p);
+ return 0;
+ }
+ if( memtraceOut ){
+ fprintf(memtraceOut, "MEMTRACE: resize %d -> %d bytes\n",
+ memtraceBase.xSize(p), memtraceBase.xRoundup(n));
+ }
+ return memtraceBase.xRealloc(p, n);
+}
+static int memtraceSize(void *p){
+ return memtraceBase.xSize(p);
+}
+static int memtraceRoundup(int n){
+ return memtraceBase.xRoundup(n);
+}
+static int memtraceInit(void *p){
+ return memtraceBase.xInit(p);
+}
+static void memtraceShutdown(void *p){
+ memtraceBase.xShutdown(p);
+}
+
+/* The substitute memory allocator */
+static sqlite3_mem_methods ersaztMethods = {
+ memtraceMalloc,
+ memtraceFree,
+ memtraceRealloc,
+ memtraceSize,
+ memtraceRoundup,
+ memtraceInit,
+ memtraceShutdown,
+ 0
+};
+
+/* Begin tracing memory allocations to out. */
+int sqlite3MemTraceActivate(FILE *out){
+ int rc = SQLITE_OK;
+ if( memtraceBase.xMalloc==0 ){
+ rc = sqlite3_config(SQLITE_CONFIG_GETMALLOC, &memtraceBase);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &ersaztMethods);
+ }
+ }
+ memtraceOut = out;
+ return rc;
+}
+
+/* Deactivate memory tracing */
+int sqlite3MemTraceDeactivate(void){
+ int rc = SQLITE_OK;
+ if( memtraceBase.xMalloc!=0 ){
+ rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &memtraceBase);
+ if( rc==SQLITE_OK ){
+ memset(&memtraceBase, 0, sizeof(memtraceBase));
+ }
+ }
+ memtraceOut = 0;
+ return rc;
+}
+
+/************************* End ../ext/misc/memtrace.c ********************/
/************************* Begin ../ext/misc/shathree.c ******************/
/*
** 2017-03-08
@@ -1412,7 +1525,7 @@ INT closedir(
** The sha3(X) function computes the SHA3 hash of the input X, or NULL if
** X is NULL.
**
-** The sha3_query(Y) function evalutes all queries in the SQL statements of Y
+** The sha3_query(Y) function evaluates all queries in the SQL statements of Y
** and returns a hash of their results.
**
** The SIZE argument is optional. If omitted, the SHA3-256 hash algorithm
@@ -1829,6 +1942,7 @@ static void SHA3Update(
unsigned int nData
){
unsigned int i = 0;
+ if( aData==0 ) return;
#if SHA3_BYTEORDER==1234
if( (p->nLoaded % 8)==0 && ((aData - (const unsigned char*)0)&7)==0 ){
for(; i+7<nData; i+=8){
@@ -2116,6 +2230,2274 @@ int sqlite3_shathree_init(
}
/************************* End ../ext/misc/shathree.c ********************/
+/************************* Begin ../ext/misc/uint.c ******************/
+/*
+** 2020-04-14
+**
+** 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 SQLite extension implements the UINT collating sequence.
+**
+** UINT works like BINARY for text, except that embedded strings
+** of digits compare in numeric order.
+**
+** * Leading zeros are handled properly, in the sense that
+** they do not mess of the maginitude comparison of embedded
+** strings of digits. "x00123y" is equal to "x123y".
+**
+** * Only unsigned integers are recognized. Plus and minus
+** signs are ignored. Decimal points and exponential notation
+** are ignored.
+**
+** * Embedded integers can be of arbitrary length. Comparison
+** is *not* limited integers that can be expressed as a
+** 64-bit machine integer.
+*/
+/* #include "sqlite3ext.h" */
+SQLITE_EXTENSION_INIT1
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+
+/*
+** Compare text in lexicographic order, except strings of digits
+** compare in numeric order.
+*/
+static int uintCollFunc(
+ void *notUsed,
+ int nKey1, const void *pKey1,
+ int nKey2, const void *pKey2
+){
+ const unsigned char *zA = (const unsigned char*)pKey1;
+ const unsigned char *zB = (const unsigned char*)pKey2;
+ int i=0, j=0, x;
+ (void)notUsed;
+ while( i<nKey1 && j<nKey2 ){
+ x = zA[i] - zB[j];
+ if( isdigit(zA[i]) ){
+ int k;
+ if( !isdigit(zB[j]) ) return x;
+ while( i<nKey1 && zA[i]=='0' ){ i++; }
+ while( j<nKey2 && zB[j]=='0' ){ j++; }
+ k = 0;
+ while( i+k<nKey1 && isdigit(zA[i+k])
+ && j+k<nKey2 && isdigit(zB[j+k]) ){
+ k++;
+ }
+ if( i+k<nKey1 && isdigit(zA[i+k]) ){
+ return +1;
+ }else if( j+k<nKey2 && isdigit(zB[j+k]) ){
+ return -1;
+ }else{
+ x = memcmp(zA+i, zB+j, k);
+ if( x ) return x;
+ i += k;
+ j += k;
+ }
+ }else if( x ){
+ return x;
+ }else{
+ i++;
+ j++;
+ }
+ }
+ return (nKey1 - i) - (nKey2 - j);
+}
+
+#ifdef _WIN32
+
+#endif
+int sqlite3_uint_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ SQLITE_EXTENSION_INIT2(pApi);
+ (void)pzErrMsg; /* Unused parameter */
+ return sqlite3_create_collation(db, "uint", SQLITE_UTF8, 0, uintCollFunc);
+}
+
+/************************* End ../ext/misc/uint.c ********************/
+/************************* Begin ../ext/misc/decimal.c ******************/
+/*
+** 2020-06-22
+**
+** 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.
+**
+******************************************************************************
+**
+** Routines to implement arbitrary-precision decimal math.
+**
+** The focus here is on simplicity and correctness, not performance.
+*/
+/* #include "sqlite3ext.h" */
+SQLITE_EXTENSION_INIT1
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+/* Mark a function parameter as unused, to suppress nuisance compiler
+** warnings. */
+#ifndef UNUSED_PARAMETER
+# define UNUSED_PARAMETER(X) (void)(X)
+#endif
+
+
+/* A decimal object */
+typedef struct Decimal Decimal;
+struct Decimal {
+ char sign; /* 0 for positive, 1 for negative */
+ char oom; /* True if an OOM is encountered */
+ char isNull; /* True if holds a NULL rather than a number */
+ char isInit; /* True upon initialization */
+ int nDigit; /* Total number of digits */
+ int nFrac; /* Number of digits to the right of the decimal point */
+ signed char *a; /* Array of digits. Most significant first. */
+};
+
+/*
+** Release memory held by a Decimal, but do not free the object itself.
+*/
+static void decimal_clear(Decimal *p){
+ sqlite3_free(p->a);
+}
+
+/*
+** Destroy a Decimal object
+*/
+static void decimal_free(Decimal *p){
+ if( p ){
+ decimal_clear(p);
+ sqlite3_free(p);
+ }
+}
+
+/*
+** Allocate a new Decimal object. Initialize it to the number given
+** by the input string.
+*/
+static Decimal *decimal_new(
+ sqlite3_context *pCtx,
+ sqlite3_value *pIn,
+ int nAlt,
+ const unsigned char *zAlt
+){
+ Decimal *p;
+ int n, i;
+ const unsigned char *zIn;
+ int iExp = 0;
+ p = sqlite3_malloc( sizeof(*p) );
+ if( p==0 ) goto new_no_mem;
+ p->sign = 0;
+ p->oom = 0;
+ p->isInit = 1;
+ p->isNull = 0;
+ p->nDigit = 0;
+ p->nFrac = 0;
+ if( zAlt ){
+ n = nAlt,
+ zIn = zAlt;
+ }else{
+ if( sqlite3_value_type(pIn)==SQLITE_NULL ){
+ p->a = 0;
+ p->isNull = 1;
+ return p;
+ }
+ n = sqlite3_value_bytes(pIn);
+ zIn = sqlite3_value_text(pIn);
+ }
+ p->a = sqlite3_malloc64( n+1 );
+ if( p->a==0 ) goto new_no_mem;
+ for(i=0; isspace(zIn[i]); i++){}
+ if( zIn[i]=='-' ){
+ p->sign = 1;
+ i++;
+ }else if( zIn[i]=='+' ){
+ i++;
+ }
+ while( i<n && zIn[i]=='0' ) i++;
+ while( i<n ){
+ char c = zIn[i];
+ if( c>='0' && c<='9' ){
+ p->a[p->nDigit++] = c - '0';
+ }else if( c=='.' ){
+ p->nFrac = p->nDigit + 1;
+ }else if( c=='e' || c=='E' ){
+ int j = i+1;
+ int neg = 0;
+ if( j>=n ) break;
+ if( zIn[j]=='-' ){
+ neg = 1;
+ j++;
+ }else if( zIn[j]=='+' ){
+ j++;
+ }
+ while( j<n && iExp<1000000 ){
+ if( zIn[j]>='0' && zIn[j]<='9' ){
+ iExp = iExp*10 + zIn[j] - '0';
+ }
+ j++;
+ }
+ if( neg ) iExp = -iExp;
+ break;
+ }
+ i++;
+ }
+ if( p->nFrac ){
+ p->nFrac = p->nDigit - (p->nFrac - 1);
+ }
+ if( iExp>0 ){
+ if( p->nFrac>0 ){
+ if( iExp<=p->nFrac ){
+ p->nFrac -= iExp;
+ iExp = 0;
+ }else{
+ iExp -= p->nFrac;
+ p->nFrac = 0;
+ }
+ }
+ if( iExp>0 ){
+ p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 );
+ if( p->a==0 ) goto new_no_mem;
+ memset(p->a+p->nDigit, 0, iExp);
+ p->nDigit += iExp;
+ }
+ }else if( iExp<0 ){
+ int nExtra;
+ iExp = -iExp;
+ nExtra = p->nDigit - p->nFrac - 1;
+ if( nExtra ){
+ if( nExtra>=iExp ){
+ p->nFrac += iExp;
+ iExp = 0;
+ }else{
+ iExp -= nExtra;
+ p->nFrac = p->nDigit - 1;
+ }
+ }
+ if( iExp>0 ){
+ p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 );
+ if( p->a==0 ) goto new_no_mem;
+ memmove(p->a+iExp, p->a, p->nDigit);
+ memset(p->a, 0, iExp);
+ p->nDigit += iExp;
+ p->nFrac += iExp;
+ }
+ }
+ return p;
+
+new_no_mem:
+ if( pCtx ) sqlite3_result_error_nomem(pCtx);
+ sqlite3_free(p);
+ return 0;
+}
+
+/*
+** Make the given Decimal the result.
+*/
+static void decimal_result(sqlite3_context *pCtx, Decimal *p){
+ char *z;
+ int i, j;
+ int n;
+ if( p==0 || p->oom ){
+ sqlite3_result_error_nomem(pCtx);
+ return;
+ }
+ if( p->isNull ){
+ sqlite3_result_null(pCtx);
+ return;
+ }
+ z = sqlite3_malloc( p->nDigit+4 );
+ if( z==0 ){
+ sqlite3_result_error_nomem(pCtx);
+ return;
+ }
+ i = 0;
+ if( p->nDigit==0 || (p->nDigit==1 && p->a[0]==0) ){
+ p->sign = 0;
+ }
+ if( p->sign ){
+ z[0] = '-';
+ i = 1;
+ }
+ n = p->nDigit - p->nFrac;
+ if( n<=0 ){
+ z[i++] = '0';
+ }
+ j = 0;
+ while( n>1 && p->a[j]==0 ){
+ j++;
+ n--;
+ }
+ while( n>0 ){
+ z[i++] = p->a[j] + '0';
+ j++;
+ n--;
+ }
+ if( p->nFrac ){
+ z[i++] = '.';
+ do{
+ z[i++] = p->a[j] + '0';
+ j++;
+ }while( j<p->nDigit );
+ }
+ z[i] = 0;
+ sqlite3_result_text(pCtx, z, i, sqlite3_free);
+}
+
+/*
+** SQL Function: decimal(X)
+**
+** Convert input X into decimal and then back into text
+*/
+static void decimalFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ Decimal *p = decimal_new(context, argv[0], 0, 0);
+ UNUSED_PARAMETER(argc);
+ decimal_result(context, p);
+ decimal_free(p);
+}
+
+/*
+** Compare to Decimal objects. Return negative, 0, or positive if the
+** first object is less than, equal to, or greater than the second.
+**
+** Preconditions for this routine:
+**
+** pA!=0
+** pA->isNull==0
+** pB!=0
+** pB->isNull==0
+*/
+static int decimal_cmp(const Decimal *pA, const Decimal *pB){
+ int nASig, nBSig, rc, n;
+ if( pA->sign!=pB->sign ){
+ return pA->sign ? -1 : +1;
+ }
+ if( pA->sign ){
+ const Decimal *pTemp = pA;
+ pA = pB;
+ pB = pTemp;
+ }
+ nASig = pA->nDigit - pA->nFrac;
+ nBSig = pB->nDigit - pB->nFrac;
+ if( nASig!=nBSig ){
+ return nASig - nBSig;
+ }
+ n = pA->nDigit;
+ if( n>pB->nDigit ) n = pB->nDigit;
+ rc = memcmp(pA->a, pB->a, n);
+ if( rc==0 ){
+ rc = pA->nDigit - pB->nDigit;
+ }
+ return rc;
+}
+
+/*
+** SQL Function: decimal_cmp(X, Y)
+**
+** Return negative, zero, or positive if X is less then, equal to, or
+** greater than Y.
+*/
+static void decimalCmpFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ Decimal *pA = 0, *pB = 0;
+ int rc;
+
+ UNUSED_PARAMETER(argc);
+ pA = decimal_new(context, argv[0], 0, 0);
+ if( pA==0 || pA->isNull ) goto cmp_done;
+ pB = decimal_new(context, argv[1], 0, 0);
+ if( pB==0 || pB->isNull ) goto cmp_done;
+ rc = decimal_cmp(pA, pB);
+ if( rc<0 ) rc = -1;
+ else if( rc>0 ) rc = +1;
+ sqlite3_result_int(context, rc);
+cmp_done:
+ decimal_free(pA);
+ decimal_free(pB);
+}
+
+/*
+** Expand the Decimal so that it has a least nDigit digits and nFrac
+** digits to the right of the decimal point.
+*/
+static void decimal_expand(Decimal *p, int nDigit, int nFrac){
+ int nAddSig;
+ int nAddFrac;
+ if( p==0 ) return;
+ nAddFrac = nFrac - p->nFrac;
+ nAddSig = (nDigit - p->nDigit) - nAddFrac;
+ if( nAddFrac==0 && nAddSig==0 ) return;
+ p->a = sqlite3_realloc64(p->a, nDigit+1);
+ if( p->a==0 ){
+ p->oom = 1;
+ return;
+ }
+ if( nAddSig ){
+ memmove(p->a+nAddSig, p->a, p->nDigit);
+ memset(p->a, 0, nAddSig);
+ p->nDigit += nAddSig;
+ }
+ if( nAddFrac ){
+ memset(p->a+p->nDigit, 0, nAddFrac);
+ p->nDigit += nAddFrac;
+ p->nFrac += nAddFrac;
+ }
+}
+
+/*
+** Add the value pB into pA.
+**
+** Both pA and pB might become denormalized by this routine.
+*/
+static void decimal_add(Decimal *pA, Decimal *pB){
+ int nSig, nFrac, nDigit;
+ int i, rc;
+ if( pA==0 ){
+ return;
+ }
+ if( pA->oom || pB==0 || pB->oom ){
+ pA->oom = 1;
+ return;
+ }
+ if( pA->isNull || pB->isNull ){
+ pA->isNull = 1;
+ return;
+ }
+ nSig = pA->nDigit - pA->nFrac;
+ if( nSig && pA->a[0]==0 ) nSig--;
+ if( nSig<pB->nDigit-pB->nFrac ){
+ nSig = pB->nDigit - pB->nFrac;
+ }
+ nFrac = pA->nFrac;
+ if( nFrac<pB->nFrac ) nFrac = pB->nFrac;
+ nDigit = nSig + nFrac + 1;
+ decimal_expand(pA, nDigit, nFrac);
+ decimal_expand(pB, nDigit, nFrac);
+ if( pA->oom || pB->oom ){
+ pA->oom = 1;
+ }else{
+ if( pA->sign==pB->sign ){
+ int carry = 0;
+ for(i=nDigit-1; i>=0; i--){
+ int x = pA->a[i] + pB->a[i] + carry;
+ if( x>=10 ){
+ carry = 1;
+ pA->a[i] = x - 10;
+ }else{
+ carry = 0;
+ pA->a[i] = x;
+ }
+ }
+ }else{
+ signed char *aA, *aB;
+ int borrow = 0;
+ rc = memcmp(pA->a, pB->a, nDigit);
+ if( rc<0 ){
+ aA = pB->a;
+ aB = pA->a;
+ pA->sign = !pA->sign;
+ }else{
+ aA = pA->a;
+ aB = pB->a;
+ }
+ for(i=nDigit-1; i>=0; i--){
+ int x = aA[i] - aB[i] - borrow;
+ if( x<0 ){
+ pA->a[i] = x+10;
+ borrow = 1;
+ }else{
+ pA->a[i] = x;
+ borrow = 0;
+ }
+ }
+ }
+ }
+}
+
+/*
+** Compare text in decimal order.
+*/
+static int decimalCollFunc(
+ void *notUsed,
+ int nKey1, const void *pKey1,
+ int nKey2, const void *pKey2
+){
+ const unsigned char *zA = (const unsigned char*)pKey1;
+ const unsigned char *zB = (const unsigned char*)pKey2;
+ Decimal *pA = decimal_new(0, 0, nKey1, zA);
+ Decimal *pB = decimal_new(0, 0, nKey2, zB);
+ int rc;
+ UNUSED_PARAMETER(notUsed);
+ if( pA==0 || pB==0 ){
+ rc = 0;
+ }else{
+ rc = decimal_cmp(pA, pB);
+ }
+ decimal_free(pA);
+ decimal_free(pB);
+ return rc;
+}
+
+
+/*
+** SQL Function: decimal_add(X, Y)
+** decimal_sub(X, Y)
+**
+** Return the sum or difference of X and Y.
+*/
+static void decimalAddFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ Decimal *pA = decimal_new(context, argv[0], 0, 0);
+ Decimal *pB = decimal_new(context, argv[1], 0, 0);
+ UNUSED_PARAMETER(argc);
+ decimal_add(pA, pB);
+ decimal_result(context, pA);
+ decimal_free(pA);
+ decimal_free(pB);
+}
+static void decimalSubFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ Decimal *pA = decimal_new(context, argv[0], 0, 0);
+ Decimal *pB = decimal_new(context, argv[1], 0, 0);
+ UNUSED_PARAMETER(argc);
+ if( pB ){
+ pB->sign = !pB->sign;
+ decimal_add(pA, pB);
+ decimal_result(context, pA);
+ }
+ decimal_free(pA);
+ decimal_free(pB);
+}
+
+/* Aggregate funcion: decimal_sum(X)
+**
+** Works like sum() except that it uses decimal arithmetic for unlimited
+** precision.
+*/
+static void decimalSumStep(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ Decimal *p;
+ Decimal *pArg;
+ UNUSED_PARAMETER(argc);
+ p = sqlite3_aggregate_context(context, sizeof(*p));
+ if( p==0 ) return;
+ if( !p->isInit ){
+ p->isInit = 1;
+ p->a = sqlite3_malloc(2);
+ if( p->a==0 ){
+ p->oom = 1;
+ }else{
+ p->a[0] = 0;
+ }
+ p->nDigit = 1;
+ p->nFrac = 0;
+ }
+ if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
+ pArg = decimal_new(context, argv[0], 0, 0);
+ decimal_add(p, pArg);
+ decimal_free(pArg);
+}
+static void decimalSumInverse(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ Decimal *p;
+ Decimal *pArg;
+ UNUSED_PARAMETER(argc);
+ p = sqlite3_aggregate_context(context, sizeof(*p));
+ if( p==0 ) return;
+ if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
+ pArg = decimal_new(context, argv[0], 0, 0);
+ if( pArg ) pArg->sign = !pArg->sign;
+ decimal_add(p, pArg);
+ decimal_free(pArg);
+}
+static void decimalSumValue(sqlite3_context *context){
+ Decimal *p = sqlite3_aggregate_context(context, 0);
+ if( p==0 ) return;
+ decimal_result(context, p);
+}
+static void decimalSumFinalize(sqlite3_context *context){
+ Decimal *p = sqlite3_aggregate_context(context, 0);
+ if( p==0 ) return;
+ decimal_result(context, p);
+ decimal_clear(p);
+}
+
+/*
+** SQL Function: decimal_mul(X, Y)
+**
+** Return the product of X and Y.
+**
+** All significant digits after the decimal point are retained.
+** Trailing zeros after the decimal point are omitted as long as
+** the number of digits after the decimal point is no less than
+** either the number of digits in either input.
+*/
+static void decimalMulFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ Decimal *pA = decimal_new(context, argv[0], 0, 0);
+ Decimal *pB = decimal_new(context, argv[1], 0, 0);
+ signed char *acc = 0;
+ int i, j, k;
+ int minFrac;
+ UNUSED_PARAMETER(argc);
+ if( pA==0 || pA->oom || pA->isNull
+ || pB==0 || pB->oom || pB->isNull
+ ){
+ goto mul_end;
+ }
+ acc = sqlite3_malloc64( pA->nDigit + pB->nDigit + 2 );
+ if( acc==0 ){
+ sqlite3_result_error_nomem(context);
+ goto mul_end;
+ }
+ memset(acc, 0, pA->nDigit + pB->nDigit + 2);
+ minFrac = pA->nFrac;
+ if( pB->nFrac<minFrac ) minFrac = pB->nFrac;
+ for(i=pA->nDigit-1; i>=0; i--){
+ signed char f = pA->a[i];
+ int carry = 0, x;
+ for(j=pB->nDigit-1, k=i+j+3; j>=0; j--, k--){
+ x = acc[k] + f*pB->a[j] + carry;
+ acc[k] = x%10;
+ carry = x/10;
+ }
+ x = acc[k] + carry;
+ acc[k] = x%10;
+ acc[k-1] += x/10;
+ }
+ sqlite3_free(pA->a);
+ pA->a = acc;
+ acc = 0;
+ pA->nDigit += pB->nDigit + 2;
+ pA->nFrac += pB->nFrac;
+ pA->sign ^= pB->sign;
+ while( pA->nFrac>minFrac && pA->a[pA->nDigit-1]==0 ){
+ pA->nFrac--;
+ pA->nDigit--;
+ }
+ decimal_result(context, pA);
+
+mul_end:
+ sqlite3_free(acc);
+ decimal_free(pA);
+ decimal_free(pB);
+}
+
+#ifdef _WIN32
+
+#endif
+int sqlite3_decimal_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ int rc = SQLITE_OK;
+ static const struct {
+ const char *zFuncName;
+ int nArg;
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
+ } aFunc[] = {
+ { "decimal", 1, decimalFunc },
+ { "decimal_cmp", 2, decimalCmpFunc },
+ { "decimal_add", 2, decimalAddFunc },
+ { "decimal_sub", 2, decimalSubFunc },
+ { "decimal_mul", 2, decimalMulFunc },
+ };
+ unsigned int i;
+ (void)pzErrMsg; /* Unused parameter */
+
+ SQLITE_EXTENSION_INIT2(pApi);
+
+ for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
+ rc = sqlite3_create_function(db, aFunc[i].zFuncName, aFunc[i].nArg,
+ SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
+ 0, aFunc[i].xFunc, 0, 0);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_window_function(db, "decimal_sum", 1,
+ SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC, 0,
+ decimalSumStep, decimalSumFinalize,
+ decimalSumValue, decimalSumInverse, 0);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_collation(db, "decimal", SQLITE_UTF8,
+ 0, decimalCollFunc);
+ }
+ return rc;
+}
+
+/************************* End ../ext/misc/decimal.c ********************/
+/************************* Begin ../ext/misc/ieee754.c ******************/
+/*
+** 2013-04-17
+**
+** 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 SQLite extension implements functions for the exact display
+** and input of IEEE754 Binary64 floating-point numbers.
+**
+** ieee754(X)
+** ieee754(Y,Z)
+**
+** In the first form, the value X should be a floating-point number.
+** The function will return a string of the form 'ieee754(Y,Z)' where
+** Y and Z are integers such that X==Y*pow(2,Z).
+**
+** In the second form, Y and Z are integers which are the mantissa and
+** base-2 exponent of a new floating point number. The function returns
+** a floating-point value equal to Y*pow(2,Z).
+**
+** Examples:
+**
+** ieee754(2.0) -> 'ieee754(2,0)'
+** ieee754(45.25) -> 'ieee754(181,-2)'
+** ieee754(2, 0) -> 2.0
+** ieee754(181, -2) -> 45.25
+**
+** Two additional functions break apart the one-argument ieee754()
+** result into separate integer values:
+**
+** ieee754_mantissa(45.25) -> 181
+** ieee754_exponent(45.25) -> -2
+**
+** These functions convert binary64 numbers into blobs and back again.
+**
+** ieee754_from_blob(x'3ff0000000000000') -> 1.0
+** ieee754_to_blob(1.0) -> x'3ff0000000000000'
+**
+** In all single-argument functions, if the argument is an 8-byte blob
+** then that blob is interpreted as a big-endian binary64 value.
+**
+**
+** EXACT DECIMAL REPRESENTATION OF BINARY64 VALUES
+** -----------------------------------------------
+**
+** This extension in combination with the separate 'decimal' extension
+** can be used to compute the exact decimal representation of binary64
+** values. To begin, first compute a table of exponent values:
+**
+** CREATE TABLE pow2(x INTEGER PRIMARY KEY, v TEXT);
+** WITH RECURSIVE c(x,v) AS (
+** VALUES(0,'1')
+** UNION ALL
+** SELECT x+1, decimal_mul(v,'2') FROM c WHERE x+1<=971
+** ) INSERT INTO pow2(x,v) SELECT x, v FROM c;
+** WITH RECURSIVE c(x,v) AS (
+** VALUES(-1,'0.5')
+** UNION ALL
+** SELECT x-1, decimal_mul(v,'0.5') FROM c WHERE x-1>=-1075
+** ) INSERT INTO pow2(x,v) SELECT x, v FROM c;
+**
+** Then, to compute the exact decimal representation of a floating
+** point value (the value 47.49 is used in the example) do:
+**
+** WITH c(n) AS (VALUES(47.49))
+** ---------------^^^^^---- Replace with whatever you want
+** SELECT decimal_mul(ieee754_mantissa(c.n),pow2.v)
+** FROM pow2, c WHERE pow2.x=ieee754_exponent(c.n);
+**
+** Here is a query to show various boundry values for the binary64
+** number format:
+**
+** WITH c(name,bin) AS (VALUES
+** ('minimum positive value', x'0000000000000001'),
+** ('maximum subnormal value', x'000fffffffffffff'),
+** ('mininum positive nornal value', x'0010000000000000'),
+** ('maximum value', x'7fefffffffffffff'))
+** SELECT c.name, decimal_mul(ieee754_mantissa(c.bin),pow2.v)
+** FROM pow2, c WHERE pow2.x=ieee754_exponent(c.bin);
+**
+*/
+/* #include "sqlite3ext.h" */
+SQLITE_EXTENSION_INIT1
+#include <assert.h>
+#include <string.h>
+
+/* Mark a function parameter as unused, to suppress nuisance compiler
+** warnings. */
+#ifndef UNUSED_PARAMETER
+# define UNUSED_PARAMETER(X) (void)(X)
+#endif
+
+/*
+** Implementation of the ieee754() function
+*/
+static void ieee754func(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ if( argc==1 ){
+ sqlite3_int64 m, a;
+ double r;
+ int e;
+ int isNeg;
+ char zResult[100];
+ assert( sizeof(m)==sizeof(r) );
+ if( sqlite3_value_type(argv[0])==SQLITE_BLOB
+ && sqlite3_value_bytes(argv[0])==sizeof(r)
+ ){
+ const unsigned char *x = sqlite3_value_blob(argv[0]);
+ unsigned int i;
+ sqlite3_uint64 v = 0;
+ for(i=0; i<sizeof(r); i++){
+ v = (v<<8) | x[i];
+ }
+ memcpy(&r, &v, sizeof(r));
+ }else{
+ r = sqlite3_value_double(argv[0]);
+ }
+ if( r<0.0 ){
+ isNeg = 1;
+ r = -r;
+ }else{
+ isNeg = 0;
+ }
+ memcpy(&a,&r,sizeof(a));
+ if( a==0 ){
+ e = 0;
+ m = 0;
+ }else{
+ e = a>>52;
+ m = a & ((((sqlite3_int64)1)<<52)-1);
+ if( e==0 ){
+ m <<= 1;
+ }else{
+ m |= ((sqlite3_int64)1)<<52;
+ }
+ while( e<1075 && m>0 && (m&1)==0 ){
+ m >>= 1;
+ e++;
+ }
+ if( isNeg ) m = -m;
+ }
+ switch( *(int*)sqlite3_user_data(context) ){
+ case 0:
+ sqlite3_snprintf(sizeof(zResult), zResult, "ieee754(%lld,%d)",
+ m, e-1075);
+ sqlite3_result_text(context, zResult, -1, SQLITE_TRANSIENT);
+ break;
+ case 1:
+ sqlite3_result_int64(context, m);
+ break;
+ case 2:
+ sqlite3_result_int(context, e-1075);
+ break;
+ }
+ }else{
+ sqlite3_int64 m, e, a;
+ double r;
+ int isNeg = 0;
+ m = sqlite3_value_int64(argv[0]);
+ e = sqlite3_value_int64(argv[1]);
+
+ /* Limit the range of e. Ticket 22dea1cfdb9151e4 2021-03-02 */
+ if( e>10000 ){
+ e = 10000;
+ }else if( e<-10000 ){
+ e = -10000;
+ }
+
+ if( m<0 ){
+ isNeg = 1;
+ m = -m;
+ if( m<0 ) return;
+ }else if( m==0 && e>-1000 && e<1000 ){
+ sqlite3_result_double(context, 0.0);
+ return;
+ }
+ while( (m>>32)&0xffe00000 ){
+ m >>= 1;
+ e++;
+ }
+ while( m!=0 && ((m>>32)&0xfff00000)==0 ){
+ m <<= 1;
+ e--;
+ }
+ e += 1075;
+ if( e<=0 ){
+ /* Subnormal */
+ if( 1-e >= 64 ){
+ m = 0;
+ }else{
+ m >>= 1-e;
+ }
+ e = 0;
+ }else if( e>0x7ff ){
+ e = 0x7ff;
+ }
+ a = m & ((((sqlite3_int64)1)<<52)-1);
+ a |= e<<52;
+ if( isNeg ) a |= ((sqlite3_uint64)1)<<63;
+ memcpy(&r, &a, sizeof(r));
+ sqlite3_result_double(context, r);
+ }
+}
+
+/*
+** Functions to convert between blobs and floats.
+*/
+static void ieee754func_from_blob(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ UNUSED_PARAMETER(argc);
+ if( sqlite3_value_type(argv[0])==SQLITE_BLOB
+ && sqlite3_value_bytes(argv[0])==sizeof(double)
+ ){
+ double r;
+ const unsigned char *x = sqlite3_value_blob(argv[0]);
+ unsigned int i;
+ sqlite3_uint64 v = 0;
+ for(i=0; i<sizeof(r); i++){
+ v = (v<<8) | x[i];
+ }
+ memcpy(&r, &v, sizeof(r));
+ sqlite3_result_double(context, r);
+ }
+}
+static void ieee754func_to_blob(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ UNUSED_PARAMETER(argc);
+ if( sqlite3_value_type(argv[0])==SQLITE_FLOAT
+ || sqlite3_value_type(argv[0])==SQLITE_INTEGER
+ ){
+ double r = sqlite3_value_double(argv[0]);
+ sqlite3_uint64 v;
+ unsigned char a[sizeof(r)];
+ unsigned int i;
+ memcpy(&v, &r, sizeof(r));
+ for(i=1; i<=sizeof(r); i++){
+ a[sizeof(r)-i] = v&0xff;
+ v >>= 8;
+ }
+ sqlite3_result_blob(context, a, sizeof(r), SQLITE_TRANSIENT);
+ }
+}
+
+
+#ifdef _WIN32
+
+#endif
+int sqlite3_ieee_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ static const struct {
+ char *zFName;
+ int nArg;
+ int iAux;
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
+ } aFunc[] = {
+ { "ieee754", 1, 0, ieee754func },
+ { "ieee754", 2, 0, ieee754func },
+ { "ieee754_mantissa", 1, 1, ieee754func },
+ { "ieee754_exponent", 1, 2, ieee754func },
+ { "ieee754_to_blob", 1, 0, ieee754func_to_blob },
+ { "ieee754_from_blob", 1, 0, ieee754func_from_blob },
+
+ };
+ unsigned int i;
+ int rc = SQLITE_OK;
+ SQLITE_EXTENSION_INIT2(pApi);
+ (void)pzErrMsg; /* Unused parameter */
+ for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
+ rc = sqlite3_create_function(db, aFunc[i].zFName, aFunc[i].nArg,
+ SQLITE_UTF8|SQLITE_INNOCUOUS,
+ (void*)&aFunc[i].iAux,
+ aFunc[i].xFunc, 0, 0);
+ }
+ return rc;
+}
+
+/************************* End ../ext/misc/ieee754.c ********************/
+/************************* Begin ../ext/misc/series.c ******************/
+/*
+** 2015-08-18
+**
+** 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 demonstrates how to create a table-valued-function using
+** a virtual table. This demo implements the generate_series() function
+** which gives similar results to the eponymous function in PostgreSQL.
+** Examples:
+**
+** SELECT * FROM generate_series(0,100,5);
+**
+** The query above returns integers from 0 through 100 counting by steps
+** of 5.
+**
+** SELECT * FROM generate_series(0,100);
+**
+** Integers from 0 through 100 with a step size of 1.
+**
+** SELECT * FROM generate_series(20) LIMIT 10;
+**
+** Integers 20 through 29.
+**
+** HOW IT WORKS
+**
+** The generate_series "function" is really a virtual table with the
+** following schema:
+**
+** CREATE TABLE generate_series(
+** value,
+** start HIDDEN,
+** stop HIDDEN,
+** step HIDDEN
+** );
+**
+** Function arguments in queries against this virtual table are translated
+** into equality constraints against successive hidden columns. In other
+** words, the following pairs of queries are equivalent to each other:
+**
+** SELECT * FROM generate_series(0,100,5);
+** SELECT * FROM generate_series WHERE start=0 AND stop=100 AND step=5;
+**
+** SELECT * FROM generate_series(0,100);
+** SELECT * FROM generate_series WHERE start=0 AND stop=100;
+**
+** SELECT * FROM generate_series(20) LIMIT 10;
+** SELECT * FROM generate_series WHERE start=20 LIMIT 10;
+**
+** The generate_series virtual table implementation leaves the xCreate method
+** set to NULL. This means that it is not possible to do a CREATE VIRTUAL
+** TABLE command with "generate_series" as the USING argument. Instead, there
+** is a single generate_series virtual table that is always available without
+** having to be created first.
+**
+** The xBestIndex method looks for equality constraints against the hidden
+** start, stop, and step columns, and if present, it uses those constraints
+** to bound the sequence of generated values. If the equality constraints
+** are missing, it uses 0 for start, 4294967295 for stop, and 1 for step.
+** xBestIndex returns a small cost when both start and stop are available,
+** and a very large cost if either start or stop are unavailable. This
+** encourages the query planner to order joins such that the bounds of the
+** series are well-defined.
+*/
+/* #include "sqlite3ext.h" */
+SQLITE_EXTENSION_INIT1
+#include <assert.h>
+#include <string.h>
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+
+
+/* series_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 series_cursor series_cursor;
+struct series_cursor {
+ sqlite3_vtab_cursor base; /* Base class - must be first */
+ int isDesc; /* True to count down rather than up */
+ sqlite3_int64 iRowid; /* The rowid */
+ sqlite3_int64 iValue; /* Current value ("value") */
+ sqlite3_int64 mnValue; /* Mimimum value ("start") */
+ sqlite3_int64 mxValue; /* Maximum value ("stop") */
+ sqlite3_int64 iStep; /* Increment ("step") */
+};
+
+/*
+** The seriesConnect() method is invoked to create a new
+** series_vtab that describes the generate_series virtual table.
+**
+** Think of this routine as the constructor for series_vtab objects.
+**
+** All this routine needs to do is:
+**
+** (1) Allocate the series_vtab object and initialize all fields.
+**
+** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
+** result set of queries against generate_series will look like.
+*/
+static int seriesConnect(
+ sqlite3 *db,
+ void *pUnused,
+ int argcUnused, const char *const*argvUnused,
+ sqlite3_vtab **ppVtab,
+ char **pzErrUnused
+){
+ sqlite3_vtab *pNew;
+ int rc;
+
+/* Column numbers */
+#define SERIES_COLUMN_VALUE 0
+#define SERIES_COLUMN_START 1
+#define SERIES_COLUMN_STOP 2
+#define SERIES_COLUMN_STEP 3
+
+ (void)pUnused;
+ (void)argcUnused;
+ (void)argvUnused;
+ (void)pzErrUnused;
+ rc = sqlite3_declare_vtab(db,
+ "CREATE TABLE x(value,start hidden,stop hidden,step hidden)");
+ if( rc==SQLITE_OK ){
+ pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
+ if( pNew==0 ) return SQLITE_NOMEM;
+ memset(pNew, 0, sizeof(*pNew));
+ sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
+ }
+ return rc;
+}
+
+/*
+** This method is the destructor for series_cursor objects.
+*/
+static int seriesDisconnect(sqlite3_vtab *pVtab){
+ sqlite3_free(pVtab);
+ return SQLITE_OK;
+}
+
+/*
+** Constructor for a new series_cursor object.
+*/
+static int seriesOpen(sqlite3_vtab *pUnused, sqlite3_vtab_cursor **ppCursor){
+ series_cursor *pCur;
+ (void)pUnused;
+ 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 series_cursor.
+*/
+static int seriesClose(sqlite3_vtab_cursor *cur){
+ sqlite3_free(cur);
+ return SQLITE_OK;
+}
+
+
+/*
+** Advance a series_cursor to its next row of output.
+*/
+static int seriesNext(sqlite3_vtab_cursor *cur){
+ series_cursor *pCur = (series_cursor*)cur;
+ if( pCur->isDesc ){
+ pCur->iValue -= pCur->iStep;
+ }else{
+ pCur->iValue += pCur->iStep;
+ }
+ pCur->iRowid++;
+ return SQLITE_OK;
+}
+
+/*
+** Return values of columns for the row at which the series_cursor
+** is currently pointing.
+*/
+static int seriesColumn(
+ sqlite3_vtab_cursor *cur, /* The cursor */
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
+ int i /* Which column to return */
+){
+ series_cursor *pCur = (series_cursor*)cur;
+ sqlite3_int64 x = 0;
+ switch( i ){
+ case SERIES_COLUMN_START: x = pCur->mnValue; break;
+ case SERIES_COLUMN_STOP: x = pCur->mxValue; break;
+ case SERIES_COLUMN_STEP: x = pCur->iStep; break;
+ default: x = pCur->iValue; break;
+ }
+ sqlite3_result_int64(ctx, x);
+ return SQLITE_OK;
+}
+
+/*
+** Return the rowid for the current row. In this implementation, the
+** first row returned is assigned rowid value 1, and each subsequent
+** row a value 1 more than that of the previous.
+*/
+static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
+ series_cursor *pCur = (series_cursor*)cur;
+ *pRowid = pCur->iRowid;
+ return SQLITE_OK;
+}
+
+/*
+** Return TRUE if the cursor has been moved off of the last
+** row of output.
+*/
+static int seriesEof(sqlite3_vtab_cursor *cur){
+ series_cursor *pCur = (series_cursor*)cur;
+ if( pCur->isDesc ){
+ return pCur->iValue < pCur->mnValue;
+ }else{
+ return pCur->iValue > pCur->mxValue;
+ }
+}
+
+/* True to cause run-time checking of the start=, stop=, and/or step=
+** parameters. The only reason to do this is for testing the
+** constraint checking logic for virtual tables in the SQLite core.
+*/
+#ifndef SQLITE_SERIES_CONSTRAINT_VERIFY
+# define SQLITE_SERIES_CONSTRAINT_VERIFY 0
+#endif
+
+/*
+** This method is called to "rewind" the series_cursor object back
+** to the first row of output. This method is always called at least
+** once prior to any call to seriesColumn() or seriesRowid() or
+** seriesEof().
+**
+** The query plan selected by seriesBestIndex is passed in the idxNum
+** parameter. (idxStr is not used in this implementation.) idxNum
+** is a bitmask showing which constraints are available:
+**
+** 1: start=VALUE
+** 2: stop=VALUE
+** 4: step=VALUE
+**
+** Also, if bit 8 is set, that means that the series should be output
+** in descending order rather than in ascending order. If bit 16 is
+** set, then output must appear in ascending order.
+**
+** This routine should initialize the cursor and position it so that it
+** is pointing at the first row, or pointing off the end of the table
+** (so that seriesEof() will return true) if the table is empty.
+*/
+static int seriesFilter(
+ sqlite3_vtab_cursor *pVtabCursor,
+ int idxNum, const char *idxStrUnused,
+ int argc, sqlite3_value **argv
+){
+ series_cursor *pCur = (series_cursor *)pVtabCursor;
+ int i = 0;
+ (void)idxStrUnused;
+ if( idxNum & 1 ){
+ pCur->mnValue = sqlite3_value_int64(argv[i++]);
+ }else{
+ pCur->mnValue = 0;
+ }
+ if( idxNum & 2 ){
+ pCur->mxValue = sqlite3_value_int64(argv[i++]);
+ }else{
+ pCur->mxValue = 0xffffffff;
+ }
+ if( idxNum & 4 ){
+ pCur->iStep = sqlite3_value_int64(argv[i++]);
+ if( pCur->iStep==0 ){
+ pCur->iStep = 1;
+ }else if( pCur->iStep<0 ){
+ pCur->iStep = -pCur->iStep;
+ if( (idxNum & 16)==0 ) idxNum |= 8;
+ }
+ }else{
+ pCur->iStep = 1;
+ }
+ for(i=0; i<argc; i++){
+ if( sqlite3_value_type(argv[i])==SQLITE_NULL ){
+ /* If any of the constraints have a NULL value, then return no rows.
+ ** See ticket https://www.sqlite.org/src/info/fac496b61722daf2 */
+ pCur->mnValue = 1;
+ pCur->mxValue = 0;
+ break;
+ }
+ }
+ if( idxNum & 8 ){
+ pCur->isDesc = 1;
+ pCur->iValue = pCur->mxValue;
+ if( pCur->iStep>0 ){
+ pCur->iValue -= (pCur->mxValue - pCur->mnValue)%pCur->iStep;
+ }
+ }else{
+ pCur->isDesc = 0;
+ pCur->iValue = pCur->mnValue;
+ }
+ pCur->iRowid = 1;
+ return SQLITE_OK;
+}
+
+/*
+** SQLite will invoke this method one or more times while planning a query
+** that uses the generate_series virtual table. This routine needs to create
+** a query plan for each invocation and compute an estimated cost for that
+** plan.
+**
+** In this implementation idxNum is used to represent the
+** query plan. idxStr is unused.
+**
+** The query plan is represented by bits in idxNum:
+**
+** (1) start = $value -- constraint exists
+** (2) stop = $value -- constraint exists
+** (4) step = $value -- constraint exists
+** (8) output in descending order
+*/
+static int seriesBestIndex(
+ sqlite3_vtab *pVTab,
+ sqlite3_index_info *pIdxInfo
+){
+ int i, j; /* Loop over constraints */
+ int idxNum = 0; /* The query plan bitmask */
+ int bStartSeen = 0; /* EQ constraint seen on the START column */
+ int unusableMask = 0; /* Mask of unusable constraints */
+ int nArg = 0; /* Number of arguments that seriesFilter() expects */
+ int aIdx[3]; /* Constraints on start, stop, and step */
+ const struct sqlite3_index_constraint *pConstraint;
+
+ /* This implementation assumes that the start, stop, and step columns
+ ** are the last three columns in the virtual table. */
+ assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 );
+ assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 );
+
+ aIdx[0] = aIdx[1] = aIdx[2] = -1;
+ pConstraint = pIdxInfo->aConstraint;
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
+ int iCol; /* 0 for start, 1 for stop, 2 for step */
+ int iMask; /* bitmask for those column */
+ if( pConstraint->iColumn<SERIES_COLUMN_START ) continue;
+ iCol = pConstraint->iColumn - SERIES_COLUMN_START;
+ assert( iCol>=0 && iCol<=2 );
+ iMask = 1 << iCol;
+ if( iCol==0 ) bStartSeen = 1;
+ if( pConstraint->usable==0 ){
+ unusableMask |= iMask;
+ continue;
+ }else if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
+ idxNum |= iMask;
+ aIdx[iCol] = i;
+ }
+ }
+ for(i=0; i<3; i++){
+ if( (j = aIdx[i])>=0 ){
+ pIdxInfo->aConstraintUsage[j].argvIndex = ++nArg;
+ pIdxInfo->aConstraintUsage[j].omit = !SQLITE_SERIES_CONSTRAINT_VERIFY;
+ }
+ }
+ /* The current generate_column() implementation requires at least one
+ ** argument (the START value). Legacy versions assumed START=0 if the
+ ** first argument was omitted. Compile with -DZERO_ARGUMENT_GENERATE_SERIES
+ ** to obtain the legacy behavior */
+#ifndef ZERO_ARGUMENT_GENERATE_SERIES
+ if( !bStartSeen ){
+ sqlite3_free(pVTab->zErrMsg);
+ pVTab->zErrMsg = sqlite3_mprintf(
+ "first argument to \"generate_series()\" missing or unusable");
+ return SQLITE_ERROR;
+ }
+#endif
+ if( (unusableMask & ~idxNum)!=0 ){
+ /* The start, stop, and step columns are inputs. Therefore if there
+ ** are unusable constraints on any of start, stop, or step then
+ ** this plan is unusable */
+ return SQLITE_CONSTRAINT;
+ }
+ if( (idxNum & 3)==3 ){
+ /* Both start= and stop= boundaries are available. This is the
+ ** the preferred case */
+ pIdxInfo->estimatedCost = (double)(2 - ((idxNum&4)!=0));
+ pIdxInfo->estimatedRows = 1000;
+ if( pIdxInfo->nOrderBy>=1 && pIdxInfo->aOrderBy[0].iColumn==0 ){
+ if( pIdxInfo->aOrderBy[0].desc ){
+ idxNum |= 8;
+ }else{
+ idxNum |= 16;
+ }
+ pIdxInfo->orderByConsumed = 1;
+ }
+ }else{
+ /* If either boundary is missing, we have to generate a huge span
+ ** of numbers. Make this case very expensive so that the query
+ ** planner will work hard to avoid it. */
+ pIdxInfo->estimatedRows = 2147483647;
+ }
+ pIdxInfo->idxNum = idxNum;
+ return SQLITE_OK;
+}
+
+/*
+** This following structure defines all the methods for the
+** generate_series virtual table.
+*/
+static sqlite3_module seriesModule = {
+ 0, /* iVersion */
+ 0, /* xCreate */
+ seriesConnect, /* xConnect */
+ seriesBestIndex, /* xBestIndex */
+ seriesDisconnect, /* xDisconnect */
+ 0, /* xDestroy */
+ seriesOpen, /* xOpen - open a cursor */
+ seriesClose, /* xClose - close a cursor */
+ seriesFilter, /* xFilter - configure scan constraints */
+ seriesNext, /* xNext - advance a cursor */
+ seriesEof, /* xEof - check for end of scan */
+ seriesColumn, /* xColumn - read data */
+ seriesRowid, /* xRowid - read data */
+ 0, /* xUpdate */
+ 0, /* xBegin */
+ 0, /* xSync */
+ 0, /* xCommit */
+ 0, /* xRollback */
+ 0, /* xFindMethod */
+ 0, /* xRename */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0, /* xRollbackTo */
+ 0 /* xShadowName */
+};
+
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+#ifdef _WIN32
+
+#endif
+int sqlite3_series_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ int rc = SQLITE_OK;
+ SQLITE_EXTENSION_INIT2(pApi);
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ if( sqlite3_libversion_number()<3008012 && pzErrMsg!=0 ){
+ *pzErrMsg = sqlite3_mprintf(
+ "generate_series() requires SQLite 3.8.12 or later");
+ return SQLITE_ERROR;
+ }
+ rc = sqlite3_create_module(db, "generate_series", &seriesModule, 0);
+#endif
+ return rc;
+}
+
+/************************* End ../ext/misc/series.c ********************/
+/************************* Begin ../ext/misc/regexp.c ******************/
+/*
+** 2012-11-13
+**
+** 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.
+**
+******************************************************************************
+**
+** The code in this file implements a compact but reasonably
+** efficient regular-expression matcher for posix extended regular
+** expressions against UTF8 text.
+**
+** This file is an SQLite extension. It registers a single function
+** named "regexp(A,B)" where A is the regular expression and B is the
+** string to be matched. By registering this function, SQLite will also
+** then implement the "B regexp A" operator. Note that with the function
+** the regular expression comes first, but with the operator it comes
+** second.
+**
+** The following regular expression syntax is supported:
+**
+** X* zero or more occurrences of X
+** X+ one or more occurrences of X
+** X? zero or one occurrences of X
+** X{p,q} between p and q occurrences of X
+** (X) match X
+** X|Y X or Y
+** ^X X occurring at the beginning of the string
+** X$ X occurring at the end of the string
+** . Match any single character
+** \c Character c where c is one of \{}()[]|*+?.
+** \c C-language escapes for c in afnrtv. ex: \t or \n
+** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX
+** \xXX Where XX is exactly 2 hex digits, unicode value XX
+** [abc] Any single character from the set abc
+** [^abc] Any single character not in the set abc
+** [a-z] Any single character in the range a-z
+** [^a-z] Any single character not in the range a-z
+** \b Word boundary
+** \w Word character. [A-Za-z0-9_]
+** \W Non-word character
+** \d Digit
+** \D Non-digit
+** \s Whitespace character
+** \S Non-whitespace character
+**
+** A nondeterministic finite automaton (NFA) is used for matching, so the
+** performance is bounded by O(N*M) where N is the size of the regular
+** expression and M is the size of the input string. The matcher never
+** exhibits exponential behavior. Note that the X{p,q} operator expands
+** to p copies of X following by q-p copies of X? and that the size of the
+** regular expression in the O(N*M) performance bound is computed after
+** this expansion.
+*/
+#include <string.h>
+#include <stdlib.h>
+/* #include "sqlite3ext.h" */
+SQLITE_EXTENSION_INIT1
+
+/*
+** The following #defines change the names of some functions implemented in
+** this file to prevent name collisions with C-library functions of the
+** same name.
+*/
+#define re_match sqlite3re_match
+#define re_compile sqlite3re_compile
+#define re_free sqlite3re_free
+
+/* The end-of-input character */
+#define RE_EOF 0 /* End of input */
+
+/* The NFA is implemented as sequence of opcodes taken from the following
+** set. Each opcode has a single integer argument.
+*/
+#define RE_OP_MATCH 1 /* Match the one character in the argument */
+#define RE_OP_ANY 2 /* Match any one character. (Implements ".") */
+#define RE_OP_ANYSTAR 3 /* Special optimized version of .* */
+#define RE_OP_FORK 4 /* Continue to both next and opcode at iArg */
+#define RE_OP_GOTO 5 /* Jump to opcode at iArg */
+#define RE_OP_ACCEPT 6 /* Halt and indicate a successful match */
+#define RE_OP_CC_INC 7 /* Beginning of a [...] character class */
+#define RE_OP_CC_EXC 8 /* Beginning of a [^...] character class */
+#define RE_OP_CC_VALUE 9 /* Single value in a character class */
+#define RE_OP_CC_RANGE 10 /* Range of values in a character class */
+#define RE_OP_WORD 11 /* Perl word character [A-Za-z0-9_] */
+#define RE_OP_NOTWORD 12 /* Not a perl word character */
+#define RE_OP_DIGIT 13 /* digit: [0-9] */
+#define RE_OP_NOTDIGIT 14 /* Not a digit */
+#define RE_OP_SPACE 15 /* space: [ \t\n\r\v\f] */
+#define RE_OP_NOTSPACE 16 /* Not a digit */
+#define RE_OP_BOUNDARY 17 /* Boundary between word and non-word */
+
+/* Each opcode is a "state" in the NFA */
+typedef unsigned short ReStateNumber;
+
+/* Because this is an NFA and not a DFA, multiple states can be active at
+** once. An instance of the following object records all active states in
+** the NFA. The implementation is optimized for the common case where the
+** number of actives states is small.
+*/
+typedef struct ReStateSet {
+ unsigned nState; /* Number of current states */
+ ReStateNumber *aState; /* Current states */
+} ReStateSet;
+
+/* An input string read one character at a time.
+*/
+typedef struct ReInput ReInput;
+struct ReInput {
+ const unsigned char *z; /* All text */
+ int i; /* Next byte to read */
+ int mx; /* EOF when i>=mx */
+};
+
+/* A compiled NFA (or an NFA that is in the process of being compiled) is
+** an instance of the following object.
+*/
+typedef struct ReCompiled ReCompiled;
+struct ReCompiled {
+ ReInput sIn; /* Regular expression text */
+ const char *zErr; /* Error message to return */
+ char *aOp; /* Operators for the virtual machine */
+ int *aArg; /* Arguments to each operator */
+ unsigned (*xNextChar)(ReInput*); /* Next character function */
+ unsigned char zInit[12]; /* Initial text to match */
+ int nInit; /* Number of characters in zInit */
+ unsigned nState; /* Number of entries in aOp[] and aArg[] */
+ unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */
+};
+
+/* Add a state to the given state set if it is not already there */
+static void re_add_state(ReStateSet *pSet, int newState){
+ unsigned i;
+ for(i=0; i<pSet->nState; i++) if( pSet->aState[i]==newState ) return;
+ pSet->aState[pSet->nState++] = (ReStateNumber)newState;
+}
+
+/* Extract the next unicode character from *pzIn and return it. Advance
+** *pzIn to the first byte past the end of the character returned. To
+** be clear: this routine converts utf8 to unicode. This routine is
+** optimized for the common case where the next character is a single byte.
+*/
+static unsigned re_next_char(ReInput *p){
+ unsigned c;
+ if( p->i>=p->mx ) return 0;
+ c = p->z[p->i++];
+ if( c>=0x80 ){
+ if( (c&0xe0)==0xc0 && p->i<p->mx && (p->z[p->i]&0xc0)==0x80 ){
+ c = (c&0x1f)<<6 | (p->z[p->i++]&0x3f);
+ if( c<0x80 ) c = 0xfffd;
+ }else if( (c&0xf0)==0xe0 && p->i+1<p->mx && (p->z[p->i]&0xc0)==0x80
+ && (p->z[p->i+1]&0xc0)==0x80 ){
+ c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f);
+ p->i += 2;
+ if( c<=0x7ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd;
+ }else if( (c&0xf8)==0xf0 && p->i+3<p->mx && (p->z[p->i]&0xc0)==0x80
+ && (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){
+ c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6)
+ | (p->z[p->i+2]&0x3f);
+ p->i += 3;
+ if( c<=0xffff || c>0x10ffff ) c = 0xfffd;
+ }else{
+ c = 0xfffd;
+ }
+ }
+ return c;
+}
+static unsigned re_next_char_nocase(ReInput *p){
+ unsigned c = re_next_char(p);
+ if( c>='A' && c<='Z' ) c += 'a' - 'A';
+ return c;
+}
+
+/* Return true if c is a perl "word" character: [A-Za-z0-9_] */
+static int re_word_char(int c){
+ return (c>='0' && c<='9') || (c>='a' && c<='z')
+ || (c>='A' && c<='Z') || c=='_';
+}
+
+/* Return true if c is a "digit" character: [0-9] */
+static int re_digit_char(int c){
+ return (c>='0' && c<='9');
+}
+
+/* Return true if c is a perl "space" character: [ \t\r\n\v\f] */
+static int re_space_char(int c){
+ return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f';
+}
+
+/* Run a compiled regular expression on the zero-terminated input
+** string zIn[]. Return true on a match and false if there is no match.
+*/
+static int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){
+ ReStateSet aStateSet[2], *pThis, *pNext;
+ ReStateNumber aSpace[100];
+ ReStateNumber *pToFree;
+ unsigned int i = 0;
+ unsigned int iSwap = 0;
+ int c = RE_EOF+1;
+ int cPrev = 0;
+ int rc = 0;
+ ReInput in;
+
+ in.z = zIn;
+ in.i = 0;
+ in.mx = nIn>=0 ? nIn : (int)strlen((char const*)zIn);
+
+ /* Look for the initial prefix match, if there is one. */
+ if( pRe->nInit ){
+ unsigned char x = pRe->zInit[0];
+ while( in.i+pRe->nInit<=in.mx
+ && (zIn[in.i]!=x ||
+ strncmp((const char*)zIn+in.i, (const char*)pRe->zInit, pRe->nInit)!=0)
+ ){
+ in.i++;
+ }
+ if( in.i+pRe->nInit>in.mx ) return 0;
+ }
+
+ if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){
+ pToFree = 0;
+ aStateSet[0].aState = aSpace;
+ }else{
+ pToFree = sqlite3_malloc64( sizeof(ReStateNumber)*2*pRe->nState );
+ if( pToFree==0 ) return -1;
+ aStateSet[0].aState = pToFree;
+ }
+ aStateSet[1].aState = &aStateSet[0].aState[pRe->nState];
+ pNext = &aStateSet[1];
+ pNext->nState = 0;
+ re_add_state(pNext, 0);
+ while( c!=RE_EOF && pNext->nState>0 ){
+ cPrev = c;
+ c = pRe->xNextChar(&in);
+ pThis = pNext;
+ pNext = &aStateSet[iSwap];
+ iSwap = 1 - iSwap;
+ pNext->nState = 0;
+ for(i=0; i<pThis->nState; i++){
+ int x = pThis->aState[i];
+ switch( pRe->aOp[x] ){
+ case RE_OP_MATCH: {
+ if( pRe->aArg[x]==c ) re_add_state(pNext, x+1);
+ break;
+ }
+ case RE_OP_ANY: {
+ if( c!=0 ) re_add_state(pNext, x+1);
+ break;
+ }
+ case RE_OP_WORD: {
+ if( re_word_char(c) ) re_add_state(pNext, x+1);
+ break;
+ }
+ case RE_OP_NOTWORD: {
+ if( !re_word_char(c) && c!=0 ) re_add_state(pNext, x+1);
+ break;
+ }
+ case RE_OP_DIGIT: {
+ if( re_digit_char(c) ) re_add_state(pNext, x+1);
+ break;
+ }
+ case RE_OP_NOTDIGIT: {
+ if( !re_digit_char(c) && c!=0 ) re_add_state(pNext, x+1);
+ break;
+ }
+ case RE_OP_SPACE: {
+ if( re_space_char(c) ) re_add_state(pNext, x+1);
+ break;
+ }
+ case RE_OP_NOTSPACE: {
+ if( !re_space_char(c) && c!=0 ) re_add_state(pNext, x+1);
+ break;
+ }
+ case RE_OP_BOUNDARY: {
+ if( re_word_char(c)!=re_word_char(cPrev) ) re_add_state(pThis, x+1);
+ break;
+ }
+ case RE_OP_ANYSTAR: {
+ re_add_state(pNext, x);
+ re_add_state(pThis, x+1);
+ break;
+ }
+ case RE_OP_FORK: {
+ re_add_state(pThis, x+pRe->aArg[x]);
+ re_add_state(pThis, x+1);
+ break;
+ }
+ case RE_OP_GOTO: {
+ re_add_state(pThis, x+pRe->aArg[x]);
+ break;
+ }
+ case RE_OP_ACCEPT: {
+ rc = 1;
+ goto re_match_end;
+ }
+ case RE_OP_CC_EXC: {
+ if( c==0 ) break;
+ /* fall-through */ goto re_op_cc_inc;
+ }
+ case RE_OP_CC_INC: re_op_cc_inc: {
+ int j = 1;
+ int n = pRe->aArg[x];
+ int hit = 0;
+ for(j=1; j>0 && j<n; j++){
+ if( pRe->aOp[x+j]==RE_OP_CC_VALUE ){
+ if( pRe->aArg[x+j]==c ){
+ hit = 1;
+ j = -1;
+ }
+ }else{
+ if( pRe->aArg[x+j]<=c && pRe->aArg[x+j+1]>=c ){
+ hit = 1;
+ j = -1;
+ }else{
+ j++;
+ }
+ }
+ }
+ if( pRe->aOp[x]==RE_OP_CC_EXC ) hit = !hit;
+ if( hit ) re_add_state(pNext, x+n);
+ break;
+ }
+ }
+ }
+ }
+ for(i=0; i<pNext->nState; i++){
+ if( pRe->aOp[pNext->aState[i]]==RE_OP_ACCEPT ){ rc = 1; break; }
+ }
+re_match_end:
+ sqlite3_free(pToFree);
+ return rc;
+}
+
+/* Resize the opcode and argument arrays for an RE under construction.
+*/
+static int re_resize(ReCompiled *p, int N){
+ char *aOp;
+ int *aArg;
+ aOp = sqlite3_realloc64(p->aOp, N*sizeof(p->aOp[0]));
+ if( aOp==0 ) return 1;
+ p->aOp = aOp;
+ aArg = sqlite3_realloc64(p->aArg, N*sizeof(p->aArg[0]));
+ if( aArg==0 ) return 1;
+ p->aArg = aArg;
+ p->nAlloc = N;
+ return 0;
+}
+
+/* Insert a new opcode and argument into an RE under construction. The
+** insertion point is just prior to existing opcode iBefore.
+*/
+static int re_insert(ReCompiled *p, int iBefore, int op, int arg){
+ int i;
+ if( p->nAlloc<=p->nState && re_resize(p, p->nAlloc*2) ) return 0;
+ for(i=p->nState; i>iBefore; i--){
+ p->aOp[i] = p->aOp[i-1];
+ p->aArg[i] = p->aArg[i-1];
+ }
+ p->nState++;
+ p->aOp[iBefore] = (char)op;
+ p->aArg[iBefore] = arg;
+ return iBefore;
+}
+
+/* Append a new opcode and argument to the end of the RE under construction.
+*/
+static int re_append(ReCompiled *p, int op, int arg){
+ return re_insert(p, p->nState, op, arg);
+}
+
+/* Make a copy of N opcodes starting at iStart onto the end of the RE
+** under construction.
+*/
+static void re_copy(ReCompiled *p, int iStart, int N){
+ if( p->nState+N>=p->nAlloc && re_resize(p, p->nAlloc*2+N) ) return;
+ memcpy(&p->aOp[p->nState], &p->aOp[iStart], N*sizeof(p->aOp[0]));
+ memcpy(&p->aArg[p->nState], &p->aArg[iStart], N*sizeof(p->aArg[0]));
+ p->nState += N;
+}
+
+/* Return true if c is a hexadecimal digit character: [0-9a-fA-F]
+** If c is a hex digit, also set *pV = (*pV)*16 + valueof(c). If
+** c is not a hex digit *pV is unchanged.
+*/
+static int re_hex(int c, int *pV){
+ if( c>='0' && c<='9' ){
+ c -= '0';
+ }else if( c>='a' && c<='f' ){
+ c -= 'a' - 10;
+ }else if( c>='A' && c<='F' ){
+ c -= 'A' - 10;
+ }else{
+ return 0;
+ }
+ *pV = (*pV)*16 + (c & 0xff);
+ return 1;
+}
+
+/* A backslash character has been seen, read the next character and
+** return its interpretation.
+*/
+static unsigned re_esc_char(ReCompiled *p){
+ static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]";
+ static const char zTrans[] = "\a\f\n\r\t\v";
+ int i, v = 0;
+ char c;
+ if( p->sIn.i>=p->sIn.mx ) return 0;
+ c = p->sIn.z[p->sIn.i];
+ if( c=='u' && p->sIn.i+4<p->sIn.mx ){
+ const unsigned char *zIn = p->sIn.z + p->sIn.i;
+ if( re_hex(zIn[1],&v)
+ && re_hex(zIn[2],&v)
+ && re_hex(zIn[3],&v)
+ && re_hex(zIn[4],&v)
+ ){
+ p->sIn.i += 5;
+ return v;
+ }
+ }
+ if( c=='x' && p->sIn.i+2<p->sIn.mx ){
+ const unsigned char *zIn = p->sIn.z + p->sIn.i;
+ if( re_hex(zIn[1],&v)
+ && re_hex(zIn[2],&v)
+ ){
+ p->sIn.i += 3;
+ return v;
+ }
+ }
+ for(i=0; zEsc[i] && zEsc[i]!=c; i++){}
+ if( zEsc[i] ){
+ if( i<6 ) c = zTrans[i];
+ p->sIn.i++;
+ }else{
+ p->zErr = "unknown \\ escape";
+ }
+ return c;
+}
+
+/* Forward declaration */
+static const char *re_subcompile_string(ReCompiled*);
+
+/* Peek at the next byte of input */
+static unsigned char rePeek(ReCompiled *p){
+ return p->sIn.i<p->sIn.mx ? p->sIn.z[p->sIn.i] : 0;
+}
+
+/* Compile RE text into a sequence of opcodes. Continue up to the
+** first unmatched ")" character, then return. If an error is found,
+** return a pointer to the error message string.
+*/
+static const char *re_subcompile_re(ReCompiled *p){
+ const char *zErr;
+ int iStart, iEnd, iGoto;
+ iStart = p->nState;
+ zErr = re_subcompile_string(p);
+ if( zErr ) return zErr;
+ while( rePeek(p)=='|' ){
+ iEnd = p->nState;
+ re_insert(p, iStart, RE_OP_FORK, iEnd + 2 - iStart);
+ iGoto = re_append(p, RE_OP_GOTO, 0);
+ p->sIn.i++;
+ zErr = re_subcompile_string(p);
+ if( zErr ) return zErr;
+ p->aArg[iGoto] = p->nState - iGoto;
+ }
+ return 0;
+}
+
+/* Compile an element of regular expression text (anything that can be
+** an operand to the "|" operator). Return NULL on success or a pointer
+** to the error message if there is a problem.
+*/
+static const char *re_subcompile_string(ReCompiled *p){
+ int iPrev = -1;
+ int iStart;
+ unsigned c;
+ const char *zErr;
+ while( (c = p->xNextChar(&p->sIn))!=0 ){
+ iStart = p->nState;
+ switch( c ){
+ case '|':
+ case '$':
+ case ')': {
+ p->sIn.i--;
+ return 0;
+ }
+ case '(': {
+ zErr = re_subcompile_re(p);
+ if( zErr ) return zErr;
+ if( rePeek(p)!=')' ) return "unmatched '('";
+ p->sIn.i++;
+ break;
+ }
+ case '.': {
+ if( rePeek(p)=='*' ){
+ re_append(p, RE_OP_ANYSTAR, 0);
+ p->sIn.i++;
+ }else{
+ re_append(p, RE_OP_ANY, 0);
+ }
+ break;
+ }
+ case '*': {
+ if( iPrev<0 ) return "'*' without operand";
+ re_insert(p, iPrev, RE_OP_GOTO, p->nState - iPrev + 1);
+ re_append(p, RE_OP_FORK, iPrev - p->nState + 1);
+ break;
+ }
+ case '+': {
+ if( iPrev<0 ) return "'+' without operand";
+ re_append(p, RE_OP_FORK, iPrev - p->nState);
+ break;
+ }
+ case '?': {
+ if( iPrev<0 ) return "'?' without operand";
+ re_insert(p, iPrev, RE_OP_FORK, p->nState - iPrev+1);
+ break;
+ }
+ case '{': {
+ int m = 0, n = 0;
+ int sz, j;
+ if( iPrev<0 ) return "'{m,n}' without operand";
+ while( (c=rePeek(p))>='0' && c<='9' ){ m = m*10 + c - '0'; p->sIn.i++; }
+ n = m;
+ if( c==',' ){
+ p->sIn.i++;
+ n = 0;
+ while( (c=rePeek(p))>='0' && c<='9' ){ n = n*10 + c-'0'; p->sIn.i++; }
+ }
+ if( c!='}' ) return "unmatched '{'";
+ if( n>0 && n<m ) return "n less than m in '{m,n}'";
+ p->sIn.i++;
+ sz = p->nState - iPrev;
+ if( m==0 ){
+ if( n==0 ) return "both m and n are zero in '{m,n}'";
+ re_insert(p, iPrev, RE_OP_FORK, sz+1);
+ n--;
+ }else{
+ for(j=1; j<m; j++) re_copy(p, iPrev, sz);
+ }
+ for(j=m; j<n; j++){
+ re_append(p, RE_OP_FORK, sz+1);
+ re_copy(p, iPrev, sz);
+ }
+ if( n==0 && m>0 ){
+ re_append(p, RE_OP_FORK, -sz);
+ }
+ break;
+ }
+ case '[': {
+ int iFirst = p->nState;
+ if( rePeek(p)=='^' ){
+ re_append(p, RE_OP_CC_EXC, 0);
+ p->sIn.i++;
+ }else{
+ re_append(p, RE_OP_CC_INC, 0);
+ }
+ while( (c = p->xNextChar(&p->sIn))!=0 ){
+ if( c=='[' && rePeek(p)==':' ){
+ return "POSIX character classes not supported";
+ }
+ if( c=='\\' ) c = re_esc_char(p);
+ if( rePeek(p)=='-' ){
+ re_append(p, RE_OP_CC_RANGE, c);
+ p->sIn.i++;
+ c = p->xNextChar(&p->sIn);
+ if( c=='\\' ) c = re_esc_char(p);
+ re_append(p, RE_OP_CC_RANGE, c);
+ }else{
+ re_append(p, RE_OP_CC_VALUE, c);
+ }
+ if( rePeek(p)==']' ){ p->sIn.i++; break; }
+ }
+ if( c==0 ) return "unclosed '['";
+ p->aArg[iFirst] = p->nState - iFirst;
+ break;
+ }
+ case '\\': {
+ int specialOp = 0;
+ switch( rePeek(p) ){
+ case 'b': specialOp = RE_OP_BOUNDARY; break;
+ case 'd': specialOp = RE_OP_DIGIT; break;
+ case 'D': specialOp = RE_OP_NOTDIGIT; break;
+ case 's': specialOp = RE_OP_SPACE; break;
+ case 'S': specialOp = RE_OP_NOTSPACE; break;
+ case 'w': specialOp = RE_OP_WORD; break;
+ case 'W': specialOp = RE_OP_NOTWORD; break;
+ }
+ if( specialOp ){
+ p->sIn.i++;
+ re_append(p, specialOp, 0);
+ }else{
+ c = re_esc_char(p);
+ re_append(p, RE_OP_MATCH, c);
+ }
+ break;
+ }
+ default: {
+ re_append(p, RE_OP_MATCH, c);
+ break;
+ }
+ }
+ iPrev = iStart;
+ }
+ return 0;
+}
+
+/* Free and reclaim all the memory used by a previously compiled
+** regular expression. Applications should invoke this routine once
+** for every call to re_compile() to avoid memory leaks.
+*/
+static void re_free(ReCompiled *pRe){
+ if( pRe ){
+ sqlite3_free(pRe->aOp);
+ sqlite3_free(pRe->aArg);
+ sqlite3_free(pRe);
+ }
+}
+
+/*
+** Compile a textual regular expression in zIn[] into a compiled regular
+** expression suitable for us by re_match() and return a pointer to the
+** compiled regular expression in *ppRe. Return NULL on success or an
+** error message if something goes wrong.
+*/
+static const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){
+ ReCompiled *pRe;
+ const char *zErr;
+ int i, j;
+
+ *ppRe = 0;
+ pRe = sqlite3_malloc( sizeof(*pRe) );
+ if( pRe==0 ){
+ return "out of memory";
+ }
+ memset(pRe, 0, sizeof(*pRe));
+ pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char;
+ if( re_resize(pRe, 30) ){
+ re_free(pRe);
+ return "out of memory";
+ }
+ if( zIn[0]=='^' ){
+ zIn++;
+ }else{
+ re_append(pRe, RE_OP_ANYSTAR, 0);
+ }
+ pRe->sIn.z = (unsigned char*)zIn;
+ pRe->sIn.i = 0;
+ pRe->sIn.mx = (int)strlen(zIn);
+ zErr = re_subcompile_re(pRe);
+ if( zErr ){
+ re_free(pRe);
+ return zErr;
+ }
+ if( rePeek(pRe)=='$' && pRe->sIn.i+1>=pRe->sIn.mx ){
+ re_append(pRe, RE_OP_MATCH, RE_EOF);
+ re_append(pRe, RE_OP_ACCEPT, 0);
+ *ppRe = pRe;
+ }else if( pRe->sIn.i>=pRe->sIn.mx ){
+ re_append(pRe, RE_OP_ACCEPT, 0);
+ *ppRe = pRe;
+ }else{
+ re_free(pRe);
+ return "unrecognized character";
+ }
+
+ /* The following is a performance optimization. If the regex begins with
+ ** ".*" (if the input regex lacks an initial "^") and afterwards there are
+ ** one or more matching characters, enter those matching characters into
+ ** zInit[]. The re_match() routine can then search ahead in the input
+ ** string looking for the initial match without having to run the whole
+ ** regex engine over the string. Do not worry able trying to match
+ ** unicode characters beyond plane 0 - those are very rare and this is
+ ** just an optimization. */
+ if( pRe->aOp[0]==RE_OP_ANYSTAR && !noCase ){
+ for(j=0, i=1; j<(int)sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){
+ unsigned x = pRe->aArg[i];
+ if( x<=127 ){
+ pRe->zInit[j++] = (unsigned char)x;
+ }else if( x<=0xfff ){
+ pRe->zInit[j++] = (unsigned char)(0xc0 | (x>>6));
+ pRe->zInit[j++] = 0x80 | (x&0x3f);
+ }else if( x<=0xffff ){
+ pRe->zInit[j++] = (unsigned char)(0xe0 | (x>>12));
+ pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f);
+ pRe->zInit[j++] = 0x80 | (x&0x3f);
+ }else{
+ break;
+ }
+ }
+ if( j>0 && pRe->zInit[j-1]==0 ) j--;
+ pRe->nInit = j;
+ }
+ return pRe->zErr;
+}
+
+/*
+** Implementation of the regexp() SQL function. This function implements
+** the build-in REGEXP operator. The first argument to the function is the
+** pattern and the second argument is the string. So, the SQL statements:
+**
+** A REGEXP B
+**
+** is implemented as regexp(B,A).
+*/
+static void re_sql_func(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ ReCompiled *pRe; /* Compiled regular expression */
+ const char *zPattern; /* The regular expression */
+ const unsigned char *zStr;/* String being searched */
+ const char *zErr; /* Compile error message */
+ int setAux = 0; /* True to invoke sqlite3_set_auxdata() */
+
+ (void)argc; /* Unused */
+ pRe = sqlite3_get_auxdata(context, 0);
+ if( pRe==0 ){
+ zPattern = (const char*)sqlite3_value_text(argv[0]);
+ if( zPattern==0 ) return;
+ zErr = re_compile(&pRe, zPattern, sqlite3_user_data(context)!=0);
+ if( zErr ){
+ re_free(pRe);
+ sqlite3_result_error(context, zErr, -1);
+ return;
+ }
+ if( pRe==0 ){
+ sqlite3_result_error_nomem(context);
+ return;
+ }
+ setAux = 1;
+ }
+ zStr = (const unsigned char*)sqlite3_value_text(argv[1]);
+ if( zStr!=0 ){
+ sqlite3_result_int(context, re_match(pRe, zStr, -1));
+ }
+ if( setAux ){
+ sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free);
+ }
+}
+
+/*
+** Invoke this routine to register the regexp() function with the
+** SQLite database connection.
+*/
+#ifdef _WIN32
+
+#endif
+int sqlite3_regexp_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ int rc = SQLITE_OK;
+ SQLITE_EXTENSION_INIT2(pApi);
+ (void)pzErrMsg; /* Unused */
+ rc = sqlite3_create_function(db, "regexp", 2,
+ SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
+ 0, re_sql_func, 0, 0);
+ if( rc==SQLITE_OK ){
+ /* The regexpi(PATTERN,STRING) function is a case-insensitive version
+ ** of regexp(PATTERN,STRING). */
+ rc = sqlite3_create_function(db, "regexpi", 2,
+ SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
+ (void*)db, re_sql_func, 0, 0);
+ }
+ return rc;
+}
+
+/************************* End ../ext/misc/regexp.c ********************/
+#ifndef SQLITE_SHELL_WASM_MODE
/************************* Begin ../ext/misc/fileio.c ******************/
/*
** 2014-06-13
@@ -2191,6 +4573,11 @@ int sqlite3_shathree_init(
** $path is a relative path, then $path is interpreted relative to $dir.
** And the paths returned in the "name" column of the table are also
** relative to directory $dir.
+**
+** Notes on building this extension for Windows:
+** Unless linked statically with the SQLite library, a preprocessor
+** symbol, FILEIO_WIN32_DLL, must be #define'd to create a stand-alone
+** DLL form of this extension for WIN32. See its use below for details.
*/
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
@@ -2344,6 +4731,22 @@ static sqlite3_uint64 fileTimeToUnixTime(
return (fileIntervals.QuadPart - epochIntervals.QuadPart) / 10000000;
}
+
+#if defined(FILEIO_WIN32_DLL) && (defined(_WIN32) || defined(WIN32))
+# /* To allow a standalone DLL, use this next replacement function: */
+# undef sqlite3_win32_utf8_to_unicode
+# define sqlite3_win32_utf8_to_unicode utf8_to_utf16
+#
+LPWSTR utf8_to_utf16(const char *z){
+ int nAllot = MultiByteToWideChar(CP_UTF8, 0, z, -1, NULL, 0);
+ LPWSTR rv = sqlite3_malloc(nAllot * sizeof(WCHAR));
+ if( rv!=0 && 0 < MultiByteToWideChar(CP_UTF8, 0, z, -1, rv, nAllot) )
+ return rv;
+ sqlite3_free(rv);
+ return 0;
+}
+#endif
+
/*
** This function attempts to normalize the time values found in the stat()
** buffer to UTC. This is necessary on Win32, where the runtime library
@@ -2466,10 +4869,11 @@ static int writeFile(
mode_t mode, /* MODE parameter passed to writefile() */
sqlite3_int64 mtime /* MTIME parameter (or -1 to not set time) */
){
+ if( zFile==0 ) return 1;
#if !defined(_WIN32) && !defined(WIN32)
if( S_ISLNK(mode) ){
const char *zTo = (const char*)sqlite3_value_text(pData);
- if( symlink(zTo, zFile)<0 ) return 1;
+ if( zTo==0 || symlink(zTo, zFile)<0 ) return 1;
}else
#endif
{
@@ -3118,6 +5522,14 @@ int sqlite3_fileio_init(
return rc;
}
+#if defined(FILEIO_WIN32_DLL) && (defined(_WIN32) || defined(WIN32))
+/* To allow a standalone DLL, make test_windirent.c use the same
+ * redefined SQLite API calls as the above extension code does.
+ * Just pull in this .c to accomplish this. As a beneficial side
+ * effect, this extension becomes a single translation unit. */
+# include "test_windirent.c"
+#endif
+
/************************* End ../ext/misc/fileio.c ********************/
/************************* Begin ../ext/misc/completion.c ******************/
/*
@@ -4156,9 +6568,11 @@ static int apndOpen(
rc = pBaseVfs->xOpen(pBaseVfs, zName, pBaseFile, flags, pOutFlags);
if( rc==SQLITE_OK ){
rc = pBaseFile->pMethods->xFileSize(pBaseFile, &sz);
+ if( rc ){
+ pBaseFile->pMethods->xClose(pBaseFile);
+ }
}
if( rc ){
- pBaseFile->pMethods->xClose(pBaseFile);
pFile->pMethods = 0;
return rc;
}
@@ -4281,6 +6695,7 @@ int sqlite3_appendvfs_init(
(void)pzErrMsg;
(void)db;
pOrig = sqlite3_vfs_find(0);
+ if( pOrig==0 ) return SQLITE_ERROR;
apnd_vfs.iVersion = pOrig->iVersion;
apnd_vfs.pAppData = pOrig;
apnd_vfs.szOsFile = pOrig->szOsFile + sizeof(ApndFile);
@@ -4295,1589 +6710,7 @@ int sqlite3_appendvfs_init(
}
/************************* End ../ext/misc/appendvfs.c ********************/
-/************************* Begin ../ext/misc/memtrace.c ******************/
-/*
-** 2019-01-21
-**
-** 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 an extension that uses the SQLITE_CONFIG_MALLOC
-** mechanism to add a tracing layer on top of SQLite. If this extension
-** is registered prior to sqlite3_initialize(), it will cause all memory
-** allocation activities to be logged on standard output, or to some other
-** FILE specified by the initializer.
-**
-** This file needs to be compiled into the application that uses it.
-**
-** This extension is used to implement the --memtrace option of the
-** command-line shell.
-*/
-#include <assert.h>
-#include <string.h>
-#include <stdio.h>
-
-/* The original memory allocation routines */
-static sqlite3_mem_methods memtraceBase;
-static FILE *memtraceOut;
-
-/* Methods that trace memory allocations */
-static void *memtraceMalloc(int n){
- if( memtraceOut ){
- fprintf(memtraceOut, "MEMTRACE: allocate %d bytes\n",
- memtraceBase.xRoundup(n));
- }
- return memtraceBase.xMalloc(n);
-}
-static void memtraceFree(void *p){
- if( p==0 ) return;
- if( memtraceOut ){
- fprintf(memtraceOut, "MEMTRACE: free %d bytes\n", memtraceBase.xSize(p));
- }
- memtraceBase.xFree(p);
-}
-static void *memtraceRealloc(void *p, int n){
- if( p==0 ) return memtraceMalloc(n);
- if( n==0 ){
- memtraceFree(p);
- return 0;
- }
- if( memtraceOut ){
- fprintf(memtraceOut, "MEMTRACE: resize %d -> %d bytes\n",
- memtraceBase.xSize(p), memtraceBase.xRoundup(n));
- }
- return memtraceBase.xRealloc(p, n);
-}
-static int memtraceSize(void *p){
- return memtraceBase.xSize(p);
-}
-static int memtraceRoundup(int n){
- return memtraceBase.xRoundup(n);
-}
-static int memtraceInit(void *p){
- return memtraceBase.xInit(p);
-}
-static void memtraceShutdown(void *p){
- memtraceBase.xShutdown(p);
-}
-
-/* The substitute memory allocator */
-static sqlite3_mem_methods ersaztMethods = {
- memtraceMalloc,
- memtraceFree,
- memtraceRealloc,
- memtraceSize,
- memtraceRoundup,
- memtraceInit,
- memtraceShutdown,
- 0
-};
-
-/* Begin tracing memory allocations to out. */
-int sqlite3MemTraceActivate(FILE *out){
- int rc = SQLITE_OK;
- if( memtraceBase.xMalloc==0 ){
- rc = sqlite3_config(SQLITE_CONFIG_GETMALLOC, &memtraceBase);
- if( rc==SQLITE_OK ){
- rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &ersaztMethods);
- }
- }
- memtraceOut = out;
- return rc;
-}
-
-/* Deactivate memory tracing */
-int sqlite3MemTraceDeactivate(void){
- int rc = SQLITE_OK;
- if( memtraceBase.xMalloc!=0 ){
- rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &memtraceBase);
- if( rc==SQLITE_OK ){
- memset(&memtraceBase, 0, sizeof(memtraceBase));
- }
- }
- memtraceOut = 0;
- return rc;
-}
-
-/************************* End ../ext/misc/memtrace.c ********************/
-/************************* Begin ../ext/misc/uint.c ******************/
-/*
-** 2020-04-14
-**
-** 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 SQLite extension implements the UINT collating sequence.
-**
-** UINT works like BINARY for text, except that embedded strings
-** of digits compare in numeric order.
-**
-** * Leading zeros are handled properly, in the sense that
-** they do not mess of the maginitude comparison of embedded
-** strings of digits. "x00123y" is equal to "x123y".
-**
-** * Only unsigned integers are recognized. Plus and minus
-** signs are ignored. Decimal points and exponential notation
-** are ignored.
-**
-** * Embedded integers can be of arbitrary length. Comparison
-** is *not* limited integers that can be expressed as a
-** 64-bit machine integer.
-*/
-/* #include "sqlite3ext.h" */
-SQLITE_EXTENSION_INIT1
-#include <assert.h>
-#include <string.h>
-#include <ctype.h>
-
-/*
-** Compare text in lexicographic order, except strings of digits
-** compare in numeric order.
-*/
-static int uintCollFunc(
- void *notUsed,
- int nKey1, const void *pKey1,
- int nKey2, const void *pKey2
-){
- const unsigned char *zA = (const unsigned char*)pKey1;
- const unsigned char *zB = (const unsigned char*)pKey2;
- int i=0, j=0, x;
- (void)notUsed;
- while( i<nKey1 && j<nKey2 ){
- x = zA[i] - zB[j];
- if( isdigit(zA[i]) ){
- int k;
- if( !isdigit(zB[j]) ) return x;
- while( i<nKey1 && zA[i]=='0' ){ i++; }
- while( j<nKey2 && zB[j]=='0' ){ j++; }
- k = 0;
- while( i+k<nKey1 && isdigit(zA[i+k])
- && j+k<nKey2 && isdigit(zB[j+k]) ){
- k++;
- }
- if( i+k<nKey1 && isdigit(zA[i+k]) ){
- return +1;
- }else if( j+k<nKey2 && isdigit(zB[j+k]) ){
- return -1;
- }else{
- x = memcmp(zA+i, zB+j, k);
- if( x ) return x;
- i += k;
- j += k;
- }
- }else if( x ){
- return x;
- }else{
- i++;
- j++;
- }
- }
- return (nKey1 - i) - (nKey2 - j);
-}
-
-#ifdef _WIN32
-
-#endif
-int sqlite3_uint_init(
- sqlite3 *db,
- char **pzErrMsg,
- const sqlite3_api_routines *pApi
-){
- SQLITE_EXTENSION_INIT2(pApi);
- (void)pzErrMsg; /* Unused parameter */
- return sqlite3_create_collation(db, "uint", SQLITE_UTF8, 0, uintCollFunc);
-}
-
-/************************* End ../ext/misc/uint.c ********************/
-/************************* Begin ../ext/misc/decimal.c ******************/
-/*
-** 2020-06-22
-**
-** 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.
-**
-******************************************************************************
-**
-** Routines to implement arbitrary-precision decimal math.
-**
-** The focus here is on simplicity and correctness, not performance.
-*/
-/* #include "sqlite3ext.h" */
-SQLITE_EXTENSION_INIT1
-#include <assert.h>
-#include <string.h>
-#include <ctype.h>
-#include <stdlib.h>
-
-/* Mark a function parameter as unused, to suppress nuisance compiler
-** warnings. */
-#ifndef UNUSED_PARAMETER
-# define UNUSED_PARAMETER(X) (void)(X)
-#endif
-
-
-/* A decimal object */
-typedef struct Decimal Decimal;
-struct Decimal {
- char sign; /* 0 for positive, 1 for negative */
- char oom; /* True if an OOM is encountered */
- char isNull; /* True if holds a NULL rather than a number */
- char isInit; /* True upon initialization */
- int nDigit; /* Total number of digits */
- int nFrac; /* Number of digits to the right of the decimal point */
- signed char *a; /* Array of digits. Most significant first. */
-};
-
-/*
-** Release memory held by a Decimal, but do not free the object itself.
-*/
-static void decimal_clear(Decimal *p){
- sqlite3_free(p->a);
-}
-
-/*
-** Destroy a Decimal object
-*/
-static void decimal_free(Decimal *p){
- if( p ){
- decimal_clear(p);
- sqlite3_free(p);
- }
-}
-
-/*
-** Allocate a new Decimal object. Initialize it to the number given
-** by the input string.
-*/
-static Decimal *decimal_new(
- sqlite3_context *pCtx,
- sqlite3_value *pIn,
- int nAlt,
- const unsigned char *zAlt
-){
- Decimal *p;
- int n, i;
- const unsigned char *zIn;
- int iExp = 0;
- p = sqlite3_malloc( sizeof(*p) );
- if( p==0 ) goto new_no_mem;
- p->sign = 0;
- p->oom = 0;
- p->isInit = 1;
- p->isNull = 0;
- p->nDigit = 0;
- p->nFrac = 0;
- if( zAlt ){
- n = nAlt,
- zIn = zAlt;
- }else{
- if( sqlite3_value_type(pIn)==SQLITE_NULL ){
- p->a = 0;
- p->isNull = 1;
- return p;
- }
- n = sqlite3_value_bytes(pIn);
- zIn = sqlite3_value_text(pIn);
- }
- p->a = sqlite3_malloc64( n+1 );
- if( p->a==0 ) goto new_no_mem;
- for(i=0; isspace(zIn[i]); i++){}
- if( zIn[i]=='-' ){
- p->sign = 1;
- i++;
- }else if( zIn[i]=='+' ){
- i++;
- }
- while( i<n && zIn[i]=='0' ) i++;
- while( i<n ){
- char c = zIn[i];
- if( c>='0' && c<='9' ){
- p->a[p->nDigit++] = c - '0';
- }else if( c=='.' ){
- p->nFrac = p->nDigit + 1;
- }else if( c=='e' || c=='E' ){
- int j = i+1;
- int neg = 0;
- if( j>=n ) break;
- if( zIn[j]=='-' ){
- neg = 1;
- j++;
- }else if( zIn[j]=='+' ){
- j++;
- }
- while( j<n && iExp<1000000 ){
- if( zIn[j]>='0' && zIn[j]<='9' ){
- iExp = iExp*10 + zIn[j] - '0';
- }
- j++;
- }
- if( neg ) iExp = -iExp;
- break;
- }
- i++;
- }
- if( p->nFrac ){
- p->nFrac = p->nDigit - (p->nFrac - 1);
- }
- if( iExp>0 ){
- if( p->nFrac>0 ){
- if( iExp<=p->nFrac ){
- p->nFrac -= iExp;
- iExp = 0;
- }else{
- iExp -= p->nFrac;
- p->nFrac = 0;
- }
- }
- if( iExp>0 ){
- p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 );
- if( p->a==0 ) goto new_no_mem;
- memset(p->a+p->nDigit, 0, iExp);
- p->nDigit += iExp;
- }
- }else if( iExp<0 ){
- int nExtra;
- iExp = -iExp;
- nExtra = p->nDigit - p->nFrac - 1;
- if( nExtra ){
- if( nExtra>=iExp ){
- p->nFrac += iExp;
- iExp = 0;
- }else{
- iExp -= nExtra;
- p->nFrac = p->nDigit - 1;
- }
- }
- if( iExp>0 ){
- p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 );
- if( p->a==0 ) goto new_no_mem;
- memmove(p->a+iExp, p->a, p->nDigit);
- memset(p->a, 0, iExp);
- p->nDigit += iExp;
- p->nFrac += iExp;
- }
- }
- return p;
-
-new_no_mem:
- if( pCtx ) sqlite3_result_error_nomem(pCtx);
- sqlite3_free(p);
- return 0;
-}
-
-/*
-** Make the given Decimal the result.
-*/
-static void decimal_result(sqlite3_context *pCtx, Decimal *p){
- char *z;
- int i, j;
- int n;
- if( p==0 || p->oom ){
- sqlite3_result_error_nomem(pCtx);
- return;
- }
- if( p->isNull ){
- sqlite3_result_null(pCtx);
- return;
- }
- z = sqlite3_malloc( p->nDigit+4 );
- if( z==0 ){
- sqlite3_result_error_nomem(pCtx);
- return;
- }
- i = 0;
- if( p->nDigit==0 || (p->nDigit==1 && p->a[0]==0) ){
- p->sign = 0;
- }
- if( p->sign ){
- z[0] = '-';
- i = 1;
- }
- n = p->nDigit - p->nFrac;
- if( n<=0 ){
- z[i++] = '0';
- }
- j = 0;
- while( n>1 && p->a[j]==0 ){
- j++;
- n--;
- }
- while( n>0 ){
- z[i++] = p->a[j] + '0';
- j++;
- n--;
- }
- if( p->nFrac ){
- z[i++] = '.';
- do{
- z[i++] = p->a[j] + '0';
- j++;
- }while( j<p->nDigit );
- }
- z[i] = 0;
- sqlite3_result_text(pCtx, z, i, sqlite3_free);
-}
-
-/*
-** SQL Function: decimal(X)
-**
-** Convert input X into decimal and then back into text
-*/
-static void decimalFunc(
- sqlite3_context *context,
- int argc,
- sqlite3_value **argv
-){
- Decimal *p = decimal_new(context, argv[0], 0, 0);
- UNUSED_PARAMETER(argc);
- decimal_result(context, p);
- decimal_free(p);
-}
-
-/*
-** Compare to Decimal objects. Return negative, 0, or positive if the
-** first object is less than, equal to, or greater than the second.
-**
-** Preconditions for this routine:
-**
-** pA!=0
-** pA->isNull==0
-** pB!=0
-** pB->isNull==0
-*/
-static int decimal_cmp(const Decimal *pA, const Decimal *pB){
- int nASig, nBSig, rc, n;
- if( pA->sign!=pB->sign ){
- return pA->sign ? -1 : +1;
- }
- if( pA->sign ){
- const Decimal *pTemp = pA;
- pA = pB;
- pB = pTemp;
- }
- nASig = pA->nDigit - pA->nFrac;
- nBSig = pB->nDigit - pB->nFrac;
- if( nASig!=nBSig ){
- return nASig - nBSig;
- }
- n = pA->nDigit;
- if( n>pB->nDigit ) n = pB->nDigit;
- rc = memcmp(pA->a, pB->a, n);
- if( rc==0 ){
- rc = pA->nDigit - pB->nDigit;
- }
- return rc;
-}
-
-/*
-** SQL Function: decimal_cmp(X, Y)
-**
-** Return negative, zero, or positive if X is less then, equal to, or
-** greater than Y.
-*/
-static void decimalCmpFunc(
- sqlite3_context *context,
- int argc,
- sqlite3_value **argv
-){
- Decimal *pA = 0, *pB = 0;
- int rc;
-
- UNUSED_PARAMETER(argc);
- pA = decimal_new(context, argv[0], 0, 0);
- if( pA==0 || pA->isNull ) goto cmp_done;
- pB = decimal_new(context, argv[1], 0, 0);
- if( pB==0 || pB->isNull ) goto cmp_done;
- rc = decimal_cmp(pA, pB);
- if( rc<0 ) rc = -1;
- else if( rc>0 ) rc = +1;
- sqlite3_result_int(context, rc);
-cmp_done:
- decimal_free(pA);
- decimal_free(pB);
-}
-
-/*
-** Expand the Decimal so that it has a least nDigit digits and nFrac
-** digits to the right of the decimal point.
-*/
-static void decimal_expand(Decimal *p, int nDigit, int nFrac){
- int nAddSig;
- int nAddFrac;
- if( p==0 ) return;
- nAddFrac = nFrac - p->nFrac;
- nAddSig = (nDigit - p->nDigit) - nAddFrac;
- if( nAddFrac==0 && nAddSig==0 ) return;
- p->a = sqlite3_realloc64(p->a, nDigit+1);
- if( p->a==0 ){
- p->oom = 1;
- return;
- }
- if( nAddSig ){
- memmove(p->a+nAddSig, p->a, p->nDigit);
- memset(p->a, 0, nAddSig);
- p->nDigit += nAddSig;
- }
- if( nAddFrac ){
- memset(p->a+p->nDigit, 0, nAddFrac);
- p->nDigit += nAddFrac;
- p->nFrac += nAddFrac;
- }
-}
-
-/*
-** Add the value pB into pA.
-**
-** Both pA and pB might become denormalized by this routine.
-*/
-static void decimal_add(Decimal *pA, Decimal *pB){
- int nSig, nFrac, nDigit;
- int i, rc;
- if( pA==0 ){
- return;
- }
- if( pA->oom || pB==0 || pB->oom ){
- pA->oom = 1;
- return;
- }
- if( pA->isNull || pB->isNull ){
- pA->isNull = 1;
- return;
- }
- nSig = pA->nDigit - pA->nFrac;
- if( nSig && pA->a[0]==0 ) nSig--;
- if( nSig<pB->nDigit-pB->nFrac ){
- nSig = pB->nDigit - pB->nFrac;
- }
- nFrac = pA->nFrac;
- if( nFrac<pB->nFrac ) nFrac = pB->nFrac;
- nDigit = nSig + nFrac + 1;
- decimal_expand(pA, nDigit, nFrac);
- decimal_expand(pB, nDigit, nFrac);
- if( pA->oom || pB->oom ){
- pA->oom = 1;
- }else{
- if( pA->sign==pB->sign ){
- int carry = 0;
- for(i=nDigit-1; i>=0; i--){
- int x = pA->a[i] + pB->a[i] + carry;
- if( x>=10 ){
- carry = 1;
- pA->a[i] = x - 10;
- }else{
- carry = 0;
- pA->a[i] = x;
- }
- }
- }else{
- signed char *aA, *aB;
- int borrow = 0;
- rc = memcmp(pA->a, pB->a, nDigit);
- if( rc<0 ){
- aA = pB->a;
- aB = pA->a;
- pA->sign = !pA->sign;
- }else{
- aA = pA->a;
- aB = pB->a;
- }
- for(i=nDigit-1; i>=0; i--){
- int x = aA[i] - aB[i] - borrow;
- if( x<0 ){
- pA->a[i] = x+10;
- borrow = 1;
- }else{
- pA->a[i] = x;
- borrow = 0;
- }
- }
- }
- }
-}
-
-/*
-** Compare text in decimal order.
-*/
-static int decimalCollFunc(
- void *notUsed,
- int nKey1, const void *pKey1,
- int nKey2, const void *pKey2
-){
- const unsigned char *zA = (const unsigned char*)pKey1;
- const unsigned char *zB = (const unsigned char*)pKey2;
- Decimal *pA = decimal_new(0, 0, nKey1, zA);
- Decimal *pB = decimal_new(0, 0, nKey2, zB);
- int rc;
- UNUSED_PARAMETER(notUsed);
- if( pA==0 || pB==0 ){
- rc = 0;
- }else{
- rc = decimal_cmp(pA, pB);
- }
- decimal_free(pA);
- decimal_free(pB);
- return rc;
-}
-
-
-/*
-** SQL Function: decimal_add(X, Y)
-** decimal_sub(X, Y)
-**
-** Return the sum or difference of X and Y.
-*/
-static void decimalAddFunc(
- sqlite3_context *context,
- int argc,
- sqlite3_value **argv
-){
- Decimal *pA = decimal_new(context, argv[0], 0, 0);
- Decimal *pB = decimal_new(context, argv[1], 0, 0);
- UNUSED_PARAMETER(argc);
- decimal_add(pA, pB);
- decimal_result(context, pA);
- decimal_free(pA);
- decimal_free(pB);
-}
-static void decimalSubFunc(
- sqlite3_context *context,
- int argc,
- sqlite3_value **argv
-){
- Decimal *pA = decimal_new(context, argv[0], 0, 0);
- Decimal *pB = decimal_new(context, argv[1], 0, 0);
- UNUSED_PARAMETER(argc);
- if( pB==0 ) return;
- pB->sign = !pB->sign;
- decimal_add(pA, pB);
- decimal_result(context, pA);
- decimal_free(pA);
- decimal_free(pB);
-}
-
-/* Aggregate funcion: decimal_sum(X)
-**
-** Works like sum() except that it uses decimal arithmetic for unlimited
-** precision.
-*/
-static void decimalSumStep(
- sqlite3_context *context,
- int argc,
- sqlite3_value **argv
-){
- Decimal *p;
- Decimal *pArg;
- UNUSED_PARAMETER(argc);
- p = sqlite3_aggregate_context(context, sizeof(*p));
- if( p==0 ) return;
- if( !p->isInit ){
- p->isInit = 1;
- p->a = sqlite3_malloc(2);
- if( p->a==0 ){
- p->oom = 1;
- }else{
- p->a[0] = 0;
- }
- p->nDigit = 1;
- p->nFrac = 0;
- }
- if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
- pArg = decimal_new(context, argv[0], 0, 0);
- decimal_add(p, pArg);
- decimal_free(pArg);
-}
-static void decimalSumInverse(
- sqlite3_context *context,
- int argc,
- sqlite3_value **argv
-){
- Decimal *p;
- Decimal *pArg;
- UNUSED_PARAMETER(argc);
- p = sqlite3_aggregate_context(context, sizeof(*p));
- if( p==0 ) return;
- if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
- pArg = decimal_new(context, argv[0], 0, 0);
- if( pArg ) pArg->sign = !pArg->sign;
- decimal_add(p, pArg);
- decimal_free(pArg);
-}
-static void decimalSumValue(sqlite3_context *context){
- Decimal *p = sqlite3_aggregate_context(context, 0);
- if( p==0 ) return;
- decimal_result(context, p);
-}
-static void decimalSumFinalize(sqlite3_context *context){
- Decimal *p = sqlite3_aggregate_context(context, 0);
- if( p==0 ) return;
- decimal_result(context, p);
- decimal_clear(p);
-}
-
-/*
-** SQL Function: decimal_mul(X, Y)
-**
-** Return the product of X and Y.
-**
-** All significant digits after the decimal point are retained.
-** Trailing zeros after the decimal point are omitted as long as
-** the number of digits after the decimal point is no less than
-** either the number of digits in either input.
-*/
-static void decimalMulFunc(
- sqlite3_context *context,
- int argc,
- sqlite3_value **argv
-){
- Decimal *pA = decimal_new(context, argv[0], 0, 0);
- Decimal *pB = decimal_new(context, argv[1], 0, 0);
- signed char *acc = 0;
- int i, j, k;
- int minFrac;
- UNUSED_PARAMETER(argc);
- if( pA==0 || pA->oom || pA->isNull
- || pB==0 || pB->oom || pB->isNull
- ){
- goto mul_end;
- }
- acc = sqlite3_malloc64( pA->nDigit + pB->nDigit + 2 );
- if( acc==0 ){
- sqlite3_result_error_nomem(context);
- goto mul_end;
- }
- memset(acc, 0, pA->nDigit + pB->nDigit + 2);
- minFrac = pA->nFrac;
- if( pB->nFrac<minFrac ) minFrac = pB->nFrac;
- for(i=pA->nDigit-1; i>=0; i--){
- signed char f = pA->a[i];
- int carry = 0, x;
- for(j=pB->nDigit-1, k=i+j+3; j>=0; j--, k--){
- x = acc[k] + f*pB->a[j] + carry;
- acc[k] = x%10;
- carry = x/10;
- }
- x = acc[k] + carry;
- acc[k] = x%10;
- acc[k-1] += x/10;
- }
- sqlite3_free(pA->a);
- pA->a = acc;
- acc = 0;
- pA->nDigit += pB->nDigit + 2;
- pA->nFrac += pB->nFrac;
- pA->sign ^= pB->sign;
- while( pA->nFrac>minFrac && pA->a[pA->nDigit-1]==0 ){
- pA->nFrac--;
- pA->nDigit--;
- }
- decimal_result(context, pA);
-
-mul_end:
- sqlite3_free(acc);
- decimal_free(pA);
- decimal_free(pB);
-}
-
-#ifdef _WIN32
-
-#endif
-int sqlite3_decimal_init(
- sqlite3 *db,
- char **pzErrMsg,
- const sqlite3_api_routines *pApi
-){
- int rc = SQLITE_OK;
- static const struct {
- const char *zFuncName;
- int nArg;
- void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
- } aFunc[] = {
- { "decimal", 1, decimalFunc },
- { "decimal_cmp", 2, decimalCmpFunc },
- { "decimal_add", 2, decimalAddFunc },
- { "decimal_sub", 2, decimalSubFunc },
- { "decimal_mul", 2, decimalMulFunc },
- };
- unsigned int i;
- (void)pzErrMsg; /* Unused parameter */
-
- SQLITE_EXTENSION_INIT2(pApi);
-
- for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
- rc = sqlite3_create_function(db, aFunc[i].zFuncName, aFunc[i].nArg,
- SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
- 0, aFunc[i].xFunc, 0, 0);
- }
- if( rc==SQLITE_OK ){
- rc = sqlite3_create_window_function(db, "decimal_sum", 1,
- SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC, 0,
- decimalSumStep, decimalSumFinalize,
- decimalSumValue, decimalSumInverse, 0);
- }
- if( rc==SQLITE_OK ){
- rc = sqlite3_create_collation(db, "decimal", SQLITE_UTF8,
- 0, decimalCollFunc);
- }
- return rc;
-}
-
-/************************* End ../ext/misc/decimal.c ********************/
-/************************* Begin ../ext/misc/ieee754.c ******************/
-/*
-** 2013-04-17
-**
-** 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 SQLite extension implements functions for the exact display
-** and input of IEEE754 Binary64 floating-point numbers.
-**
-** ieee754(X)
-** ieee754(Y,Z)
-**
-** In the first form, the value X should be a floating-point number.
-** The function will return a string of the form 'ieee754(Y,Z)' where
-** Y and Z are integers such that X==Y*pow(2,Z).
-**
-** In the second form, Y and Z are integers which are the mantissa and
-** base-2 exponent of a new floating point number. The function returns
-** a floating-point value equal to Y*pow(2,Z).
-**
-** Examples:
-**
-** ieee754(2.0) -> 'ieee754(2,0)'
-** ieee754(45.25) -> 'ieee754(181,-2)'
-** ieee754(2, 0) -> 2.0
-** ieee754(181, -2) -> 45.25
-**
-** Two additional functions break apart the one-argument ieee754()
-** result into separate integer values:
-**
-** ieee754_mantissa(45.25) -> 181
-** ieee754_exponent(45.25) -> -2
-**
-** These functions convert binary64 numbers into blobs and back again.
-**
-** ieee754_from_blob(x'3ff0000000000000') -> 1.0
-** ieee754_to_blob(1.0) -> x'3ff0000000000000'
-**
-** In all single-argument functions, if the argument is an 8-byte blob
-** then that blob is interpreted as a big-endian binary64 value.
-**
-**
-** EXACT DECIMAL REPRESENTATION OF BINARY64 VALUES
-** -----------------------------------------------
-**
-** This extension in combination with the separate 'decimal' extension
-** can be used to compute the exact decimal representation of binary64
-** values. To begin, first compute a table of exponent values:
-**
-** CREATE TABLE pow2(x INTEGER PRIMARY KEY, v TEXT);
-** WITH RECURSIVE c(x,v) AS (
-** VALUES(0,'1')
-** UNION ALL
-** SELECT x+1, decimal_mul(v,'2') FROM c WHERE x+1<=971
-** ) INSERT INTO pow2(x,v) SELECT x, v FROM c;
-** WITH RECURSIVE c(x,v) AS (
-** VALUES(-1,'0.5')
-** UNION ALL
-** SELECT x-1, decimal_mul(v,'0.5') FROM c WHERE x-1>=-1075
-** ) INSERT INTO pow2(x,v) SELECT x, v FROM c;
-**
-** Then, to compute the exact decimal representation of a floating
-** point value (the value 47.49 is used in the example) do:
-**
-** WITH c(n) AS (VALUES(47.49))
-** ---------------^^^^^---- Replace with whatever you want
-** SELECT decimal_mul(ieee754_mantissa(c.n),pow2.v)
-** FROM pow2, c WHERE pow2.x=ieee754_exponent(c.n);
-**
-** Here is a query to show various boundry values for the binary64
-** number format:
-**
-** WITH c(name,bin) AS (VALUES
-** ('minimum positive value', x'0000000000000001'),
-** ('maximum subnormal value', x'000fffffffffffff'),
-** ('mininum positive nornal value', x'0010000000000000'),
-** ('maximum value', x'7fefffffffffffff'))
-** SELECT c.name, decimal_mul(ieee754_mantissa(c.bin),pow2.v)
-** FROM pow2, c WHERE pow2.x=ieee754_exponent(c.bin);
-**
-*/
-/* #include "sqlite3ext.h" */
-SQLITE_EXTENSION_INIT1
-#include <assert.h>
-#include <string.h>
-
-/* Mark a function parameter as unused, to suppress nuisance compiler
-** warnings. */
-#ifndef UNUSED_PARAMETER
-# define UNUSED_PARAMETER(X) (void)(X)
-#endif
-
-/*
-** Implementation of the ieee754() function
-*/
-static void ieee754func(
- sqlite3_context *context,
- int argc,
- sqlite3_value **argv
-){
- if( argc==1 ){
- sqlite3_int64 m, a;
- double r;
- int e;
- int isNeg;
- char zResult[100];
- assert( sizeof(m)==sizeof(r) );
- if( sqlite3_value_type(argv[0])==SQLITE_BLOB
- && sqlite3_value_bytes(argv[0])==sizeof(r)
- ){
- const unsigned char *x = sqlite3_value_blob(argv[0]);
- unsigned int i;
- sqlite3_uint64 v = 0;
- for(i=0; i<sizeof(r); i++){
- v = (v<<8) | x[i];
- }
- memcpy(&r, &v, sizeof(r));
- }else{
- r = sqlite3_value_double(argv[0]);
- }
- if( r<0.0 ){
- isNeg = 1;
- r = -r;
- }else{
- isNeg = 0;
- }
- memcpy(&a,&r,sizeof(a));
- if( a==0 ){
- e = 0;
- m = 0;
- }else{
- e = a>>52;
- m = a & ((((sqlite3_int64)1)<<52)-1);
- if( e==0 ){
- m <<= 1;
- }else{
- m |= ((sqlite3_int64)1)<<52;
- }
- while( e<1075 && m>0 && (m&1)==0 ){
- m >>= 1;
- e++;
- }
- if( isNeg ) m = -m;
- }
- switch( *(int*)sqlite3_user_data(context) ){
- case 0:
- sqlite3_snprintf(sizeof(zResult), zResult, "ieee754(%lld,%d)",
- m, e-1075);
- sqlite3_result_text(context, zResult, -1, SQLITE_TRANSIENT);
- break;
- case 1:
- sqlite3_result_int64(context, m);
- break;
- case 2:
- sqlite3_result_int(context, e-1075);
- break;
- }
- }else{
- sqlite3_int64 m, e, a;
- double r;
- int isNeg = 0;
- m = sqlite3_value_int64(argv[0]);
- e = sqlite3_value_int64(argv[1]);
-
- /* Limit the range of e. Ticket 22dea1cfdb9151e4 2021-03-02 */
- if( e>10000 ){
- e = 10000;
- }else if( e<-10000 ){
- e = -10000;
- }
-
- if( m<0 ){
- isNeg = 1;
- m = -m;
- if( m<0 ) return;
- }else if( m==0 && e>-1000 && e<1000 ){
- sqlite3_result_double(context, 0.0);
- return;
- }
- while( (m>>32)&0xffe00000 ){
- m >>= 1;
- e++;
- }
- while( m!=0 && ((m>>32)&0xfff00000)==0 ){
- m <<= 1;
- e--;
- }
- e += 1075;
- if( e<=0 ){
- /* Subnormal */
- m >>= 1-e;
- e = 0;
- }else if( e>0x7ff ){
- e = 0x7ff;
- }
- a = m & ((((sqlite3_int64)1)<<52)-1);
- a |= e<<52;
- if( isNeg ) a |= ((sqlite3_uint64)1)<<63;
- memcpy(&r, &a, sizeof(r));
- sqlite3_result_double(context, r);
- }
-}
-
-/*
-** Functions to convert between blobs and floats.
-*/
-static void ieee754func_from_blob(
- sqlite3_context *context,
- int argc,
- sqlite3_value **argv
-){
- UNUSED_PARAMETER(argc);
- if( sqlite3_value_type(argv[0])==SQLITE_BLOB
- && sqlite3_value_bytes(argv[0])==sizeof(double)
- ){
- double r;
- const unsigned char *x = sqlite3_value_blob(argv[0]);
- unsigned int i;
- sqlite3_uint64 v = 0;
- for(i=0; i<sizeof(r); i++){
- v = (v<<8) | x[i];
- }
- memcpy(&r, &v, sizeof(r));
- sqlite3_result_double(context, r);
- }
-}
-static void ieee754func_to_blob(
- sqlite3_context *context,
- int argc,
- sqlite3_value **argv
-){
- UNUSED_PARAMETER(argc);
- if( sqlite3_value_type(argv[0])==SQLITE_FLOAT
- || sqlite3_value_type(argv[0])==SQLITE_INTEGER
- ){
- double r = sqlite3_value_double(argv[0]);
- sqlite3_uint64 v;
- unsigned char a[sizeof(r)];
- unsigned int i;
- memcpy(&v, &r, sizeof(r));
- for(i=1; i<=sizeof(r); i++){
- a[sizeof(r)-i] = v&0xff;
- v >>= 8;
- }
- sqlite3_result_blob(context, a, sizeof(r), SQLITE_TRANSIENT);
- }
-}
-
-
-#ifdef _WIN32
-
-#endif
-int sqlite3_ieee_init(
- sqlite3 *db,
- char **pzErrMsg,
- const sqlite3_api_routines *pApi
-){
- static const struct {
- char *zFName;
- int nArg;
- int iAux;
- void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
- } aFunc[] = {
- { "ieee754", 1, 0, ieee754func },
- { "ieee754", 2, 0, ieee754func },
- { "ieee754_mantissa", 1, 1, ieee754func },
- { "ieee754_exponent", 1, 2, ieee754func },
- { "ieee754_to_blob", 1, 0, ieee754func_to_blob },
- { "ieee754_from_blob", 1, 0, ieee754func_from_blob },
-
- };
- unsigned int i;
- int rc = SQLITE_OK;
- SQLITE_EXTENSION_INIT2(pApi);
- (void)pzErrMsg; /* Unused parameter */
- for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
- rc = sqlite3_create_function(db, aFunc[i].zFName, aFunc[i].nArg,
- SQLITE_UTF8|SQLITE_INNOCUOUS,
- (void*)&aFunc[i].iAux,
- aFunc[i].xFunc, 0, 0);
- }
- return rc;
-}
-
-/************************* End ../ext/misc/ieee754.c ********************/
-/************************* Begin ../ext/misc/series.c ******************/
-/*
-** 2015-08-18
-**
-** 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 demonstrates how to create a table-valued-function using
-** a virtual table. This demo implements the generate_series() function
-** which gives similar results to the eponymous function in PostgreSQL.
-** Examples:
-**
-** SELECT * FROM generate_series(0,100,5);
-**
-** The query above returns integers from 0 through 100 counting by steps
-** of 5.
-**
-** SELECT * FROM generate_series(0,100);
-**
-** Integers from 0 through 100 with a step size of 1.
-**
-** SELECT * FROM generate_series(20) LIMIT 10;
-**
-** Integers 20 through 29.
-**
-** HOW IT WORKS
-**
-** The generate_series "function" is really a virtual table with the
-** following schema:
-**
-** CREATE TABLE generate_series(
-** value,
-** start HIDDEN,
-** stop HIDDEN,
-** step HIDDEN
-** );
-**
-** Function arguments in queries against this virtual table are translated
-** into equality constraints against successive hidden columns. In other
-** words, the following pairs of queries are equivalent to each other:
-**
-** SELECT * FROM generate_series(0,100,5);
-** SELECT * FROM generate_series WHERE start=0 AND stop=100 AND step=5;
-**
-** SELECT * FROM generate_series(0,100);
-** SELECT * FROM generate_series WHERE start=0 AND stop=100;
-**
-** SELECT * FROM generate_series(20) LIMIT 10;
-** SELECT * FROM generate_series WHERE start=20 LIMIT 10;
-**
-** The generate_series virtual table implementation leaves the xCreate method
-** set to NULL. This means that it is not possible to do a CREATE VIRTUAL
-** TABLE command with "generate_series" as the USING argument. Instead, there
-** is a single generate_series virtual table that is always available without
-** having to be created first.
-**
-** The xBestIndex method looks for equality constraints against the hidden
-** start, stop, and step columns, and if present, it uses those constraints
-** to bound the sequence of generated values. If the equality constraints
-** are missing, it uses 0 for start, 4294967295 for stop, and 1 for step.
-** xBestIndex returns a small cost when both start and stop are available,
-** and a very large cost if either start or stop are unavailable. This
-** encourages the query planner to order joins such that the bounds of the
-** series are well-defined.
-*/
-/* #include "sqlite3ext.h" */
-SQLITE_EXTENSION_INIT1
-#include <assert.h>
-#include <string.h>
-
-#ifndef SQLITE_OMIT_VIRTUALTABLE
-
-
-/* series_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 series_cursor series_cursor;
-struct series_cursor {
- sqlite3_vtab_cursor base; /* Base class - must be first */
- int isDesc; /* True to count down rather than up */
- sqlite3_int64 iRowid; /* The rowid */
- sqlite3_int64 iValue; /* Current value ("value") */
- sqlite3_int64 mnValue; /* Mimimum value ("start") */
- sqlite3_int64 mxValue; /* Maximum value ("stop") */
- sqlite3_int64 iStep; /* Increment ("step") */
-};
-
-/*
-** The seriesConnect() method is invoked to create a new
-** series_vtab that describes the generate_series virtual table.
-**
-** Think of this routine as the constructor for series_vtab objects.
-**
-** All this routine needs to do is:
-**
-** (1) Allocate the series_vtab object and initialize all fields.
-**
-** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
-** result set of queries against generate_series will look like.
-*/
-static int seriesConnect(
- sqlite3 *db,
- void *pUnused,
- int argcUnused, const char *const*argvUnused,
- sqlite3_vtab **ppVtab,
- char **pzErrUnused
-){
- sqlite3_vtab *pNew;
- int rc;
-
-/* Column numbers */
-#define SERIES_COLUMN_VALUE 0
-#define SERIES_COLUMN_START 1
-#define SERIES_COLUMN_STOP 2
-#define SERIES_COLUMN_STEP 3
-
- (void)pUnused;
- (void)argcUnused;
- (void)argvUnused;
- (void)pzErrUnused;
- rc = sqlite3_declare_vtab(db,
- "CREATE TABLE x(value,start hidden,stop hidden,step hidden)");
- if( rc==SQLITE_OK ){
- pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
- if( pNew==0 ) return SQLITE_NOMEM;
- memset(pNew, 0, sizeof(*pNew));
- sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
- }
- return rc;
-}
-
-/*
-** This method is the destructor for series_cursor objects.
-*/
-static int seriesDisconnect(sqlite3_vtab *pVtab){
- sqlite3_free(pVtab);
- return SQLITE_OK;
-}
-
-/*
-** Constructor for a new series_cursor object.
-*/
-static int seriesOpen(sqlite3_vtab *pUnused, sqlite3_vtab_cursor **ppCursor){
- series_cursor *pCur;
- (void)pUnused;
- 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 series_cursor.
-*/
-static int seriesClose(sqlite3_vtab_cursor *cur){
- sqlite3_free(cur);
- return SQLITE_OK;
-}
-
-
-/*
-** Advance a series_cursor to its next row of output.
-*/
-static int seriesNext(sqlite3_vtab_cursor *cur){
- series_cursor *pCur = (series_cursor*)cur;
- if( pCur->isDesc ){
- pCur->iValue -= pCur->iStep;
- }else{
- pCur->iValue += pCur->iStep;
- }
- pCur->iRowid++;
- return SQLITE_OK;
-}
-
-/*
-** Return values of columns for the row at which the series_cursor
-** is currently pointing.
-*/
-static int seriesColumn(
- sqlite3_vtab_cursor *cur, /* The cursor */
- sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
- int i /* Which column to return */
-){
- series_cursor *pCur = (series_cursor*)cur;
- sqlite3_int64 x = 0;
- switch( i ){
- case SERIES_COLUMN_START: x = pCur->mnValue; break;
- case SERIES_COLUMN_STOP: x = pCur->mxValue; break;
- case SERIES_COLUMN_STEP: x = pCur->iStep; break;
- default: x = pCur->iValue; break;
- }
- sqlite3_result_int64(ctx, x);
- return SQLITE_OK;
-}
-
-/*
-** Return the rowid for the current row. In this implementation, the
-** first row returned is assigned rowid value 1, and each subsequent
-** row a value 1 more than that of the previous.
-*/
-static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
- series_cursor *pCur = (series_cursor*)cur;
- *pRowid = pCur->iRowid;
- return SQLITE_OK;
-}
-
-/*
-** Return TRUE if the cursor has been moved off of the last
-** row of output.
-*/
-static int seriesEof(sqlite3_vtab_cursor *cur){
- series_cursor *pCur = (series_cursor*)cur;
- if( pCur->isDesc ){
- return pCur->iValue < pCur->mnValue;
- }else{
- return pCur->iValue > pCur->mxValue;
- }
-}
-
-/* True to cause run-time checking of the start=, stop=, and/or step=
-** parameters. The only reason to do this is for testing the
-** constraint checking logic for virtual tables in the SQLite core.
-*/
-#ifndef SQLITE_SERIES_CONSTRAINT_VERIFY
-# define SQLITE_SERIES_CONSTRAINT_VERIFY 0
-#endif
-
-/*
-** This method is called to "rewind" the series_cursor object back
-** to the first row of output. This method is always called at least
-** once prior to any call to seriesColumn() or seriesRowid() or
-** seriesEof().
-**
-** The query plan selected by seriesBestIndex is passed in the idxNum
-** parameter. (idxStr is not used in this implementation.) idxNum
-** is a bitmask showing which constraints are available:
-**
-** 1: start=VALUE
-** 2: stop=VALUE
-** 4: step=VALUE
-**
-** Also, if bit 8 is set, that means that the series should be output
-** in descending order rather than in ascending order. If bit 16 is
-** set, then output must appear in ascending order.
-**
-** This routine should initialize the cursor and position it so that it
-** is pointing at the first row, or pointing off the end of the table
-** (so that seriesEof() will return true) if the table is empty.
-*/
-static int seriesFilter(
- sqlite3_vtab_cursor *pVtabCursor,
- int idxNum, const char *idxStrUnused,
- int argc, sqlite3_value **argv
-){
- series_cursor *pCur = (series_cursor *)pVtabCursor;
- int i = 0;
- (void)idxStrUnused;
- if( idxNum & 1 ){
- pCur->mnValue = sqlite3_value_int64(argv[i++]);
- }else{
- pCur->mnValue = 0;
- }
- if( idxNum & 2 ){
- pCur->mxValue = sqlite3_value_int64(argv[i++]);
- }else{
- pCur->mxValue = 0xffffffff;
- }
- if( idxNum & 4 ){
- pCur->iStep = sqlite3_value_int64(argv[i++]);
- if( pCur->iStep==0 ){
- pCur->iStep = 1;
- }else if( pCur->iStep<0 ){
- pCur->iStep = -pCur->iStep;
- if( (idxNum & 16)==0 ) idxNum |= 8;
- }
- }else{
- pCur->iStep = 1;
- }
- for(i=0; i<argc; i++){
- if( sqlite3_value_type(argv[i])==SQLITE_NULL ){
- /* If any of the constraints have a NULL value, then return no rows.
- ** See ticket https://www.sqlite.org/src/info/fac496b61722daf2 */
- pCur->mnValue = 1;
- pCur->mxValue = 0;
- break;
- }
- }
- if( idxNum & 8 ){
- pCur->isDesc = 1;
- pCur->iValue = pCur->mxValue;
- if( pCur->iStep>0 ){
- pCur->iValue -= (pCur->mxValue - pCur->mnValue)%pCur->iStep;
- }
- }else{
- pCur->isDesc = 0;
- pCur->iValue = pCur->mnValue;
- }
- pCur->iRowid = 1;
- return SQLITE_OK;
-}
-
-/*
-** SQLite will invoke this method one or more times while planning a query
-** that uses the generate_series virtual table. This routine needs to create
-** a query plan for each invocation and compute an estimated cost for that
-** plan.
-**
-** In this implementation idxNum is used to represent the
-** query plan. idxStr is unused.
-**
-** The query plan is represented by bits in idxNum:
-**
-** (1) start = $value -- constraint exists
-** (2) stop = $value -- constraint exists
-** (4) step = $value -- constraint exists
-** (8) output in descending order
-*/
-static int seriesBestIndex(
- sqlite3_vtab *tabUnused,
- sqlite3_index_info *pIdxInfo
-){
- int i, j; /* Loop over constraints */
- int idxNum = 0; /* The query plan bitmask */
- int unusableMask = 0; /* Mask of unusable constraints */
- int nArg = 0; /* Number of arguments that seriesFilter() expects */
- int aIdx[3]; /* Constraints on start, stop, and step */
- const struct sqlite3_index_constraint *pConstraint;
-
- /* This implementation assumes that the start, stop, and step columns
- ** are the last three columns in the virtual table. */
- assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 );
- assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 );
- (void)tabUnused;
- aIdx[0] = aIdx[1] = aIdx[2] = -1;
- pConstraint = pIdxInfo->aConstraint;
- for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
- int iCol; /* 0 for start, 1 for stop, 2 for step */
- int iMask; /* bitmask for those column */
- if( pConstraint->iColumn<SERIES_COLUMN_START ) continue;
- iCol = pConstraint->iColumn - SERIES_COLUMN_START;
- assert( iCol>=0 && iCol<=2 );
- iMask = 1 << iCol;
- if( pConstraint->usable==0 ){
- unusableMask |= iMask;
- continue;
- }else if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
- idxNum |= iMask;
- aIdx[iCol] = i;
- }
- }
- for(i=0; i<3; i++){
- if( (j = aIdx[i])>=0 ){
- pIdxInfo->aConstraintUsage[j].argvIndex = ++nArg;
- pIdxInfo->aConstraintUsage[j].omit = !SQLITE_SERIES_CONSTRAINT_VERIFY;
- }
- }
- if( (unusableMask & ~idxNum)!=0 ){
- /* The start, stop, and step columns are inputs. Therefore if there
- ** are unusable constraints on any of start, stop, or step then
- ** this plan is unusable */
- return SQLITE_CONSTRAINT;
- }
- if( (idxNum & 3)==3 ){
- /* Both start= and stop= boundaries are available. This is the
- ** the preferred case */
- pIdxInfo->estimatedCost = (double)(2 - ((idxNum&4)!=0));
- pIdxInfo->estimatedRows = 1000;
- if( pIdxInfo->nOrderBy==1 ){
- if( pIdxInfo->aOrderBy[0].desc ){
- idxNum |= 8;
- }else{
- idxNum |= 16;
- }
- pIdxInfo->orderByConsumed = 1;
- }
- }else{
- /* If either boundary is missing, we have to generate a huge span
- ** of numbers. Make this case very expensive so that the query
- ** planner will work hard to avoid it. */
- pIdxInfo->estimatedRows = 2147483647;
- }
- pIdxInfo->idxNum = idxNum;
- return SQLITE_OK;
-}
-
-/*
-** This following structure defines all the methods for the
-** generate_series virtual table.
-*/
-static sqlite3_module seriesModule = {
- 0, /* iVersion */
- 0, /* xCreate */
- seriesConnect, /* xConnect */
- seriesBestIndex, /* xBestIndex */
- seriesDisconnect, /* xDisconnect */
- 0, /* xDestroy */
- seriesOpen, /* xOpen - open a cursor */
- seriesClose, /* xClose - close a cursor */
- seriesFilter, /* xFilter - configure scan constraints */
- seriesNext, /* xNext - advance a cursor */
- seriesEof, /* xEof - check for end of scan */
- seriesColumn, /* xColumn - read data */
- seriesRowid, /* xRowid - read data */
- 0, /* xUpdate */
- 0, /* xBegin */
- 0, /* xSync */
- 0, /* xCommit */
- 0, /* xRollback */
- 0, /* xFindMethod */
- 0, /* xRename */
- 0, /* xSavepoint */
- 0, /* xRelease */
- 0, /* xRollbackTo */
- 0 /* xShadowName */
-};
-
-#endif /* SQLITE_OMIT_VIRTUALTABLE */
-
-#ifdef _WIN32
-
-#endif
-int sqlite3_series_init(
- sqlite3 *db,
- char **pzErrMsg,
- const sqlite3_api_routines *pApi
-){
- int rc = SQLITE_OK;
- SQLITE_EXTENSION_INIT2(pApi);
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( sqlite3_libversion_number()<3008012 ){
- *pzErrMsg = sqlite3_mprintf(
- "generate_series() requires SQLite 3.8.12 or later");
- return SQLITE_ERROR;
- }
- rc = sqlite3_create_module(db, "generate_series", &seriesModule, 0);
#endif
- return rc;
-}
-
-/************************* End ../ext/misc/series.c ********************/
#ifdef SQLITE_HAVE_ZLIB
/************************* Begin ../ext/misc/zipfile.c ******************/
/*
@@ -5918,13 +6751,30 @@ SQLITE_EXTENSION_INIT1
#ifndef SQLITE_AMALGAMATION
+#ifndef UINT32_TYPE
+# ifdef HAVE_UINT32_T
+# define UINT32_TYPE uint32_t
+# else
+# define UINT32_TYPE unsigned int
+# endif
+#endif
+#ifndef UINT16_TYPE
+# ifdef HAVE_UINT16_T
+# define UINT16_TYPE uint16_t
+# else
+# define UINT16_TYPE unsigned short int
+# endif
+#endif
/* typedef sqlite3_int64 i64; */
/* typedef unsigned char u8; */
-typedef unsigned short u16;
-typedef unsigned long u32;
+typedef UINT32_TYPE u32; /* 4-byte unsigned integer */
+typedef UINT16_TYPE u16; /* 2-byte unsigned integer */
#define MIN(a,b) ((a)<(b) ? (a) : (b))
#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
+# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1
+#endif
+#if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS)
# define ALWAYS(X) (1)
# define NEVER(X) (0)
#elif !defined(NDEBUG)
@@ -6429,6 +7279,7 @@ static u16 zipfileGetU16(const u8 *aBuf){
** Read and return a 32-bit little-endian unsigned integer from buffer aBuf.
*/
static u32 zipfileGetU32(const u8 *aBuf){
+ if( aBuf==0 ) return 0;
return ((u32)(aBuf[3]) << 24)
+ ((u32)(aBuf[2]) << 16)
+ ((u32)(aBuf[1]) << 8)
@@ -6588,34 +7439,24 @@ static int zipfileScanExtra(u8 *aExtra, int nExtra, u32 *pmTime){
** https://msdn.microsoft.com/en-us/library/9kkf9tah.aspx
*/
static u32 zipfileMtime(ZipfileCDS *pCDS){
- int Y = (1980 + ((pCDS->mDate >> 9) & 0x7F));
- int M = ((pCDS->mDate >> 5) & 0x0F);
- int D = (pCDS->mDate & 0x1F);
- int B = -13;
-
- int sec = (pCDS->mTime & 0x1F)*2;
- int min = (pCDS->mTime >> 5) & 0x3F;
- int hr = (pCDS->mTime >> 11) & 0x1F;
- i64 JD;
-
- /* JD = INT(365.25 * (Y+4716)) + INT(30.6001 * (M+1)) + D + B - 1524.5 */
-
- /* Calculate the JD in seconds for noon on the day in question */
- if( M<3 ){
- Y = Y-1;
- M = M+12;
- }
- JD = (i64)(24*60*60) * (
- (int)(365.25 * (Y + 4716))
- + (int)(30.6001 * (M + 1))
- + D + B - 1524
- );
-
- /* Correct the JD for the time within the day */
- JD += (hr-12) * 3600 + min * 60 + sec;
-
- /* Convert JD to unix timestamp (the JD epoch is 2440587.5) */
- return (u32)(JD - (i64)(24405875) * 24*60*6);
+ int Y,M,D,X1,X2,A,B,sec,min,hr;
+ i64 JDsec;
+ Y = (1980 + ((pCDS->mDate >> 9) & 0x7F));
+ M = ((pCDS->mDate >> 5) & 0x0F);
+ D = (pCDS->mDate & 0x1F);
+ sec = (pCDS->mTime & 0x1F)*2;
+ min = (pCDS->mTime >> 5) & 0x3F;
+ hr = (pCDS->mTime >> 11) & 0x1F;
+ if( M<=2 ){
+ Y--;
+ M += 12;
+ }
+ X1 = 36525*(Y+4716)/100;
+ X2 = 306001*(M+1)/10000;
+ A = Y/100;
+ B = 2 - A + (A/4);
+ JDsec = (i64)((X1 + X2 + D + B - 1524.5)*86400) + hr*3600 + min*60 + sec;
+ return (u32)(JDsec - (i64)24405875*(i64)8640);
}
/*
@@ -6741,7 +7582,7 @@ static int zipfileGetEntry(
aRead = (u8*)&aBlob[pNew->cds.iOffset];
}
- rc = zipfileReadLFH(aRead, &lfh);
+ if( rc==SQLITE_OK ) rc = zipfileReadLFH(aRead, &lfh);
if( rc==SQLITE_OK ){
pNew->iDataOff = pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ;
pNew->iDataOff += lfh.nFile + lfh.nExtra;
@@ -7017,13 +7858,13 @@ static int zipfileReadEOCD(
int nRead; /* Bytes to read from file */
int rc = SQLITE_OK;
+ memset(pEOCD, 0, sizeof(ZipfileEOCD));
if( aBlob==0 ){
i64 iOff; /* Offset to read from */
i64 szFile; /* Total size of file in bytes */
fseek(pFile, 0, SEEK_END);
szFile = (i64)ftell(pFile);
if( szFile==0 ){
- memset(pEOCD, 0, sizeof(ZipfileEOCD));
return SQLITE_OK;
}
nRead = (int)(MIN(szFile, ZIPFILE_BUFFER_SIZE));
@@ -7136,9 +7977,14 @@ static int zipfileFilter(
zipfileCursorErr(pCsr, "zipfile() function requires an argument");
return SQLITE_ERROR;
}else if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){
+ static const u8 aEmptyBlob = 0;
const u8 *aBlob = (const u8*)sqlite3_value_blob(argv[0]);
int nBlob = sqlite3_value_bytes(argv[0]);
assert( pTab->pFirstEntry==0 );
+ if( aBlob==0 ){
+ aBlob = &aEmptyBlob;
+ nBlob = 0;
+ }
rc = zipfileLoadDirectory(pTab, aBlob, nBlob);
pCsr->pFreeEntry = pTab->pFirstEntry;
pTab->pFirstEntry = pTab->pLastEntry = 0;
@@ -7364,6 +8210,7 @@ static int zipfileBegin(sqlite3_vtab *pVtab){
static u32 zipfileTime(void){
sqlite3_vfs *pVfs = sqlite3_vfs_find(0);
u32 ret;
+ if( pVfs==0 ) return 0;
if( pVfs->iVersion>=2 && pVfs->xCurrentTimeInt64 ){
i64 ms;
pVfs->xCurrentTimeInt64(pVfs, &ms);
@@ -7811,7 +8658,7 @@ static int zipfileBufferGrow(ZipfileBuffer *pBuf, int nByte){
** SELECT zipfile(name,mode,mtime,data) ...
** SELECT zipfile(name,mode,mtime,data,method) ...
*/
-void zipfileStep(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal){
+static void zipfileStep(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal){
ZipfileCtx *p; /* Aggregate function context */
ZipfileEntry e; /* New entry to add to zip archive */
@@ -7986,7 +8833,7 @@ void zipfileStep(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal){
/*
** xFinalize() callback for zipfile aggregate function.
*/
-void zipfileFinal(sqlite3_context *pCtx){
+static void zipfileFinal(sqlite3_context *pCtx){
ZipfileCtx *p;
ZipfileEOCD eocd;
sqlite3_int64 nZip;
@@ -8043,6 +8890,10 @@ static int zipfileRegister(sqlite3 *db){
zipfileRollback, /* xRollback */
zipfileFindFunction, /* xFindMethod */
0, /* xRename */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0, /* xRollback */
+ 0 /* xShadowName */
};
int rc = sqlite3_create_module(db, "zipfile" , &zipfileModule, 0);
@@ -8052,6 +8903,10 @@ static int zipfileRegister(sqlite3 *db){
zipfileStep, zipfileFinal
);
}
+ assert( sizeof(i64)==8 );
+ assert( sizeof(u32)==4 );
+ assert( sizeof(u16)==2 );
+ assert( sizeof(u8)==1 );
return rc;
}
#else /* SQLITE_OMIT_VIRTUALTABLE */
@@ -8389,6 +9244,23 @@ void sqlite3_expert_destroy(sqlite3expert*);
#include <string.h>
#include <stdio.h>
+#if !defined(SQLITE_AMALGAMATION)
+#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
+# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1
+#endif
+#if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS)
+# define ALWAYS(X) (1)
+# define NEVER(X) (0)
+#elif !defined(NDEBUG)
+# define ALWAYS(X) ((X)?1:(assert(0),0))
+# define NEVER(X) ((X)?(assert(0),1):0)
+#else
+# define ALWAYS(X) (X)
+# define NEVER(X) (X)
+#endif
+#endif /* !defined(SQLITE_AMALGAMATION) */
+
+
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* typedef sqlite3_int64 i64; */
@@ -9054,21 +9926,31 @@ static int idxGetTableInfo(
){
sqlite3_stmt *p1 = 0;
int nCol = 0;
- int nTab = STRLEN(zTab);
- int nByte = sizeof(IdxTable) + nTab + 1;
+ int nTab;
+ int nByte;
IdxTable *pNew = 0;
int rc, rc2;
char *pCsr = 0;
int nPk = 0;
+ *ppOut = 0;
+ if( zTab==0 ) return SQLITE_ERROR;
+ nTab = STRLEN(zTab);
+ nByte = sizeof(IdxTable) + nTab + 1;
rc = idxPrintfPrepareStmt(db, &p1, pzErrmsg, "PRAGMA table_xinfo=%Q", zTab);
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(p1) ){
const char *zCol = (const char*)sqlite3_column_text(p1, 1);
+ const char *zColSeq = 0;
+ if( zCol==0 ){
+ rc = SQLITE_ERROR;
+ break;
+ }
nByte += 1 + STRLEN(zCol);
rc = sqlite3_table_column_metadata(
- db, "main", zTab, zCol, 0, &zCol, 0, 0, 0
+ db, "main", zTab, zCol, 0, &zColSeq, 0, 0, 0
);
- nByte += 1 + STRLEN(zCol);
+ if( zColSeq==0 ) zColSeq = "binary";
+ nByte += 1 + STRLEN(zColSeq);
nCol++;
nPk += (sqlite3_column_int(p1, 5)>0);
}
@@ -9088,19 +9970,23 @@ static int idxGetTableInfo(
nCol = 0;
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(p1) ){
const char *zCol = (const char*)sqlite3_column_text(p1, 1);
- int nCopy = STRLEN(zCol) + 1;
+ const char *zColSeq = 0;
+ int nCopy;
+ if( zCol==0 ) continue;
+ nCopy = STRLEN(zCol) + 1;
pNew->aCol[nCol].zName = pCsr;
pNew->aCol[nCol].iPk = (sqlite3_column_int(p1, 5)==1 && nPk==1);
memcpy(pCsr, zCol, nCopy);
pCsr += nCopy;
rc = sqlite3_table_column_metadata(
- db, "main", zTab, zCol, 0, &zCol, 0, 0, 0
+ db, "main", zTab, zCol, 0, &zColSeq, 0, 0, 0
);
if( rc==SQLITE_OK ){
- nCopy = STRLEN(zCol) + 1;
+ if( zColSeq==0 ) zColSeq = "binary";
+ nCopy = STRLEN(zColSeq) + 1;
pNew->aCol[nCol].zColl = pCsr;
- memcpy(pCsr, zCol, nCopy);
+ memcpy(pCsr, zColSeq, nCopy);
pCsr += nCopy;
}
@@ -9111,9 +9997,9 @@ static int idxGetTableInfo(
if( rc!=SQLITE_OK ){
sqlite3_free(pNew);
pNew = 0;
- }else{
+ }else if( ALWAYS(pNew!=0) ){
pNew->zName = pCsr;
- memcpy(pNew->zName, zTab, nTab+1);
+ if( ALWAYS(pNew->zName!=0) ) memcpy(pNew->zName, zTab, nTab+1);
}
*ppOut = pNew;
@@ -9239,6 +10125,7 @@ static int idxFindCompatible(
IdxConstraint *pT = pTail;
sqlite3_stmt *pInfo = 0;
const char *zIdx = (const char*)sqlite3_column_text(pIdxList, 1);
+ if( zIdx==0 ) continue;
/* Zero the IdxConstraint.bFlag values in the pEq list */
for(pIter=pEq; pIter; pIter=pIter->pLink) pIter->bFlag = 0;
@@ -9284,6 +10171,19 @@ static int idxFindCompatible(
return 0;
}
+/* Callback for sqlite3_exec() with query with leading count(*) column.
+ * The first argument is expected to be an int*, referent to be incremented
+ * if that leading column is not exactly '0'.
+ */
+static int countNonzeros(void* pCount, int nc,
+ char* azResults[], char* azColumns[]){
+ (void)azColumns; /* Suppress unused parameter warning */
+ if( nc>0 && (azResults[0][0]!='0' || azResults[0][1]!=0) ){
+ *((int *)pCount) += 1;
+ }
+ return 0;
+}
+
static int idxCreateFromCons(
sqlite3expert *p,
IdxScan *pScan,
@@ -9310,17 +10210,40 @@ static int idxCreateFromCons(
if( rc==SQLITE_OK ){
/* Hash the list of columns to come up with a name for the index */
const char *zTable = pScan->pTab->zName;
- char *zName; /* Index name */
- int i;
- for(i=0; zCols[i]; i++){
- h += ((h<<3) + zCols[i]);
- }
- zName = sqlite3_mprintf("%s_idx_%08x", zTable, h);
- if( zName==0 ){
+ int quoteTable = idxIdentifierRequiresQuotes(zTable);
+ char *zName = 0; /* Index name */
+ int collisions = 0;
+ do{
+ int i;
+ char *zFind;
+ for(i=0; zCols[i]; i++){
+ h += ((h<<3) + zCols[i]);
+ }
+ sqlite3_free(zName);
+ zName = sqlite3_mprintf("%s_idx_%08x", zTable, h);
+ if( zName==0 ) break;
+ /* Is is unique among table, view and index names? */
+ zFmt = "SELECT count(*) FROM sqlite_schema WHERE name=%Q"
+ " AND type in ('index','table','view')";
+ zFind = sqlite3_mprintf(zFmt, zName);
+ i = 0;
+ rc = sqlite3_exec(dbm, zFind, countNonzeros, &i, 0);
+ assert(rc==SQLITE_OK);
+ sqlite3_free(zFind);
+ if( i==0 ){
+ collisions = 0;
+ break;
+ }
+ ++collisions;
+ }while( collisions<50 && zName!=0 );
+ if( collisions ){
+ /* This return means "Gave up trying to find a unique index name." */
+ rc = SQLITE_BUSY_TIMEOUT;
+ }else if( zName==0 ){
rc = SQLITE_NOMEM;
}else{
- if( idxIdentifierRequiresQuotes(zTable) ){
- zFmt = "CREATE INDEX '%q' ON %Q(%s)";
+ if( quoteTable ){
+ zFmt = "CREATE INDEX \"%w\" ON \"%w\"(%s)";
}else{
zFmt = "CREATE INDEX %s ON %s(%s)";
}
@@ -9329,7 +10252,11 @@ static int idxCreateFromCons(
rc = SQLITE_NOMEM;
}else{
rc = sqlite3_exec(dbm, zIdx, 0, 0, p->pzErrmsg);
- idxHashAdd(&rc, &p->hIdx, zName, zIdx);
+ if( rc!=SQLITE_OK ){
+ rc = SQLITE_BUSY_TIMEOUT;
+ }else{
+ idxHashAdd(&rc, &p->hIdx, zName, zIdx);
+ }
}
sqlite3_free(zName);
sqlite3_free(zIdx);
@@ -9481,7 +10408,7 @@ static void idxWriteFree(IdxWrite *pTab){
** runs all the queries to see which indexes they prefer, and populates
** IdxStatement.zIdx and IdxStatement.zEQP with the results.
*/
-int idxFindIndexes(
+static int idxFindIndexes(
sqlite3expert *p,
char **pzErr /* OUT: Error message (sqlite3_malloc) */
){
@@ -9610,6 +10537,7 @@ static int idxProcessOneTrigger(
rc = idxPrintfPrepareStmt(p->db, &pSelect, pzErr, zSql, zTab, zTab);
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSelect) ){
const char *zCreate = (const char*)sqlite3_column_text(pSelect, 0);
+ if( zCreate==0 ) continue;
rc = sqlite3_exec(p->dbv, zCreate, 0, 0, pzErr);
}
idxFinalize(&rc, pSelect);
@@ -9712,8 +10640,9 @@ static int idxCreateVtabSchema(sqlite3expert *p, char **pzErrmsg){
const char *zName = (const char*)sqlite3_column_text(pSchema, 1);
const char *zSql = (const char*)sqlite3_column_text(pSchema, 2);
+ if( zType==0 || zName==0 ) continue;
if( zType[0]=='v' || zType[1]=='r' ){
- rc = sqlite3_exec(p->dbv, zSql, 0, 0, pzErrmsg);
+ if( zSql ) rc = sqlite3_exec(p->dbv, zSql, 0, 0, pzErrmsg);
}else{
IdxTable *pTab;
rc = idxGetTableInfo(p->db, zName, &pTab, pzErrmsg);
@@ -9850,6 +10779,7 @@ static void idxRemFunc(
case SQLITE_BLOB:
case SQLITE_TEXT: {
int nByte = sqlite3_value_bytes(argv[1]);
+ const void *pData = 0;
if( nByte>pSlot->nByte ){
char *zNew = (char*)sqlite3_realloc(pSlot->z, nByte*2);
if( zNew==0 ){
@@ -9861,9 +10791,11 @@ static void idxRemFunc(
}
pSlot->n = nByte;
if( pSlot->eType==SQLITE_BLOB ){
- memcpy(pSlot->z, sqlite3_value_blob(argv[1]), nByte);
+ pData = sqlite3_value_blob(argv[1]);
+ if( pData ) memcpy(pSlot->z, pData, nByte);
}else{
- memcpy(pSlot->z, sqlite3_value_text(argv[1]), nByte);
+ pData = sqlite3_value_text(argv[1]);
+ memcpy(pSlot->z, pData, nByte);
}
break;
}
@@ -10074,6 +11006,7 @@ static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
i64 iRowid = sqlite3_column_int64(pAllIndex, 0);
const char *zTab = (const char*)sqlite3_column_text(pAllIndex, 1);
const char *zIdx = (const char*)sqlite3_column_text(pAllIndex, 2);
+ if( zTab==0 || zIdx==0 ) continue;
if( p->iSample<100 && iPrev!=iRowid ){
samplectx.target = (double)p->iSample / 100.0;
samplectx.iTarget = p->iSample;
@@ -10140,14 +11073,14 @@ sqlite3expert *sqlite3_expert_new(sqlite3 *db, char **pzErrmsg){
/* Copy the entire schema of database [db] into [dbm]. */
if( rc==SQLITE_OK ){
- sqlite3_stmt *pSql;
+ sqlite3_stmt *pSql = 0;
rc = idxPrintfPrepareStmt(pNew->db, &pSql, pzErrmsg,
"SELECT sql FROM sqlite_schema WHERE name NOT LIKE 'sqlite_%%'"
" AND sql NOT LIKE 'CREATE VIRTUAL %%'"
);
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
const char *zSql = (const char*)sqlite3_column_text(pSql, 0);
- rc = sqlite3_exec(pNew->dbm, zSql, 0, 0, pzErrmsg);
+ if( zSql ) rc = sqlite3_exec(pNew->dbm, zSql, 0, 0, pzErrmsg);
}
idxFinalize(&rc, pSql);
}
@@ -10253,6 +11186,10 @@ int sqlite3_expert_analyze(sqlite3expert *p, char **pzErr){
/* Create candidate indexes within the in-memory database file */
if( rc==SQLITE_OK ){
rc = idxCreateCandidates(p);
+ }else if ( rc==SQLITE_BUSY_TIMEOUT ){
+ if( pzErr )
+ *pzErr = sqlite3_mprintf("Cannot find a unique index name to propose.");
+ return rc;
}
/* Generate the stat1 data */
@@ -11230,6 +12167,15 @@ struct EQPGraph {
char zPrefix[100]; /* Graph prefix */
};
+/* Parameters affecting columnar mode result display (defaulting together) */
+typedef struct ColModeOpts {
+ int iWrap; /* In columnar modes, wrap lines reaching this limit */
+ u8 bQuote; /* Quote results for .mode box and table */
+ u8 bWordWrap; /* In columnar modes, wrap at word boundaries */
+} ColModeOpts;
+#define ColModeOpts_default { 60, 0, 0 }
+#define ColModeOpts_default_qbox { 60, 1, 0 }
+
/*
** State information about the database connection is contained in an
** instance of the following structure.
@@ -11246,8 +12192,12 @@ struct ShellState {
u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */
u8 nEqpLevel; /* Depth of the EQP output graph */
u8 eTraceType; /* SHELL_TRACE_* value for type of trace */
+ u8 bSafeMode; /* True to prohibit unsafe operations */
+ u8 bSafeModePersist; /* The long-term value of bSafeMode */
+ ColModeOpts cmOpts; /* Option values affecting columnar mode output */
unsigned statsOn; /* True to display memory stats before each finalize */
unsigned mEqpLines; /* Mask of veritical lines in the EQP output graph */
+ int inputNesting; /* Track nesting level of .read and other redirects */
int outCount; /* Revert to stdout when reaching zero */
int cnt; /* Number of records displayed so far */
int lineno; /* Line number of last line read from in */
@@ -11282,22 +12232,36 @@ struct ShellState {
char nullValue[20]; /* The text to print when a NULL comes back from
** the database */
char outfile[FILENAME_MAX]; /* Filename for *out */
- const char *zDbFilename; /* name of the database file */
- char *zFreeOnClose; /* Filename to free when closing */
- const char *zVfs; /* Name of VFS to use */
sqlite3_stmt *pStmt; /* Current statement if any. */
FILE *pLog; /* Write log output here */
+ struct AuxDb { /* Storage space for auxiliary database connections */
+ sqlite3 *db; /* Connection pointer */
+ const char *zDbFilename; /* Filename used to open the connection */
+ char *zFreeOnClose; /* Free this memory allocation on close */
+#if defined(SQLITE_ENABLE_SESSION)
+ int nSession; /* Number of active sessions */
+ OpenSession aSession[4]; /* Array of sessions. [0] is in focus. */
+#endif
+ } aAuxDb[5], /* Array of all database connections */
+ *pAuxDb; /* Currently active database connection */
int *aiIndent; /* Array of indents used in MODE_Explain */
int nIndent; /* Size of array aiIndent[] */
int iIndent; /* Index of current op in aiIndent[] */
+ char *zNonce; /* Nonce for temporary safe-mode excapes */
EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */
-#if defined(SQLITE_ENABLE_SESSION)
- int nSession; /* Number of active sessions */
- OpenSession aSession[4]; /* Array of sessions. [0] is in focus. */
+ ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */
+#ifdef SQLITE_SHELL_WASM_MODE
+ struct {
+ const char * zInput; /* Input string from wasm/JS proxy */
+ const char * zPos; /* Cursor pos into zInput */
+ } wasm;
#endif
- ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */
};
+#ifdef SQLITE_SHELL_WASM_MODE
+static ShellState shellState;
+#endif
+
/* Allowed values for ShellState.autoEQP
*/
@@ -11338,8 +12302,8 @@ struct ShellState {
#define SHFLG_PreserveRowid 0x00000008 /* .dump preserves rowid values */
#define SHFLG_Newlines 0x00000010 /* .dump --newline flag */
#define SHFLG_CountChanges 0x00000020 /* .changes setting */
-#define SHFLG_Echo 0x00000040 /* .echo or --echo setting */
-#define SHFLG_HeaderSet 0x00000080 /* .header has been used */
+#define SHFLG_Echo 0x00000040 /* .echo on/off, or --echo setting */
+#define SHFLG_HeaderSet 0x00000080 /* showHeader has been specified */
#define SHFLG_DumpDataOnly 0x00000100 /* .dump show data only */
#define SHFLG_DumpNoSys 0x00000200 /* .dump omits system tables */
@@ -11370,6 +12334,8 @@ struct ShellState {
#define MODE_Markdown 14 /* Markdown formatting */
#define MODE_Table 15 /* MySQL-style table formatting */
#define MODE_Box 16 /* Unicode box-drawing characters */
+#define MODE_Count 17 /* Output only a count of the rows of output */
+#define MODE_Off 18 /* No query output shown */
static const char *modeDescr[] = {
"line",
@@ -11388,7 +12354,9 @@ static const char *modeDescr[] = {
"json",
"markdown",
"table",
- "box"
+ "box",
+ "count",
+ "off"
};
/*
@@ -11405,6 +12373,12 @@ static const char *modeDescr[] = {
#define SEP_Record "\x1E"
/*
+** Limit input nesting via .read or any other input redirect.
+** It's not too expensive, so a generous allowance can be made.
+*/
+#define MAX_INPUT_NESTING 25
+
+/*
** A callback for the sqlite3_log() interface.
*/
static void shellLog(void *pArg, int iErrCode, const char *zMsg){
@@ -11432,6 +12406,27 @@ static void shellPutsFunc(
}
/*
+** If in safe mode, print an error message described by the arguments
+** and exit immediately.
+*/
+static void failIfSafeMode(
+ ShellState *p,
+ const char *zErrMsg,
+ ...
+){
+ if( p->bSafeMode ){
+ va_list ap;
+ char *zMsg;
+ va_start(ap, zErrMsg);
+ zMsg = sqlite3_vmprintf(zErrMsg, ap);
+ va_end(ap);
+ raw_printf(stderr, "line %d: ", p->lineno);
+ utf8_printf(stderr, "%s\n", zMsg);
+ exit(1);
+ }
+}
+
+/*
** SQL function: edit(VALUE)
** edit(VALUE,EDITOR)
**
@@ -11551,7 +12546,6 @@ static void editFunc(
sqlite3_int64 i, j;
if( hasCRNL ){
/* If the original contains \r\n then do no conversions back to \n */
- j = sz;
}else{
/* If the file did not originally contain \r\n then convert any new
** \r\n back into \n */
@@ -11859,18 +12853,16 @@ static void output_csv(ShellState *p, const char *z, int bSep){
if( z==0 ){
utf8_printf(out,"%s",p->nullValue);
}else{
- int i;
- int nSep = strlen30(p->colSeparator);
+ unsigned i;
for(i=0; z[i]; i++){
- if( needCsvQuote[((unsigned char*)z)[i]]
- || (z[i]==p->colSeparator[0] &&
- (nSep==1 || memcmp(z, p->colSeparator, nSep)==0)) ){
+ if( needCsvQuote[((unsigned char*)z)[i]] ){
i = 0;
break;
}
}
- if( i==0 ){
+ if( i==0 || strstr(z, p->colSeparator)!=0 ){
char *zQuoted = sqlite3_mprintf("\"%w\"", z);
+ shell_check_oom(zQuoted);
utf8_printf(out, "%s", zQuoted);
sqlite3_free(zQuoted);
}else{
@@ -11909,6 +12901,53 @@ static BOOL WINAPI ConsoleCtrlHandler(
#ifndef SQLITE_OMIT_AUTHORIZATION
/*
+** This authorizer runs in safe mode.
+*/
+static int safeModeAuth(
+ void *pClientData,
+ int op,
+ const char *zA1,
+ const char *zA2,
+ const char *zA3,
+ const char *zA4
+){
+ ShellState *p = (ShellState*)pClientData;
+ static const char *azProhibitedFunctions[] = {
+ "edit",
+ "fts3_tokenizer",
+ "load_extension",
+ "readfile",
+ "writefile",
+ "zipfile",
+ "zipfile_cds",
+ };
+ UNUSED_PARAMETER(zA2);
+ UNUSED_PARAMETER(zA3);
+ UNUSED_PARAMETER(zA4);
+ switch( op ){
+ case SQLITE_ATTACH: {
+#ifndef SQLITE_SHELL_WASM_MODE
+ /* In WASM builds the filesystem is a virtual sandbox, so
+ ** there's no harm in using ATTACH. */
+ failIfSafeMode(p, "cannot run ATTACH in safe mode");
+#endif
+ break;
+ }
+ case SQLITE_FUNCTION: {
+ int i;
+ for(i=0; i<ArraySize(azProhibitedFunctions); i++){
+ if( sqlite3_stricmp(zA1, azProhibitedFunctions[i])==0 ){
+ failIfSafeMode(p, "cannot use the %s() function in safe mode",
+ azProhibitedFunctions[i]);
+ }
+ }
+ break;
+ }
+ }
+ return SQLITE_OK;
+}
+
+/*
** When the ".auth ON" is set, the following authorizer callback is
** invoked. It always returns SQLITE_OK.
*/
@@ -11950,6 +12989,7 @@ static int shellAuth(
}
}
raw_printf(p->out, "\n");
+ if( p->bSafeMode ) (void)safeModeAuth(pClientData, op, zA1, zA2, zA3, zA4);
return SQLITE_OK;
}
#endif
@@ -12001,7 +13041,7 @@ static void eqp_append(ShellState *p, int iEqpId, int p2, const char *zText){
utf8_printf(p->out, "%d,%d,%s\n", iEqpId, p2, zText);
}
pNew = sqlite3_malloc64( sizeof(*pNew) + nText );
- if( pNew==0 ) shell_out_of_memory();
+ shell_check_oom(pNew);
pNew->iEqpId = iEqpId;
pNew->iParentId = p2;
memcpy(pNew->zText, zText, nText+1);
@@ -12149,6 +13189,10 @@ static int shell_callback(
if( azArg==0 ) return 0;
switch( p->cMode ){
+ case MODE_Count:
+ case MODE_Off: {
+ break;
+ }
case MODE_Line: {
int w = 5;
if( azArg==0 ) break;
@@ -12218,6 +13262,7 @@ static int shell_callback(
break;
}
z = sqlite3_mprintf("%s", azArg[0]);
+ shell_check_oom(z);
j = 0;
for(i=0; IsSpace(z[i]); i++){}
for(; (c = z[i])!=0; i++){
@@ -12349,6 +13394,7 @@ static int shell_callback(
if( i>0 ) raw_printf(p->out, ",");
if( quoteChar(azCol[i]) ){
char *z = sqlite3_mprintf("\"%w\"", azCol[i]);
+ shell_check_oom(z);
utf8_printf(p->out, "%s", z);
sqlite3_free(z);
}else{
@@ -12380,7 +13426,12 @@ static int shell_callback(
}else if( ur==0xfff0000000000000LL ){
raw_printf(p->out, "-1e999");
}else{
- sqlite3_snprintf(50,z,"%!.20g", r);
+ sqlite3_int64 ir = (sqlite3_int64)r;
+ if( r==(double)ir ){
+ sqlite3_snprintf(50,z,"%lld.0", ir);
+ }else{
+ sqlite3_snprintf(50,z,"%!.20g", r);
+ }
raw_printf(p->out, "%s", z);
}
}else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){
@@ -12594,7 +13645,7 @@ static void set_table_name(ShellState *p, const char *zName){
n = strlen30(zName);
if( cQuote ) n += n+2;
z = p->zDestTable = malloc( n+1 );
- if( z==0 ) shell_out_of_memory();
+ shell_check_oom(z);
n = 0;
if( cQuote ) z[n++] = cQuote;
for(i=0; zName[i]; i++){
@@ -12605,6 +13656,47 @@ static void set_table_name(ShellState *p, const char *zName){
z[n] = 0;
}
+/*
+** Maybe construct two lines of text that point out the position of a
+** syntax error. Return a pointer to the text, in memory obtained from
+** sqlite3_malloc(). Or, if the most recent error does not involve a
+** specific token that we can point to, return an empty string.
+**
+** In all cases, the memory returned is obtained from sqlite3_malloc64()
+** and should be released by the caller invoking sqlite3_free().
+*/
+static char *shell_error_context(const char *zSql, sqlite3 *db){
+ int iOffset;
+ size_t len;
+ char *zCode;
+ char *zMsg;
+ int i;
+ if( db==0
+ || zSql==0
+ || (iOffset = sqlite3_error_offset(db))<0
+ ){
+ return sqlite3_mprintf("");
+ }
+ while( iOffset>50 ){
+ iOffset--;
+ zSql++;
+ while( (zSql[0]&0xc0)==0x80 ){ zSql++; iOffset--; }
+ }
+ len = strlen(zSql);
+ if( len>78 ){
+ len = 78;
+ while( (zSql[len]&0xc0)==0x80 ) len--;
+ }
+ zCode = sqlite3_mprintf("%.*s", len, zSql);
+ for(i=0; zCode[i]; i++){ if( IsSpace(zSql[i]) ) zCode[i] = ' '; }
+ if( iOffset<25 ){
+ zMsg = sqlite3_mprintf("\n %z\n %*s^--- error here", zCode, iOffset, "");
+ }else{
+ zMsg = sqlite3_mprintf("\n %z\n %*serror here ---^", zCode, iOffset-14, "");
+ }
+ return zMsg;
+}
+
/*
** Execute a query statement that will generate SQL output. Print
@@ -12627,8 +13719,10 @@ static int run_table_dump_query(
const char *z;
rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0);
if( rc!=SQLITE_OK || !pSelect ){
- utf8_printf(p->out, "/**** ERROR: (%d) %s *****/\n", rc,
- sqlite3_errmsg(p->db));
+ char *zContext = shell_error_context(zSelect, p->db);
+ utf8_printf(p->out, "/**** ERROR: (%d) %s *****/\n%s", rc,
+ sqlite3_errmsg(p->db), zContext);
+ sqlite3_free(zContext);
if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++;
return rc;
}
@@ -12659,17 +13753,29 @@ static int run_table_dump_query(
}
/*
-** Allocate space and save off current error string.
+** Allocate space and save off string indicating current error.
*/
static char *save_err_msg(
- sqlite3 *db /* Database to query */
+ sqlite3 *db, /* Database to query */
+ const char *zPhase, /* When the error occcurs */
+ int rc, /* Error code returned from API */
+ const char *zSql /* SQL string, or NULL */
){
- int nErrMsg = 1+strlen30(sqlite3_errmsg(db));
- char *zErrMsg = sqlite3_malloc64(nErrMsg);
- if( zErrMsg ){
- memcpy(zErrMsg, sqlite3_errmsg(db), nErrMsg);
+ char *zErr;
+ char *zContext;
+ sqlite3_str *pStr = sqlite3_str_new(0);
+ sqlite3_str_appendf(pStr, "%s, %s", zPhase, sqlite3_errmsg(db));
+ if( rc>1 ){
+ sqlite3_str_appendf(pStr, " (%d)", rc);
+ }
+ zContext = shell_error_context(zSql, db);
+ if( zContext ){
+ sqlite3_str_appendall(pStr, zContext);
+ sqlite3_free(zContext);
}
- return zErrMsg;
+ zErr = sqlite3_str_finish(pStr);
+ shell_check_oom(zErr);
+ return zErr;
}
#ifdef __linux__
@@ -12847,6 +13953,7 @@ static int display_stats(
}
if( pArg->pStmt ){
+ int iHit, iMiss;
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP,
bReset);
raw_printf(pArg->out, "Fullscan Steps: %d\n", iCur);
@@ -12854,6 +13961,12 @@ static int display_stats(
raw_printf(pArg->out, "Sort Operations: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset);
raw_printf(pArg->out, "Autoindex Inserts: %d\n", iCur);
+ iHit = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_HIT, bReset);
+ iMiss = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_MISS, bReset);
+ if( iHit || iMiss ){
+ raw_printf(pArg->out, "Bloom filter bypass taken: %d/%d\n",
+ iHit, iHit+iMiss);
+ }
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset);
raw_printf(pArg->out, "Virtual Machine Steps: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_REPREPARE,bReset);
@@ -12946,6 +14059,9 @@ static int str_in_array(const char *zStr, const char **azArray){
** all opcodes that occur between the p2 jump destination and the opcode
** itself by 2 spaces.
**
+** * Do the previous for "Return" instructions for when P2 is positive.
+** See tag-20220407a in wherecode.c and vdbe.c.
+**
** * For each "Goto", if the jump destination is earlier in the program
** and ends on one of:
** Yield SeekGt SeekLt RowSetRead Rewind
@@ -12960,7 +14076,8 @@ static void explain_data_prepare(ShellState *p, sqlite3_stmt *pSql){
int nAlloc = 0; /* Allocated size of p->aiIndent[], abYield */
int iOp; /* Index of operation in p->aiIndent[] */
- const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext", 0 };
+ const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext",
+ "Return", 0 };
const char *azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead",
"Rewind", 0 };
const char *azGoto[] = { "Goto", 0 };
@@ -13010,15 +14127,15 @@ static void explain_data_prepare(ShellState *p, sqlite3_stmt *pSql){
}
nAlloc += 100;
p->aiIndent = (int*)sqlite3_realloc64(p->aiIndent, nAlloc*sizeof(int));
- if( p->aiIndent==0 ) shell_out_of_memory();
+ shell_check_oom(p->aiIndent);
abYield = (int*)sqlite3_realloc64(abYield, nAlloc*sizeof(int));
- if( abYield==0 ) shell_out_of_memory();
+ shell_check_oom(abYield);
}
abYield[iOp] = str_in_array(zOp, azYield);
p->aiIndent[iOp] = 0;
p->nIndent = iOp+1;
- if( str_in_array(zOp, azNext) ){
+ if( str_in_array(zOp, azNext) && p2op>0 ){
for(i=p2op; i<iOp; i++) p->aiIndent[i] += 2;
}
if( str_in_array(zOp, azGoto) && p2op<p->nIndent
@@ -13044,7 +14161,7 @@ static void explain_data_delete(ShellState *p){
}
/*
-** Disable and restore .wheretrace and .selecttrace settings.
+** Disable and restore .wheretrace and .treetrace/.selecttrace settings.
*/
static unsigned int savedSelectTrace;
static unsigned int savedWhereTrace;
@@ -13071,7 +14188,7 @@ static void bind_table_init(ShellState *p){
sqlite3_exec(p->db,
"CREATE TABLE IF NOT EXISTS temp.sqlite_parameters(\n"
" key TEXT PRIMARY KEY,\n"
- " value ANY\n"
+ " value\n"
") WITHOUT ROWID;",
0, 0, 0);
sqlite3_db_config(p->db, SQLITE_DBCONFIG_WRITABLE_SCHEMA, wrSchema, 0);
@@ -13188,7 +14305,134 @@ static void print_box_row_separator(
fputs("\n", p->out);
}
+/*
+** z[] is a line of text that is to be displayed the .mode box or table or
+** similar tabular formats. z[] might contain control characters such
+** as \n, \t, \f, or \r.
+**
+** Compute characters to display on the first line of z[]. Stop at the
+** first \r, \n, or \f. Expand \t into spaces. Return a copy (obtained
+** from malloc()) of that first line, which caller should free sometime.
+** Write anything to display on the next line into *pzTail. If this is
+** the last line, write a NULL into *pzTail. (*pzTail is not allocated.)
+*/
+static char *translateForDisplayAndDup(
+ const unsigned char *z, /* Input text to be transformed */
+ const unsigned char **pzTail, /* OUT: Tail of the input for next line */
+ int mxWidth, /* Max width. 0 means no limit */
+ u8 bWordWrap /* If true, avoid breaking mid-word */
+){
+ int i; /* Input bytes consumed */
+ int j; /* Output bytes generated */
+ int k; /* Input bytes to be displayed */
+ int n; /* Output column number */
+ unsigned char *zOut; /* Output text */
+ if( z==0 ){
+ *pzTail = 0;
+ return 0;
+ }
+ if( mxWidth<0 ) mxWidth = -mxWidth;
+ if( mxWidth==0 ) mxWidth = 1000000;
+ i = j = n = 0;
+ while( n<mxWidth ){
+ if( z[i]>=' ' ){
+ n++;
+ do{ i++; j++; }while( (z[i]&0xc0)==0x80 );
+ continue;
+ }
+ if( z[i]=='\t' ){
+ do{
+ n++;
+ j++;
+ }while( (n&7)!=0 && n<mxWidth );
+ i++;
+ continue;
+ }
+ break;
+ }
+ if( n>=mxWidth && bWordWrap ){
+ /* Perhaps try to back up to a better place to break the line */
+ for(k=i; k>i/2; k--){
+ if( isspace(z[k-1]) ) break;
+ }
+ if( k<=i/2 ){
+ for(k=i; k>i/2; k--){
+ if( isalnum(z[k-1])!=isalnum(z[k]) && (z[k]&0xc0)!=0x80 ) break;
+ }
+ }
+ if( k<=i/2 ){
+ k = i;
+ }else{
+ i = k;
+ while( z[i]==' ' ) i++;
+ }
+ }else{
+ k = i;
+ }
+ if( n>=mxWidth && z[i]>=' ' ){
+ *pzTail = &z[i];
+ }else if( z[i]=='\r' && z[i+1]=='\n' ){
+ *pzTail = z[i+2] ? &z[i+2] : 0;
+ }else if( z[i]==0 || z[i+1]==0 ){
+ *pzTail = 0;
+ }else{
+ *pzTail = &z[i+1];
+ }
+ zOut = malloc( j+1 );
+ shell_check_oom(zOut);
+ i = j = n = 0;
+ while( i<k ){
+ if( z[i]>=' ' ){
+ n++;
+ do{ zOut[j++] = z[i++]; }while( (z[i]&0xc0)==0x80 );
+ continue;
+ }
+ if( z[i]=='\t' ){
+ do{
+ n++;
+ zOut[j++] = ' ';
+ }while( (n&7)!=0 && n<mxWidth );
+ i++;
+ continue;
+ }
+ break;
+ }
+ zOut[j] = 0;
+ return (char*)zOut;
+}
+
+/* Extract the value of the i-th current column for pStmt as an SQL literal
+** value. Memory is obtained from sqlite3_malloc64() and must be freed by
+** the caller.
+*/
+static char *quoted_column(sqlite3_stmt *pStmt, int i){
+ switch( sqlite3_column_type(pStmt, i) ){
+ case SQLITE_NULL: {
+ return sqlite3_mprintf("NULL");
+ }
+ case SQLITE_INTEGER:
+ case SQLITE_FLOAT: {
+ return sqlite3_mprintf("%s",sqlite3_column_text(pStmt,i));
+ }
+ case SQLITE_TEXT: {
+ return sqlite3_mprintf("%Q",sqlite3_column_text(pStmt,i));
+ }
+ case SQLITE_BLOB: {
+ int j;
+ sqlite3_str *pStr = sqlite3_str_new(0);
+ const unsigned char *a = sqlite3_column_blob(pStmt,i);
+ int n = sqlite3_column_bytes(pStmt,i);
+ sqlite3_str_append(pStr, "x'", 2);
+ for(j=0; j<n; j++){
+ sqlite3_str_appendf(pStr, "%02x", a[j]);
+ }
+ sqlite3_str_append(pStr, "'", 1);
+ return sqlite3_str_finish(pStr);
+ }
+ }
+ return 0; /* Not reached */
+}
/*
** Run a prepared statement and output the result in one of the
@@ -13208,12 +14452,21 @@ static void exec_prepared_stmt_columnar(
int nColumn = 0;
char **azData = 0;
sqlite3_int64 nAlloc = 0;
+ char *abRowDiv = 0;
+ const unsigned char *uz;
const char *z;
+ char **azQuoted = 0;
int rc;
sqlite3_int64 i, nData;
int j, nTotal, w, n;
const char *colSep = 0;
const char *rowSep = 0;
+ const unsigned char **azNextLine = 0;
+ int bNextLine = 0;
+ int bMultiLineRowExists = 0;
+ int bw = p->cmOpts.bWordWrap;
+ const char *zEmpty = "";
+ const char *zShowNull = p->nullValue;
rc = sqlite3_step(pStmt);
if( rc!=SQLITE_ROW ) return;
@@ -13221,25 +14474,20 @@ static void exec_prepared_stmt_columnar(
nAlloc = nColumn*4;
if( nAlloc<=0 ) nAlloc = 1;
azData = sqlite3_malloc64( nAlloc*sizeof(char*) );
- if( azData==0 ) shell_out_of_memory();
- for(i=0; i<nColumn; i++){
- azData[i] = strdup(sqlite3_column_name(pStmt,i));
- }
- do{
- if( (nRow+2)*nColumn >= nAlloc ){
- nAlloc *= 2;
- azData = sqlite3_realloc64(azData, nAlloc*sizeof(char*));
- if( azData==0 ) shell_out_of_memory();
- }
- nRow++;
- for(i=0; i<nColumn; i++){
- z = (const char*)sqlite3_column_text(pStmt,i);
- azData[nRow*nColumn + i] = z ? strdup(z) : 0;
- }
- }while( (rc = sqlite3_step(pStmt))==SQLITE_ROW );
+ shell_check_oom(azData);
+ azNextLine = sqlite3_malloc64( nColumn*sizeof(char*) );
+ shell_check_oom((void*)azNextLine);
+ memset((void*)azNextLine, 0, nColumn*sizeof(char*) );
+ if( p->cmOpts.bQuote ){
+ azQuoted = sqlite3_malloc64( nColumn*sizeof(char*) );
+ shell_check_oom(azQuoted);
+ memset(azQuoted, 0, nColumn*sizeof(char*) );
+ }
+ abRowDiv = sqlite3_malloc64( nAlloc/nColumn );
+ shell_check_oom(abRowDiv);
if( nColumn>p->nWidth ){
- p->colWidth = realloc(p->colWidth, nColumn*2*sizeof(int));
- if( p->colWidth==0 ) shell_out_of_memory();
+ p->colWidth = realloc(p->colWidth, (nColumn+1)*2*sizeof(int));
+ shell_check_oom(p->colWidth);
for(i=p->nWidth; i<nColumn; i++) p->colWidth[i] = 0;
p->nWidth = nColumn;
p->actualWidth = &p->colWidth[nColumn];
@@ -13250,10 +14498,58 @@ static void exec_prepared_stmt_columnar(
if( w<0 ) w = -w;
p->actualWidth[i] = w;
}
+ for(i=0; i<nColumn; i++){
+ const unsigned char *zNotUsed;
+ int wx = p->colWidth[i];
+ if( wx==0 ){
+ wx = p->cmOpts.iWrap;
+ }
+ if( wx<0 ) wx = -wx;
+ uz = (const unsigned char*)sqlite3_column_name(pStmt,i);
+ azData[i] = translateForDisplayAndDup(uz, &zNotUsed, wx, bw);
+ }
+ do{
+ int useNextLine = bNextLine;
+ bNextLine = 0;
+ if( (nRow+2)*nColumn >= nAlloc ){
+ nAlloc *= 2;
+ azData = sqlite3_realloc64(azData, nAlloc*sizeof(char*));
+ shell_check_oom(azData);
+ abRowDiv = sqlite3_realloc64(abRowDiv, nAlloc/nColumn);
+ shell_check_oom(abRowDiv);
+ }
+ abRowDiv[nRow] = 1;
+ nRow++;
+ for(i=0; i<nColumn; i++){
+ int wx = p->colWidth[i];
+ if( wx==0 ){
+ wx = p->cmOpts.iWrap;
+ }
+ if( wx<0 ) wx = -wx;
+ if( useNextLine ){
+ uz = azNextLine[i];
+ if( uz==0 ) uz = (u8*)zEmpty;
+ }else if( p->cmOpts.bQuote ){
+ sqlite3_free(azQuoted[i]);
+ azQuoted[i] = quoted_column(pStmt,i);
+ uz = (const unsigned char*)azQuoted[i];
+ }else{
+ uz = (const unsigned char*)sqlite3_column_text(pStmt,i);
+ if( uz==0 ) uz = (u8*)zShowNull;
+ }
+ azData[nRow*nColumn + i]
+ = translateForDisplayAndDup(uz, &azNextLine[i], wx, bw);
+ if( azNextLine[i] ){
+ bNextLine = 1;
+ abRowDiv[nRow-1] = 0;
+ bMultiLineRowExists = 1;
+ }
+ }
+ }while( bNextLine || sqlite3_step(pStmt)==SQLITE_ROW );
nTotal = nColumn*(nRow+1);
for(i=0; i<nTotal; i++){
z = azData[i];
- if( z==0 ) z = p->nullValue;
+ if( z==0 ) z = (char*)zEmpty;
n = strlenChar(z);
j = i%nColumn;
if( n>p->actualWidth[j] ) p->actualWidth[j] = n;
@@ -13332,6 +14628,15 @@ static void exec_prepared_stmt_columnar(
utf8_width_print(p->out, w, z);
if( j==nColumn-1 ){
utf8_printf(p->out, "%s", rowSep);
+ if( bMultiLineRowExists && abRowDiv[i/nColumn-1] && i+1<nTotal ){
+ if( p->cMode==MODE_Table ){
+ print_row_separator(p, nColumn, "+");
+ }else if( p->cMode==MODE_Box ){
+ print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134);
+ }else if( p->cMode==MODE_Column ){
+ raw_printf(p->out, "\n");
+ }
+ }
j = -1;
if( seenInterrupt ) goto columnar_end;
}else{
@@ -13348,8 +14653,17 @@ columnar_end:
utf8_printf(p->out, "Interrupt\n");
}
nData = (nRow+1)*nColumn;
- for(i=0; i<nData; i++) free(azData[i]);
+ for(i=0; i<nData; i++){
+ z = azData[i];
+ if( z!=zEmpty && z!=zShowNull ) free(azData[i]);
+ }
sqlite3_free(azData);
+ sqlite3_free((void*)azNextLine);
+ sqlite3_free(abRowDiv);
+ if( azQuoted ){
+ for(i=0; i<nColumn; i++) sqlite3_free(azQuoted[i]);
+ sqlite3_free(azQuoted);
+ }
}
/*
@@ -13360,6 +14674,7 @@ static void exec_prepared_stmt(
sqlite3_stmt *pStmt /* Statment to run */
){
int rc;
+ sqlite3_uint64 nRow = 0;
if( pArg->cMode==MODE_Column
|| pArg->cMode==MODE_Table
@@ -13380,7 +14695,7 @@ static void exec_prepared_stmt(
int nCol = sqlite3_column_count(pStmt);
void *pData = sqlite3_malloc64(3*nCol*sizeof(const char*) + 1);
if( !pData ){
- rc = SQLITE_NOMEM;
+ shell_out_of_memory();
}else{
char **azCols = (char **)pData; /* Names of result columns */
char **azVals = &azCols[nCol]; /* Results */
@@ -13392,10 +14707,14 @@ static void exec_prepared_stmt(
azCols[i] = (char *)sqlite3_column_name(pStmt, i);
}
do{
+ nRow++;
/* extract the data and data types */
for(i=0; i<nCol; i++){
aiTypes[i] = x = sqlite3_column_type(pStmt, i);
- if( x==SQLITE_BLOB && pArg && pArg->cMode==MODE_Insert ){
+ if( x==SQLITE_BLOB
+ && pArg
+ && (pArg->cMode==MODE_Insert || pArg->cMode==MODE_Quote)
+ ){
azVals[i] = "";
}else{
azVals[i] = (char*)sqlite3_column_text(pStmt, i);
@@ -13419,6 +14738,11 @@ static void exec_prepared_stmt(
sqlite3_free(pData);
if( pArg->cMode==MODE_Json ){
fputs("]\n", pArg->out);
+ }else if( pArg->cMode==MODE_Count ){
+ char zBuf[200];
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%llu row%s\n",
+ nRow, nRow!=1 ? "s" : "");
+ printf("%s", zBuf);
}
}
}
@@ -13542,7 +14866,7 @@ static int expertDotCommand(
if( rc==SQLITE_OK ){
pState->expert.pExpert = sqlite3_expert_new(pState->db, &zErr);
if( pState->expert.pExpert==0 ){
- raw_printf(stderr, "sqlite3_expert_new: %s\n", zErr);
+ raw_printf(stderr, "sqlite3_expert_new: %s\n", zErr ? zErr : "out of memory");
rc = SQLITE_ERROR;
}else{
sqlite3_expert_config(
@@ -13550,6 +14874,7 @@ static int expertDotCommand(
);
}
}
+ sqlite3_free(zErr);
return rc;
}
@@ -13591,7 +14916,7 @@ static int shell_exec(
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover);
if( SQLITE_OK != rc ){
if( pzErrMsg ){
- *pzErrMsg = save_err_msg(db);
+ *pzErrMsg = save_err_msg(db, "in prepare", rc, zSql);
}
}else{
if( !pStmt ){
@@ -13610,11 +14935,6 @@ static int shell_exec(
pArg->cnt = 0;
}
- /* echo the sql statement if echo on */
- if( pArg && ShellHasFlag(pArg, SHFLG_Echo) ){
- utf8_printf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql);
- }
-
/* Show the EXPLAIN QUERY PLAN if .eqp is on */
if( pArg && pArg->autoEQP && sqlite3_stmt_isexplain(pStmt)==0 ){
sqlite3_stmt *pExplain;
@@ -13626,6 +14946,7 @@ static int shell_exec(
sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 1, 0);
}
zEQP = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", zStmtSql);
+ shell_check_oom(zEQP);
rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0);
if( rc==SQLITE_OK ){
while( sqlite3_step(pExplain)==SQLITE_ROW ){
@@ -13643,6 +14964,7 @@ static int shell_exec(
if( pArg->autoEQP>=AUTOEQP_full ){
/* Also do an EXPLAIN for ".eqp full" mode */
zEQP = sqlite3_mprintf("EXPLAIN %s", zStmtSql);
+ shell_check_oom(zEQP);
rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0);
if( rc==SQLITE_OK ){
pArg->cMode = MODE_Explain;
@@ -13705,7 +15027,7 @@ static int shell_exec(
zSql = zLeftover;
while( IsSpace(zSql[0]) ) zSql++;
}else if( pzErrMsg ){
- *pzErrMsg = save_err_msg(db);
+ *pzErrMsg = save_err_msg(db, "stepping", rc, 0);
}
/* clear saved stmt handle */
@@ -13755,6 +15077,7 @@ static char **tableColumnList(ShellState *p, const char *zTab){
int rc;
zSql = sqlite3_mprintf("PRAGMA table_info=%Q", zTab);
+ shell_check_oom(zSql);
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
if( rc ) return 0;
@@ -13762,9 +15085,10 @@ static char **tableColumnList(ShellState *p, const char *zTab){
if( nCol>=nAlloc-2 ){
nAlloc = nAlloc*2 + nCol + 10;
azCol = sqlite3_realloc(azCol, nAlloc*sizeof(azCol[0]));
- if( azCol==0 ) shell_out_of_memory();
+ shell_check_oom(azCol);
}
azCol[++nCol] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1));
+ shell_check_oom(azCol[nCol]);
if( sqlite3_column_int(pStmt, 5) ){
nPK++;
if( nPK==1
@@ -13798,6 +15122,7 @@ static char **tableColumnList(ShellState *p, const char *zTab){
*/
zSql = sqlite3_mprintf("SELECT 1 FROM pragma_index_list(%Q)"
" WHERE origin='pk'", zTab);
+ shell_check_oom(zSql);
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
if( rc ){
@@ -13889,6 +15214,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){
"INSERT INTO sqlite_schema(type,name,tbl_name,rootpage,sql)"
"VALUES('table','%q','%q',0,'%q');",
zTable, zTable, zSql);
+ shell_check_oom(zIns);
utf8_printf(p->out, "%s\n", zIns);
sqlite3_free(zIns);
return 0;
@@ -14007,18 +15333,20 @@ static int run_schema_dump_query(
** Text of help messages.
**
** The help text for each individual command begins with a line that starts
-** with ".". Subsequent lines are supplimental information.
+** with ".". Subsequent lines are supplemental information.
**
** There must be two or more spaces between the end of the command and the
** start of the description of what that command does.
*/
static const char *(azHelp[]) = {
-#if defined(SQLITE_HAVE_ZLIB) && !defined(SQLITE_OMIT_VIRTUALTABLE)
+#if defined(SQLITE_HAVE_ZLIB) && !defined(SQLITE_OMIT_VIRTUALTABLE) \
+ && !defined(SQLITE_SHELL_WASM_MODE)
".archive ... Manage SQL archives",
" Each command must have exactly one of the following options:",
" -c, --create Create a new archive",
" -u, --update Add or update files with changed mtime",
" -i, --insert Like -u but always add even if unchanged",
+ " -r, --remove Remove files from archive",
" -t, --list List contents of archive",
" -x, --extract Extract files from archive",
" Optional arguments:",
@@ -14026,29 +15354,40 @@ static const char *(azHelp[]) = {
" -f FILE, --file FILE Use archive FILE (default is current db)",
" -a FILE, --append FILE Open FILE using the apndvfs VFS",
" -C DIR, --directory DIR Read/extract files from directory DIR",
+ " -g, --glob Use glob matching for names in archive",
" -n, --dryrun Show the SQL that would have occurred",
" Examples:",
" .ar -cf ARCHIVE foo bar # Create ARCHIVE from files foo and bar",
" .ar -tf ARCHIVE # List members of ARCHIVE",
" .ar -xvf ARCHIVE # Verbosely extract files from ARCHIVE",
" See also:",
- " http://sqlite.org/cli.html#sqlar_archive_support",
+ " http://sqlite.org/cli.html#sqlite_archive_support",
#endif
#ifndef SQLITE_OMIT_AUTHORIZATION
".auth ON|OFF Show authorizer callbacks",
#endif
+#ifndef SQLITE_SHELL_WASM_MODE
".backup ?DB? FILE Backup DB (default \"main\") to FILE",
+ " Options:",
" --append Use the appendvfs",
" --async Write to FILE without journal and fsync()",
+#endif
".bail on|off Stop after hitting an error. Default OFF",
".binary on|off Turn binary output on or off. Default OFF",
+#ifndef SQLITE_SHELL_WASM_MODE
".cd DIRECTORY Change the working directory to DIRECTORY",
+#endif
".changes on|off Show number of rows changed by SQL",
+#ifndef SQLITE_SHELL_WASM_MODE
".check GLOB Fail if output since .testcase does not match",
".clone NEWDB Clone data into NEWDB from the existing database",
+#endif
+ ".connection [close] [#] Open or close an auxiliary database connection",
".databases List names and files of attached databases",
".dbconfig ?op? ?val? List or change sqlite3_db_config() options",
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
".dbinfo ?DB? Show status information about the database",
+#endif
".dump ?OBJECTS? Render database content as SQL",
" Options:",
" --data-only Output only INSERT statements",
@@ -14065,9 +15404,13 @@ static const char *(azHelp[]) = {
" trace Like \"full\" but enable \"PRAGMA vdbe_trace\"",
#endif
" trigger Like \"full\" but also show trigger bytecode",
+#ifndef SQLITE_SHELL_WASM_MODE
".excel Display the output of next command in spreadsheet",
" --bom Put a UTF8 byte-order mark on intermediate file",
+#endif
+#ifndef SQLITE_SHELL_WASM_MODE
".exit ?CODE? Exit this program with return-code CODE",
+#endif
".expert EXPERIMENTAL. Suggest indexes for queries",
".explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto",
".filectrl CMD ... Run various sqlite3_file_control() operations",
@@ -14076,11 +15419,13 @@ static const char *(azHelp[]) = {
".fullschema ?--indent? Show schema and the content of sqlite_stat tables",
".headers on|off Turn display of headers on or off",
".help ?-all? ?PATTERN? Show help text for PATTERN",
+#ifndef SQLITE_SHELL_WASM_MODE
".import FILE TABLE Import data from FILE into TABLE",
" Options:",
" --ascii Use \\037 and \\036 as column and row separators",
" --csv Use , and \\n as column and row separators",
" --skip N Skip the first N rows of input",
+ " --schema S Target table to be S.TABLE",
" -v \"Verbose\" - increase auxiliary output",
" Notes:",
" * If TABLE does not exist, it is created. The first row of input",
@@ -14089,6 +15434,7 @@ static const char *(azHelp[]) = {
" from the \".mode\" output mode",
" * If FILE begins with \"|\" then it is a command that generates the",
" input text.",
+#endif
#ifndef SQLITE_OMIT_TEST_CONTROL
".imposter INDEX TABLE Create imposter table TABLE on index INDEX",
#endif
@@ -14102,40 +15448,55 @@ static const char *(azHelp[]) = {
".lint OPTIONS Report potential schema issues.",
" Options:",
" fkey-indexes Find missing foreign key indexes",
-#ifndef SQLITE_OMIT_LOAD_EXTENSION
+#if !defined(SQLITE_OMIT_LOAD_EXTENSION) && !defined(SQLITE_SHELL_WASM_MODE)
".load FILE ?ENTRY? Load an extension library",
#endif
+#ifndef SQLITE_SHELL_WASM_MODE
".log FILE|off Turn logging on or off. FILE can be stderr/stdout",
- ".mode MODE ?TABLE? Set output mode",
+#endif
+ ".mode MODE ?OPTIONS? Set output mode",
" MODE is one of:",
- " ascii Columns/rows delimited by 0x1F and 0x1E",
- " box Tables using unicode box-drawing characters",
- " csv Comma-separated values",
- " column Output in columns. (See .width)",
- " html HTML <table> code",
- " insert SQL insert statements for TABLE",
- " json Results in a JSON array",
- " line One value per line",
- " list Values delimited by \"|\"",
- " markdown Markdown table format",
- " quote Escape answers as for SQL",
- " table ASCII-art table",
- " tabs Tab-separated values",
- " tcl TCL list elements",
+ " ascii Columns/rows delimited by 0x1F and 0x1E",
+ " box Tables using unicode box-drawing characters",
+ " csv Comma-separated values",
+ " column Output in columns. (See .width)",
+ " html HTML <table> code",
+ " insert SQL insert statements for TABLE",
+ " json Results in a JSON array",
+ " line One value per line",
+ " list Values delimited by \"|\"",
+ " markdown Markdown table format",
+ " qbox Shorthand for \"box --width 60 --quote\"",
+ " quote Escape answers as for SQL",
+ " table ASCII-art table",
+ " tabs Tab-separated values",
+ " tcl TCL list elements",
+ " OPTIONS: (for columnar modes or insert mode):",
+ " --wrap N Wrap output lines to no longer than N characters",
+ " --wordwrap B Wrap or not at word boundaries per B (on/off)",
+ " --ww Shorthand for \"--wordwrap 1\"",
+ " --quote Quote output text as SQL literals",
+ " --noquote Do not quote output text",
+ " TABLE The name of SQL table used for \"insert\" mode",
+#ifndef SQLITE_SHELL_WASM_MODE
+ ".nonce STRING Suspend safe mode for one command if nonce matches",
+#endif
".nullvalue STRING Use STRING in place of NULL values",
+#ifndef SQLITE_SHELL_WASM_MODE
".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE",
" If FILE begins with '|' then open as a pipe",
" --bom Put a UTF8 byte-order mark at the beginning",
" -e Send output to the system text editor",
" -x Send output as CSV to a spreadsheet (same as \".excel\")",
-#ifdef SQLITE_DEBUG
- ".oom ?--repeat M? ?N? Simulate an OOM error on the N-th allocation",
-#endif
+ /* Note that .open is (partially) available in WASM builds but is
+ ** currently only intended to be used by the fiddle tool, not
+ ** end users, so is "undocumented." */
".open ?OPTIONS? ?FILE? Close existing database and reopen FILE",
" Options:",
" --append Use appendvfs to append database to the end of FILE",
-#ifdef SQLITE_ENABLE_DESERIALIZE
- " --deserialize Load into memory useing sqlite3_deserialize()",
+#endif
+#ifndef SQLITE_OMIT_DESERIALIZE
+ " --deserialize Load into memory using sqlite3_deserialize()",
" --hexdb Load the output of \"dbtotxt\" as an in-memory db",
" --maxsize N Maximum size for --hexdb or --deserialized database",
#endif
@@ -14143,12 +15504,14 @@ static const char *(azHelp[]) = {
" --nofollow Do not follow symbolic links",
" --readonly Open FILE readonly",
" --zip FILE is a ZIP archive",
+#ifndef SQLITE_SHELL_WASM_MODE
".output ?FILE? Send output to FILE or stdout if FILE is omitted",
" If FILE begins with '|' then open it as a pipe.",
" Options:",
" --bom Prefix output with a UTF8 byte-order mark",
" -e Send output to the system text editor",
" -x Send output as CSV to a spreadsheet",
+#endif
".parameter CMD ... Manage SQL parameter bindings",
" clear Erase all bindings",
" init Initialize the TEMP table that holds bindings",
@@ -14165,8 +15528,11 @@ static const char *(azHelp[]) = {
" --reset Reset the count for each input and interrupt",
#endif
".prompt MAIN CONTINUE Replace the standard prompts",
+#ifndef SQLITE_SHELL_WASM_MODE
".quit Exit this program",
- ".read FILE Read input from FILE",
+ ".read FILE Read input from FILE or command output",
+ " If FILE begins with \"|\", it is a command that generates the input.",
+#endif
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
".recover Recover as much data as possible from corrupt db.",
" --freelist-corrupt Assume the freelist is corrupt",
@@ -14175,8 +15541,10 @@ static const char *(azHelp[]) = {
" --no-rowids Do not attempt to recover rowid values",
" that are not also INTEGER PRIMARY KEYs",
#endif
+#ifndef SQLITE_SHELL_WASM_MODE
".restore ?DB? FILE Restore content of DB (default \"main\") from FILE",
- ".save FILE Write in-memory database into FILE",
+ ".save ?OPTIONS? FILE Write database to FILE (an alias for .backup ...)",
+#endif
".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off",
".schema ?PATTERN? Show the CREATE statements matching PATTERN",
" Options:",
@@ -14210,7 +15578,7 @@ static const char *(azHelp[]) = {
" --sha3-384 Use the sha3-384 algorithm",
" --sha3-512 Use the sha3-512 algorithm",
" Any other argument is a LIKE pattern for tables to hash",
-#ifndef SQLITE_NOHAVE_SYSTEM
+#if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_WASM_MODE)
".shell CMD ARGS... Run CMD ARGS... in a system shell",
#endif
".show Show the current values for various settings",
@@ -14219,11 +15587,13 @@ static const char *(azHelp[]) = {
" on Turn on automatic stat display",
" stmt Show statement stats",
" vmstep Show the virtual machine step count only",
-#ifndef SQLITE_NOHAVE_SYSTEM
+#if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_WASM_MODE)
".system CMD ARGS... Run CMD ARGS... in a system shell",
#endif
".tables ?TABLE? List names of tables matching LIKE pattern TABLE",
+#ifndef SQLITE_SHELL_WASM_MODE
".testcase NAME Begin redirecting output to 'testcase-out.txt'",
+#endif
".testctrl CMD ... Run various sqlite3_test_control() operations",
" Run \".testctrl\" with no arguments for details",
".timeout MS Try opening locked tables for MS milliseconds",
@@ -14286,6 +15656,7 @@ static int showHelp(FILE *out, const char *zPattern){
}else{
/* Look for commands that for which zPattern is an exact prefix */
zPat = sqlite3_mprintf(".%s*", zPattern);
+ shell_check_oom(zPat);
for(i=0; i<ArraySize(azHelp); i++){
if( sqlite3_strglob(zPat, azHelp[i])==0 ){
utf8_printf(out, "%s\n", azHelp[i]);
@@ -14308,6 +15679,7 @@ static int showHelp(FILE *out, const char *zPattern){
/* Look for commands that contain zPattern anywhere. Show the complete
** text of all commands that match. */
zPat = sqlite3_mprintf("%%%s%%", zPattern);
+ shell_check_oom(zPat);
for(i=0; i<ArraySize(azHelp); i++){
if( azHelp[i][0]=='.' ) j = i;
if( sqlite3_strlike(zPat, azHelp[i], 0)==0 ){
@@ -14386,15 +15758,16 @@ static void session_close(OpenSession *pSession){
** Close all OpenSession objects and release all associated resources.
*/
#if defined(SQLITE_ENABLE_SESSION)
-static void session_close_all(ShellState *p){
- int i;
- for(i=0; i<p->nSession; i++){
- session_close(&p->aSession[i]);
+static void session_close_all(ShellState *p, int i){
+ int j;
+ struct AuxDb *pAuxDb = i<0 ? p->pAuxDb : &p->aAuxDb[i];
+ for(j=0; j<pAuxDb->nSession; j++){
+ session_close(&pAuxDb->aSession[j]);
}
- p->nSession = 0;
+ pAuxDb->nSession = 0;
}
#else
-# define session_close_all(X)
+# define session_close_all(X,Y)
#endif
/*
@@ -14456,11 +15829,11 @@ int deduceDatabaseType(const char *zName, int dfltZip){
return rc;
}
-#ifdef SQLITE_ENABLE_DESERIALIZE
+#ifndef SQLITE_OMIT_DESERIALIZE
/*
** Reconstruct an in-memory database using the output from the "dbtotxt"
-** program. Read content from the file in p->zDbFilename. If p->zDbFilename
-** is 0, then read from standard input.
+** program. Read content from the file in p->aAuxDb[].zDbFilename.
+** If p->aAuxDb[].zDbFilename is 0, then read from standard input.
*/
static unsigned char *readHexDb(ShellState *p, int *pnData){
unsigned char *a = 0;
@@ -14471,12 +15844,13 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){
int j, k;
int rc;
FILE *in;
+ const char *zDbFilename = p->pAuxDb->zDbFilename;
unsigned int x[16];
char zLine[1000];
- if( p->zDbFilename ){
- in = fopen(p->zDbFilename, "r");
+ if( zDbFilename ){
+ in = fopen(zDbFilename, "r");
if( in==0 ){
- utf8_printf(stderr, "cannot open \"%s\" for reading\n", p->zDbFilename);
+ utf8_printf(stderr, "cannot open \"%s\" for reading\n", zDbFilename);
return 0;
}
nLine = 0;
@@ -14494,10 +15868,7 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){
if( pgsz<512 || pgsz>65536 || (pgsz&(pgsz-1))!=0 ) goto readHexDb_error;
n = (n+pgsz-1)&~(pgsz-1); /* Round n up to the next multiple of pgsz */
a = sqlite3_malloc( n ? n : 1 );
- if( a==0 ){
- utf8_printf(stderr, "Out of memory!\n");
- goto readHexDb_error;
- }
+ shell_check_oom(a);
memset(a, 0, n);
if( pgsz<512 || pgsz>65536 || (pgsz & (pgsz-1))!=0 ){
utf8_printf(stderr, "invalid pagesize\n");
@@ -14517,7 +15888,7 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){
&x[8], &x[9], &x[10], &x[11], &x[12], &x[13], &x[14], &x[15]);
if( rc==17 ){
k = iOffset+j;
- if( k+16<=n ){
+ if( k+16<=n && k>=0 ){
int ii;
for(ii=0; ii<16; ii++) a[k+ii] = x[ii]&0xff;
}
@@ -14545,7 +15916,7 @@ readHexDb_error:
utf8_printf(stderr,"Error on line %d of --hexdb input\n", nLine);
return 0;
}
-#endif /* SQLITE_ENABLE_DESERIALIZE */
+#endif /* SQLITE_OMIT_DESERIALIZE */
/*
** Scalar function "shell_int32". The first argument to this function
@@ -14628,7 +15999,7 @@ static void shellEscapeCrnl(
){
const char *zText = (const char*)sqlite3_value_text(argv[0]);
UNUSED_PARAMETER(argc);
- if( zText[0]=='\'' ){
+ if( zText && zText[0]=='\'' ){
int nText = sqlite3_value_bytes(argv[0]);
int i;
char zBuf1[20];
@@ -14718,17 +16089,18 @@ static void shellEscapeCrnl(
*/
static void open_db(ShellState *p, int openFlags){
if( p->db==0 ){
+ const char *zDbFilename = p->pAuxDb->zDbFilename;
if( p->openMode==SHELL_OPEN_UNSPEC ){
- if( p->zDbFilename==0 || p->zDbFilename[0]==0 ){
+ if( zDbFilename==0 || zDbFilename[0]==0 ){
p->openMode = SHELL_OPEN_NORMAL;
}else{
- p->openMode = (u8)deduceDatabaseType(p->zDbFilename,
+ p->openMode = (u8)deduceDatabaseType(zDbFilename,
(openFlags & OPEN_DB_ZIPFILE)!=0);
}
}
switch( p->openMode ){
case SHELL_OPEN_APPENDVFS: {
- sqlite3_open_v2(p->zDbFilename, &p->db,
+ sqlite3_open_v2(zDbFilename, &p->db,
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, "apndvfs");
break;
}
@@ -14742,13 +16114,13 @@ static void open_db(ShellState *p, int openFlags){
break;
}
case SHELL_OPEN_READONLY: {
- sqlite3_open_v2(p->zDbFilename, &p->db,
+ sqlite3_open_v2(zDbFilename, &p->db,
SQLITE_OPEN_READONLY|p->openFlags, 0);
break;
}
case SHELL_OPEN_UNSPEC:
case SHELL_OPEN_NORMAL: {
- sqlite3_open_v2(p->zDbFilename, &p->db,
+ sqlite3_open_v2(zDbFilename, &p->db,
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, 0);
break;
}
@@ -14756,7 +16128,7 @@ static void open_db(ShellState *p, int openFlags){
globalDb = p->db;
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n",
- p->zDbFilename, sqlite3_errmsg(p->db));
+ zDbFilename, sqlite3_errmsg(p->db));
if( openFlags & OPEN_DB_KEEPALIVE ){
sqlite3_open(":memory:", &p->db);
return;
@@ -14766,19 +16138,24 @@ static void open_db(ShellState *p, int openFlags){
#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_uint_init(p->db, 0, 0);
sqlite3_decimal_init(p->db, 0, 0);
+ sqlite3_regexp_init(p->db, 0, 0);
sqlite3_ieee_init(p->db, 0, 0);
sqlite3_series_init(p->db, 0, 0);
+#ifndef SQLITE_SHELL_WASM_MODE
+ sqlite3_fileio_init(p->db, 0, 0);
+ sqlite3_completion_init(p->db, 0, 0);
+#endif
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
sqlite3_dbdata_init(p->db, 0, 0);
#endif
#ifdef SQLITE_HAVE_ZLIB
- sqlite3_zipfile_init(p->db, 0, 0);
- sqlite3_sqlar_init(p->db, 0, 0);
+ if( !p->bSafeModePersist ){
+ 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);
@@ -14802,18 +16179,19 @@ static void open_db(ShellState *p, int openFlags){
#endif
if( p->openMode==SHELL_OPEN_ZIPFILE ){
char *zSql = sqlite3_mprintf(
- "CREATE VIRTUAL TABLE zip USING zipfile(%Q);", p->zDbFilename);
+ "CREATE VIRTUAL TABLE zip USING zipfile(%Q);", zDbFilename);
+ shell_check_oom(zSql);
sqlite3_exec(p->db, zSql, 0, 0, 0);
sqlite3_free(zSql);
}
-#ifdef SQLITE_ENABLE_DESERIALIZE
+#ifndef SQLITE_OMIT_DESERIALIZE
else
if( p->openMode==SHELL_OPEN_DESERIALIZE || p->openMode==SHELL_OPEN_HEXDB ){
int rc;
int nData = 0;
unsigned char *aData;
if( p->openMode==SHELL_OPEN_DESERIALIZE ){
- aData = (unsigned char*)readFile(p->zDbFilename, &nData);
+ aData = (unsigned char*)readFile(zDbFilename, &nData);
}else{
aData = readHexDb(p, &nData);
if( aData==0 ){
@@ -14832,6 +16210,9 @@ static void open_db(ShellState *p, int openFlags){
}
#endif
}
+ if( p->bSafeModePersist && p->db!=0 ){
+ sqlite3_set_authorizer(p->db, safeModeAuth, p);
+ }
}
/*
@@ -14857,11 +16238,13 @@ static char *readline_completion_generator(const char *text, int state){
sqlite3_finalize(pStmt);
zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase"
" FROM completion(%Q) ORDER BY 1", text);
+ shell_check_oom(zSql);
sqlite3_prepare_v2(globalDb, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
}
if( sqlite3_step(pStmt)==SQLITE_ROW ){
- zRet = strdup((const char*)sqlite3_column_text(pStmt, 0));
+ const char *z = (const char*)sqlite3_column_text(pStmt,0);
+ zRet = z ? strdup(z) : 0;
}else{
sqlite3_finalize(pStmt);
pStmt = 0;
@@ -14894,13 +16277,14 @@ static void linenoise_completion(const char *zLine, linenoiseCompletions *lc){
zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase"
" FROM completion(%Q,%Q) ORDER BY 1",
&zLine[iStart], zLine);
+ shell_check_oom(zSql);
sqlite3_prepare_v2(globalDb, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
sqlite3_exec(globalDb, "PRAGMA page_count", 0, 0, 0); /* Load the schema */
while( sqlite3_step(pStmt)==SQLITE_ROW ){
const char *zCompletion = (const char*)sqlite3_column_text(pStmt, 0);
int nCompletion = sqlite3_column_bytes(pStmt, 0);
- if( iStart+nCompletion < sizeof(zBuf)-1 ){
+ if( iStart+nCompletion < sizeof(zBuf)-1 && zCompletion ){
memcpy(zBuf+iStart, zCompletion, nCompletion+1);
linenoiseAddCompletion(lc, zBuf);
}
@@ -15135,7 +16519,7 @@ static void import_append_char(ImportCtx *p, int c){
if( p->n+1>=p->nAlloc ){
p->nAlloc += p->nAlloc + 100;
p->z = sqlite3_realloc64(p->z, p->nAlloc);
- if( p->z==0 ) shell_out_of_memory();
+ shell_check_oom(p->z);
}
p->z[p->n++] = (char)c;
}
@@ -15287,6 +16671,7 @@ static void tryToCloneData(
const int spinRate = 10000;
zQuery = sqlite3_mprintf("SELECT * FROM \"%w\"", zTable);
+ shell_check_oom(zQuery);
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
utf8_printf(stderr, "Error %d: %s on [%s]\n",
@@ -15296,7 +16681,7 @@ static void tryToCloneData(
}
n = sqlite3_column_count(pQuery);
zInsert = sqlite3_malloc64(200 + nTable + n*3);
- if( zInsert==0 ) shell_out_of_memory();
+ shell_check_oom(zInsert);
sqlite3_snprintf(200+nTable,zInsert,
"INSERT OR IGNORE INTO \"%s\" VALUES(?", zTable);
i = strlen30(zInsert);
@@ -15359,6 +16744,7 @@ static void tryToCloneData(
sqlite3_free(zQuery);
zQuery = sqlite3_mprintf("SELECT * FROM \"%w\" ORDER BY rowid DESC;",
zTable);
+ shell_check_oom(zQuery);
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
utf8_printf(stderr, "Warning: cannot step \"%s\" backwards", zTable);
@@ -15395,6 +16781,7 @@ static void tryToCloneSchema(
zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema"
" WHERE %s", zWhere);
+ shell_check_oom(zQuery);
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
utf8_printf(stderr, "Error: (%d) %s on [%s]\n",
@@ -15405,6 +16792,7 @@ static void tryToCloneSchema(
while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){
zName = sqlite3_column_text(pQuery, 0);
zSql = sqlite3_column_text(pQuery, 1);
+ if( zName==0 || zSql==0 ) continue;
printf("%s... ", zName); fflush(stdout);
sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg);
if( zErrMsg ){
@@ -15422,6 +16810,7 @@ static void tryToCloneSchema(
sqlite3_free(zQuery);
zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema"
" WHERE %s ORDER BY rowid DESC", zWhere);
+ shell_check_oom(zQuery);
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
utf8_printf(stderr, "Error: (%d) %s on [%s]\n",
@@ -15429,9 +16818,10 @@ static void tryToCloneSchema(
zQuery);
goto end_schema_xfer;
}
- while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){
+ while( sqlite3_step(pQuery)==SQLITE_ROW ){
zName = sqlite3_column_text(pQuery, 0);
zSql = sqlite3_column_text(pQuery, 1);
+ if( zName==0 || zSql==0 ) continue;
printf("%s... ", zName); fflush(stdout);
sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg);
if( zErrMsg ){
@@ -15524,10 +16914,10 @@ static void output_reset(ShellState *p){
/*
** Run an SQL command and return the single integer result.
*/
-static int db_int(ShellState *p, const char *zSql){
+static int db_int(sqlite3 *db, const char *zSql){
sqlite3_stmt *pStmt;
int res = 0;
- sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+ sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
res = sqlite3_column_int(pStmt,0);
}
@@ -15535,6 +16925,7 @@ static int db_int(ShellState *p, const char *zSql){
return res;
}
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
/*
** Convert a 2-byte or 4-byte big-endian integer into a native integer
*/
@@ -15632,7 +17023,7 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){
}
for(i=0; i<ArraySize(aQuery); i++){
char *zSql = sqlite3_mprintf(aQuery[i].zSql, zSchemaTab);
- int val = db_int(p, zSql);
+ int val = db_int(p->db, zSql);
sqlite3_free(zSql);
utf8_printf(p->out, "%-20s %d\n", aQuery[i].zName, val);
}
@@ -15641,6 +17032,8 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){
utf8_printf(p->out, "%-20s %u\n", "data version", iDataVersion);
return 0;
}
+#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE)
+ && defined(SQLITE_ENABLE_DBPAGE_VTAB) */
/*
** Print the current sqlite3_errmsg() value to stderr and return 1.
@@ -15816,10 +17209,7 @@ static void newTempFile(ShellState *p, const char *zSuffix){
}else{
p->zTempFile = sqlite3_mprintf("%z.%s", p->zTempFile, zSuffix);
}
- if( p->zTempFile==0 ){
- raw_printf(stderr, "out of memory\n");
- exit(1);
- }
+ shell_check_oom(p->zTempFile);
}
@@ -15935,7 +17325,7 @@ static int lintFkeyIndexes(
" || fkey_collate_clause("
" f.[table], COALESCE(f.[to], p.[name]), s.name, f.[from]),' AND ')"
", "
- " 'SEARCH TABLE ' || s.name || ' USING COVERING INDEX*('"
+ " 'SEARCH ' || s.name || ' USING COVERING INDEX*('"
" || group_concat('*=?', ' AND ') || ')'"
", "
" s.name || '(' || group_concat(f.[from], ', ') || ')'"
@@ -15955,7 +17345,7 @@ static int lintFkeyIndexes(
"GROUP BY s.name, f.id "
"ORDER BY (CASE WHEN ? THEN f.[table] ELSE s.name END)"
;
- const char *zGlobIPK = "SEARCH TABLE * USING INTEGER PRIMARY KEY (rowid=?)";
+ const char *zGlobIPK = "SEARCH * USING INTEGER PRIMARY KEY (rowid=?)";
for(i=2; i<nArg; i++){
int n = strlen30(azArg[i]);
@@ -16000,14 +17390,14 @@ static int lintFkeyIndexes(
const char *zCI = (const char*)sqlite3_column_text(pSql, 4);
const char *zParent = (const char*)sqlite3_column_text(pSql, 5);
+ if( zEQP==0 ) continue;
+ if( zGlob==0 ) continue;
rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0);
if( rc!=SQLITE_OK ) break;
if( SQLITE_ROW==sqlite3_step(pExplain) ){
const char *zPlan = (const char*)sqlite3_column_text(pExplain, 3);
- res = (
- 0==sqlite3_strglob(zGlob, zPlan)
- || 0==sqlite3_strglob(zGlobIPK, zPlan)
- );
+ res = zPlan!=0 && ( 0==sqlite3_strglob(zGlob, zPlan)
+ || 0==sqlite3_strglob(zGlobIPK, zPlan));
}
rc = sqlite3_finalize(pExplain);
if( rc!=SQLITE_OK ) break;
@@ -16178,6 +17568,7 @@ struct ArCommand {
u8 bZip; /* True if the archive is a ZIP */
u8 bDryRun; /* True if --dry-run */
u8 bAppend; /* True if --append */
+ u8 bGlob; /* True if --glob */
u8 fromCmdLine; /* Run from -A instead of .archive */
int nArg; /* Number of command arguments */
char *zSrcTable; /* "sqlar", "zipfile($file)" or "zip" */
@@ -16225,21 +17616,24 @@ static int arErrorMsg(ArCommand *pAr, const char *zFmt, ...){
#define AR_CMD_EXTRACT 4
#define AR_CMD_LIST 5
#define AR_CMD_HELP 6
+#define AR_CMD_REMOVE 7
/*
** Other (non-command) switches.
*/
-#define AR_SWITCH_VERBOSE 7
-#define AR_SWITCH_FILE 8
-#define AR_SWITCH_DIRECTORY 9
-#define AR_SWITCH_APPEND 10
-#define AR_SWITCH_DRYRUN 11
+#define AR_SWITCH_VERBOSE 8
+#define AR_SWITCH_FILE 9
+#define AR_SWITCH_DIRECTORY 10
+#define AR_SWITCH_APPEND 11
+#define AR_SWITCH_DRYRUN 12
+#define AR_SWITCH_GLOB 13
static int arProcessSwitch(ArCommand *pAr, int eSwitch, const char *zArg){
switch( eSwitch ){
case AR_CMD_CREATE:
case AR_CMD_EXTRACT:
case AR_CMD_LIST:
+ case AR_CMD_REMOVE:
case AR_CMD_UPDATE:
case AR_CMD_INSERT:
case AR_CMD_HELP:
@@ -16252,6 +17646,9 @@ static int arProcessSwitch(ArCommand *pAr, int eSwitch, const char *zArg){
case AR_SWITCH_DRYRUN:
pAr->bDryRun = 1;
break;
+ case AR_SWITCH_GLOB:
+ pAr->bGlob = 1;
+ break;
case AR_SWITCH_VERBOSE:
pAr->bVerbose = 1;
break;
@@ -16290,6 +17687,7 @@ static int arParseCommand(
{ "extract", 'x', AR_CMD_EXTRACT, 0 },
{ "insert", 'i', AR_CMD_INSERT, 0 },
{ "list", 't', AR_CMD_LIST, 0 },
+ { "remove", 'r', AR_CMD_REMOVE, 0 },
{ "update", 'u', AR_CMD_UPDATE, 0 },
{ "help", 'h', AR_CMD_HELP, 0 },
{ "verbose", 'v', AR_SWITCH_VERBOSE, 0 },
@@ -16297,6 +17695,7 @@ static int arParseCommand(
{ "append", 'a', AR_SWITCH_APPEND, 1 },
{ "directory", 'C', AR_SWITCH_DIRECTORY, 1 },
{ "dryrun", 'n', AR_SWITCH_DRYRUN, 0 },
+ { "glob", 'g', AR_SWITCH_GLOB, 0 },
};
int nSwitch = sizeof(aSwitch) / sizeof(struct ArSwitch);
struct ArSwitch *pEnd = &aSwitch[nSwitch];
@@ -16413,11 +17812,13 @@ static int arParseCommand(
/*
** This function assumes that all arguments within the ArCommand.azArg[]
-** array refer to archive members, as for the --extract or --list commands.
-** It checks that each of them are present. If any specified file is not
-** present in the archive, an error is printed to stderr and an error
-** code returned. Otherwise, if all specified arguments are present in
-** the archive, SQLITE_OK is returned.
+** array refer to archive members, as for the --extract, --list or --remove
+** commands. It checks that each of them are "present". If any specified
+** file is not present in the archive, an error is printed to stderr and an
+** error code returned. Otherwise, if all specified arguments are present
+** in the archive, SQLITE_OK is returned. Here, "present" means either an
+** exact equality when pAr->bGlob is false or a "name GLOB pattern" match
+** when pAr->bGlob is true.
**
** This function strips any trailing '/' characters from each argument.
** This is consistent with the way the [tar] command seems to work on
@@ -16428,11 +17829,11 @@ static int arCheckEntries(ArCommand *pAr){
if( pAr->nArg ){
int i, j;
sqlite3_stmt *pTest = 0;
+ const char *zSel = (pAr->bGlob)
+ ? "SELECT name FROM %s WHERE glob($name,name)"
+ : "SELECT name FROM %s WHERE name=$name";
- shellPreparePrintf(pAr->db, &rc, &pTest,
- "SELECT name FROM %s WHERE name=$name",
- pAr->zSrcTable
- );
+ shellPreparePrintf(pAr->db, &rc, &pTest, zSel, pAr->zSrcTable);
j = sqlite3_bind_parameter_index(pTest, "$name");
for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){
char *z = pAr->azArg[i];
@@ -16460,14 +17861,16 @@ static int arCheckEntries(ArCommand *pAr){
** identify all archive members that match the command arguments held
** in (*pAr). Leave this WHERE clause in (*pzWhere) before returning.
** The caller is responsible for eventually calling sqlite3_free() on
-** any non-NULL (*pzWhere) value.
+** any non-NULL (*pzWhere) value. Here, "match" means strict equality
+** when pAr->bGlob is false and GLOB match when pAr->bGlob is true.
*/
static void arWhereClause(
int *pRc,
- ArCommand *pAr,
+ ArCommand *pAr,
char **pzWhere /* OUT: New WHERE clause */
){
char *zWhere = 0;
+ const char *zSameOp = (pAr->bGlob)? "GLOB" : "=";
if( *pRc==SQLITE_OK ){
if( pAr->nArg==0 ){
zWhere = sqlite3_mprintf("1");
@@ -16477,8 +17880,8 @@ static void arWhereClause(
for(i=0; i<pAr->nArg; i++){
const char *z = pAr->azArg[i];
zWhere = sqlite3_mprintf(
- "%z%s name = '%q' OR substr(name,1,%d) = '%q/'",
- zWhere, zSep, z, strlen30(z)+1, z
+ "%z%s name %s '%q' OR substr(name,1,%d) %s '%q/'",
+ zWhere, zSep, zSameOp, z, strlen30(z)+1, zSameOp, z
);
if( zWhere==0 ){
*pRc = SQLITE_NOMEM;
@@ -16533,6 +17936,47 @@ static int arListCommand(ArCommand *pAr){
/*
+** Implementation of .ar "Remove" command.
+*/
+static int arRemoveCommand(ArCommand *pAr){
+ int rc = 0;
+ char *zSql = 0;
+ char *zWhere = 0;
+
+ if( pAr->nArg ){
+ /* Verify that args actually exist within the archive before proceeding.
+ ** And formulate a WHERE clause to match them. */
+ rc = arCheckEntries(pAr);
+ arWhereClause(&rc, pAr, &zWhere);
+ }
+ if( rc==SQLITE_OK ){
+ zSql = sqlite3_mprintf("DELETE FROM %s WHERE %s;",
+ pAr->zSrcTable, zWhere);
+ if( pAr->bDryRun ){
+ utf8_printf(pAr->p->out, "%s\n", zSql);
+ }else{
+ char *zErr = 0;
+ rc = sqlite3_exec(pAr->db, "SAVEPOINT ar;", 0, 0, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_exec(pAr->db, zSql, 0, 0, &zErr);
+ if( rc!=SQLITE_OK ){
+ sqlite3_exec(pAr->db, "ROLLBACK TO ar; RELEASE ar;", 0, 0, 0);
+ }else{
+ rc = sqlite3_exec(pAr->db, "RELEASE ar;", 0, 0, 0);
+ }
+ }
+ if( zErr ){
+ utf8_printf(stdout, "ERROR: %s\n", zErr);
+ sqlite3_free(zErr);
+ }
+ }
+ }
+ sqlite3_free(zWhere);
+ sqlite3_free(zSql);
+ return rc;
+}
+
+/*
** Implementation of .ar "eXtract" command.
*/
static int arExtractCommand(ArCommand *pAr){
@@ -16784,7 +18228,7 @@ static int arDotCommand(
int flags;
if( cmd.bAppend ) eDbType = SHELL_OPEN_APPENDVFS;
if( cmd.eCmd==AR_CMD_CREATE || cmd.eCmd==AR_CMD_INSERT
- || cmd.eCmd==AR_CMD_UPDATE ){
+ || cmd.eCmd==AR_CMD_REMOVE || cmd.eCmd==AR_CMD_UPDATE ){
flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
}else{
flags = SQLITE_OPEN_READONLY;
@@ -16840,6 +18284,10 @@ static int arDotCommand(
rc = arCreateOrUpdateCommand(&cmd, 1, 0);
break;
+ case AR_CMD_REMOVE:
+ rc = arRemoveCommand(&cmd);
+ break;
+
default:
assert( cmd.eCmd==AR_CMD_UPDATE );
rc = arCreateOrUpdateCommand(&cmd, 1, 1);
@@ -16873,6 +18321,7 @@ static void shellExec(sqlite3 *db, int *pRc, const char *zSql){
if( rc!=SQLITE_OK ){
raw_printf(stderr, "SQL error: %s\n", zErr);
}
+ sqlite3_free(zErr);
*pRc = rc;
}
}
@@ -16940,6 +18389,7 @@ static char *shellMPrintf(int *pRc, const char *zFmt, ...){
return z;
}
+
/*
** When running the ".recover" command, each output table, and the special
** orphaned row table if it is required, is represented by an instance
@@ -17053,6 +18503,7 @@ static RecoverTable *recoverNewTable(
if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pPkFinder) ){
pTab->iPk = sqlite3_column_int(pPkFinder, 0);
zPk = (const char*)sqlite3_column_text(pPkFinder, 1);
+ if( zPk==0 ){ zPk = "_"; /* Defensive. Should never happen */ }
}
}
@@ -17137,8 +18588,10 @@ static RecoverTable *recoverFindTable(
if( sqlite3_stricmp(zType, "table")==0 ){
zName = (const char*)sqlite3_column_text(pStmt, 1);
zSql = (const char*)sqlite3_column_text(pStmt, 2);
- pRet = recoverNewTable(pRc, zName, zSql, bIntkey, nCol);
- break;
+ if( zName!=0 && zSql!=0 ){
+ pRet = recoverNewTable(pRc, zName, zSql, bIntkey, nCol);
+ break;
+ }
}
}
@@ -17535,6 +18988,223 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){
/*
+ * zAutoColumn(zCol, &db, ?) => Maybe init db, add column zCol to it.
+ * zAutoColumn(0, &db, ?) => (db!=0) Form columns spec for CREATE TABLE,
+ * close db and set it to 0, and return the columns spec, to later
+ * be sqlite3_free()'ed by the caller.
+ * The return is 0 when either:
+ * (a) The db was not initialized and zCol==0 (There are no columns.)
+ * (b) zCol!=0 (Column was added, db initialized as needed.)
+ * The 3rd argument, pRenamed, references an out parameter. If the
+ * pointer is non-zero, its referent will be set to a summary of renames
+ * done if renaming was necessary, or set to 0 if none was done. The out
+ * string (if any) must be sqlite3_free()'ed by the caller.
+ */
+#ifdef SHELL_DEBUG
+#define rc_err_oom_die(rc) \
+ if( rc==SQLITE_NOMEM ) shell_check_oom(0); \
+ else if(!(rc==SQLITE_OK||rc==SQLITE_DONE)) \
+ fprintf(stderr,"E:%d\n",rc), assert(0)
+#else
+static void rc_err_oom_die(int rc){
+ if( rc==SQLITE_NOMEM ) shell_check_oom(0);
+ assert(rc==SQLITE_OK||rc==SQLITE_DONE);
+}
+#endif
+
+#ifdef SHELL_COLFIX_DB /* If this is set, the DB can be in a file. */
+static char zCOL_DB[] = SHELL_STRINGIFY(SHELL_COLFIX_DB);
+#else /* Otherwise, memory is faster/better for the transient DB. */
+static const char *zCOL_DB = ":memory:";
+#endif
+
+/* Define character (as C string) to separate generated column ordinal
+ * from protected part of incoming column names. This defaults to "_"
+ * so that incoming column identifiers that did not need not be quoted
+ * remain usable without being quoted. It must be one character.
+ */
+#ifndef SHELL_AUTOCOLUMN_SEP
+# define AUTOCOLUMN_SEP "_"
+#else
+# define AUTOCOLUMN_SEP SHELL_STRINGIFY(SHELL_AUTOCOLUMN_SEP)
+#endif
+
+static char *zAutoColumn(const char *zColNew, sqlite3 **pDb, char **pzRenamed){
+ /* Queries and D{D,M}L used here */
+ static const char * const zTabMake = "\
+CREATE TABLE ColNames(\
+ cpos INTEGER PRIMARY KEY,\
+ name TEXT, nlen INT, chop INT, reps INT, suff TEXT);\
+CREATE VIEW RepeatedNames AS \
+SELECT DISTINCT t.name FROM ColNames t \
+WHERE t.name COLLATE NOCASE IN (\
+ SELECT o.name FROM ColNames o WHERE o.cpos<>t.cpos\
+);\
+";
+ static const char * const zTabFill = "\
+INSERT INTO ColNames(name,nlen,chop,reps,suff)\
+ VALUES(iif(length(?1)>0,?1,'?'),max(length(?1),1),0,0,'')\
+";
+ static const char * const zHasDupes = "\
+SELECT count(DISTINCT (substring(name,1,nlen-chop)||suff) COLLATE NOCASE)\
+ <count(name) FROM ColNames\
+";
+#ifdef SHELL_COLUMN_RENAME_CLEAN
+ static const char * const zDedoctor = "\
+UPDATE ColNames SET chop=iif(\
+ (substring(name,nlen,1) BETWEEN '0' AND '9')\
+ AND (rtrim(name,'0123456790') glob '*"AUTOCOLUMN_SEP"'),\
+ nlen-length(rtrim(name, '"AUTOCOLUMN_SEP"0123456789')),\
+ 0\
+)\
+";
+#endif
+ static const char * const zSetReps = "\
+UPDATE ColNames AS t SET reps=\
+(SELECT count(*) FROM ColNames d \
+ WHERE substring(t.name,1,t.nlen-t.chop)=substring(d.name,1,d.nlen-d.chop)\
+ COLLATE NOCASE\
+)\
+";
+#ifdef SQLITE_ENABLE_MATH_FUNCTIONS
+ static const char * const zColDigits = "\
+SELECT CAST(ceil(log(count(*)+0.5)) AS INT) FROM ColNames \
+";
+#else
+ /* Counting on SQLITE_MAX_COLUMN < 100,000 here. (32767 is the hard limit.) */
+ static const char * const zColDigits = "\
+SELECT CASE WHEN (nc < 10) THEN 1 WHEN (nc < 100) THEN 2 \
+ WHEN (nc < 1000) THEN 3 WHEN (nc < 10000) THEN 4 \
+ ELSE 5 FROM (SELECT count(*) AS nc FROM ColNames) \
+";
+#endif
+ static const char * const zRenameRank =
+#ifdef SHELL_COLUMN_RENAME_CLEAN
+ "UPDATE ColNames AS t SET suff="
+ "iif(reps>1, printf('%c%0*d', '"AUTOCOLUMN_SEP"', $1, cpos), '')"
+#else /* ...RENAME_MINIMAL_ONE_PASS */
+"WITH Lzn(nlz) AS (" /* Find minimum extraneous leading 0's for uniqueness */
+" SELECT 0 AS nlz"
+" UNION"
+" SELECT nlz+1 AS nlz FROM Lzn"
+" WHERE EXISTS("
+" SELECT 1"
+" FROM ColNames t, ColNames o"
+" WHERE"
+" iif(t.name IN (SELECT * FROM RepeatedNames),"
+" printf('%s"AUTOCOLUMN_SEP"%s',"
+" t.name, substring(printf('%.*c%0.*d',nlz+1,'0',$1,t.cpos),2)),"
+" t.name"
+" )"
+" ="
+" iif(o.name IN (SELECT * FROM RepeatedNames),"
+" printf('%s"AUTOCOLUMN_SEP"%s',"
+" o.name, substring(printf('%.*c%0.*d',nlz+1,'0',$1,o.cpos),2)),"
+" o.name"
+" )"
+" COLLATE NOCASE"
+" AND o.cpos<>t.cpos"
+" GROUP BY t.cpos"
+" )"
+") UPDATE Colnames AS t SET"
+" chop = 0," /* No chopping, never touch incoming names. */
+" suff = iif(name IN (SELECT * FROM RepeatedNames),"
+" printf('"AUTOCOLUMN_SEP"%s', substring("
+" printf('%.*c%0.*d',(SELECT max(nlz) FROM Lzn)+1,'0',1,t.cpos),2)),"
+" ''"
+" )"
+#endif
+ ;
+ static const char * const zCollectVar = "\
+SELECT\
+ '('||x'0a'\
+ || group_concat(\
+ cname||' TEXT',\
+ ','||iif((cpos-1)%4>0, ' ', x'0a'||' '))\
+ ||')' AS ColsSpec \
+FROM (\
+ SELECT cpos, printf('\"%w\"',printf('%!.*s%s', nlen-chop,name,suff)) AS cname \
+ FROM ColNames ORDER BY cpos\
+)";
+ static const char * const zRenamesDone =
+ "SELECT group_concat("
+ " printf('\"%w\" to \"%w\"',name,printf('%!.*s%s', nlen-chop, name, suff)),"
+ " ','||x'0a')"
+ "FROM ColNames WHERE suff<>'' OR chop!=0"
+ ;
+ int rc;
+ sqlite3_stmt *pStmt = 0;
+ assert(pDb!=0);
+ if( zColNew ){
+ /* Add initial or additional column. Init db if necessary. */
+ if( *pDb==0 ){
+ if( SQLITE_OK!=sqlite3_open(zCOL_DB, pDb) ) return 0;
+#ifdef SHELL_COLFIX_DB
+ if(*zCOL_DB!=':')
+ sqlite3_exec(*pDb,"drop table if exists ColNames;"
+ "drop view if exists RepeatedNames;",0,0,0);
+#endif
+ rc = sqlite3_exec(*pDb, zTabMake, 0, 0, 0);
+ rc_err_oom_die(rc);
+ }
+ assert(*pDb!=0);
+ rc = sqlite3_prepare_v2(*pDb, zTabFill, -1, &pStmt, 0);
+ rc_err_oom_die(rc);
+ rc = sqlite3_bind_text(pStmt, 1, zColNew, -1, 0);
+ rc_err_oom_die(rc);
+ rc = sqlite3_step(pStmt);
+ rc_err_oom_die(rc);
+ sqlite3_finalize(pStmt);
+ return 0;
+ }else if( *pDb==0 ){
+ return 0;
+ }else{
+ /* Formulate the columns spec, close the DB, zero *pDb. */
+ char *zColsSpec = 0;
+ int hasDupes = db_int(*pDb, zHasDupes);
+ int nDigits = (hasDupes)? db_int(*pDb, zColDigits) : 0;
+ if( hasDupes ){
+#ifdef SHELL_COLUMN_RENAME_CLEAN
+ rc = sqlite3_exec(*pDb, zDedoctor, 0, 0, 0);
+ rc_err_oom_die(rc);
+#endif
+ rc = sqlite3_exec(*pDb, zSetReps, 0, 0, 0);
+ rc_err_oom_die(rc);
+ rc = sqlite3_prepare_v2(*pDb, zRenameRank, -1, &pStmt, 0);
+ rc_err_oom_die(rc);
+ sqlite3_bind_int(pStmt, 1, nDigits);
+ rc = sqlite3_step(pStmt);
+ sqlite3_finalize(pStmt);
+ assert(rc==SQLITE_DONE);
+ }
+ assert(db_int(*pDb, zHasDupes)==0); /* Consider: remove this */
+ rc = sqlite3_prepare_v2(*pDb, zCollectVar, -1, &pStmt, 0);
+ rc_err_oom_die(rc);
+ rc = sqlite3_step(pStmt);
+ if( rc==SQLITE_ROW ){
+ zColsSpec = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
+ }else{
+ zColsSpec = 0;
+ }
+ if( pzRenamed!=0 ){
+ if( !hasDupes ) *pzRenamed = 0;
+ else{
+ sqlite3_finalize(pStmt);
+ if( SQLITE_OK==sqlite3_prepare_v2(*pDb, zRenamesDone, -1, &pStmt, 0)
+ && SQLITE_ROW==sqlite3_step(pStmt) ){
+ *pzRenamed = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
+ }else
+ *pzRenamed = 0;
+ }
+ }
+ sqlite3_finalize(pStmt);
+ sqlite3_close(*pDb);
+ *pDb = 0;
+ return zColsSpec;
+ }
+}
+
+/*
** If an input line begins with "." then invoke this routine to
** process that line.
**
@@ -17595,19 +19265,24 @@ static int do_meta_command(char *zLine, ShellState *p){
open_db(p, 0);
if( booleanValue(azArg[1]) ){
sqlite3_set_authorizer(p->db, shellAuth, p);
+ }else if( p->bSafeModePersist ){
+ sqlite3_set_authorizer(p->db, safeModeAuth, p);
}else{
sqlite3_set_authorizer(p->db, 0, 0);
}
}else
#endif
-#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) \
+ && !defined(SQLITE_SHELL_WASM_MODE)
if( c=='a' && strncmp(azArg[0], "archive", n)==0 ){
open_db(p, 0);
+ failIfSafeMode(p, "cannot run .archive in safe mode");
rc = arDotCommand(p, 0, azArg, nArg);
}else
#endif
+#ifndef SQLITE_SHELL_WASM_MODE
if( (c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0)
|| (c=='s' && n>=3 && strncmp(azArg[0], "save", n)==0)
){
@@ -17618,6 +19293,7 @@ static int do_meta_command(char *zLine, ShellState *p){
int j;
int bAsync = 0;
const char *zVfs = 0;
+ failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]);
for(j=1; j<nArg; j++){
const char *z = azArg[j];
if( z[0]=='-' ){
@@ -17675,6 +19351,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
close_db(pDest);
}else
+#endif /* !defined(SQLITE_SHELL_WASM_MODE) */
if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 ){
if( nArg==2 ){
@@ -17698,7 +19375,16 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else
+ /* The undocumented ".breakpoint" command causes a call to the no-op
+ ** routine named test_breakpoint().
+ */
+ if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){
+ test_breakpoint();
+ }else
+
+#ifndef SQLITE_SHELL_WASM_MODE
if( c=='c' && strcmp(azArg[0],"cd")==0 ){
+ failIfSafeMode(p, "cannot run .cd in safe mode");
if( nArg==2 ){
#if defined(_WIN32) || defined(WIN32)
wchar_t *z = sqlite3_win32_utf8_to_unicode(azArg[1]);
@@ -17716,13 +19402,7 @@ static int do_meta_command(char *zLine, ShellState *p){
rc = 1;
}
}else
-
- /* The undocumented ".breakpoint" command causes a call to the no-op
- ** routine named test_breakpoint().
- */
- if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){
- test_breakpoint();
- }else
+#endif /* !defined(SQLITE_SHELL_WASM_MODE) */
if( c=='c' && n>=3 && strncmp(azArg[0], "changes", n)==0 ){
if( nArg==2 ){
@@ -17733,6 +19413,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else
+#ifndef SQLITE_SHELL_WASM_MODE
/* Cancel output redirection, if it is currently set (by .testcase)
** Then read the content of the testcase-out.txt file and compare against
** azArg[1]. If there are differences, report an error and exit.
@@ -17757,8 +19438,11 @@ static int do_meta_command(char *zLine, ShellState *p){
}
sqlite3_free(zRes);
}else
+#endif /* !defined(SQLITE_SHELL_WASM_MODE) */
+#ifndef SQLITE_SHELL_WASM_MODE
if( c=='c' && strncmp(azArg[0], "clone", n)==0 ){
+ failIfSafeMode(p, "cannot run .clone in safe mode");
if( nArg==2 ){
tryToClone(p, azArg[1]);
}else{
@@ -17766,6 +19450,53 @@ static int do_meta_command(char *zLine, ShellState *p){
rc = 1;
}
}else
+#endif /* !defined(SQLITE_SHELL_WASM_MODE) */
+
+ if( c=='c' && strncmp(azArg[0], "connection", n)==0 ){
+ if( nArg==1 ){
+ /* List available connections */
+ int i;
+ for(i=0; i<ArraySize(p->aAuxDb); i++){
+ const char *zFile = p->aAuxDb[i].zDbFilename;
+ if( p->aAuxDb[i].db==0 && p->pAuxDb!=&p->aAuxDb[i] ){
+ zFile = "(not open)";
+ }else if( zFile==0 ){
+ zFile = "(memory)";
+ }else if( zFile[0]==0 ){
+ zFile = "(temporary-file)";
+ }
+ if( p->pAuxDb == &p->aAuxDb[i] ){
+ utf8_printf(stdout, "ACTIVE %d: %s\n", i, zFile);
+ }else if( p->aAuxDb[i].db!=0 ){
+ utf8_printf(stdout, " %d: %s\n", i, zFile);
+ }
+ }
+ }else if( nArg==2 && IsDigit(azArg[1][0]) && azArg[1][1]==0 ){
+ int i = azArg[1][0] - '0';
+ if( p->pAuxDb != &p->aAuxDb[i] && i>=0 && i<ArraySize(p->aAuxDb) ){
+ p->pAuxDb->db = p->db;
+ p->pAuxDb = &p->aAuxDb[i];
+ globalDb = p->db = p->pAuxDb->db;
+ p->pAuxDb->db = 0;
+ }
+ }else if( nArg==3 && strcmp(azArg[1], "close")==0
+ && IsDigit(azArg[2][0]) && azArg[2][1]==0 ){
+ int i = azArg[2][0] - '0';
+ if( i<0 || i>=ArraySize(p->aAuxDb) ){
+ /* No-op */
+ }else if( p->pAuxDb == &p->aAuxDb[i] ){
+ raw_printf(stderr, "cannot close the active database connection\n");
+ rc = 1;
+ }else if( p->aAuxDb[i].db ){
+ session_close_all(p, i);
+ close_db(p->aAuxDb[i].db);
+ p->aAuxDb[i].db = 0;
+ }
+ }else{
+ raw_printf(stderr, "Usage: .connection [close] [CONNECTION-NUMBER]\n");
+ rc = 1;
+ }
+ }else
if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 ){
char **azName = 0;
@@ -17781,8 +19512,9 @@ static int do_meta_command(char *zLine, ShellState *p){
while( sqlite3_step(pStmt)==SQLITE_ROW ){
const char *zSchema = (const char *)sqlite3_column_text(pStmt,1);
const char *zFile = (const char*)sqlite3_column_text(pStmt,2);
+ if( zSchema==0 || zFile==0 ) continue;
azName = sqlite3_realloc(azName, (nName+1)*2*sizeof(char*));
- if( azName==0 ){ shell_out_of_memory(); /* Does not return */ }
+ shell_check_oom(azName);
azName[nName*2] = strdup(zSchema);
azName[nName*2+1] = strdup(zFile);
nName++;
@@ -17844,11 +19576,11 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
if( c=='d' && n>=3 && strncmp(azArg[0], "dbinfo", n)==0 ){
rc = shell_dbinfo_command(p, nArg, azArg);
}else
-#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
if( c=='r' && strncmp(azArg[0], "recover", n)==0 ){
open_db(p, 0);
rc = recoverDatabaseCmd(p, nArg, azArg);
@@ -17861,7 +19593,7 @@ static int do_meta_command(char *zLine, ShellState *p){
int i;
int savedShowHeader = p->showHeader;
int savedShellFlags = p->shellFlgs;
- ShellClearFlag(p,
+ ShellClearFlag(p,
SHFLG_PreserveRowid|SHFLG_Newlines|SHFLG_Echo
|SHFLG_DumpDataOnly|SHFLG_DumpNoSys);
for(i=1; i<nArg; i++){
@@ -17894,11 +19626,26 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_free(zLike);
goto meta_command_exit;
}
- }else if( zLike ){
- zLike = sqlite3_mprintf("%z OR name LIKE %Q ESCAPE '\\'",
- zLike, azArg[i]);
}else{
- zLike = sqlite3_mprintf("name LIKE %Q ESCAPE '\\'", azArg[i]);
+ /* azArg[i] contains a LIKE pattern. This ".dump" request should
+ ** only dump data for tables for which either the table name matches
+ ** the LIKE pattern, or the table appears to be a shadow table of
+ ** a virtual table for which the name matches the LIKE pattern.
+ */
+ char *zExpr = sqlite3_mprintf(
+ "name LIKE %Q ESCAPE '\\' OR EXISTS ("
+ " SELECT 1 FROM sqlite_schema WHERE "
+ " name LIKE %Q ESCAPE '\\' AND"
+ " sql LIKE 'CREATE VIRTUAL TABLE%%' AND"
+ " substr(o.name, 1, length(name)+1) == (name||'_')"
+ ")", azArg[i], azArg[i]
+ );
+
+ if( zLike ){
+ zLike = sqlite3_mprintf("%z OR %z", zLike, zExpr);
+ }else{
+ zLike = zExpr;
+ }
}
}
@@ -17920,7 +19667,7 @@ static int do_meta_command(char *zLine, ShellState *p){
p->nErr = 0;
if( zLike==0 ) zLike = sqlite3_mprintf("true");
zSql = sqlite3_mprintf(
- "SELECT name, type, sql FROM sqlite_schema "
+ "SELECT name, type, sql FROM sqlite_schema AS o "
"WHERE (%s) AND type=='table'"
" AND sql NOT NULL"
" ORDER BY tbl_name='sqlite_sequence', rowid",
@@ -17930,7 +19677,7 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_free(zSql);
if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){
zSql = sqlite3_mprintf(
- "SELECT sql FROM sqlite_schema "
+ "SELECT sql FROM sqlite_schema AS o "
"WHERE (%s) AND sql NOT NULL"
" AND type IN ('index','trigger','view')",
zLike
@@ -17992,10 +19739,12 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else
+#ifndef SQLITE_SHELL_WASM_MODE
if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){
if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) exit(rc);
rc = 2;
}else
+#endif
/* The ".explain" command is automatic now. It is largely pointless. It
** retained purely for backwards compatibility */
@@ -18023,8 +19772,15 @@ static int do_meta_command(char *zLine, ShellState *p){
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( c=='e' && strncmp(azArg[0], "expert", n)==0 ){
- open_db(p, 0);
- expertDotCommand(p, azArg, nArg);
+ if( p->bSafeMode ){
+ raw_printf(stderr,
+ "Cannot run experimental commands such as \"%s\" in safe mode\n",
+ azArg[0]);
+ rc = 1;
+ }else{
+ open_db(p, 0);
+ expertDotCommand(p, azArg, nArg);
+ }
}else
#endif
@@ -18177,7 +19933,6 @@ static int do_meta_command(char *zLine, ShellState *p){
if( c=='f' && strncmp(azArg[0], "fullschema", n)==0 ){
ShellState data;
- char *zErrMsg = 0;
int doStats = 0;
memcpy(&data, p, sizeof(data));
data.showHeader = 0;
@@ -18198,8 +19953,8 @@ static int do_meta_command(char *zLine, ShellState *p){
" FROM sqlite_schema UNION ALL"
" SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_schema) "
"WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%' "
- "ORDER BY rowid",
- callback, &data, &zErrMsg
+ "ORDER BY x",
+ callback, &data, 0
);
if( rc==SQLITE_OK ){
sqlite3_stmt *pStmt;
@@ -18214,13 +19969,11 @@ static int do_meta_command(char *zLine, ShellState *p){
raw_printf(p->out, "/* No STAT tables available */\n");
}else{
raw_printf(p->out, "ANALYZE sqlite_schema;\n");
- sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_schema'",
- callback, &data, &zErrMsg);
data.cMode = data.mode = MODE_Insert;
data.zDestTable = "sqlite_stat1";
- shell_exec(&data, "SELECT * FROM sqlite_stat1", &zErrMsg);
+ shell_exec(&data, "SELECT * FROM sqlite_stat1", 0);
data.zDestTable = "sqlite_stat4";
- shell_exec(&data, "SELECT * FROM sqlite_stat4", &zErrMsg);
+ shell_exec(&data, "SELECT * FROM sqlite_stat4", 0);
raw_printf(p->out, "ANALYZE sqlite_schema;\n");
}
}else
@@ -18246,8 +19999,10 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else
+#ifndef SQLITE_SHELL_WASM_MODE
if( c=='i' && strncmp(azArg[0], "import", n)==0 ){
char *zTable = 0; /* Insert data into this table */
+ char *zSchema = 0; /* within this schema (may default to "main") */
char *zFile = 0; /* Name of file to extra content from */
sqlite3_stmt *pStmt = NULL; /* A statement */
int nCol; /* Number of columns in the table */
@@ -18256,18 +20011,22 @@ static int do_meta_command(char *zLine, ShellState *p){
int needCommit; /* True to COMMIT or ROLLBACK at end */
int nSep; /* Number of bytes in p->colSeparator[] */
char *zSql; /* An SQL statement */
+ char *zFullTabName; /* Table name with schema if applicable */
ImportCtx sCtx; /* Reader context */
char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */
int eVerbose = 0; /* Larger for more console output */
int nSkip = 0; /* Initial lines to skip */
int useOutputMode = 1; /* Use output mode to determine separators */
+ char *zCreate = 0; /* CREATE TABLE statement text */
+ failIfSafeMode(p, "cannot run .import in safe mode");
memset(&sCtx, 0, sizeof(sCtx));
if( p->mode==MODE_Ascii ){
xRead = ascii_read_one_field;
}else{
xRead = csv_read_one_field;
}
+ rc = 1;
for(i=1; i<nArg; i++){
char *z = azArg[i];
if( z[0]=='-' && z[1]=='-' ) z++;
@@ -18279,11 +20038,12 @@ static int do_meta_command(char *zLine, ShellState *p){
}else{
utf8_printf(p->out, "ERROR: extra argument: \"%s\". Usage:\n", z);
showHelp(p->out, "import");
- rc = 1;
goto meta_command_exit;
}
}else if( strcmp(z,"-v")==0 ){
eVerbose++;
+ }else if( strcmp(z,"-schema")==0 && i<nArg-1 ){
+ zSchema = azArg[++i];
}else if( strcmp(z,"-skip")==0 && i<nArg-1 ){
nSkip = integerValue(azArg[++i]);
}else if( strcmp(z,"-ascii")==0 ){
@@ -18299,7 +20059,6 @@ static int do_meta_command(char *zLine, ShellState *p){
}else{
utf8_printf(p->out, "ERROR: unknown option: \"%s\". Usage:\n", z);
showHelp(p->out, "import");
- rc = 1;
goto meta_command_exit;
}
}
@@ -18307,7 +20066,6 @@ static int do_meta_command(char *zLine, ShellState *p){
utf8_printf(p->out, "ERROR: missing %s argument. Usage:\n",
zFile==0 ? "FILE" : "TABLE");
showHelp(p->out, "import");
- rc = 1;
goto meta_command_exit;
}
seenInterrupt = 0;
@@ -18319,21 +20077,18 @@ static int do_meta_command(char *zLine, ShellState *p){
if( nSep==0 ){
raw_printf(stderr,
"Error: non-null column separator required for import\n");
- rc = 1;
goto meta_command_exit;
}
if( nSep>1 ){
- raw_printf(stderr,
+ raw_printf(stderr,
"Error: multi-character column separators not allowed"
" for import\n");
- rc = 1;
goto meta_command_exit;
}
nSep = strlen30(p->rowSeparator);
if( nSep==0 ){
raw_printf(stderr,
"Error: non-null row separator required for import\n");
- rc = 1;
goto meta_command_exit;
}
if( nSep==2 && p->mode==MODE_Csv && strcmp(p->rowSeparator,SEP_CrLf)==0 ){
@@ -18347,7 +20102,6 @@ static int do_meta_command(char *zLine, ShellState *p){
if( nSep>1 ){
raw_printf(stderr, "Error: multi-character row separators not allowed"
" for import\n");
- rc = 1;
goto meta_command_exit;
}
sCtx.cColSep = p->colSeparator[0];
@@ -18358,7 +20112,6 @@ static int do_meta_command(char *zLine, ShellState *p){
if( sCtx.zFile[0]=='|' ){
#ifdef SQLITE_OMIT_POPEN
raw_printf(stderr, "Error: pipes are not supported in this OS\n");
- rc = 1;
goto meta_command_exit;
#else
sCtx.in = popen(sCtx.zFile+1, "r");
@@ -18371,7 +20124,6 @@ static int do_meta_command(char *zLine, ShellState *p){
}
if( sCtx.in==0 ){
utf8_printf(stderr, "Error: cannot open \"%s\"\n", zFile);
- rc = 1;
goto meta_command_exit;
}
if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){
@@ -18385,11 +20137,22 @@ static int do_meta_command(char *zLine, ShellState *p){
output_c_string(p->out, zSep);
utf8_printf(p->out, "\n");
}
+ sCtx.z = sqlite3_malloc64(120);
+ if( sCtx.z==0 ){
+ import_cleanup(&sCtx);
+ shell_out_of_memory();
+ }
+ /* Below, resources must be freed before exit. */
while( (nSkip--)>0 ){
while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){}
}
- zSql = sqlite3_mprintf("SELECT * FROM \"%w\"", zTable);
- if( zSql==0 ){
+ if( zSchema!=0 ){
+ zFullTabName = sqlite3_mprintf("\"%w\".\"%w\"", zSchema, zTable);
+ }else{
+ zFullTabName = sqlite3_mprintf("\"%w\"", zTable);
+ }
+ zSql = sqlite3_mprintf("SELECT * FROM %s", zFullTabName);
+ if( zSql==0 || zFullTabName==0 ){
import_cleanup(&sCtx);
shell_out_of_memory();
}
@@ -18397,43 +20160,51 @@ static int do_meta_command(char *zLine, ShellState *p){
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */
if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(p->db))==0 ){
- char *zCreate = sqlite3_mprintf("CREATE TABLE \"%w\"", zTable);
- char cSep = '(';
+ sqlite3 *dbCols = 0;
+ char *zRenames = 0;
+ char *zColDefs;
+ zCreate = sqlite3_mprintf("CREATE TABLE %s", zFullTabName);
while( xRead(&sCtx) ){
- zCreate = sqlite3_mprintf("%z%c\n \"%w\" TEXT", zCreate, cSep, sCtx.z);
- cSep = ',';
+ zAutoColumn(sCtx.z, &dbCols, 0);
if( sCtx.cTerm!=sCtx.cColSep ) break;
}
- if( cSep=='(' ){
+ zColDefs = zAutoColumn(0, &dbCols, &zRenames);
+ if( zRenames!=0 ){
+ utf8_printf((stdin_is_interactive && p->in==stdin)? p->out : stderr,
+ "Columns renamed during .import %s due to duplicates:\n"
+ "%s\n", sCtx.zFile, zRenames);
+ sqlite3_free(zRenames);
+ }
+ assert(dbCols==0);
+ if( zColDefs==0 ){
+ utf8_printf(stderr,"%s: empty file\n", sCtx.zFile);
+ import_fail:
sqlite3_free(zCreate);
+ sqlite3_free(zSql);
+ sqlite3_free(zFullTabName);
import_cleanup(&sCtx);
- utf8_printf(stderr,"%s: empty file\n", sCtx.zFile);
rc = 1;
goto meta_command_exit;
}
- zCreate = sqlite3_mprintf("%z\n)", zCreate);
+ zCreate = sqlite3_mprintf("%z%z\n", zCreate, zColDefs);
if( eVerbose>=1 ){
utf8_printf(p->out, "%s\n", zCreate);
}
rc = sqlite3_exec(p->db, zCreate, 0, 0, 0);
- sqlite3_free(zCreate);
if( rc ){
- utf8_printf(stderr, "CREATE TABLE \"%s\"(...) failed: %s\n", zTable,
- sqlite3_errmsg(p->db));
- import_cleanup(&sCtx);
- rc = 1;
- goto meta_command_exit;
+ utf8_printf(stderr, "%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db));
+ goto import_fail;
}
+ sqlite3_free(zCreate);
+ zCreate = 0;
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
}
- sqlite3_free(zSql);
if( rc ){
if (pStmt) sqlite3_finalize(pStmt);
utf8_printf(stderr,"Error: %s\n", sqlite3_errmsg(p->db));
- import_cleanup(&sCtx);
- rc = 1;
- goto meta_command_exit;
+ goto import_fail;
}
+ sqlite3_free(zSql);
nCol = sqlite3_column_count(pStmt);
sqlite3_finalize(pStmt);
pStmt = 0;
@@ -18443,7 +20214,7 @@ static int do_meta_command(char *zLine, ShellState *p){
import_cleanup(&sCtx);
shell_out_of_memory();
}
- sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable);
+ sqlite3_snprintf(nByte+20, zSql, "INSERT INTO %s VALUES(?", zFullTabName);
j = strlen30(zSql);
for(i=1; i<nCol; i++){
zSql[j++] = ',';
@@ -18455,14 +20226,13 @@ static int do_meta_command(char *zLine, ShellState *p){
utf8_printf(p->out, "Insert using: %s\n", zSql);
}
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
- sqlite3_free(zSql);
if( rc ){
utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
if (pStmt) sqlite3_finalize(pStmt);
- import_cleanup(&sCtx);
- rc = 1;
- goto meta_command_exit;
+ goto import_fail;
}
+ sqlite3_free(zSql);
+ sqlite3_free(zFullTabName);
needCommit = sqlite3_get_autocommit(p->db);
if( needCommit ) sqlite3_exec(p->db, "BEGIN", 0, 0, 0);
do{
@@ -18520,6 +20290,7 @@ static int do_meta_command(char *zLine, ShellState *p){
sCtx.nRow, sCtx.nErr, sCtx.nLine-1);
}
}else
+#endif /* !defined(SQLITE_SHELL_WASM_MODE) */
#ifndef SQLITE_UNTESTABLE
if( c=='i' && strncmp(azArg[0], "imposter", n)==0 ){
@@ -18568,7 +20339,7 @@ static int do_meta_command(char *zLine, ShellState *p){
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
i = 0;
- while( sqlite3_step(pStmt)==SQLITE_ROW ){
+ while( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
char zLabel[20];
const char *zCol = (const char*)sqlite3_column_text(pStmt,2);
i++;
@@ -18709,10 +20480,11 @@ static int do_meta_command(char *zLine, ShellState *p){
lintDotCommand(p, azArg, nArg);
}else
-#ifndef SQLITE_OMIT_LOAD_EXTENSION
+#if !defined(SQLITE_OMIT_LOAD_EXTENSION) && !defined(SQLITE_SHELL_WASM_MODE)
if( c=='l' && strncmp(azArg[0], "load", n)==0 ){
const char *zFile, *zProc;
char *zErrMsg = 0;
+ failIfSafeMode(p, "cannot run .load in safe mode");
if( nArg<2 ){
raw_printf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n");
rc = 1;
@@ -18730,7 +20502,9 @@ static int do_meta_command(char *zLine, ShellState *p){
}else
#endif
+#ifndef SQLITE_SHELL_WASM_MODE
if( c=='l' && strncmp(azArg[0], "log", n)==0 ){
+ failIfSafeMode(p, "cannot run .log in safe mode");
if( nArg!=2 ){
raw_printf(stderr, "Usage: .log FILENAME\n");
rc = 1;
@@ -18740,67 +20514,148 @@ static int do_meta_command(char *zLine, ShellState *p){
p->pLog = output_file_open(zFile, 0);
}
}else
+#endif
if( c=='m' && strncmp(azArg[0], "mode", n)==0 ){
- const char *zMode = nArg>=2 ? azArg[1] : "";
- int n2 = strlen30(zMode);
- int c2 = zMode[0];
- if( c2=='l' && n2>2 && strncmp(azArg[1],"lines",n2)==0 ){
+ const char *zMode = 0;
+ const char *zTabname = 0;
+ int i, n2;
+ ColModeOpts cmOpts = ColModeOpts_default;
+ for(i=1; i<nArg; i++){
+ const char *z = azArg[i];
+ if( optionMatch(z,"wrap") && i+1<nArg ){
+ cmOpts.iWrap = integerValue(azArg[++i]);
+ }else if( optionMatch(z,"ww") ){
+ cmOpts.bWordWrap = 1;
+ }else if( optionMatch(z,"wordwrap") && i+1<nArg ){
+ cmOpts.bWordWrap = (u8)booleanValue(azArg[++i]);
+ }else if( optionMatch(z,"quote") ){
+ cmOpts.bQuote = 1;
+ }else if( optionMatch(z,"noquote") ){
+ cmOpts.bQuote = 0;
+ }else if( zMode==0 ){
+ zMode = z;
+ /* Apply defaults for qbox pseudo-mods. If that
+ * overwrites already-set values, user was informed of this.
+ */
+ if( strcmp(z, "qbox")==0 ){
+ ColModeOpts cmo = ColModeOpts_default_qbox;
+ zMode = "box";
+ cmOpts = cmo;
+ }
+ }else if( zTabname==0 ){
+ zTabname = z;
+ }else if( z[0]=='-' ){
+ utf8_printf(stderr, "unknown option: %s\n", z);
+ utf8_printf(stderr, "options:\n"
+ " --noquote\n"
+ " --quote\n"
+ " --wordwrap on/off\n"
+ " --wrap N\n"
+ " --ww\n");
+ rc = 1;
+ goto meta_command_exit;
+ }else{
+ utf8_printf(stderr, "extra argument: \"%s\"\n", z);
+ rc = 1;
+ goto meta_command_exit;
+ }
+ }
+ if( zMode==0 ){
+ if( p->mode==MODE_Column
+ || (p->mode>=MODE_Markdown && p->mode<=MODE_Box)
+ ){
+ raw_printf
+ (p->out,
+ "current output mode: %s --wrap %d --wordwrap %s --%squote\n",
+ modeDescr[p->mode], p->cmOpts.iWrap,
+ p->cmOpts.bWordWrap ? "on" : "off",
+ p->cmOpts.bQuote ? "" : "no");
+ }else{
+ raw_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]);
+ }
+ zMode = modeDescr[p->mode];
+ }
+ n2 = strlen30(zMode);
+ if( strncmp(zMode,"lines",n2)==0 ){
p->mode = MODE_Line;
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
- }else if( c2=='c' && strncmp(azArg[1],"columns",n2)==0 ){
+ }else if( strncmp(zMode,"columns",n2)==0 ){
p->mode = MODE_Column;
if( (p->shellFlgs & SHFLG_HeaderSet)==0 ){
p->showHeader = 1;
}
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
- }else if( c2=='l' && n2>2 && strncmp(azArg[1],"list",n2)==0 ){
+ p->cmOpts = cmOpts;
+ }else if( strncmp(zMode,"list",n2)==0 ){
p->mode = MODE_List;
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Column);
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
- }else if( c2=='h' && strncmp(azArg[1],"html",n2)==0 ){
+ }else if( strncmp(zMode,"html",n2)==0 ){
p->mode = MODE_Html;
- }else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){
+ }else if( strncmp(zMode,"tcl",n2)==0 ){
p->mode = MODE_Tcl;
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Space);
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
- }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){
+ }else if( strncmp(zMode,"csv",n2)==0 ){
p->mode = MODE_Csv;
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma);
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf);
- }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){
+ }else if( strncmp(zMode,"tabs",n2)==0 ){
p->mode = MODE_List;
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab);
- }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){
+ }else if( strncmp(zMode,"insert",n2)==0 ){
p->mode = MODE_Insert;
- set_table_name(p, nArg>=3 ? azArg[2] : "table");
- }else if( c2=='q' && strncmp(azArg[1],"quote",n2)==0 ){
+ set_table_name(p, zTabname ? zTabname : "table");
+ }else if( strncmp(zMode,"quote",n2)==0 ){
p->mode = MODE_Quote;
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma);
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
- }else if( c2=='a' && strncmp(azArg[1],"ascii",n2)==0 ){
+ }else if( strncmp(zMode,"ascii",n2)==0 ){
p->mode = MODE_Ascii;
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit);
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record);
- }else if( c2=='m' && strncmp(azArg[1],"markdown",n2)==0 ){
+ }else if( strncmp(zMode,"markdown",n2)==0 ){
p->mode = MODE_Markdown;
- }else if( c2=='t' && strncmp(azArg[1],"table",n2)==0 ){
+ p->cmOpts = cmOpts;
+ }else if( strncmp(zMode,"table",n2)==0 ){
p->mode = MODE_Table;
- }else if( c2=='b' && strncmp(azArg[1],"box",n2)==0 ){
+ p->cmOpts = cmOpts;
+ }else if( strncmp(zMode,"box",n2)==0 ){
p->mode = MODE_Box;
- }else if( c2=='j' && strncmp(azArg[1],"json",n2)==0 ){
+ p->cmOpts = cmOpts;
+ }else if( strncmp(zMode,"count",n2)==0 ){
+ p->mode = MODE_Count;
+ }else if( strncmp(zMode,"off",n2)==0 ){
+ p->mode = MODE_Off;
+ }else if( strncmp(zMode,"json",n2)==0 ){
p->mode = MODE_Json;
- }else if( nArg==1 ){
- raw_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]);
}else{
raw_printf(stderr, "Error: mode should be one of: "
"ascii box column csv html insert json line list markdown "
- "quote table tabs tcl\n");
+ "qbox quote table tabs tcl\n");
rc = 1;
}
p->cMode = p->mode;
}else
+#ifndef SQLITE_SHELL_WASM_MODE
+ if( c=='n' && strcmp(azArg[0], "nonce")==0 ){
+ if( nArg!=2 ){
+ raw_printf(stderr, "Usage: .nonce NONCE\n");
+ rc = 1;
+ }else if( p->zNonce==0 || strcmp(azArg[1],p->zNonce)!=0 ){
+ raw_printf(stderr, "line %d: incorrect nonce: \"%s\"\n",
+ p->lineno, azArg[1]);
+ exit(1);
+ }else{
+ p->bSafeMode = 0;
+ return 0; /* Return immediately to bypass the safe mode reset
+ ** at the end of this procedure */
+ }
+ }else
+#endif /* !defined(SQLITE_SHELL_WASM_MODE) */
+
if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){
if( nArg==2 ){
sqlite3_snprintf(sizeof(p->nullValue), p->nullValue,
@@ -18811,102 +20666,100 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else
-#ifdef SQLITE_DEBUG
- if( c=='o' && strcmp(azArg[0],"oom")==0 ){
- int i;
- for(i=1; i<nArg; i++){
- const char *z = azArg[i];
- if( z[0]=='-' && z[1]=='-' ) z++;
- if( strcmp(z,"-repeat")==0 ){
- if( i==nArg-1 ){
- raw_printf(p->out, "missing argument on \"%s\"\n", azArg[i]);
- rc = 1;
- }else{
- oomRepeat = (int)integerValue(azArg[++i]);
- }
- }else if( IsDigit(z[0]) ){
- oomCounter = (int)integerValue(azArg[i]);
- }else{
- raw_printf(p->out, "unknown argument: \"%s\"\n", azArg[i]);
- raw_printf(p->out, "Usage: .oom [--repeat N] [M]\n");
- rc = 1;
- }
- }
- if( rc==0 ){
- raw_printf(p->out, "oomCounter = %d\n", oomCounter);
- raw_printf(p->out, "oomRepeat = %d\n", oomRepeat);
- }
- }else
-#endif /* SQLITE_DEBUG */
-
if( c=='o' && strncmp(azArg[0], "open", n)==0 && n>=2 ){
+ const char *zFN = 0; /* Pointer to constant filename */
char *zNewFilename = 0; /* Name of the database file to open */
int iName = 1; /* Index in azArg[] of the filename */
int newFlag = 0; /* True to delete file before opening */
- /* Close the existing database */
- session_close_all(p);
- close_db(p->db);
- p->db = 0;
- p->zDbFilename = 0;
- sqlite3_free(p->zFreeOnClose);
- p->zFreeOnClose = 0;
- p->openMode = SHELL_OPEN_UNSPEC;
- p->openFlags = 0;
- p->szMax = 0;
+ int openMode = SHELL_OPEN_UNSPEC;
+
/* Check for command-line arguments */
for(iName=1; iName<nArg; iName++){
const char *z = azArg[iName];
+#ifndef SQLITE_SHELL_WASM_MODE
if( optionMatch(z,"new") ){
newFlag = 1;
#ifdef SQLITE_HAVE_ZLIB
}else if( optionMatch(z, "zip") ){
- p->openMode = SHELL_OPEN_ZIPFILE;
+ openMode = SHELL_OPEN_ZIPFILE;
#endif
}else if( optionMatch(z, "append") ){
- p->openMode = SHELL_OPEN_APPENDVFS;
+ openMode = SHELL_OPEN_APPENDVFS;
}else if( optionMatch(z, "readonly") ){
- p->openMode = SHELL_OPEN_READONLY;
+ openMode = SHELL_OPEN_READONLY;
}else if( optionMatch(z, "nofollow") ){
p->openFlags |= SQLITE_OPEN_NOFOLLOW;
-#ifdef SQLITE_ENABLE_DESERIALIZE
+#ifndef SQLITE_OMIT_DESERIALIZE
}else if( optionMatch(z, "deserialize") ){
- p->openMode = SHELL_OPEN_DESERIALIZE;
+ openMode = SHELL_OPEN_DESERIALIZE;
}else if( optionMatch(z, "hexdb") ){
- p->openMode = SHELL_OPEN_HEXDB;
+ openMode = SHELL_OPEN_HEXDB;
}else if( optionMatch(z, "maxsize") && iName+1<nArg ){
p->szMax = integerValue(azArg[++iName]);
-#endif /* SQLITE_ENABLE_DESERIALIZE */
- }else if( z[0]=='-' ){
+#endif /* SQLITE_OMIT_DESERIALIZE */
+ }else
+#endif /* !SQLITE_SHELL_WASM_MODE */
+ if( z[0]=='-' ){
utf8_printf(stderr, "unknown option: %s\n", z);
rc = 1;
goto meta_command_exit;
- }else if( zNewFilename ){
+ }else if( zFN ){
utf8_printf(stderr, "extra argument: \"%s\"\n", z);
rc = 1;
goto meta_command_exit;
}else{
- zNewFilename = sqlite3_mprintf("%s", z);
+ zFN = z;
}
}
+
+ /* Close the existing database */
+ session_close_all(p, -1);
+ close_db(p->db);
+ p->db = 0;
+ p->pAuxDb->zDbFilename = 0;
+ sqlite3_free(p->pAuxDb->zFreeOnClose);
+ p->pAuxDb->zFreeOnClose = 0;
+ p->openMode = openMode;
+ p->openFlags = 0;
+ p->szMax = 0;
+
/* If a filename is specified, try to open it first */
- if( zNewFilename || p->openMode==SHELL_OPEN_HEXDB ){
- if( newFlag ) shellDeleteFile(zNewFilename);
- p->zDbFilename = zNewFilename;
+ if( zFN || p->openMode==SHELL_OPEN_HEXDB ){
+ if( newFlag && zFN && !p->bSafeMode ) shellDeleteFile(zFN);
+#ifndef SQLITE_SHELL_WASM_MODE
+ if( p->bSafeMode
+ && p->openMode!=SHELL_OPEN_HEXDB
+ && zFN
+ && strcmp(zFN,":memory:")!=0
+ ){
+ failIfSafeMode(p, "cannot open disk-based database files in safe mode");
+ }
+#else
+ /* WASM mode has its own sandboxed pseudo-filesystem. */
+#endif
+ if( zFN ){
+ zNewFilename = sqlite3_mprintf("%s", zFN);
+ shell_check_oom(zNewFilename);
+ }else{
+ zNewFilename = 0;
+ }
+ p->pAuxDb->zDbFilename = zNewFilename;
open_db(p, OPEN_DB_KEEPALIVE);
if( p->db==0 ){
utf8_printf(stderr, "Error: cannot open '%s'\n", zNewFilename);
sqlite3_free(zNewFilename);
}else{
- p->zFreeOnClose = zNewFilename;
+ p->pAuxDb->zFreeOnClose = zNewFilename;
}
}
if( p->db==0 ){
/* As a fall-back open a TEMP database */
- p->zDbFilename = 0;
+ p->pAuxDb->zDbFilename = 0;
open_db(p, 0);
}
}else
+#ifndef SQLITE_SHELL_WASM_MODE
if( (c=='o'
&& (strncmp(azArg[0], "output", n)==0||strncmp(azArg[0], "once", n)==0))
|| (c=='e' && n==5 && strcmp(azArg[0],"excel")==0)
@@ -18915,9 +20768,11 @@ static int do_meta_command(char *zLine, ShellState *p){
int bTxtMode = 0;
int i;
int eMode = 0;
- int bBOM = 0;
- int bOnce = 0; /* 0: .output, 1: .once, 2: .excel */
+ int bOnce = 0; /* 0: .output, 1: .once, 2: .excel */
+ unsigned char zBOM[4]; /* Byte-order mark to using if --bom is present */
+ zBOM[0] = 0;
+ failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]);
if( c=='e' ){
eMode = 'x';
bOnce = 2;
@@ -18929,7 +20784,10 @@ static int do_meta_command(char *zLine, ShellState *p){
if( z[0]=='-' ){
if( z[1]=='-' ) z++;
if( strcmp(z,"-bom")==0 ){
- bBOM = 1;
+ zBOM[0] = 0xef;
+ zBOM[1] = 0xbb;
+ zBOM[2] = 0xbf;
+ zBOM[3] = 0;
}else if( c!='e' && strcmp(z,"-x")==0 ){
eMode = 'x'; /* spreadsheet */
}else if( c!='e' && strcmp(z,"-e")==0 ){
@@ -18943,7 +20801,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else if( zFile==0 && eMode!='e' && eMode!='x' ){
zFile = sqlite3_mprintf("%s", z);
- if( zFile[0]=='|' ){
+ if( zFile && zFile[0]=='|' ){
while( i+1<nArg ) zFile = sqlite3_mprintf("%z %s", zFile, azArg[++i]);
break;
}
@@ -18956,7 +20814,9 @@ static int do_meta_command(char *zLine, ShellState *p){
goto meta_command_exit;
}
}
- if( zFile==0 ) zFile = sqlite3_mprintf("stdout");
+ if( zFile==0 ){
+ zFile = sqlite3_mprintf("stdout");
+ }
if( bOnce ){
p->outCount = 2;
}else{
@@ -18983,6 +20843,7 @@ static int do_meta_command(char *zLine, ShellState *p){
zFile = sqlite3_mprintf("%s", p->zTempFile);
}
#endif /* SQLITE_NOHAVE_SYSTEM */
+ shell_check_oom(zFile);
if( zFile[0]=='|' ){
#ifdef SQLITE_OMIT_POPEN
raw_printf(stderr, "Error: pipes are not supported in this OS\n");
@@ -18995,7 +20856,7 @@ static int do_meta_command(char *zLine, ShellState *p){
p->out = stdout;
rc = 1;
}else{
- if( bBOM ) fprintf(p->out,"\357\273\277");
+ if( zBOM[0] ) fwrite(zBOM, 1, 3, p->out);
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
}
#endif
@@ -19008,12 +20869,13 @@ static int do_meta_command(char *zLine, ShellState *p){
p->out = stdout;
rc = 1;
} else {
- if( bBOM ) fprintf(p->out,"\357\273\277");
+ if( zBOM[0] ) fwrite(zBOM, 1, 3, p->out);
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
}
}
sqlite3_free(zFile);
}else
+#endif /* !defined(SQLITE_SHELL_WASM_MODE) */
if( c=='p' && n>=3 && strncmp(azArg[0], "parameter", n)==0 ){
open_db(p,0);
@@ -19047,7 +20909,7 @@ static int do_meta_command(char *zLine, ShellState *p){
rx = sqlite3_prepare_v2(p->db,
"SELECT key, quote(value) "
"FROM temp.sqlite_parameters;", -1, &pStmt, 0);
- while( sqlite3_step(pStmt)==SQLITE_ROW ){
+ while( rx==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
utf8_printf(p->out, "%-*s %s\n", len, sqlite3_column_text(pStmt,0),
sqlite3_column_text(pStmt,1));
}
@@ -19079,7 +20941,7 @@ static int do_meta_command(char *zLine, ShellState *p){
zSql = sqlite3_mprintf(
"REPLACE INTO temp.sqlite_parameters(key,value)"
"VALUES(%Q,%s);", zKey, zValue);
- if( zSql==0 ) shell_out_of_memory();
+ shell_check_oom(zSql);
pStmt = 0;
rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
@@ -19089,7 +20951,7 @@ static int do_meta_command(char *zLine, ShellState *p){
zSql = sqlite3_mprintf(
"REPLACE INTO temp.sqlite_parameters(key,value)"
"VALUES(%Q,%Q);", zKey, zValue);
- if( zSql==0 ) shell_out_of_memory();
+ shell_check_oom(zSql);
rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
if( rx!=SQLITE_OK ){
@@ -19110,7 +20972,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( nArg==3 && strcmp(azArg[1],"unset")==0 ){
char *zSql = sqlite3_mprintf(
"DELETE FROM temp.sqlite_parameters WHERE key=%Q", azArg[2]);
- if( zSql==0 ) shell_out_of_memory();
+ shell_check_oom(zSql);
sqlite3_exec(p->db, zSql, 0, 0, 0);
sqlite3_free(zSql);
}else
@@ -19183,13 +21045,17 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else
+#ifndef SQLITE_SHELL_WASM_MODE
if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){
rc = 2;
}else
+#endif
+#ifndef SQLITE_SHELL_WASM_MODE
if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 ){
FILE *inSaved = p->in;
int savedLineno = p->lineno;
+ failIfSafeMode(p, "cannot run .read in safe mode");
if( nArg!=2 ){
raw_printf(stderr, "Usage: .read FILE\n");
rc = 1;
@@ -19210,7 +21076,7 @@ static int do_meta_command(char *zLine, ShellState *p){
pclose(p->in);
}
#endif
- }else if( notNormalFile(azArg[1]) || (p->in = fopen(azArg[1], "rb"))==0 ){
+ }else if( (p->in = openChrSource(azArg[1]))==0 ){
utf8_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
rc = 1;
}else{
@@ -19220,7 +21086,9 @@ static int do_meta_command(char *zLine, ShellState *p){
p->in = inSaved;
p->lineno = savedLineno;
}else
+#endif /* !defined(SQLITE_SHELL_WASM_MODE) */
+#ifndef SQLITE_SHELL_WASM_MODE
if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 ){
const char *zSrcFile;
const char *zDb;
@@ -19228,6 +21096,7 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_backup *pBackup;
int nTimeout = 0;
+ failIfSafeMode(p, "cannot run .restore in safe mode");
if( nArg==2 ){
zSrcFile = azArg[1];
zDb = "main";
@@ -19271,6 +21140,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
close_db(pSrc);
}else
+#endif /* !defined(SQLITE_SHELL_WASM_MODE) */
if( c=='s' && strncmp(azArg[0], "scanstats", n)==0 ){
if( nArg==2 ){
@@ -19334,6 +21204,7 @@ static int do_meta_command(char *zLine, ShellState *p){
" rootpage integer,\n"
" sql text\n"
")", zName);
+ shell_check_oom(new_argv[0]);
new_argv[1] = 0;
new_colv[0] = "sql";
new_colv[1] = 0;
@@ -19385,8 +21256,10 @@ static int do_meta_command(char *zLine, ShellState *p){
appendText(&sSelect, ") WHERE ", 0);
if( zName ){
char *zQarg = sqlite3_mprintf("%Q", zName);
- int bGlob = strchr(zName, '*') != 0 || strchr(zName, '?') != 0 ||
- strchr(zName, '[') != 0;
+ int bGlob;
+ shell_check_oom(zQarg);
+ bGlob = strchr(zName, '*') != 0 || strchr(zName, '?') != 0 ||
+ strchr(zName, '[') != 0;
if( strchr(zName, '.') ){
appendText(&sSelect, "lower(printf('%s.%s',sname,tbl_name))", 0);
}else{
@@ -19424,14 +21297,17 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else
- if( c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0 ){
+ if( (c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0)
+ || (c=='t' && n==9 && strncmp(azArg[0], "treetrace", n)==0)
+ ){
unsigned int x = nArg>=2 ? (unsigned int)integerValue(azArg[1]) : 0xffffffff;
sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 1, &x);
}else
#if defined(SQLITE_ENABLE_SESSION)
if( c=='s' && strncmp(azArg[0],"session",n)==0 && n>=3 ){
- OpenSession *pSession = &p->aSession[0];
+ struct AuxDb *pAuxDb = p->pAuxDb;
+ OpenSession *pSession = &pAuxDb->aSession[0];
char **azCmd = &azArg[1];
int iSes = 0;
int nCmd = nArg - 1;
@@ -19439,15 +21315,15 @@ static int do_meta_command(char *zLine, ShellState *p){
if( nArg<=1 ) goto session_syntax_error;
open_db(p, 0);
if( nArg>=3 ){
- for(iSes=0; iSes<p->nSession; iSes++){
- if( strcmp(p->aSession[iSes].zName, azArg[1])==0 ) break;
+ for(iSes=0; iSes<pAuxDb->nSession; iSes++){
+ if( strcmp(pAuxDb->aSession[iSes].zName, azArg[1])==0 ) break;
}
- if( iSes<p->nSession ){
- pSession = &p->aSession[iSes];
+ if( iSes<pAuxDb->nSession ){
+ pSession = &pAuxDb->aSession[iSes];
azCmd++;
nCmd--;
}else{
- pSession = &p->aSession[0];
+ pSession = &pAuxDb->aSession[0];
iSes = 0;
}
}
@@ -19476,6 +21352,7 @@ static int do_meta_command(char *zLine, ShellState *p){
*/
if( strcmp(azCmd[0],"changeset")==0 || strcmp(azCmd[0],"patchset")==0 ){
FILE *out = 0;
+ failIfSafeMode(p, "cannot run \".session %s\" in safe mode", azCmd[0]);
if( nCmd!=2 ) goto session_syntax_error;
if( pSession->p==0 ) goto session_not_open;
out = fopen(azCmd[1], "wb");
@@ -19509,9 +21386,9 @@ static int do_meta_command(char *zLine, ShellState *p){
*/
if( strcmp(azCmd[0], "close")==0 ){
if( nCmd!=1 ) goto session_syntax_error;
- if( p->nSession ){
+ if( pAuxDb->nSession ){
session_close(pSession);
- p->aSession[iSes] = p->aSession[--p->nSession];
+ pAuxDb->aSession[iSes] = pAuxDb->aSession[--pAuxDb->nSession];
}
}else
@@ -19522,7 +21399,7 @@ static int do_meta_command(char *zLine, ShellState *p){
int ii;
if( nCmd>2 ) goto session_syntax_error;
ii = nCmd==1 ? -1 : booleanValue(azCmd[1]);
- if( p->nSession ){
+ if( pAuxDb->nSession ){
ii = sqlite3session_enable(pSession->p, ii);
utf8_printf(p->out, "session %s enable flag = %d\n",
pSession->zName, ii);
@@ -19535,7 +21412,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( strcmp(azCmd[0], "filter")==0 ){
int ii, nByte;
if( nCmd<2 ) goto session_syntax_error;
- if( p->nSession ){
+ if( pAuxDb->nSession ){
for(ii=0; ii<pSession->nFilter; ii++){
sqlite3_free(pSession->azFilter[ii]);
}
@@ -19547,7 +21424,8 @@ static int do_meta_command(char *zLine, ShellState *p){
exit(1);
}
for(ii=1; ii<nCmd; ii++){
- pSession->azFilter[ii-1] = sqlite3_mprintf("%s", azCmd[ii]);
+ char *x = pSession->azFilter[ii-1] = sqlite3_mprintf("%s", azCmd[ii]);
+ shell_check_oom(x);
}
pSession->nFilter = ii-1;
}
@@ -19560,7 +21438,7 @@ static int do_meta_command(char *zLine, ShellState *p){
int ii;
if( nCmd>2 ) goto session_syntax_error;
ii = nCmd==1 ? -1 : booleanValue(azCmd[1]);
- if( p->nSession ){
+ if( pAuxDb->nSession ){
ii = sqlite3session_indirect(pSession->p, ii);
utf8_printf(p->out, "session %s indirect flag = %d\n",
pSession->zName, ii);
@@ -19573,7 +21451,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( strcmp(azCmd[0], "isempty")==0 ){
int ii;
if( nCmd!=1 ) goto session_syntax_error;
- if( p->nSession ){
+ if( pAuxDb->nSession ){
ii = sqlite3session_isempty(pSession->p);
utf8_printf(p->out, "session %s isempty flag = %d\n",
pSession->zName, ii);
@@ -19584,8 +21462,8 @@ static int do_meta_command(char *zLine, ShellState *p){
** List all currently open sessions
*/
if( strcmp(azCmd[0],"list")==0 ){
- for(i=0; i<p->nSession; i++){
- utf8_printf(p->out, "%d %s\n", i, p->aSession[i].zName);
+ for(i=0; i<pAuxDb->nSession; i++){
+ utf8_printf(p->out, "%d %s\n", i, pAuxDb->aSession[i].zName);
}
}else
@@ -19598,17 +21476,17 @@ static int do_meta_command(char *zLine, ShellState *p){
if( nCmd!=3 ) goto session_syntax_error;
zName = azCmd[2];
if( zName[0]==0 ) goto session_syntax_error;
- for(i=0; i<p->nSession; i++){
- if( strcmp(p->aSession[i].zName,zName)==0 ){
+ for(i=0; i<pAuxDb->nSession; i++){
+ if( strcmp(pAuxDb->aSession[i].zName,zName)==0 ){
utf8_printf(stderr, "Session \"%s\" already exists\n", zName);
goto meta_command_exit;
}
}
- if( p->nSession>=ArraySize(p->aSession) ){
- raw_printf(stderr, "Maximum of %d sessions\n", ArraySize(p->aSession));
+ if( pAuxDb->nSession>=ArraySize(pAuxDb->aSession) ){
+ raw_printf(stderr, "Maximum of %d sessions\n", ArraySize(pAuxDb->aSession));
goto meta_command_exit;
}
- pSession = &p->aSession[p->nSession];
+ pSession = &pAuxDb->aSession[pAuxDb->nSession];
rc = sqlite3session_create(p->db, azCmd[1], &pSession->p);
if( rc ){
raw_printf(stderr, "Cannot open session: error code=%d\n", rc);
@@ -19617,8 +21495,9 @@ static int do_meta_command(char *zLine, ShellState *p){
}
pSession->nFilter = 0;
sqlite3session_table_filter(pSession->p, session_filter, pSession);
- p->nSession++;
+ pAuxDb->nSession++;
pSession->zName = sqlite3_mprintf("%s", zName);
+ shell_check_oom(pSession->zName);
}else
/* If no command name matches, show a syntax error */
session_syntax_error:
@@ -19712,11 +21591,12 @@ static int do_meta_command(char *zLine, ShellState *p){
const char *zSql = (const char*)sqlite3_column_text(pStmt, 2);
const char *zAns = (const char*)sqlite3_column_text(pStmt, 3);
+ if( zOp==0 ) continue;
+ if( zSql==0 ) continue;
+ if( zAns==0 ) continue;
k = 0;
if( bVerbose>0 ){
- char *zQuote = sqlite3_mprintf("%q", zSql);
printf("%d: %s %s\n", tno, zOp, zSql);
- sqlite3_free(zQuote);
}
if( strcmp(zOp,"memo")==0 ){
utf8_printf(p->out, "%s\n", zSql);
@@ -19834,6 +21714,7 @@ static int do_meta_command(char *zLine, ShellState *p){
zSep = "VALUES(";
while( SQLITE_ROW==sqlite3_step(pStmt) ){
const char *zTab = (const char*)sqlite3_column_text(pStmt,0);
+ if( zTab==0 ) continue;
if( zLike && sqlite3_strlike(zLike, zTab, 0)!=0 ) continue;
if( strncmp(zTab, "sqlite_",7)!=0 ){
appendText(&sQuery,"SELECT * FROM ", 0);
@@ -19874,6 +21755,7 @@ static int do_meta_command(char *zLine, ShellState *p){
" FROM [sha3sum$query]",
sSql.z, iSize);
}
+ shell_check_oom(zSql);
freeText(&sQuery);
freeText(&sSql);
if( bDebug ){
@@ -19884,27 +21766,28 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_free(zSql);
}else
-#ifndef SQLITE_NOHAVE_SYSTEM
+#if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_WASM_MODE)
if( c=='s'
&& (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0)
){
char *zCmd;
int i, x;
+ failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]);
if( nArg<2 ){
raw_printf(stderr, "Usage: .system COMMAND\n");
rc = 1;
goto meta_command_exit;
}
zCmd = sqlite3_mprintf(strchr(azArg[1],' ')==0?"%s":"\"%s\"", azArg[1]);
- for(i=2; i<nArg; i++){
+ for(i=2; i<nArg && zCmd!=0; i++){
zCmd = sqlite3_mprintf(strchr(azArg[i],' ')==0?"%z %s":"%z \"%s\"",
zCmd, azArg[i]);
}
- x = system(zCmd);
+ x = zCmd!=0 ? system(zCmd) : 1;
sqlite3_free(zCmd);
if( x ) raw_printf(stderr, "System command returns %d\n", x);
}else
-#endif /* !defined(SQLITE_NOHAVE_SYSTEM) */
+#endif /* !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_WASM_MODE) */
if( c=='s' && strncmp(azArg[0], "show", n)==0 ){
static const char *azBool[] = { "off", "on", "trigger", "full"};
@@ -19916,12 +21799,22 @@ static int do_meta_command(char *zLine, ShellState *p){
goto meta_command_exit;
}
utf8_printf(p->out, "%12.12s: %s\n","echo",
- azBool[ShellHasFlag(p, SHFLG_Echo)]);
+ azBool[ShellHasFlag(p, SHFLG_Echo)]);
utf8_printf(p->out, "%12.12s: %s\n","eqp", azBool[p->autoEQP&3]);
utf8_printf(p->out, "%12.12s: %s\n","explain",
p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off");
utf8_printf(p->out,"%12.12s: %s\n","headers", azBool[p->showHeader!=0]);
- utf8_printf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]);
+ if( p->mode==MODE_Column
+ || (p->mode>=MODE_Markdown && p->mode<=MODE_Box)
+ ){
+ utf8_printf
+ (p->out, "%12.12s: %s --wrap %d --wordwrap %s --%squote\n", "mode",
+ modeDescr[p->mode], p->cmOpts.iWrap,
+ p->cmOpts.bWordWrap ? "on" : "off",
+ p->cmOpts.bQuote ? "" : "no");
+ }else{
+ utf8_printf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]);
+ }
utf8_printf(p->out, "%12.12s: ", "nullvalue");
output_c_string(p->out, p->nullValue);
raw_printf(p->out, "\n");
@@ -19946,7 +21839,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
raw_printf(p->out, "\n");
utf8_printf(p->out, "%12.12s: %s\n", "filename",
- p->zDbFilename ? p->zDbFilename : "");
+ p->pAuxDb->zDbFilename ? p->pAuxDb->zDbFilename : "");
}else
if( c=='s' && strncmp(azArg[0], "stats", n)==0 ){
@@ -20015,8 +21908,10 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}
rc = sqlite3_finalize(pStmt);
- appendText(&s, " ORDER BY 1", 0);
- rc = sqlite3_prepare_v2(p->db, s.z, -1, &pStmt, 0);
+ if( rc==SQLITE_OK ){
+ appendText(&s, " ORDER BY 1", 0);
+ rc = sqlite3_prepare_v2(p->db, s.z, -1, &pStmt, 0);
+ }
freeText(&s);
if( rc ) return shellDatabaseError(p->db);
@@ -20034,12 +21929,12 @@ static int do_meta_command(char *zLine, ShellState *p){
char **azNew;
int n2 = nAlloc*2 + 10;
azNew = sqlite3_realloc64(azResult, sizeof(azResult[0])*n2);
- if( azNew==0 ) shell_out_of_memory();
+ shell_check_oom(azNew);
nAlloc = n2;
azResult = azNew;
}
azResult[nRow] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
- if( 0==azResult[nRow] ) shell_out_of_memory();
+ shell_check_oom(azResult[nRow]);
nRow++;
}
if( sqlite3_finalize(pStmt)!=SQLITE_OK ){
@@ -20072,6 +21967,7 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_free(azResult);
}else
+#ifndef SQLITE_SHELL_WASM_MODE
/* Begin redirecting output to the file "testcase-out.txt" */
if( c=='t' && strcmp(azArg[0],"testcase")==0 ){
output_reset(p);
@@ -20085,34 +21981,38 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "?");
}
}else
+#endif /* !defined(SQLITE_SHELL_WASM_MODE) */
#ifndef SQLITE_UNTESTABLE
if( c=='t' && n>=8 && strncmp(azArg[0], "testctrl", n)==0 ){
static const struct {
const char *zCtrlName; /* Name of a test-control option */
int ctrlCode; /* Integer code for that option */
+ int unSafe; /* Not valid for --safe mode */
const char *zUsage; /* Usage notes */
} aCtrl[] = {
- { "always", SQLITE_TESTCTRL_ALWAYS, "BOOLEAN" },
- { "assert", SQLITE_TESTCTRL_ASSERT, "BOOLEAN" },
- /*{ "benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS, "" },*/
- /*{ "bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, "" },*/
- { "byteorder", SQLITE_TESTCTRL_BYTEORDER, "" },
- { "extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,"BOOLEAN" },
- /*{ "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, "" },*/
- { "imposter", SQLITE_TESTCTRL_IMPOSTER, "SCHEMA ON/OFF ROOTPAGE"},
- { "internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS, "" },
- { "localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,"BOOLEAN" },
- { "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT, "BOOLEAN" },
- { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS, "DISABLE-MASK" },
+ { "always", SQLITE_TESTCTRL_ALWAYS, 1, "BOOLEAN" },
+ { "assert", SQLITE_TESTCTRL_ASSERT, 1, "BOOLEAN" },
+ /*{ "benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS,1, "" },*/
+ /*{ "bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, 1, "" },*/
+ { "byteorder", SQLITE_TESTCTRL_BYTEORDER, 0, "" },
+ { "extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,0,"BOOLEAN" },
+ /*{ "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, 1,"" },*/
+ { "imposter", SQLITE_TESTCTRL_IMPOSTER,1,"SCHEMA ON/OFF ROOTPAGE"},
+ { "internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS,0,"" },
+ { "localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,0,"BOOLEAN" },
+ { "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT,1, "BOOLEAN" },
+ { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS,0,"DISABLE-MASK" },
#ifdef YYCOVERAGE
- { "parser_coverage", SQLITE_TESTCTRL_PARSER_COVERAGE, "" },
+ { "parser_coverage", SQLITE_TESTCTRL_PARSER_COVERAGE,0,"" },
#endif
- { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE, "OFFSET " },
- { "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE, "" },
- { "prng_save", SQLITE_TESTCTRL_PRNG_SAVE, "" },
- { "prng_seed", SQLITE_TESTCTRL_PRNG_SEED, "SEED ?db?" },
- { "seek_count", SQLITE_TESTCTRL_SEEK_COUNT, "" },
+ { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE,0, "OFFSET " },
+ { "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE,0, "" },
+ { "prng_save", SQLITE_TESTCTRL_PRNG_SAVE, 0, "" },
+ { "prng_seed", SQLITE_TESTCTRL_PRNG_SEED, 0, "SEED ?db?" },
+ { "seek_count", SQLITE_TESTCTRL_SEEK_COUNT, 0, "" },
+ { "sorter_mmap", SQLITE_TESTCTRL_SORTER_MMAP, 0, "NMAX" },
+ { "tune", SQLITE_TESTCTRL_TUNE, 1, "ID VALUE" },
};
int testctrl = -1;
int iCtrl = -1;
@@ -20160,6 +22060,11 @@ static int do_meta_command(char *zLine, ShellState *p){
if( testctrl<0 ){
utf8_printf(stderr,"Error: unknown test-control: %s\n"
"Use \".testctrl --help\" for help\n", zCmd);
+ }else if( aCtrl[iCtrl].unSafe && p->bSafeMode ){
+ utf8_printf(stderr,
+ "line %d: \".testctrl %s\" may not be used in safe mode\n",
+ p->lineno, aCtrl[iCtrl].zCtrlName);
+ exit(1);
}else{
switch(testctrl){
@@ -20257,12 +22162,48 @@ static int do_meta_command(char *zLine, ShellState *p){
}
#ifdef YYCOVERAGE
- case SQLITE_TESTCTRL_PARSER_COVERAGE:
+ case SQLITE_TESTCTRL_PARSER_COVERAGE: {
if( nArg==2 ){
sqlite3_test_control(testctrl, p->out);
isOk = 3;
}
+ break;
+ }
+#endif
+#ifdef SQLITE_DEBUG
+ case SQLITE_TESTCTRL_TUNE: {
+ if( nArg==4 ){
+ int id = (int)integerValue(azArg[2]);
+ int val = (int)integerValue(azArg[3]);
+ sqlite3_test_control(testctrl, id, &val);
+ isOk = 3;
+ }else if( nArg==3 ){
+ int id = (int)integerValue(azArg[2]);
+ sqlite3_test_control(testctrl, -id, &rc2);
+ isOk = 1;
+ }else if( nArg==2 ){
+ int id = 1;
+ while(1){
+ int val = 0;
+ rc2 = sqlite3_test_control(testctrl, -id, &val);
+ if( rc2!=SQLITE_OK ) break;
+ if( id>1 ) utf8_printf(p->out, " ");
+ utf8_printf(p->out, "%d: %d", id, val);
+ id++;
+ }
+ if( id>1 ) utf8_printf(p->out, "\n");
+ isOk = 3;
+ }
+ break;
+ }
#endif
+ case SQLITE_TESTCTRL_SORTER_MMAP:
+ if( nArg==3 ){
+ int opt = (unsigned int)integerValue(azArg[2]);
+ rc2 = sqlite3_test_control(testctrl, p->db, opt);
+ isOk = 3;
+ }
+ break;
}
}
if( isOk==0 && iCtrl>=0 ){
@@ -20504,7 +22445,7 @@ static int do_meta_command(char *zLine, ShellState *p){
int j;
assert( nArg<=ArraySize(azArg) );
p->nWidth = nArg-1;
- p->colWidth = realloc(p->colWidth, p->nWidth*sizeof(int)*2);
+ p->colWidth = realloc(p->colWidth, (p->nWidth+1)*sizeof(int)*2);
if( p->colWidth==0 && p->nWidth>0 ) shell_out_of_memory();
if( p->nWidth ) p->actualWidth = &p->colWidth[p->nWidth];
for(j=1; j<nArg; j++){
@@ -20523,41 +22464,99 @@ meta_command_exit:
p->outCount--;
if( p->outCount==0 ) output_reset(p);
}
+ p->bSafeMode = p->bSafeModePersist;
return rc;
}
-/*
-** Return TRUE if a semicolon occurs anywhere in the first N characters
-** of string z[].
-*/
-static int line_contains_semicolon(const char *z, int N){
- int i;
- for(i=0; i<N; i++){ if( z[i]==';' ) return 1; }
- return 0;
-}
-
-/*
-** Test to see if a line consists entirely of whitespace.
+/* Line scan result and intermediate states (supporting scan resumption)
*/
-static int _all_whitespace(const char *z){
- for(; *z; z++){
- if( IsSpace(z[0]) ) continue;
- if( *z=='/' && z[1]=='*' ){
- z += 2;
- while( *z && (*z!='*' || z[1]!='/') ){ z++; }
- if( *z==0 ) return 0;
- z++;
- continue;
+#ifndef CHAR_BIT
+# define CHAR_BIT 8
+#endif
+typedef enum {
+ QSS_HasDark = 1<<CHAR_BIT, QSS_EndingSemi = 2<<CHAR_BIT,
+ QSS_CharMask = (1<<CHAR_BIT)-1, QSS_ScanMask = 3<<CHAR_BIT,
+ QSS_Start = 0
+} QuickScanState;
+#define QSS_SETV(qss, newst) ((newst) | ((qss) & QSS_ScanMask))
+#define QSS_INPLAIN(qss) (((qss)&QSS_CharMask)==QSS_Start)
+#define QSS_PLAINWHITE(qss) (((qss)&~QSS_EndingSemi)==QSS_Start)
+#define QSS_PLAINDARK(qss) (((qss)&~QSS_EndingSemi)==QSS_HasDark)
+#define QSS_SEMITERM(qss) (((qss)&~QSS_HasDark)==QSS_EndingSemi)
+
+/*
+** Scan line for classification to guide shell's handling.
+** The scan is resumable for subsequent lines when prior
+** return values are passed as the 2nd argument.
+*/
+static QuickScanState quickscan(char *zLine, QuickScanState qss){
+ char cin;
+ char cWait = (char)qss; /* intentional narrowing loss */
+ if( cWait==0 ){
+ PlainScan:
+ assert( cWait==0 );
+ while( (cin = *zLine++)!=0 ){
+ if( IsSpace(cin) )
+ continue;
+ switch (cin){
+ case '-':
+ if( *zLine!='-' )
+ break;
+ while((cin = *++zLine)!=0 )
+ if( cin=='\n')
+ goto PlainScan;
+ return qss;
+ case ';':
+ qss |= QSS_EndingSemi;
+ continue;
+ case '/':
+ if( *zLine=='*' ){
+ ++zLine;
+ cWait = '*';
+ qss = QSS_SETV(qss, cWait);
+ goto TermScan;
+ }
+ break;
+ case '[':
+ cin = ']';
+ /* fall thru */
+ case '`': case '\'': case '"':
+ cWait = cin;
+ qss = QSS_HasDark | cWait;
+ goto TermScan;
+ default:
+ break;
+ }
+ qss = (qss & ~QSS_EndingSemi) | QSS_HasDark;
}
- if( *z=='-' && z[1]=='-' ){
- z += 2;
- while( *z && *z!='\n' ){ z++; }
- if( *z==0 ) return 1;
- continue;
+ }else{
+ TermScan:
+ while( (cin = *zLine++)!=0 ){
+ if( cin==cWait ){
+ switch( cWait ){
+ case '*':
+ if( *zLine != '/' )
+ continue;
+ ++zLine;
+ cWait = 0;
+ qss = QSS_SETV(qss, 0);
+ goto PlainScan;
+ case '`': case '\'': case '"':
+ if(*zLine==cWait){
+ ++zLine;
+ continue;
+ }
+ /* fall thru */
+ case ']':
+ cWait = 0;
+ qss = QSS_SETV(qss, 0);
+ goto PlainScan;
+ default: assert(0);
+ }
+ }
}
- return 0;
}
- return 1;
+ return qss;
}
/*
@@ -20565,16 +22564,15 @@ static int _all_whitespace(const char *z){
** than a semi-colon. The SQL Server style "go" command is understood
** as is the Oracle "/".
*/
-static int line_is_command_terminator(const char *zLine){
+static int line_is_command_terminator(char *zLine){
while( IsSpace(zLine[0]) ){ zLine++; };
- if( zLine[0]=='/' && _all_whitespace(&zLine[1]) ){
- return 1; /* Oracle */
- }
- if( ToLower(zLine[0])=='g' && ToLower(zLine[1])=='o'
- && _all_whitespace(&zLine[2]) ){
- return 1; /* SQL Server */
- }
- return 0;
+ if( zLine[0]=='/' )
+ zLine += 1; /* Oracle */
+ else if ( ToLower(zLine[0])=='g' && ToLower(zLine[1])=='o' )
+ zLine += 2; /* SQL Server */
+ else
+ return 0;
+ return quickscan(zLine, QSS_Start)==QSS_Start;
}
/*
@@ -20616,27 +22614,77 @@ static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){
END_TIMER;
if( rc || zErrMsg ){
char zPrefix[100];
- if( in!=0 || !stdin_is_interactive ){
- sqlite3_snprintf(sizeof(zPrefix), zPrefix,
- "Error: near line %d:", startline);
+ const char *zErrorTail;
+ const char *zErrorType;
+ if( zErrMsg==0 ){
+ zErrorType = "Error";
+ zErrorTail = sqlite3_errmsg(p->db);
+ }else if( strncmp(zErrMsg, "in prepare, ",12)==0 ){
+ zErrorType = "Parse error";
+ zErrorTail = &zErrMsg[12];
+ }else if( strncmp(zErrMsg, "stepping, ", 10)==0 ){
+ zErrorType = "Runtime error";
+ zErrorTail = &zErrMsg[10];
}else{
- sqlite3_snprintf(sizeof(zPrefix), zPrefix, "Error:");
+ zErrorType = "Error";
+ zErrorTail = zErrMsg;
}
- if( zErrMsg!=0 ){
- utf8_printf(stderr, "%s %s\n", zPrefix, zErrMsg);
- sqlite3_free(zErrMsg);
- zErrMsg = 0;
+ if( in!=0 || !stdin_is_interactive ){
+ sqlite3_snprintf(sizeof(zPrefix), zPrefix,
+ "%s near line %d:", zErrorType, startline);
}else{
- utf8_printf(stderr, "%s %s\n", zPrefix, sqlite3_errmsg(p->db));
+ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%s:", zErrorType);
}
+ utf8_printf(stderr, "%s %s\n", zPrefix, zErrorTail);
+ sqlite3_free(zErrMsg);
+ zErrMsg = 0;
return 1;
}else if( ShellHasFlag(p, SHFLG_CountChanges) ){
- raw_printf(p->out, "changes: %3d total_changes: %d\n",
- sqlite3_changes(p->db), sqlite3_total_changes(p->db));
+ char zLineBuf[2000];
+ sqlite3_snprintf(sizeof(zLineBuf), zLineBuf,
+ "changes: %lld total_changes: %lld",
+ sqlite3_changes64(p->db), sqlite3_total_changes64(p->db));
+ raw_printf(p->out, "%s\n", zLineBuf);
}
return 0;
}
+static void echo_group_input(ShellState *p, const char *zDo){
+ if( ShellHasFlag(p, SHFLG_Echo) ) utf8_printf(p->out, "%s\n", zDo);
+}
+
+#ifdef SQLITE_SHELL_WASM_MODE
+/*
+** Alternate one_input_line() impl for wasm mode. This is not in the primary impl
+** because we need the global shellState and cannot access it from that function
+** without moving lots of code around (creating a larger/messier diff).
+*/
+static char *one_input_line(FILE *in, char *zPrior, int isContinuation){
+ /* Parse the next line from shellState.wasm.zInput. */
+ const char *zBegin = shellState.wasm.zPos;
+ const char *z = zBegin;
+ char *zLine = 0;
+ int nZ = 0;
+
+ UNUSED_PARAMETER(in);
+ UNUSED_PARAMETER(isContinuation);
+ if(!z || !*z){
+ return 0;
+ }
+ while(*z && isspace(*z)) ++z;
+ zBegin = z;
+ for(; *z && '\n'!=*z; ++nZ, ++z){}
+ if(nZ>0 && '\r'==zBegin[nZ-1]){
+ --nZ;
+ }
+ shellState.wasm.zPos = z;
+ zLine = realloc(zPrior, nZ+1);
+ shell_check_oom(zLine);
+ memcpy(zLine, zBegin, (size_t)nZ);
+ zLine[nZ] = 0;
+ return zLine;
+}
+#endif /* SQLITE_SHELL_WASM_MODE */
/*
** Read input from *in and process it. If *in==0 then input
@@ -20653,11 +22701,18 @@ static int process_input(ShellState *p){
int nLine; /* Length of current line */
int nSql = 0; /* Bytes of zSql[] used */
int nAlloc = 0; /* Allocated zSql[] space */
- int nSqlPrior = 0; /* Bytes of zSql[] used by prior line */
int rc; /* Error code */
int errCnt = 0; /* Number of errors seen */
int startline = 0; /* Line number for start of current input */
+ QuickScanState qss = QSS_Start; /* Accumulated line status (so far) */
+ if( p->inputNesting==MAX_INPUT_NESTING ){
+ /* This will be more informative in a later version. */
+ utf8_printf(stderr,"Input nesting limit (%d) reached at line %d."
+ " Check recursion.\n", MAX_INPUT_NESTING, p->lineno);
+ return 1;
+ }
+ ++p->inputNesting;
p->lineno = 0;
while( errCnt==0 || !bail_on_error || (p->in==0 && stdin_is_interactive) ){
fflush(p->out);
@@ -20672,12 +22727,20 @@ static int process_input(ShellState *p){
seenInterrupt = 0;
}
p->lineno++;
- if( nSql==0 && _all_whitespace(zLine) ){
- if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zLine);
+ if( QSS_INPLAIN(qss)
+ && line_is_command_terminator(zLine)
+ && line_is_complete(zSql, nSql) ){
+ memcpy(zLine,";",2);
+ }
+ qss = quickscan(zLine, qss);
+ if( QSS_PLAINWHITE(qss) && nSql==0 ){
+ /* Just swallow single-line whitespace */
+ echo_group_input(p, zLine);
+ qss = QSS_Start;
continue;
}
if( zLine && (zLine[0]=='.' || zLine[0]=='#') && nSql==0 ){
- if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zLine);
+ echo_group_input(p, zLine);
if( zLine[0]=='.' ){
rc = do_meta_command(zLine, p);
if( rc==2 ){ /* exit requested */
@@ -20686,18 +22749,17 @@ static int process_input(ShellState *p){
errCnt++;
}
}
+ qss = QSS_Start;
continue;
}
- if( line_is_command_terminator(zLine) && line_is_complete(zSql, nSql) ){
- memcpy(zLine,";",2);
- }
+ /* No single-line dispositions remain; accumulate line(s). */
nLine = strlen30(zLine);
if( nSql+nLine+2>=nAlloc ){
- nAlloc = nSql+nLine+100;
+ /* Grow buffer by half-again increments when big. */
+ nAlloc = nSql+(nSql>>1)+nLine+100;
zSql = realloc(zSql, nAlloc);
- if( zSql==0 ) shell_out_of_memory();
+ shell_check_oom(zSql);
}
- nSqlPrior = nSql;
if( nSql==0 ){
int i;
for(i=0; zLine[i] && IsSpace(zLine[i]); i++){}
@@ -20710,8 +22772,8 @@ static int process_input(ShellState *p){
memcpy(zSql+nSql, zLine, nLine+1);
nSql += nLine;
}
- if( nSql && line_contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior)
- && sqlite3_complete(zSql) ){
+ if( nSql && QSS_SEMITERM(qss) && sqlite3_complete(zSql) ){
+ echo_group_input(p, zSql);
errCnt += runOneSqlLine(p, zSql, p->in, startline);
nSql = 0;
if( p->outCount ){
@@ -20720,16 +22782,22 @@ static int process_input(ShellState *p){
}else{
clearTempFile(p);
}
- }else if( nSql && _all_whitespace(zSql) ){
- if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zSql);
+ p->bSafeMode = p->bSafeModePersist;
+ qss = QSS_Start;
+ }else if( nSql && QSS_PLAINWHITE(qss) ){
+ echo_group_input(p, zSql);
nSql = 0;
+ qss = QSS_Start;
}
}
- if( nSql && !_all_whitespace(zSql) ){
+ if( nSql ){
+ /* This may be incomplete. Let the SQL parser deal with that. */
+ echo_group_input(p, zSql);
errCnt += runOneSqlLine(p, zSql, p->in, startline);
}
free(zSql);
free(zLine);
+ --p->inputNesting;
return errCnt>0;
}
@@ -20826,6 +22894,7 @@ static void process_sqliterc(
return;
}
zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir);
+ shell_check_oom(zBuf);
sqliterc = zBuf;
}
p->in = fopen(sqliterc,"rb");
@@ -20859,10 +22928,10 @@ 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"
-#if defined(SQLITE_ENABLE_DESERIALIZE)
+#if !defined(SQLITE_OMIT_DESERIALIZE)
" -deserialize open the database using sqlite3_deserialize()\n"
#endif
- " -echo print commands before execution\n"
+ " -echo print inputs 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)
@@ -20876,7 +22945,7 @@ static const char zOptions[] =
" -list set output mode to 'list'\n"
" -lookaside SIZE N use N entries of SZ bytes for lookaside memory\n"
" -markdown set output mode to 'markdown'\n"
-#if defined(SQLITE_ENABLE_DESERIALIZE)
+#if !defined(SQLITE_OMIT_DESERIALIZE)
" -maxsize N maximum size for a --deserialize database\n"
#endif
" -memtrace trace all memory allocations and deallocations\n"
@@ -20886,10 +22955,12 @@ static const char zOptions[] =
#endif
" -newline SEP set output row separator. Default: '\\n'\n"
" -nofollow refuse to open symbolic links to database files\n"
+ " -nonce STRING set the safe-mode escape nonce\n"
" -nullvalue TEXT set text string for NULL values. Default ''\n"
" -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n"
" -quote set output mode to 'quote'\n"
" -readonly open the database read-only\n"
+ " -safe enable safe-mode\n"
" -separator SEP set output column separator. Default: '|'\n"
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
" -sorterref SIZE sorter references threshold size\n"
@@ -20937,6 +23008,7 @@ static void main_init(ShellState *data) {
memset(data, 0, sizeof(*data));
data->normalMode = data->cMode = data->mode = MODE_List;
data->autoExplain = 1;
+ data->pAuxDb = &data->aAuxDb[0];
memcpy(data->colSeparator,SEP_Column, 2);
memcpy(data->rowSeparator,SEP_Row, 2);
data->showHeader = 0;
@@ -20995,14 +23067,25 @@ static char *cmdline_option_value(int argc, char **argv, int i){
# endif
#endif
+#ifdef SQLITE_SHELL_WASM_MODE
+# define main fiddle_main
+#endif
+
#if SQLITE_SHELL_IS_UTF8
int SQLITE_CDECL main(int argc, char **argv){
#else
int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
char **argv;
#endif
+#ifdef SQLITE_DEBUG
+ sqlite3_int64 mem_main_enter = sqlite3_memory_used();
+#endif
char *zErrMsg = 0;
+#ifdef SQLITE_SHELL_WASM_MODE
+# define data shellState
+#else
ShellState data;
+#endif
const char *zInitFile = 0;
int i;
int rc = 0;
@@ -21018,11 +23101,12 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
setBinaryMode(stdin, 0);
setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */
+#ifdef SQLITE_SHELL_WASM_MODE
+ stdin_is_interactive = 0;
+ stdout_is_console = 1;
+#else
stdin_is_interactive = isatty(0);
stdout_is_console = isatty(1);
-
-#ifdef SQLITE_DEBUG
- registerOomSimulator();
#endif
#if !defined(_WIN32_WCE)
@@ -21064,16 +23148,16 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
#if !SQLITE_SHELL_IS_UTF8
sqlite3_initialize();
argvToFree = malloc(sizeof(argv[0])*argc*2);
+ shell_check_oom(argvToFree);
argcToFree = argc;
argv = argvToFree + argc;
- if( argv==0 ) shell_out_of_memory();
for(i=0; i<argc; i++){
char *z = sqlite3_win32_unicode_to_utf8(wargv[i]);
int n;
- if( z==0 ) shell_out_of_memory();
+ shell_check_oom(z);
n = (int)strlen(z);
argv[i] = malloc( n+1 );
- if( argv[i]==0 ) shell_out_of_memory();
+ shell_check_oom(argv[i]);
memcpy(argv[i], z, n+1);
argvToFree[i] = argv[i];
sqlite3_free(z);
@@ -21100,7 +23184,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
** this compile-time option to embed this shell program in larger
** applications. */
extern void SQLITE_SHELL_DBNAME_PROC(const char**);
- SQLITE_SHELL_DBNAME_PROC(&data.zDbFilename);
+ SQLITE_SHELL_DBNAME_PROC(&data.pAuxDb->zDbFilename);
warnInmemoryDb = 0;
}
#endif
@@ -21115,15 +23199,15 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
char *z;
z = argv[i];
if( z[0]!='-' ){
- if( data.zDbFilename==0 ){
- data.zDbFilename = z;
+ if( data.aAuxDb->zDbFilename==0 ){
+ data.aAuxDb->zDbFilename = z;
}else{
/* Excesss arguments are interpreted as SQL (or dot-commands) and
** mean that nothing is read from stdin */
readStdin = 0;
nCmd++;
azCmd = realloc(azCmd, sizeof(azCmd[0])*nCmd);
- if( azCmd==0 ) shell_out_of_memory();
+ shell_check_oom(azCmd);
azCmd[nCmd-1] = z;
}
}
@@ -21174,6 +23258,14 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
if( n<0 ) n = 0;
sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, n);
if( sz*n==0 ) data.shellFlgs &= ~SHFLG_Lookaside;
+ }else if( strcmp(z,"-threadsafe")==0 ){
+ int n;
+ n = (int)integerValue(cmdline_option_value(argc,argv,++i));
+ switch( n ){
+ case 0: sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); break;
+ case 2: sqlite3_config(SQLITE_CONFIG_MULTITHREAD); break;
+ default: sqlite3_config(SQLITE_CONFIG_SERIALIZED); break;
+ }
#ifdef SQLITE_ENABLE_VFSTRACE
}else if( strcmp(z,"-vfstrace")==0 ){
extern int vfstrace_register(
@@ -21206,7 +23298,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
#endif
}else if( strcmp(z,"-append")==0 ){
data.openMode = SHELL_OPEN_APPENDVFS;
-#ifdef SQLITE_ENABLE_DESERIALIZE
+#ifndef SQLITE_OMIT_DESERIALIZE
}else if( strcmp(z,"-deserialize")==0 ){
data.openMode = SHELL_OPEN_DESERIALIZE;
}else if( strcmp(z,"-maxsize")==0 && i+1<argc ){
@@ -21226,6 +23318,11 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
sqlite3MemTraceActivate(stderr);
}else if( strcmp(z,"-bail")==0 ){
bail_on_error = 1;
+ }else if( strcmp(z,"-nonce")==0 ){
+ free(data.zNonce);
+ data.zNonce = strdup(argv[++i]);
+ }else if( strcmp(z,"-safe")==0 ){
+ /* no-op - catch this on the second pass */
}
}
verify_uninitialized();
@@ -21256,9 +23353,9 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
}
}
- if( data.zDbFilename==0 ){
+ if( data.pAuxDb->zDbFilename==0 ){
#ifndef SQLITE_OMIT_MEMORYDB
- data.zDbFilename = ":memory:";
+ data.pAuxDb->zDbFilename = ":memory:";
warnInmemoryDb = argc==1;
#else
utf8_printf(stderr,"%s: Error: no database filename specified\n", Argv0);
@@ -21266,14 +23363,16 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
#endif
}
data.out = stdout;
+#ifndef SQLITE_SHELL_WASM_MODE
sqlite3_appendvfs_init(0,0,0);
+#endif
/* Go ahead and open the database file if it already exists. If the
** file does not exist, delay opening it. This prevents empty database
** files from being created if a user mistypes the database name argument
** to the sqlite command-line tool.
*/
- if( access(data.zDbFilename, 0)==0 ){
+ if( access(data.pAuxDb->zDbFilename, 0)==0 ){
open_db(&data, 0);
}
@@ -21323,7 +23422,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
#endif
}else if( strcmp(z,"-append")==0 ){
data.openMode = SHELL_OPEN_APPENDVFS;
-#ifdef SQLITE_ENABLE_DESERIALIZE
+#ifndef SQLITE_OMIT_DESERIALIZE
}else if( strcmp(z,"-deserialize")==0 ){
data.openMode = SHELL_OPEN_DESERIALIZE;
}else if( strcmp(z,"-maxsize")==0 && i+1<argc ){
@@ -21352,8 +23451,10 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
"%s",cmdline_option_value(argc,argv,++i));
}else if( strcmp(z,"-header")==0 ){
data.showHeader = 1;
- }else if( strcmp(z,"-noheader")==0 ){
+ ShellSetFlag(&data, SHFLG_HeaderSet);
+ }else if( strcmp(z,"-noheader")==0 ){
data.showHeader = 0;
+ ShellSetFlag(&data, SHFLG_HeaderSet);
}else if( strcmp(z,"-echo")==0 ){
ShellSetFlag(&data, SHFLG_Echo);
}else if( strcmp(z,"-eqp")==0 ){
@@ -21386,6 +23487,10 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
i+=2;
}else if( strcmp(z,"-lookaside")==0 ){
i+=2;
+ }else if( strcmp(z,"-threadsafe")==0 ){
+ i+=2;
+ }else if( strcmp(z,"-nonce")==0 ){
+ i += 2;
}else if( strcmp(z,"-mmap")==0 ){
i++;
}else if( strcmp(z,"-memtrace")==0 ){
@@ -21444,6 +23549,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
readStdin = 0;
break;
#endif
+ }else if( strcmp(z,"-safe")==0 ){
+ data.bSafeMode = data.bSafeModePersist = 1;
}else{
utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
raw_printf(stderr,"Use -help for a list of options.\n");
@@ -21524,13 +23631,22 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
rc = process_input(&data);
}
}
+#ifndef SQLITE_SHELL_WASM_MODE
+ /* In WASM mode we have to leave the db state in place so that
+ ** client code can "push" SQL into it after this call returns. */
free(azCmd);
set_table_name(&data, 0);
if( data.db ){
- session_close_all(&data);
+ session_close_all(&data, -1);
close_db(data.db);
}
- sqlite3_free(data.zFreeOnClose);
+ for(i=0; i<ArraySize(data.aAuxDb); i++){
+ sqlite3_free(data.aAuxDb[i].zFreeOnClose);
+ if( data.aAuxDb[i].db ){
+ session_close_all(&data, i);
+ close_db(data.aAuxDb[i].db);
+ }
+ }
find_home_dir(1);
output_reset(&data);
data.doXdgOpen = 0;
@@ -21540,8 +23656,140 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
free(argvToFree);
#endif
free(data.colWidth);
+ free(data.zNonce);
/* Clear the global data structure so that valgrind will detect memory
** leaks */
memset(&data, 0, sizeof(data));
+#ifdef SQLITE_DEBUG
+ if( sqlite3_memory_used()>mem_main_enter ){
+ utf8_printf(stderr, "Memory leaked: %u bytes\n",
+ (unsigned int)(sqlite3_memory_used()-mem_main_enter));
+ }
+#endif
+#endif /* !SQLITE_SHELL_WASM_MODE */
return rc;
}
+
+
+#ifdef SQLITE_SHELL_WASM_MODE
+/* Only for emcc experimentation purposes. */
+int fiddle_experiment(int a,int b){
+ return a + b;
+}
+
+/* Only for emcc experimentation purposes.
+
+ Define this function in JS using:
+
+ emcc ... --js-library somefile.js
+
+ containing:
+
+mergeInto(LibraryManager.library, {
+ my_foo: function(){
+ console.debug("my_foo()",arguments);
+ }
+});
+*/
+/*extern void my_foo(sqlite3 *);*/
+/* Only for emcc experimentation purposes. */
+sqlite3 * fiddle_the_db(){
+ printf("fiddle_the_db(%p)\n", (const void*)globalDb);
+ /*my_foo(globalDb);*/
+ return globalDb;
+}
+/* Only for emcc experimentation purposes. */
+sqlite3 * fiddle_db_arg(sqlite3 *arg){
+ printf("fiddle_db_arg(%p)\n", (const void*)arg);
+ return arg;
+}
+
+/*
+** Intended to be called via a SharedWorker() while a separate
+** SharedWorker() (which manages the wasm module) is performing work
+** which should be interrupted. Unfortunately, SharedWorker is not
+** portable enough to make real use of.
+*/
+void fiddle_interrupt(void){
+ if(globalDb) sqlite3_interrupt(globalDb);
+}
+
+/*
+** Returns the filename of the given db name, assuming "main" if
+** zDbName is NULL. Returns NULL if globalDb is not opened.
+*/
+const char * fiddle_db_filename(const char * zDbName){
+ return globalDb
+ ? sqlite3_db_filename(globalDb, zDbName ? zDbName : "main")
+ : NULL;
+}
+
+/*
+** Closes, unlinks, and reopens the db using its current filename (or
+** the default if the db is currently closed). It is assumed, for
+** purposes of the fiddle build, that the file is in a transient
+** virtual filesystem within the browser.
+*/
+void fiddle_reset_db(void){
+ char *zFilename = 0;
+ if(0==globalDb){
+ shellState.pAuxDb->zDbFilename = "/fiddle.sqlite3";
+ }else{
+ zFilename =
+ sqlite3_mprintf("%s", sqlite3_db_filename(globalDb, "main"));
+ shell_check_oom(zFilename);
+ close_db(globalDb);
+ shellDeleteFile(zFilename);
+ shellState.db = 0;
+ shellState.pAuxDb->zDbFilename = zFilename;
+ }
+ open_db(&shellState, 0);
+ sqlite3_free(zFilename);
+}
+
+/*
+** Trivial exportable function for emscripten. Needs to be exported using:
+**
+** emcc ..flags... -sEXPORTED_FUNCTIONS=_fiddle_exec -sEXPORTED_RUNTIME_METHODS=ccall,cwrap
+**
+** (Note the underscore before the function name.) It processes zSql
+** as if it were input to the sqlite3 shell and redirects all output
+** to the wasm binding.
+*/
+void fiddle_exec(const char * zSql){
+ static int once = 0;
+ int rc = 0;
+ if(!once){
+ /* Simulate an argv array for main() */
+ static char * argv[] = {"fiddle",
+ "-bail",
+ "-safe"};
+ rc = fiddle_main((int)(sizeof(argv)/sizeof(argv[0])), argv);
+ once = rc ? -1 : 1;
+ memset(&shellState.wasm, 0, sizeof(shellState.wasm));
+ printf(
+ "SQLite version %s %.19s\n" /*extra-version-info*/,
+ sqlite3_libversion(), sqlite3_sourceid()
+ );
+ puts("WASM shell");
+ puts("Enter \".help\" for usage hints.");
+ if(once>0){
+ fiddle_reset_db();
+ }
+ if(shellState.db){
+ printf("Connected to %s.\n", fiddle_db_filename(NULL));
+ }else{
+ fprintf(stderr,"ERROR initializing db!\n");
+ return;
+ }
+ }
+ if(once<0){
+ puts("DB init failed. Not executing SQL.");
+ }else if(zSql && *zSql){
+ shellState.wasm.zInput = zSql;
+ shellState.wasm.zPos = zSql;
+ process_input(&shellState);
+ memset(&shellState.wasm, 0, sizeof(shellState.wasm));
+ }
+}
+#endif /* SQLITE_SHELL_WASM_MODE */