--- /dev/null
- static ResourceMark exit_mark = 0;
- #ifndef SHELL_OMIT_LONGJMP
- static jmp_buf *p_exit_ripper = 0;
- #endif
+/*
+** 2023 May 8
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file implements a simple resource management package, interface
+** and function of which is described in resmanage.h .
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <assert.h>
+#include "resmanage.h"
+
+/* Track how to free various held resources. */
+typedef enum FreeableResourceKind {
+ FRK_Indirect = 1<<15,
+ FRK_Malloc = 0, FRK_DbConn = 1, FRK_DbStmt = 2,
+ FRK_DbMem = 3, FRK_File = 4,
+#if (!defined(_WIN32) && !defined(WIN32)) || !SQLITE_OS_WINRT
+ FRK_Pipe,
+#endif
+#ifdef SHELL_MANAGE_TEXT
+ FRK_Text,
+#endif
+ FRK_AnyRef,
+ FRK_CustomBase /* series of values for custom freers */
+} FreeableResourceKind;
+
+#if defined(_WIN32) || defined(WIN32)
+# if !SQLITE_OS_WINRT
+# include <io.h>
+# include <fcntl.h>
+# undef pclose
+# define pclose _pclose
+# endif
+#else
+extern int pclose(FILE*);
+#endif
+
+typedef struct ResourceHeld {
+ union {
+ void *p_any;
+ void *p_malloced;
+ sqlite3 *p_conn;
+ sqlite3_stmt *p_stmt;
+ void *p_s3mem;
+ FILE *p_stream;
+#ifdef SHELL_MANAGE_TEXT
+ ShellText *p_text;
+#endif
+ AnyResourceHolder *p_any_rh;
+ } held;
+ FreeableResourceKind frk;
+} ResourceHeld;
+
+/* The held-resource stack. This is for single-threaded use only. */
+static ResourceHeld *pResHold = 0;
+static ResourceCount numResHold = 0;
+static ResourceCount numResAlloc = 0;
+
+/* A small set of custom freers. It is linearly searched, used for
+** layered heap-allocated (and other-allocated) data structures, so
+** tends to have use limited to where slow things are happening.
+*/
+static ResourceCount numCustom = 0; /* number of the set */
+static ResourceCount numCustomAlloc = 0; /* allocated space */
+typedef void (*FreerFunction)(void *);
+static FreerFunction *aCustomFreers = 0; /* content of set */
+
+const char *resmanage_oom_message = "out of memory, aborting";
+
+/* Info recorded in support of quit_moan(...) and stack-ripping */
- fprintf(stderr, "Auto-freed %d resources.\n", holder_free(exit_mark));
++static RipStackDest *pRipStack = 0;
+
+/* Current position of the held-resource stack */
+ResourceMark holder_mark(){
+ return numResHold;
+}
+
+/* Strip resource stack then strip call stack (or exit.) */
+void quit_moan(const char *zMoan, int errCode){
++ RipStackDest *pRSD = pRipStack;
+ int nFreed;
+ if( zMoan ){
+ fprintf(stderr, "Error: Terminating due to %s.\n", zMoan);
+ }
- if( p_exit_ripper!=0 ){
- longjmp(*p_exit_ripper, errCode);
++ fprintf(stderr, "Auto-freed %d resources.\n",
++ holder_free( (pRSD)? pRSD->resDest : 0 ));
++ pRipStack = (pRSD)? pRSD->pPrev : 0;
+#ifndef SHELL_OMIT_LONGJMP
- #ifndef SHELL_OMIT_LONGJMP
++ if( pRSD!=0 ){
++ longjmp(pRSD->exeDest, errCode);
+ } else
+#endif
+ exit(errCode);
+}
+
+/* Free a single resource item. (ignorant of stack) */
+static int free_rk( ResourceHeld *pRH ){
+ int rv = 0;
+ if( pRH->held.p_any == 0 ) return rv;
+ if( pRH->frk & FRK_Indirect ){
+ pRH->held.p_any = *(void**)pRH->held.p_any;
+ if( pRH->held.p_any == 0 ) return rv;
+ pRH->frk &= ~FRK_Indirect;
+ }
+ ++rv;
+ switch( pRH->frk ){
+ case FRK_Malloc:
+ free(pRH->held.p_malloced);
+ break;
+ case FRK_DbConn:
+ sqlite3_close_v2(pRH->held.p_conn);
+ break;
+ case FRK_DbStmt:
+ sqlite3_clear_bindings(pRH->held.p_stmt);
+ sqlite3_finalize(pRH->held.p_stmt);
+ break;
+ case FRK_DbMem:
+ sqlite3_free(pRH->held.p_s3mem);
+ break;
+ case FRK_File:
+ fclose(pRH->held.p_stream);
+ break;
+#if (!defined(_WIN32) && !defined(WIN32)) || !SQLITE_OS_WINRT
+ case FRK_Pipe:
+ pclose(pRH->held.p_stream);
+ break;
+#endif
+#ifdef SHELL_MANAGE_TEXT
+ case FRK_Text:
+ freeText(pRH->held.p_text);
+ break;
+#endif
+ case FRK_AnyRef:
+ (pRH->held.p_any_rh->its_freer)(pRH->held.p_any_rh->pAny);
+ break;
+ default:
+ {
+ int ck = pRH->frk - FRK_CustomBase;
+ assert(ck>=0);
+ if( ck < numCustom ){
+ aCustomFreers[ck]( pRH->held.p_any );
+ }else --rv;
+ }
+ }
+ pRH->held.p_any = 0;
+ return rv;
+}
+
+/* Take back a held resource pointer, leaving held as NULL. (no-op) */
+void* take_held(ResourceMark mark, ResourceCount offset){
+ return swap_held(mark,offset,0);
+}
+
+/* Swap a held resource pointer for a new one. */
+void* swap_held(ResourceMark mark, ResourceCount offset, void *pNew){
+ ResourceCount rix = mark + offset;
+ assert(rix < numResHold && numResHold > 0);
+ if( rix < numResHold && numResHold > 0 ){
+ void *rv = pResHold[rix].held.p_any;
+ pResHold[rix].held.p_any = pNew;
+ return rv;
+ }else return 0;
+}
+
+/* Reserve room for at least count additional holders, with no chance
+** of an OOM abrupt exit. This is used internally only by this package.
+** The return is either 1 for success, or 0 indicating an OOM failure.
+ */
+static int more_holders_try(ResourceCount count){
+ ResourceHeld *prh;
+ ResourceCount newAlloc = numResHold + count;
+ if( newAlloc < numResHold ) return 0; /* Overflow, sorry. */
+ if( newAlloc <= numResAlloc ) return 1;
+ prh = (ResourceHeld*)realloc(pResHold, newAlloc*sizeof(ResourceHeld));
+ if( prh == 0 ) return 0;
+ pResHold = prh;
+ numResAlloc = newAlloc;
+ return 1;
+}
+
+/* Lose one or more holders. */
+void* pop_holder(void){
+ assert(numResHold>0);
+ if( numResHold>0 ) return pResHold[--numResHold].held.p_any;
+ return 0;
+}
+void pop_holders(ResourceCount num){
+ assert(numResHold>=num);
+ if( numResHold>=num ) numResHold -= num;
+ else numResHold = 0;
+}
+
+/* Shared resource-stack pushing code */
+static void res_hold(void *pv, FreeableResourceKind frk){
+ ResourceHeld rh = { pv, frk };
+ if( numResHold == numResAlloc ){
+ ResourceCount nrn = (ResourceCount)((numResAlloc>>2) + 5);
+ if( !more_holders_try(nrn) ){
+ free_rk(&rh);
+ quit_moan(resmanage_oom_message,1);
+ }
+ }
+ pResHold[numResHold++] = rh;
+}
+
+/* Hold a NULL (Assure swap_held() cannot fail.) */
+void more_holders(ResourceCount more){
+ if( !more_holders_try(more) ) quit_moan(resmanage_oom_message,1);
+}
+
+/* Hold anything in the malloc() heap */
+void* mmem_holder(void *pm){
+ res_hold(pm, FRK_Malloc);
+ return pm;
+}
+/* Hold a C string in the malloc() heap */
+char* mstr_holder(char *z){
+ res_hold(z, FRK_Malloc);
+ return z;
+}
+/* Hold a C string in the SQLite heap */
+char* sstr_holder(char *z){
+ res_hold(z, FRK_DbMem);
+ return z;
+}
+/* Hold a C string in the SQLite heap, reference to */
+void sstr_ptr_holder(char **pz){
+ assert(pz!=0);
+ res_hold(pz, FRK_DbMem|FRK_Indirect);
+}
+/* Hold an open C runtime FILE */
+void file_holder(FILE *pf){
+ res_hold(pf, FRK_File);
+}
+#if (!defined(_WIN32) && !defined(WIN32)) || !SQLITE_OS_WINRT
+/* Hold an open C runtime pipe */
+void pipe_holder(FILE *pp){
+ res_hold(pp, FRK_Pipe);
+}
+#endif
+#ifdef SHELL_MANAGE_TEXT
+/* a ShellText object, reference to (storage for which not managed) */
+static void text_ref_holder(ShellText *pt){
+ res_hold(pt, FRK_Text);
+}
+#endif
+/* Hold anything together with arbitrary freeing function */
+void* any_holder(void *pm, void (*its_freer)(void*)){
+ int i = 0;
+ while( i < numCustom ){
+ if( its_freer == aCustomFreers[i] ) break;
+ ++i;
+ }
+ if( i == numCustom ){
+ size_t ncf = numCustom + 2;
+ FreerFunction *pcf;
+ pcf = (FreerFunction *)realloc(aCustomFreers, ncf*sizeof(FreerFunction));
+ if( pcf!=0 ){
+ assert(ncf < (1<<16));
+ numCustomAlloc = (ResourceCount)ncf;
+ aCustomFreers = pcf;
+ aCustomFreers[numCustom++] = its_freer;
+ }else{
+ quit_moan(resmanage_oom_message,1);
+ }
+ }
+ res_hold(pm, i + FRK_CustomBase);
+ return pm;
+}
+/* Hold some SQLite-allocated memory */
+void* smem_holder(void *pm){
+ res_hold(pm, FRK_DbMem);
+ return pm;
+}
+/* Hold a SQLite database "connection" */
+void conn_holder(sqlite3 *pdb){
+ res_hold(pdb, FRK_DbConn);
+}
+/* Hold a SQLite prepared statement */
+void stmt_holder(sqlite3_stmt *pstmt){
+ res_hold(pstmt, FRK_DbStmt);
+}
+/* Hold a SQLite prepared statement, reference to */
+void stmt_ptr_holder(sqlite3_stmt **ppstmt){
+ assert(ppstmt!=0);
+ res_hold(ppstmt, FRK_DbStmt|FRK_Indirect);
+}
+/* Hold a reference to an AnyResourceHolder (in stack frame) */
+void any_ref_holder(AnyResourceHolder *parh){
+ assert(parh!=0 && parh->its_freer!=0);
+ res_hold(parh, FRK_AnyRef);
+}
+
+/* Free all held resources in excess of given resource stack mark,
+** then return how many needed freeing. */
+int holder_free(ResourceMark mark){
+ int rv = 0;
+ while( numResHold > mark ){
+ rv += free_rk(&pResHold[--numResHold]);
+ }
+ if( mark==0 ){
+ if( numResAlloc>0 ){
+ free(pResHold);
+ pResHold = 0;
+ numResAlloc = 0;
+ }
+ if( numCustomAlloc>0 ){
+ free(aCustomFreers);
+ aCustomFreers = 0;
+ numCustom = 0;
+ numCustomAlloc = 0;
+ }
+ }
+ return rv;
+}
+
- void register_exit_ripper(jmp_buf *pjb, ResourceMark rip_mark){
- exit_mark = rip_mark;
- p_exit_ripper = pjb;
+/* Record a resource stack and call stack rip-to position */
- /* Undo register_exit_ripper effect, back to default state. */
- void forget_exit_ripper(jmp_buf *pjb){
- exit_mark = 0;
- assert(p_exit_ripper == pjb);
- p_exit_ripper = 0;
++void register_exit_ripper(RipStackDest *pRSD){
++ assert(pRSD!=0);
++ pRSD->pPrev = pRipStack;
++ pRSD->resDest = holder_mark();
++ pRipStack = pRSD;
+}
- #endif
++/* Undo register_exit_ripper effect, back to previous state. */
++void forget_exit_ripper(RipStackDest *pRSD){
++ if( pRSD==0 ) pRipStack = 0;
++ else{
++ pRipStack = pRSD->pPrev;
++ pRSD->pPrev = 0;
++ }
+}
if( seenInterrupt ) fprintf(stderr, "Program interrupted.\n");
}
- /* A vector of command strings collected from . */
++/* A vector of command strings collected from the command line. */
+typedef struct CmdArgs {
- /* Array is malloc'ed, but not its elements except by wmain()'s futzing. */
+ char **azCmd; /* the strings */
+ int nCmd; /* how many collected */
++ u8 bArgsHeld; /* whether "the strings" are owned by this object */
+} CmdArgs;
+/* Data collected during args scanning. */
+typedef struct ArgsData {
+ int readStdin; /* whether stdin will be read */
+ int nOptsEnd; /* where -- seen, else argc */
+ const char *zInitFile; /* specified init file */
+ const char *zVfs; /* -vfs command-line option */
+ short bQuiet; /* -quiet option */
+} ArgsData;
++
++static void freeCmdArgs(CmdArgs *pca){
++ int i;
++ if( !pca ) return;
++ if( pca->bArgsHeld ){
++ for( i=0; i<pca->nCmd; ++i ){
++ free(pca->azCmd[i]);
++ }
++ }
++ free(pca->azCmd);
++ pca->azCmd = 0;
++ pca->nCmd = 0;
++}
+/*
+** Perform CLI invocation argument processing.
+** This code is collected here for convenience, to declutter main()
+** and to make this processing a little simpler to understand.
+*/
+static int scanInvokeArgs(int argc, char **argv, int pass, ShellInState *psi,
+ CmdArgs *pca, ArgsData *pad){
+ int rc = 0;
+ DotCmdRC drc;
+ int i;
+ if( pass==1 ){
+ for(i=1; i<argc && rc<2; i++){
+ char *z = argv[i];
+ if( z[0]!='-' || i>pad->nOptsEnd ){
+ if( psi->aAuxDb->zDbFilename==0 ){
+ psi->aAuxDb->zDbFilename = z;
+ }else{
+ void *vaz = realloc(pca->azCmd, sizeof(pca->azCmd[0])*(pca->nCmd+1));
+ shell_check_oom(vaz);
+ pca->azCmd = (char**)vaz;
+ pca->azCmd[pca->nCmd++] = z;
+ /* Excesss arguments are interpreted as SQL (or dot-commands)
+ ** and mean that nothing is to be read from stdin. */
+ pad->readStdin = 0;
+ }
+ continue;
+ }
+ if( z[1]=='-' ) z++;
+ if( cli_strcmp(z, "-")==0 ){
+ pad->nOptsEnd = i;
+ continue;
+ }else 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);
+ /* Will pickup value on next pass. */
+ }else if( cli_strcmp(z,"-init")==0 ){
+ pad->zInitFile = cmdline_option_value(argc, argv, ++i);
+ }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( cli_strcmp(z,"-heap")==0 ){
+#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
+ const char *zSize;
+ sqlite3_int64 szHeap;
+
+ zSize = cmdline_option_value(argc, argv, ++i);
+ szHeap = integerValue(zSize);
+ if( szHeap>0x7fff0000 ) szHeap = 0x7fff0000;
+ verify_uninitialized();
+ sqlite3_config(SQLITE_CONFIG_HEAP,malloc((int)szHeap),(int)szHeap, 64);
+#else
+ (void)cmdline_option_value(argc, argv, ++i);
+#endif
+ }else if( cli_strcmp(z,"-pagecache")==0 ){
+ sqlite3_int64 n, sz;
+ void *pvCache = 0;
+ sz = integerValue(cmdline_option_value(argc,argv,++i));
+ if( sz>70000 ) sz = 70000;
+ if( sz<0 ) sz = 0;
+ n = integerValue(cmdline_option_value(argc,argv,++i));
+ if( sz>0 && n>0 && 0xffffffffffffLL/sz<n ){
+ n = 0xffffffffffffLL/sz;
+ }
+ verify_uninitialized();
+ if( n>0 && sz>0 ) pvCache = malloc(n*sz);
+ shell_check_oom(pvCache);
+ sqlite3_config(SQLITE_CONFIG_PAGECACHE, pvCache, sz, n);
+ psi->shellFlgs |= SHFLG_Pagecache;
+ }else if( cli_strcmp(z,"-lookaside")==0 ){
+ int n, sz;
+ sz = (int)integerValue(cmdline_option_value(argc,argv,++i));
+ if( sz<0 ) sz = 0;
+ n = (int)integerValue(cmdline_option_value(argc,argv,++i));
+ if( n<0 ) n = 0;
+ verify_uninitialized();
+ sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, n);
+ if( sz*n==0 ) psi->shellFlgs &= ~SHFLG_Lookaside;
+ }else if( cli_strcmp(z,"-threadsafe")==0 ){
+ int n;
+ n = (int)integerValue(cmdline_option_value(argc,argv,++i));
+ verify_uninitialized();
+ switch( n ){
+ case 0: sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); break;
+ case 2: sqlite3_config(SQLITE_CONFIG_MULTITHREAD); break;
+ default: sqlite3_config(SQLITE_CONFIG_SERIALIZED); break;
+ }
+#ifdef SQLITE_ENABLE_VFSTRACE
+ }else if( cli_strcmp(z,"-vfstrace")==0 ){
+ extern int vfstrace_register(
+ const char *zTraceName,
+ const char *zOldVfsName,
+ int (*xOut)(const char*,void*),
+ void *pOutArg,
+ int makeDefault
+ );
+ vfstrace_register("trace",0,(int(*)(const char*,void*))fputs,STD_ERR,1);
+#endif
+#ifdef SQLITE_ENABLE_MULTIPLEX
+ }else if( cli_strcmp(z,"-multiplex")==0 ){
+ extern int sqlite3_multiple_initialize(const char*,int);
+ sqlite3_multiplex_initialize(0, 1);
+#endif
+ }else if( cli_strcmp(z,"-mmap")==0 ){
+ sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i));
+ verify_uninitialized();
+ sqlite3_config(SQLITE_CONFIG_MMAP_SIZE, sz, sz);
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ }else if( cli_strcmp(z,"-sorterref")==0 ){
+ sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i));
+ verify_uninitialized();
+ sqlite3_config(SQLITE_CONFIG_SORTERREF_SIZE, (int)sz);
+#endif
+ }else if( cli_strcmp(z,"-vfs")==0 ){
+ pad->zVfs = cmdline_option_value(argc, argv, ++i);
+#ifdef SQLITE_HAVE_ZLIB
+ }else if( cli_strcmp(z,"-zip")==0 ){
+ psi->openMode = SHELL_OPEN_ZIPFILE;
+#endif
+ }else if( cli_strcmp(z,"-append")==0 ){
+ psi->openMode = SHELL_OPEN_APPENDVFS;
+#ifndef SQLITE_OMIT_DESERIALIZE
+ }else if( cli_strcmp(z,"-deserialize")==0 ){
+ psi->openMode = SHELL_OPEN_DESERIALIZE;
+ }else if( cli_strcmp(z,"-maxsize")==0 && i+1<argc ){
+ psi->szMax = integerValue(argv[++i]);
+#endif
+ }else if( cli_strcmp(z,"-readonly")==0 ){
+ psi->openMode = SHELL_OPEN_READONLY;
+ }else if( cli_strcmp(z,"-nofollow")==0 ){
+ psi->openFlags = SQLITE_OPEN_NOFOLLOW;
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
+ }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( cli_strcmp(z, "-memtrace")==0 ){
+ sqlite3MemTraceActivate(STD_ERR);
+ }else if( cli_strcmp(z,"-bail")==0 ){
+ bail_on_error = 1;
+#if SHELL_EXTENSIONS
+ }else if( cli_strcmp(z,"-shxopts")==0 ){
+ psi->bExtendedDotCmds = (u8)integerValue(argv[++i]);
+#endif
+ }else if( cli_strcmp(z,"-nonce")==0 ){
+ free(psi->zNonce);
+ psi->zNonce = strdup(argv[++i]);
+ shell_check_oom(psi->zNonce);
+ }else if( cli_strcmp(z,"-quiet")==0 ){
+ pad->bQuiet = (int)integerValue(cmdline_option_value(argc,argv,++i));
+ }else if( cli_strcmp(z,"-unsafe-testing")==0 ){
+ psi->shellFlgs |= SHFLG_TestingMode;
+ }else if( cli_strcmp(z,"-safe")==0 ){
+ /* catch this on the second pass (Unsafe is fine on invocation.) */
+ }
+ }
+ }else if( pass==2 ){
+ for(i=1; i<argc && rc<2; i++){
+ char *z = argv[i];
+ char *zModeSet = 0;
+ if( z[0]!='-' || i>=pad->nOptsEnd ) continue;
+ if( z[1]=='-' ){ z++; }
+ if( cli_strcmp(z,"-init")==0 ){
+ i++;
+ }else if( cli_strcmp(z,"-html")==0 ){
+ zModeSet = z;
+ }else if( cli_strcmp(z,"-list")==0 ){
+ zModeSet = z;
+ }else if( cli_strcmp(z,"-quote")==0 ){
+ zModeSet = z;
+ }else if( cli_strcmp(z,"-line")==0 ){
+ zModeSet = z;
+ }else if( cli_strcmp(z,"-column")==0 ){
+ zModeSet = z;
+ }else if( cli_strcmp(z,"-json")==0 ){
+ zModeSet = z;
+ }else if( cli_strcmp(z,"-markdown")==0 ){
+ zModeSet = z;
+ }else if( cli_strcmp(z,"-table")==0 ){
+ zModeSet = z;
+ }else if( cli_strcmp(z,"-box")==0 ){
+ zModeSet = z;
+ }else if( cli_strcmp(z,"-csv")==0 ){
+ zModeSet = z;
+ }else if( cli_strcmp(z,"-ascii")==0 ){
+ zModeSet = z;
+ }else if( cli_strcmp(z,"-tabs")==0 ){
+ zModeSet = z;
+#ifdef SQLITE_HAVE_ZLIB
+ }else if( cli_strcmp(z,"-zip")==0 ){
+ psi->openMode = SHELL_OPEN_ZIPFILE;
+#endif
+ }else if( cli_strcmp(z,"-append")==0 ){
+ psi->openMode = SHELL_OPEN_APPENDVFS;
+#ifndef SQLITE_OMIT_DESERIALIZE
+ }else if( cli_strcmp(z,"-deserialize")==0 ){
+ psi->openMode = SHELL_OPEN_DESERIALIZE;
+ }else if( cli_strcmp(z,"-maxsize")==0 && i+1<argc ){
+ psi->szMax = integerValue(argv[++i]);
+#endif
+ }else if( cli_strcmp(z,"-readonly")==0 ){
+ psi->openMode = SHELL_OPEN_READONLY;
+ }else if( cli_strcmp(z,"-nofollow")==0 ){
+ psi->openFlags |= SQLITE_OPEN_NOFOLLOW;
+ }else if( cli_strcmp(z,"-separator")==0 ){
+ sqlite3_snprintf(sizeof(psi->colSeparator), psi->colSeparator,
+ "%s",cmdline_option_value(argc,argv,++i));
+ }else if( cli_strcmp(z,"-newline")==0 ){
+ sqlite3_snprintf(sizeof(psi->rowSeparator), psi->rowSeparator,
+ "%s",cmdline_option_value(argc,argv,++i));
+ }else if( cli_strcmp(z,"-nullvalue")==0 ){
+ sqlite3_snprintf(sizeof(psi->nullValue), psi->nullValue,
+ "%s",cmdline_option_value(argc,argv,++i));
+ }else if( cli_strcmp(z,"-header")==0 ){
+ psi->showHeader = 1;
+ ShellSetFlagI(psi, SHFLG_HeaderSet);
+ }else if( cli_strcmp(z,"-noheader")==0 ){
+ psi->showHeader = 0;
+ ShellSetFlagI(psi, SHFLG_HeaderSet);
+ }else if( cli_strcmp(z,"-echo")==0 ){
+ ShellSetFlagI(psi, SHFLG_Echo);
+ }else if( cli_strcmp(z,"-eqp")==0 ){
+ psi->autoEQP = AUTOEQP_on;
+ }else if( cli_strcmp(z,"-eqpfull")==0 ){
+ psi->autoEQP = AUTOEQP_full;
+ }else if( cli_strcmp(z,"-stats")==0 ){
+ psi->statsOn = 1;
+ }else if( cli_strcmp(z,"-scanstats")==0 ){
+ psi->scanstatsOn = 1;
+ }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.
+ */
+ ShellSetFlagI(psi, SHFLG_Backslash);
+ }else if( cli_strcmp(z,"-bail")==0 ){
+ /* No-op. The bail_on_error flag should already be set. */
+#if SHELL_EXTENSIONS
+ }else if( cli_strcmp(z,"-shxopts")==0 ){
+ i++; /* Handled on first pass. */
+#endif
+ }else if( cli_strcmp(z,"-version")==0 ){
+ fprintf(STD_OUT, "%s %s\n", sqlite3_libversion(), sqlite3_sourceid());
+ rc = 2;
+ }else if( cli_strcmp(z,"-interactive")==0 ){
+ stdin_is_interactive = 1;
+ }else if( cli_strcmp(z,"-batch")==0 ){
+ stdin_is_interactive = 0;
+ }else if( cli_strcmp(z,"-utf8")==0 ){
+#if SHELL_WIN_UTF8_OPT
+ console_utf8 = 1;
+#endif /* SHELL_WIN_UTF8_OPT */
+ }else if( cli_strcmp(z,"-heap")==0 ){
+ i++;
+ }else if( cli_strcmp(z,"-pagecache")==0 ){
+ i+=2;
+ }else if( cli_strcmp(z,"-lookaside")==0 ){
+ i+=2;
+ }else if( cli_strcmp(z,"-threadsafe")==0 ){
+ i+=2;
+ }else if( cli_strcmp(z,"-nonce")==0 ){
+ i+=2;
+ }else if( cli_strcmp(z,"-mmap")==0 ){
+ i++;
+ }else if( cli_strcmp(z,"-memtrace")==0 ){
+ i++;
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ }else if( cli_strcmp(z,"-sorterref")==0 ){
+ i++;
+#endif
+ }else if( cli_strcmp(z,"-vfs")==0 ){
+ i++;
+#ifdef SQLITE_ENABLE_VFSTRACE
+ }else if( cli_strcmp(z,"-vfstrace")==0 ){
+ i++;
+#endif
+#ifdef SQLITE_ENABLE_MULTIPLEX
+ }else if( cli_strcmp(z,"-multiplex")==0 ){
+ i++;
+#endif
+ }else if( cli_strcmp(z,"-help")==0 ){
+ usage(1);
+ }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
+ ** we retain the goofy behavior for historical compatibility. */
+ if( i==argc-1 ) break; /* Pretend specified command is empty. */
+ set_invocation_cmd(cmdline_option_value(argc,argv,++i));
+ drc = process_input(psi);
+ rc = (drc>2)? 2 : drc;
+ if( rc>0 ){
+ break;
+ }
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
+ }else if( cli_strncmp(z, "-A", 2)==0 ){
+ if( pca->nCmd>0 ){
+ utf8_printf(STD_ERR, "Error: cannot mix regular SQL or dot-commands"
+ " with \"%s\"\n", z);
+ rc = 1;
+ break;
+ }
+ open_db(XSS(psi), OPEN_DB_ZIPFILE);
+ if( z[2] ){
+ argv[i] = &z[2];
+ drc = arDotCommand(XSS(psi), 1, argv+(i-1), argc-(i-1));
+ }else{
+ drc = arDotCommand(XSS(psi), 1, argv+i, argc-i);
+ }
+ rc = (drc>2)? 2 : drc;
+ pad->readStdin = 0;
+ break;
+#endif
+ }else if( cli_strcmp(z,"-safe")==0 ){
+ psi->bSafeMode = psi->bSafeModeFuture = 1;
+ }else if( cli_strcmp(z,"-unsafe-testing")==0 ){
+ /* Acted upon in first pass. */
+ }else if( cli_strcmp(z,"-quiet")==0 ){
+ ++i;
+ }else{
+ utf8_printf(STD_ERR,"%s: Error: unknown option: %s\n", Argv0, z);
+ raw_printf(STD_ERR,"Use -help for a list of options.\n");
+ rc = 2;
+ }
+ if( zModeSet!=0 ){
+ char *azModeCmd[] = { ".mode", zModeSet+1 };
+ modeCommand(azModeCmd, 2, XSS(psi), 0);
+ psi->cMode = psi->mode;
+ }
+ }
+ }
+ return rc;
+}
+
#ifndef SQLITE_SHELL_IS_UTF8
# if (defined(_WIN32) || defined(WIN32)) \
&& (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__)))
#ifdef SQLITE_DEBUG
sqlite3_int64 mem_main_enter = 0;
#endif
- char *zErrMsg = 0;
#ifdef SQLITE_SHELL_FIDDLE
-# define data shellState
+# define datai shellStateI
+# define datax shellStateX
#else
- ShellState data;
+ ShellInState datai;
+ ShellExState datax;
#endif
- RIP_STATE(exit_jb);
+#if SHELL_DATAIO_EXT
+ BuiltInFFExporter ffExporter = BI_FF_EXPORTER_INIT( &datai );
+ BuiltInCMExporter cmExporter = BI_CM_EXPORTER_INIT( &datai );
+#endif
++ RipStackDest mainRipDest = RIP_STACK_DEST_INIT;
const char *zInitFile = 0;
- int i;
+ int bQuiet = 0; /* for testing, to suppress banner and history actions */
+ int i, aec;
int rc = 0;
+ DotCmdRC drc = DCR_Ok;
int warnInmemoryDb = 0;
- /* azCmd, nCmd */
- CmdArgs cmdArgs = {0,0};
- int readStdin = 1;
- int nCmd = 0;
- int nOptsEnd = argc;
- char **azCmd = 0;
- const char *zVfs = 0; /* Value of -vfs command-line option */
++ /* azCmd, nCmd, bArgsHeld */
++ CmdArgs cmdArgs = {0,0,0};
+ /* readStdin, nOptsEnd, zInitFile, zVfs, bQuiet */
+ ArgsData argsData = { 1, argc, 0,0,0 };
- int nOptsEnd = argc;
#if !SQLITE_SHELL_IS_UTF8
-- char **argvToFree = 0;
-- int argcToFree = 0;
++ CmdArgs argsUtf8 = {0,0,1};
++ AnyResourceHolder caRH = {&argsUtf8, freeCmdArgs};
#endif
- main_resource_mark = holder_mark();
- setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */
+ setvbuf(STD_ERR, 0, _IONBF, 0); /* Make sure stderr is unbuffered */
#ifdef SQLITE_SHELL_FIDDLE
stdin_is_interactive = 0;
stdout_is_console = 1;
exit(1);
}
#endif
- main_init(&data);
+ main_init(&datai,&datax);
+#if SHELL_DATAIO_EXT
+ datai.pFreeformExporter = (ExportHandler*)&ffExporter;
+ datai.pColumnarExporter = (ExportHandler*)&cmExporter;
+ datai.pActiveExporter = (ExportHandler*)&ffExporter;
+#endif
- /* On Windows, we must translate command-line arguments into UTF-8.
- ** The SQLite memory allocator subsystem has to be enabled in order to
- ** do this. But we want to run an sqlite3_shutdown() afterwards so that
- ** subsequent sqlite3_config() calls will work. So copy all results into
- ** memory that does not come from the SQLite memory allocator.
+ /* From here on, within the true clause of this next test, various
+ ** heap allocations are made which may fail, resulting in an abrupt
+ ** shell exit. Such an exit happens in 1 of 2 ways: A held resource
+ ** stack and the call stack are ripped back to this point; or just
+ ** the held resource stack is ripped back and a process exit occurs.
*/
- register_exit_ripper(&exit_jb, main_resource_mark);
- if( 0==RIP_TO_HERE(exit_jb) ){
++ register_exit_ripper(&mainRipDest);
++ if( 0==RIP_TO_HERE(mainRipDest) ){
++ main_resource_mark = mainRipDest.resDest;
+
+ /* On Windows, we must translate command-line arguments into UTF-8.
+ ** The SQLite memory allocator subsystem has to be enabled in order to
+ ** do this. But we want to run an sqlite3_shutdown() afterwards so that
+ ** subsequent sqlite3_config() calls will work. So copy all results into
+ ** memory that does not come from the SQLite memory allocator.
+ */
#if !SQLITE_SHELL_IS_UTF8
- sqlite3_initialize();
- argvToFree = malloc(sizeof(argv[0])*argc*2);
- shell_check_oom(argvToFree);
- argcToFree = argc;
- argv = argvToFree + argc;
- for(i=0; i<argc; i++){
- char *z = sqlite3_win32_unicode_to_utf8(wargv[i]);
- i64 n;
- shell_check_oom(z);
- n = strlen(z);
- argv[i] = malloc( n+1 );
- shell_check_oom(argv[i]);
- memcpy(argv[i], z, n+1);
- argvToFree[i] = argv[i];
- sqlite3_free(z);
- }
- sqlite3_shutdown();
+ sqlite3_initialize();
- argvToFree = malloc(sizeof(argv[0])*argc*2);
- shell_check_oom(argvToFree);
- argcToFree = argc;
- argv = argvToFree + argc;
++ argsUtf8.azCmd = malloc(sizeof(argv[0])*argc);
++ shell_check_oom(argsUtf8.azCmd);
++ argsUtf8.nCmd = 0;
++ any_ref_holder(&caRH);
++ ++main_resource_mark;
+ for(i=0; i<argc; i++){
+ char *z = sqlite3_win32_unicode_to_utf8(wargv[i]);
+ i64 n;
+ shell_check_oom(z);
++ ++main_resource_mark;
++ sstr_holder(z);
+ n = strlen(z);
- argv[i] = malloc( n+1 );
- shell_check_oom(argv[i]);
- memcpy(argv[i], z, n+1);
- argvToFree[i] = argv[i];
- sqlite3_free(z);
++ argsUtf8.azCmd[i] = malloc( n+1 );
++ if( argsUtf8.azCmd[i] ) ++argsUtf8.nCmd;
++ else shell_out_of_memory();
++ sqlite3_free(pop_holder());
++ --main_resource_mark;
++ memcpy(argsUtf8.azCmd[i], z, n+1);
+ }
+ sqlite3_shutdown();
++ argv = argsUtf8.azCmd;
#endif
- assert( argc>=1 && argv && argv[0] );
- Argv0 = argv[0];
+ assert( argc>=1 && argv && argv[0] );
+ Argv0 = argv[0];
+#if SHELL_DYNAMIC_EXTENSION
+ initStartupDir();
+ if( isExtendedBasename(Argv0) ){
+ datai.bExtendedDotCmds = SHELL_ALL_EXTENSIONS;
+ }
+#endif
#ifdef SQLITE_SHELL_DBNAME_PROC
- {
- /* If the SQLITE_SHELL_DBNAME_PROC macro is defined, then it is the name
- ** of a C-function that will provide the name of the database file. Use
- ** this compile-time option to embed this shell program in larger
- ** applications. */
- extern void SQLITE_SHELL_DBNAME_PROC(const char**);
- SQLITE_SHELL_DBNAME_PROC(&data.pAuxDb->zDbFilename);
- warnInmemoryDb = 0;
- }
+ {
+ /* If the SQLITE_SHELL_DBNAME_PROC macro is defined, then it is the name
+ ** of a C-function that will provide the name of the database file. Use
+ ** this compile-time option to embed this shell program in larger
+ ** applications. */
+ extern void SQLITE_SHELL_DBNAME_PROC(const char**);
+ SQLITE_SHELL_DBNAME_PROC(&datai.pAuxDb->zDbFilename);
+ warnInmemoryDb = 0;
+ }
#endif
- /* Do an initial pass through the command-line argument to locate
- ** the name of the database file, the name of the initialization file,
- ** the size of the alternative malloc heap,
- ** and the first command to execute.
- */
+ /* Do an initial pass through the command-line argument to locate
+ ** the name of the database file, the name of the initialization file,
+ ** the size of the alternative malloc heap,
+ ** and the first command to execute.
+ */
#ifndef SQLITE_SHELL_FIDDLE
- verify_uninitialized();
-#endif
- for(i=1; i<argc; i++){
- char *z;
- z = argv[i];
- if( z[0]!='-' || i>nOptsEnd ){
- if( data.aAuxDb->zDbFilename==0 ){
- data.aAuxDb->zDbFilename = z;
- }else{
- /* Excesss arguments are interpreted as SQL (or dot-commands) and
- ** mean that nothing is read from stdin */
- readStdin = 0;
- nCmd++;
- azCmd = realloc(azCmd, sizeof(azCmd[0])*nCmd);
- shell_check_oom(azCmd);
- azCmd[nCmd-1] = z;
- }
- continue;
- }
- if( z[1]=='-' ) z++;
- if( cli_strcmp(z, "-")==0 ){
- nOptsEnd = i;
- continue;
- }else 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( cli_strcmp(z,"-init")==0 ){
- zInitFile = cmdline_option_value(argc, argv, ++i);
- }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( cli_strcmp(z,"-heap")==0 ){
-#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
- const char *zSize;
- sqlite3_int64 szHeap;
-
- zSize = cmdline_option_value(argc, argv, ++i);
- szHeap = integerValue(zSize);
- if( szHeap>0x7fff0000 ) szHeap = 0x7fff0000;
- verify_uninitialized();
- sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64);
-#else
- (void)cmdline_option_value(argc, argv, ++i);
-#endif
- }else if( cli_strcmp(z,"-pagecache")==0 ){
- sqlite3_int64 n, sz;
- sz = integerValue(cmdline_option_value(argc,argv,++i));
- if( sz>70000 ) sz = 70000;
- if( sz<0 ) sz = 0;
- n = integerValue(cmdline_option_value(argc,argv,++i));
- if( sz>0 && n>0 && 0xffffffffffffLL/sz<n ){
- n = 0xffffffffffffLL/sz;
- }
- verify_uninitialized();
- sqlite3_config(SQLITE_CONFIG_PAGECACHE,
- (n>0 && sz>0) ? malloc(n*sz) : 0, sz, n);
- data.shellFlgs |= SHFLG_Pagecache;
- }else if( cli_strcmp(z,"-lookaside")==0 ){
- int n, sz;
- sz = (int)integerValue(cmdline_option_value(argc,argv,++i));
- if( sz<0 ) sz = 0;
- n = (int)integerValue(cmdline_option_value(argc,argv,++i));
- if( n<0 ) n = 0;
- verify_uninitialized();
- sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, n);
- if( sz*n==0 ) data.shellFlgs &= ~SHFLG_Lookaside;
- }else if( cli_strcmp(z,"-threadsafe")==0 ){
- int n;
- n = (int)integerValue(cmdline_option_value(argc,argv,++i));
- verify_uninitialized();
- switch( n ){
- case 0: sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); break;
- case 2: sqlite3_config(SQLITE_CONFIG_MULTITHREAD); break;
- default: sqlite3_config(SQLITE_CONFIG_SERIALIZED); break;
- }
-#ifdef SQLITE_ENABLE_VFSTRACE
- }else if( cli_strcmp(z,"-vfstrace")==0 ){
- extern int vfstrace_register(
- const char *zTraceName,
- const char *zOldVfsName,
- int (*xOut)(const char*,void*),
- void *pOutArg,
- int makeDefault
- );
- vfstrace_register("trace",0,(int(*)(const char*,void*))fputs,stderr,1);
-#endif
-#ifdef SQLITE_ENABLE_MULTIPLEX
- }else if( cli_strcmp(z,"-multiplex")==0 ){
- extern int sqlite3_multiple_initialize(const char*,int);
- sqlite3_multiplex_initialize(0, 1);
-#endif
- }else if( cli_strcmp(z,"-mmap")==0 ){
- sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i));
- verify_uninitialized();
- sqlite3_config(SQLITE_CONFIG_MMAP_SIZE, sz, sz);
-#if defined(SQLITE_ENABLE_SORTER_REFERENCES)
- }else if( cli_strcmp(z,"-sorterref")==0 ){
- sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i));
- verify_uninitialized();
- sqlite3_config(SQLITE_CONFIG_SORTERREF_SIZE, (int)sz);
-#endif
- }else if( cli_strcmp(z,"-vfs")==0 ){
- zVfs = cmdline_option_value(argc, argv, ++i);
-#ifdef SQLITE_HAVE_ZLIB
- }else if( cli_strcmp(z,"-zip")==0 ){
- data.openMode = SHELL_OPEN_ZIPFILE;
-#endif
- }else if( cli_strcmp(z,"-append")==0 ){
- data.openMode = SHELL_OPEN_APPENDVFS;
-#ifndef SQLITE_OMIT_DESERIALIZE
- }else if( cli_strcmp(z,"-deserialize")==0 ){
- data.openMode = SHELL_OPEN_DESERIALIZE;
- }else if( cli_strcmp(z,"-maxsize")==0 && i+1<argc ){
- data.szMax = integerValue(argv[++i]);
-#endif
- }else if( cli_strcmp(z,"-readonly")==0 ){
- data.openMode = SHELL_OPEN_READONLY;
- }else if( cli_strcmp(z,"-nofollow")==0 ){
- data.openFlags = SQLITE_OPEN_NOFOLLOW;
-#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
- }else if( cli_strncmp(z, "-A",2)==0 ){
- /* All remaining command-line arguments are passed to the ".archive"
- ** command, so ignore them */
- break;
+ verify_uninitialized();
#endif
- }else if( cli_strcmp(z, "-memtrace")==0 ){
- sqlite3MemTraceActivate(stderr);
- }else if( cli_strcmp(z,"-bail")==0 ){
- bail_on_error = 1;
- }else if( cli_strcmp(z,"-nonce")==0 ){
- free(data.zNonce);
- data.zNonce = strdup(argv[++i]);
- }else if( cli_strcmp(z,"-unsafe-testing")==0 ){
- ShellSetFlag(&data,SHFLG_TestingMode);
- }else if( cli_strcmp(z,"-safe")==0 ){
- /* no-op - catch this on the second pass */
- }
- }
+ i = scanInvokeArgs(argc, argv, 1, &datai, &cmdArgs, &argsData);
#ifndef SQLITE_SHELL_FIDDLE
- verify_uninitialized();
+ verify_uninitialized();
#endif
-
#ifdef SQLITE_SHELL_INIT_PROC
- {
- /* If the SQLITE_SHELL_INIT_PROC macro is defined, then it is the name
- ** of a C-function that will perform initialization actions on SQLite that
- ** occur just before or after sqlite3_initialize(). Use this compile-time
- ** option to embed this shell program in larger applications. */
- extern void SQLITE_SHELL_INIT_PROC(void);
- SQLITE_SHELL_INIT_PROC();
- }
+ {
+ /* If the SQLITE_SHELL_INIT_PROC macro is defined, then it is the name
+ ** of a C-function that will perform initialization actions on SQLite that
+ ** occur just before or after sqlite3_initialize(). Use this compile-time
+ ** option to embed this shell program in larger applications. */
+ extern void SQLITE_SHELL_INIT_PROC(void);
+ SQLITE_SHELL_INIT_PROC();
+ }
#else
- /* All the sqlite3_config() calls have now been made. So it is safe
- ** to call sqlite3_initialize() and process any command line -vfs option. */
- sqlite3_initialize();
+ /* All the sqlite3_config() calls have now been made. So it is safe
+ ** to call sqlite3_initialize() and process any command line -vfs option. */
+ sqlite3_initialize();
#endif
- if( zVfs ){
- sqlite3_vfs *pVfs = sqlite3_vfs_find(zVfs);
- if( pVfs ){
- sqlite3_vfs_register(pVfs, 1);
- }else{
- utf8_printf(stderr, "no such VFS: \"%s\"\n", zVfs);
- exit(1);
+ /* Create a mutex for thread-safe query execution interruption. */
+ if( pGlobalDbLock==0 ){
+ pGlobalDbLock = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+ }
+ if( atexit_registered==0 ){
+ atexit(zapGlobalDbLock);
+ ++atexit_registered;
+ }
+ /* Register the control-C (SIGINT) handler.
+ ** Make sure we have a valid signal handler early, before anything
+ ** is done that might take long. */
+#ifdef SIGINT
+ signal(SIGINT, interrupt_handler);
+#elif (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE)
+ SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
+#endif
+
+ if( argsData.zVfs ){
+ sqlite3_vfs *pVfs = sqlite3_vfs_find(argsData.zVfs);
+ if( pVfs ){
+ sqlite3_vfs_register(pVfs, 1);
+ }else{
+ utf8_printf(STD_ERR, "no such VFS: \"%s\"\n", argsData.zVfs);
+ rc = 1;
+ goto shell_bail;
+ }
}
- }
- if( data.pAuxDb->zDbFilename==0 ){
+ if( datai.pAuxDb->zDbFilename==0 ){
#ifndef SQLITE_OMIT_MEMORYDB
- data.pAuxDb->zDbFilename = ":memory:";
- warnInmemoryDb = argc==1;
+ datai.pAuxDb->zDbFilename = ":memory:";
+ warnInmemoryDb = argc==1;
#else
- utf8_printf(stderr,"%s: Error: no database filename specified\n", Argv0);
- return 1;
+ utf8_printf(STD_ERR,"%s: Error: no database filename specified\n", Argv0);
+ rc = 1;
+ goto shell_bail;
#endif
- }
- data.out = stdout;
+ }
+ datai.out = STD_OUT;
#ifndef SQLITE_SHELL_FIDDLE
- sqlite3_appendvfs_init(0,0,0);
+ sqlite3_appendvfs_init(0,0,0);
#endif
- /* Go ahead and open the database file if it already exists. If the
- ** file does not exist, delay opening it. This prevents empty database
- ** files from being created if a user mistypes the database name argument
- ** to the sqlite command-line tool.
- */
- if( access(data.pAuxDb->zDbFilename, 0)==0 ){
- open_db(&data, 0);
- }
+ /* Go ahead and open the database file if it already exists. If the
+ ** file does not exist, delay opening it. This prevents empty database
+ ** files from being created if a user mistypes the database name argument
+ ** to the sqlite command-line tool.
+ */
+ if( access(datai.pAuxDb->zDbFilename, 0)==0 ){
+ open_db(&datax, 0);
+ }
- /* Process the initialization file if there is one. If no -init option
- ** is given on the command line, look for a file named ~/.sqliterc and
- ** try to process it.
- */
- process_sqliterc(&data,zInitFile);
+ /* Process the initialization file if there is one. If no -init option
+ ** is given on the command line, look for a file named ~/.sqliterc and
+ ** try to process it, without any quitting or bail-on-error.
+ */
+ process_sqliterc(&datai,argsData.zInitFile);
+
+ /* Make a second pass through the command-line argument and set
+ ** options. This second pass is delayed until after the initialization
+ ** file is processed so that the command-line arguments will override
+ ** settings in the initialization file.
+ */
+ rc = scanInvokeArgs(argc, argv, 2, &datai, &cmdArgs, &argsData);
+ if( rc>0 ){
+ goto shell_bail;
+ }
- /* Make a second pass through the command-line argument and set
- ** options. This second pass is delayed until after the initialization
- ** file is processed so that the command-line arguments will override
- ** settings in the initialization file.
- */
- for(i=1; i<argc; i++){
- char *z = argv[i];
- if( z[0]!='-' || i>=nOptsEnd ) continue;
- if( z[1]=='-' ){ z++; }
- if( cli_strcmp(z,"-init")==0 ){
- i++;
- }else if( cli_strcmp(z,"-html")==0 ){
- data.mode = MODE_Html;
- }else if( cli_strcmp(z,"-list")==0 ){
- data.mode = MODE_List;
- }else if( cli_strcmp(z,"-quote")==0 ){
- data.mode = MODE_Quote;
- sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, SEP_Comma);
- sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, SEP_Row);
- }else if( cli_strcmp(z,"-line")==0 ){
- data.mode = MODE_Line;
- }else if( cli_strcmp(z,"-column")==0 ){
- data.mode = MODE_Column;
- }else if( cli_strcmp(z,"-json")==0 ){
- data.mode = MODE_Json;
- }else if( cli_strcmp(z,"-markdown")==0 ){
- data.mode = MODE_Markdown;
- }else if( cli_strcmp(z,"-table")==0 ){
- data.mode = MODE_Table;
- }else if( cli_strcmp(z,"-box")==0 ){
- data.mode = MODE_Box;
- }else if( cli_strcmp(z,"-csv")==0 ){
- data.mode = MODE_Csv;
- memcpy(data.colSeparator,",",2);
-#ifdef SQLITE_HAVE_ZLIB
- }else if( cli_strcmp(z,"-zip")==0 ){
- data.openMode = SHELL_OPEN_ZIPFILE;
-#endif
- }else if( cli_strcmp(z,"-append")==0 ){
- data.openMode = SHELL_OPEN_APPENDVFS;
-#ifndef SQLITE_OMIT_DESERIALIZE
- }else if( cli_strcmp(z,"-deserialize")==0 ){
- data.openMode = SHELL_OPEN_DESERIALIZE;
- }else if( cli_strcmp(z,"-maxsize")==0 && i+1<argc ){
- data.szMax = integerValue(argv[++i]);
-#endif
- }else if( cli_strcmp(z,"-readonly")==0 ){
- data.openMode = SHELL_OPEN_READONLY;
- }else if( cli_strcmp(z,"-nofollow")==0 ){
- data.openFlags |= SQLITE_OPEN_NOFOLLOW;
- }else if( cli_strcmp(z,"-ascii")==0 ){
- data.mode = MODE_Ascii;
- sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,SEP_Unit);
- sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,SEP_Record);
- }else if( cli_strcmp(z,"-tabs")==0 ){
- data.mode = MODE_List;
- sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,SEP_Tab);
- sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,SEP_Row);
- }else if( cli_strcmp(z,"-separator")==0 ){
- sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,
- "%s",cmdline_option_value(argc,argv,++i));
- }else if( cli_strcmp(z,"-newline")==0 ){
- sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,
- "%s",cmdline_option_value(argc,argv,++i));
- }else if( cli_strcmp(z,"-nullvalue")==0 ){
- sqlite3_snprintf(sizeof(data.nullValue), data.nullValue,
- "%s",cmdline_option_value(argc,argv,++i));
- }else if( cli_strcmp(z,"-header")==0 ){
- data.showHeader = 1;
- ShellSetFlag(&data, SHFLG_HeaderSet);
- }else if( cli_strcmp(z,"-noheader")==0 ){
- data.showHeader = 0;
- ShellSetFlag(&data, SHFLG_HeaderSet);
- }else if( cli_strcmp(z,"-echo")==0 ){
- ShellSetFlag(&data, SHFLG_Echo);
- }else if( cli_strcmp(z,"-eqp")==0 ){
- data.autoEQP = AUTOEQP_on;
- }else if( cli_strcmp(z,"-eqpfull")==0 ){
- data.autoEQP = AUTOEQP_full;
- }else if( cli_strcmp(z,"-stats")==0 ){
- data.statsOn = 1;
- }else if( cli_strcmp(z,"-scanstats")==0 ){
- data.scanstatsOn = 1;
- }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(&data, SHFLG_Backslash);
- }else if( cli_strcmp(z,"-bail")==0 ){
- /* No-op. The bail_on_error flag should already be set. */
- }else if( cli_strcmp(z,"-version")==0 ){
- printf("%s %s\n", sqlite3_libversion(), sqlite3_sourceid());
- return 0;
- }else if( cli_strcmp(z,"-interactive")==0 ){
- stdin_is_interactive = 1;
- }else if( cli_strcmp(z,"-batch")==0 ){
- stdin_is_interactive = 0;
- }else if( cli_strcmp(z,"-utf8")==0 ){
#if SHELL_WIN_UTF8_OPT
- console_utf8 = 1;
-#endif /* SHELL_WIN_UTF8_OPT */
- }else if( cli_strcmp(z,"-heap")==0 ){
- i++;
- }else if( cli_strcmp(z,"-pagecache")==0 ){
- i+=2;
- }else if( cli_strcmp(z,"-lookaside")==0 ){
- i+=2;
- }else if( cli_strcmp(z,"-threadsafe")==0 ){
- i+=2;
- }else if( cli_strcmp(z,"-nonce")==0 ){
- i += 2;
- }else if( cli_strcmp(z,"-mmap")==0 ){
- i++;
- }else if( cli_strcmp(z,"-memtrace")==0 ){
- i++;
-#ifdef SQLITE_ENABLE_SORTER_REFERENCES
- }else if( cli_strcmp(z,"-sorterref")==0 ){
- i++;
-#endif
- }else if( cli_strcmp(z,"-vfs")==0 ){
- i++;
-#ifdef SQLITE_ENABLE_VFSTRACE
- }else if( cli_strcmp(z,"-vfstrace")==0 ){
- i++;
-#endif
-#ifdef SQLITE_ENABLE_MULTIPLEX
- }else if( cli_strcmp(z,"-multiplex")==0 ){
- i++;
-#endif
- }else if( cli_strcmp(z,"-help")==0 ){
- usage(1);
- }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
- ** we retain the goofy behavior for historical compatibility. */
- if( i==argc-1 ) break;
- z = cmdline_option_value(argc,argv,++i);
- if( z[0]=='.' ){
- rc = do_meta_command(z, &data);
- if( rc && bail_on_error ) return rc==2 ? 0 : rc;
- }else{
- open_db(&data, 0);
- rc = shell_exec(&data, z, &zErrMsg);
- if( zErrMsg!=0 ){
- utf8_printf(stderr,"Error: %s\n", zErrMsg);
- if( bail_on_error ) return rc!=0 ? rc : 1;
- }else if( rc!=0 ){
- utf8_printf(stderr,"Error: unable to process SQL \"%s\"\n", z);
- if( bail_on_error ) return rc;
- }
- }
-#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
- }else if( cli_strncmp(z, "-A", 2)==0 ){
- if( nCmd>0 ){
- utf8_printf(stderr, "Error: cannot mix regular SQL or dot-commands"
- " with \"%s\"\n", z);
- return 1;
- }
- open_db(&data, OPEN_DB_ZIPFILE);
- if( z[2] ){
- argv[i] = &z[2];
- arDotCommand(&data, 1, argv+(i-1), argc-(i-1));
- }else{
- arDotCommand(&data, 1, argv+i, argc-i);
- }
- readStdin = 0;
- break;
-#endif
- }else if( cli_strcmp(z,"-safe")==0 ){
- data.bSafeMode = data.bSafeModePersist = 1;
- }else if( cli_strcmp(z,"-unsafe-testing")==0 ){
- /* Acted upon in first pass. */
+ if( console_utf8 && stdin_is_interactive ){
+ console_prepare();
}else{
- utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
- raw_printf(stderr,"Use -help for a list of options.\n");
- return 1;
+ setBinaryMode(stdin, 0);
+ console_utf8 = 0;
}
- data.cMode = data.mode;
- }
-#if SHELL_WIN_UTF8_OPT
- if( console_utf8 && stdin_is_interactive ){
- console_prepare();
- }else{
- setBinaryMode(stdin, 0);
- console_utf8 = 0;
- }
#endif
- if( !readStdin ){
- /* Run all arguments that do not begin with '-' as if they were separate
- ** command-line inputs, except for the argToSkip argument which contains
- ** the database filename.
- */
- for(i=0; i<nCmd; i++){
- if( azCmd[i][0]=='.' ){
- rc = do_meta_command(azCmd[i], &data);
- if( rc ){
- free(azCmd);
- return rc==2 ? 0 : rc;
- }
- }else{
- open_db(&data, 0);
- echo_group_input(&data, azCmd[i]);
- rc = shell_exec(&data, azCmd[i], &zErrMsg);
- if( zErrMsg || rc ){
- if( zErrMsg!=0 ){
- utf8_printf(stderr,"Error: %s\n", zErrMsg);
- }else{
- utf8_printf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]);
- }
- sqlite3_free(zErrMsg);
- free(azCmd);
- return rc!=0 ? rc : 1;
- }
- }
- }
- }else{
- /* Run commands received from standard input
- */
- if( stdin_is_interactive ){
- char *zHome;
- char *zHistory;
- int nHistory;
- printf(
- "SQLite version %s %.19s\n" /*extra-version-info*/
- "Enter \".help\" for usage hints.\n",
- sqlite3_libversion(), sqlite3_sourceid()
- );
- if( warnInmemoryDb ){
- printf("Connected to a ");
- printBold("transient in-memory database");
- printf(".\nUse \".open FILENAME\" to reopen on a "
- "persistent database.\n");
- }
- zHistory = getenv("SQLITE_HISTORY");
- if( zHistory ){
- zHistory = strdup(zHistory);
- }else if( (zHome = find_home_dir(0))!=0 ){
- nHistory = strlen30(zHome) + 20;
- if( (zHistory = malloc(nHistory))!=0 ){
- sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
+ if( !argsData.readStdin ){
+ /* Run all arguments that are not the DB name or do not begin with '-'
+ ** as if they were separate command-line inputs. */
+ for(i=0; i<cmdArgs.nCmd && rc<2; i++){
+ set_invocation_cmd(cmdArgs.azCmd[i]);
+ drc = process_input(&datai);
+ rc = (drc>2)? 2 : drc;
+ if( rc>0 ){
+ goto shell_bail;
}
}
- if( zHistory ){ shell_read_history(zHistory); }
+ }else{
+ /* Run commands received from standard input
+ */
+ if( stdin_is_interactive ){
+ char *zHome;
+ char *zHistory = 0;
+ if( argsData.bQuiet ){
+ /* bQuiet is almost like normal interactive, but quieter
+ ** and avoids history keeping and line editor completions. */
+ mainPrompt[0] = 0;
+ continuePrompt[0] = 0;
+ }else{
+ fprintf(STD_OUT,
+ "SQLite version %s %.19s\n" /*extra-version-info*/
+ "Enter \".help\" for usage hints.\n",
+ sqlite3_libversion(), sqlite3_sourceid()
+ );
+ if( warnInmemoryDb ){
+ fprintf(STD_OUT, "Connected to a ");
+ printBold("transient in-memory database");
+ fprintf(STD_OUT, ".\nUse \".open FILENAME\" to reopen on a "
+ "persistent database.\n");
+ }
+ zHistory = getenv("SQLITE_HISTORY");
+ if( zHistory ){
+ zHistory = strdup(zHistory);
+ }else if( (zHome = find_home_dir(0))!=0 ){
+ int nHistory = strlen30(zHome) + 20;
+ if( (zHistory = malloc(nHistory))!=0 ){
+ sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
+ }
+ }
+ if( zHistory ){ shell_read_history(zHistory); }
#if HAVE_READLINE || HAVE_EDITLINE
- rl_attempted_completion_function = readline_completion;
+ rl_attempted_completion_function = readline_completion;
#elif HAVE_LINENOISE
- linenoiseSetCompletionCallback(linenoise_completion);
+ linenoiseSetCompletionCallback(linenoise_completion);
#endif
- data.in = 0;
- rc = process_input(&data);
- if( zHistory ){
- shell_stifle_history(2000);
- shell_write_history(zHistory);
- free(zHistory);
+ }
+ datai.pInSource = &termInSource; /* read from stdin interactively */
+ drc = process_input(&datai);
+ rc = (drc>2)? 2 : drc;
+ if( !bQuiet ){
+ if( zHistory ){
+ shell_stifle_history(2000);
+ shell_write_history(zHistory);
+ free(zHistory);
+ }
+ }
+ }else{
+ datai.pInSource = &stdInSource; /* read from stdin without prompts */
+ drc = process_input(&datai);
+ rc = (drc>2)? 2 : drc;
}
- }else{
- data.in = stdin;
- rc = process_input(&data);
}
+ }else{
+ /* An abrupt, stack-ripping exit arrives here. */
+ }
+ shell_bail:
+ /* All users of resource managment should have left its stack as
+ ** it was near the beginning of shell execution. Verify this. */
+ assert(main_resource_mark==holder_mark());
+ if( cmdArgs.azCmd!=0 ){
+ free(cmdArgs.azCmd);
+ cmdArgs.azCmd = 0;
}
#ifndef SQLITE_SHELL_FIDDLE
/* In WASM mode we have to leave the db state in place so that
}
}
find_home_dir(1);
- output_reset(&data);
- data.doXdgOpen = 0;
- clearTempFile(&data);
-#if !SQLITE_SHELL_IS_UTF8
- for(i=0; i<argcToFree; i++) free(argvToFree[i]);
- free(argvToFree);
-#endif
- free(data.colWidth);
- free(data.zNonce);
- /* Clear the global data structure so that valgrind will detect memory
- ** leaks */
- memset(&data, 0, sizeof(data));
-#ifdef SQLITE_DEBUG
+ output_reset(&datai);
+ datai.doXdgOpen = 0;
+ clearTempFile(&datai);
+ sqlite3_free(datai.zEditor);
+# if SHELL_DYNAMIC_EXTENSION
+ notify_subscribers(&datai, 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
+ * are therefor (automatically) unloaded when it is closed. */
+ notify_subscribers(&datai, NK_ExtensionUnload, datax.dbShell);
+ /* Forcefully unsubscribe any extension which ignored above or did
+ * not unsubscribe upon getting above event. */
+ unsubscribe_extensions(&datai);
+ /* This must be done before any extensions unload. */
+ free_all_shext_tracking(&datai);
+ /* Unload extensions and free the DB used for dealing with them. */
+ sqlite3_close(datax.dbShell);
+ /* This notification can only reach statically linked extensions. */
+ notify_subscribers(&datai, NK_ShutdownImminent, 0);
+ /* Forcefull unsubscribe static extension event listeners. */
+ subscribe_events(&datax, 0, &datax, NK_Unsubscribe, 0);
- # endif
- # if !SQLITE_SHELL_IS_UTF8
- for(i=0; i<argcToFree; i++) free(argvToFree[i]);
- free(argvToFree);
+# endif
+ free(datax.pSpecWidths);
+ free(datai.zNonce);
+ for(i=0; i<datai.nSavedModes; ++i) sqlite3_free(datai.pModeStack[i]);
+# if SHELL_DATAIO_EXT
+ cmExporter.pMethods->destruct((ExportHandler*)&cmExporter);
+ ffExporter.pMethods->destruct((ExportHandler*)&ffExporter);
+# endif
+ aec = datax.shellAbruptExit;
+ /* Clear shell state objects so that valgrind detects real memory leaks. */
+ memset(&datai, 0, sizeof(datai));
+ 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));