From: larrybr Date: Sat, 13 May 2023 16:54:12 +0000 (+0000) Subject: Extend CLI resource manager usage. Improve interface to it for clarity and simplicity. X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f43776e655a327ccead1e2723cace7b3d476c91c;p=thirdparty%2Fsqlite.git Extend CLI resource manager usage. Improve interface to it for clarity and simplicity. FossilOrigin-Name: fd379f22926d55d52176b34b20e6dda2cd1218adaaed446e4945c38a5efe0fb1 --- f43776e655a327ccead1e2723cace7b3d476c91c diff --cc manifest index f6ac686c3c,a3ebde84d1..0cf46cf647 --- a/manifest +++ b/manifest @@@ -1,5 -1,5 +1,5 @@@ - C CLI\scloser\sto\sdoing\sfull\scleanup\son\serror\sexits.\sNeeds\stesting\swith\sOOM\ssimulation. - D 2023-05-13T07:23:14.350 -C Mention\sthat\ssqlite3_mutex_free()\saccepts\sNULL\sas\sa\sno-op. -D 2023-05-13T15:34:15.805 ++C Extend\sCLI\sresource\smanager\susage.\sImprove\sinterface\sto\sit\sfor\sclarity\sand\ssimplicity. ++D 2023-05-13T16:54:12.397 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@@ -637,14 -633,11 +637,14 @@@ F src/pragma.h e690a356c18e98414d2e870e F src/prepare.c 6350675966bd0e7ac3a464af9dbfe26db6f0d4237f4e1f1acdb17b12ad371e6e F src/printf.c b9320cdbeca0b336c3f139fd36dd121e4167dd62b35fbe9ccaa9bab44c0af38d F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c - F src/resmanage.c 3495deb8d4e7499eec30c1de4c4ce85a610a611f42fd74a24584416f0d1f9aba - F src/resmanage.h 9f937b7d2334b15f5ed2c3f38d707fec5062b414ecd2483d788a4325855eb3fd ++F src/resmanage.c 3b3a76d32e45471356e83dc59b67f5ea537b3381df09f550c1a9810648769799 ++F src/resmanage.h 161405806c22fe80b6aa786a0f8b0962f21cd25c1966c8aaafaf497e563c84d4 F src/resolve.c 3e53e02ce87c9582bd7e7d22f13f4094a271678d9dc72820fa257a2abb5e4032 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c 12aa3168be4ff175702fe0ebeaf544312be22d275d378a28e7b2fad32d552d36 - F src/shell.c.in d5761d88d2f47ba75c778f0383573f7def7eb6ba4248c301eda09f40606e9c42 -F src/shell.c.in 1e18312f58d365042036fc9d19dcef416074f783702b168f07814332c2268ee0 ++F src/shell.c.in 498b7653702e8a4391fe6c69fcd535069e7eb8033c07a2481cf817eee79882ca +F src/shext_linkage.h 27dcf7624df05b2a7a6d367834339a6db3636f3035157f641f7db2ec499f8f6d - F src/sqlite.h.in 27ca1d4b2eda8feee468af5735182390e8fe4696522751eec0136d17323201ad + F src/sqlite.h.in c14a4471fcd897a03631ac7ad3d05505e895e7b6419ec5b96cae9bc4df7a9fc6 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h da473ce2b3d0ae407a6300c4a164589b9a6bfdbec9462688a8593ff16f3bb6e4 F src/sqliteInt.h 6e08039aa944e874d3878c4f12bbd78cea797c988dad147adc9ffb1c2b179402 @@@ -2080,8 -2069,8 +2080,8 @@@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a9 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 - P ce25a07950e10e5f0c33f179f9b7d307a73b23ad859b5a97e5c6d7bc9e68b254 f06c16a8b0e7a15ce4f7d99af3376a1bf1bfbfc0fdc048b079418ae74c619d6b - R 383a924fa91f5b4145d9f04d112422b7 -P 67001887044da71ab50c07d8345bd950a0a1f9688cf9cd6966312ea163cf6e17 -R f137c75703c92c2af2a3b7644ec04694 ++P 8751f93fa505a514d8ab7eae4f9093310ee60b90046f4632e80858001781cb31 603d9ad5012ca8996783996d7b8cd6a1aabf12b21604a2ccc137f4c2d99427b9 ++R 90a259a6a8128da5d9e2c168718fd7cd U larrybr - Z 69b83efe69b378ac6c729ccaf8b045c5 -Z a68f3963d62b5a0e832809880b122d2b ++Z bcc2e382aa2ea964989bed4339357d06 # Remove this line to create a well-formed Fossil manifest. diff --cc manifest.uuid index 889ced5443,08988c19c4..17264b230f --- a/manifest.uuid +++ b/manifest.uuid @@@ -1,1 -1,1 +1,1 @@@ - 8751f93fa505a514d8ab7eae4f9093310ee60b90046f4632e80858001781cb31 -603d9ad5012ca8996783996d7b8cd6a1aabf12b21604a2ccc137f4c2d99427b9 ++fd379f22926d55d52176b34b20e6dda2cd1218adaaed446e4945c38a5efe0fb1 diff --cc src/resmanage.c index f0f79bf39c,0000000000..7dd9883de6 mode 100644,000000..100644 --- a/src/resmanage.c +++ b/src/resmanage.c @@@ -1,339 -1,0 +1,341 @@@ +/* +** 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 +#include + +#include +#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 +# include +# 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 */ - static ResourceMark exit_mark = 0; - #ifndef SHELL_OMIT_LONGJMP - static jmp_buf *p_exit_ripper = 0; - #endif ++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); + } - fprintf(stderr, "Auto-freed %d resources.\n", holder_free(exit_mark)); ++ fprintf(stderr, "Auto-freed %d resources.\n", ++ holder_free( (pRSD)? pRSD->resDest : 0 )); ++ pRipStack = (pRSD)? pRSD->pPrev : 0; +#ifndef SHELL_OMIT_LONGJMP - if( p_exit_ripper!=0 ){ - longjmp(*p_exit_ripper, errCode); ++ 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; +} + - #ifndef SHELL_OMIT_LONGJMP +/* 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; ++void register_exit_ripper(RipStackDest *pRSD){ ++ assert(pRSD!=0); ++ pRSD->pPrev = pRipStack; ++ pRSD->resDest = holder_mark(); ++ pRipStack = pRSD; +} - /* 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; ++/* 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; ++ } +} - #endif diff --cc src/resmanage.h index b083d1724e,0000000000..4401a55351 mode 100644,000000..100644 --- a/src/resmanage.h +++ b/src/resmanage.h @@@ -1,165 -1,0 +1,171 @@@ +/* +** 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 declares the interface of a simple resource management package +** which supports freeing of resources upon an abrupt, program-initiated +** termination of a function from somewhere in a call tree. The package is +** designed to be used with setjmp()/longjmp() to effect a pattern similar +** to the try/throw/catch available in some programming languages. (But see +** below regarding usage when the setmp()/longjmp() pair is unavailable.) +** +** The general scheme is that routines whose pre-return code might be +** bypassed, thereby leaking resources, do not rely on pre-return code +** to release locally held resources. Instead, they give ownership of +** such resources to this package, via xxxx_holder(...) calls, and use +** its holder_mark() and holder_free(...) functions to release locally +** acquired resources. +** +** For environments where setmp()/longjmp() are unavailable, (indicated by +** SHELL_OMIT_LONGJMP defined), the package substitutes a process exit for +** resumption of execution at a chosen code location. The resources in the +** package's held resource stack are still released. And the ability to +** free locally acquired resources as functions return is retained. This +** can simplify early function exit logic, but a body of code relying on +** process exit to ease error handling is unsuitable for use as a called +** routine within a larger application. That use is most of the reason for +** this package's existence. +*/ + +#ifndef RES_MANAGE_H +# define RES_MANAGE_H + +#ifndef SHELL_OMIT_LONGJMP +# include - # define RIP_STATE(jb) jmp_buf jb - # define RIP_TO_HERE(jb) setjmp(jb) - #else - # define RIP_STATE(jb) - # define RIP_TO_HERE(jb) 0 +#endif +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sqlite3.h" + +/* Type used for marking/conveying positions within a held-resource stack */ +typedef unsigned short ResourceMark; +typedef unsigned short ResourceCount; + ++/* Type used to record a possible succession of recovery destinations */ ++typedef struct RipStackDest { ++ struct RipStackDest *pPrev; ++ ResourceMark resDest; ++#ifndef SHELL_OMIT_LONGJMP ++ jmp_buf exeDest; ++#endif ++} RipStackDest; ++#define RIP_STACK_DEST_INIT {0} ++ ++/* This macro effects stack mark and potential rip-back, keeping needed ++** details in a RipStackDest object (independent of SHELL_OMIT_LONGJMP.) */ ++#ifndef SHELL_OMIT_LONGJMP ++# define RIP_TO_HERE(RSD) (RSD.resDest=holder_mark(), setjmp(RSD.exeDest)) ++#else ++# define RIP_TO_HERE(RSD) (RSD.resDest=holder_mark(), 0) ++#endif ++ +/* Type used for generic free functions (to fib about their signature.) */ +typedef void (*GenericFreer)(void*); + +/* 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); + +/* Lose one or more holders, without freeing anything. */ +extern void* pop_holder(void); +extern void pop_holders(ResourceCount num); + +/* +** Routines for holding resources on held-resource stack together +** with enough information for them to be freed by this package. +*/ + +/* xxxx_holder(...) +** The referenced objects are directly freed; they are stored in +** the heap with lifetime not bound to the caller's activation. +*/ +/* anything together with arbitrary freeing function */ +extern void* any_holder(void *pm, void (*its_freer)(void*)); +/* anything in the malloc() heap */ +extern void* mmem_holder(void *pm); +/* a C string in the malloc() heap */ +extern char* mstr_holder(char *z); +/* some SQLite-allocated memory */ +extern void* smem_holder(void *pm); +/* a C string in the SQLite heap */ +extern char* sstr_holder(char *z); +/* a SQLite database "connection" */ +extern void conn_holder(sqlite3 *pdb); +/* a SQLite prepared statement */ +extern void stmt_holder(sqlite3_stmt *pstmt); +/* an open C runtime FILE */ +extern void file_holder(FILE *); +#if (!defined(_WIN32) && !defined(WIN32)) || !SQLITE_OS_WINRT +/* an open C runtime pipe */ +extern void pipe_holder(FILE *); +#endif + +/* xxxx_ptr_holder(...) or xxxx_ref_holder(...) +** The referenced objects are indirectly freed; they are stored +** in the caller's stack frame, with lifetime limited to the +** the caller's activation. It is a grave error to use these +** then fail to call holder_free() before returning. For abrupt +** exits, this condition is met because holder_free() is called +** by the abrupt exiter before the execution stack is stripped. +*/ +typedef struct AnyResourceHolder { + void *pAny; + GenericFreer its_freer; +} AnyResourceHolder; + +/* a reference to an AnyResourceHolder (whose storage is not managed) */ +extern void any_ref_holder(AnyResourceHolder *parh); +/* a C string in the SQLite heap, reference to */ +extern void sstr_ptr_holder(char **pz); +/* a SQLite prepared statement, reference to */ +extern void stmt_ptr_holder(sqlite3_stmt **ppstmt); +#ifdef SHELL_MANAGE_TEXT +/* a ShellText object, reference to (storage for which not managed) */ +static void text_ref_holder(ShellText *); +#endif + +/* Take back a held resource pointer, leaving held as NULL. (no-op) */ +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. +** Return count of number actually freed (rather than being 0.) */ +extern int holder_free(ResourceMark mark); + - #ifndef SHELL_OMIT_LONGJMP - /* Remember a longjmp() destination together with a resource stack - ** mark which determines how far the resource stack may be stripped. - ** This info determines how far the execution and resource stacks - ** will be stripped back should quit_moan(...) be called. ++/* Remember execution and resource stack postion/state. This determines ++** how far these stacks may be stripped should quit_moan(...) be called. +*/ - extern void register_exit_ripper(jmp_buf *pjb, ResourceMark rip_mark); ++extern void register_exit_ripper(RipStackDest *); +/* Forget whatever register_exit_ripper() has been recorded. */ - extern void forget_exit_ripper(jmp_buf *pjb); - #else - #define register_exit_ripper(jb, rm) - #define forget_exit_ripper() - #endif ++extern void forget_exit_ripper(RipStackDest *); + +/* Strip resource stack and execute previously registered longjmp() as +** previously prepared by register_exit_ripper() call. Or, if no such +** prep done (or possible), strip the whole stack and exit the process. +*/ +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 +#endif /* RES_MANAGE_H */ diff --cc src/shell.c.in index fef2bb43a9,ccaed896d9..6cc019333d --- a/src/shell.c.in +++ b/src/shell.c.in @@@ -16153,361 -11941,6 +16153,374 @@@ static void sayAbnormalExit(void) 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; inCmd; ++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; ipad->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/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. */ + 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__))) @@@ -16538,36 -11963,27 +16551,34 @@@ int SQLITE_CDECL SHELL_MAIN(int argc, w #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 +#if SHELL_DATAIO_EXT + BuiltInFFExporter ffExporter = BI_FF_EXPORTER_INIT( &datai ); + BuiltInCMExporter cmExporter = BI_CM_EXPORTER_INIT( &datai ); +#endif - RIP_STATE(exit_jb); ++ 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; @@@ -16622,250 -12035,521 +16633,256 @@@ 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=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; inOptsEnd ){ - 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/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; - } -#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+1zDbFilename==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=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+10 ){ - 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; i2)? 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 @@@ -16892,44 -12568,19 +16909,40 @@@ } } find_home_dir(1); - output_reset(&data); - data.doXdgOpen = 0; - clearTempFile(&data); -#if !SQLITE_SHELL_IS_UTF8 - for(i=0; idestruct((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));