From: larrybr Date: Thu, 11 May 2023 03:11:20 +0000 (+0000) Subject: WIP, CLI resource manager worked in, barely used yet. (Leaking one object.) X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c92093a86899dd719d01b2344bebe6988d2c238f;p=thirdparty%2Fsqlite.git WIP, CLI resource manager worked in, barely used yet. (Leaking one object.) FossilOrigin-Name: dbd00bcaa081ec73c2cb6eef0be5f82c890c8293aa90876942bc049e71f3c83a --- diff --git a/Makefile.in b/Makefile.in index 26aba17857..6405bbca9c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1162,6 +1162,8 @@ SHELL_SRC = \ $(TOP)/ext/misc/memtrace.c \ $(TOP)/src/shext_linkage.h \ $(TOP)/src/obj_interfaces.h \ + $(TOP)/src/resmanage.h \ + $(TOP)/src/resmanage.c \ $(TOP)/ext/recover/dbdata.c \ $(TOP)/ext/misc/zipfile.c \ $(TOP)/src/test_windirent.c diff --git a/Makefile.msc b/Makefile.msc index 4095eff2c8..91e77e8145 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -2244,25 +2244,27 @@ keywordhash.h: $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe # Source files that go into making shell.c SHELL_SRC = \ - $(TOP)\src\shell.c.in \ - $(TOP)\ext\misc\appendvfs.c \ - $(TOP)\ext\misc\completion.c \ - $(TOP)\ext\misc\base64.c \ - $(TOP)\ext\misc\base85.c \ - $(TOP)\ext\misc\decimal.c \ - $(TOP)\ext\misc\fileio.c \ - $(TOP)\ext\misc\ieee754.c \ - $(TOP)\ext\misc\regexp.c \ - $(TOP)\ext\misc\series.c \ - $(TOP)\ext\misc\shathree.c \ - $(TOP)\ext\misc\uint.c \ - $(TOP)\ext\expert\sqlite3expert.c \ - $(TOP)\ext\expert\sqlite3expert.h \ - $(TOP)\ext\misc\memtrace.c \ - $(TOP)/ext/recover/dbdata.c \ - $(TOP)/ext/recover/sqlite3recover.c \ - $(TOP)/ext/recover/sqlite3recover.h \ - $(TOP)\src\test_windirent.c + $(TOP)\src\shell.c.in \ + $(TOP)\ext\misc\appendvfs.c \ + $(TOP)\ext\misc\completion.c \ + $(TOP)\ext\misc\base64.c \ + $(TOP)\ext\misc\base85.c \ + $(TOP)\ext\misc\decimal.c \ + $(TOP)\ext\misc\fileio.c \ + $(TOP)\ext\misc\ieee754.c \ + $(TOP)\ext\misc\regexp.c \ + $(TOP)\ext\misc\series.c \ + $(TOP)\ext\misc\shathree.c \ + $(TOP)\ext\misc\uint.c \ + $(TOP)\ext\expert\sqlite3expert.c \ + $(TOP)\ext\expert\sqlite3expert.h \ + $(TOP)\ext\misc\memtrace.c \ + $(TOP)/ext/recover/dbdata.c \ + $(TOP)/ext/recover/sqlite3recover.c \ + $(TOP)/ext/recover/sqlite3recover.h \ + $(TOP)\src\resmanage.h \ + $(TOP)\src\resmanage.c \ + $(TOP)\src\test_windirent.c # If use of zlib is enabled, add the "zipfile.c" source file. # diff --git a/manifest b/manifest index 75d0dd1b33..2f1e3eae9b 100644 --- a/manifest +++ b/manifest @@ -1,11 +1,11 @@ -C Begin\suse\sof\sa\sresource\smanager\sfor\sCLI. -D 2023-05-10T09:51:51.040 +C WIP,\sCLI\sresource\smanager\sworked\sin,\sbarely\sused\syet.\s(Leaking\sone\sobject.) +D 2023-05-11T03:11:20.248 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 -F Makefile.in 8e5d01f0df1584356ddb25fbdd3eb03d98a81f1c9f295a6943f8648464bc80f4 +F Makefile.in d97bb86f0fbd38e2b71b3bd8089158c89d6b03ef575b72183348c0d7322c90b7 F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241 -F Makefile.msc 997ff8f34c6e8ec157d2b61e73cf4e1d2d5bcb9ffc6f9af068f4fd4deef0eebf +F Makefile.msc ecdd1e9fd753ecc02cd52f0d256a1bd86323be6eab6a3b2a196005d54edea3d2 F README.md e05bd8fcb45da04ab045c37f79a98654e8aa3b3b8f302cfbba80a0d510df75f7 F VERSION 17f95ae2fdf21f0e9575eb0b0511ea63f15d71dfff431b21c2b4adbfa70cfbbf F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 @@ -637,12 +637,12 @@ F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c 6350675966bd0e7ac3a464af9dbfe26db6f0d4237f4e1f1acdb17b12ad371e6e F src/printf.c b9320cdbeca0b336c3f139fd36dd121e4167dd62b35fbe9ccaa9bab44c0af38d F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c -F src/resmanage.c 1af14f7ee7a1ae77f1cd21214661912d6cbd786e33e9c303e2aa272d6fd86de0 -F src/resmanage.h 63778a96198495727b40579291341fbe2a631bf8082e5be906f3a6b1e6b89f2f +F src/resmanage.c 242d629812d391121f3a4f801f2802b17c89227985798a58f8ad1bdb25a8b356 +F src/resmanage.h 41e38ab8308611055582b031fdcbd179344025fce2f9814e3061b79f53efc34d F src/resolve.c 3e53e02ce87c9582bd7e7d22f13f4094a271678d9dc72820fa257a2abb5e4032 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c f9333ef8181192c22662f5cb8d257efc4a2880f9ee4853c6c4616f783d27e1b5 -F src/shell.c.in e84be25a7f2a4a9bfc3fb0755000cc35726e09e81c88af28a1a380eec5c56228 +F src/shell.c.in 154be768b7c1f1ba3de689f7567386647931e36030c50608d35e7e52d5d786c0 F src/shext_linkage.h 27dcf7624df05b2a7a6d367834339a6db3636f3035157f641f7db2ec499f8f6d F src/sqlite.h.in 27ca1d4b2eda8feee468af5735182390e8fe4696522751eec0136d17323201ad F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 @@ -2079,8 +2079,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 05ecd8a59ec576d4afe7f8785bdcc052c0887b7a5933783a832895b8c40cdeb9 -R 87fb9edd1be1a9bd6d2d5e412cb26bac +P 1527d429d66505d4f4bb8635c4d9d7ab926037b136554605d67835ee5d19d2de +R 80825c1180825bb4f4ec0bb8962d01e6 U larrybr -Z 1047f910d7d5ef2b2fd5d09f9bd5a949 +Z a4f9d6999be83f832b2f293999c7ee60 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index fa3275e3f6..16c1536eca 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1527d429d66505d4f4bb8635c4d9d7ab926037b136554605d67835ee5d19d2de \ No newline at end of file +dbd00bcaa081ec73c2cb6eef0be5f82c890c8293aa90876942bc049e71f3c83a \ No newline at end of file diff --git a/src/resmanage.c b/src/resmanage.c index f9aeda3327..99b30a0253 100644 --- a/src/resmanage.c +++ b/src/resmanage.c @@ -54,30 +54,32 @@ typedef struct ResourceHeld { /* The held-resource stack. This is for single-threaded use only. */ static ResourceHeld *pResHold = 0; -static unsigned short numResHold = 0; -static unsigned short numResAlloc = 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 unsigned short numCustom = 0; /* number of the set */ -static unsigned short numCustomAlloc = 0; /* allocated space */ +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 */ static ResourceMark exit_mark = 0; #ifndef SHELL_OMIT_LONGJMP static jmp_buf *p_exit_ripper = 0; #endif -/* Implementation per header comment */ +/* Current position of the held-resource stack */ ResourceMark holder_mark(){ return numResHold; } -/* Implementation per header comment */ +/* Strip resource stack then strip call stack (or exit.) */ void quit_moan(const char *zMoan, int errCode){ if( zMoan ){ fprintf(stderr, "Quitting due to %s, freeing %d resources.\n", @@ -129,58 +131,82 @@ static void free_rk( ResourceHeld *pRH ){ pRH->held.p_any = 0; } -void* take_held(ResourceMark mark, unsigned short offset){ - unsigned short rix = mark + offset; +/* 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 = 0; + 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; +} + /* Shared resource-stack pushing code */ static void res_hold(void *pv, FreeableResourceKind frk){ ResourceHeld rh = { pv, frk }; if( numResHold == numResAlloc ){ - size_t nrn = numResAlloc + (numResAlloc>>2) + 5; - ResourceHeld *prh; - prh = (ResourceHeld*)realloc(pResHold, nrn*sizeof(ResourceHeld)); - if( prh!=0 ){ - pResHold = prh; - numResAlloc = nrn; - }else{ - quit_moan("Out of memory",1); + ResourceCount nrn = (ResourceCount)((numResAlloc>>2) + 5); + if( !more_holders_try(nrn) ){ + free_rk(&rh); + quit_moan(resmanage_oom_message,1); } } pResHold[numResHold++] = rh; } -/* Implementation per header comment */ +/* 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; } -/* Implementation per header comment */ +/* Hold a C string in the malloc() heap */ char* mstr_holder(char *z){ if( z!=0 ) res_hold(z, FRK_Malloc); return z; } -/* Implementation per header comment */ +/* Hold a C string in the SQLite heap */ char* sstr_holder(char *z){ if( z!=0 ) res_hold(z, FRK_DbMem); return z; } -/* Implementation per header comment */ +/* Hold an open C runtime FILE */ void file_holder(FILE *pf){ if( pf!=0 ) res_hold(pf, FRK_File); } #if (!defined(_WIN32) && !defined(WIN32)) || !SQLITE_OS_WINRT -/* Implementation per header comment */ +/* Hold an open C runtime pipe */ void pipe_holder(FILE *pp){ if( pp!=0 ) res_hold(pp, FRK_Pipe); } #endif -/* Implementation per header comment */ +/* Hold anything together with arbitrary freeing function */ void* any_holder(void *pm, void (*its_freer)(void*)){ int i = 0; while( i < numCustom ){ @@ -196,27 +222,27 @@ void* any_holder(void *pm, void (*its_freer)(void*)){ aCustomFreers = pcf; aCustomFreers[numCustom++] = its_freer; }else{ - quit_moan("Out of memory",1); + quit_moan(resmanage_oom_message,1); } } res_hold(pm, i + FRK_CustomBase); return pm; } -/* Implementation per header comment */ +/* Hold some SQLite-allocated memory */ void* smem_holder(void *pm){ res_hold(pm, FRK_DbMem); return pm; } -/* Implementation per header comment */ +/* Hold a SQLite database "connection" */ void conn_holder(sqlite3 *pdb){ res_hold(pdb, FRK_DbConn); } -/* Implementation per header comment */ +/* Hold a SQLite prepared statement */ void stmt_holder(sqlite3_stmt *pstmt){ res_hold(pstmt, FRK_DbStmt); } -/* Implementation per header comment */ +/* Free all held resources in excess of given resource stack mark. */ void holder_free(ResourceMark mark){ while( numResHold > mark ){ free_rk(&pResHold[--numResHold]); @@ -237,12 +263,12 @@ void holder_free(ResourceMark mark){ } #ifndef SHELL_OMIT_LONGJMP -/* Implementation per header comment */ +/* Record a resource stack and call stack rip-to position */ void register_exit_ripper(jmp_buf *pjb, ResourceMark rip_mark){ exit_mark = rip_mark; p_exit_ripper = pjb; } -/* Implementation per header comment */ +/* Undo register_exit_ripper effect, back to default state. */ void forget_exit_ripper(jmp_buf *pjb){ exit_mark = 0; assert(p_exit_ripper == pjb); diff --git a/src/resmanage.h b/src/resmanage.h index a43bee0b97..24b3dca803 100644 --- a/src/resmanage.h +++ b/src/resmanage.h @@ -46,6 +46,7 @@ # define RIP_TO_HERE(jb) 0 #endif #include +#include #ifdef __cplusplus extern "C" { @@ -55,10 +56,15 @@ extern "C" { /* Type used for marking positions within a held-resource stack */ typedef unsigned short ResourceMark; +typedef unsigned short ResourceCount; /* Current position of the held-resource stack */ extern ResourceMark holder_mark(); +/* Assure no allocation failure for some more xxx_holder() calls. +** Note that this call may fail with an OOM abrupt exit. */ +extern void more_holders(ResourceCount more); + /* Routines for holding resources on held-resource stack together ** with enough information for them to be freed by this package. */ @@ -84,7 +90,10 @@ extern void pipe_holder(FILE *); #endif /* Take back a held resource pointer, leaving held as NULL. (no-op) */ -extern void* take_held(ResourceMark mark, unsigned short offset); +extern void* take_held(ResourceMark mark, ResourceCount offset); + +/* Swap a held resource pointer for a new one. */ +extern void* swap_held(ResourceMark mark, ResourceCount offset, void *pNew); /* Free all held resources in excess of given resource stack mark. */ extern void holder_free(ResourceMark mark); @@ -109,6 +118,9 @@ extern void forget_exit_ripper(jmp_buf *pjb); */ extern void quit_moan(const char *zMoan, int errCode); +/* What the complaint will be for OOM failures and abrupt exits. */ +extern const char *resmanage_oom_message; + #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif diff --git a/src/shell.c.in b/src/shell.c.in index d33a5a5d36..0ead7dbf32 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -242,6 +242,9 @@ extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText); /* Get the shell extension interfaces and structs. */ INCLUDE shext_linkage.h +/* Get resource management package, supporting OOM and safe mode exits. */ +INCLUDE resmanage.c + /* For an embedded shell, allow the 3 standard streams to be specified. ** If used, these names will have to refer to something globally reachable ** from the same thread which called the shell's main(). @@ -502,7 +505,7 @@ static sqlite3 *globalDb = 0; static sqlite3_mutex *pGlobalDbLock = 0; /* -** True if an interrupt (Control-C) has been received. +** Greater than 0 if an interrupt (Control-C) has been received. */ static volatile int seenInterrupt = 0; @@ -1905,6 +1908,8 @@ static void updateSafeMode(ShellInState *psi){ #define ShellHasFlag(psx,X) (((psx)->pSIS->shellFlgs & (X))!=0) #define ShellSetFlag(psx,X) ((psx)->pSIS->shellFlgs|=(X)) #define ShellClearFlag(psx,X) ((psx)->pSIS->shellFlgs&=(~(X))) +#define ShellSetFlagI(psi,X) ((psi)->shellFlgs|=(X)) +#define ShellClearFlagI(psi,X) ((psi)->shellFlgs&=(~(X))) /* ** These are the allowed modes, in search order (for abbreviation matches.) @@ -15886,7 +15891,8 @@ static void verify_uninitialized(void){ } /* -** Initialize the state information in data and datax +** Initialize the state information in data and datax. +** Does no heap allocation. */ static void main_init(ShellInState *pData, ShellExState *pDatax) { memset(pData, 0, sizeof(*pData)); @@ -15950,7 +15956,7 @@ static char *cmdline_option_value(int argc, char **argv, int i){ if( i==argc ){ utf8_printf(STD_ERR, "%s: Error: missing argument to %s\n", argv[0], argv[argc-1]); - exit(1); + quit_moan("invocation error", 1); } return argv[i]; } @@ -15959,6 +15965,373 @@ static void sayAbnormalExit(void){ if( seenInterrupt ) fprintf(stderr, "Program interrupted.\n"); } +/* A vector of command strings collected from . */ +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 */ + ResourceMark buffMark; /* where to grab held array pointer */ +} 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; +/* +** 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; ipad->nOptsEnd ){ + if( psi->aAuxDb->zDbFilename==0 ){ + psi->aAuxDb->zDbFilename = z; + }else{ + void *vaz; + if( pca->nCmd == 0 ){ + pca->buffMark = holder_mark(); + more_holders(1); + } + vaz = realloc(pca->azCmd, sizeof(pca->azCmd[0])*(pca->nCmd+1)); + shell_check_oom(vaz); + if( pca->nCmd == 0 ) mmem_holder(vaz); + else swap_held(pca->buffMark, 0, 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/sz0 && 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+1szMax = 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=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+1szMax = 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. */ + z = cmdline_option_value(argc,argv,++i); + if( z[0]=='.' ){ + drc = do_dot_command(z, psi->pSXS); + rc = (drc>2)? 2 : drc; + }else{ + rc = run_single_query(psi->pSXS, z); + } + if( rc && bail_on_error ){ + 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__))) @@ -16000,21 +16373,23 @@ int SQLITE_CDECL SHELL_MAIN(int argc, wchar_t **wargv){ BuiltInFFExporter ffExporter = BI_FF_EXPORTER_INIT( &data ); BuiltInCMExporter cmExporter = BI_CM_EXPORTER_INIT( &data ); #endif + RIP_STATE(exit_jb); const char *zInitFile = 0; int bQuiet = 0; /* for testing, to suppress banner and history actions */ int i, aec; int rc = 0; DotCmdRC drc = DCR_Ok; int warnInmemoryDb = 0; - int readStdin = 1; - int nCmd = 0; + /* azCmd, nCmd, buffMark */ + CmdArgs cmdArgs = {0,0,0}; + /* readStdin, nOptsEnd, zInitFile, zVfs, bQuiet */ + ArgsData argsData = { 1, argc, 0,0,0 }; int nOptsEnd = argc; - char **azCmd = 0; - const char *zVfs = 0; /* Value of -vfs command-line option */ #if !SQLITE_SHELL_IS_UTF8 char **argvToFree = 0; int argcToFree = 0; #endif + ResourceMark entry_mark = holder_mark(); setvbuf(STD_ERR, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ #ifdef SQLITE_SHELL_FIDDLE @@ -16076,541 +16451,235 @@ int SQLITE_CDECL SHELL_MAIN(int argc, wchar_t **wargv){ data.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. */ -#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=1 && argv && argv[0] ); - Argv0 = argv[0]; -#if SHELL_DYNAMIC_EXTENSION - initStartupDir(); - if( isExtendedBasename(Argv0) ) data.bExtendedDotCmds = SHELL_ALL_EXTENSIONS; -#endif + register_exit_ripper(&exit_jb, entry_mark); + if( 0==RIP_TO_HERE(exit_jb) ){ + + /* 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; izDbFilename); - warnInmemoryDb = 0; - } -#endif + assert( argc>=1 && argv && argv[0] ); + Argv0 = argv[0]; + #if SHELL_DYNAMIC_EXTENSION + initStartupDir(); + if( isExtendedBasename(Argv0) ) data.bExtendedDotCmds = SHELL_ALL_EXTENSIONS; + #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. - */ -#ifndef SQLITE_SHELL_FIDDLE - verify_uninitialized(); -#endif - for(i=1; inOptsEnd ){ - if( data.aAuxDb->zDbFilename==0 ){ - data.aAuxDb->zDbFilename = z; + #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; + } + #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. + */ + #ifndef SQLITE_SHELL_FIDDLE + verify_uninitialized(); + #endif + i = scanInvokeArgs(argc, argv, 1, &data, &cmdArgs, &argsData); + #ifndef SQLITE_SHELL_FIDDLE + 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(); + } + #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(); + #endif + + /* Register the control-C (SIGINT) handler. + ** Make sure we have a valid signal handler early, before anything + ** is done that might take long. */ + pGlobalDbLock = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + #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{ - /* 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/sz0 && 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; + utf8_printf(STD_ERR, "no such VFS: \"%s\"\n", argsData.zVfs); + rc = 1; + goto shell_bail; } -#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 ){ - 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+1zDbFilename==0 ){ + #ifndef SQLITE_OMIT_MEMORYDB + data.pAuxDb->zDbFilename = ":memory:"; + warnInmemoryDb = argc==1; + #else + utf8_printf(STD_ERR,"%s: Error: no database filename specified\n", Argv0); rc = 1; goto shell_bail; + #endif } - } + data.out = STD_OUT; + #ifndef SQLITE_SHELL_FIDDLE + sqlite3_appendvfs_init(0,0,0); + #endif - if( data.pAuxDb->zDbFilename==0 ){ -#ifndef SQLITE_OMIT_MEMORYDB - data.pAuxDb->zDbFilename = ":memory:"; - warnInmemoryDb = argc==1; -#else - utf8_printf(STD_ERR,"%s: Error: no database filename specified\n", Argv0); - rc = 1; - goto shell_bail; -#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 + ** 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(&datax, 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(data.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, without any quitting or bail-on-error. + */ + process_sqliterc(&data,argsData.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(&data,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, &data, &cmdArgs, &argsData); + if( (rc && bail_on_error) || rc>1 ){ + if( rc==2 ) 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=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 ){ - 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+12)? 2 : drc; - }else{ - rc = run_single_query(&datax, z); - } - if( rc && bail_on_error ){ - if( rc==2 ) rc = 0; - goto shell_bail; - } -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) - }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); - rc = 1; - goto shell_bail; - } - open_db(&datax, OPEN_DB_ZIPFILE); - if( z[2] ){ - argv[i] = &z[2]; - drc = arDotCommand(&datax, 1, argv+(i-1), argc-(i-1)); - }else{ - drc = arDotCommand(&datax, 1, argv+i, argc-i); - } - rc = (drc>2)? 2 : drc; - readStdin = 0; - break; -#endif - }else if( cli_strcmp(z,"-safe")==0 ){ - data.bSafeMode = data.bSafeModeFuture = 1; - }else if( cli_strcmp(z,"-unsafe-testing")==0 ){ - /* Acted upon in first pass. */ - }else if( cli_strcmp(z,"-quiet")==0 ){ - ++i; + #if SHELL_WIN_UTF8_OPT + if( console_utf8 && stdin_is_interactive ){ + console_prepare(); }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, &datax, 0); - data.cMode = data.mode; + setBinaryMode(stdin, 0); + console_utf8 = 0; } - } -#if SHELL_WIN_UTF8_OPT - if( console_utf8 && stdin_is_interactive ){ - console_prepare(); - }else{ - setBinaryMode(stdin, 0); - console_utf8 = 0; - } -#endif + #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; i2)? 2 : drc; - }else{ - rc = run_single_query(&datax, azCmd[i]); - } - if( rc && bail_on_error ){ - goto shell_bail; - } - } - }else{ - /* Run commands received from standard input - */ - if( stdin_is_interactive ){ - char *zHome; - char *zHistory = 0; - if( bQuiet ){ - /* bQuiet is almost like normal interactive, but quieter. */ - 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"); + if( !argsData.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; i2)? 2 : drc; + }else{ + rc = run_single_query(&datax, cmdArgs.azCmd[i]); } - 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( rc && bail_on_error ){ + goto shell_bail; } - if( zHistory ){ shell_read_history(zHistory); } -#if HAVE_READLINE || HAVE_EDITLINE - rl_attempted_completion_function = readline_completion; -#elif HAVE_LINENOISE - linenoiseSetCompletionCallback(linenoise_completion); -#endif } - data.pInSource = &termInSource; /* read from stdin interactively */ - drc = process_input(&data); - rc = (drc>2)? 2 : drc; - if( !bQuiet ){ - if( zHistory ){ - shell_stifle_history(2000); - shell_write_history(zHistory); - free(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; + #elif HAVE_LINENOISE + linenoiseSetCompletionCallback(linenoise_completion); + #endif + } + data.pInSource = &termInSource; /* read from stdin interactively */ + drc = process_input(&data); + rc = (drc>2)? 2 : drc; + if( !bQuiet ){ + if( zHistory ){ + shell_stifle_history(2000); + shell_write_history(zHistory); + free(zHistory); + } } + }else{ + data.pInSource = &stdInSource; /* read from stdin without prompts */ + drc = process_input(&data); + rc = (drc>2)? 2 : drc; } - }else{ - data.pInSource = &stdInSource; /* read from stdin without prompts */ - drc = process_input(&data); - rc = (drc>2)? 2 : drc; } + }else{ + /* An abrupt, stack-ripping exit arrives here. */ } shell_bail: + holder_free(entry_mark); #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. @@ -16663,7 +16732,6 @@ int SQLITE_CDECL SHELL_MAIN(int argc, wchar_t **wargv){ free(datax.pSpecWidths); free(data.zNonce); for(i=0; idestruct((ExportHandler*)&cmExporter); ffExporter.pMethods->destruct((ExportHandler*)&ffExporter);