WIP, pre-sync-to-trunk check-in to capture extensive changes to shell source. (WASM and usual shell tweaks)
FossilOrigin-Name: 3db119c8d754979ceb16253f1b79b645a5bc68b399406cacc4c50a2a71e84e2d
diff --git a/manifest b/manifest
index c242d0b..09b0d04 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Cause\sCLI\sto\suse\sExportHandler\sinterface\sfor\sits\squery\soutput,\sand\simplement\sbuilt-in\ssubclasses\sof\sit,\sall\sin\spreparation\sfor\ssupporting\simplementations\sby\sshell\sextensions.\s(a\sWIP)
-D 2022-05-05T03:49:35.900
+C WIP,\spre-sync-to-trunk\scheck-in\sto\scapture\sextensive\schanges\sto\sshell\ssource.\s(WASM\sand\susual\sshell\stweaks)
+D 2022-12-18T10:27:43.328
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -558,7 +558,7 @@
F src/resolve.c f72bb13359dd5a74d440df25f320dc2c1baff5cde4fc9f0d1bc3feba90b8932a
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
F src/select.c cc1a7581403fc074eee85283ba8d81de50a831ae175cb65a5751be00f621c0d5
-F src/shell.c.in 08ca1d0f9e563003efa9b34f9040ae79e06c08a5c3485ecb8ee2e3657f7b7570
+F src/shell.c.in e28bdaa7cbbc50936f06a592609ed6f7d1f04216a1d3f067516da243bd23fc46
F src/shext_linkage.h 27dcf7624df05b2a7a6d367834339a6db3636f3035157f641f7db2ec499f8f6d
F src/sqlite.h.in 2a35f62185eb5e7ecc64a2f68442b538ce9be74f80f28a00abc24837edcf1c17
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
@@ -1960,8 +1960,8 @@
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 27ff5ce5170ef5902f15ca8fe4133e41b139e0ef5214f8f5a58d12e852a2b782
-R 4beaadfe3bf5ec7c6332a3e29a67a3d9
+P 9b37e0be1a416a49671bbfb1a7e62c66f07b3a9ef6b4ce6cf72a135b046675c5
+R a90d7f8c6d6e0ed2a501682f3b3291b1
U larrybr
-Z 81b8545fbd4fe6f77f4f8bcaa8f1132f
+Z a4435e17e1a04d73b0b697a3cb1915ae
# Remove this line to create a well-formed Fossil manifest.
diff --git a/manifest.uuid b/manifest.uuid
index e82c59f..d009320 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-9b37e0be1a416a49671bbfb1a7e62c66f07b3a9ef6b4ce6cf72a135b046675c5
\ No newline at end of file
+3db119c8d754979ceb16253f1b79b645a5bc68b399406cacc4c50a2a71e84e2d
\ No newline at end of file
diff --git a/src/shell.c.in b/src/shell.c.in
index bd0f695..1e1ca18 100644
--- a/src/shell.c.in
+++ b/src/shell.c.in
@@ -16,6 +16,8 @@
/* This needs to come before any includes for MSVC compiler */
#define _CRT_SECURE_NO_WARNINGS
#endif
+typedef unsigned int u32;
+typedef unsigned short int u16;
/*
** Optionally #include a user-defined header, whereby compilation options
@@ -38,6 +40,15 @@
#endif
/*
+** If SQLITE_SHELL_FIDDLE is defined then the shell is modified
+** somewhat for use as a WASM module in a web browser. This flag
+** should only be used when building the "fiddle" web application, as
+** the browser-mode build has much different user input requirements
+** and this build mode rewires the user input subsystem to account for
+** that.
+*/
+
+/*
** Warning pragmas copied from msvc.h in the core.
*/
#if defined(_MSC_VER)
@@ -76,6 +87,14 @@
# define _LARGEFILE_SOURCE 1
#endif
+#if defined(SQLITE_SHELL_FIDDLE) && !defined(_POSIX_SOURCE)
+/*
+** emcc requires _POSIX_SOURCE (or one of several similar defines)
+** to expose strdup().
+*/
+# define _POSIX_SOURCE
+#endif
+
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
@@ -256,6 +275,18 @@
/* True if the timer is enabled */
static int enableTimer = 0;
+/* A version of strcmp() that works with NULL values */
+static int cli_strcmp(const char *a, const char *b){
+ if( a==0 ) a = "";
+ if( b==0 ) b = "";
+ return strcmp(a,b);
+}
+static int cli_strncmp(const char *a, const char *b, size_t n){
+ if( a==0 ) a = "";
+ if( b==0 ) b = "";
+ return strncmp(a,b,n);
+}
+
/* Return the current wall-clock time */
static sqlite3_int64 timeOfDay(void){
static sqlite3_vfs *clockVfs = 0;
@@ -461,9 +492,104 @@
** Prompt strings. Initialized in main. Settable with
** .prompt main continue
*/
-static char mainPrompt[20]; /* First line prompt. default: "sqlite> "*/
-static char continuePrompt[20]; /* Continuation prompt. default: " ...> " */
-static Prompts shellPrompts = { mainPrompt, continuePrompt };
+#define PROMPT_LEN_MAX 20
+
+/* First line prompt. default: "sqlite> " */
+static char mainPrompt[PROMPT_LEN_MAX];
+/* Continuation prompt. default: " ...> " */
+static char continuePrompt[PROMPT_LEN_MAX];
+#define SET_MAIN_PROMPT(z) strncpy(mainPrompt,z,PROMPT_LEN_MAX-1)
+#define SET_MORE_PROMPT(z) strncpy(continuePrompt,z,PROMPT_LEN_MAX-1);
+/* Prompts as ready to be used by shell's input function */
+static Prompts shellPrompts = { mainPrompt, continuePrompt, 0 };
+
+#ifdef SQLITE_OMIT_DYNAPROMPT
+/*
+** Optionally disable dynamic continuation prompt.
+** Unless disabled, the continuation prompt shows open SQL lexemes if any,
+** or open parentheses level if non-zero, or continuation prompt as set.
+** This facility interacts with the scanner and process_input() where the
+** below 5 macros are used.
+*/
+# define PROMPTS_UPDATE(ika) /**/
+# define CONTINUE_PROMPT_RESET
+# define CONTINUE_PROMPT_AWAITS(p,s)
+# define CONTINUE_PROMPT_AWAITC(p,c)
+# define CONTINUE_PAREN_INCR(p,n)
+# define CONTINUE_PROMPT_PSTATE 0
+typedef void *t_NoDynaPrompt;
+# define SCAN_TRACKER_REFTYPE t_NoDynaPrompt
+
+#else
+# define PROMPTS_UPDATE(ikActionable) \
+ if(ikActionable) dynamicContinuePrompt(); else
+# define CONTINUE_PROMPT_RESET \
+ do {setLexemeOpen(&dynPrompt,0,0); trackParenLevel(&dynPrompt,0);} while(0)
+# define CONTINUE_PROMPT_AWAITS(p,s) \
+ if(p && stdin_is_interactive) setLexemeOpen(p, s, 0)
+# define CONTINUE_PROMPT_AWAITC(p,c) \
+ if(p && stdin_is_interactive) setLexemeOpen(p, 0, c)
+# define CONTINUE_PAREN_INCR(p,n) \
+ if(p && stdin_is_interactive) (trackParenLevel(p,n))
+# define CONTINUE_PROMPT_PSTATE (&dynPrompt)
+typedef struct DynaPrompt *t_DynaPromptRef;
+# define SCAN_TRACKER_REFTYPE t_DynaPromptRef
+
+static struct DynaPrompt {
+ char dynamicPrompt[PROMPT_LEN_MAX];
+ char acAwait[2];
+ int inParenLevel;
+ char *zScannerAwaits;
+} dynPrompt = { {0}, {0}, 0, 0 };
+
+/* Record parenthesis nesting level change, or force level to 0. */
+static void trackParenLevel(struct DynaPrompt *p, int ni){
+ p->inParenLevel += ni;
+ if( ni==0 ) p->inParenLevel = 0;
+ p->zScannerAwaits = 0;
+}
+
+/* Record that a lexeme is opened, or closed with args==0. */
+static void setLexemeOpen(struct DynaPrompt *p, char *s, char c){
+ if( s!=0 || c==0 ){
+ p->zScannerAwaits = s;
+ p->acAwait[0] = 0;
+ }else{
+ p->acAwait[0] = c;
+ p->zScannerAwaits = p->acAwait;
+ }
+}
+
+/* Upon demand, derive the continuation prompt to display. */
+static void dynamicContinuePrompt(void){
+ if( continuePrompt[0]==0
+ || (dynPrompt.zScannerAwaits==0 && dynPrompt.inParenLevel == 0) ){
+ plain_continuation:
+ shellPrompts.zContinue = continuePrompt;
+ return;
+ }
+ if( dynPrompt.zScannerAwaits ){
+ size_t ncp = strlen(continuePrompt);
+ size_t ndp = strlen(dynPrompt.zScannerAwaits);
+ if( ndp > ncp-3 ) goto plain continuation;
+ strcpy(dynPrompt.dynamicPrompt, dynPrompt.zScannerAwaits);
+ while( ndp<3 ) dynPrompt.dynamicPrompt[ndp++] = ' ';
+ strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3,
+ PROMPT_LEN_MAX-4);
+ }else{
+ if( dynPrompt.inParenLevel>9 ){
+ strncpy(dynPrompt.dynamicPrompt, "(..", 4);
+ }else if( dynPrompt.inParenLevel<0 ){
+ strncpy(dynPrompt.dynamicPrompt, ")x!", 4);
+ }else{
+ strncpy(dynPrompt.dynamicPrompt, "(x.", 4);
+ dynPrompt.dynamicPrompt[2] = (char)('0'+dynPrompt.inParenLevel);
+ }
+ strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3, PROMPT_LEN_MAX-4);
+ }
+ shellPrompts.zContinue = dynPrompt.dynamicPrompt;
+}
+#endif /* !defined(SQLITE_OMIT_DYNAPROMPT) */
/*
** Render output like fprintf(). Except, if the output is going to the
@@ -567,6 +693,7 @@
int i;
int n;
int aw = w<0 ? -w : w;
+ if( zUtf==0 ) zUtf = "";
for(i=n=0; zUtf[i]; i++){
if( (zUtf[i]&0xc0)!=0x80 ){
n++;
@@ -772,7 +899,7 @@
if( stdin_is_interactive && pInSrc==&stdInSource ){
char *zTrans = sqlite3_win32_mbcs_to_utf8_v2(zLine, 0);
if( zTrans ){
- int nTrans = strlen30(zTrans)+1;
+ i64 nTrans = strlen(zTrans)+1;
if( nTrans>nLine ){
zLine = realloc(zLine, nTrans);
shell_check_oom(zLine);
@@ -785,6 +912,7 @@
return zLine;
}
+#ifndef SQLITE_SHELL_FIDDLE
/*
** Retrieve a single line of input text from designated input source.
**
@@ -816,7 +944,7 @@
if( !INSOURCE_IS_INTERACTIVE(pInSrc) ){
return local_getline(zPrior, pInSrc);
}else{
- static Prompts cueDefault = { "$ ","> " };
+ static Prompts cueDefault = { "$ ","> ", 0 };
const char *zPrompt;
if( pCue==0 ) pCue = &cueDefault;
zPrompt = isContinuation ? pCue->zContinue : pCue->zMain;
@@ -834,6 +962,38 @@
#endif
}
}
+#else /* !defined(SQLITE_SHELL_FIDDLE) */
+/*
+** Alternate one_input_line() 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;
+ i64 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, nZ);
+ zLine[nZ] = 0;
+ return zLine;
+}
+#endif /* SQLITE_SHELL_FIDDLE */
/* For use by shell extensions. See footnote [a] to above function. */
void free_input_line(char *z){
@@ -926,10 +1086,10 @@
** 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){
- int len;
- int i;
- int nAppend = strlen30(zAppend);
+static void appendText(ShellText *p, const char *zAppend, char quote){
+ i64 len;
+ i64 i;
+ i64 nAppend = strlen30(zAppend);
len = nAppend+p->n+1;
if( quote ){
@@ -1085,10 +1245,10 @@
const char *zName = (const char*)sqlite3_value_text(apVal[2]);
sqlite3 *db = sqlite3_context_db_handle(pCtx);
UNUSED_PARAMETER(nVal);
- if( zIn!=0 && strncmp(zIn, "CREATE ", 7)==0 ){
+ if( zIn!=0 && cli_strncmp(zIn, "CREATE ", 7)==0 ){
for(i=0; i<ArraySize(aPrefix); i++){
int n = strlen30(aPrefix[i]);
- if( strncmp(zIn+7, aPrefix[i], n)==0 && zIn[n+7]==' ' ){
+ if( cli_strncmp(zIn+7, aPrefix[i], n)==0 && zIn[n+7]==' ' ){
char *z = 0;
char *zFake = 0;
if( zSchema ){
@@ -1144,6 +1304,11 @@
INCLUDE ../ext/misc/ieee754.c
INCLUDE ../ext/misc/series.c
INCLUDE ../ext/misc/regexp.c
+#ifndef SQLITE_SHELL_FIDDLE
+INCLUDE ../ext/misc/fileio.c
+INCLUDE ../ext/misc/completion.c
+INCLUDE ../ext/misc/appendvfs.c
+#endif
#ifdef SQLITE_HAVE_ZLIB
INCLUDE ../ext/misc/zipfile.c
INCLUDE ../ext/misc/sqlar.c
@@ -1152,7 +1317,19 @@
INCLUDE ../ext/expert/sqlite3expert.c
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
-INCLUDE ../ext/misc/dbdata.c
+# define SQLITE_SHELL_HAVE_RECOVER 1
+#else
+# define SQLITE_SHELL_HAVE_RECOVER 0
+#endif
+#if SQLITE_SHELL_HAVE_RECOVER
+INCLUDE ../ext/recover/sqlite3recover.h
+# ifndef SQLITE_HAVE_SQLITE3R
+INCLUDE ../ext/recover/dbdata.c
+INCLUDE ../ext/recover/sqlite3recover.c
+# endif
+#endif
+#ifdef SQLITE_SHELL_EXTSRC
+# include SHELL_STRINGIFY(SQLITE_SHELL_EXTSRC)
#endif
#if defined(SQLITE_ENABLE_SESSION)
@@ -1419,6 +1596,14 @@
EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */
ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */
+#ifdef SQLITE_SHELL_FIDDLE
+ struct {
+ const char * zInput; /* Input string from wasm/JS proxy */
+ const char * zPos; /* Cursor pos into zInput */
+ const char * zDefaultDbName; /* Default name for db file */
+ } wasm;
+#endif
+
#if SHELL_DYNAMIC_EXTENSION
/* extension management */
int numExtLoaded; /* Number of extensions presently loaded or emulated */
@@ -1447,6 +1632,11 @@
ShellExState *pSXS; /* Pointer to companion, exposed shell state */
} ShellInState;
+#ifdef SQLITE_SHELL_FIDDLE
+/* For WASM, keep a static instance so pseudo-main can be called repeatedly. */
+static ShellInState shellState;
+#endif
+
/*
** Limit input nesting via .read or any other input redirect.
** It's not too expensive, so a generous allowance can be made.
@@ -1515,7 +1705,7 @@
#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_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 */
@@ -1913,10 +2103,21 @@
*/
static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){
int i;
- char *zBlob = (char *)pBlob;
- raw_printf(out,"X'");
- for(i=0; i<nBlob; i++){ raw_printf(out,"%02x",zBlob[i]&0xff); }
- raw_printf(out,"'");
+ unsigned char *aBlob = (unsigned char*)pBlob;
+
+ char *zStr = sqlite3_malloc(nBlob*2 + 1);
+ shell_check_oom(zStr);
+ for(i=0; i<nBlob; i++){
+ static const char aHex[] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+ };
+ zStr[i*2] = aHex[ (aBlob[i] >> 4) ];
+ zStr[i*2+1] = aHex[ (aBlob[i] & 0x0F) ];
+ }
+ zStr[i*2] = '\0';
+ raw_printf(out,"X'%s'", zStr);
+ sqlite3_free(zStr);
}
/*
@@ -2076,9 +2277,9 @@
/*
** Output the given string as a quoted according to JSON quoting rules.
*/
-static void output_json_string(FILE *out, const char *z, int n){
+static void output_json_string(FILE *out, const char *z, i64 n){
unsigned int c;
- if( n<0 ) n = (int)strlen(z);
+ if( n<0 ) n = strlen(z);
fputc('"', out);
while( n-- ){
c = *(z++);
@@ -2253,19 +2454,22 @@
"zipfile",
"zipfile_cds",
};
- UNUSED_PARAMETER(zA2);
+ UNUSED_PARAMETER(zA1);
UNUSED_PARAMETER(zA3);
UNUSED_PARAMETER(zA4);
switch( op ){
case SQLITE_ATTACH: {
+#ifndef SQLITE_SHELL_FIDDLE
+ /* In WASM builds the filesystem is a virtual sandbox, so allow ATTACH. */
if ( failIfSafeMode(psx, "cannot run ATTACH in safe mode") )
return SQLITE_ERROR;
+#endif
break;
}
case SQLITE_FUNCTION: {
int i;
for(i=0; i<ArraySize(azProhibitedFunctions); i++){
- if( sqlite3_stricmp(zA1, azProhibitedFunctions[i])==0 ){
+ if( sqlite3_stricmp(zA2, azProhibitedFunctions[i])==0 ){
if( failIfSafeMode(psx, "cannot use the %s() function in safe mode",
azProhibitedFunctions[i]) )
return SQLITE_ERROR;
@@ -2330,15 +2534,37 @@
**
** This routine converts some CREATE TABLE statements for shadow tables
** in FTS3/4/5 into CREATE TABLE IF NOT EXISTS statements.
+**
+** If the schema statement in z[] contains a start-of-comment and if
+** sqlite3_complete() returns false, try to terminate the comment before
+** printing the result. https://sqlite.org/forum/forumpost/d7be961c5c
*/
static void printSchemaLine(FILE *out, const char *z, const char *zTail){
+ char *zToFree = 0;
if( z==0 ) return;
if( zTail==0 ) return;
+ if( zTail[0]==';' && (strstr(z, "/*")!=0 || strstr(z,"--")!=0) ){
+ const char *zOrig = z;
+ static const char *azTerm[] = { "", "*/", "\n" };
+ int i;
+ for(i=0; i<ArraySize(azTerm); i++){
+ char *zNew = sqlite3_mprintf("%s%s;", zOrig, azTerm[i]);
+ if( sqlite3_complete(zNew) ){
+ size_t n = strlen(zNew);
+ zNew[n-1] = 0;
+ zToFree = zNew;
+ z = zNew;
+ break;
+ }
+ sqlite3_free(zNew);
+ }
+ }
if( sqlite3_strglob("CREATE TABLE ['\"]*", z)==0 ){
utf8_printf(out, "CREATE TABLE IF NOT EXISTS %s%s", z+13, zTail);
}else{
utf8_printf(out, "%s%s", z, zTail);
}
+ sqlite3_free(zToFree);
}
static void printSchemaLineN(FILE *out, char *z, int n, const char *zTail){
char c = z[n];
@@ -2368,7 +2594,9 @@
static void eqp_append(ShellInState *psi, int iEqpId, int p2,
const char *zText){
EQPGraphRow *pNew;
- int nText = strlen30(zText);
+ i64 nText;
+ if( zText==0 ) return;
+ nText = strlen(zText);
if( psi->autoEQPtest ){
utf8_printf(psi->out, "%d,%d,%s\n", iEqpId, p2, zText);
}
@@ -2414,14 +2642,14 @@
*/
static void eqp_render_level(ShellInState *psi, int iEqpId){
EQPGraphRow *pRow, *pNext;
- int n = strlen30(psi->sGraph.zPrefix);
+ i64 n = strlen(psi->sGraph.zPrefix);
char *z;
for(pRow = eqp_next_row(psi, iEqpId, 0); pRow; pRow = pNext){
pNext = eqp_next_row(psi, iEqpId, pRow);
z = pRow->zText;
utf8_printf(psi->out, "%s%s%s\n", psi->sGraph.zPrefix,
pNext ? "|--" : "`--", z);
- if( n<(int)sizeof(psi->sGraph.zPrefix)-7 ){
+ if( n<(i64)sizeof(psi->sGraph.zPrefix)-7 ){
memcpy(&psi->sGraph.zPrefix[n], pNext ? "| " : " ", 4);
eqp_render_level(psi, pRow->iEqpId);
psi->sGraph.zPrefix[n] = 0;
@@ -2432,7 +2660,7 @@
/*
** Display and reset the EXPLAIN QUERY PLAN data
*/
-static void eqp_render(ShellInState *psi){
+static void eqp_render(ShellInState *psi, i64 nCycle){
EQPGraphRow *pRow = psi->sGraph.pRow;
if( pRow ){
if( pRow->zText[0]=='-' ){
@@ -2443,6 +2671,8 @@
utf8_printf(psi->out, "%s\n", pRow->zText+3);
psi->sGraph.pRow = pRow->pNext;
sqlite3_free(pRow);
+ }else if( nCycle>0 ){
+ utf8_printf(psi->out, "QUERY PLAN (cycles=%lld [100%%])\n", nCycle);
}else{
utf8_printf(psi->out, "QUERY PLAN\n");
}
@@ -3120,6 +3350,7 @@
while( (zSql[len]&0xc0)==0x80 ) len--;
}
zCode = smprintf("%.*s", len, zSql);
+ shell_check_oom(zCode);
for(i=0; zCode[i]; i++){ if( IsSpace(zSql[i]) ) zCode[i] = ' '; }
if( iOffset<25 ){
zMsg = smprintf("\n %z\n %*s^--- error here",
@@ -3240,7 +3471,7 @@
int i;
for(i=0; i<ArraySize(aTrans); i++){
int n = strlen30(aTrans[i].zPattern);
- if( strncmp(aTrans[i].zPattern, z, n)==0 ){
+ if( cli_strncmp(aTrans[i].zPattern, z, n)==0 ){
utf8_printf(out, "%-36s %s", aTrans[i].zDesc, &z[n]);
break;
}
@@ -3422,47 +3653,113 @@
return 0;
}
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+static int scanStatsHeight(sqlite3_stmt *p, int iEntry){
+ int iPid = 0;
+ int ret = 1;
+ sqlite3_stmt_scanstatus_v2(p, iEntry,
+ SQLITE_SCANSTAT_SELECTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iPid
+ );
+ while( iPid!=0 ){
+ int ii;
+ for(ii=0; 1; ii++){
+ int iId;
+ int res;
+ res = sqlite3_stmt_scanstatus_v2(p, ii,
+ SQLITE_SCANSTAT_SELECTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iId
+ );
+ if( res ) break;
+ if( iId==iPid ){
+ sqlite3_stmt_scanstatus_v2(p, ii,
+ SQLITE_SCANSTAT_PARENTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iPid
+ );
+ }
+ }
+ ret++;
+ }
+ return ret;
+}
+#endif
+
/*
** Display scan stats.
*/
-static void display_scanstats(ShellInState *psi){
+static void display_scanstats(sqlite3 *db, ShellInState *psi){
#ifndef SQLITE_ENABLE_STMT_SCANSTATUS
+ UNUSED_PARAMETER(db);
UNUSED_PARAMETER(psi);
#else
- int i, k, n, mx;
- raw_printf(psi->out, "-------- scanstats --------\n");
- mx = 0;
- for(k=0; k<=mx; k++){
- double rEstLoop = 1.0;
- for(i=n=0; 1; i++){
- sqlite3_stmt *p = psi->pStmt;
- sqlite3_int64 nLoop, nVisit;
- double rEst;
- int iSid;
- const char *zExplain;
- if( sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_NLOOP, (void*)&nLoop) ){
- break;
+ static const int f = SQLITE_SCANSTAT_COMPLEX;
+ sqlite3_stmt *p = psi->pStmt;
+ int ii = 0;
+ i64 nTotal = 0;
+ int nWidth = 0;
+ eqp_reset(psi);
+
+ for(ii=0; 1; ii++){
+ const char *z = 0;
+ int n = 0;
+ if( sqlite3_stmt_scanstatus_v2(p,ii,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&z) ){
+ break;
+ }
+ n = strlen(z) + scanStatsHeight(p, ii)*3;
+ if( n>nWidth ) nWidth = n;
+ }
+ nWidth += 4;
+
+ sqlite3_stmt_scanstatus_v2(p, -1, SQLITE_SCANSTAT_NCYCLE, f, (void*)&nTotal);
+ for(ii=0; 1; ii++){
+ i64 nLoop = 0;
+ i64 nRow = 0;
+ i64 nCycle = 0;
+ int iId = 0;
+ int iPid = 0;
+ const char *z = 0;
+ const char *zName = 0;
+ char *zText = 0;
+ double rEst = 0.0;
+
+ if( sqlite3_stmt_scanstatus_v2(p,ii,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&z) ){
+ break;
+ }
+ sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_EST,f,(void*)&rEst);
+ sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NLOOP,f,(void*)&nLoop);
+ sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NVISIT,f,(void*)&nRow);
+ sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NCYCLE,f,(void*)&nCycle);
+ sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_SELECTID,f,(void*)&iId);
+ sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_PARENTID,f,(void*)&iPid);
+ sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NAME,f,(void*)&zName);
+
+ zText = sqlite3_mprintf("%s", z);
+ if( nCycle>=0 || nLoop>=0 || nRow>=0 ){
+ char *z = 0;
+ if( nCycle>=0 && nTotal>0 ){
+ z = sqlite3_mprintf("%zcycles=%lld [%d%%]", z,
+ nCycle, ((nCycle*100)+nTotal/2) / nTotal
+ );
}
- sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_SELECTID, (void*)&iSid);
- if( iSid>mx ) mx = iSid;
- if( iSid!=k ) continue;
- if( n==0 ){
- rEstLoop = (double)nLoop;
- if( k>0 ) raw_printf(psi->out, "-------- subquery %d -------\n", k);
+ if( nLoop>=0 ){
+ z = sqlite3_mprintf("%z%sloops=%lld", z, z ? " " : "", nLoop);
}
- n++;
- sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_NVISIT, (void*)&nVisit);
- sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_EST, (void*)&rEst);
- sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_EXPLAIN, (void*)&zExplain);
- utf8_printf(psi->out, "Loop %2d: %s\n", n, zExplain);
- rEstLoop *= rEst;
- raw_printf(psi->out,
- " nLoop=%-8lld nRow=%-8lld estRow=%-8lld estRow/Loop=%-8g\n",
- nLoop, nVisit, (sqlite3_int64)(rEstLoop+0.5), rEst
+ if( nRow>=0 ){
+ z = sqlite3_mprintf("%z%srows=%lld", z, z ? " " : "", nRow);
+ }
+
+ if( zName && psi->scanstatsOn>1 ){
+ double rpl = (double)nRow / (double)nLoop;
+ z = sqlite3_mprintf("%z rpl=%.1f est=%.1f", z, rpl, rEst);
+ }
+
+ zText = sqlite3_mprintf(
+ "% *z (%z)", -1*(nWidth-scanStatsHeight(p, ii)*3), zText, z
);
}
+
+ eqp_append(psi, iId, iPid, zText);
+ sqlite3_free(zText);
}
- raw_printf(psi->out, "---------------------------\n");
+
+ eqp_render(psi, nTotal);
#endif
}
@@ -3475,7 +3772,7 @@
static int str_in_array(const char *zStr, const char **azArray){
int i;
for(i=0; azArray[i]; i++){
- if( 0==strcmp(zStr, azArray[i]) ) return 1;
+ if( 0==cli_strcmp(zStr, azArray[i]) ) return 1;
}
return 0;
}
@@ -3550,7 +3847,7 @@
"addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment" };
int jj;
for(jj=0; jj<ArraySize(explainCols); jj++){
- if( strcmp(sqlite3_column_name(pSql,jj),explainCols[jj])!=0 ){
+ if( cli_strcmp(sqlite3_column_name(pSql,jj),explainCols[jj])!=0 ){
psi->cMode = psi->mode;
sqlite3_reset(pSql);
return;
@@ -3673,7 +3970,7 @@
}
if( zEd && zEd[0]=='-' ){
zEd += 1 + (zEd[1]=='-');
- if( strncmp(zEd,"editor=",7)==0 ){
+ if( cli_strncmp(zEd,"editor=",7)==0 ){
sqlite3_free(psi->zEditor);
/* Accept an initial -editor=? option. */
psi->zEditor = smprintf("%s", zEd+7);
@@ -4467,9 +4764,6 @@
psx->resultCount = 0;
}
- /* echo the sql statement if echo on */
- if( psx ) echo_group_input( psi, zStmtSql ? zStmtSql : zSql);
-
/* Show the EXPLAIN QUERY PLAN if .eqp is on */
if( psx && psi->autoEQP && sqlite3_stmt_isexplain(pStmt)==0 ){
sqlite3_stmt *pExplain;
@@ -4489,10 +4783,10 @@
int iEqpId = sqlite3_column_int(pExplain, 0);
int iParentId = sqlite3_column_int(pExplain, 1);
if( zEQPLine==0 ) zEQPLine = "";
- if( zEQPLine[0]=='-' ) eqp_render(psi);
+ if( zEQPLine[0]=='-' ) eqp_render(psi, 0);
eqp_append(psi, iEqpId, iParentId, zEQPLine);
}
- eqp_render(psi);
+ eqp_render(psi, 0);
}
sqlite3_finalize(pExplain);
sqlite3_free(zEQP);
@@ -4550,7 +4844,7 @@
bind_prepared_stmt(DBX(psx), pStmt);
exec_prepared_stmt(psx, pStmt);
explain_data_delete(psi);
- eqp_render(psi);
+ eqp_render(psi, 0);
/* print usage stats if stats on */
if( psx && psi->statsOn ){
@@ -4736,18 +5030,19 @@
zTable = azArg[0];
zType = azArg[1];
zSql = azArg[2];
+ if( zTable==0 || zType==0 ) return 0;
dataOnly = (psi->shellFlgs & SHFLG_DumpDataOnly)!=0;
noSys = (psi->shellFlgs & SHFLG_DumpNoSys)!=0;
- if( strcmp(zTable, "sqlite_sequence")==0 && !noSys ){
+ if( cli_strcmp(zTable, "sqlite_sequence")==0 && !noSys ){
if( !dataOnly ) raw_printf(psi->out, "DELETE FROM sqlite_sequence;\n");
}else if( sqlite3_strglob("sqlite_stat?", zTable)==0 && !noSys ){
if( !dataOnly ) raw_printf(psi->out, "ANALYZE sqlite_schema;\n");
- }else if( psi->bAllowSysDump==0 && strncmp(zTable, "sqlite_", 7)==0 ){
+ }else if( psi->bAllowSysDump==0 && cli_strncmp(zTable, "sqlite_", 7)==0 ){
return 0;
}else if( dataOnly ){
/* no-op */
- }else if( strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){
+ }else if( cli_strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){
char *zIns;
if( !psi->writableSchema ){
raw_printf(psi->out, "PRAGMA writable_schema=ON;\n");
@@ -4764,7 +5059,7 @@
printSchemaLine(psi->out, zSql, ";\n");
}
- if( strcmp(zType, "table")==0 ){
+ if( cli_strcmp(zType, "table")==0 ){
ShellText sSelect;
ShellText sTable;
char **azCol;
@@ -5086,7 +5381,7 @@
iOffset = k;
continue;
}
- if( strncmp(zLine, zEndMarker, 6)==0 ){
+ if( cli_strncmp(zLine, zEndMarker, 6)==0 ){
break;
}
rc = sscanf(zLine,"| %d: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x",
@@ -5113,7 +5408,7 @@
if( psi->pInSource!=&inRedir ){
/* Since taking input inline, consume through its end marker. */
while( strLineGet(zLine, sizeof(zLine), psi->pInSource)!=0 ){
- if(strncmp(zLine, zEndMarker, 6)==0 ) break;
+ if(cli_strncmp(zLine, zEndMarker, 6)==0 ) break;
}
}
sqlite3_free(a);
@@ -5205,28 +5500,28 @@
const char *zText = (const char*)sqlite3_value_text(argv[0]);
UNUSED_PARAMETER(argc);
if( zText && zText[0]=='\'' ){
- int nText = sqlite3_value_bytes(argv[0]);
- int i;
+ i64 nText = sqlite3_value_bytes(argv[0]);
+ i64 i;
char zBuf1[20];
char zBuf2[20];
const char *zNL = 0;
const char *zCR = 0;
- int nCR = 0;
- int nNL = 0;
+ i64 nCR = 0;
+ i64 nNL = 0;
for(i=0; zText[i]; i++){
if( zNL==0 && zText[i]=='\n' ){
zNL = unused_string(zText, "\\n", "\\012", zBuf1);
- nNL = (int)strlen(zNL);
+ nNL = strlen(zNL);
}
if( zCR==0 && zText[i]=='\r' ){
zCR = unused_string(zText, "\\r", "\\015", zBuf2);
- nCR = (int)strlen(zCR);
+ nCR = strlen(zCR);
}
}
if( zNL || zCR ){
- int iOut = 0;
+ i64 iOut = 0;
i64 nMax = (nNL > nCR) ? nNL : nCR;
i64 nAlloc = nMax * nText + (nMax+64)*2;
char *zOut = (char*)sqlite3_malloc64(nAlloc);
@@ -5349,15 +5644,17 @@
#ifndef SQLITE_OMIT_LOAD_EXTENSION
sqlite3_enable_load_extension(globalDb, 1);
#endif
- sqlite3_fileio_init(globalDb, 0, 0);
sqlite3_shathree_init(globalDb, 0, 0);
- sqlite3_completion_init(globalDb, 0, 0);
sqlite3_uint_init(globalDb, 0, 0);
sqlite3_decimal_init(globalDb, 0, 0);
sqlite3_regexp_init(globalDb, 0, 0);
sqlite3_ieee_init(globalDb, 0, 0);
sqlite3_series_init(globalDb, 0, 0);
-#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
+#ifndef SQLITE_SHELL_FIDDLE
+ sqlite3_fileio_init(globalDb, 0, 0);
+ sqlite3_completion_init(globalDb, 0, 0);
+#endif
+#if SQLITE_SHELL_HAVE_RECOVER
sqlite3_dbdata_init(globalDb, 0, 0);
#endif
#ifdef SQLITE_HAVE_ZLIB
@@ -5366,6 +5663,35 @@
sqlite3_sqlar_init(globalDb, 0, 0);
}
#endif
+
+#ifdef SQLITE_SHELL_EXTFUNCS
+ /* Create a preprocessing mechanism for extensions to make
+ * their own provisions for being built into the shell.
+ * This is a short-span macro. See further below for usage.
+ */
+#define SHELL_SUB_MACRO(base, variant) base ## _ ## variant
+#define SHELL_SUBMACRO(base, variant) SHELL_SUB_MACRO(base, variant)
+ /* Let custom-included extensions get their ..._init() called.
+ * The WHATEVER_INIT( db, pzErrorMsg, pApi ) macro should cause
+ * the extension's sqlite3_*_init( db, pzErrorMsg, pApi )
+ * inititialization routine to be called.
+ */
+ {
+ int irc = SHELL_SUBMACRO(SQLITE_SHELL_EXTFUNCS, INIT)(p->db);
+ /* Let custom-included extensions expose their functionality.
+ * The WHATEVER_EXPOSE( db, pzErrorMsg ) macro should cause
+ * the SQL functions, virtual tables, collating sequences or
+ * VFS's implemented by the extension to be registered.
+ */
+ if( irc==SQLITE_OK
+ || irc==SQLITE_OK_LOAD_PERMANENTLY ){
+ SHELL_SUBMACRO(SQLITE_SHELL_EXTFUNCS, EXPOSE)(p->db, 0);
+ }
+#undef SHELL_SUB_MACRO
+#undef SHELL_SUBMACRO
+ }
+#endif
+
sqlite3_create_function(globalDb, "shell_add_schema", 3, SQLITE_UTF8, 0,
shellAddSchemaName, 0, 0);
sqlite3_create_function(globalDb, "shell_module_schema", 1, SQLITE_UTF8, 0,
@@ -5487,8 +5813,8 @@
** Linenoise completion callback
*/
static void linenoise_completion(const char *zLine, linenoiseCompletions *lc){
- int nLine = strlen30(zLine);
- int i, iStart;
+ i64 nLine = strlen(zLine);
+ i64 i, iStart;
sqlite3_stmt *pStmt = 0;
char *zSql;
char zBuf[1000];
@@ -5630,11 +5956,11 @@
*/
static FILE *output_file_open(const char *zFile, int bTextMode){
FILE *f;
- if( strcmp(zFile,"stdout")==0 ){
+ if( cli_strcmp(zFile,"stdout")==0 ){
f = STD_OUT;
- }else if( strcmp(zFile, "stderr")==0 ){
+ }else if( cli_strcmp(zFile, "stderr")==0 ){
f = STD_ERR;
- }else if( strcmp(zFile, "off")==0 ){
+ }else if( cli_strcmp(zFile, "off")==0 ){
f = 0;
}else{
f = fopen(zFile, bTextMode ? "w" : "wb");
@@ -5658,7 +5984,7 @@
ShellInState *psi = (ShellInState*)pArg;
sqlite3_stmt *pStmt;
const char *zSql;
- int nSql;
+ i64 nSql;
if( psi->traceOut==0 ) return 0;
if( mType==SQLITE_TRACE_CLOSE ){
utf8_printf(psi->traceOut, "-- closing database connection\n");
@@ -5686,17 +6012,18 @@
}
}
if( zSql==0 ) return 0;
- nSql = strlen30(zSql);
+ nSql = strlen(zSql);
+ if( nSql>1000000000 ) nSql = 1000000000; /* clamp to 1 billion */
while( nSql>0 && zSql[nSql-1]==';' ){ nSql--; }
switch( mType ){
case SQLITE_TRACE_ROW:
case SQLITE_TRACE_STMT: {
- utf8_printf(psi->traceOut, "%.*s;\n", nSql, zSql);
+ utf8_printf(psi->traceOut, "%.*s;\n", (int)nSql, zSql);
break;
}
case SQLITE_TRACE_PROFILE: {
sqlite3_int64 nNanosec = *(sqlite3_int64*)pX;
- utf8_printf(psi->traceOut, "%.*s; -- %lld ns\n", nSql, zSql, nNanosec);
+ utf8_printf(psi->traceOut,"%.*s; -- %lld ns\n", (int)nSql,zSql,nNanosec);
break;
}
}
@@ -6177,6 +6504,7 @@
return zRes;
}
+#if SQLITE_SHELL_HAVE_RECOVER
/*
** Convert a 2-byte or 4-byte big-endian integer into a native integer
*/
@@ -6269,7 +6597,7 @@
}
if( zDb==0 ){
zSchemaTab = smprintf("main.sqlite_schema");
- }else if( strcmp(zDb,"temp")==0 ){
+ }else if( cli_strcmp(zDb,"temp")==0 ){
zSchemaTab = smprintf("%s", "sqlite_temp_schema");
}else{
zSchemaTab = smprintf("\"%w\".sqlite_schema", zDb);
@@ -6285,6 +6613,7 @@
utf8_printf(out, "%-20s %u\n", "data version", iDataVersion);
return 0;
}
+#endif /* SQLITE_SHELL_HAVE_RECOVER */
/*
** Print the current sqlite3_errmsg() value to stderr and return 1.
@@ -6399,7 +6728,7 @@
if( zStr[0]!='-' ) return 0;
zStr++;
if( zStr[0]=='-' ) zStr++;
- return strcmp(zStr, zOpt)==0;
+ return cli_strcmp(zStr, zOpt)==0;
}
/*
@@ -6606,7 +6935,8 @@
}
#endif /* !defined SQLITE_OMIT_VIRTUALTABLE */
-#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) \
+ && !defined(SQLITE_SHELL_FIDDLE)
/******************************************************************************
** The ".archive" or ".ar" command.
*/
@@ -7368,368 +7698,83 @@
*******************************************************************************/
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) */
-#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
+#if SQLITE_SHELL_HAVE_RECOVER
/*
-** If (*pRc) is not SQLITE_OK when this function is called, it is a no-op.
-** Otherwise, the SQL statement or statements in zSql are executed using
-** database connection db and the error code written to *pRc before
-** this function returns.
+** This function is used as a callback by the recover extension. Simply
+** print the supplied SQL statement to stdout.
*/
-static void shellExec(sqlite3 *db, int *pRc, const char *zSql){
- int rc = *pRc;
- if( rc==SQLITE_OK ){
- char *zErr = 0;
- rc = sqlite3_exec(db, zSql, 0, 0, &zErr);
- if( rc!=SQLITE_OK ){
- raw_printf(STD_ERR, "SQL error: %s\n", zErr);
- }
- sqlite3_free(zErr);
- *pRc = rc;
- }
+static int recoverSqlCb(void *pCtx, const char *zSql){
+ ShellInState *pState = (ShellInState*)pCtx;
+ utf8_printf(pState->out, "%s;\n", zSql); /* ToDo: get right member here. */
+ return SQLITE_OK;
}
/*
-** Like shellExec(), except that zFmt is a printf() style format string.
+** This function is called to recover data from the database. A script
+** to construct a new database containing all recovered data is output
+** on stream pState->out.
*/
-static void shellExecPrintf(sqlite3 *db, int *pRc, const char *zFmt, ...){
- char *z = 0;
- if( *pRc==SQLITE_OK ){
- va_list ap;
- va_start(ap, zFmt);
- z = sqlite3_vmprintf(zFmt, ap);
- va_end(ap);
- if( z==0 ){
- *pRc = SQLITE_NOMEM;
- }else{
- shellExec(db, pRc, z);
+static int recoverDatabaseCmd(ShellInState *pState, int nArg, char **azArg){
+ int rc = SQLITE_OK;
+ const char *zRecoveryDb = ""; /* Name of "recovery" database. Debug only */
+ const char *zLAF = "lost_and_found";
+ int bFreelist = 1; /* 0 if --ignore-freelist is specified */
+ int bRowids = 1; /* 0 if --no-rowids */
+ sqlite3_recover *p = 0;
+ int i = 0;
+
+ for(i=1; i<nArg; i++){
+ char *z = azArg[i];
+ int n;
+ if( z[0]=='-' && z[1]=='-' ) z++;
+ n = strlen30(z);
+ if( n<=17 && memcmp("-ignore-freelist", z, n)==0 ){
+ bFreelist = 0;
+ }else
+ if( n<=12 && memcmp("-recovery-db", z, n)==0 && i<(nArg-1) ){
+ /* This option determines the name of the ATTACH-ed database used
+ ** internally by the recovery extension. The default is "" which
+ ** means to use a temporary database that is automatically deleted
+ ** when closed. This option is undocumented and might disappear at
+ ** any moment. */
+ i++;
+ zRecoveryDb = azArg[i];
+ }else
+ if( n<=15 && memcmp("-lost-and-found", z, n)==0 && i<(nArg-1) ){
+ i++;
+ zLAF = azArg[i];
+ }else
+ if( n<=10 && memcmp("-no-rowids", z, n)==0 ){
+ bRowids = 0;
}
- sqlite3_free(z);
- }
-}
-
-/*
-** If *pRc is not SQLITE_OK when this function is called, it is a no-op.
-** Otherwise, an attempt is made to allocate, zero and return a pointer
-** to a buffer nByte bytes in size. If an OOM error occurs, *pRc is set
-** to SQLITE_NOMEM and NULL returned.
-*/
-static void *shellMalloc(int *pRc, sqlite3_int64 nByte){
- void *pRet = 0;
- if( *pRc==SQLITE_OK ){
- pRet = sqlite3_malloc64(nByte);
- if( pRet==0 ){
- *pRc = SQLITE_NOMEM;
- }else{
- memset(pRet, 0, nByte);
- }
- }
- return pRet;
-}
-
-static char *shellMPrintf(int *pRc, const char *zFmt, ...);
-
-/*
-** When running the ".recover" command, each output table, and the special
-** orphaned row table if it is required, is represented by an instance
-** of the following struct.
-*/
-typedef struct RecoverTable RecoverTable;
-struct RecoverTable {
- char *zQuoted; /* Quoted version of table name */
- int nCol; /* Number of columns in table */
- char **azlCol; /* Array of column lists */
- int iPk; /* Index of IPK column */
-};
-
-/*
-** Free a RecoverTable object allocated by recoverFindTable() or
-** recoverOrphanTable().
-*/
-static void recoverFreeTable(RecoverTable *pTab){
- if( pTab ){
- sqlite3_free(pTab->zQuoted);
- if( pTab->azlCol ){
- int i;
- for(i=0; i<=pTab->nCol; i++){
- sqlite3_free(pTab->azlCol[i]);
- }
- sqlite3_free(pTab->azlCol);
- }
- sqlite3_free(pTab);
- }
-}
-
-/*
-** This function is a no-op if (*pRc) is not SQLITE_OK when it is called.
-** Otherwise, it allocates and returns a RecoverTable object based on the
-** final four arguments passed to this function. It is the responsibility
-** of the caller to eventually free the returned object using
-** recoverFreeTable().
-*/
-static RecoverTable *recoverNewTable(
- int *pRc, /* IN/OUT: Error code */
- const char *zName, /* Name of table */
- const char *zSql, /* CREATE TABLE statement */
- int bIntkey,
- int nCol
-){
- sqlite3 *dbtmp = 0; /* sqlite3 handle for testing CREATE TABLE */
- int rc = *pRc;
- RecoverTable *pTab = 0;
-
- pTab = (RecoverTable*)shellMalloc(&rc, sizeof(RecoverTable));
- if( rc==SQLITE_OK ){
- int nSqlCol = 0;
- int bSqlIntkey = 0;
- sqlite3_stmt *pStmt = 0;
-
- rc = sqlite3_open("", &dbtmp);
- if( rc==SQLITE_OK ){
- sqlite3_create_function(dbtmp, "shell_idquote", 1, SQLITE_UTF8, 0,
- shellIdQuote, 0, 0);
- }
- if( rc==SQLITE_OK ){
- rc = sqlite3_exec(dbtmp, "PRAGMA writable_schema = on", 0, 0, 0);
- }
- if( rc==SQLITE_OK ){
- rc = sqlite3_exec(dbtmp, zSql, 0, 0, 0);
- if( rc==SQLITE_ERROR ){
- rc = SQLITE_OK;
- goto finished;
- }
- }
- shellPreparePrintf(dbtmp, &rc, &pStmt,
- "SELECT count(*) FROM pragma_table_info(%Q)", zName
- );
- if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
- nSqlCol = sqlite3_column_int(pStmt, 0);
- }
- shellFinalize(&rc, pStmt);
-
- if( rc!=SQLITE_OK || nSqlCol<nCol ){
- goto finished;
- }
-
- shellPreparePrintf(dbtmp, &rc, &pStmt,
- "SELECT ("
- " SELECT substr(data,1,1)==X'0D' FROM sqlite_dbpage WHERE pgno=rootpage"
- ") FROM sqlite_schema WHERE name = %Q", zName
- );
- if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
- bSqlIntkey = sqlite3_column_int(pStmt, 0);
- }
- shellFinalize(&rc, pStmt);
-
- if( bIntkey==bSqlIntkey ){
- int i;
- const char *zPk = "_rowid_";
- sqlite3_stmt *pPkFinder = 0;
-
- /* If this is an intkey table and there is an INTEGER PRIMARY KEY,
- ** set zPk to the name of the PK column, and pTab->iPk to the index
- ** of the column, where columns are 0-numbered from left to right.
- ** Or, if this is a WITHOUT ROWID table or if there is no IPK column,
- ** leave zPk as "_rowid_" and pTab->iPk at -2. */
- pTab->iPk = -2;
- if( bIntkey ){
- shellPreparePrintf(dbtmp, &rc, &pPkFinder,
- "SELECT cid, name FROM pragma_table_info(%Q) "
- " WHERE pk=1 AND type='integer' COLLATE nocase"
- " AND NOT EXISTS (SELECT cid FROM pragma_table_info(%Q) WHERE pk=2)"
- , zName, zName
- );
- 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 */ }
- }
- }
-
- pTab->zQuoted = shellMPrintf(&rc, "\"%w\"", zName);
- pTab->azlCol = (char**)shellMalloc(&rc, sizeof(char*) * (nSqlCol+1));
- pTab->nCol = nSqlCol;
-
- if( bIntkey ){
- pTab->azlCol[0] = shellMPrintf(&rc, "\"%w\"", zPk);
- }else{
- pTab->azlCol[0] = shellMPrintf(&rc, "");
- }
- i = 1;
- shellPreparePrintf(dbtmp, &rc, &pStmt,
- "SELECT %Q || group_concat(shell_idquote(name), ', ') "
- " FILTER (WHERE cid!=%d) OVER (ORDER BY %s cid) "
- "FROM pragma_table_info(%Q)",
- bIntkey ? ", " : "", pTab->iPk,
- bIntkey ? "" : "(CASE WHEN pk=0 THEN 1000000 ELSE pk END), ",
- zName
- );
- while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
- const char *zText = (const char*)sqlite3_column_text(pStmt, 0);
- pTab->azlCol[i] = shellMPrintf(&rc, "%s%s", pTab->azlCol[0], zText);
- i++;
- }
- shellFinalize(&rc, pStmt);
-
- shellFinalize(&rc, pPkFinder);
+ else{
+ utf8_printf(stderr, "unexpected option: %s\n", azArg[i]);
+ showHelp(pState->out, azArg[0]);
+ return 1;
}
}
- finished:
- sqlite3_close(dbtmp);
- *pRc = rc;
- if( rc!=SQLITE_OK || (pTab && pTab->zQuoted==0) ){
- recoverFreeTable(pTab);
- pTab = 0;
- }
- return pTab;
-}
-
-/*
-** This function is called to search the schema recovered from the
-** sqlite_schema table of the (possibly) corrupt database as part
-** of a ".recover" command. Specifically, for a table with root page
-** iRoot and at least nCol columns. Additionally, if bIntkey is 0, the
-** table must be a WITHOUT ROWID table, or if non-zero, not one of
-** those.
-**
-** If a table is found, a (RecoverTable*) object is returned. Or, if
-** no such table is found, but bIntkey is false and iRoot is the
-** root page of an index in the recovered schema, then (*pbNoop) is
-** set to true and NULL returned. Or, if there is no such table or
-** index, NULL is returned and (*pbNoop) set to 0, indicating that
-** the caller should write data to the orphans table.
-*/
-static RecoverTable *recoverFindTable(
- sqlite3 *db, /* DB from which to recover */
- int *pRc, /* IN/OUT: Error code */
- int iRoot, /* Root page of table */
- int bIntkey, /* True for an intkey table */
- int nCol, /* Number of columns in table */
- int *pbNoop /* OUT: True if iRoot is root of index */
-){
- sqlite3_stmt *pStmt = 0;
- RecoverTable *pRet = 0;
- int bNoop = 0;
- const char *zSql = 0;
- const char *zName = 0;
-
- /* Search the recovered schema for an object with root page iRoot. */
- shellPreparePrintf(db, pRc, &pStmt,
- "SELECT type, name, sql FROM recovery.schema WHERE rootpage=%d", iRoot
+ p = sqlite3_recover_init_sql(
+ pState->db, "main", recoverSqlCb, (void*)pState
);
- while( *pRc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
- const char *zType = (const char*)sqlite3_column_text(pStmt, 0);
- if( bIntkey==0 && sqlite3_stricmp(zType, "index")==0 ){
- bNoop = 1;
- break;
- }
- if( sqlite3_stricmp(zType, "table")==0 ){
- zName = (const char*)sqlite3_column_text(pStmt, 1);
- zSql = (const char*)sqlite3_column_text(pStmt, 2);
- if( zName!=0 && zSql!=0 ){
- pRet = recoverNewTable(pRc, zName, zSql, bIntkey, nCol);
- break;
- }
- }
+
+ sqlite3_recover_config(p, 789, (void*)zRecoveryDb); /* Debug use only */
+ sqlite3_recover_config(p, SQLITE_RECOVER_LOST_AND_FOUND, (void*)zLAF);
+ sqlite3_recover_config(p, SQLITE_RECOVER_ROWIDS, (void*)&bRowids);
+ sqlite3_recover_config(p, SQLITE_RECOVER_FREELIST_CORRUPT,(void*)&bFreelist);
+
+ sqlite3_recover_run(p);
+ if( sqlite3_recover_errcode(p)!=SQLITE_OK ){
+ const char *zErr = sqlite3_recover_errmsg(p);
+ int errCode = sqlite3_recover_errcode(p);
+ raw_printf(stderr, "sql error: %s (%d)\n", zErr, errCode);
}
-
- shellFinalize(pRc, pStmt);
- *pbNoop = bNoop;
- return pRet;
+ rc = sqlite3_recover_finish(p);
+ return rc;
}
+#endif /* SQLITE_SHELL_HAVE_RECOVER */
-/*
-** Return a RecoverTable object representing the orphans table.
-*/
-static RecoverTable *recoverOrphanTable(
- sqlite3 *db, /* DB from which to recover */
- FILE *out, /* Where to put recovery DDL */
- int *pRc, /* IN/OUT: Error code */
- const char *zLostAndFound, /* Base name for orphans table */
- int nCol /* Number of user data columns */
-){
- RecoverTable *pTab = 0;
- if( nCol>=0 && *pRc==SQLITE_OK ){
- int i;
-
- /* This block determines the name of the orphan table. The prefered
- ** name is zLostAndFound. But if that clashes with another name
- ** in the recovered schema, try zLostAndFound_0, zLostAndFound_1
- ** and so on until a non-clashing name is found. */
- int iTab = 0;
- char *zTab = shellMPrintf(pRc, "%s", zLostAndFound);
- sqlite3_stmt *pTest = 0;
- shellPrepare(db, pRc,
- "SELECT 1 FROM recovery.schema WHERE name=?", &pTest
- );
- if( pTest ) sqlite3_bind_text(pTest, 1, zTab, -1, SQLITE_TRANSIENT);
- while( *pRc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pTest) ){
- shellReset(pRc, pTest);
- sqlite3_free(zTab);
- zTab = shellMPrintf(pRc, "%s_%d", zLostAndFound, iTab++);
- sqlite3_bind_text(pTest, 1, zTab, -1, SQLITE_TRANSIENT);
- }
- shellFinalize(pRc, pTest);
-
- pTab = (RecoverTable*)shellMalloc(pRc, sizeof(RecoverTable));
- if( pTab ){
- pTab->zQuoted = shellMPrintf(pRc, "\"%w\"", zTab);
- pTab->nCol = nCol;
- pTab->iPk = -2;
- if( nCol>0 ){
- pTab->azlCol = (char**)shellMalloc(pRc, sizeof(char*) * (nCol+1));
- if( pTab->azlCol ){
- pTab->azlCol[nCol] = shellMPrintf(pRc, "");
- for(i=nCol-1; i>=0; i--){
- pTab->azlCol[i] = shellMPrintf(pRc, "%s, NULL", pTab->azlCol[i+1]);
- }
- }
- }
-
- if( *pRc!=SQLITE_OK ){
- recoverFreeTable(pTab);
- pTab = 0;
- }else{
- raw_printf(out,
- "CREATE TABLE %s(rootpgno INTEGER, "
- "pgno INTEGER, nfield INTEGER, id INTEGER", pTab->zQuoted
- );
- for(i=0; i<nCol; i++){
- raw_printf(out, ", c%d", i);
- }
- raw_printf(out, ");\n");
- }
- }
- sqlite3_free(zTab);
- }
- return pTab;
-}
-#endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */
-/*
-** If pRc!=0 and *pRc is not SQLITE_OK when this function is called, it is a
-** no-op. Otherwise, zFmt is treated as a printf() style string. The result
-** of formatting it along with any trailing arguments is written into a
-** buffer obtained from sqlite3_malloc(), and pointer to which is returned.
-** It is the responsibility of the caller to eventually free this buffer
-** using a call to sqlite3_free().
-**
-** If an OOM error occurs, (*pRc) is set to SQLITE_NOMEM (if pRc!=0)
-** and a NULL pointer returned.
-*/
-static char *shellMPrintf(int *pRc, const char *zFmt, ...){
- char *z = 0;
- if( pRc==0 || *pRc==SQLITE_OK ){
- va_list ap;
- va_start(ap, zFmt);
- z = sqlite3_vmprintf(zFmt, ap);
- va_end(ap);
- if( z==0 && pRc!=0 ){
- *pRc = SQLITE_NOMEM;
- }
- }
- return z;
-}
-
+#ifndef SQLITE_SHELL_FIDDLE
static DotCmdRC
writeDb( char *azArg[], int nArg, ShellExState *psx, char **pzErr ){
int rc = 0;
@@ -7745,9 +7790,9 @@
const char *z = azArg[j];
if( z[0]=='-' ){
if( z[1]=='-' ) z++;
- if( strcmp(z, "-append")==0 ){
+ if( cli_strcmp(z, "-append")==0 ){
zVfs = "apndvfs";
- }else if( strcmp(z, "-async")==0 ){
+ }else if( cli_strcmp(z, "-async")==0 ){
bAsync = 1;
}else{
return DCR_Unknown|j;
@@ -7794,6 +7839,7 @@
close_db(pDest);
return DCR_Ok|rc;
}
+#endif /* !defined(SQLITE_SHELL_FIDDLE) */
/*
* zAutoColumn(zCol, &db, ?) => Maybe init db, add column zCol to it.
@@ -7931,7 +7977,7 @@
','||iif((cpos-1)%4>0, ' ', x'0a'||' '))\
||')' AS ColsSpec \
FROM (\
- SELECT cpos, printf('\"%w\"',printf('%!.*s%s', nlen-chop,name,suff)) AS cname \
+ SELECT cpos, printf('\"%w\"',printf('%!.*s%s',nlen-chop,name,suff)) AS cname \
FROM ColNames ORDER BY cpos\
)";
static const char * const zRenamesDone =
@@ -8587,7 +8633,7 @@
psei->ppDotCommands[nc++] = pMC;
psei->numDotCommands = nc;
notify_subscribers(psi, NK_NewDotCommand, pMC);
- if( strcmp("unknown", zName)==0 ){
+ if( cli_strcmp("unknown", zName)==0 ){
psi->pUnknown = pMC;
psei->pUnknown = pMC;
}
@@ -8669,7 +8715,7 @@
}
/*
- * Subscribe to (or unsubscribe from) messages about various changes.
+ * Subscribe to (or unsubscribe from) messages about various changes.
* Unsubscribe, effected when nkMin==NK_Unsubscribe, always succeeds.
* Return SQLITE_OK on success, or one of these error codes:
* SQLITE_ERROR when the nkMin value is unsupported by this host;
@@ -9073,7 +9119,8 @@
return DCR_Ok;
}
-CONDITION_COMMAND(archive !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB));
+/* Future: Make macro processor accept newline escapes to shorten this. */
+CONDITION_COMMAND(archive !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) && !defined(SQLITE_SHELL_FIDDLE));
/*****************
* The .archive command
*/
@@ -9129,6 +9176,8 @@
* The .backup and .save commands (aliases for each other)
* These defer to writeDb in the dispatch table, so are not here.
*/
+CONDITION_COMMAND(backup !defined(SQLITE_SHELL_FIDDLE));
+CONDITION_COMMAND(save !defined(SQLITE_SHELL_FIDDLE) );
COLLECT_HELP_TEXT[
".backup ?DB? FILE Backup DB (default \"main\") to FILE",
" Options:",
@@ -9157,6 +9206,7 @@
return DCR_Ok;
}
+CONDITION_COMMAND(cd !defined(SQLITE_SHELL_FIDDLE));
/*****************
* The .binary and .cd commands
*/
@@ -9201,6 +9251,8 @@
return DCR_Ok;
}
+CONDITION_COMMAND(check !defined(SQLITE_SHELL_FIDDLE));
+CONDITION_COMMAND(clone !defined(SQLITE_SHELL_FIDDLE));
/*****************
* The .changes, .check, .clone and .connection commands
*/
@@ -9280,7 +9332,7 @@
#endif
psi->pAuxDb->db = 0;
}
- }else if( nArg==3 && strcmp(azArg[1], "close")==0
+ }else if( nArg==3 && cli_strcmp(azArg[1], "close")==0
&& IsDigit(azArg[2][0]) && azArg[2][1]==0 ){
int i = azArg[2][0] - '0';
if( i<0 || i>=ArraySize(psi->aAuxDb) ){
@@ -9299,6 +9351,7 @@
return DCR_Ok;
}
+CONDITION_COMMAND(dbinfo SQLITE_SHELL_HAVE_RECOVER);
/*****************
* The .databases, .dbconfig and .dbinfo commands
*/
@@ -9377,7 +9430,7 @@
int ii, v;
open_db(p, 0);
for(ii=0; ii<ArraySize(aDbConfig); ii++){
- if( nArg>1 && strcmp(azArg[1], aDbConfig[ii].zName)!=0 ) continue;
+ if( nArg>1 && cli_strcmp(azArg[1], aDbConfig[ii].zName)!=0 ) continue;
if( nArg>=3 ){
sqlite3_db_config(DBX(p), aDbConfig[ii].op, booleanValue(azArg[2]), 0);
}
@@ -9435,7 +9488,7 @@
if( azArg[i][0]=='-' ){
const char *z = azArg[i]+1;
if( z[0]=='-' ) z++;
- if( strcmp(z,"preserve-rowids")==0 ){
+ if( cli_strcmp(z,"preserve-rowids")==0 ){
#ifdef SQLITE_OMIT_VIRTUALTABLE
*pzErr = smprintf("The --preserve-rowids option is not compatible"
" with SQLITE_OMIT_VIRTUALTABLE\n");
@@ -9445,13 +9498,13 @@
ShellSetFlag(p, SHFLG_PreserveRowid);
#endif
}else{
- if( strcmp(z,"newlines")==0 ){
+ if( cli_strcmp(z,"newlines")==0 ){
ShellSetFlag(p, SHFLG_Newlines);
- }else if( strcmp(z,"data-only")==0 ){
+ }else if( cli_strcmp(z,"data-only")==0 ){
ShellSetFlag(p, SHFLG_DumpDataOnly);
- }else if( strcmp(z,"nosys")==0 ){
+ }else if( cli_strcmp(z,"nosys")==0 ){
ShellSetFlag(p, SHFLG_DumpNoSys);
- }else if( strcmp(z,"schema")==0 && ++i<nArg ){
+ }else if( cli_strcmp(z,"schema")==0 && ++i<nArg ){
zSchema = azArg[i];
}else{
*pzErr = smprintf("Unknown option \"%s\" on \".dump\"\n", azArg[i]);
@@ -9542,15 +9595,15 @@
if( DBX(p) ) sqlite3_exec(DBX(p), "PRAGMA vdbe_trace=OFF;", 0, 0, 0);
psi->autoEQPtrace = 0;
}
- if( strcmp(azArg[1],"full")==0 ){
+ if( cli_strcmp(azArg[1],"full")==0 ){
psi->autoEQP = AUTOEQP_full;
- }else if( strcmp(azArg[1],"trigger")==0 ){
+ }else if( cli_strcmp(azArg[1],"trigger")==0 ){
psi->autoEQP = AUTOEQP_trigger;
#ifdef SQLITE_DEBUG
- }else if( strcmp(azArg[1],"test")==0 ){
+ }else if( cli_strcmp(azArg[1],"test")==0 ){
psi->autoEQP = AUTOEQP_on;
psi->autoEQPtest = 1;
- }else if( strcmp(azArg[1],"trace")==0 ){
+ }else if( cli_strcmp(azArg[1],"trace")==0 ){
psi->autoEQP = AUTOEQP_full;
psi->autoEQPtrace = 1;
open_db(p, 0);
@@ -9566,6 +9619,9 @@
return DCR_Ok;
}
+CONDITION_COMMAND(cease !defined(SQLITE_SHELL_FIDDLE));
+CONDITION_COMMAND(exit !defined(SQLITE_SHELL_FIDDLE));
+CONDITION_COMMAND(quit !defined(SQLITE_SHELL_FIDDLE));
/*****************
* The .cease, .exit and .quit commands
* These are together so that their differing effects are apparent.
@@ -9629,10 +9685,10 @@
int n;
if( z[0]=='-' && z[1]=='-' ) z++;
n = strlen30(z);
- if( n>=2 && 0==strncmp(z, "-verbose", n) ){
+ if( n>=2 && 0==cli_strncmp(z, "-verbose", n) ){
psi->expert.bVerbose = 1;
}
- else if( n>=2 && 0==strncmp(z, "-sample", n) ){
+ else if( n>=2 && 0==cli_strncmp(z, "-sample", n) ){
if( i==(nArg-1) ){
return DCR_Unpaired|i;
}else{
@@ -9666,7 +9722,7 @@
ShellInState *psi = ISS(p);
int val = 1;
if( nArg>1 ){
- if( strcmp(azArg[1],"auto")==0 ){
+ if( cli_strcmp(azArg[1],"auto")==0 ){
val = 99;
}else{
val = booleanValue(azArg[1]);
@@ -9690,6 +9746,10 @@
* The .excel, .once and .output commands
* These share much implementation, so they stick together.
*/
+CONDITION_COMMAND(excel !defined(SQLITE_SHELL_FIDDLE));
+CONDITION_COMMAND(once !defined(SQLITE_SHELL_FIDDLE));
+CONDITION_COMMAND(output !defined(SQLITE_SHELL_FIDDLE));
+
COLLECT_HELP_TEXT[
".excel Display the output of next command in spreadsheet",
" --bom Prefix the file with a UTF8 byte-order mark",
@@ -9706,6 +9766,7 @@
" -e Send output to the system text editor",
" -x Send output as CSV to a spreadsheet (same as \".excel\")",
];
+#ifndef SQLITE_SHELL_FIDDLE
/* Shared implementation of .excel, .once and .output */
static DotCmdRC outputRedirs(char *azArg[], int nArg,
ShellInState *psi, char **pzErr,
@@ -9723,11 +9784,11 @@
char *z = azArg[i];
if( z[0]=='-' ){
if( z[1]=='-' ) z++;
- if( strcmp(z,"-bom")==0 ){
+ if( cli_strcmp(z,"-bom")==0 ){
bPutBOM = 1;
- }else if( bOnce!=2 && strcmp(z,"-x")==0 ){
+ }else if( bOnce!=2 && cli_strcmp(z,"-x")==0 ){
eMode = 'x'; /* spreadsheet */
- }else if( bOnce!=2 && strcmp(z,"-e")==0 ){
+ }else if( bOnce!=2 && cli_strcmp(z,"-e")==0 ){
eMode = 'e'; /* text editor */
}else{
return DCR_Unknown|i;
@@ -9797,7 +9858,7 @@
}else{
psi->out = output_file_open(zFile, bTxtMode);
if( psi->out==0 ){
- if( strcmp(zFile,"off")!=0 ){
+ if( cli_strcmp(zFile,"off")!=0 ){
*pzErr = smprintf("cannot write to \"%s\"\n", zFile);
}
psi->out = STD_OUT;
@@ -9810,6 +9871,8 @@
sqlite3_free(zFile);
return DCR_Ok|rc;
}
+#endif /* !defined(SQLITE_SHELL_FIDDLE)*/
+
DISPATCHABLE_COMMAND( excel ? 1 2 ){
return outputRedirs(azArg, nArg, ISS(p), pzErr, 2, 'x');
}
@@ -9861,7 +9924,7 @@
zCmd = nArg>=2 ? azArg[1] : "help";
if( zCmd[0]=='-'
- && (strcmp(zCmd,"--schema")==0 || strcmp(zCmd,"-schema")==0)
+ && (cli_strcmp(zCmd,"--schema")==0 || cli_strcmp(zCmd,"-schema")==0)
&& nArg>=4
){
zSchema = azArg[2];
@@ -9877,7 +9940,7 @@
}
/* --help lists all file-controls */
- if( strcmp(zCmd,"help")==0 ){
+ if( cli_strcmp(zCmd,"help")==0 ){
utf8_printf(psi->out, "Available file-controls:\n");
for(i=0; i<ArraySize(aCtrl); i++){
utf8_printf(psi->out, " .filectrl %s %s\n",
@@ -9890,7 +9953,7 @@
** unique prefix of the option name, or a numerical value. */
n2 = strlen30(zCmd);
for(i=0; i<ArraySize(aCtrl); i++){
- if( strncmp(zCmd, aCtrl[i].zCtrlName, n2)==0 ){
+ if( cli_strncmp(zCmd, aCtrl[i].zCtrlName, n2)==0 ){
if( filectrl<0 ){
filectrl = aCtrl[i].ctrlCode;
iCtrl = i;
@@ -10058,10 +10121,10 @@
FILE *out = ISS(p)->out;
if( nArg>1 ){
char *z = azArg[1];
- if( nArg==3 && strcmp(z, zHelpAll)==0 && strcmp(azArg[2], zHelpAll)==0 ){
+ if( nArg==3 && cli_strcmp(z, zHelpAll)==0 && cli_strcmp(azArg[2], zHelpAll)==0 ){
/* Show the undocumented command help */
zPat = zHelpAll;
- }else if( strcmp(z,"-a")==0 || optionMatch(z, "all") ){
+ }else if( cli_strcmp(z,"-a")==0 || optionMatch(z, "all") ){
zPat = "";
}else{
zPat = z;
@@ -10074,6 +10137,7 @@
return DCR_Ok;
}
+CONDITION_COMMAND(import !defined(SQLITE_SHELL_FIDDLE));
/*****************
* The .import command
*/
@@ -10117,10 +10181,6 @@
if(psi->bSafeMode) return DCR_AbortError;
memset(&sCtx, 0, sizeof(sCtx));
- if( 0==(sCtx.z = sqlite3_malloc64(120)) ){
- shell_out_of_memory();
- }
-
if( psi->mode==MODE_Ascii ){
xRead = ascii_read_one_field;
}else{
@@ -10137,18 +10197,18 @@
}else{
return DCR_TooMany|i;
}
- }else if( strcmp(z,"-v")==0 ){
+ }else if( cli_strcmp(z,"-v")==0 ){
eVerbose++;
- }else if( strcmp(z,"-schema")==0 && i<nArg-1 ){
+ }else if( cli_strcmp(z,"-schema")==0 && i<nArg-1 ){
zSchema = azArg[++i];
- }else if( strcmp(z,"-skip")==0 && i<nArg-1 ){
+ }else if( cli_strcmp(z,"-skip")==0 && i<nArg-1 ){
nSkip = integerValue(azArg[++i]);
- }else if( strcmp(z,"-ascii")==0 ){
+ }else if( cli_strcmp(z,"-ascii")==0 ){
sCtx.cColSep = SEP_Unit[0];
sCtx.cRowSep = SEP_Record[0];
xRead = ascii_read_one_field;
useOutputMode = 0;
- }else if( strcmp(z,"-csv")==0 ){
+ }else if( cli_strcmp(z,"-csv")==0 ){
sCtx.cColSep = ',';
sCtx.cRowSep = '\n';
xRead = csv_read_one_field;
@@ -10184,7 +10244,7 @@
return DCR_Error;
}
if( nSep==2 && psi->mode==MODE_Csv
- && strcmp(psi->rowSeparator,SEP_CrLf)==0 ){
+ && cli_strcmp(psi->rowSeparator,SEP_CrLf)==0 ){
/* When importing CSV (only), if the row separator is set to the
** default output row separator, change it to the default input
** row separator. This avoids having to maintain different input
@@ -10216,10 +10276,14 @@
sCtx.xCloser = fclose;
}
if( sCtx.in==0 ){
- *pzErr = smprintf("cannot open \"%s\"\n", zFile);
- import_cleanup(&sCtx);
+ *pzErr = smprintf("cannot open \"%s\"\n", zFile);
return DCR_Error;
}
+ sCtx.z = sqlite3_malloc64(120);
+ if( sCtx.z==0 ){
+ import_cleanup(&sCtx);
+ shell_out_of_memory();
+ }
/* Here and below, resources must be freed before exit. */
if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){
char zSep[2];
@@ -10420,11 +10484,12 @@
}
/*****************
- * The .imposter, .iotrace, limit, lint and .log commands
+ * The .imposter, .iotrace, .limit, .lint and .log commands
*/
CONDITION_COMMAND( imposter !defined(SQLITE_OMIT_TEST_CONTROL) );
CONDITION_COMMAND( iotrace defined(SQLITE_ENABLE_IOTRACE) );
-CONDITION_COMMAND( load !defined(SQLITE_OMIT_LOAD_EXTENSION) );
+CONDITION_COMMAND( load !defined(SQLITE_OMIT_LOAD_EXTENSION) && !defined(SQLITE_SHELL_FIDDLE));
+CONDITION_COMMAND(log !defined(SQLITE_SHELL_FIDDLE));
COLLECT_HELP_TEXT[
".imposter INDEX TABLE Create imposter table TABLE on index INDEX",
".iotrace FILE Enable I/O diagnostic logging to FILE",
@@ -10536,7 +10601,7 @@
iotrace = 0;
if( nArg<2 ){
sqlite3IoTrace = 0;
- }else if( strcmp(azArg[1], "-")==0 ){
+ }else if( cli_strcmp(azArg[1], "-")==0 ){
sqlite3IoTrace = iotracePrintf;
iotrace = STD_OUT;
}else{
@@ -10900,7 +10965,7 @@
" line One value per line",
" list Values delimited by \"|\"",
" markdown Markdown table format",
- " qbox Shorthand for \"box --width 60 --quote\"",
+ " qbox Shorthand for \"box --wrap 60 --quote\"",
" quote Escape answers as for SQL",
" table ASCII-art table",
" tabs Tab-separated values",
@@ -10943,13 +11008,13 @@
if( foundMode!=MODE_COUNT_OF ) goto mode_badarg;
for( im=0; im<MODE_COUNT_OF; ++im ){
if( modeDescr[im].bUserBlocked ) continue;
- if( strncmp(zArg,modeDescr[im].zModeName,nza)==0 ){
+ if( cli_strncmp(zArg,modeDescr[im].zModeName,nza)==0 ){
foundMode = (u8)im;
setMode = modeDescr[im].iAliasFor;
break;
}
}
- if( strcmp(zArg, "qbox")==0 ){
+ if( cli_strcmp(zArg, "qbox")==0 ){
ColModeOpts cmo = ColModeOpts_default_qbox;
foundMode = setMode = MODE_Box;
cmOpts = cmo;
@@ -11012,9 +11077,19 @@
return DCR_ArgWrong|aix;
}
+/* 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." */
+#ifdef SQLITE_SHELL_FIDDLE
+# define HOPEN ",open"
+#else
+# define HOPEN ".open"
+#endif
+/* ToDo: Get defined help text collection into macro processor. */
/*****************
- * The .open, .nonce and .nullvalue commands
+ * The .nonce, .nullvalue and .open commands
*/
+CONDITION_COMMAND(nonce !defined(SQLITE_SHELL_FIDDLE));
COLLECT_HELP_TEXT[
".open ?OPTIONS? ?FILE? Close existing database and reopen FILE",
" Options:",
@@ -11044,27 +11119,30 @@
/* Check for command-line arguments */
for(iName=1; iName<nArg; iName++){
const char *z = azArg[iName];
+#ifndef SQLITE_SHELL_FIDDLE
if( optionMatch(z,"new") ){
newFlag = 1;
-#ifdef SQLITE_HAVE_ZLIB
+# ifdef SQLITE_HAVE_ZLIB
}else if( optionMatch(z, "zip") ){
openMode = SHELL_OPEN_ZIPFILE;
-#endif
+# endif
}else if( optionMatch(z, "append") ){
openMode = SHELL_OPEN_APPENDVFS;
}else if( optionMatch(z, "readonly") ){
openMode = SHELL_OPEN_READONLY;
}else if( optionMatch(z, "nofollow") ){
openFlags |= SQLITE_OPEN_NOFOLLOW;
-#ifndef SQLITE_OMIT_DESERIALIZE
+# ifndef SQLITE_OMIT_DESERIALIZE
}else if( optionMatch(z, "deserialize") ){
openMode = SHELL_OPEN_DESERIALIZE;
}else if( optionMatch(z, "hexdb") ){
openMode = SHELL_OPEN_HEXDB;
}else if( optionMatch(z, "maxsize") && iName+1<nArg ){
szMax = integerValue(azArg[++iName]);
-#endif /* SQLITE_OMIT_DESERIALIZE */
- }else if( z[0]=='-' ){
+# endif /* SQLITE_OMIT_DESERIALIZE */
+ }else
+#endif /* !defined(SQLITE_SHELL_FIDDLE) */
+ if( z[0]=='-' ){
return DCR_Unknown|iName;
}else if( zFN ){
*pzErr = smprintf("extra argument: \"%s\"\n", z);
@@ -11091,14 +11169,18 @@
/* If a filename is specified, try to open it first */
if( zFN || psi->openMode==SHELL_OPEN_HEXDB ){
if( newFlag && zFN && !psi->bSafeMode ) shellDeleteFile(zFN);
+#ifndef SQLITE_SHELL_FIDDLE
if( psi->bSafeMode
&& psi->openMode!=SHELL_OPEN_HEXDB
&& zFN
- && strcmp(zFN,":memory:")!=0
+ && cli_strcmp(zFN,":memory:")!=0
){
*pzErr = smprintf("cannot open database files in safe mode");
return DCR_AbortError;
}
+#else
+ /* WASM mode has its own sandboxed pseudo-filesystem. */
+#endif
if( zFN ){
zNewFilename = smprintf("%s", zFN);
shell_check_oom(zNewFilename);
@@ -11127,7 +11209,7 @@
DISPATCHABLE_COMMAND( nonce ? 2 2 ){
ShellInState *psi = ISS(p);
- if( psi->zNonce==0 || strcmp(azArg[1],psi->zNonce)!=0 ){
+ if( psi->zNonce==0 || cli_strcmp(azArg[1],psi->zNonce)!=0 ){
raw_printf(STD_ERR, "line %d: incorrect nonce: \"%s\"\n",
psi->pInSource->lineno, azArg[1]);
exit(1);
@@ -11150,7 +11232,7 @@
static int kv_find_callback(void *pData, int nc, char **pV, char **pC){
assert(nc>=1);
- assert(strcmp(pC[0],"value")==0);
+ assert(cli_strcmp(pC[0],"value")==0);
struct keyval_row *pParam = (struct keyval_row *)pData;
assert(pParam->value==0); /* key values are supposedly unique. */
if( pParam->value!=0 ) sqlite3_free( pParam->value );
@@ -11290,7 +11372,7 @@
* If the return differs from the input, it must be sqlite3_free()'ed.
*/
static const char *kv_store_path(const char *zSpec, ParamTableUse ptu){
- if( zSpec==0 || zSpec[0]==0 || strcmp(zSpec,"~")==0 ){
+ if( zSpec==0 || zSpec[0]==0 || cli_strcmp(zSpec,"~")==0 ){
const char *zDef;
switch( ptu ){
case PTU_Binding: zDef = zDefaultParamStore; break;
@@ -11684,7 +11766,7 @@
** Delete some or all parameters from the TEMP table that holds them.
** Without any arguments, clear deletes them all and unset does nothing.
*/
- if( strcmp(azArg[1],"clear")==0 || strcmp(azArg[1],"unset")==0 ){
+ if( cli_strcmp(azArg[1],"clear")==0 || cli_strcmp(azArg[1],"unset")==0 ){
if( param_table_exists(db) && (nArg>2 || azArg[1][0]=='c') ){
sqlite3_str *sbZap = sqlite3_str_new(db);
char *zSql;
@@ -11702,7 +11784,7 @@
/* .parameter edit ?NAMES?
** Edit the named parameters. Any that do not exist are created.
*/
- if( strcmp(azArg[1],"edit")==0 ){
+ if( cli_strcmp(azArg[1],"edit")==0 ){
ShellInState *psi = ISS(p);
int ia = 2;
int eval = 0;
@@ -11751,7 +11833,7 @@
** Make sure the TEMP table used to hold bind parameters exists.
** Create it if necessary.
*/
- if( nArg==2 && strcmp(azArg[1],"init")==0 ){
+ if( nArg==2 && cli_strcmp(azArg[1],"init")==0 ){
param_table_init(db);
}else
@@ -11760,15 +11842,15 @@
** list displays names, values and uses.
** ls displays just the names.
*/
- if( nArg>=2 && ((strcmp(azArg[1],"list")==0)
- || (strcmp(azArg[1],"ls")==0)) ){
+ if( nArg>=2 && ((cli_strcmp(azArg[1],"list")==0)
+ || (cli_strcmp(azArg[1],"ls")==0)) ){
list_pov_entries(p, PTU_Binding, azArg[1][1]=='s', azArg+2, nArg-2);
}else
/* .parameter load
** Load all or named parameters from specified or default (DB) file.
*/
- if( strcmp(azArg[1],"load")==0 ){
+ if( cli_strcmp(azArg[1],"load")==0 ){
param_table_init(db);
rc = kv_pairs_load(db, PTU_Binding, (const char **)azArg+1, nArg-1);
}else
@@ -11776,7 +11858,7 @@
/* .parameter save
** Save all or named parameters into specified or default (DB) file.
*/
- if( strcmp(azArg[1],"save")==0 ){
+ if( cli_strcmp(azArg[1],"save")==0 ){
rc = kv_pairs_save(db, PTU_Binding, (const char **)azArg+1, nArg-1);
}else
@@ -11786,7 +11868,7 @@
** VALUE can be in either SQL literal notation, or if not it will be
** understood to be a text string.
*/
- if( nArg>=4 && strcmp(azArg[1],"set")==0 ){
+ if( nArg>=4 && cli_strcmp(azArg[1],"set")==0 ){
char cCast = option_char(azArg[2]);
int inv = 2 + (cCast != 0);
ParamTableUse ptu = classify_param_name(azArg[inv]);
@@ -11844,19 +11926,19 @@
if( z[0]=='-' ){
z++;
if( z[0]=='-' ) z++;
- if( strcmp(z,"quiet")==0 || strcmp(z,"q")==0 ){
+ if( cli_strcmp(z,"quiet")==0 || cli_strcmp(z,"q")==0 ){
psi->flgProgress |= SHELL_PROGRESS_QUIET;
continue;
}
- if( strcmp(z,"reset")==0 ){
+ if( cli_strcmp(z,"reset")==0 ){
psi->flgProgress |= SHELL_PROGRESS_RESET;
continue;
}
- if( strcmp(z,"once")==0 ){
+ if( cli_strcmp(z,"once")==0 ){
psi->flgProgress |= SHELL_PROGRESS_ONCE;
continue;
}
- if( strcmp(z,"limit")==0 ){
+ if( cli_strcmp(z,"limit")==0 ){
if( i+1>=nArg ){
*pzErr = smprintf("missing argument on --limit\n");
return DCR_Unpaired|i;
@@ -11874,13 +11956,14 @@
sqlite3_progress_handler(DBX(p), nn, progress_handler, psi);
return DCR_Ok;
}
+
/* Allow too few arguments by tradition, (a form of no-op.) */
DISPATCHABLE_COMMAND( prompt ? 1 3 ){
if( nArg >= 2) {
- strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1);
+ SET_MAIN_PROMPT(azArg[1]);
}
if( nArg >= 3) {
- strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1);
+ SET_MORE_PROMPT(azArg[2]);
}
return DCR_Ok;
}
@@ -11888,11 +11971,11 @@
/*****************
* The .recover and .restore commands
*/
-CONDITION_COMMAND( recover !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) );
+CONDITION_COMMAND( recover SQLITE_SHELL_HAVE_RECOVER );
+CONDITION_COMMAND( restore !defined(SQLITE_SHELL_FIDDLE) );
COLLECT_HELP_TEXT[
".recover Recover as much data as possible from corrupt db.",
- " --freelist-corrupt Assume the freelist is corrupt",
- " --recovery-db NAME Store recovery metadata in database file NAME",
+ " --ignore-freelist Ignore pages that appear to be on db freelist",
" --lost-and-found TABLE Alternative name for the lost-and-found table",
" --no-rowids Do not attempt to recover rowid values",
" that are not also INTEGER PRIMARY KEYs",
@@ -12277,7 +12360,7 @@
* The .scanstats and .schema commands
*/
COLLECT_HELP_TEXT[
- ".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off",
+ ".scanstats on|off|est Turn sqlite3_stmt_scanstatus() metrics on or off",
".schema ?PATTERN? Show the CREATE statements matching PATTERN",
" Options:",
" --indent Try to pretty-print the schema",
@@ -12285,6 +12368,11 @@
];
DISPATCHABLE_COMMAND( scanstats ? 2 2 ){
ISS(p)->scanstatsOn = (u8)booleanValue(azArg[1]);
+ if( cli_strcmp(azArg[1], "est")==0 ){
+ p->scanstatsOn = 2;
+ }else{
+ p->scanstatsOn = (u8)booleanValue(azArg[1]);
+ }
#ifndef SQLITE_ENABLE_STMT_SCANSTATUS
raw_printf(STD_ERR, "Warning: .scanstats not available in this build.\n");
#endif
@@ -12492,7 +12580,7 @@
open_db(p, 0);
if( nArg>=3 ){
for(iSes=0; iSes<pAuxDb->nSession; iSes++){
- if( strcmp(pAuxDb->aSession[iSes].zName, azArg[1])==0 ) break;
+ if( cli_strcmp(pAuxDb->aSession[iSes].zName, azArg[1])==0 ) break;
}
if( iSes<pAuxDb->nSession ){
pSession = &pAuxDb->aSession[iSes];
@@ -12508,7 +12596,7 @@
** Invoke the sqlite3session_attach() interface to attach a particular
** table so that it is never filtered.
*/
- if( strcmp(azCmd[0],"attach")==0 ){
+ if( cli_strcmp(azCmd[0],"attach")==0 ){
if( nCmd!=2 ) goto session_syntax_error;
if( pSession->p==0 ){
session_not_open:
@@ -12526,7 +12614,8 @@
** .session patchset FILE
** Write a changeset or patchset into a file. The file is overwritten.
*/
- if( strcmp(azCmd[0],"changeset")==0 || strcmp(azCmd[0],"patchset")==0 ){
+ if( cli_strcmp(azCmd[0],"changeset")==0
+ || cli_strcmp(azCmd[0],"patchset")==0 ){
FILE *cs_out = 0;
if( failIfSafeMode
(p, "cannot run \".session %s\" in safe mode", azCmd[0]) ){
@@ -12563,7 +12652,7 @@
/* .session close
** Close the identified session
*/
- if( strcmp(azCmd[0], "close")==0 ){
+ if( cli_strcmp(azCmd[0], "close")==0 ){
if( nCmd!=1 ) goto session_syntax_error;
if( pAuxDb->nSession ){
session_close(pSession);
@@ -12574,7 +12663,7 @@
/* .session enable ?BOOLEAN?
** Query or set the enable flag
*/
- if( strcmp(azCmd[0], "enable")==0 ){
+ if( cli_strcmp(azCmd[0], "enable")==0 ){
int ii;
if( nCmd>2 ) goto session_syntax_error;
ii = nCmd==1 ? -1 : booleanValue(azCmd[1]);
@@ -12588,7 +12677,7 @@
/* .session filter GLOB ....
** Set a list of GLOB patterns of table names to be excluded.
*/
- if( strcmp(azCmd[0], "filter")==0 ){
+ if( cli_strcmp(azCmd[0], "filter")==0 ){
int ii, nByte;
if( nCmd<2 ) goto session_syntax_error;
if( pAuxDb->nSession ){
@@ -12612,7 +12701,7 @@
/* .session indirect ?BOOLEAN?
** Query or set the indirect flag
*/
- if( strcmp(azCmd[0], "indirect")==0 ){
+ if( cli_strcmp(azCmd[0], "indirect")==0 ){
int ii;
if( nCmd>2 ) goto session_syntax_error;
ii = nCmd==1 ? -1 : booleanValue(azCmd[1]);
@@ -12626,7 +12715,7 @@
/* .session isempty
** Determine if the session is empty
*/
- if( strcmp(azCmd[0], "isempty")==0 ){
+ if( cli_strcmp(azCmd[0], "isempty")==0 ){
int ii;
if( nCmd!=1 ) goto session_syntax_error;
if( pAuxDb->nSession ){
@@ -12639,7 +12728,7 @@
/* .session list
** List all currently open sessions
*/
- if( strcmp(azCmd[0],"list")==0 ){
+ if( cli_strcmp(azCmd[0],"list")==0 ){
for(i=0; i<pAuxDb->nSession; i++){
utf8_printf(out, "%d %s\n", i, pAuxDb->aSession[i].zName);
}
@@ -12649,13 +12738,13 @@
** Open a new session called NAME on the attached database DB.
** DB is normally "main".
*/
- if( strcmp(azCmd[0],"open")==0 ){
+ if( cli_strcmp(azCmd[0],"open")==0 ){
char *zName;
if( nCmd!=3 ) goto session_syntax_error;
zName = azCmd[2];
if( zName[0]==0 ) goto session_syntax_error;
for(i=0; i<pAuxDb->nSession; i++){
- if( strcmp(pAuxDb->aSession[i].zName,zName)==0 ){
+ if( cli_strcmp(pAuxDb->aSession[i].zName,zName)==0 ){
utf8_printf(STD_ERR, "Session \"%s\" already exists\n", zName);
return rc;
}
@@ -12702,15 +12791,15 @@
if( z[0]=='-' ){
z++;
if( z[0]=='-' ) z++;
- if( strcmp(z,"schema")==0 ){
+ if( cli_strcmp(z,"schema")==0 ){
bSchema = 1;
}else
- if( strcmp(z,"sha3-224")==0 || strcmp(z,"sha3-256")==0
- || strcmp(z,"sha3-384")==0 || strcmp(z,"sha3-512")==0
+ if( cli_strcmp(z,"sha3-224")==0 || cli_strcmp(z,"sha3-256")==0
+ || cli_strcmp(z,"sha3-384")==0 || cli_strcmp(z,"sha3-512")==0
){
iSize = atoi(&z[5]);
}else
- if( strcmp(z,"debug")==0 ){
+ if( cli_strcmp(z,"debug")==0 ){
bDebug = 1;
}else
{
@@ -12727,12 +12816,12 @@
}
}
if( bSchema ){
- zSql = "SELECT lower(name) FROM sqlite_schema"
+ zSql = "SELECT lower(name) AS tname FROM sqlite_schema"
" WHERE type='table' AND coalesce(rootpage,0)>1"
" UNION ALL SELECT 'sqlite_schema'"
" ORDER BY 1 collate nocase";
}else{
- zSql = "SELECT lower(name) FROM sqlite_schema"
+ zSql = "SELECT lower(name) AS tname FROM sqlite_schema"
" WHERE type='table' AND coalesce(rootpage,0)>1"
" AND name NOT LIKE 'sqlite_%'"
" ORDER BY 1 collate nocase";
@@ -12746,20 +12835,20 @@
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 ){
+ if( cli_strncmp(zTab, "sqlite_",7)!=0 ){
appendText(&sQuery,"SELECT * FROM ", 0);
appendText(&sQuery,zTab,'"');
appendText(&sQuery," NOT INDEXED;", 0);
- }else if( strcmp(zTab, "sqlite_schema")==0 ){
+ }else if( cli_strcmp(zTab, "sqlite_schema")==0 ){
appendText(&sQuery,"SELECT type,name,tbl_name,sql FROM sqlite_schema"
" ORDER BY name;", 0);
- }else if( strcmp(zTab, "sqlite_sequence")==0 ){
+ }else if( cli_strcmp(zTab, "sqlite_sequence")==0 ){
appendText(&sQuery,"SELECT name,seq FROM sqlite_sequence"
" ORDER BY name;", 0);
- }else if( strcmp(zTab, "sqlite_stat1")==0 ){
+ }else if( cli_strcmp(zTab, "sqlite_stat1")==0 ){
appendText(&sQuery,"SELECT tbl,idx,stat FROM sqlite_stat1"
" ORDER BY tbl,idx;", 0);
- }else if( strcmp(zTab, "sqlite_stat4")==0 ){
+ }else if( cli_strcmp(zTab, "sqlite_stat4")==0 ){
appendText(&sQuery, "SELECT * FROM ", 0);
appendText(&sQuery, zTab, 0);
appendText(&sQuery, " ORDER BY tbl, idx, rowid;\n", 0);
@@ -12793,12 +12882,64 @@
}else{
shell_exec(p, zSql, 0);
}
+#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && !defined(SQLITE_OMIT_VIRTUALTABLE)
+ {
+ int lrc;
+ char *zRevText = /* Query for reversible to-blob-to-text check */
+ "SELECT lower(name) as tname FROM sqlite_schema\n"
+ "WHERE type='table' AND coalesce(rootpage,0)>1\n"
+ "AND name NOT LIKE 'sqlite_%%'%s\n"
+ "ORDER BY 1 collate nocase";
+ zRevText = sqlite3_mprintf(zRevText, zLike? " AND name LIKE $tspec" : "");
+ zRevText = sqlite3_mprintf(
+ /* lower-case query is first run, producing upper-case query. */
+ "with tabcols as materialized(\n"
+ "select tname, cname\n"
+ "from ("
+ " select ss.tname as tname, ti.name as cname\n"
+ " from (%z) ss\n inner join pragma_table_info(tname) ti))\n"
+ "select 'SELECT total(bad_text_count) AS bad_text_count\n"
+ "FROM ('||group_concat(query, ' UNION ALL ')||')' as btc_query\n"
+ " from (select 'SELECT COUNT(*) AS bad_text_count\n"
+ "FROM '||tname||' WHERE '\n"
+ "||group_concat('CAST(CAST('||cname||' AS BLOB) AS TEXT)<>'||cname\n"
+ "|| ' AND typeof('||cname||')=''text'' ',\n"
+ "' OR ') as query, tname from tabcols group by tname)"
+ , zRevText);
+ shell_check_oom(zRevText);
+ if( bDebug ) utf8_printf(p->out, "%s\n", zRevText);
+ lrc = sqlite3_prepare_v2(p->db, zRevText, -1, &pStmt, 0);
+ assert(lrc==SQLITE_OK);
+ if( zLike ) sqlite3_bind_text(pStmt,1,zLike,-1,SQLITE_STATIC);
+ lrc = SQLITE_ROW==sqlite3_step(pStmt);
+ if( lrc ){
+ const char *zGenQuery = (char*)sqlite3_column_text(pStmt,0);
+ sqlite3_stmt *pCheckStmt;
+ lrc = sqlite3_prepare_v2(p->db, zGenQuery, -1, &pCheckStmt, 0);
+ if( bDebug ) utf8_printf(p->out, "%s\n", zGenQuery);
+ if( SQLITE_OK==lrc ){
+ if( SQLITE_ROW==sqlite3_step(pCheckStmt) ){
+ double countIrreversible = sqlite3_column_double(pCheckStmt, 0);
+ if( countIrreversible>0 ){
+ int sz = (int)(countIrreversible + 0.5);
+ utf8_printf(stderr,
+ "Digest includes %d invalidly encoded text field%s.\n",
+ sz, (sz>1)? "s": "");
+ }
+ }
+ sqlite3_finalize(pCheckStmt);
+ }
+ sqlite3_finalize(pStmt);
+ }
+ sqlite3_free(zRevText);
+ }
+#endif /* !defined(*_OMIT_SCHEMA_PRAGMAS) && !defined(*_OMIT_VIRTUALTABLE) */
sqlite3_free(zSql);
return DCR_Ok;
}
/*****************
- * The .selftest*, .shell, and .show commands
+ * The .selftest* and .show commands
*/
CONDITION_COMMAND( selftest_bool defined(SQLITE_DEBUG) );
CONDITION_COMMAND( selftest_int defined(SQLITE_DEBUG) );
@@ -12846,10 +12987,10 @@
for(i=1; i<nArg; i++){
const char *z = azArg[i];
if( z[0]=='-' && z[1]=='-' ) z++;
- if( strcmp(z,"-init")==0 ){
+ if( cli_strcmp(z,"-init")==0 ){
bIsInit = 1;
}else
- if( strcmp(z,"-v")==0 ){
+ if( cli_strcmp(z,"-v")==0 ){
bVerbose++;
}else
{
@@ -12900,9 +13041,9 @@
/* This unusually directed output is for test purposes. */
fprintf(STD_OUT, "%d: %s %s\n", tno, zOp, zSql);
}
- if( strcmp(zOp,"memo")==0 ){
+ if( cli_strcmp(zOp,"memo")==0 ){
utf8_printf(psi->out, "%s\n", zSql);
- }else if( strcmp(zOp,"run")==0 ){
+ }else if( cli_strcmp(zOp,"run")==0 ){
char *zErrMsg = 0;
str.n = 0;
str.z[0] = 0;
@@ -12916,7 +13057,7 @@
rc = 1;
utf8_printf(psi->out, "%d: error-code-%d: %s\n", tno, rc, zErrMsg);
sqlite3_free(zErrMsg);
- }else if( strcmp(zAns,str.z)!=0 ){
+ }else if( cli_strcmp(zAns,str.z)!=0 ){
nErr++;
rc = 1;
utf8_printf(psi->out, "%d: Expected: [%s]\n", tno, zAns);
@@ -12939,14 +13080,14 @@
/*****************
* The .shell and .system commands
*/
-CONDITION_COMMAND( shell !defined(SQLITE_NOHAVE_SYSTEM) );
-CONDITION_COMMAND( system !defined(SQLITE_NOHAVE_SYSTEM) );
+CONDITION_COMMAND( shell !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE) );
+CONDITION_COMMAND( system !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE) );
COLLECT_HELP_TEXT[
".shell CMD ARGS... Run CMD ARGS... in a system shell",
".system CMD ARGS... Run CMD ARGS... in a system shell",
];
-#ifndef SQLITE_NOHAVE_SYSTEM
+#if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE)
static DotCmdRC shellOut(char *azArg[], int nArg,
ShellExState *psx, char **pzErr){
char *zCmd;
@@ -12994,7 +13135,7 @@
if( ISS(p)->bSafeMode ) return DCR_AbortError;
while( ai<nArg ){
const char *zA = azArg[ai++];
- if( strcmp(zA, "--")==0 ){
+ if( cli_strcmp(zA, "--")==0 ){
pzExtArgs = azArg + ai;
break;
}
@@ -13032,7 +13173,7 @@
goto moan_error;
}
for( io=0; io<ArraySize(shopts); ++io ){
- if( strcmp(azArg[ia]+1, shopts[io].name)==0 ){
+ if( cli_strcmp(azArg[ia]+1, shopts[io].name)==0 ){
if( cs=='+' ) psi->bExtendedDotCmds |= shopts[io].mask;
else psi->bExtendedDotCmds &= ~shopts[io].mask;
break;
@@ -13127,9 +13268,9 @@
DISPATCHABLE_COMMAND( stats ? 0 0 ){
ShellInState *psi = ISS(p);
if( nArg==2 ){
- if( strcmp(azArg[1],"stmt")==0 ){
+ if( cli_strcmp(azArg[1],"stmt")==0 ){
psi->statsOn = 2;
- }else if( strcmp(azArg[1],"vmstep")==0 ){
+ }else if( cli_strcmp(azArg[1],"vmstep")==0 ){
psi->statsOn = 3;
}else{
psi->statsOn = (u8)booleanValue(azArg[1]);
@@ -13315,8 +13456,27 @@
}
/*****************
+ * The .selecttrace, .treetrace and .wheretrace commands (undocumented)
+ */
+static DotCmdRC setTrace( char *azArg[], int nArg, ShellExState *psx, int ts ){
+ unsigned int x = nArg>1 ? (unsigned int)integerValue(azArg[1]) : ~0;
+ sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, ts, &x);
+ return DCR_Ok;
+}
+DISPATCHABLE_COMMAND( selecttrace 0 1 2 ){
+ return setTrace(azArg, nArg, p, 1);
+}
+DISPATCHABLE_COMMAND( treetrace 0 1 2 ){
+ return setTrace(azArg, nArg, p, 1);
+}
+DISPATCHABLE_COMMAND( wheretrace 0 1 2 ){
+ return setTrace(azArg, nArg, p, 3);
+}
+
+/*****************
* The .testcase, .testctrl, .timeout, .timer and .trace commands
*/
+CONDITION_COMMAND( testcase !defined(SQLITE_SHELL_FIDDLE) );
CONDITION_COMMAND( testctrl !defined(SQLITE_UNTESTABLE) );
CONDITION_COMMAND( trace !defined(SQLITE_OMIT_TRACE) );
COLLECT_HELP_TEXT[
@@ -13364,28 +13524,28 @@
int unSafe; /* Not valid for --safe mode */
const char *zUsage; /* Usage notes */
} aCtrl[] = {
- { "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" },
+ {"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,0,"" },
+ {"parser_coverage", SQLITE_TESTCTRL_PARSER_COVERAGE,0,"" },
#endif
- { "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" },
+ {"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;
@@ -13404,7 +13564,7 @@
}
/* --help lists all test-controls */
- if( strcmp(zCmd,"help")==0 ){
+ if( cli_strcmp(zCmd,"help")==0 ){
utf8_printf(out, "Available test-controls:\n");
for(i=0; i<ArraySize(aCtrl); i++){
utf8_printf(out, " .testctrl %s %s\n",
@@ -13417,7 +13577,7 @@
** of the option name, or a numerical value. */
n2 = strlen30(zCmd);
for(i=0; i<ArraySize(aCtrl); i++){
- if( strncmp(zCmd, aCtrl[i].zCtrlName, n2)==0 ){
+ if( cli_strncmp(zCmd, aCtrl[i].zCtrlName, n2)==0 ){
if( testctrl<0 ){
testctrl = aCtrl[i].ctrlCode;
iCtrl = i;
@@ -13473,7 +13633,7 @@
if( nArg==3 || nArg==4 ){
int ii = (int)integerValue(azArg[2]);
sqlite3 *db;
- if( ii==0 && strcmp(azArg[2],"random")==0 ){
+ if( ii==0 && cli_strcmp(azArg[2],"random")==0 ){
sqlite3_randomness(sizeof(ii),&ii);
fprintf(STD_OUT, "-- random seed: %d\n", ii);
}
@@ -13630,7 +13790,7 @@
}
}else{
output_file_close(psi->traceOut);
- psi->traceOut = output_file_open(azArg[1], 0);
+ psi->traceOut = output_file_open(z, 0);
}
}
if( psi->traceOut==0 ){
@@ -13673,7 +13833,7 @@
zOpt = azArg[1];
if( zOpt[0]=='-' && zOpt[1]=='-' && zOpt[2]!=0 ) zOpt++;
lenOpt = (int)strlen(zOpt);
- if( lenOpt>=3 && strncmp(zOpt, "-allexcept",lenOpt)==0 ){
+ if( lenOpt>=3 && cli_strncmp(zOpt, "-allexcept",lenOpt)==0 ){
assert( azArg[nArg]==0 );
sqlite3_drop_modules(DBX(p), nArg>2 ? (const char**)(azArg+2) : 0);
}else{
@@ -13705,7 +13865,7 @@
return DCR_SayUsage;
}
open_db(p, 0);
- if( strcmp(azArg[1],"login")==0 ){
+ if( cli_strcmp(azArg[1],"login")==0 ){
if( nArg!=4 ){
goto teach_fail;
}
@@ -13715,7 +13875,7 @@
*pzErr = shellMPrintf(0,"Authentication failed for user %s\n", azArg[2]);
return DCR_Error;
}
- }else if( strcmp(azArg[1],"add")==0 ){
+ }else if( cli_strcmp(azArg[1],"add")==0 ){
if( nArg!=5 ){
goto teach_fail;
}
@@ -13725,7 +13885,7 @@
*pzErr = shellMPrintf(0,"User-Add failed: %d\n", rc);
return DCR_Error;
}
- }else if( strcmp(azArg[1],"edit")==0 ){
+ }else if( cli_strcmp(azArg[1],"edit")==0 ){
if( nArg!=5 ){
goto teach_fail;
}
@@ -13735,7 +13895,7 @@
*pzErr = shellMPrintf(0,"User-Edit failed: %d\n", rc);
return DCR_Error;
}
- }else if( strcmp(azArg[1],"delete")==0 ){
+ }else if( cli_strcmp(azArg[1],"delete")==0 ){
if( nArg!=3 ){
goto teach_fail;
}
@@ -13779,7 +13939,7 @@
int ncCmd = strlen30(zCmd);
if( *zCmd>'e' && ncCmd<2 ) return DCR_Ambiguous|1;
-#define SUBCMD(scn) (strncmp(zCmd, scn, ncCmd)==0)
+#define SUBCMD(scn) (cli_strncmp(zCmd, scn, ncCmd)==0)
/* This could be done lazily, but with more code. */
if( (dbs && ensure_shvars_table(dbs)!=SQLITE_OK) ){
@@ -13956,8 +14116,7 @@
}
/*****************
- * The .width and .wheretrace commands
- * The .wheretrace command has no help.
+ * The .width command
*/
static void setColumnWidths(ShellExState *p, char *azWidths[], int nWidths){
int j;
@@ -13980,17 +14139,12 @@
setColumnWidths(p, azArg+1, nArg-1);
return DCR_Ok;
}
-DISPATCHABLE_COMMAND( wheretrace ? 1 2 ){
- unsigned int x = nArg>=2 ? (unsigned int)integerValue(azArg[1]) : 0xffffffff;
- sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 3, &x);
- return DCR_Ok;
-}
/*****************
* The .x, .read and .eval commands
* These are together because they share some function and implementation.
*/
-
+CONDITION_COMMAND(read !defined(SQLITE_SHELL_FIDDLE));
COLLECT_HELP_TEXT[
".eval ?ARGS? Process each ARG's content as shell input.",
".read FILE Read input from FILE",
@@ -14344,7 +14498,7 @@
}
/* Prepare an iterator that will produce a sequence of DotCommand
- * pointers whose referents' names match the given cmdFragment.
+ * pointers whose referents' names match the given cmdFragment.
* Return how many will match (if iterated upon return.) */
static int findMatchingDotCmds(const char *cmdFragment,
CmdMatchIter *pMMI,
@@ -14476,7 +14630,7 @@
int ixb = 0, ixe = numCommands-1;
while( ixb <= ixe ){
int ixm = (ixb+ixe)/2;
- int md = strncmp(cmdName, command_table[ixm].cmdName, cmdLen);
+ int md = cli_strncmp(cmdName, command_table[ixm].cmdName, cmdLen);
if( md>0 ){
ixb = ixm+1;
}else if( md<0 ){
@@ -14485,10 +14639,10 @@
/* Have a match, see whether it's ambiguous. */
if( command_table[ixm].minLen > cmdLen ){
if( (ixm>0
- && !strncmp(cmdName, command_table[ixm-1].cmdName, cmdLen))
+ && !cli_strncmp(cmdName, command_table[ixm-1].cmdName, cmdLen))
||
(ixm<ixe
- && !strncmp(cmdName, command_table[ixm+1].cmdName, cmdLen)) ){
+ && !cli_strncmp(cmdName, command_table[ixm+1].cmdName, cmdLen))){
/* Yes, a neighbor matches too. */
if( pnFound ) *pnFound = 2; /* could be more, but >1 suffices */
return 0;
@@ -14971,7 +15125,8 @@
** The scan is resumable for subsequent lines when prior
** return values are passed as the 2nd argument.
*/
-static void sql_prescan(const char *zLine, SqlScanState *pSSS){
+static void sql_prescan(const char *zLine, SqlScanState *pSSS,
+ SCAN_TRACKER_REFTYPE pst){
SqlScanState sss = *pSSS;
char cin;
char cWait = (char)sss; /* intentional narrowing loss */
@@ -14996,6 +15151,7 @@
if( *zLine=='*' ){
++zLine;
cWait = '*';
+ CONTINUE_PROMPT_AWAITS(pst, "/*");
sss = SSS_SETV(sss, cWait);
goto TermScan;
}
@@ -15007,7 +15163,14 @@
case '`': case '\'': case '"':
cWait = cin;
sss = SSS_HasDark | cWait;
+ CONTINUE_PROMPT_AWAITC(pst, cin);
goto TermScan;
+ case '(':
+ CONTINUE_PAREN_INCR(pst, 1);
+ break;
+ case ')':
+ CONTINUE_PAREN_INCR(pst, -1);
+ break;
default:
break;
}
@@ -15023,17 +15186,19 @@
continue;
++zLine;
cWait = 0;
-
+ CONTINUE_PROMPT_AWAITC(pst, 0);
sss = SSS_SETV(sss, 0);
goto PlainScan;
case '`': case '\'': case '"':
if(*zLine==cWait){
+ /* Swallow doubled end-delimiter.*/
++zLine;
continue;
}
/* fall thru */
case ']':
cWait = 0;
+ CONTINUE_PROMPT_AWAITC(pst, 0);
sss = SSS_SETV(sss, 0);
goto PlainScan;
default: assert(0);
@@ -15059,20 +15224,18 @@
iSkip = 2; /* SQL Server */
if( iSkip>0 ){
SqlScanState sss = SSS_Start;
- sql_prescan(zDark+iSkip,&sss);
+ sql_prescan(zDark+iSkip, &sss, 0);
if( sss==SSS_Start ) return (char *)zDark;
}
return 0;
}
/*
-** We need a default sqlite3_complete() implementation to use in case
-** the shell is compiled with SQLITE_OMIT_COMPLETE. The default assumes
-** any arbitrary text is a complete SQL statement. This is not very
-** user-friendly, but it does seem to work.
+** The CLI needs a working sqlite3_complete() to work properly. So error
+** out of the build if compiling with SQLITE_OMIT_COMPLETE.
*/
#ifdef SQLITE_OMIT_COMPLETE
-#define sqlite3_complete(x) 1
+# error the CLI application is imcompatable with SQLITE_OMIT_COMPLETE.
#endif
/*
@@ -15112,10 +15275,10 @@
if( zErrMsg==0 ){
zErrorType = "Error";
zErrorTail = sqlite3_errmsg(DBX(psx));
- }else if( strncmp(zErrMsg, "in prepare, ",12)==0 ){
+ }else if( cli_strncmp(zErrMsg, "in prepare, ",12)==0 ){
zErrorType = "Parse error";
zErrorTail = &zErrMsg[12];
- }else if( strncmp(zErrMsg, "stepping, ", 10)==0 ){
+ }else if( cli_strncmp(zErrMsg, "stepping, ", 10)==0 ){
zErrorType = "Runtime error";
zErrorTail = &zErrMsg[10];
}else{
@@ -15141,6 +15304,10 @@
return 0;
}
+static void echo_group_input(ShellState *p, const char *zDo){
+ if( ShellHasFlag(p, SHFLG_Echo) ) utf8_printf(p->out, "%s\n", zDo);
+}
+
#if SHELL_EXTENDED_PARSING
/* Resumable line classsifier for dot-commands
**
@@ -15172,12 +15339,14 @@
isOpenMask = 1|4 /* bit test */
} DCmd_ScanState;
-static void dot_command_scan(char *zCmd, DCmd_ScanState *pScanState){
+static void dot_command_scan(char *zCmd, DCmd_ScanState *pScanState,
+ SCAN_TRACKER_REFTYPE pst){
DCmd_ScanState ss = *pScanState & ~endEscaped;
char c = (ss&isOpenMask)? 1 : *zCmd++;
while( c!=0 ){
switch( ss ){
case twixtArgs:
+ CONTINUE_PROMPT_AWAITC(pst, 0);
while( IsSpace(c) ){
if( (c=*zCmd++)==0 ) goto atEnd;
}
@@ -15193,6 +15362,7 @@
}
inSq:
case inSqArg:
+ CONTINUE_PROMPT_AWAITC(pst, '\'');
while( (c=*zCmd++)!='\'' ){
if( c==0 ) goto atEnd;
if( c=='\\' && *zCmd==0 ){
@@ -15205,6 +15375,7 @@
continue;
inDq:
case inDqArg:
+ CONTINUE_PROMPT_AWAITC(pst, '"');
do {
if( (c=*zCmd++)==0 ) goto atEnd;
if( c=='\\' ){
@@ -15220,6 +15391,7 @@
continue;
inDark:
case inDarkArg:
+ CONTINUE_PROMPT_AWAITC(pst, 0);
while( !IsSpace(c) ){
if( c=='\\' && *zCmd==0 ){
ss |= endEscaped;
@@ -15301,7 +15473,7 @@
/* Above two pointers could be local to the group handling loop, but are
* not so that the number of memory allocations can be reduced. They are
* reused from one incoming group to another, realloc()'ed as needed. */
- int naAccum = 0; /* tracking how big zLineAccum buffer has become */
+ i64 naAccum = 0; /* tracking how big zLineAccum buffer has become */
/* Some flags for ending the overall group processing loop, always 1 or 0 */
u8 bInputEnd=0, bInterrupted=0;
/* Termination kind: DCR_Ok, DCR_Error, DCR_Return, DCR_Exit, DCR_Abort,
@@ -15336,9 +15508,9 @@
ScriptSupport *pSS = psi->script;
#endif
int nGroupLines = 0; /* count of lines belonging to this group */
- int ncLineIn = 0; /* how many (non-zero) chars are in zLineInput */
- int ncLineAcc = 0; /* how many (non-zero) chars are in zLineAccum */
- int iLastLine = 0; /* index of last accumulated line start */
+ i64 ncLineIn = 0; /* how many (non-zero) chars are in zLineInput */
+ i64 ncLineAcc = 0; /* how many (non-zero) chars are in zLineAccum */
+ i64 iLastLine = 0; /* index of last accumulated line start */
/* Initialize resumable scanner(s). */
SqlScanState sqScanState = SSS_Start; /* for SQL scan */
#if SHELL_EXTENDED_PARSING
@@ -15366,6 +15538,7 @@
int iStartline = 0; /* starting line number of group */
fflush(psi->out);
+ CONTINUE_PROMPT_RESET;
zLineInput = one_input_line(psi->pInSource, zLineInput,
nGroupLines>0, &shellPrompts);
if( zLineInput==0 ){
@@ -15398,14 +15571,15 @@
switch( zLineInput[nLeadWhite] ){
case '.':
inKind = Cmd;
- dot_command_scan(zLineInput+nLeadWhite, &dcScanState);
+ dot_command_scan(zLineInput+nLeadWhite, &dcScanState,
+ CONTINUE_PROMPT_PSTATE);
break;
case '#':
inKind = Comment;
break;
default:
/* Might be SQL, or a swallowable whole SQL comment. */
- sql_prescan(zLineInput, &sqScanState);
+ sql_prescan(zLineInput, &sqScanState, CONTINUE_PROMPT_PSTATE);
if( SSS_PLAINWHITE(sqScanState) ){
/* It's either all blank or a whole SQL comment. Swallowable. */
inKind = Comment;
@@ -15427,6 +15601,7 @@
* before it is ready, with the group marked as erroneous.
*/
while( disposition==Incoming ){
+ PROMPTS_UPDATE(inKind == Sql || inKind == Cmd);
/* Check whether more to accumulate, or ready for final disposition. */
switch( inKind ){
case Comment:
@@ -15499,10 +15674,10 @@
/* Scan line just input (if needed) and append to accumulation. */
switch( inKind ){
case Cmd:
- dot_command_scan(zLineInput, &dcScanState);
+ dot_command_scan(zLineInput, &dcScanState, CONTINUE_PROMPT_PSTATE);
break;
case Sql:
- sql_prescan(zLineInput, &sqScanState);
+ sql_prescan(zLineInput, &sqScanState, CONTINUE_PROMPT_PSTATE);
break;
default:
break;
@@ -15520,6 +15695,7 @@
} /* end glom another line */
} /* end group collection loop */
/* Here, the group is fully collected or known to be incomplete forever. */
+ CONTINUE_PROMPT_RESET;
switch( disposition ){
case Dumpable:
echo_group_input(psi, *pzLineUse);
@@ -15553,7 +15729,7 @@
dcr = pSS->pMethods->runScript(pSS, *pzLineUse+nLeadWhite,
XSS(psi), &zErr);
if( dcr!=DCR_Ok || zErr!=0 ){
- /* ToDo: Handle errors more informatively and like dot commands. */
+ /* Future: Handle errors more informatively and like dot commands. */
nErrors += (dcr!=DCR_Ok);
if( zErr!=0 ){
utf8_printf(STD_ERR, "Error: %s\n", zErr);
@@ -15689,10 +15865,43 @@
return rc;
}
+/*
+** On non-Windows platforms, look for $XDG_CONFIG_HOME.
+** If ${XDG_CONFIG_HOME}/sqlite3/sqliterc is found, return
+** the path to it, else return 0. The result is cached for
+** subsequent calls.
+*/
+static const char *find_xdg_config(void){
+#if defined(_WIN32) || defined(WIN32) || defined(_WIN32_WCE) \
+ || defined(__RTP__) || defined(_WRS_KERNEL)
+ return 0;
+#else
+ static int alreadyTried = 0;
+ static char *zConfig = 0;
+ const char *zXdgHome;
+
+ if( alreadyTried!=0 ){
+ return zConfig;
+ }
+ alreadyTried = 1;
+ zXdgHome = getenv("XDG_CONFIG_HOME");
+ if( zXdgHome==0 ){
+ return 0;
+ }
+ zConfig = sqlite3_mprintf("%s/sqlite3/sqliterc", zXdgHome);
+ shell_check_oom(zConfig);
+ if( access(zConfig,0)!=0 ){
+ sqlite3_free(zConfig);
+ zConfig = 0;
+ }
+ return zConfig;
+#endif
+}
/*
** Read input from the file given by sqliterc_override. Or if that
-** parameter is NULL, take input from ~/.sqliterc
+** parameter is NULL, take input from the first of find_xdg_config()
+** or ~/.sqliterc which can be found, else do nothing.
** The return is similar to process_input() (0 success, 1 error, x abort)
*/
static void process_sqliterc(
@@ -15704,7 +15913,10 @@
char *zBuf = 0;
FILE *inUse;
- if (sqliterc == NULL) {
+ if( sqliterc == NULL ){
+ sqliterc = find_xdg_config();
+ }
+ if( sqliterc == NULL ){
home_dir = find_home_dir(0);
if( home_dir==0 ){
raw_printf(STD_ERR, "-- warning: cannot find home directory;"
@@ -15755,7 +15967,7 @@
#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)
@@ -15912,14 +16124,26 @@
# endif
#endif
+#ifdef SQLITE_SHELL_FIDDLE
+# define main fiddle_main
+#endif
+
#if SQLITE_SHELL_IS_UTF8
int SQLITE_CDECL SHELL_MAIN(int argc, char **argv){
#else
int SQLITE_CDECL SHELL_MAIN(int argc, wchar_t **wargv){
char **argv;
#endif
+#ifdef SQLITE_DEBUG
+ sqlite3_int64 mem_main_enter = sqlite3_memory_used();
+#endif
+#ifdef SQLITE_SHELL_FIDDLE
+# define data shellState
+# define datax shellStateX
+#else
ShellInState data;
ShellExState datax;
+#endif
#if SHELL_DATAIO_EXT
BuiltInFFExporter ffExporter = BI_FF_EXPORTER_INIT( &data );
BuiltInCMExporter cmExporter = BI_CM_EXPORTER_INIT( &data );
@@ -15940,8 +16164,14 @@
#endif
setBinaryMode(STD_IN, 0);
setvbuf(STD_ERR, 0, _IONBF, 0); /* Make sure stderr is unbuffered */
+#ifdef SQLITE_SHELL_FIDDLE
+ stdin_is_interactive = 0;
+ stdout_is_console = 1;
+ data.wasm.zDefaultDbName = "/fiddle.sqlite3";
+#else
stdin_is_interactive = isatty(0);
stdout_is_console = isatty(1);
+#endif
#if !defined(_WIN32_WCE)
if( getenv("SQLITE_DEBUG_BREAK") ){
@@ -15965,7 +16195,7 @@
#endif
#if USE_SYSTEM_SQLITE+0!=1
- if( strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){
+ if( cli_strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){
utf8_printf(STD_ERR, "SQLite header and source version mismatch\n%s\n%s\n",
sqlite3_sourceid(), SQLITE_SOURCE_ID);
exit(1);
@@ -15992,11 +16222,11 @@
argv = argvToFree + argc;
for(i=0; i<argc; i++){
char *z = sqlite3_win32_unicode_to_utf8(wargv[i]);
- int n;
+ i64 n;
shell_check_oom(z);
- n = (int)strlen(z);
+ n = 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);
@@ -16046,21 +16276,21 @@
}
}
if( z[1]=='-' ) z++;
- if( strcmp(z,"-separator")==0
- || strcmp(z,"-nullvalue")==0
- || strcmp(z,"-newline")==0
- || strcmp(z,"-cmd")==0
+ if( cli_strcmp(z,"-separator")==0
+ || cli_strcmp(z,"-nullvalue")==0
+ || cli_strcmp(z,"-newline")==0
+ || cli_strcmp(z,"-cmd")==0
){
(void)cmdline_option_value(argc, argv, ++i);
- }else if( strcmp(z,"-init")==0 ){
+ }else if( cli_strcmp(z,"-init")==0 ){
zInitFile = cmdline_option_value(argc, argv, ++i);
- }else if( strcmp(z,"-batch")==0 ){
+ }else if( cli_strcmp(z,"-batch")==0 ){
/* Need to check for batch mode here to so we can avoid printing
** informational messages (like from process_sqliterc) before
** we do the actual processing of arguments later in a second pass.
*/
stdin_is_interactive = 0;
- }else if( strcmp(z,"-heap")==0 ){
+ }else if( cli_strcmp(z,"-heap")==0 ){
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
const char *zSize;
sqlite3_int64 szHeap;
@@ -16072,7 +16302,7 @@
#else
(void)cmdline_option_value(argc, argv, ++i);
#endif
- }else if( strcmp(z,"-pagecache")==0 ){
+ }else if( cli_strcmp(z,"-pagecache")==0 ){
sqlite3_int64 n, sz;
sz = integerValue(cmdline_option_value(argc,argv,++i));
if( sz>70000 ) sz = 70000;
@@ -16084,7 +16314,7 @@
sqlite3_config(SQLITE_CONFIG_PAGECACHE,
(n>0 && sz>0) ? malloc(n*sz) : 0, sz, n);
data.shellFlgs |= SHFLG_Pagecache;
- }else if( strcmp(z,"-lookaside")==0 ){
+ }else if( cli_strcmp(z,"-lookaside")==0 ){
int n, sz;
sz = (int)integerValue(cmdline_option_value(argc,argv,++i));
if( sz<0 ) sz = 0;
@@ -16092,7 +16322,7 @@
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 ){
+ }else if( cli_strcmp(z,"-threadsafe")==0 ){
int n;
n = (int)integerValue(cmdline_option_value(argc,argv,++i));
switch( n ){
@@ -16101,7 +16331,7 @@
default: sqlite3_config(SQLITE_CONFIG_SERIALIZED); break;
}
#ifdef SQLITE_ENABLE_VFSTRACE
- }else if( strcmp(z,"-vfstrace")==0 ){
+ }else if( cli_strcmp(z,"-vfstrace")==0 ){
extern int vfstrace_register(
const char *zTraceName,
const char *zOldVfsName,
@@ -16112,56 +16342,56 @@
vfstrace_register("trace",0,(int(*)(const char*,void*))fputs,STD_ERR,1);
#endif
#ifdef SQLITE_ENABLE_MULTIPLEX
- }else if( strcmp(z,"-multiplex")==0 ){
+ }else if( cli_strcmp(z,"-multiplex")==0 ){
extern int sqlite3_multiple_initialize(const char*,int);
sqlite3_multiplex_initialize(0, 1);
#endif
- }else if( strcmp(z,"-mmap")==0 ){
+ }else if( cli_strcmp(z,"-mmap")==0 ){
sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i));
sqlite3_config(SQLITE_CONFIG_MMAP_SIZE, sz, sz);
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
- }else if( strcmp(z,"-sorterref")==0 ){
+ }else if( cli_strcmp(z,"-sorterref")==0 ){
sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i));
sqlite3_config(SQLITE_CONFIG_SORTERREF_SIZE, (int)sz);
#endif
- }else if( strcmp(z,"-vfs")==0 ){
+ }else if( cli_strcmp(z,"-vfs")==0 ){
zVfs = cmdline_option_value(argc, argv, ++i);
#ifdef SQLITE_HAVE_ZLIB
- }else if( strcmp(z,"-zip")==0 ){
+ }else if( cli_strcmp(z,"-zip")==0 ){
data.openMode = SHELL_OPEN_ZIPFILE;
#endif
- }else if( strcmp(z,"-append")==0 ){
+ }else if( cli_strcmp(z,"-append")==0 ){
data.openMode = SHELL_OPEN_APPENDVFS;
#ifndef SQLITE_OMIT_DESERIALIZE
- }else if( strcmp(z,"-deserialize")==0 ){
+ }else if( cli_strcmp(z,"-deserialize")==0 ){
data.openMode = SHELL_OPEN_DESERIALIZE;
- }else if( strcmp(z,"-maxsize")==0 && i+1<argc ){
+ }else if( cli_strcmp(z,"-maxsize")==0 && i+1<argc ){
data.szMax = integerValue(argv[++i]);
#endif
- }else if( strcmp(z,"-readonly")==0 ){
+ }else if( cli_strcmp(z,"-readonly")==0 ){
data.openMode = SHELL_OPEN_READONLY;
- }else if( strcmp(z,"-nofollow")==0 ){
+ }else if( cli_strcmp(z,"-nofollow")==0 ){
data.openFlags = SQLITE_OPEN_NOFOLLOW;
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
- }else if( strncmp(z, "-A",2)==0 ){
+ }else if( cli_strncmp(z, "-A",2)==0 ){
/* All remaining command-line arguments are passed to the ".archive"
** command, so ignore them */
break;
#endif
- }else if( strcmp(z, "-memtrace")==0 ){
+ }else if( cli_strcmp(z, "-memtrace")==0 ){
sqlite3MemTraceActivate(STD_ERR);
- }else if( strcmp(z,"-bail")==0 ){
+ }else if( cli_strcmp(z,"-bail")==0 ){
bail_on_error = 1;
#if SHELL_EXTENSIONS
- }else if( strcmp(z,"-shxopts")==0 ){
+ }else if( cli_strcmp(z,"-shxopts")==0 ){
data.bExtendedDotCmds = (u8)integerValue(argv[++i]);
#endif
- }else if( strcmp(z,"-nonce")==0 ){
+ }else if( cli_strcmp(z,"-nonce")==0 ){
free(data.zNonce);
data.zNonce = strdup(argv[++i]);
- }else if( strcmp(z,"-quiet")==0 ){
+ }else if( cli_strcmp(z,"-quiet")==0 ){
bQuiet = (int)integerValue(cmdline_option_value(argc,argv,++i));
- }else if( strcmp(z,"-safe")==0 ){
+ }else if( cli_strcmp(z,"-safe")==0 ){
/* catch this on the second pass (Unsafe is fine on invocation.) */
}
}
@@ -16197,7 +16427,7 @@
if( pVfs ){
sqlite3_vfs_register(pVfs, 1);
}else{
- utf8_printf(STD_ERR, "no such VFS: \"%s\"\n", argv[i]);
+ utf8_printf(STD_ERR, "no such VFS: \"%s\"\n", zVfs);
rc = 1;
goto shell_bail;
}
@@ -16214,7 +16444,9 @@
#endif
}
data.out = STD_OUT;
+#ifndef SQLITE_SHELL_FIDDLE
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
@@ -16241,124 +16473,124 @@
char *zModeSet = 0;
if( z[0]!='-' ) continue;
if( z[1]=='-' ){ z++; }
- if( strcmp(z,"-init")==0 ){
+ if( cli_strcmp(z,"-init")==0 ){
i++;
- }else if( strcmp(z,"-html")==0 ){
+ }else if( cli_strcmp(z,"-html")==0 ){
zModeSet = z;
- }else if( strcmp(z,"-list")==0 ){
+ }else if( cli_strcmp(z,"-list")==0 ){
zModeSet = z;
- }else if( strcmp(z,"-quote")==0 ){
+ }else if( cli_strcmp(z,"-quote")==0 ){
zModeSet = z;
- }else if( strcmp(z,"-line")==0 ){
+ }else if( cli_strcmp(z,"-line")==0 ){
zModeSet = z;
- }else if( strcmp(z,"-column")==0 ){
+ }else if( cli_strcmp(z,"-column")==0 ){
zModeSet = z;
- }else if( strcmp(z,"-json")==0 ){
+ }else if( cli_strcmp(z,"-json")==0 ){
zModeSet = z;
- }else if( strcmp(z,"-markdown")==0 ){
+ }else if( cli_strcmp(z,"-markdown")==0 ){
zModeSet = z;
- }else if( strcmp(z,"-table")==0 ){
+ }else if( cli_strcmp(z,"-table")==0 ){
zModeSet = z;
- }else if( strcmp(z,"-box")==0 ){
+ }else if( cli_strcmp(z,"-box")==0 ){
zModeSet = z;
- }else if( strcmp(z,"-csv")==0 ){
+ }else if( cli_strcmp(z,"-csv")==0 ){
zModeSet = z;
- }else if( strcmp(z,"-ascii")==0 ){
+ }else if( cli_strcmp(z,"-ascii")==0 ){
zModeSet = z;
- }else if( strcmp(z,"-tabs")==0 ){
+ }else if( cli_strcmp(z,"-tabs")==0 ){
zModeSet = z;
#ifdef SQLITE_HAVE_ZLIB
- }else if( strcmp(z,"-zip")==0 ){
+ }else if( cli_strcmp(z,"-zip")==0 ){
data.openMode = SHELL_OPEN_ZIPFILE;
#endif
- }else if( strcmp(z,"-append")==0 ){
+ }else if( cli_strcmp(z,"-append")==0 ){
data.openMode = SHELL_OPEN_APPENDVFS;
#ifndef SQLITE_OMIT_DESERIALIZE
- }else if( strcmp(z,"-deserialize")==0 ){
+ }else if( cli_strcmp(z,"-deserialize")==0 ){
data.openMode = SHELL_OPEN_DESERIALIZE;
- }else if( strcmp(z,"-maxsize")==0 && i+1<argc ){
+ }else if( cli_strcmp(z,"-maxsize")==0 && i+1<argc ){
data.szMax = integerValue(argv[++i]);
#endif
- }else if( strcmp(z,"-readonly")==0 ){
+ }else if( cli_strcmp(z,"-readonly")==0 ){
data.openMode = SHELL_OPEN_READONLY;
- }else if( strcmp(z,"-nofollow")==0 ){
+ }else if( cli_strcmp(z,"-nofollow")==0 ){
data.openFlags |= SQLITE_OPEN_NOFOLLOW;
- }else if( strcmp(z,"-separator")==0 ){
+ }else if( cli_strcmp(z,"-separator")==0 ){
sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,
"%s",cmdline_option_value(argc,argv,++i));
- }else if( strcmp(z,"-newline")==0 ){
+ }else if( cli_strcmp(z,"-newline")==0 ){
sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,
"%s",cmdline_option_value(argc,argv,++i));
- }else if( strcmp(z,"-nullvalue")==0 ){
+ }else if( cli_strcmp(z,"-nullvalue")==0 ){
sqlite3_snprintf(sizeof(data.nullValue), data.nullValue,
"%s",cmdline_option_value(argc,argv,++i));
- }else if( strcmp(z,"-header")==0 ){
+ }else if( cli_strcmp(z,"-header")==0 ){
data.showHeader = 1;
ShellSetFlag(&datax, SHFLG_HeaderSet);
- }else if( strcmp(z,"-noheader")==0 ){
+ }else if( cli_strcmp(z,"-noheader")==0 ){
data.showHeader = 0;
ShellSetFlag(&datax, SHFLG_HeaderSet);
- }else if( strcmp(z,"-echo")==0 ){
+ }else if( cli_strcmp(z,"-echo")==0 ){
ShellSetFlag(&datax, SHFLG_Echo);
- }else if( strcmp(z,"-eqp")==0 ){
+ }else if( cli_strcmp(z,"-eqp")==0 ){
data.autoEQP = AUTOEQP_on;
- }else if( strcmp(z,"-eqpfull")==0 ){
+ }else if( cli_strcmp(z,"-eqpfull")==0 ){
data.autoEQP = AUTOEQP_full;
- }else if( strcmp(z,"-stats")==0 ){
+ }else if( cli_strcmp(z,"-stats")==0 ){
data.statsOn = 1;
- }else if( strcmp(z,"-scanstats")==0 ){
+ }else if( cli_strcmp(z,"-scanstats")==0 ){
data.scanstatsOn = 1;
- }else if( strcmp(z,"-backslash")==0 ){
+ }else if( cli_strcmp(z,"-backslash")==0 ){
/* Undocumented command-line option: -backslash
** Causes C-style backslash escapes to be evaluated in SQL statements
** prior to sending the SQL into SQLite. Useful for injecting
** crazy bytes in the middle of SQL statements for testing and debugging.
*/
ShellSetFlag(&datax, SHFLG_Backslash);
- }else if( strcmp(z,"-bail")==0 ){
+ }else if( cli_strcmp(z,"-bail")==0 ){
/* No-op. The bail_on_error flag should already be set. */
#if SHELL_EXTENSIONS
- }else if( strcmp(z,"-shxopts")==0 ){
+ }else if( cli_strcmp(z,"-shxopts")==0 ){
i++; /* Handled on first pass. */
#endif
- }else if( strcmp(z,"-version")==0 ){
+ }else if( cli_strcmp(z,"-version")==0 ){
fprintf(STD_OUT, "%s %s\n", sqlite3_libversion(), sqlite3_sourceid());
rc = 2;
- }else if( strcmp(z,"-interactive")==0 ){
+ }else if( cli_strcmp(z,"-interactive")==0 ){
stdin_is_interactive = 1;
- }else if( strcmp(z,"-batch")==0 ){
+ }else if( cli_strcmp(z,"-batch")==0 ){
stdin_is_interactive = 0;
- }else if( strcmp(z,"-heap")==0 ){
+ }else if( cli_strcmp(z,"-heap")==0 ){
i++;
- }else if( strcmp(z,"-pagecache")==0 ){
+ }else if( cli_strcmp(z,"-pagecache")==0 ){
i+=2;
- }else if( strcmp(z,"-lookaside")==0 ){
+ }else if( cli_strcmp(z,"-lookaside")==0 ){
i+=2;
- }else if( strcmp(z,"-threadsafe")==0 ){
+ }else if( cli_strcmp(z,"-threadsafe")==0 ){
i+=2;
- }else if( strcmp(z,"-nonce")==0 ){
+ }else if( cli_strcmp(z,"-nonce")==0 ){
i += 2;
- }else if( strcmp(z,"-mmap")==0 ){
+ }else if( cli_strcmp(z,"-mmap")==0 ){
i++;
- }else if( strcmp(z,"-memtrace")==0 ){
+ }else if( cli_strcmp(z,"-memtrace")==0 ){
i++;
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
- }else if( strcmp(z,"-sorterref")==0 ){
+ }else if( cli_strcmp(z,"-sorterref")==0 ){
i++;
#endif
- }else if( strcmp(z,"-vfs")==0 ){
+ }else if( cli_strcmp(z,"-vfs")==0 ){
i++;
#ifdef SQLITE_ENABLE_VFSTRACE
- }else if( strcmp(z,"-vfstrace")==0 ){
+ }else if( cli_strcmp(z,"-vfstrace")==0 ){
i++;
#endif
#ifdef SQLITE_ENABLE_MULTIPLEX
- }else if( strcmp(z,"-multiplex")==0 ){
+ }else if( cli_strcmp(z,"-multiplex")==0 ){
i++;
#endif
- }else if( strcmp(z,"-help")==0 ){
+ }else if( cli_strcmp(z,"-help")==0 ){
usage(1);
- }else if( strcmp(z,"-cmd")==0 ){
+ }else if( cli_strcmp(z,"-cmd")==0 ){
/* Run commands that follow -cmd first and separately from commands
** that simply appear on the command-line. This seems goofy. It would
** be better if all commands ran in the order that they appear. But
@@ -16376,7 +16608,7 @@
goto shell_bail;
}
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
- }else if( strncmp(z, "-A", 2)==0 ){
+ }else if( cli_strncmp(z, "-A", 2)==0 ){
if( nCmd>0 ){
utf8_printf(STD_ERR, "Error: cannot mix regular SQL or dot-commands"
" with \"%s\"\n", z);
@@ -16394,9 +16626,9 @@
readStdin = 0;
break;
#endif
- }else if( strcmp(z,"-safe")==0 ){
+ }else if( cli_strcmp(z,"-safe")==0 ){
data.bSafeMode = data.bSafeModeFuture = 1;
- }else if( strcmp(z,"-quiet")==0 ){
+ }else if( cli_strcmp(z,"-quiet")==0 ){
++i;
}else{
utf8_printf(STD_ERR,"%s: Error: unknown option: %s\n", Argv0, z);
@@ -16481,12 +16713,17 @@
}
}
shell_bail:
+#ifndef SQLITE_SHELL_FIDDLE
+ /* 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.
+ ** For that build, just bypass freeing all acquired resources.
+ */
set_table_name(&datax, 0);
if( datax.dbUser ){
session_close_all(&data, -1);
-#if SHELL_DYNAMIC_EXTENSION
+# if SHELL_DYNAMIC_EXTENSION
notify_subscribers(&data, NK_DbAboutToClose, datax.dbUser);
-#endif
+# endif
close_db(datax.dbUser);
}
sqlite3_mutex_free(pGlobalDbLock);
@@ -16503,7 +16740,7 @@
data.doXdgOpen = 0;
clearTempFile(&data);
sqlite3_free(data.zEditor);
-#if SHELL_DYNAMIC_EXTENSION
+# if SHELL_DYNAMIC_EXTENSION
notify_subscribers(&data, NK_DbAboutToClose, datax.dbShell);
/* It is necessary that the shell DB be closed after the user DBs.
* This is because loaded extensions are held by the shell DB and
@@ -16520,23 +16757,33 @@
notify_subscribers(&data, NK_ShutdownImminent, 0);
/* Forcefull unsubscribe static extension event listeners. */
subscribe_events(&datax, 0, &datax, NK_Unsubscribe, 0);
-#endif
-#if !SQLITE_SHELL_IS_UTF8
+# endif
+# if !SQLITE_SHELL_IS_UTF8
for(i=0; i<argcToFree; i++) free(argvToFree[i]);
free(argvToFree);
-#endif
+# endif
free(datax.pSpecWidths);
free(data.zNonce);
for(i=0; i<data.nSavedModes; ++i) sqlite3_free(data.pModeStack[i]);
free(azCmd);
-#if SHELL_DATAIO_EXT
+# if SHELL_DATAIO_EXT
cmExporter.pMethods->destruct((ExportHandler*)&cmExporter);
ffExporter.pMethods->destruct((ExportHandler*)&ffExporter);
-#endif
+# endif
aec = datax.shellAbruptExit;
/* Clear shell state objects so that valgrind detects real memory leaks. */
memset(&data, 0, sizeof(data));
memset(&datax, 0, sizeof(datax));
+# 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 /* !defined(SQLITE_SHELL_FIDDLE) */
+#ifdef SQLITE_SHELL_FIDDLE
+ aec = datax.shellAbruptExit;
+#endif
/* Process exit codes to yield single shell exit code.
* rc == 2 is a quit signal, resulting in no error by itself.
* datax.shellAbruptExit conveyed either a normal (success or error)
@@ -16552,3 +16799,127 @@
}
return rc;
}
+
+#ifdef SQLITE_SHELL_FIDDLE
+/* Only for emcc experimentation purposes. */
+int fiddle_experiment(int a,int b){
+ return a + b;
+}
+
+/*
+** Returns a pointer to the current DB handle.
+*/
+sqlite3 * fiddle_db_handle(){
+ return globalDb;
+}
+
+/*
+** Returns a pointer to the given DB name's VFS. If zDbName is 0 then
+** "main" is assumed. Returns 0 if no db with the given name is
+** open.
+*/
+sqlite3_vfs * fiddle_db_vfs(const char *zDbName){
+ sqlite3_vfs * pVfs = 0;
+ if(globalDb){
+ sqlite3_file_control(globalDb, zDbName ? zDbName : "main",
+ SQLITE_FCNTL_VFS_POINTER, &pVfs);
+ }
+ return pVfs;
+}
+
+/* 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;
+}
+
+/*
+** Completely wipes out the contents of the currently-opened database
+** but leaves its storage intact for reuse.
+*/
+void fiddle_reset_db(void){
+ if( globalDb ){
+ int rc = sqlite3_db_config(globalDb, SQLITE_DBCONFIG_RESET_DATABASE, 1, 0);
+ if( 0==rc ) rc = sqlite3_exec(globalDb, "VACUUM", 0, 0, 0);
+ sqlite3_db_config(globalDb, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0);
+ }
+}
+
+/*
+** Uses the current database's VFS xRead to stream the db file's
+** contents out to the given callback. The callback gets a single
+** chunk of size n (its 2nd argument) on each call and must return 0
+** on success, non-0 on error. This function returns 0 on success,
+** SQLITE_NOTFOUND if no db is open, or propagates any other non-0
+** code from the callback. Note that this is not thread-friendly: it
+** expects that it will be the only thread reading the db file and
+** takes no measures to ensure that is the case.
+*/
+int fiddle_export_db( int (*xCallback)(unsigned const char *zOut, int n) ){
+ sqlite3_int64 nSize = 0;
+ sqlite3_int64 nPos = 0;
+ sqlite3_file * pFile = 0;
+ unsigned char buf[1024 * 8];
+ int nBuf = (int)sizeof(buf);
+ int rc = shellState.db
+ ? sqlite3_file_control(shellState.db, "main",
+ SQLITE_FCNTL_FILE_POINTER, &pFile)
+ : SQLITE_NOTFOUND;
+ if( rc ) return rc;
+ rc = pFile->pMethods->xFileSize(pFile, &nSize);
+ if( rc ) return rc;
+ if(nSize % nBuf){
+ /* DB size is not an even multiple of the buffer size. Reduce
+ ** buffer size so that we do not unduly inflate the db size when
+ ** exporting. */
+ if(0 == nSize % 4096) nBuf = 4096;
+ else if(0 == nSize % 2048) nBuf = 2048;
+ else if(0 == nSize % 1024) nBuf = 1024;
+ else nBuf = 512;
+ }
+ for( ; 0==rc && nPos<nSize; nPos += nBuf ){
+ rc = pFile->pMethods->xRead(pFile, buf, nBuf, nPos);
+ if(SQLITE_IOERR_SHORT_READ == rc){
+ rc = (nPos + nBuf) < nSize ? rc : 0/*assume EOF*/;
+ }
+ if( 0==rc ) rc = xCallback(buf, nBuf);
+ }
+ return rc;
+}
+
+/*
+** Trivial exportable function for emscripten. It processes zSql as if
+** it were input to the sqlite3 shell and redirects all output to the
+** wasm binding. fiddle_main() must have been called before this
+** is called, or results are undefined.
+*/
+void fiddle_exec(const char * zSql){
+ if(zSql && *zSql){
+ if('.'==*zSql) puts(zSql);
+ shellState.wasm.zInput = zSql;
+ shellState.wasm.zPos = zSql;
+ process_input(&shellState);
+ shellState.wasm.zInput = shellState.wasm.zPos = 0;
+ }
+}
+#endif /* defined(SQLITE_SHELL_FIDDLE) */