From: larrybr Date: Sun, 6 Mar 2022 03:22:13 +0000 (+0000) Subject: (WIP) shell extension load and ShellState internal/external partitioning in place... X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=961f27a668717cd10495b5096ec9105f7c979573;p=thirdparty%2Fsqlite.git (WIP) shell extension load and ShellState internal/external partitioning in place, passing shell tests with trivial exceptions FossilOrigin-Name: a85679d3e9e1edf71ed6308377783cdaa677c51290f644a28da9a69d429e7be9 --- diff --git a/Makefile.in b/Makefile.in index e5d30d030b..62c64e2102 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1086,22 +1086,24 @@ keywordhash.h: $(TOP)/tool/mkkeywordhash.c # Source files that go into making shell.c SHELL_SRC = \ - $(TOP)/src/shell.c.in \ + $(TOP)/src/shell.c.in \ $(TOP)/ext/misc/appendvfs.c \ - $(TOP)/ext/misc/completion.c \ + $(TOP)/ext/misc/completion.c \ $(TOP)/ext/misc/decimal.c \ - $(TOP)/ext/misc/fileio.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/sqlar.c \ + $(TOP)/ext/misc/shathree.c \ + $(TOP)/ext/misc/sqlar.c \ $(TOP)/ext/misc/uint.c \ - $(TOP)/ext/expert/sqlite3expert.c \ - $(TOP)/ext/expert/sqlite3expert.h \ - $(TOP)/ext/misc/zipfile.c \ - $(TOP)/ext/misc/memtrace.c \ - $(TOP)/src/test_windirent.c + $(TOP)/ext/expert/sqlite3expert.c \ + $(TOP)/ext/expert/sqlite3expert.h \ + $(TOP)/ext/misc/zipfile.c \ + $(TOP)/ext/misc/memtrace.c \ + $(TOP)/src/test_windirent.c \ + $(TOP)/src/shext_linkage.h \ + $(TOP)/src/obj_interfaces.h shell.c: $(SHELL_SRC) $(TOP)/tool/mkshellc.tcl $(TCLSH_CMD) $(TOP)/tool/mkshellc.tcl >shell.c diff --git a/manifest b/manifest index 19d125b550..d514a66ceb 100644 --- a/manifest +++ b/manifest @@ -1,9 +1,9 @@ -C Merge\sshell-tweaks\senhancements\s(with\s3.38) -D 2022-02-25T00:09:07.697 +C (WIP)\sshell\sextension\sload\sand\sShellState\sinternal/external\spartitioning\sin\splace,\spassing\sshell\stests\swith\strivial\sexceptions +D 2022-03-06T03:22:13.817 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 -F Makefile.in b210ad2733317f1a4353085dfb9d385ceec30b0e6a61d20a5accabecac6b1949 +F Makefile.in 39fc2a1f2ba85066c41c33c229e84e26fa51e953efa6bb703856153a308d70e2 F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241 F Makefile.msc b28a8a7a977e7312f6859f560348e1eb110c21bd6cf9fab0d16537c0a514eef3 F README.md 2dd87a5c1d108b224921f3dd47dea567973f706e1f6959386282a626f459a70c @@ -555,8 +555,8 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c F src/resolve.c ea935b87d6fb36c78b70cdc7b28561dc8f33f2ef37048389549c7b5ef9b0ba5e F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c 3baa9dd8cf240654773c7974e2bcce398ac9dd24419c36684156963defe43b35 -F src/shell.c.in 0da11ed800103e60c0cea85538efb025bebdba25c1e8516aa20ee67237e16be6 -F src/shext_linkage.h 5897e8140a06cb733d07a927994b30d41225eb93b5b8cf2ad0e3460cb4e95fd6 +F src/shell.c.in da85b36617af6eee9bc1b3ecb1ef2d7b17bed3acc42b367ff9d1ee8794687532 +F src/shext_linkage.h bff86090e82d1df3a6dd0c566324f38ac67cb75d6e72697d9a8a3065545b4f0c F src/sqlite.h.in e30cedf008d9c51511f4027a3739b727a588da553424748b48d2393f85dbde41 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h a95cb9ed106e3d39e2118e4dcc15a14faec3fa50d0093425083d340d9dfd96e6 @@ -605,6 +605,7 @@ F src/test_quota.h 2a8ad1952d1d2ca9af0ce0465e56e6c023b5e15d F src/test_rtree.c 671f3fae50ff116ef2e32a3bf1fe21b5615b4b7b F src/test_schema.c f5d6067dfc2f2845c4dd56df63e66ee826fb23877855c785f75cc2ca83fd0c1b F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe +F src/test_shellext.c ea066d41fb25c55a3cf8b65f82e8beacc5ac477a19b770822003c1e48a9ab03b F src/test_sqllog.c 540feaea7280cd5f926168aee9deb1065ae136d0bbbe7361e2ef3541783e187a F src/test_superlock.c 4839644b9201da822f181c5bc406c0b2385f672e F src/test_syscall.c 1073306ba2e9bfc886771871a13d3de281ed3939 @@ -1882,7 +1883,7 @@ F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61 F tool/mkopcodeh.tcl 130b88697da6ec5b89b41844d955d08fb62c2552e889dec8c7bcecb28d8f50bd F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa F tool/mkpragmatab.tcl bd07bd59d45d0f3448e123d6937e9811195f9908a51e09d774609883055bfd3d -F tool/mkshellc.tcl 754c73032cd7d279ce42ddeaaa49d1ece71adf96293e10e3b0670c0d6b0f8972 +F tool/mkshellc.tcl 36ecc5f66eab5c4687af1a0a4cb8435e9b5ab8ec9422ea63096b74453f83bf9b F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9 F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97 F tool/mksqlite3c-noext.tcl 4f7cfef5152b0c91920355cbfc1d608a4ad242cb819f1aea07f6d0274f584a7f @@ -1948,8 +1949,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 6d0557244f69e78474f9222aae95c4c47925a466aa15e5666d0ada237538e262 8c9a5fb26ba045edef1269c5f5e8c8d87fa890b88ddb1121be72514a389a845d -R 791b63806b337836957b5b12020a8987 +P 4c7d94d3f339d5de98ab5488e75b5dfea2c11fadb2cd44a9f6bc4e0769dbd34a +R 4896c526cf9c30b9709825dd81bf0bdb U larrybr -Z 6cdde194291306233e0d54419a5b14ca +Z 8541d8e1a52cfb449200764f60f8482a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 632da48fb7..7ba0c374f0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4c7d94d3f339d5de98ab5488e75b5dfea2c11fadb2cd44a9f6bc4e0769dbd34a \ No newline at end of file +a85679d3e9e1edf71ed6308377783cdaa677c51290f644a28da9a69d429e7be9 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index d227a9bd49..1528586763 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -211,10 +211,11 @@ extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText); #endif /* Get the shell extension interfaces and structs. */ -INCLUDE shext_linkage.h +INCLUDE shext_linkage.h -/* For an embedded shell, allow the 3 standard streams to be specified. -** If used, these names will have to refer to something globally reachable. +/* 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(). **/ #ifndef STD_IN # define STD_IN stdin @@ -248,6 +249,7 @@ static void setTextMode(FILE *file, int isOutput){ static const char *(azHelp[]); static unsigned numCommands; +static FILE *currentOutputFile(ShellExState *p); /* True if the timer is enabled */ static int enableTimer = 0; @@ -663,7 +665,7 @@ static FILE * openChrSource(const char *zFile){ /* ** Arrange for shell input from either a FILE or a string. -** For applicable invariants, see str_line_get(...) which is +** For applicable invariants, see strLineGet(...) which is ** the only modifier of this struct. (3rd and 4th members) ** All other members are simply initialized to select the ** input stream and track input sources, set by whatever @@ -685,12 +687,12 @@ typedef struct InSource { /* This instance's address is taken as part of interactive input test. */ static InSource termInSource = { 0 /*stdin*/, 0, 0, 0, "", 0}; static InSource stdInSource = { 0 /*stdin*/, 0, 0, 0, "", 0}; -static void init_std_inputs( FILE *pIn ){ +static void init_std_inputs(FILE *pIn ){ termInSource.inFile = pIn; stdInSource.inFile = pIn; } -static char *str_line_get( char *zBuf, int ncMax, InSource *pInSrc){ +static char *strLineGet(char *zBuf, int ncMax, InSource *pInSrc){ if( pInSrc->inFile!=0 ){ char *zRet = fgets(zBuf, ncMax, pInSrc->inFile ); if( zRet!=0 ){ @@ -742,7 +744,7 @@ static char *local_getline(char *zLine, InSource *pInSrc){ zLine = realloc(zLine, nLine); shell_check_oom(zLine); } - if( str_line_get(&zLine[n], nLine - n, pInSrc)==0 ){ + if( strLineGet(&zLine[n], nLine - n, pInSrc)==0 ){ if( n==0 ){ free(zLine); return 0; @@ -1174,7 +1176,7 @@ struct EQPGraph { /* By default, omit the extension options that are not done yet. */ #ifndef SHELL_OMIT_EXTENSIONS -# define SHELL_OMIT_EXTENSIONS 6 +# define SHELL_OMIT_EXTENSIONS 4 #endif /* Selectively omit features with one PP variable. Value is true iff @@ -1186,35 +1188,35 @@ struct EQPGraph { #define SHEXT_PARSING_BIT 0 #define SHELL_EXTENDED_PARSING \ NOT_IFDEF_BIT(SHELL_OMIT_EXTENSIONS, SHEXT_PARSING_BIT) -/* Whether build will include dynamic dot-command extension */ -#define SHEXT_DYNCMDS_BIT 1 -#define SHELL_DYNAMIC_COMMANDS \ - NOT_IFDEF_BIT(SHELL_OMIT_EXTENSIONS, SHEXT_DYNCMDS_BIT) +/* Whether build will include runtime extension via .load -extension */ +#define SHEXT_DYNEXT_BIT 1 +#define SHELL_DYNAMIC_EXTENSION ( !defined(SQLITE_OMIT_LOAD_EXTENSION) \ + && NOT_IFDEF_BIT(SHELL_OMIT_EXTENSIONS, SHEXT_DYNEXT_BIT) ) /* Whether build will include expansion of variables in dot-commands */ #define SHEXT_VAREXP_BIT 2 #define SHELL_VARIABLE_EXPANSION \ NOT_IFDEF_BIT(SHELL_OMIT_EXTENSIONS, SHEXT_VAREXP_BIT) #define SHELL_ALL_EXTENSIONS \ - (1<bExtendedDotCmds&(1<bExtendedDotCmds&(1<bExtendedDotCmds&(1<bExtendedDotCmds&(1<pSIS +#define XSS(psi) (psi)->pSXS +#define DBI(psi) (psi)->pSXS->dbUser +#define DBX(psx) (psx)->dbUser + +typedef struct ShellInState { + /* sqlite3 *db; user database moved to ShellExState.dbUser */ int openFlags; /* Additional flags to open. (SQLITE_OPEN_NOFOLLOW) */ u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */ + sqlite3_int64 szMax; /* --maxsize argument to .open */ u8 autoExplain; /* Automatically turn on .explain mode */ u8 autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */ u8 autoEQPtest; /* autoEQP is in test mode */ @@ -1276,22 +1299,27 @@ struct ShellState { /* Output mode state-keep for certain ad-hoc save/restore ops: */ u8 cMode; /* temporary output mode for the current query */ u8 normalMode; /* Output mode before ".explain on" */ +#if 0 /* moved to ShellExState and renamed */ int *colWidth; /* Requested width of each column in columnar modes */ int *actualWidth; /* Actual width of each column */ int nWidth; /* Number of slots in colWidth[] and actualWidth[] */ +#endif char nullValue[20]; /* Text to print for NULL retrieved from database */ unsigned statsOn; /* True to display memory stats before each finalize */ unsigned mEqpLines; /* Mask of veritical lines in the EQP output graph */ int outCount; /* Revert to stdout when reaching zero */ +#if 0 /* moved to ShellExState as resultCount */ int cnt; /* Number of records displayed so far */ - +#endif int inputNesting; /* Track nesting level of .read and other redirects */ InSource *pInSource; /* Read commands and SQL from this stream source */ FILE *out; /* Write results here */ FILE *traceOut; /* Output for sqlite3_trace() */ +#if 0 /* moved to ShellExState */ int abruptExit; /* Flag for immediate shell exit, exit code */ +#endif int nErr; /* Number of errors seen */ int writableSchema; /* True if PRAGMA writable_schema=ON */ int nCheck; /* Number of ".check" commands run */ @@ -1299,8 +1327,6 @@ struct ShellState { unsigned mxProgress; /* Maximum progress callbacks before failing */ unsigned flgProgress; /* Flags for the progress callback */ - sqlite3_int64 szMax; /* --maxsize argument to .open */ - char *zDestTable; /* Name of destination table when MODE_Insert */ char *zTempFile; /* Temporary file that might need deleting */ char *zEditor; /* Name of editor if non-zero, then deletable */ char zTestcase[30]; /* Name of current test case */ @@ -1323,7 +1349,8 @@ struct ShellState { char *zNonce; /* Nonce for temporary safe-mode suspension */ EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */ ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */ -}; + ShellExState *pSXS; /* Pointer to companion, exposed shell state */ +} ShellInState; /* ** Limit input nesting via .read or any other input redirect. @@ -1340,28 +1367,28 @@ struct ShellState { ** equal 1 => Safe mode is and will remain active. ** N > 1 => Safe mode is suspended for N-1 operations. */ -static void updateSafeMode(ShellState *pSS){ - switch( pSS->bSafeModeFuture ){ +static void updateSafeMode(ShellInState *psi){ + switch( psi->bSafeModeFuture ){ case 2: default: - --pSS->bSafeModeFuture; + --psi->bSafeModeFuture; /* Fall thru, suspension is in effect. */ case 0: - pSS->bSafeMode = 0; + psi->bSafeMode = 0; break; case 1: - pSS->bSafeMode = 1; + psi->bSafeMode = 1; } } -/* Allowed values for ShellState.autoEQP +/* Allowed values for ShellInState.autoEQP */ #define AUTOEQP_off 0 /* Automatic EXPLAIN QUERY PLAN is off */ #define AUTOEQP_on 1 /* Automatic EQP is on */ #define AUTOEQP_trigger 2 /* On and also show plans for triggers */ #define AUTOEQP_full 3 /* Show full EXPLAIN */ -/* Allowed values for ShellState.openMode +/* Allowed values for ShellInState.openMode */ #define SHELL_OPEN_UNSPEC 0 /* No open-mode specified */ #define SHELL_OPEN_NORMAL 1 /* Normal database file */ @@ -1371,13 +1398,13 @@ static void updateSafeMode(ShellState *pSS){ #define SHELL_OPEN_DESERIALIZE 5 /* Open using sqlite3_deserialize() */ #define SHELL_OPEN_HEXDB 6 /* Use "dbtotxt" output as data source */ -/* Allowed values for ShellState.eTraceType +/* Allowed values for ShellInState.eTraceType */ #define SHELL_TRACE_PLAIN 0 /* Show input SQL text */ #define SHELL_TRACE_EXPANDED 1 /* Show expanded SQL text */ #define SHELL_TRACE_NORMALIZED 2 /* Show normalized SQL text */ -/* Bits in the ShellState.flgProgress variable */ +/* Bits in the ShellInState.flgProgress variable */ #define SHELL_PROGRESS_QUIET 0x01 /* Omit announcing every progress callback */ #define SHELL_PROGRESS_RESET 0x02 /* Reset the count when the progres ** callback limit is reached, and for each @@ -1401,9 +1428,9 @@ static void updateSafeMode(ShellState *pSS){ /* ** Macros for testing and setting shellFlgs */ -#define ShellHasFlag(P,X) (((P)->shellFlgs & (X))!=0) -#define ShellSetFlag(P,X) ((P)->shellFlgs|=(X)) -#define ShellClearFlag(P,X) ((P)->shellFlgs&=(~(X))) +#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))) /* ** These are the allowed modes, in search order (for abbreviation matches.) @@ -1428,7 +1455,7 @@ static void updateSafeMode(ShellState *pSS){ #define MODE_Explain 17 /* Like MODE_Column, but do not truncate data */ #define MODE_Pretty 18 /* Pretty-print schemas */ #define MODE_Semi 19 /* Same as MODE_List but append ";" to each line */ -#define MODE_COUNT_OF 20 /* also an invalid MODE_x value */ +#define MODE_COUNT_OF 20 /* also a known invalid MODE_x value */ /* Note that some of above ordering is assumed for this mode classification. */ #define MODE_IS_COLUMNAR(m) \ @@ -1479,10 +1506,10 @@ static struct { ** A callback for the sqlite3_log() interface. */ static void shellLog(void *pArg, int iErrCode, const char *zMsg){ - ShellState *p = (ShellState*)pArg; - if( p->pLog==0 ) return; - utf8_printf(p->pLog, "(%d) %s\n", iErrCode, zMsg); - fflush(p->pLog); + ShellInState *psi = (ShellInState*)pArg; + if( psi->pLog==0 ) return; + utf8_printf(psi->pLog, "(%d) %s\n", iErrCode, zMsg); + fflush(psi->pLog); } /* @@ -1496,9 +1523,9 @@ static void shellPutsFunc( int nVal, sqlite3_value **apVal ){ - ShellState *p = (ShellState*)sqlite3_user_data(pCtx); + ShellExState *psx = (ShellExState*)sqlite3_user_data(pCtx); (void)nVal; - utf8_printf(p->out, "%s\n", sqlite3_value_text(apVal[0])); + utf8_printf(ISS(psx)->out, "%s\n", sqlite3_value_text(apVal[0])); sqlite3_result_value(pCtx, apVal[0]); } @@ -1515,19 +1542,19 @@ static void shellPutsFunc( ** The return is true if failing, 0 otherwise. */ static int failIfSafeMode( - ShellState *p, + ShellExState *psx, const char *zErrMsg, ... ){ - if( p->bSafeMode==1 ){ + if( ISS(psx)->bSafeMode==1 ){ va_list ap; char *zMsg; va_start(ap, zErrMsg); zMsg = sqlite3_vmprintf(zErrMsg, ap); va_end(ap); - raw_printf(STD_ERR, "line %d: ", p->pInSource->lineno); + raw_printf(STD_ERR, "line %d: ", ISS(psx)->pInSource->lineno); utf8_printf(STD_ERR, "%s\n", zMsg); - p->abruptExit = 3; + psx->shellAbruptExit = 3; return 1; } return 0; @@ -1693,7 +1720,7 @@ static struct { size_t size; } outputModeCopy[] = { #define SS_MEMBER_COPY(mn) \ - { MEMBER_OFFSET(ShellState,mn), MEMBER_SIZEOF(ShellState,mn) } + { MEMBER_OFFSET(ShellInState,mn), MEMBER_SIZEOF(ShellInState,mn) } SS_MEMBER_COPY(showHeader), SS_MEMBER_COPY(shellFlgs), SS_MEMBER_COPY(mode), SS_MEMBER_COPY(cmOpts), SS_MEMBER_COPY(colSeparator), SS_MEMBER_COPY(rowSeparator) @@ -1701,7 +1728,7 @@ static struct { }; /* Allocate a buffer, copy requested output mode data to it, and return it. */ -static OutputModeSave *outputModeSave(ShellState *p, SaveWhatMode w){ +static OutputModeSave *outputModeSave(ShellInState *psi, SaveWhatMode w){ u16 what = (u16)w; int i, nAlloc = sizeof(what)+1, mask = 1; char *pSaved = 0, *pFill; @@ -1716,7 +1743,7 @@ static OutputModeSave *outputModeSave(ShellState *p, SaveWhatMode w){ for( mask=1, i=0; isizeof(what))? *((u16 *)pSaved) : 0; int i, nAlloc = sizeof(what)+1, mask = 1; @@ -1736,7 +1763,7 @@ static void outputModeRestore(ShellState *p, OutputModeSave *pSaved){ for( i=0; inSavedModesnSavedModes>=MODE_STACK_MAX ) return; - pOMS = outputModeSave(p, w); + assert(psi->nSavedModesnSavedModes>=MODE_STACK_MAX ) return; + pOMS = outputModeSave(psi, w); shell_check_oom(pOMS); - p->pModeStack[p->nSavedModes++] = pOMS; + psi->pModeStack[psi->nSavedModes++] = pOMS; } -static void outputModePush(ShellState *p){ - outputModePushSome(p, SWM_everything); +static void outputModePush(ShellInState *psi){ + outputModePushSome(psi, SWM_everything); } -static void outputModePop(ShellState *p){ +static void outputModePop(ShellInState *p){ OutputModeSave *pOMS; assert(p->nSavedModes>0); /* Should not be here unless something pushed. */ if( p->nSavedModes==0 ) return; @@ -2031,10 +2058,11 @@ static const char needCsvQuote[] = { ** the null value. Strings are quoted if necessary. The separator ** is only issued if bSep is true. */ -static void output_csv(ShellState *p, const char *z, int bSep){ - FILE *out = p->out; +static void output_csv(ShellExState *psx, const char *z, int bSep){ + FILE *out = ISS(psx)->out; + char *zColSep = psx->zFieldSeparator; if( z==0 ){ - utf8_printf(out,"%s",p->nullValue); + utf8_printf(out,"%s",psx->zNullValue); }else{ unsigned i; for(i=0; z[i]; i++){ @@ -2043,7 +2071,7 @@ static void output_csv(ShellState *p, const char *z, int bSep){ break; } } - if( i==0 || strstr(z, p->colSeparator)!=0 ){ + if( i==0 || strstr(z, zColSep)!=0 ){ char *zQuoted = sqlite3_mprintf("\"%w\"", z); shell_check_oom(zQuoted); utf8_printf(out, "%s", zQuoted); @@ -2053,7 +2081,7 @@ static void output_csv(ShellState *p, const char *z, int bSep){ } } if( bSep ){ - utf8_printf(p->out, "%s", p->colSeparator); + utf8_printf(out, "%s", zColSep); } } @@ -2102,7 +2130,7 @@ static int safeModeAuth( const char *zA3, const char *zA4 ){ - ShellState *p = (ShellState*)pClientData; + ShellExState *psx = (ShellExState*)pClientData; static const char *azProhibitedFunctions[] = { "edit", "fts3_tokenizer", @@ -2117,7 +2145,7 @@ static int safeModeAuth( UNUSED_PARAMETER(zA4); switch( op ){ case SQLITE_ATTACH: { - if ( failIfSafeMode(p, "cannot run ATTACH in safe mode") ) + if ( failIfSafeMode(psx, "cannot run ATTACH in safe mode") ) return SQLITE_ERROR; break; } @@ -2125,7 +2153,7 @@ static int safeModeAuth( int i; for(i=0; iout, "authorizer: %s", azAction[op]); + utf8_printf(psi->out, "authorizer: %s", azAction[op]); for(i=0; i<4; i++){ - raw_printf(p->out, " "); + raw_printf(psi->out, " "); if( az[i] ){ - output_c_string(p->out, az[i]); + output_c_string(psi->out, az[i]); }else{ - raw_printf(p->out, "NULL"); + raw_printf(psi->out, "NULL"); } } - raw_printf(p->out, "\n"); - if( p->bSafeMode ) (void)safeModeAuth(pClientData, op, zA1, zA2, zA3, zA4); + raw_printf(psi->out, "\n"); + if( psi->bSafeMode ) (void)safeModeAuth(pClientData, op, zA1, zA2, zA3, zA4); return SQLITE_OK; } #endif @@ -2223,11 +2252,12 @@ static int wsToEol(const char *z){ /* ** Add a new entry to the EXPLAIN QUERY PLAN data */ -static void eqp_append(ShellState *p, int iEqpId, int p2, const char *zText){ +static void eqp_append(ShellInState *psi, int iEqpId, int p2, + const char *zText){ EQPGraphRow *pNew; int nText = strlen30(zText); - if( p->autoEQPtest ){ - utf8_printf(p->out, "%d,%d,%s\n", iEqpId, p2, zText); + if( psi->autoEQPtest ){ + utf8_printf(psi->out, "%d,%d,%s\n", iEqpId, p2, zText); } pNew = sqlite3_malloc64( sizeof(*pNew) + nText ); shell_check_oom(pNew); @@ -2235,32 +2265,33 @@ static void eqp_append(ShellState *p, int iEqpId, int p2, const char *zText){ pNew->iParentId = p2; memcpy(pNew->zText, zText, nText+1); pNew->pNext = 0; - if( p->sGraph.pLast ){ - p->sGraph.pLast->pNext = pNew; + if( psi->sGraph.pLast ){ + psi->sGraph.pLast->pNext = pNew; }else{ - p->sGraph.pRow = pNew; + psi->sGraph.pRow = pNew; } - p->sGraph.pLast = pNew; + psi->sGraph.pLast = pNew; } /* ** Free and reset the EXPLAIN QUERY PLAN data that has been collected ** in p->sGraph. */ -static void eqp_reset(ShellState *p){ +static void eqp_reset(ShellInState *psi){ EQPGraphRow *pRow, *pNext; - for(pRow = p->sGraph.pRow; pRow; pRow = pNext){ + for(pRow = psi->sGraph.pRow; pRow; pRow = pNext){ pNext = pRow->pNext; sqlite3_free(pRow); } - memset(&p->sGraph, 0, sizeof(p->sGraph)); + memset(&psi->sGraph, 0, sizeof(psi->sGraph)); } /* Return the next EXPLAIN QUERY PLAN line with iEqpId that occurs after ** pOld, or return the first such line if pOld is NULL */ -static EQPGraphRow *eqp_next_row(ShellState *p, int iEqpId, EQPGraphRow *pOld){ - EQPGraphRow *pRow = pOld ? pOld->pNext : p->sGraph.pRow; +static EQPGraphRow *eqp_next_row(ShellInState *psi, int iEqpId, + EQPGraphRow *pOld){ + EQPGraphRow *pRow = pOld ? pOld->pNext : psi->sGraph.pRow; while( pRow && pRow->iParentId!=iEqpId ) pRow = pRow->pNext; return pRow; } @@ -2268,19 +2299,19 @@ static EQPGraphRow *eqp_next_row(ShellState *p, int iEqpId, EQPGraphRow *pOld){ /* Render a single level of the graph that has iEqpId as its parent. Called ** recursively to render sublevels. */ -static void eqp_render_level(ShellState *p, int iEqpId){ +static void eqp_render_level(ShellInState *psi, int iEqpId){ EQPGraphRow *pRow, *pNext; - int n = strlen30(p->sGraph.zPrefix); + int n = strlen30(psi->sGraph.zPrefix); char *z; - for(pRow = eqp_next_row(p, iEqpId, 0); pRow; pRow = pNext){ - pNext = eqp_next_row(p, iEqpId, pRow); + for(pRow = eqp_next_row(psi, iEqpId, 0); pRow; pRow = pNext){ + pNext = eqp_next_row(psi, iEqpId, pRow); z = pRow->zText; - utf8_printf(p->out, "%s%s%s\n", p->sGraph.zPrefix, + utf8_printf(psi->out, "%s%s%s\n", psi->sGraph.zPrefix, pNext ? "|--" : "`--", z); - if( n<(int)sizeof(p->sGraph.zPrefix)-7 ){ - memcpy(&p->sGraph.zPrefix[n], pNext ? "| " : " ", 4); - eqp_render_level(p, pRow->iEqpId); - p->sGraph.zPrefix[n] = 0; + if( n<(int)sizeof(psi->sGraph.zPrefix)-7 ){ + memcpy(&psi->sGraph.zPrefix[n], pNext ? "| " : " ", 4); + eqp_render_level(psi, pRow->iEqpId); + psi->sGraph.zPrefix[n] = 0; } } } @@ -2288,23 +2319,23 @@ static void eqp_render_level(ShellState *p, int iEqpId){ /* ** Display and reset the EXPLAIN QUERY PLAN data */ -static void eqp_render(ShellState *p){ - EQPGraphRow *pRow = p->sGraph.pRow; +static void eqp_render(ShellInState *psi){ + EQPGraphRow *pRow = psi->sGraph.pRow; if( pRow ){ if( pRow->zText[0]=='-' ){ if( pRow->pNext==0 ){ - eqp_reset(p); + eqp_reset(psi); return; } - utf8_printf(p->out, "%s\n", pRow->zText+3); - p->sGraph.pRow = pRow->pNext; + utf8_printf(psi->out, "%s\n", pRow->zText+3); + psi->sGraph.pRow = pRow->pNext; sqlite3_free(pRow); }else{ - utf8_printf(p->out, "QUERY PLAN\n"); + utf8_printf(psi->out, "QUERY PLAN\n"); } - p->sGraph.zPrefix[0] = 0; - eqp_render_level(p, 0); - eqp_reset(p); + psi->sGraph.zPrefix[0] = 0; + eqp_render_level(psi, 0); + eqp_reset(psi); } } @@ -2313,16 +2344,16 @@ static void eqp_render(ShellState *p){ ** Progress handler callback. */ static int progress_handler(void *pClientData) { - ShellState *p = (ShellState*)pClientData; - p->nProgress++; - if( p->nProgress>=p->mxProgress && p->mxProgress>0 ){ - raw_printf(p->out, "Progress limit reached (%u)\n", p->nProgress); - if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; - if( p->flgProgress & SHELL_PROGRESS_ONCE ) p->mxProgress = 0; + ShellInState *psi = (ShellInState*)pClientData; + psi->nProgress++; + if( psi->nProgress>=psi->mxProgress && psi->mxProgress>0 ){ + raw_printf(psi->out, "Progress limit reached (%u)\n", psi->nProgress); + if( psi->flgProgress & SHELL_PROGRESS_RESET ) psi->nProgress = 0; + if( psi->flgProgress & SHELL_PROGRESS_ONCE ) psi->mxProgress = 0; return 1; } - if( (p->flgProgress & SHELL_PROGRESS_QUIET)==0 ){ - raw_printf(p->out, "Progress %u\n", p->nProgress); + if( (psi->flgProgress & SHELL_PROGRESS_QUIET)==0 ){ + raw_printf(psi->out, "Progress %u\n", psi->nProgress); } return 0; } @@ -2353,21 +2384,21 @@ static void print_dashes(FILE *out, int N){ ** Print a markdown or table-style row separator using ascii-art */ static void print_row_separator( - ShellState *p, + ShellExState *psx, int nArg, const char *zSep ){ int i; if( nArg>0 ){ - fputs(zSep, p->out); - print_dashes(p->out, p->actualWidth[0]+2); + fputs(zSep, ISS(psx)->out); + print_dashes(ISS(psx)->out, psx->pHaveWidths[0]+2); for(i=1; iout); - print_dashes(p->out, p->actualWidth[i]+2); + fputs(zSep, ISS(psx)->out); + print_dashes(ISS(psx)->out, psx->pHaveWidths[i]+2); } - fputs(zSep, p->out); + fputs(zSep, ISS(psx)->out); } - fputs("\n", p->out); + fputs("\n", ISS(psx)->out); } /* @@ -2382,10 +2413,12 @@ static int shell_callback( int *aiType /* Column types. Might be NULL */ ){ int i; - ShellState *p = (ShellState*)pArg; + ShellExState *psx = (ShellExState*)pArg; + ShellInState *psi = ISS(psx); + FILE *out = psi->out; if( azArg==0 ) return 0; - switch( p->cMode ){ + switch( psi->cMode ){ case MODE_Count: case MODE_Off: { break; @@ -2397,10 +2430,10 @@ static int shell_callback( int len = strlen30(azCol[i] ? azCol[i] : ""); if( len>w ) w = len; } - if( p->cnt++>0 ) utf8_printf(p->out, "%s", p->rowSeparator); + if( psx->resultCount++>0 ) utf8_printf(out, "%s", psi->rowSeparator); for(i=0; iout,"%*s = %s%s", w, azCol[i], - azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); + utf8_printf(out,"%*s = %s%s", w, azCol[i], + azArg[i] ? azArg[i] : psi->nullValue, psi->rowSeparator); } break; } @@ -2409,16 +2442,16 @@ static int shell_callback( if( nArg>ArraySize(aExplainWidth) ){ nArg = ArraySize(aExplainWidth); } - if( p->cnt++==0 ){ + if( psx->resultCount++==0 ){ for(i=0; iout, w, azCol[i]); - fputs(i==nArg-1 ? "\n" : " ", p->out); + utf8_width_print(out, w, azCol[i]); + fputs(i==nArg-1 ? "\n" : " ", out); } for(i=0; iout, w); - fputs(i==nArg-1 ? "\n" : " ", p->out); + print_dashes(out, w); + fputs(i==nArg-1 ? "\n" : " ", out); } } if( azArg==0 ) break; @@ -2428,19 +2461,19 @@ static int shell_callback( if( azArg[i] && strlenChar(azArg[i])>w ){ w = strlenChar(azArg[i]); } - if( i==1 && p->aiIndent && p->pStmt ){ - if( p->iIndentnIndent ){ - utf8_printf(p->out, "%*.s", p->aiIndent[p->iIndent], ""); + if( i==1 && psi->aiIndent && psi->pStmt ){ + if( psi->iIndentnIndent ){ + utf8_printf(out, "%*.s", psi->aiIndent[psi->iIndent], ""); } - p->iIndent++; + psi->iIndent++; } - utf8_width_print(p->out, w, azArg[i] ? azArg[i] : p->nullValue); - fputs(i==nArg-1 ? "\n" : " ", p->out); + utf8_width_print(out, w, azArg[i] ? azArg[i] : psi->nullValue); + fputs(i==nArg-1 ? "\n" : " ", out); } break; } case MODE_Semi: { /* .schema and .fullschema output */ - printSchemaLine(p->out, azArg[0], ";\n"); + printSchemaLine(out, azArg[0], ";\n"); break; } case MODE_Pretty: { /* .schema and .fullschema with --indent */ @@ -2455,7 +2488,7 @@ static int shell_callback( if( sqlite3_strlike("CREATE VIEW%", azArg[0], 0)==0 || sqlite3_strlike("CREATE TRIG%", azArg[0], 0)==0 ){ - utf8_printf(p->out, "%s;\n", azArg[0]); + utf8_printf(out, "%s;\n", azArg[0]); break; } z = sqlite3_mprintf("%s", azArg[0]); @@ -2488,7 +2521,7 @@ static int shell_callback( }else if( c==')' ){ nParen--; if( nLine>0 && nParen==0 && j>0 ){ - printSchemaLineN(p->out, z, j, "\n"); + printSchemaLineN(out, z, j, "\n"); j = 0; } } @@ -2497,7 +2530,7 @@ static int shell_callback( && (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1))) ){ if( c=='\n' ) j--; - printSchemaLineN(p->out, z, j, "\n "); + printSchemaLineN(out, z, j, "\n "); j = 0; nLine++; while( IsSpace(z[i+1]) ){ i++; } @@ -2505,123 +2538,123 @@ static int shell_callback( } z[j] = 0; } - printSchemaLine(p->out, z, ";\n"); + printSchemaLine(out, z, ";\n"); sqlite3_free(z); break; } case MODE_List: { - if( p->cnt++==0 && p->showHeader ){ + if( psx->resultCount++==0 && psi->showHeader ){ for(i=0; iout,"%s%s",azCol[i], - i==nArg-1 ? p->rowSeparator : p->colSeparator); + utf8_printf(out,"%s%s",azCol[i], + i==nArg-1 ? psi->rowSeparator : psi->colSeparator); } } if( azArg==0 ) break; for(i=0; inullValue; - utf8_printf(p->out, "%s", z); + if( z==0 ) z = psi->nullValue; + utf8_printf(out, "%s", z); if( iout, "%s", p->colSeparator); + utf8_printf(out, "%s", psi->colSeparator); }else{ - utf8_printf(p->out, "%s", p->rowSeparator); + utf8_printf(out, "%s", psi->rowSeparator); } } break; } case MODE_Html: { - if( p->cnt++==0 && p->showHeader ){ - raw_printf(p->out,""); + if( psx->resultCount++==0 && psi->showHeader ){ + raw_printf(out,""); for(i=0; iout,""); - output_html_string(p->out, azCol[i]); - raw_printf(p->out,"\n"); + raw_printf(out,""); + output_html_string(out, azCol[i]); + raw_printf(out,"\n"); } - raw_printf(p->out,"\n"); + raw_printf(out,"\n"); } if( azArg==0 ) break; - raw_printf(p->out,""); + raw_printf(out,""); for(i=0; iout,""); - output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue); - raw_printf(p->out,"\n"); + raw_printf(out,""); + output_html_string(out, azArg[i] ? azArg[i] : psi->nullValue); + raw_printf(out,"\n"); } - raw_printf(p->out,"\n"); + raw_printf(out,"\n"); break; } case MODE_Tcl: { - if( p->cnt++==0 && p->showHeader ){ + if( psx->resultCount++==0 && psi->showHeader ){ for(i=0; iout,azCol[i] ? azCol[i] : ""); - if(iout, "%s", p->colSeparator); + output_c_string(out,azCol[i] ? azCol[i] : ""); + if(icolSeparator); } - utf8_printf(p->out, "%s", p->rowSeparator); + utf8_printf(out, "%s", psi->rowSeparator); } if( azArg==0 ) break; for(i=0; iout, azArg[i] ? azArg[i] : p->nullValue); - if(iout, "%s", p->colSeparator); + output_c_string(out, azArg[i] ? azArg[i] : psi->nullValue); + if(icolSeparator); } - utf8_printf(p->out, "%s", p->rowSeparator); + utf8_printf(out, "%s", psi->rowSeparator); break; } case MODE_Csv: { - setBinaryMode(p->out, 1); - if( p->cnt++==0 && p->showHeader ){ + setBinaryMode(out, 1); + if( psx->resultCount++==0 && psi->showHeader ){ for(i=0; iout, "%s", p->rowSeparator); + utf8_printf(out, "%s", psi->rowSeparator); } if( nArg>0 ){ for(i=0; iout, "%s", p->rowSeparator); + utf8_printf(out, "%s", psi->rowSeparator); } - setTextMode(p->out, 1); + setTextMode(out, 1); break; } case MODE_Insert: { if( azArg==0 ) break; - utf8_printf(p->out,"INSERT INTO %s",p->zDestTable); - if( p->showHeader ){ - raw_printf(p->out,"("); + utf8_printf(out,"INSERT INTO %s",psx->zDestTable); + if( psi->showHeader ){ + raw_printf(out,"("); for(i=0; i0 ) raw_printf(p->out, ","); + if( i>0 ) raw_printf(out, ","); if( quoteChar(azCol[i]) ){ char *z = sqlite3_mprintf("\"%w\"", azCol[i]); shell_check_oom(z); - utf8_printf(p->out, "%s", z); + utf8_printf(out, "%s", z); sqlite3_free(z); }else{ - raw_printf(p->out, "%s", azCol[i]); + raw_printf(out, "%s", azCol[i]); } } - raw_printf(p->out,")"); + raw_printf(out,")"); } - p->cnt++; + psx->resultCount++; for(i=0; iout, i>0 ? "," : " VALUES("); + raw_printf(out, i>0 ? "," : " VALUES("); if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ - utf8_printf(p->out,"NULL"); + utf8_printf(out,"NULL"); }else if( aiType && aiType[i]==SQLITE_TEXT ){ - if( ShellHasFlag(p, SHFLG_Newlines) ){ - output_quoted_string(p->out, azArg[i]); + if( ShellHasFlag(psx, SHFLG_Newlines) ){ + output_quoted_string(out, azArg[i]); }else{ - output_quoted_escaped_string(p->out, azArg[i]); + output_quoted_escaped_string(out, azArg[i]); } }else if( aiType && aiType[i]==SQLITE_INTEGER ){ - utf8_printf(p->out,"%s", azArg[i]); + utf8_printf(out,"%s", azArg[i]); }else if( aiType && aiType[i]==SQLITE_FLOAT ){ char z[50]; - double r = sqlite3_column_double(p->pStmt, i); + double r = sqlite3_column_double(psi->pStmt, i); sqlite3_uint64 ur; memcpy(&ur,&r,sizeof(r)); if( ur==0x7ff0000000000000LL ){ - raw_printf(p->out, "1e999"); + raw_printf(out, "1e999"); }else if( ur==0xfff0000000000000LL ){ - raw_printf(p->out, "-1e999"); + raw_printf(out, "-1e999"); }else{ sqlite3_int64 ir = (sqlite3_int64)r; if( r==(double)ir ){ @@ -2629,119 +2662,119 @@ static int shell_callback( }else{ sqlite3_snprintf(50,z,"%!.20g", r); } - raw_printf(p->out, "%s", z); + raw_printf(out, "%s", z); } - }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ - const void *pBlob = sqlite3_column_blob(p->pStmt, i); - int nBlob = sqlite3_column_bytes(p->pStmt, i); - output_hex_blob(p->out, pBlob, nBlob); + }else if( aiType && aiType[i]==SQLITE_BLOB && psi->pStmt ){ + const void *pBlob = sqlite3_column_blob(psi->pStmt, i); + int nBlob = sqlite3_column_bytes(psi->pStmt, i); + output_hex_blob(out, pBlob, nBlob); }else if( isNumber(azArg[i], 0) ){ - utf8_printf(p->out,"%s", azArg[i]); - }else if( ShellHasFlag(p, SHFLG_Newlines) ){ - output_quoted_string(p->out, azArg[i]); + utf8_printf(out,"%s", azArg[i]); + }else if( ShellHasFlag(psx, SHFLG_Newlines) ){ + output_quoted_string(out, azArg[i]); }else{ - output_quoted_escaped_string(p->out, azArg[i]); + output_quoted_escaped_string(out, azArg[i]); } } - raw_printf(p->out,");\n"); + raw_printf(out,");\n"); break; } case MODE_Json: { if( azArg==0 ) break; - if( p->cnt==0 ){ - fputs("[{", p->out); + if( psx->resultCount==0 ){ + fputs("[{", out); }else{ - fputs(",\n{", p->out); + fputs(",\n{", out); } - p->cnt++; + psx->resultCount++; for(i=0; iout, azCol[i], -1); - putc(':', p->out); + output_json_string(out, azCol[i], -1); + putc(':', out); if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ - fputs("null",p->out); + fputs("null",out); }else if( aiType && aiType[i]==SQLITE_FLOAT ){ char z[50]; - double r = sqlite3_column_double(p->pStmt, i); + double r = sqlite3_column_double(psi->pStmt, i); sqlite3_uint64 ur; memcpy(&ur,&r,sizeof(r)); if( ur==0x7ff0000000000000LL ){ - raw_printf(p->out, "1e999"); + raw_printf(out, "1e999"); }else if( ur==0xfff0000000000000LL ){ - raw_printf(p->out, "-1e999"); + raw_printf(out, "-1e999"); }else{ sqlite3_snprintf(50,z,"%!.20g", r); - raw_printf(p->out, "%s", z); + raw_printf(out, "%s", z); } - }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ - const void *pBlob = sqlite3_column_blob(p->pStmt, i); - int nBlob = sqlite3_column_bytes(p->pStmt, i); - output_json_string(p->out, pBlob, nBlob); + }else if( aiType && aiType[i]==SQLITE_BLOB && psi->pStmt ){ + const void *pBlob = sqlite3_column_blob(psi->pStmt, i); + int nBlob = sqlite3_column_bytes(psi->pStmt, i); + output_json_string(out, pBlob, nBlob); }else if( aiType && aiType[i]==SQLITE_TEXT ){ - output_json_string(p->out, azArg[i], -1); + output_json_string(out, azArg[i], -1); }else{ - utf8_printf(p->out,"%s", azArg[i]); + utf8_printf(out,"%s", azArg[i]); } if( iout); + putc(',', out); } } - putc('}', p->out); + putc('}', out); break; } case MODE_Quote: { if( azArg==0 ) break; - if( p->cnt==0 && p->showHeader ){ + if( psx->resultCount==0 && psi->showHeader ){ for(i=0; i0 ) fputs(p->colSeparator, p->out); - output_quoted_string(p->out, azCol[i]); + if( i>0 ) fputs(psi->colSeparator, out); + output_quoted_string(out, azCol[i]); } - fputs(p->rowSeparator, p->out); + fputs(psi->rowSeparator, out); } - p->cnt++; + psx->resultCount++; for(i=0; i0 ) fputs(p->colSeparator, p->out); + if( i>0 ) fputs(psi->colSeparator, out); if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ - utf8_printf(p->out,"NULL"); + utf8_printf(out,"NULL"); }else if( aiType && aiType[i]==SQLITE_TEXT ){ - output_quoted_string(p->out, azArg[i]); + output_quoted_string(out, azArg[i]); }else if( aiType && aiType[i]==SQLITE_INTEGER ){ - utf8_printf(p->out,"%s", azArg[i]); + utf8_printf(out,"%s", azArg[i]); }else if( aiType && aiType[i]==SQLITE_FLOAT ){ char z[50]; - double r = sqlite3_column_double(p->pStmt, i); + double r = sqlite3_column_double(psi->pStmt, i); sqlite3_snprintf(50,z,"%!.20g", r); - raw_printf(p->out, "%s", z); - }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ - const void *pBlob = sqlite3_column_blob(p->pStmt, i); - int nBlob = sqlite3_column_bytes(p->pStmt, i); - output_hex_blob(p->out, pBlob, nBlob); + raw_printf(out, "%s", z); + }else if( aiType && aiType[i]==SQLITE_BLOB && psi->pStmt ){ + const void *pBlob = sqlite3_column_blob(psi->pStmt, i); + int nBlob = sqlite3_column_bytes(psi->pStmt, i); + output_hex_blob(out, pBlob, nBlob); }else if( isNumber(azArg[i], 0) ){ - utf8_printf(p->out,"%s", azArg[i]); + utf8_printf(out,"%s", azArg[i]); }else{ - output_quoted_string(p->out, azArg[i]); + output_quoted_string(out, azArg[i]); } } - fputs(p->rowSeparator, p->out); + fputs(psi->rowSeparator, out); break; } case MODE_Ascii: { - if( p->cnt++==0 && p->showHeader ){ + if( psx->resultCount++==0 && psi->showHeader ){ for(i=0; i0 ) utf8_printf(p->out, "%s", p->colSeparator); - utf8_printf(p->out,"%s",azCol[i] ? azCol[i] : ""); + if( i>0 ) utf8_printf(out, "%s", psi->colSeparator); + utf8_printf(out,"%s",azCol[i] ? azCol[i] : ""); } - utf8_printf(p->out, "%s", p->rowSeparator); + utf8_printf(out, "%s", psi->rowSeparator); } if( azArg==0 ) break; for(i=0; i0 ) utf8_printf(p->out, "%s", p->colSeparator); - utf8_printf(p->out,"%s",azArg[i] ? azArg[i] : p->nullValue); + if( i>0 ) utf8_printf(out, "%s", psi->colSeparator); + utf8_printf(out,"%s",azArg[i] ? azArg[i] : psi->nullValue); } - utf8_printf(p->out, "%s", p->rowSeparator); + utf8_printf(out, "%s", psi->rowSeparator); break; } case MODE_EQP: { - eqp_append(p, atoi(azArg[0]), atoi(azArg[1]), azArg[3]); + eqp_append(psi, atoi(azArg[0]), atoi(azArg[1]), azArg[3]); break; } } @@ -2777,9 +2810,9 @@ static int captureOutputCallback(void *pArg, int nArg, char **azArg, char **az){ /* ** Generate an appropriate SELFTEST table in the main database. */ -static void createSelftestTable(ShellState *p){ +static void createSelftestTable(ShellInState *p){ char *zErrMsg = 0; - sqlite3_exec(p->db, + sqlite3_exec(DBI(p), "SAVEPOINT selftest_init;\n" "CREATE TABLE IF NOT EXISTS selftest(\n" " tno INTEGER PRIMARY KEY,\n" /* Test number */ @@ -2819,29 +2852,29 @@ static void createSelftestTable(ShellState *p){ utf8_printf(STD_ERR, "SELFTEST initialization failure: %s\n", zErrMsg); sqlite3_free(zErrMsg); } - sqlite3_exec(p->db, "RELEASE selftest_init",0,0,0); + sqlite3_exec(DBI(p), "RELEASE selftest_init",0,0,0); } /* -** Set the destination table field of the ShellState structure to +** Set the destination table field of the shell state structure to ** the name of the table given. Escape any quote characters in the ** table name. */ -static void set_table_name(ShellState *p, const char *zName){ +static void set_table_name(ShellExState *psx, const char *zName){ int i, n; char cQuote; char *z; - if( p->zDestTable ){ - free(p->zDestTable); - p->zDestTable = 0; + if( psx->zDestTable ){ + free((void *)(psx->zDestTable)); + psx->zDestTable = 0; } if( zName==0 ) return; cQuote = quoteChar(zName); n = strlen30(zName); if( cQuote ) n += n+2; - z = p->zDestTable = malloc( n+1 ); + psx->zDestTable = (z = malloc( n+1 )); shell_check_oom(z); n = 0; if( cQuote ) z[n++] = cQuote; @@ -2909,7 +2942,7 @@ static char *shell_error_context(const char *zSql, sqlite3 *db){ ** won't consume the semicolon terminator. */ static int run_table_dump_query( - ShellState *p, /* Query context */ + ShellInState *psi, /* Query context */ const char *zSelect /* SELECT statement to extract content */ ){ sqlite3_stmt *pSelect; @@ -2917,37 +2950,37 @@ static int run_table_dump_query( int nResult; int i; const char *z; - rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0); + rc = sqlite3_prepare_v2(DBI(psi), zSelect, -1, &pSelect, 0); if( rc!=SQLITE_OK || !pSelect ){ - char *zContext = shell_error_context(zSelect, p->db); - utf8_printf(p->out, "/**** ERROR: (%d) %s *****/\n%s", rc, - sqlite3_errmsg(p->db), zContext); + char *zContext = shell_error_context(zSelect, DBI(psi)); + utf8_printf(psi->out, "/**** ERROR: (%d) %s *****/\n%s", rc, + sqlite3_errmsg(DBI(psi)), zContext); sqlite3_free(zContext); - if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; + if( (rc&0xff)!=SQLITE_CORRUPT ) psi->nErr++; return rc; } rc = sqlite3_step(pSelect); nResult = sqlite3_column_count(pSelect); while( rc==SQLITE_ROW ){ z = (const char*)sqlite3_column_text(pSelect, 0); - utf8_printf(p->out, "%s", z); + utf8_printf(psi->out, "%s", z); for(i=1; iout, ",%s", sqlite3_column_text(pSelect, i)); + utf8_printf(psi->out, ",%s", sqlite3_column_text(pSelect, i)); } if( z==0 ) z = ""; while( z[0] && (z[0]!='-' || z[1]!='-') ) z++; if( z[0] ){ - raw_printf(p->out, "\n;\n"); + raw_printf(psi->out, "\n;\n"); }else{ - raw_printf(p->out, ";\n"); + raw_printf(psi->out, ";\n"); } rc = sqlite3_step(pSelect); } rc = sqlite3_finalize(pSelect); if( rc!=SQLITE_OK ){ - utf8_printf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, - sqlite3_errmsg(p->db)); - if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; + utf8_printf(psi->out, "/**** ERROR: (%d) %s *****/\n", rc, + sqlite3_errmsg(DBI(psi))); + if( (rc&0xff)!=SQLITE_CORRUPT ) psi->nErr++; } return rc; } @@ -3019,7 +3052,7 @@ static void displayLinuxIoStats(FILE *out){ ** Display a single line of status using 64-bit values. */ static void displayStatLine( - ShellState *p, /* The shell context */ + ShellInState *psi, /* The shell context (internal) */ char *zLabel, /* Label for this one line */ char *zFormat, /* Format for the result */ int iStatusCtrl, /* Which status to display */ @@ -3038,7 +3071,7 @@ static void displayStatLine( }else{ sqlite3_snprintf(sizeof(zLine), zLine, zFormat, iHiwtr); } - raw_printf(p->out, "%-36s %s\n", zLabel, zLine); + raw_printf(psi->out, "%-36s %s\n", zLabel, zLine); } /* @@ -3046,18 +3079,18 @@ static void displayStatLine( */ static int display_stats( sqlite3 *db, /* Database to query */ - ShellState *pArg, /* Pointer to ShellState */ + ShellInState *psi, /* Pointer to shell internal state */ int bReset /* True to reset the stats */ ){ int iCur; int iHiwtr; FILE *out; - if( pArg==0 || pArg->out==0 ) return 0; - out = pArg->out; + if( psi==0 || psi->out==0 ) return 0; + out = psi->out; - if( pArg->pStmt && pArg->statsOn==2 ){ + if( psi->pStmt && psi->statsOn==2 ){ int nCol, i, x; - sqlite3_stmt *pStmt = pArg->pStmt; + sqlite3_stmt *pStmt = psi->pStmt; char z[100]; nCol = sqlite3_column_count(pStmt); raw_printf(out, "%-36s %d\n", "Number of output columns:", nCol); @@ -3079,107 +3112,107 @@ static int display_stats( } } - if( pArg->statsOn==3 ){ - if( pArg->pStmt ){ - iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); - raw_printf(pArg->out, "VM-steps: %d\n", iCur); + if( psi->statsOn==3 ){ + if( psi->pStmt ){ + iCur = sqlite3_stmt_status(psi->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); + raw_printf(psi->out, "VM-steps: %d\n", iCur); } return 0; } - displayStatLine(pArg, "Memory Used:", + displayStatLine(psi, "Memory Used:", "%lld (max %lld) bytes", SQLITE_STATUS_MEMORY_USED, bReset); - displayStatLine(pArg, "Number of Outstanding Allocations:", + displayStatLine(psi, "Number of Outstanding Allocations:", "%lld (max %lld)", SQLITE_STATUS_MALLOC_COUNT, bReset); - if( pArg->shellFlgs & SHFLG_Pagecache ){ - displayStatLine(pArg, "Number of Pcache Pages Used:", + if( psi->shellFlgs & SHFLG_Pagecache ){ + displayStatLine(psi, "Number of Pcache Pages Used:", "%lld (max %lld) pages", SQLITE_STATUS_PAGECACHE_USED, bReset); } - displayStatLine(pArg, "Number of Pcache Overflow Bytes:", + displayStatLine(psi, "Number of Pcache Overflow Bytes:", "%lld (max %lld) bytes", SQLITE_STATUS_PAGECACHE_OVERFLOW, bReset); - displayStatLine(pArg, "Largest Allocation:", + displayStatLine(psi, "Largest Allocation:", "%lld bytes", SQLITE_STATUS_MALLOC_SIZE, bReset); - displayStatLine(pArg, "Largest Pcache Allocation:", + displayStatLine(psi, "Largest Pcache Allocation:", "%lld bytes", SQLITE_STATUS_PAGECACHE_SIZE, bReset); #ifdef YYTRACKMAXSTACKDEPTH - displayStatLine(pArg, "Deepest Parser Stack:", + displayStatLine(psi, "Deepest Parser Stack:", "%lld (max %lld)", SQLITE_STATUS_PARSER_STACK, bReset); #endif if( db ){ - if( pArg->shellFlgs & SHFLG_Lookaside ){ + if( psi->shellFlgs & SHFLG_Lookaside ){ iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, + raw_printf(psi->out, "Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr); sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Successful lookaside attempts: %d\n", + raw_printf(psi->out, "Successful lookaside attempts: %d\n", iHiwtr); sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Lookaside failures due to size: %d\n", + raw_printf(psi->out, "Lookaside failures due to size: %d\n", iHiwtr); sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Lookaside failures due to OOM: %d\n", + raw_printf(psi->out, "Lookaside failures due to OOM: %d\n", iHiwtr); } iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Pager Heap Usage: %d bytes\n", + raw_printf(psi->out, "Pager Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1); - raw_printf(pArg->out, "Page cache hits: %d\n", iCur); + raw_printf(psi->out, "Page cache hits: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1); - raw_printf(pArg->out, "Page cache misses: %d\n", iCur); + raw_printf(psi->out, "Page cache misses: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1); - raw_printf(pArg->out, "Page cache writes: %d\n", iCur); + raw_printf(psi->out, "Page cache writes: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_SPILL, &iCur, &iHiwtr, 1); - raw_printf(pArg->out, "Page cache spills: %d\n", iCur); + raw_printf(psi->out, "Page cache spills: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Schema Heap Usage: %d bytes\n", + raw_printf(psi->out, "Schema Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Statement Heap/Lookaside Usage: %d bytes\n", + raw_printf(psi->out, "Statement Heap/Lookaside Usage: %d bytes\n", iCur); } - if( pArg->pStmt ){ + if( psi->pStmt ){ int iHit, iMiss; - iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, + iCur = sqlite3_stmt_status(psi->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, bReset); - raw_printf(pArg->out, "Fullscan Steps: %d\n", iCur); - iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); - raw_printf(pArg->out, "Sort Operations: %d\n", iCur); - iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset); - raw_printf(pArg->out, "Autoindex Inserts: %d\n", iCur); - iHit = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_HIT, bReset); - iMiss = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_MISS, bReset); + raw_printf(psi->out, "Fullscan Steps: %d\n", iCur); + iCur = sqlite3_stmt_status(psi->pStmt, SQLITE_STMTSTATUS_SORT, bReset); + raw_printf(psi->out, "Sort Operations: %d\n", iCur); + iCur = sqlite3_stmt_status(psi->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset); + raw_printf(psi->out, "Autoindex Inserts: %d\n", iCur); + iHit = sqlite3_stmt_status(psi->pStmt, SQLITE_STMTSTATUS_FILTER_HIT, bReset); + iMiss = sqlite3_stmt_status(psi->pStmt, SQLITE_STMTSTATUS_FILTER_MISS, bReset); if( iHit || iMiss ){ - raw_printf(pArg->out, "Bloom filter bypass taken: %d/%d\n", + raw_printf(psi->out, "Bloom filter bypass taken: %d/%d\n", iHit, iHit+iMiss); } - iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); - raw_printf(pArg->out, "Virtual Machine Steps: %d\n", iCur); - iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_REPREPARE,bReset); - raw_printf(pArg->out, "Reprepare operations: %d\n", iCur); - iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_RUN, bReset); - raw_printf(pArg->out, "Number of times run: %d\n", iCur); - iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_MEMUSED, bReset); - raw_printf(pArg->out, "Memory used by prepared stmt: %d\n", iCur); + iCur = sqlite3_stmt_status(psi->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); + raw_printf(psi->out, "Virtual Machine Steps: %d\n", iCur); + iCur = sqlite3_stmt_status(psi->pStmt, SQLITE_STMTSTATUS_REPREPARE,bReset); + raw_printf(psi->out, "Reprepare operations: %d\n", iCur); + iCur = sqlite3_stmt_status(psi->pStmt, SQLITE_STMTSTATUS_RUN, bReset); + raw_printf(psi->out, "Number of times run: %d\n", iCur); + iCur = sqlite3_stmt_status(psi->pStmt, SQLITE_STMTSTATUS_MEMUSED, bReset); + raw_printf(psi->out, "Memory used by prepared stmt: %d\n", iCur); } #ifdef __linux__ - displayLinuxIoStats(pArg->out); + displayLinuxIoStats(psi->out); #endif /* Do not remove this machine readable comment: extra-stats-output-here */ @@ -3190,21 +3223,17 @@ static int display_stats( /* ** Display scan stats. */ -static void display_scanstats( - sqlite3 *db, /* Database to query */ - ShellState *pArg /* Pointer to ShellState */ -){ +static void display_scanstats(ShellInState *psi){ #ifndef SQLITE_ENABLE_STMT_SCANSTATUS - UNUSED_PARAMETER(db); - UNUSED_PARAMETER(pArg); + UNUSED_PARAMETER(psi); #else int i, k, n, mx; - raw_printf(pArg->out, "-------- scanstats --------\n"); + raw_printf(psi->out, "-------- scanstats --------\n"); mx = 0; for(k=0; k<=mx; k++){ double rEstLoop = 1.0; for(i=n=0; 1; i++){ - sqlite3_stmt *p = pArg->pStmt; + sqlite3_stmt *p = psi->pStmt; sqlite3_int64 nLoop, nVisit; double rEst; int iSid; @@ -3217,21 +3246,21 @@ static void display_scanstats( if( iSid!=k ) continue; if( n==0 ){ rEstLoop = (double)nLoop; - if( k>0 ) raw_printf(pArg->out, "-------- subquery %d -------\n", k); + if( k>0 ) raw_printf(psi->out, "-------- subquery %d -------\n", k); } n++; sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_NVISIT, (void*)&nVisit); sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_EST, (void*)&rEst); sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_EXPLAIN, (void*)&zExplain); - utf8_printf(pArg->out, "Loop %2d: %s\n", n, zExplain); + utf8_printf(psi->out, "Loop %2d: %s\n", n, zExplain); rEstLoop *= rEst; - raw_printf(pArg->out, + raw_printf(psi->out, " nLoop=%-8lld nRow=%-8lld estRow=%-8lld estRow/Loop=%-8g\n", nLoop, nVisit, (sqlite3_int64)(rEstLoop+0.5), rEst ); } } - raw_printf(pArg->out, "---------------------------\n"); + raw_printf(psi->out, "---------------------------\n"); #endif } @@ -3251,7 +3280,7 @@ static int str_in_array(const char *zStr, const char **azArray){ /* ** If compiled statement pSql appears to be an EXPLAIN statement, allocate -** and populate the ShellState.aiIndent[] array with the number of +** and populate the ShellInState.aiIndent[] array with the number of ** spaces each opcode should be indented before it is output. ** ** The indenting rules are: @@ -3267,7 +3296,7 @@ static int str_in_array(const char *zStr, const char **azArray){ ** then indent all opcodes between the earlier instruction ** and "Goto" by 2 spaces. */ -static void explain_data_prepare(ShellState *p, sqlite3_stmt *pSql){ +static void explain_data_prepare(ShellInState *psi, sqlite3_stmt *pSql){ const char *zSql; /* The text of the SQL statement */ const char *z; /* Used to check if this is an EXPLAIN */ int *abYield = 0; /* True if op is an OP_Yield */ @@ -3282,14 +3311,14 @@ static void explain_data_prepare(ShellState *p, sqlite3_stmt *pSql){ /* Try to figure out if this is really an EXPLAIN statement. If this ** cannot be verified, return early. */ if( sqlite3_column_count(pSql)!=8 ){ - p->cMode = p->mode; + psi->cMode = psi->mode; return; } zSql = sqlite3_sql(pSql); if( zSql==0 ) return; for(z=zSql; *z==' ' || *z=='\t' || *z=='\n' || *z=='\f' || *z=='\r'; z++); if( sqlite3_strnicmp(z, "explain", 7) ){ - p->cMode = p->mode; + psi->cMode = psi->mode; return; } @@ -3316,33 +3345,34 @@ static void explain_data_prepare(ShellState *p, sqlite3_stmt *pSql){ int jj; for(jj=0; jjcMode = p->mode; + psi->cMode = psi->mode; sqlite3_reset(pSql); return; } } } nAlloc += 100; - p->aiIndent = (int*)sqlite3_realloc64(p->aiIndent, nAlloc*sizeof(int)); - shell_check_oom(p->aiIndent); + psi->aiIndent = + (int*)sqlite3_realloc64(psi->aiIndent, nAlloc*sizeof(int)); + shell_check_oom(psi->aiIndent); abYield = (int*)sqlite3_realloc64(abYield, nAlloc*sizeof(int)); shell_check_oom(abYield); } abYield[iOp] = str_in_array(zOp, azYield); - p->aiIndent[iOp] = 0; - p->nIndent = iOp+1; + psi->aiIndent[iOp] = 0; + psi->nIndent = iOp+1; if( str_in_array(zOp, azNext) ){ - for(i=p2op; iaiIndent[i] += 2; + for(i=p2op; iaiIndent[i] += 2; } - if( str_in_array(zOp, azGoto) && p2opnIndent + if( str_in_array(zOp, azGoto) && p2opnIndent && (abYield[p2op] || sqlite3_column_int(pSql, 2)) ){ - for(i=p2op; iaiIndent[i] += 2; + for(i=p2op; iaiIndent[i] += 2; } } - p->iIndent = 0; + psi->iIndent = 0; sqlite3_free(abYield); sqlite3_reset(pSql); } @@ -3350,11 +3380,11 @@ static void explain_data_prepare(ShellState *p, sqlite3_stmt *pSql){ /* ** Free the array allocated by explain_data_prepare(). */ -static void explain_data_delete(ShellState *p){ - sqlite3_free(p->aiIndent); - p->aiIndent = 0; - p->nIndent = 0; - p->iIndent = 0; +static void explain_data_delete(ShellInState *psi){ + sqlite3_free(psi->aiIndent); + psi->aiIndent = 0; + psi->nIndent = 0; + psi->iIndent = 0; } /* @@ -3424,16 +3454,16 @@ static ParamTableUse classify_param_name( const char *zName ){ #define PARAM_STORE_SNAME PARAM_STORE_SCHEMA"."PARAM_STORE_NAME /* Create the TEMP table used to store parameter bindings and SQL statements */ -static void param_table_init(ShellState *p){ - DbProtectState dps = allow_sys_schema_change(p->db); - sqlite3_exec(p->db, +static void param_table_init(sqlite3 *db){ + DbProtectState dps = allow_sys_schema_change(db); + sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS "PARAM_TABLE_SNAME"(\n" " key TEXT PRIMARY KEY,\n" " value,\n" " uses INT DEFAULT (0)" /* aka PTU_Binding */ ") WITHOUT ROWID;", 0, 0, 0); - restore_sys_schema_protection( p->db, &dps ); + restore_sys_schema_protection( db, &dps ); } /* Tell whether the above-created table exists, return true iff exists. */ @@ -3530,23 +3560,24 @@ static void print_box_line(FILE *out, int N){ ** Draw a horizontal separator for a MODE_Box table. */ static void print_box_row_separator( - ShellState *p, + ShellExState *psx, int nArg, const char *zSep1, const char *zSep2, const char *zSep3 ){ int i; + FILE *out = ISS(psx)->out; if( nArg>0 ){ - utf8_printf(p->out, "%s", zSep1); - print_box_line(p->out, p->actualWidth[0]+2); + utf8_printf(out, "%s", zSep1); + print_box_line(out, psx->pHaveWidths[0]+2); for(i=1; iout, "%s", zSep2); - print_box_line(p->out, p->actualWidth[i]+2); + utf8_printf(out, "%s", zSep2); + print_box_line(out, psx->pHaveWidths[i]+2); } - utf8_printf(p->out, "%s", zSep3); + utf8_printf(out, "%s", zSep3); } - fputs("\n", p->out); + fputs("\n", out); } /* @@ -3689,9 +3720,10 @@ static char *quoted_column(sqlite3_stmt *pStmt, int i){ ** any output. */ static void exec_prepared_stmt_columnar( - ShellState *p, /* Pointer to ShellState */ + ShellExState *psx, /* Pointer to shell state */ sqlite3_stmt *pStmt /* Statment to run */ ){ + ShellInState *psi = ISS(psx); sqlite3_int64 nRow = 0; int nColumn = 0; char **azData = 0; @@ -3708,7 +3740,7 @@ static void exec_prepared_stmt_columnar( const unsigned char **azNextLine = 0; int bNextLine = 0; int bMultiLineRowExists = 0; - int bw = p->cmOpts.bWordWrap; + int bw = psi->cmOpts.bWordWrap; rc = sqlite3_step(pStmt); if( rc!=SQLITE_ROW ) return; @@ -3720,31 +3752,31 @@ static void exec_prepared_stmt_columnar( azNextLine = sqlite3_malloc64( nColumn*sizeof(char*) ); shell_check_oom((void*)azNextLine); memset((void*)azNextLine, 0, nColumn*sizeof(char*) ); - if( p->cmOpts.bQuote ){ + if( psi->cmOpts.bQuote ){ azQuoted = sqlite3_malloc64( nColumn*sizeof(char*) ); shell_check_oom(azQuoted); memset(azQuoted, 0, nColumn*sizeof(char*) ); } abRowDiv = sqlite3_malloc64( nAlloc/nColumn ); shell_check_oom(abRowDiv); - if( nColumn>p->nWidth ){ - p->colWidth = realloc(p->colWidth, (nColumn+1)*2*sizeof(int)); - shell_check_oom(p->colWidth); - for(i=p->nWidth; icolWidth[i] = 0; - p->nWidth = nColumn; - p->actualWidth = &p->colWidth[nColumn]; - } - memset(p->actualWidth, 0, nColumn*sizeof(int)); + if( nColumn>psx->numWidths ){ + psx->pSpecWidths = realloc(psx->pSpecWidths, (nColumn+1)*2*sizeof(int)); + shell_check_oom(psx->pSpecWidths); + for(i=psx->numWidths; ipSpecWidths[i] = 0; + psx->numWidths = nColumn; + psx->pHaveWidths = &psx->pSpecWidths[nColumn]; + } + memset(psx->pHaveWidths, 0, nColumn*sizeof(int)); for(i=0; icolWidth[i]; + w = psx->pSpecWidths[i]; if( w<0 ) w = -w; - p->actualWidth[i] = w; + psx->pHaveWidths[i] = w; } for(i=0; icolWidth[i]; + int wx = psx->pSpecWidths[i]; if( wx==0 ){ - wx = p->cmOpts.iWrap; + wx = psi->cmOpts.iWrap; } if( wx<0 ) wx = -wx; uz = (const unsigned char*)sqlite3_column_name(pStmt,i); @@ -3763,14 +3795,14 @@ static void exec_prepared_stmt_columnar( abRowDiv[nRow] = 1; nRow++; for(i=0; icolWidth[i]; + int wx = psx->pSpecWidths[i]; if( wx==0 ){ - wx = p->cmOpts.iWrap; + wx = psi->cmOpts.iWrap; } if( wx<0 ) wx = -wx; if( useNextLine ){ uz = azNextLine[i]; - }else if( p->cmOpts.bQuote ){ + }else if( psi->cmOpts.bQuote ){ sqlite3_free(azQuoted[i]); azQuoted[i] = quoted_column(pStmt,i); uz = (const unsigned char*)azQuoted[i]; @@ -3789,27 +3821,27 @@ static void exec_prepared_stmt_columnar( nTotal = nColumn*(nRow+1); for(i=0; inullValue; + if( z==0 ) z = psi->nullValue; n = strlenChar(z); j = i%nColumn; - if( n>p->actualWidth[j] ) p->actualWidth[j] = n; + if( n>psx->pHaveWidths[j] ) psx->pHaveWidths[j] = n; } if( seenInterrupt ) goto columnar_end; if( nColumn==0 ) goto columnar_end; - switch( p->cMode ){ + switch( psi->cMode ){ case MODE_Column: { colSep = " "; rowSep = "\n"; - if( p->showHeader ){ + if( psi->showHeader ){ for(i=0; iactualWidth[i]; - if( p->colWidth[i]<0 ) w = -w; - utf8_width_print(p->out, w, azData[i]); - fputs(i==nColumn-1?"\n":" ", p->out); + w = psx->pHaveWidths[i]; + if( psx->pSpecWidths[i]<0 ) w = -w; + utf8_width_print(psi->out, w, azData[i]); + fputs(i==nColumn-1?"\n":" ", psi->out); } for(i=0; iout, p->actualWidth[i]); - fputs(i==nColumn-1?"\n":" ", p->out); + print_dashes(psi->out, psx->pHaveWidths[i]); + fputs(i==nColumn-1?"\n":" ", psi->out); } } break; @@ -3817,80 +3849,82 @@ static void exec_prepared_stmt_columnar( case MODE_Table: { colSep = " | "; rowSep = " |\n"; - print_row_separator(p, nColumn, "+"); - fputs("| ", p->out); + print_row_separator(psx, nColumn, "+"); + fputs("| ", psi->out); for(i=0; iactualWidth[i]; + w = psx->pHaveWidths[i]; n = strlenChar(azData[i]); - utf8_printf(p->out, "%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); - fputs(i==nColumn-1?" |\n":" | ", p->out); + utf8_printf(psi->out, "%*s%s%*s", + (w-n)/2, "", azData[i], (w-n+1)/2, ""); + fputs(i==nColumn-1?" |\n":" | ", psi->out); } - print_row_separator(p, nColumn, "+"); + print_row_separator(psx, nColumn, "+"); break; } case MODE_Markdown: { colSep = " | "; rowSep = " |\n"; - fputs("| ", p->out); + fputs("| ", psi->out); for(i=0; iactualWidth[i]; + w = psx->pHaveWidths[i]; n = strlenChar(azData[i]); - utf8_printf(p->out, "%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); - fputs(i==nColumn-1?" |\n":" | ", p->out); + utf8_printf(psi->out, "%*s%s%*s", + (w-n)/2, "", azData[i], (w-n+1)/2, ""); + fputs(i==nColumn-1?" |\n":" | ", psi->out); } - print_row_separator(p, nColumn, "|"); + print_row_separator(psx, nColumn, "|"); break; } case MODE_Box: { colSep = " " BOX_13 " "; rowSep = " " BOX_13 "\n"; - print_box_row_separator(p, nColumn, BOX_23, BOX_234, BOX_34); - utf8_printf(p->out, BOX_13 " "); + print_box_row_separator(psx, nColumn, BOX_23, BOX_234, BOX_34); + utf8_printf(psi->out, BOX_13 " "); for(i=0; iactualWidth[i]; + w = psx->pHaveWidths[i]; n = strlenChar(azData[i]); - utf8_printf(p->out, "%*s%s%*s%s", + utf8_printf(psi->out, "%*s%s%*s%s", (w-n)/2, "", azData[i], (w-n+1)/2, "", i==nColumn-1?" "BOX_13"\n":" "BOX_13" "); } - print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134); + print_box_row_separator(psx, nColumn, BOX_123, BOX_1234, BOX_134); break; } } for(i=nColumn, j=0; icMode!=MODE_Column ){ - utf8_printf(p->out, "%s", p->cMode==MODE_Box?BOX_13" ":"| "); + if( j==0 && psi->cMode!=MODE_Column ){ + utf8_printf(psi->out, "%s", psi->cMode==MODE_Box?BOX_13" ":"| "); } z = azData[i]; - if( z==0 ) z = p->nullValue; - w = p->actualWidth[j]; - if( p->colWidth[j]<0 ) w = -w; - utf8_width_print(p->out, w, z); + if( z==0 ) z = psi->nullValue; + w = psx->pHaveWidths[j]; + if( psx->pSpecWidths[j]<0 ) w = -w; + utf8_width_print(psi->out, w, z); if( j==nColumn-1 ){ - utf8_printf(p->out, "%s", rowSep); + utf8_printf(psi->out, "%s", rowSep); if( bMultiLineRowExists && abRowDiv[i/nColumn-1] && i+1cMode==MODE_Table ){ - print_row_separator(p, nColumn, "+"); - }else if( p->cMode==MODE_Box ){ - print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134); - }else if( p->cMode==MODE_Column ){ - raw_printf(p->out, "\n"); + if( psi->cMode==MODE_Table ){ + print_row_separator(psx, nColumn, "+"); + }else if( psi->cMode==MODE_Box ){ + print_box_row_separator(psx, nColumn, BOX_123, BOX_1234, BOX_134); + }else if( psi->cMode==MODE_Column ){ + raw_printf(psi->out, "\n"); } } j = -1; if( seenInterrupt ) goto columnar_end; }else{ - utf8_printf(p->out, "%s", colSep); + utf8_printf(psi->out, "%s", colSep); } } - if( p->cMode==MODE_Table ){ - print_row_separator(p, nColumn, "+"); - }else if( p->cMode==MODE_Box ){ - print_box_row_separator(p, nColumn, BOX_12, BOX_124, BOX_14); + if( psi->cMode==MODE_Table ){ + print_row_separator(psx, nColumn, "+"); + }else if( psi->cMode==MODE_Box ){ + print_box_row_separator(psx, nColumn, BOX_12, BOX_124, BOX_14); } columnar_end: if( seenInterrupt ){ - utf8_printf(p->out, "Interrupt\n"); + utf8_printf(psi->out, "Interrupt\n"); } nData = (nRow+1)*nColumn; for(i=0; icMode) ){ + if( MODE_IS_COLUMNAR(psi->cMode) ){ exec_prepared_stmt_columnar(pArg, pStmt); return; } @@ -3945,7 +3980,7 @@ static void exec_prepared_stmt( for(i=0; icMode==MODE_Insert || pArg->cMode==MODE_Quote) ){ + && (psi->cMode==MODE_Insert || psi->cMode==MODE_Quote) ){ azVals[i] = ""; }else{ azVals[i] = (char*)sqlite3_column_text(pStmt, i); @@ -3967,10 +4002,10 @@ static void exec_prepared_stmt( } } while( SQLITE_ROW == rc ); sqlite3_free(pData); - if( pArg->cMode==MODE_Json ){ - fputs("]\n", pArg->out); - }else if( pArg->cMode==MODE_Count ){ - printf("%llu row%s\n", nRow, nRow!=1 ? "s" : ""); + if( psi->cMode==MODE_Json ){ + fputs("]\n", psi->out); + }else if( psi->cMode==MODE_Count ){ + utf8_printf(psi->out, "%llu row%s\n", nRow, nRow!=1 ? "s" : ""); } } } @@ -3988,13 +4023,13 @@ static void exec_prepared_stmt( ** caller to eventually free this buffer using sqlite3_free(). */ static int expertHandleSQL( - ShellState *pState, + ShellInState *psi, const char *zSql, char **pzErr ){ - assert( pState->expert.pExpert ); + assert( psi->expert.pExpert ); assert( pzErr==0 || *pzErr==0 ); - return sqlite3_expert_sql(pState->expert.pExpert, zSql, pzErr); + return sqlite3_expert_sql(psi->expert.pExpert, zSql, pzErr); } /* @@ -4008,17 +4043,17 @@ static int expertHandleSQL( ** caller to eventually free this buffer using sqlite3_free(). */ static int expertFinish( - ShellState *pState, + ShellInState *psi, int bCancel, char **pzErr ){ int rc = SQLITE_OK; - sqlite3expert *p = pState->expert.pExpert; + sqlite3expert *p = psi->expert.pExpert; assert( p ); assert( bCancel || pzErr==0 || *pzErr==0 ); if( bCancel==0 ){ - FILE *out = pState->out; - int bVerbose = pState->expert.bVerbose; + FILE *out = psi->out; + int bVerbose = psi->expert.bVerbose; rc = sqlite3_expert_analyze(p, pzErr); if( rc==SQLITE_OK ){ @@ -4045,7 +4080,7 @@ static int expertFinish( } } sqlite3_expert_destroy(p); - pState->expert.pExpert = 0; + psi->expert.pExpert = 0; return rc; } @@ -4053,7 +4088,7 @@ static int expertFinish( ** Implementation of ".expert" dot command. */ static int expertDotCommand( - ShellState *pState, /* Current shell tool state */ + ShellInState *psi, /* Current shell tool state */ char **azArg, /* Array of arguments passed to dot command */ int nArg /* Number of entries in azArg[] */ ){ @@ -4062,14 +4097,14 @@ static int expertDotCommand( int i; int iSample = 0; - if( pState->bSafeMode ){ + if( psi->bSafeMode ){ raw_printf(STD_ERR, "Cannot run experimental commands such as \"%s\" in safe mode\n", azArg[0]); return 1; } - assert( pState->expert.pExpert==0 ); - memset(&pState->expert, 0, sizeof(ExpertInfo)); + assert( psi->expert.pExpert==0 ); + memset(&psi->expert, 0, sizeof(ExpertInfo)); for(i=1; rc==SQLITE_OK && i=2 && 0==strncmp(z, "-verbose", n) ){ - pState->expert.bVerbose = 1; + psi->expert.bVerbose = 1; } else if( n>=2 && 0==strncmp(z, "-sample", n) ){ if( i==(nArg-1) ){ @@ -4098,14 +4133,14 @@ static int expertDotCommand( } if( rc==SQLITE_OK ){ - pState->expert.pExpert = sqlite3_expert_new(pState->db, &zErr); - if( pState->expert.pExpert==0 ){ + psi->expert.pExpert = sqlite3_expert_new(DBI(psi), &zErr); + if( psi->expert.pExpert==0 ){ raw_printf(STD_ERR, "sqlite3_expert_new: %s\n", zErr ? zErr : "out of memory"); rc = SQLITE_ERROR; }else{ sqlite3_expert_config( - pState->expert.pExpert, EXPERT_CONFIG_SAMPLE, iSample + psi->expert.pExpert, EXPERT_CONFIG_SAMPLE, iSample ); } } @@ -4119,8 +4154,8 @@ static int expertDotCommand( ** not about to dumped as a no-op. Someday, a tracing facility may enhance ** this function's output to show where the input group has originated. */ -static void echo_group_input(ShellState *p, const char *zDo){ - if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zDo); +static void echo_group_input(ShellInState *psi, const char *zDo){ + if( (psi->shellFlgs & SHFLG_Echo)!=0 ) printf("%s\n", zDo); } /* @@ -4133,24 +4168,25 @@ static void echo_group_input(ShellState *p, const char *zDo){ ** and callback data argument. */ static int shell_exec( - ShellState *pArg, /* Pointer to ShellState */ + ShellExState *psx, /* Pointer to shell state */ const char *zSql, /* SQL to be evaluated */ char **pzErrMsg /* Error msg written here */ ){ + ShellInState *psi = ISS(psx); sqlite3_stmt *pStmt = NULL; /* Statement to execute. */ int rc = SQLITE_OK; /* Return Code */ int rc2; const char *zLeftover; /* Tail of unprocessed SQL */ - sqlite3 *db = pArg->db; + sqlite3 *db = DBX(psx); if( pzErrMsg ){ *pzErrMsg = NULL; } #ifndef SQLITE_OMIT_VIRTUALTABLE - if( pArg->expert.pExpert ){ - rc = expertHandleSQL(pArg, zSql, pzErrMsg); - return expertFinish(pArg, (rc!=SQLITE_OK), pzErrMsg); + if( psi->expert.pExpert ){ + rc = expertHandleSQL(psi, zSql, pzErrMsg); + return expertFinish(psi, (rc!=SQLITE_OK), pzErrMsg); } #endif @@ -4172,22 +4208,22 @@ static int shell_exec( else zStmtSql = skipWhite(zStmtSql); /* save off the prepared statment handle and reset row count */ - if( pArg ){ - pArg->pStmt = pStmt; - pArg->cnt = 0; + if( psx ){ + psi->pStmt = pStmt; + psx->resultCount = 0; } /* echo the sql statement if echo on */ - if( pArg ) echo_group_input( pArg, zStmtSql ? zStmtSql : zSql); + if( psx ) echo_group_input( psi, zStmtSql ? zStmtSql : zSql); /* Show the EXPLAIN QUERY PLAN if .eqp is on */ - if( pArg && pArg->autoEQP && sqlite3_stmt_isexplain(pStmt)==0 ){ + if( psx && psi->autoEQP && sqlite3_stmt_isexplain(pStmt)==0 ){ sqlite3_stmt *pExplain; char *zEQP; int triggerEQP = 0; disable_debug_trace_modes(); sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, -1, &triggerEQP); - if( pArg->autoEQP>=AUTOEQP_trigger ){ + if( psi->autoEQP>=AUTOEQP_trigger ){ sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 1, 0); } zEQP = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", zStmtSql); @@ -4199,68 +4235,68 @@ static int shell_exec( int iEqpId = sqlite3_column_int(pExplain, 0); int iParentId = sqlite3_column_int(pExplain, 1); if( zEQPLine==0 ) zEQPLine = ""; - if( zEQPLine[0]=='-' ) eqp_render(pArg); - eqp_append(pArg, iEqpId, iParentId, zEQPLine); + if( zEQPLine[0]=='-' ) eqp_render(psi); + eqp_append(psi, iEqpId, iParentId, zEQPLine); } - eqp_render(pArg); + eqp_render(psi); } sqlite3_finalize(pExplain); sqlite3_free(zEQP); - if( pArg->autoEQP>=AUTOEQP_full ){ + if( psi->autoEQP>=AUTOEQP_full ){ /* Also do an EXPLAIN for ".eqp full" mode */ zEQP = sqlite3_mprintf("EXPLAIN %s", zStmtSql); shell_check_oom(zEQP); rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0); if( rc==SQLITE_OK ){ - pArg->cMode = MODE_Explain; - explain_data_prepare(pArg, pExplain); - exec_prepared_stmt(pArg, pExplain); - explain_data_delete(pArg); + psi->cMode = MODE_Explain; + explain_data_prepare(psi, pExplain); + exec_prepared_stmt(psx, pExplain); + explain_data_delete(psi); } sqlite3_finalize(pExplain); sqlite3_free(zEQP); } - if( pArg->autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){ + if( psi->autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){ sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 0, 0); /* Reprepare pStmt before reactiving trace modes */ sqlite3_finalize(pStmt); sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); - if( pArg ) pArg->pStmt = pStmt; + if( psx ) psi->pStmt = pStmt; } restore_debug_trace_modes(); } - if( pArg ){ - pArg->cMode = pArg->mode; - if( pArg->autoExplain ){ + if( psx ){ + psi->cMode = psi->mode; + if( psi->autoExplain ){ if( sqlite3_stmt_isexplain(pStmt)==1 ){ - pArg->cMode = MODE_Explain; + psi->cMode = MODE_Explain; } if( sqlite3_stmt_isexplain(pStmt)==2 ){ - pArg->cMode = MODE_EQP; + psi->cMode = MODE_EQP; } } /* If the shell is currently in ".explain" mode, gather the extra ** data required to add indents to the output.*/ - if( pArg->cMode==MODE_Explain ){ - explain_data_prepare(pArg, pStmt); + if( psi->cMode==MODE_Explain ){ + explain_data_prepare(psi, pStmt); } } - bind_prepared_stmt(pArg->db, pStmt); - exec_prepared_stmt(pArg, pStmt); - explain_data_delete(pArg); - eqp_render(pArg); + bind_prepared_stmt(DBX(psx), pStmt); + exec_prepared_stmt(psx, pStmt); + explain_data_delete(psi); + eqp_render(psi); /* print usage stats if stats on */ - if( pArg && pArg->statsOn ){ - display_stats(db, pArg, 0); + if( psx && psi->statsOn ){ + display_stats(db, psi, 0); } /* print loop-counters if required */ - if( pArg && pArg->scanstatsOn ){ - display_scanstats(db, pArg); + if( psx && psi->scanstatsOn ){ + display_scanstats(psi); } /* Finalize the statement just executed. If this fails, save a @@ -4275,8 +4311,8 @@ static int shell_exec( } /* clear saved stmt handle */ - if( pArg ){ - pArg->pStmt = NULL; + if( psx ){ + psi->pStmt = NULL; } } } /* end while */ @@ -4298,7 +4334,7 @@ static void freeColumnList(char **azCol){ /* ** Return a list of pointers to strings which are the names of all -** columns in table zTab. The memory to hold the names is dynamically +** columns in table zTab. The memory to hold the names is dynamically ** allocated and must be released by the caller using a subsequent call ** to freeColumnList(). ** @@ -4309,7 +4345,7 @@ static void freeColumnList(char **azCol){ ** The first regular column in the table is azCol[1]. The list is terminated ** by an entry with azCol[i]==0. */ -static char **tableColumnList(ShellState *p, const char *zTab){ +static char **tableColumnList(sqlite3 *db, const char *zTab, int preserveRowid){ char **azCol = 0; sqlite3_stmt *pStmt; char *zSql; @@ -4317,12 +4353,11 @@ static char **tableColumnList(ShellState *p, const char *zTab){ int nAlloc = 0; int nPK = 0; /* Number of PRIMARY KEY columns seen */ int isIPK = 0; /* True if one PRIMARY KEY column of type INTEGER */ - int preserveRowid = ShellHasFlag(p, SHFLG_PreserveRowid); int rc; zSql = sqlite3_mprintf("PRAGMA table_info=%Q", zTab); shell_check_oom(zSql); - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rc ) return 0; while( sqlite3_step(pStmt)==SQLITE_ROW ){ @@ -4367,7 +4402,7 @@ static char **tableColumnList(ShellState *p, const char *zTab){ zSql = sqlite3_mprintf("SELECT 1 FROM pragma_index_list(%Q)" " WHERE origin='pk'", zTab); shell_check_oom(zSql); - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rc ){ freeColumnList(azCol); @@ -4391,7 +4426,7 @@ static char **tableColumnList(ShellState *p, const char *zTab){ ** ordinary column in the table. Verify that azRowid[j] is a valid ** name for the rowid before adding it to azCol[0]. WITHOUT ROWID ** tables will fail this last check */ - rc = sqlite3_table_column_metadata(p->db,0,zTab,azRowid[j],0,0,0,0,0); + rc = sqlite3_table_column_metadata(db,0,zTab,azRowid[j],0,0,0,0,0); if( rc==SQLITE_OK ) azCol[0] = azRowid[j]; break; } @@ -4428,7 +4463,8 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ const char *zTable; const char *zType; const char *zSql; - ShellState *p = (ShellState *)pArg; + ShellInState *psi = (ShellInState *)pArg; + ShellExState *psx = XSS(psi); int dataOnly; int noSys; @@ -4437,33 +4473,33 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ zTable = azArg[0]; zType = azArg[1]; zSql = azArg[2]; - dataOnly = (p->shellFlgs & SHFLG_DumpDataOnly)!=0; - noSys = (p->shellFlgs & SHFLG_DumpNoSys)!=0; + dataOnly = (psi->shellFlgs & SHFLG_DumpDataOnly)!=0; + noSys = (psi->shellFlgs & SHFLG_DumpNoSys)!=0; if( strcmp(zTable, "sqlite_sequence")==0 && !noSys ){ - if( !dataOnly ) raw_printf(p->out, "DELETE FROM sqlite_sequence;\n"); + if( !dataOnly ) raw_printf(psi->out, "DELETE FROM sqlite_sequence;\n"); }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 && !noSys ){ - if( !dataOnly ) raw_printf(p->out, "ANALYZE sqlite_schema;\n"); - }else if( p->bAllowSysDump==0 && strncmp(zTable, "sqlite_", 7)==0 ){ + if( !dataOnly ) raw_printf(psi->out, "ANALYZE sqlite_schema;\n"); + }else if( psi->bAllowSysDump==0 && strncmp(zTable, "sqlite_", 7)==0 ){ return 0; }else if( dataOnly ){ /* no-op */ }else if( strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){ char *zIns; - if( !p->writableSchema ){ - raw_printf(p->out, "PRAGMA writable_schema=ON;\n"); - p->writableSchema = 1; + if( !psi->writableSchema ){ + raw_printf(psi->out, "PRAGMA writable_schema=ON;\n"); + psi->writableSchema = 1; } zIns = sqlite3_mprintf( "INSERT INTO sqlite_schema(type,name,tbl_name,rootpage,sql)" "VALUES('table','%q','%q',0,'%q');", zTable, zTable, zSql); shell_check_oom(zIns); - utf8_printf(p->out, "%s\n", zIns); + utf8_printf(psi->out, "%s\n", zIns); sqlite3_free(zIns); return 0; }else{ - printSchemaLine(p->out, zSql, ";\n"); + printSchemaLine(psi->out, zSql, ";\n"); } if( strcmp(zType, "table")==0 ){ @@ -4471,12 +4507,13 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ ShellText sTable; char **azCol; int i; - char *savedDestTable; + const char *savedDestTable; int savedMode; + int preserveRowid = (psi->shellFlgs & SHFLG_PreserveRowid)!=0; - azCol = tableColumnList(p, zTable); + azCol = tableColumnList(DBI(psi), zTable, preserveRowid); if( azCol==0 ){ - p->nErr++; + psi->nErr++; return 0; } @@ -4515,22 +4552,22 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ appendText(&sSelect, " FROM ", 0); appendText(&sSelect, zTable, quoteChar(zTable)); - savedDestTable = p->zDestTable; - savedMode = p->mode; - p->zDestTable = sTable.z; - p->mode = p->cMode = MODE_Insert; - rc = shell_exec(p, sSelect.z, 0); + savedDestTable = psx->zDestTable; + savedMode = psi->mode; + psx->zDestTable = sTable.z; + psi->mode = psi->cMode = MODE_Insert; + rc = shell_exec(psx, sSelect.z, 0); if( (rc&0xff)==SQLITE_CORRUPT ){ - raw_printf(p->out, "/****** CORRUPTION ERROR *******/\n"); - toggleSelectOrder(p->db); - shell_exec(p, sSelect.z, 0); - toggleSelectOrder(p->db); + raw_printf(psi->out, "/****** CORRUPTION ERROR *******/\n"); + toggleSelectOrder(DBX(psx)); + shell_exec(psx, sSelect.z, 0); + toggleSelectOrder(DBX(psx)); } - p->zDestTable = savedDestTable; - p->mode = savedMode; + psx->zDestTable = savedDestTable; + psi->mode = savedMode; freeText(&sTable); freeText(&sSelect); - if( rc ) p->nErr++; + if( rc ) psi->nErr++; } return 0; } @@ -4543,27 +4580,27 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ ** "ORDER BY rowid DESC" to the end. */ static int run_schema_dump_query( - ShellState *p, + ShellInState *psi, const char *zQuery ){ int rc; char *zErr = 0; - rc = sqlite3_exec(p->db, zQuery, dump_callback, p, &zErr); + rc = sqlite3_exec(DBI(psi), zQuery, dump_callback, psi, &zErr); if( rc==SQLITE_CORRUPT ){ char *zQ2; int len = strlen30(zQuery); - raw_printf(p->out, "/****** CORRUPTION ERROR *******/\n"); + raw_printf(psi->out, "/****** CORRUPTION ERROR *******/\n"); if( zErr ){ - utf8_printf(p->out, "/****** %s ******/\n", zErr); + utf8_printf(psi->out, "/****** %s ******/\n", zErr); sqlite3_free(zErr); zErr = 0; } zQ2 = malloc( len+100 ); if( zQ2==0 ) return rc; sqlite3_snprintf(len+100, zQ2, "%s ORDER BY rowid DESC", zQuery); - rc = sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr); + rc = sqlite3_exec(DBI(psi), zQ2, dump_callback, psi, &zErr); if( rc ){ - utf8_printf(p->out, "/****** ERROR: %s ******/\n", zErr); + utf8_printf(psi->out, "/****** ERROR: %s ******/\n", zErr); }else{ rc = SQLITE_CORRUPT; } @@ -4573,8 +4610,9 @@ static int run_schema_dump_query( return rc; } -/* Configure help text generation to have coalesced secondary - * help lines with trailing newlines on all help lines. +/* Configure help text generation to have coalesced secondary help lines + * with trailing newlines on all help lines. This allow help text to be + * representable as an array of two C-strings per meta-command. */ DISPATCH_CONFIG[ HELP_COALESCE=1 @@ -4669,7 +4707,7 @@ static int showHelp(FILE *out, const char *zPattern){ } /* Forward reference */ -static int process_input(ShellState *p); +static int process_input(ShellInState *psx); /* ** Read the content of file zName into memory obtained from sqlite3_malloc64() @@ -4729,9 +4767,9 @@ static void session_close(OpenSession *pSession){ ** Close all OpenSession objects and release all associated resources. */ #if defined(SQLITE_ENABLE_SESSION) -static void session_close_all(ShellState *p, int i){ +static void session_close_all(ShellInState *psi, int i){ int j; - struct AuxDb *pAuxDb = i<0 ? p->pAuxDb : &p->aAuxDb[i]; + struct AuxDb *pAuxDb = i<0 ? psi->pAuxDb : &psi->aAuxDb[i]; for(j=0; jnSession; j++){ session_close(&pAuxDb->aSession[j]); } @@ -4806,7 +4844,7 @@ u8 deduceDatabaseType(const char *zName, int dfltZip){ ** program. Read content from the file in p->aAuxDb[].zDbFilename. ** If p->aAuxDb[].zDbFilename is 0, then read from the present input. */ -static unsigned char *readHexDb(ShellState *p, int *pnData){ +static unsigned char *readHexDb(ShellInState *psi, int *pnData){ unsigned char *a = 0; int n = 0; int pgsz = 0; @@ -4814,9 +4852,9 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){ int j, k, nlError; int rc; static const char *zEndMarker = "| end "; - const char *zDbFilename = p->pAuxDb->zDbFilename; + const char *zDbFilename = psi->pAuxDb->zDbFilename; /* Need next two objects only if redirecting input to get the hex. */ - InSource inRedir = INSOURCE_FILE_REDIR(0, zDbFilename, p->pInSource); + InSource inRedir = INSOURCE_FILE_REDIR(0, zDbFilename, psi->pInSource); unsigned int x[16]; char zLine[1000]; if( zDbFilename ){ @@ -4825,17 +4863,17 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){ utf8_printf(STD_ERR, "cannot open \"%s\" for reading\n", zDbFilename); return 0; } - p->pInSource = &inRedir; + psi->pInSource = &inRedir; }else{ /* Will read hex DB lines inline from present input, without redirect. */ - if( INSOURCE_IS_INTERACTIVE(p->pInSource) ){ + if( INSOURCE_IS_INTERACTIVE(psi->pInSource) ){ printf("Reading hex DB from \"%s\", until end-marker input like:\n%s\n", - p->pInSource->zSourceSay, zEndMarker); + psi->pInSource->zSourceSay, zEndMarker); fflush(STD_OUT); } } *pnData = 0; - if( str_line_get(zLine,sizeof(zLine), p->pInSource)==0 ) goto readHexDb_error; + if( strLineGet(zLine,sizeof(zLine), psi->pInSource)==0 ) goto readHexDb_error; rc = sscanf(zLine, "| size %d pagesize %d", &n, &pgsz); if( rc!=2 ) goto readHexDb_error; if( n<0 ) goto readHexDb_error; @@ -4848,7 +4886,7 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){ utf8_printf(STD_ERR, "invalid pagesize\n"); goto readHexDb_error; } - while( str_line_get(zLine,sizeof(zLine), p->pInSource)!=0 ){ + while( strLineGet(zLine,sizeof(zLine), psi->pInSource)!=0 ){ rc = sscanf(zLine, "| page %d offset %d", &j, &k); if( rc==2 ){ iOffset = k; @@ -4870,21 +4908,19 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){ } *pnData = n; /* Record success and size. */ readHexDb_cleanup: - if( p->pInSource==&inRedir ){ + if( psi->pInSource==&inRedir ){ fclose( inRedir.inFile ); - p->pInSource = inRedir.pFrom; + psi->pInSource = inRedir.pFrom; } return a; readHexDb_error: - nlError = p->pInSource->lineno; - if( p->pInSource!=&inRedir ){ + nlError = psi->pInSource->lineno; + if( psi->pInSource!=&inRedir ){ /* Since taking input inline, consume through its end marker. */ - while( str_line_get(zLine, sizeof(zLine), p->pInSource)!=0 ){ - + while( strLineGet(zLine, sizeof(zLine), psi->pInSource)!=0 ){ if(strncmp(zLine, zEndMarker, 6)==0 ) break; } - } sqlite3_free(a); a = 0; @@ -5062,127 +5098,135 @@ static void shellEscapeCrnl( ** Make sure the database is open. If it is not, then open it. If ** the database fails to open, print an error message and exit. */ -static void open_db(ShellState *p, int openFlags){ - if( p->db==0 ){ - const char *zDbFilename = p->pAuxDb->zDbFilename; - if( p->openMode==SHELL_OPEN_UNSPEC ){ +static void open_db(ShellExState *psx, int openFlags){ + ShellInState *psi = ISS(psx); + if( DBX(psx)==0 ){ + sqlite3 **pDb = &DBX(psx); + const char *zDbFilename = psi->pAuxDb->zDbFilename; + if( psi->openMode==SHELL_OPEN_UNSPEC ){ if( zDbFilename==0 || zDbFilename[0]==0 ){ - p->openMode = SHELL_OPEN_NORMAL; + psi->openMode = SHELL_OPEN_NORMAL; }else{ - p->openMode = deduceDatabaseType(p->pAuxDb->zDbFilename, - (openFlags & OPEN_DB_ZIPFILE)!=0); + psi->openMode = deduceDatabaseType(psi->pAuxDb->zDbFilename, + (openFlags & OPEN_DB_ZIPFILE)!=0); } } - switch( p->openMode ){ + switch( psi->openMode ){ case SHELL_OPEN_APPENDVFS: { - sqlite3_open_v2(zDbFilename, &p->db, - SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, "apndvfs"); + sqlite3_open_v2 + (zDbFilename, pDb, + SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|psi->openFlags, + "apndvfs"); break; } case SHELL_OPEN_HEXDB: case SHELL_OPEN_DESERIALIZE: { - sqlite3_open(0, &p->db); + sqlite3_open(0, pDb); break; } case SHELL_OPEN_ZIPFILE: { - sqlite3_open(":memory:", &p->db); + sqlite3_open(":memory:", pDb); break; } case SHELL_OPEN_READONLY: { - sqlite3_open_v2(zDbFilename, &p->db, - SQLITE_OPEN_READONLY|p->openFlags, 0); + sqlite3_open_v2(zDbFilename, pDb, + SQLITE_OPEN_READONLY|psi->openFlags, 0); break; } case SHELL_OPEN_UNSPEC: case SHELL_OPEN_NORMAL: { - sqlite3_open_v2(zDbFilename, &p->db, - SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, 0); + sqlite3_open_v2(zDbFilename, pDb, + SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|psi->openFlags, 0); break; } } - globalDb = p->db; - if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ + globalDb = DBX(psx); + if( DBX(psx)==0 || SQLITE_OK!=sqlite3_errcode(DBX(psx)) ){ + const char *zWhy = (DBX(psx)==0)? "(?)" : sqlite3_errmsg(DBX(psx)); utf8_printf(STD_ERR,"Error: unable to open database \"%s\": %s\n", - zDbFilename, sqlite3_errmsg(p->db)); + zDbFilename, zWhy); if( openFlags & OPEN_DB_KEEPALIVE ){ - sqlite3_open(":memory:", &p->db); + sqlite3_open(":memory:", pDb); + globalDb = *pDb; return; } exit(1); } #ifndef SQLITE_OMIT_LOAD_EXTENSION - sqlite3_enable_load_extension(p->db, 1); -#endif - sqlite3_fileio_init(p->db, 0, 0); - sqlite3_shathree_init(p->db, 0, 0); - sqlite3_completion_init(p->db, 0, 0); - sqlite3_uint_init(p->db, 0, 0); - sqlite3_decimal_init(p->db, 0, 0); - sqlite3_regexp_init(p->db, 0, 0); - sqlite3_ieee_init(p->db, 0, 0); - sqlite3_series_init(p->db, 0, 0); + sqlite3_enable_load_extension(globalDb, 1); +#endif + sqlite3_fileio_init(globalDb, 0, 0); + sqlite3_shathree_init(globalDb, 0, 0); + sqlite3_completion_init(globalDb, 0, 0); + sqlite3_uint_init(globalDb, 0, 0); + sqlite3_decimal_init(globalDb, 0, 0); + sqlite3_regexp_init(globalDb, 0, 0); + sqlite3_ieee_init(globalDb, 0, 0); + sqlite3_series_init(globalDb, 0, 0); #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) - sqlite3_dbdata_init(p->db, 0, 0); + sqlite3_dbdata_init(globalDb, 0, 0); #endif #ifdef SQLITE_HAVE_ZLIB - sqlite3_zipfile_init(p->db, 0, 0); - sqlite3_sqlar_init(p->db, 0, 0); + sqlite3_zipfile_init(globalDb, 0, 0); + sqlite3_sqlar_init(globalDb, 0, 0); #endif - sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0, + sqlite3_create_function(globalDb, "shell_add_schema", 3, SQLITE_UTF8, 0, shellAddSchemaName, 0, 0); - sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0, + sqlite3_create_function(globalDb, "shell_module_schema", 1, SQLITE_UTF8, 0, shellModuleSchema, 0, 0); - sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p, + sqlite3_create_function(globalDb, "shell_putsnl", 1, SQLITE_UTF8, psx, shellPutsFunc, 0, 0); - sqlite3_create_function(p->db, "shell_escape_crnl", 1, SQLITE_UTF8, 0, + sqlite3_create_function(globalDb, "shell_escape_crnl", 1, SQLITE_UTF8, 0, shellEscapeCrnl, 0, 0); - sqlite3_create_function(p->db, "shell_int32", 2, SQLITE_UTF8, 0, + sqlite3_create_function(globalDb, "shell_int32", 2, SQLITE_UTF8, 0, shellInt32, 0, 0); - sqlite3_create_function(p->db, "shell_idquote", 1, SQLITE_UTF8, 0, + sqlite3_create_function(globalDb, "shell_idquote", 1, SQLITE_UTF8, 0, shellIdQuote, 0, 0); - sqlite3_create_function(p->db, "usleep",1,SQLITE_UTF8,0, + sqlite3_create_function(globalDb, "usleep",1,SQLITE_UTF8, 0, shellUSleepFunc, 0, 0); #ifndef SQLITE_NOHAVE_SYSTEM - sqlite3_create_function(p->db, "edit", 1, SQLITE_UTF8, 0, + sqlite3_create_function(globalDb, "edit", 1, SQLITE_UTF8, 0, editFunc, 0, 0); - sqlite3_create_function(p->db, "edit", 2, SQLITE_UTF8, 0, + sqlite3_create_function(globalDb, "edit", 2, SQLITE_UTF8, 0, editFunc, 0, 0); #endif - if( p->openMode==SHELL_OPEN_ZIPFILE ){ + if( psi->openMode==SHELL_OPEN_ZIPFILE ){ char *zSql = sqlite3_mprintf( "CREATE VIRTUAL TABLE zip USING zipfile(%Q);", zDbFilename); shell_check_oom(zSql); - sqlite3_exec(p->db, zSql, 0, 0, 0); + sqlite3_exec(DBX(psx), zSql, 0, 0, 0); sqlite3_free(zSql); } #ifndef SQLITE_OMIT_DESERIALIZE else - if( p->openMode==SHELL_OPEN_DESERIALIZE || p->openMode==SHELL_OPEN_HEXDB ){ + if( psi->openMode==SHELL_OPEN_DESERIALIZE + || psi->openMode==SHELL_OPEN_HEXDB ){ int rc; int nData = 0; unsigned char *aData; - if( p->openMode==SHELL_OPEN_DESERIALIZE ){ + if( psi->openMode==SHELL_OPEN_DESERIALIZE ){ aData = (unsigned char*)readFile(zDbFilename, &nData); }else{ - aData = readHexDb(p, &nData); + aData = readHexDb(psi, &nData); if( aData==0 ){ return; } } - rc = sqlite3_deserialize(p->db, "main", aData, nData, nData, + rc = sqlite3_deserialize(DBX(psx), "main", aData, nData, nData, SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE); if( rc ){ utf8_printf(STD_ERR, "Error: sqlite3_deserialize() returns %d\n", rc); } - if( p->szMax>0 ){ - sqlite3_file_control(p->db, "main", SQLITE_FCNTL_SIZE_LIMIT, &p->szMax); + if( psi->szMax>0 ){ + sqlite3_file_control(DBX(psx), "main", SQLITE_FCNTL_SIZE_LIMIT, + &psi->szMax); } } #endif } - if( p->bSafeModeFuture && p->db!=0 ){ - sqlite3_set_authorizer(p->db, safeModeAuth, p); + if( psi->bSafeModeFuture && DBX(psx)!=0 ){ + sqlite3_set_authorizer(DBX(psx), safeModeAuth, psx); } } @@ -5365,16 +5409,16 @@ static int booleanValue(const char *zArg){ /* ** Set or clear a shell flag according to a boolean value. */ -static void setOrClearFlag(ShellState *p, unsigned mFlag, const char *zArg){ +static void setOrClearFlag(ShellExState *psx, unsigned mFlag, const char *zArg){ if( booleanValue(zArg) ){ - ShellSetFlag(p, mFlag); + ShellSetFlag(psx, mFlag); }else{ - ShellClearFlag(p, mFlag); + ShellClearFlag(psx, mFlag); } } /* -** Close an output file, assuming it is not stderr or stdout +** Close an output file, provided it is not stderr or stdout */ static void output_file_close(FILE *f){ if( f && f!=STD_OUT && f!=STD_ERR ) fclose(f); @@ -5408,24 +5452,24 @@ static FILE *output_file_open(const char *zFile, int bTextMode){ */ static int sql_trace_callback( unsigned mType, /* The trace type */ - void *pArg, /* The ShellState pointer */ + void *pArg, /* The shell state pointer */ void *pP, /* Usually a pointer to sqlite_stmt */ void *pX /* Auxiliary output */ ){ - ShellState *p = (ShellState*)pArg; + ShellInState *psi = (ShellInState*)pArg; sqlite3_stmt *pStmt; const char *zSql; int nSql; - if( p->traceOut==0 ) return 0; + if( psi->traceOut==0 ) return 0; if( mType==SQLITE_TRACE_CLOSE ){ - utf8_printf(p->traceOut, "-- closing database connection\n"); + utf8_printf(psi->traceOut, "-- closing database connection\n"); return 0; } if( mType!=SQLITE_TRACE_ROW && ((const char*)pX)[0]=='-' ){ zSql = (const char*)pX; }else{ pStmt = (sqlite3_stmt*)pP; - switch( p->eTraceType ){ + switch( psi->eTraceType ){ case SHELL_TRACE_EXPANDED: { zSql = sqlite3_expanded_sql(pStmt); break; @@ -5448,12 +5492,12 @@ static int sql_trace_callback( switch( mType ){ case SQLITE_TRACE_ROW: case SQLITE_TRACE_STMT: { - utf8_printf(p->traceOut, "%.*s;\n", nSql, zSql); + utf8_printf(psi->traceOut, "%.*s;\n", nSql, zSql); break; } case SQLITE_TRACE_PROFILE: { sqlite3_int64 nNanosec = *(sqlite3_int64*)pX; - utf8_printf(p->traceOut, "%.*s; -- %lld ns\n", nSql, zSql, nNanosec); + utf8_printf(psi->traceOut, "%.*s; -- %lld ns\n", nSql, zSql, nNanosec); break; } } @@ -5641,7 +5685,7 @@ static char *SQLITE_CDECL ascii_read_one_field(ImportCtx *p){ ** work for WITHOUT ROWID tables. */ static void tryToCloneData( - ShellState *p, + ShellExState *psx, sqlite3 *newDb, const char *zTable ){ @@ -5658,10 +5702,10 @@ static void tryToCloneData( zQuery = sqlite3_mprintf("SELECT * FROM \"%w\"", zTable); shell_check_oom(zQuery); - rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); + rc = sqlite3_prepare_v2(DBX(psx), zQuery, -1, &pQuery, 0); if( rc ){ utf8_printf(STD_ERR, "Error %d: %s on [%s]\n", - sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), + sqlite3_extended_errcode(DBX(psx)), sqlite3_errmsg(DBX(psx)), zQuery); goto end_data_xfer; } @@ -5731,7 +5775,7 @@ static void tryToCloneData( zQuery = sqlite3_mprintf("SELECT * FROM \"%w\" ORDER BY rowid DESC;", zTable); shell_check_oom(zQuery); - rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); + rc = sqlite3_prepare_v2(DBX(psx), zQuery, -1, &pQuery, 0); if( rc ){ utf8_printf(STD_ERR, "Warning: cannot step \"%s\" backwards", zTable); break; @@ -5753,10 +5797,10 @@ end_data_xfer: ** sqlite_schema table, try again moving backwards. */ static void tryToCloneSchema( - ShellState *p, + ShellExState *psx, sqlite3 *newDb, const char *zWhere, - void (*xForEach)(ShellState*,sqlite3*,const char*) + void (*xForEach)(ShellExState*,sqlite3*,const char*) ){ sqlite3_stmt *pQuery = 0; char *zQuery = 0; @@ -5768,17 +5812,18 @@ static void tryToCloneSchema( zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema" " WHERE %s", zWhere); shell_check_oom(zQuery); - rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); + rc = sqlite3_prepare_v2(DBX(psx), zQuery, -1, &pQuery, 0); if( rc ){ utf8_printf(STD_ERR, "Error: (%d) %s on [%s]\n", - sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), - zQuery); + sqlite3_extended_errcode(DBX(psx)), + sqlite3_errmsg(DBX(psx)), zQuery); goto end_schema_xfer; } while( sqlite3_step(pQuery)==SQLITE_ROW ){ zName = sqlite3_column_text(pQuery, 0); zSql = sqlite3_column_text(pQuery, 1); if( zName==0 || zSql==0 ) continue; + /* Consider directing this output to current output. */ fprintf(STD_OUT, "%s... ", zName); fflush(STD_OUT); sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); if( zErrMsg ){ @@ -5787,8 +5832,9 @@ static void tryToCloneSchema( zErrMsg = 0; } if( xForEach ){ - xForEach(p, newDb, (const char*)zName); + xForEach(psx, newDb, (const char*)zName); } + /* Consider directing this output to current output. */ fprintf(STD_OUT, "done\n"); } if( rc!=SQLITE_DONE ){ @@ -5797,17 +5843,18 @@ static void tryToCloneSchema( zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema" " WHERE %s ORDER BY rowid DESC", zWhere); shell_check_oom(zQuery); - rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); + rc = sqlite3_prepare_v2(DBX(psx), zQuery, -1, &pQuery, 0); if( rc ){ utf8_printf(STD_ERR, "Error: (%d) %s on [%s]\n", - sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), - zQuery); + sqlite3_extended_errcode(DBX(psx)), + sqlite3_errmsg(DBX(psx)), zQuery); goto end_schema_xfer; } while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ zName = sqlite3_column_text(pQuery, 0); zSql = sqlite3_column_text(pQuery, 1); if( zName==0 || zSql==0 ) continue; + /* Consider directing ... */ fprintf(STD_OUT, "%s... ", zName); fflush(STD_OUT); sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); if( zErrMsg ){ @@ -5816,8 +5863,9 @@ static void tryToCloneSchema( zErrMsg = 0; } if( xForEach ){ - xForEach(p, newDb, (const char*)zName); + xForEach(psx, newDb, (const char*)zName); } + /* Consider directing ... */ fprintf(STD_OUT, "done\n"); } } @@ -5831,7 +5879,7 @@ end_schema_xfer: ** as possible out of the main database (which might be corrupt) and write it ** into zNewDb. */ -static void tryToClone(ShellState *p, const char *zNewDb){ +static void tryToClone(ShellExState *psx, const char *zNewDb){ int rc; sqlite3 *newDb = 0; if( access(zNewDb,0)==0 ){ @@ -5843,12 +5891,12 @@ static void tryToClone(ShellState *p, const char *zNewDb){ utf8_printf(STD_ERR, "Cannot create output database: %s\n", sqlite3_errmsg(newDb)); }else{ - sqlite3_exec(p->db, "PRAGMA writable_schema=ON;", 0, 0, 0); + sqlite3_exec(DBX(psx), "PRAGMA writable_schema=ON;", 0, 0, 0); sqlite3_exec(newDb, "BEGIN EXCLUSIVE;", 0, 0, 0); - tryToCloneSchema(p, newDb, "type='table'", tryToCloneData); - tryToCloneSchema(p, newDb, "type!='table'", 0); + tryToCloneSchema(psx, newDb, "type='table'", tryToCloneData); + tryToCloneSchema(psx, newDb, "type!='table'", 0); sqlite3_exec(newDb, "COMMIT;", 0, 0, 0); - sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); + sqlite3_exec(DBX(psx), "PRAGMA writable_schema=OFF;", 0, 0, 0); } close_db(newDb); } @@ -5856,19 +5904,19 @@ static void tryToClone(ShellState *p, const char *zNewDb){ /* ** Change the output file back to stdout. ** -** If the p->doXdgOpen flag is set, that means the output was being -** redirected to a temporary file named by p->zTempFile. In that case, +** If the psi->doXdgOpen flag is set, that means the output was being +** redirected to a temporary file named by psi->zTempFile. In that case, ** launch start/open/xdg-open on that temporary file. */ -static void output_reset(ShellState *p){ - if( p->outfile[0]=='|' ){ +static void output_reset(ShellInState *psi){ + if( psi->outfile[0]=='|' ){ #ifndef SQLITE_OMIT_POPEN - pclose(p->out); + pclose(psi->out); #endif }else{ - output_file_close(p->out); + output_file_close(psi->out); #ifndef SQLITE_NOHAVE_SYSTEM - if( p->doXdgOpen ){ + if( psi->doXdgOpen ){ const char *zXdgOpenCmd = #if defined(_WIN32) "start"; @@ -5878,23 +5926,23 @@ static void output_reset(ShellState *p){ "xdg-open"; #endif char *zCmd; - zCmd = sqlite3_mprintf("%s %s", zXdgOpenCmd, p->zTempFile); + zCmd = sqlite3_mprintf("%s %s", zXdgOpenCmd, psi->zTempFile); if( system(zCmd) ){ utf8_printf(STD_ERR, "Failed: [%s]\n", zCmd); }else{ /* Give the start/open/xdg-open command some time to get ** going before we continue, and potential delete the - ** p->zTempFile data file out from under it */ + ** psi->zTempFile data file out from under it */ sqlite3_sleep(2000); } sqlite3_free(zCmd); - outputModePop(p); - p->doXdgOpen = 0; + outputModePop(psi); + psi->doXdgOpen = 0; } #endif /* !defined(SQLITE_NOHAVE_SYSTEM) */ } - p->outfile[0] = 0; - p->out = STD_OUT; + psi->outfile[0] = 0; + psi->out = STD_OUT; } /* @@ -5947,7 +5995,7 @@ static unsigned int get4byteInt(unsigned char *a){ ** ** Return 1 on error, 2 to exit, and 0 otherwise. */ -static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ +static int shell_dbinfo_command(ShellExState *psx, int nArg, char **azArg){ static const struct { const char *zName; int ofst; } aField[] = { { "file change counter:", 24 }, { "database page count:", 28 }, @@ -5980,13 +6028,15 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ char *zDb = nArg>=2 ? azArg[1] : "main"; sqlite3_stmt *pStmt = 0; unsigned char aHdr[100]; - open_db(p, 0); - if( p->db==0 ) return 1; - rc = sqlite3_prepare_v2(p->db, + FILE *out = ISS(psx)->out; + + open_db(psx, 0); + if( DBX(psx)==0 ) return 1; + rc = sqlite3_prepare_v2(DBX(psx), "SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1", -1, &pStmt, 0); if( rc ){ - utf8_printf(STD_ERR, "error: %s\n", sqlite3_errmsg(p->db)); + utf8_printf(STD_ERR, "error: %s\n", sqlite3_errmsg(DBX(psx))); sqlite3_finalize(pStmt); return 1; } @@ -6003,22 +6053,22 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ } i = get2byteInt(aHdr+16); if( i==1 ) i = 65536; - utf8_printf(p->out, "%-20s %d\n", "database page size:", i); - utf8_printf(p->out, "%-20s %d\n", "write format:", aHdr[18]); - utf8_printf(p->out, "%-20s %d\n", "read format:", aHdr[19]); - utf8_printf(p->out, "%-20s %d\n", "reserved bytes:", aHdr[20]); + utf8_printf(out, "%-20s %d\n", "database page size:", i); + utf8_printf(out, "%-20s %d\n", "write format:", aHdr[18]); + utf8_printf(out, "%-20s %d\n", "read format:", aHdr[19]); + utf8_printf(out, "%-20s %d\n", "reserved bytes:", aHdr[20]); for(i=0; iout, "%-20s %u", aField[i].zName, val); + utf8_printf(out, "%-20s %u", aField[i].zName, val); switch( ofst ){ case 56: { - if( val==1 ) raw_printf(p->out, " (utf8)"); - if( val==2 ) raw_printf(p->out, " (utf16le)"); - if( val==3 ) raw_printf(p->out, " (utf16be)"); + if( val==1 ) raw_printf(out, " (utf8)"); + if( val==2 ) raw_printf(out, " (utf16le)"); + if( val==3 ) raw_printf(out, " (utf16be)"); } } - raw_printf(p->out, "\n"); + raw_printf(out, "\n"); } if( zDb==0 ){ zSchemaTab = sqlite3_mprintf("main.sqlite_schema"); @@ -6029,13 +6079,13 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ } for(i=0; idb, zSql); + int val = db_int(DBX(psx), zSql); sqlite3_free(zSql); - utf8_printf(p->out, "%-20s %d\n", aQuery[i].zName, val); + utf8_printf(out, "%-20s %d\n", aQuery[i].zName, val); } sqlite3_free(zSchemaTab); - sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion); - utf8_printf(p->out, "%-20s %u\n", "data version", iDataVersion); + sqlite3_file_control(DBX(psx), zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion); + utf8_printf(out, "%-20s %u\n", "data version", iDataVersion); return 0; } @@ -6174,26 +6224,27 @@ int shellDeleteFile(const char *zFilename){ ** Try to delete the temporary file (if there is one) and free the ** memory used to hold the name of the temp file. */ -static void clearTempFile(ShellState *p){ - if( p->zTempFile==0 ) return; - if( p->doXdgOpen ) return; - if( shellDeleteFile(p->zTempFile) ) return; - sqlite3_free(p->zTempFile); - p->zTempFile = 0; +static void clearTempFile(ShellInState *psi){ + if( psi->zTempFile==0 ) return; + if( psi->doXdgOpen ) return; + if( shellDeleteFile(psi->zTempFile) ) return; + sqlite3_free(psi->zTempFile); + psi->zTempFile = 0; } /* ** Create a new temp file name with the given suffix. */ -static void newTempFile(ShellState *p, const char *zSuffix){ - clearTempFile(p); - sqlite3_free(p->zTempFile); - p->zTempFile = 0; - if( p->db ){ - sqlite3_file_control(p->db, 0, SQLITE_FCNTL_TEMPFILENAME, &p->zTempFile); +static void newTempFile(ShellInState *psi, const char *zSuffix){ + clearTempFile(psi); + sqlite3_free(psi->zTempFile); + psi->zTempFile = 0; + if( DBI(psi) ){ + sqlite3_file_control(DBI(psi), 0, SQLITE_FCNTL_TEMPFILENAME, + &psi->zTempFile); } - if( p->zTempFile==0 ){ - /* If p->db is an in-memory database then the TEMPFILENAME file-control + if( psi->zTempFile==0 ){ + /* If DB is an in-memory database then the TEMPFILENAME file-control ** will not work and we will need to fallback to guessing */ char *zTemp; sqlite3_uint64 r; @@ -6207,11 +6258,11 @@ static void newTempFile(ShellState *p, const char *zSuffix){ zTemp = "/tmp"; #endif } - p->zTempFile = sqlite3_mprintf("%s/temp%llx.%s", zTemp, r, zSuffix); + psi->zTempFile = sqlite3_mprintf("%s/temp%llx.%s", zTemp, r, zSuffix); }else{ - p->zTempFile = sqlite3_mprintf("%z.%s", p->zTempFile, zSuffix); + psi->zTempFile = sqlite3_mprintf("%z.%s", psi->zTempFile, zSuffix); } - shell_check_oom(p->zTempFile); + shell_check_oom(psi->zTempFile); } /* @@ -6266,183 +6317,6 @@ static void shellFkeyCollateClause( } } - -/* -** The implementation of dot-command ".lint fkey-indexes". -*/ -static int lintFkeyIndexes( - ShellState *pState, /* Current shell tool state */ - char **azArg, /* Array of arguments passed to dot command */ - int nArg /* Number of entries in azArg[] */ -){ - sqlite3 *db = pState->db; /* Database handle to query "main" db of */ - FILE *out = pState->out; /* Stream to write non-error output to */ - int bVerbose = 0; /* If -verbose is present */ - int bGroupByParent = 0; /* If -groupbyparent is present */ - int i; /* To iterate through azArg[] */ - const char *zIndent = ""; /* How much to indent CREATE INDEX by */ - int rc; /* Return code */ - sqlite3_stmt *pSql = 0; /* Compiled version of SQL statement below */ - - /* - ** This SELECT statement returns one row for each foreign key constraint - ** in the schema of the main database. The column values are: - ** - ** 0. The text of an SQL statement similar to: - ** - ** "EXPLAIN QUERY PLAN SELECT 1 FROM child_table WHERE child_key=?" - ** - ** This SELECT is similar to the one that the foreign keys implementation - ** needs to run internally on child tables. If there is an index that can - ** be used to optimize this query, then it can also be used by the FK - ** implementation to optimize DELETE or UPDATE statements on the parent - ** table. - ** - ** 1. A GLOB pattern suitable for sqlite3_strglob(). If the plan output by - ** the EXPLAIN QUERY PLAN command matches this pattern, then the schema - ** contains an index that can be used to optimize the query. - ** - ** 2. Human readable text that describes the child table and columns. e.g. - ** - ** "child_table(child_key1, child_key2)" - ** - ** 3. Human readable text that describes the parent table and columns. e.g. - ** - ** "parent_table(parent_key1, parent_key2)" - ** - ** 4. A full CREATE INDEX statement for an index that could be used to - ** optimize DELETE or UPDATE statements on the parent table. e.g. - ** - ** "CREATE INDEX child_table_child_key ON child_table(child_key)" - ** - ** 5. The name of the parent table. - ** - ** These six values are used by the C logic below to generate the report. - */ - const char *zSql = - "SELECT " - " 'EXPLAIN QUERY PLAN SELECT 1 FROM ' || quote(s.name) || ' WHERE '" - " || group_concat(quote(s.name) || '.' || quote(f.[from]) || '=?' " - " || fkey_collate_clause(" - " f.[table], COALESCE(f.[to], p.[name]), s.name, f.[from]),' AND ')" - ", " - " 'SEARCH ' || s.name || ' USING COVERING INDEX*('" - " || group_concat('*=?', ' AND ') || ')'" - ", " - " s.name || '(' || group_concat(f.[from], ', ') || ')'" - ", " - " f.[table] || '(' || group_concat(COALESCE(f.[to], p.[name])) || ')'" - ", " - " 'CREATE INDEX ' || quote(s.name ||'_'|| group_concat(f.[from], '_'))" - " || ' ON ' || quote(s.name) || '('" - " || group_concat(quote(f.[from]) ||" - " fkey_collate_clause(" - " f.[table], COALESCE(f.[to], p.[name]), s.name, f.[from]), ', ')" - " || ');'" - ", " - " f.[table] " - "FROM sqlite_schema AS s, pragma_foreign_key_list(s.name) AS f " - "LEFT JOIN pragma_table_info AS p ON (pk-1=seq AND p.arg=f.[table]) " - "GROUP BY s.name, f.id " - "ORDER BY (CASE WHEN ? THEN f.[table] ELSE s.name END)" - ; - const char *zGlobIPK = "SEARCH * USING INTEGER PRIMARY KEY (rowid=?)"; - - for(i=2; i1 && sqlite3_strnicmp("-verbose", azArg[i], n)==0 ){ - bVerbose = 1; - } - else if( n>1 && sqlite3_strnicmp("-groupbyparent", azArg[i], n)==0 ){ - bGroupByParent = 1; - zIndent = " "; - } - else{ - raw_printf(STD_ERR, "Usage: %s %s ?-verbose? ?-groupbyparent?\n", - azArg[0], azArg[1] - ); - return SQLITE_ERROR; - } - } - - /* Register the fkey_collate_clause() SQL function */ - rc = sqlite3_create_function(db, "fkey_collate_clause", 4, SQLITE_UTF8, - 0, shellFkeyCollateClause, 0, 0 - ); - - - if( rc==SQLITE_OK ){ - rc = sqlite3_prepare_v2(db, zSql, -1, &pSql, 0); - } - if( rc==SQLITE_OK ){ - sqlite3_bind_int(pSql, 1, bGroupByParent); - } - - if( rc==SQLITE_OK ){ - int rc2; - char *zPrev = 0; - while( SQLITE_ROW==sqlite3_step(pSql) ){ - int res = -1; - sqlite3_stmt *pExplain = 0; - const char *zEQP = (const char*)sqlite3_column_text(pSql, 0); - const char *zGlob = (const char*)sqlite3_column_text(pSql, 1); - const char *zFrom = (const char*)sqlite3_column_text(pSql, 2); - const char *zTarget = (const char*)sqlite3_column_text(pSql, 3); - const char *zCI = (const char*)sqlite3_column_text(pSql, 4); - const char *zParent = (const char*)sqlite3_column_text(pSql, 5); - - if( zEQP==0 || zGlob==0 ) continue; - rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0); - if( rc!=SQLITE_OK ) break; - if( SQLITE_ROW==sqlite3_step(pExplain) ){ - const char *zPlan = (const char*)sqlite3_column_text(pExplain, 3); - res = zPlan!=0 && ( 0==sqlite3_strglob(zGlob, zPlan) - || 0==sqlite3_strglob(zGlobIPK, zPlan)); - } - rc = sqlite3_finalize(pExplain); - if( rc!=SQLITE_OK ) break; - - if( res<0 ){ - raw_printf(STD_ERR, "Error: internal error"); - break; - }else{ - if( bGroupByParent - && (bVerbose || res==0) - && (zPrev==0 || sqlite3_stricmp(zParent, zPrev)) - ){ - raw_printf(out, "-- Parent table %s\n", zParent); - sqlite3_free(zPrev); - zPrev = sqlite3_mprintf("%s", zParent); - } - - if( res==0 ){ - raw_printf(out, "%s%s --> %s\n", zIndent, zCI, zTarget); - }else if( bVerbose ){ - raw_printf(out, "%s/* no extra indexes required for %s -> %s */\n", - zIndent, zFrom, zTarget - ); - } - } - } - sqlite3_free(zPrev); - - if( rc!=SQLITE_OK ){ - raw_printf(STD_ERR, "%s\n", sqlite3_errmsg(db)); - } - - rc2 = sqlite3_finalize(pSql); - if( rc==SQLITE_OK && rc2!=SQLITE_OK ){ - rc = rc2; - raw_printf(STD_ERR, "%s\n", sqlite3_errmsg(db)); - } - }else{ - raw_printf(STD_ERR, "%s\n", sqlite3_errmsg(db)); - } - - return rc; -} - - #if !defined SQLITE_OMIT_VIRTUALTABLE static void shellPrepare( sqlite3 *db, @@ -6556,7 +6430,8 @@ struct ArCommand { const char *zFile; /* --file argument, or NULL */ const char *zDir; /* --directory argument, or NULL */ char **azArg; /* Array of command arguments */ - ShellState *p; /* Shell state */ + ShellExState *p; /* Shell state */ + FILE *out; /* Where to put normal messages or info */ sqlite3 *db; /* Database containing the archive */ }; @@ -6900,18 +6775,18 @@ static int arListCommand(ArCommand *pAr){ shellPreparePrintf(pAr->db, &rc, &pSql, zSql, azCols[pAr->bVerbose], pAr->zSrcTable, zWhere); if( pAr->bDryRun ){ - utf8_printf(pAr->p->out, "%s\n", sqlite3_sql(pSql)); + utf8_printf(pAr->out, "%s\n", sqlite3_sql(pSql)); }else{ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ if( pAr->bVerbose ){ - utf8_printf(pAr->p->out, "%s % 10d %s %s\n", + utf8_printf(pAr->out, "%s % 10d %s %s\n", sqlite3_column_text(pSql, 0), sqlite3_column_int(pSql, 1), sqlite3_column_text(pSql, 2), sqlite3_column_text(pSql, 3) ); }else{ - utf8_printf(pAr->p->out, "%s\n", sqlite3_column_text(pSql, 0)); + utf8_printf(pAr->out, "%s\n", sqlite3_column_text(pSql, 0)); } } } @@ -6939,7 +6814,7 @@ static int arRemoveCommand(ArCommand *pAr){ zSql = sqlite3_mprintf("DELETE FROM %s WHERE %s;", pAr->zSrcTable, zWhere); if( pAr->bDryRun ){ - utf8_printf(pAr->p->out, "%s\n", zSql); + utf8_printf(pAr->out, "%s\n", zSql); }else{ char *zErr = 0; rc = sqlite3_exec(pAr->db, "SAVEPOINT ar;", 0, 0, 0); @@ -7016,11 +6891,11 @@ static int arExtractCommand(ArCommand *pAr){ j = sqlite3_bind_parameter_index(pSql, "$dirOnly"); sqlite3_bind_int(pSql, j, i); if( pAr->bDryRun ){ - utf8_printf(pAr->p->out, "%s\n", sqlite3_sql(pSql)); + utf8_printf(pAr->out, "%s\n", sqlite3_sql(pSql)); }else{ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ if( i==0 && pAr->bVerbose ){ - utf8_printf(pAr->p->out, "%s\n", sqlite3_column_text(pSql, 0)); + utf8_printf(pAr->out, "%s\n", sqlite3_column_text(pSql, 0)); } } } @@ -7040,7 +6915,7 @@ static int arExtractCommand(ArCommand *pAr){ static int arExecSql(ArCommand *pAr, const char *zSql){ int rc; if( pAr->bDryRun ){ - utf8_printf(pAr->p->out, "%s\n", zSql); + utf8_printf(pAr->out, "%s\n", zSql); rc = SQLITE_OK; }else{ char *zErr = 0; @@ -7182,7 +7057,7 @@ end_ar_transaction: ** Implementation of ".ar" dot command. */ static int arDotCommand( - ShellState *pState, /* Current shell tool state */ + ShellExState *pState, /* Current shell tool state */ int fromCmdLine, /* True if -A command-line option, not .ar cmd */ char **azArg, /* Array of arguments passed to dot command */ int nArg /* Number of entries in azArg[] */ @@ -7192,14 +7067,15 @@ static int arDotCommand( memset(&cmd, 0, sizeof(cmd)); cmd.fromCmdLine = fromCmdLine; rc = arParseCommand(azArg, nArg, &cmd); + cmd.out = currentOutputFile(pState); if( rc==SQLITE_OK ){ int eDbType = SHELL_OPEN_UNSPEC; cmd.p = pState; - cmd.db = pState->db; + cmd.db = DBX(pState); if( cmd.zFile ){ eDbType = deduceDatabaseType(cmd.zFile, 1); }else{ - eDbType = pState->openMode; + eDbType = ISS(pState)->openMode; } if( eDbType==SHELL_OPEN_ZIPFILE ){ if( cmd.eCmd==AR_CMD_EXTRACT || cmd.eCmd==AR_CMD_LIST ){ @@ -7221,7 +7097,7 @@ static int arDotCommand( } cmd.db = 0; if( cmd.bDryRun ){ - utf8_printf(pState->out, "-- open database '%s'%s\n", cmd.zFile, + utf8_printf(cmd.out, "-- open database '%s'%s\n", cmd.zFile, eDbType==SHELL_OPEN_APPENDVFS ? " using 'apndvfs'" : ""); } rc = sqlite3_open_v2(cmd.zFile, &cmd.db, flags, @@ -7236,7 +7112,6 @@ static int arDotCommand( sqlite3_sqlar_init(cmd.db, 0, 0); sqlite3_create_function(cmd.db, "shell_putsnl", 1, SQLITE_UTF8, cmd.p, shellPutsFunc, 0, 0); - } if( cmd.zSrcTable==0 && cmd.bZip==0 && cmd.eCmd!=AR_CMD_HELP ){ if( cmd.eCmd!=AR_CMD_CREATE @@ -7263,7 +7138,7 @@ static int arDotCommand( break; case AR_CMD_HELP: - arUsage(pState->out); + arUsage(cmd.out); break; case AR_CMD_INSERT: @@ -7281,7 +7156,7 @@ static int arDotCommand( } } end_ar_command: - if( cmd.db!=pState->db ){ + if( cmd.db!=DBX(pState) ){ close_db(cmd.db); } sqlite3_free(cmd.zSrcTable); @@ -7547,7 +7422,7 @@ static RecoverTable *recoverNewTable( ** the caller should write data to the orphans table. */ static RecoverTable *recoverFindTable( - ShellState *pState, /* Shell state object */ + sqlite3 *db, /* DB from which to recover */ int *pRc, /* IN/OUT: Error code */ int iRoot, /* Root page of table */ int bIntkey, /* True for an intkey table */ @@ -7561,7 +7436,7 @@ static RecoverTable *recoverFindTable( const char *zName = 0; /* Search the recovered schema for an object with root page iRoot. */ - shellPreparePrintf(pState->db, pRc, &pStmt, + shellPreparePrintf(db, pRc, &pStmt, "SELECT type, name, sql FROM recovery.schema WHERE rootpage=%d", iRoot ); while( *pRc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ @@ -7589,7 +7464,8 @@ static RecoverTable *recoverFindTable( ** Return a RecoverTable object representing the orphans table. */ static RecoverTable *recoverOrphanTable( - ShellState *pState, /* Shell state object */ + sqlite3 *db, /* DB from which to recover */ + FILE *out, /* Where to put recovery DDL */ int *pRc, /* IN/OUT: Error code */ const char *zLostAndFound, /* Base name for orphans table */ int nCol /* Number of user data columns */ @@ -7605,7 +7481,7 @@ static RecoverTable *recoverOrphanTable( int iTab = 0; char *zTab = shellMPrintf(pRc, "%s", zLostAndFound); sqlite3_stmt *pTest = 0; - shellPrepare(pState->db, pRc, + shellPrepare(db, pRc, "SELECT 1 FROM recovery.schema WHERE name=?", &pTest ); if( pTest ) sqlite3_bind_text(pTest, 1, zTab, -1, SQLITE_TRANSIENT); @@ -7636,14 +7512,14 @@ static RecoverTable *recoverOrphanTable( recoverFreeTable(pTab); pTab = 0; }else{ - raw_printf(pState->out, + raw_printf(out, "CREATE TABLE %s(rootpgno INTEGER, " "pgno INTEGER, nfield INTEGER, id INTEGER", pTab->zQuoted ); for(i=0; iout, ", c%d", i); + raw_printf(out, ", c%d", i); } - raw_printf(pState->out, ");\n"); + raw_printf(out, ");\n"); } } sqlite3_free(zTab); @@ -7652,7 +7528,7 @@ static RecoverTable *recoverOrphanTable( } #endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */ -static int writeDb( char *azArg[], int nArg, ShellState *p, char **pzErr ){ +static int writeDb( char *azArg[], int nArg, ShellExState *psx, char **pzErr ){ int rc = 0; const char *zDestFile = 0; const char *zDb = 0; @@ -7661,7 +7537,7 @@ static int writeDb( char *azArg[], int nArg, ShellState *p, char **pzErr ){ int j; int bAsync = 0; const char *zVfs = 0; - if( p->bSafeMode ) return SHELL_FORBIDDEN_OP; + if( ISS(psx)->bSafeMode ) return SHELL_FORBIDDEN_OP; for(j=1; jdb, zDb); + open_db(psx, 0); + pBackup = sqlite3_backup_init(pDest, "main", DBX(psx), zDb); if( pBackup==0 ){ utf8_printf(STD_ERR, "Error: %s\n", sqlite3_errmsg(pDest)); close_db(pDest); @@ -7933,8 +7809,118 @@ FROM (\ } } +#if SHELL_DYNAMIC_EXTENSION +/* Register a meta-command */ +static int register_meta_command(ShellExState *p, + ExtensionId eid, MetaCommand *pMC){ + return SQLITE_ERROR; +} + +/* Register an output data display (or other disposition) mode */ +static int register_out_mode(ShellExState *p, + ExtensionId eid, OutModeHandler *pOMH){ + return SQLITE_ERROR; +} + +/* Register an import variation from (various sources) for .import */ +static int register_importer(ShellExState *p, + ExtensionId eid, ImportHandler *pIH){ + return SQLITE_ERROR; +} + +static FILE *currentOutputFile(ShellExState *p){ + return ISS(p)->out; +} + +static struct InSource *currentInputSource(ShellExState *p){ + return ISS(p)->pInSource; +} + +static int nowInteractive(ShellExState *p){ + return INSOURCE_IS_INTERACTIVE(ISS(p)->pInSource); +} + +static void setColumnWidths(ShellExState *p, char *azWidths[], int nWidths); + +static ExtensionHelpers extHelpers = { + 6, + { + failIfSafeMode, + currentOutputFile, + currentInputSource, + strLineGet, + setColumnWidths, + nowInteractive, + 0 + } +}; + +static ShellExtensionAPI shellExtAPI = { + &extHelpers, 3, { + register_meta_command, + register_out_mode, + register_importer, + 0 + } +}; + +/* This SQL function provides a way for a just-loaded shell extension to + * obtain a ShellExtensionLink pointer from the shell core while using + * the same sqlite3_load_extension API used for SQLite extensions. + * This serves as an alternative to deriving the same pointer from + * the pzErr argument passed into that API. + */ +static void shell_linkage( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + int linkKind = 0; + if( argc>0 ){ + linkKind = sqlite3_value_int(argv[0]); + } + switch (linkKind){ + case 0: + sqlite3_result_pointer(context, sqlite3_user_data(context), + SHELLEXT_API_POINTERS, 0); + break; + default: + sqlite3_result_null(context); + } +} + +static int load_shell_extension(ShellExState *psx, const char *zFile, + const char *zProc, char **pzErr){ + ShellExtensionLink shxLink = { + sizeof(ShellExtensionLink), + &shellExtAPI, + 0, /* zErrMsg */ + 0, /* ExtensionId */ + 0 /* Extension destructor */ + }; + int rc; + if( psx->dbShell==0 ){ + rc = sqlite3_open(":memory:", &psx->dbShell); + if( rc!=SQLITE_OK ) return 1; + sqlite3_enable_load_extension(psx->dbShell, 1); + } + sqlite3_create_function(psx->dbShell, "shext_pointer", 1, + SQLITE_DIRECTONLY|SQLITE_UTF8, + &shxLink, shell_linkage, 0, 0); + rc = sqlite3_load_extension(psx->dbShell, zFile, zProc, &shxLink.zErrMsg); + sqlite3_create_function(psx->dbShell, "shext_pointer", 1, + SQLITE_DIRECTONLY|SQLITE_UTF8, + 0, 0, 0, 0); /* deregister */ + if( pzErr!=0 ) *pzErr = shxLink.zErrMsg; + if( rc==SQLITE_OK ){ + /* Keep extension's id and destructor for later disposal. */ + } + return rc!=SQLITE_OK; +} +#endif + #ifndef OBJECTIFY_COMMANDS -# define OBJECTIFY_COMMANDS 1 +# define OBJECTIFY_COMMANDS 1 /* This value required for extensibility. */ #endif /* Meta-command implementation functions are defined in this section. @@ -7948,13 +7934,14 @@ COMMENT Generally, this section defines dispatchable functions inline and COMMENT causes collection of dispatch and help table entries, to be later COMMENT emitted by certain macros. (See EMIT_* further on.) ** All dispatchable meta-command execute functions have this signature: -static int someCommand(char *azArg[], int nArg, ShellState *p, char **pzErr); +static int someCommand(char *azArg[], int nArg, ShellExState *p, char **pzErr); */ DISPATCH_CONFIG[ RETURN_TYPE=int STORAGE_CLASS=static - ARGS_SIGNATURE=char *$arg4\[\], int $arg5, ShellState *$arg6, char **$arg7 + ARGS_SIGNATURE=char *$arg4\[\], int $arg5, ShellExState *$arg6, char **$arg7 DISPATCH_ENTRY={ "$cmd", ${cmd}Command, $arg1, $arg2, $arg3 }, + METACMD_INIT=META_CMD_INFO(${cmd}, $arg1,$arg2,$arg3, , ), CMD_CAPTURE_RE=^\s*{\s*"(\w+)" DISPATCHEE_NAME=${cmd}Command DC_ARG1_DEFAULT=[string length $cmd] @@ -7977,7 +7964,7 @@ COLLECT_HELP_TEXT[ DISPATCHABLE_COMMAND( seeargs ? 0 0 azArg nArg p ){ int rc = 0; for (rc=1; rcout, "%s%s", azArg[rc], (rc==nArg-1)? "|\n" : "|"); + raw_printf(ISS(p)->out, "%s%s", azArg[rc], (rc==nArg-1)? "|\n" : "|"); return 0; } @@ -8010,7 +7997,7 @@ COLLECT_HELP_TEXT[ ]; DISPATCHABLE_COMMAND( archive ? 0 0 azArg nArg p ){ open_db(p, 0); - if( p->bSafeMode ) return SHELL_FORBIDDEN_OP; + if( ISS(p)->bSafeMode ) return SHELL_FORBIDDEN_OP; return arDotCommand(p, 0, azArg, nArg); } @@ -8025,11 +8012,11 @@ DISPATCHABLE_COMMAND( auth 3 2 2 azArg nArg p ){ int rc = 0; open_db(p, 0); if( booleanValue(azArg[1]) ){ - sqlite3_set_authorizer(p->db, shellAuth, p); - }else if( p->bSafeModeFuture ){ - sqlite3_set_authorizer(p->db, safeModeAuth, p); + sqlite3_set_authorizer(DBX(p), shellAuth, p); + }else if( ISS(p)->bSafeModeFuture ){ + sqlite3_set_authorizer(DBX(p), safeModeAuth, p); }else{ - sqlite3_set_authorizer(p->db, 0, 0); + sqlite3_set_authorizer(DBX(p), 0, 0); } return rc; } @@ -8048,10 +8035,12 @@ COLLECT_HELP_TEXT[ " --append Use the appendvfs", " --async Write the FILE without journal and fsync()", ]; -COLLECT_DISPATCH( * )[ - { "backup", writeDb, 4, 2, 5 }, - { "save", writeDb, 3, 2, 5 }, -]; +DISPATCHABLE_COMMAND( backup 4 2 5 ){ + return writeDb( azArg, nArg, p, pzErr); +} +DISPATCHABLE_COMMAND( save 3 2 5 ){ + return writeDb( azArg, nArg, p, pzErr); +} /***************** * The .bail command @@ -8073,16 +8062,16 @@ COLLECT_HELP_TEXT[ ]; DISPATCHABLE_COMMAND( binary 3 2 2 ){ if( booleanValue(azArg[1]) ){ - setBinaryMode(p->out, 1); + setBinaryMode(ISS(p)->out, 1); }else{ - setTextMode(p->out, 1); + setTextMode(ISS(p)->out, 1); } return 0; } DISPATCHABLE_COMMAND( cd ? 2 2 ){ int rc=0; - if( p->bSafeMode ) return SHELL_FORBIDDEN_OP; + if( ISS(p)->bSafeMode ) return SHELL_FORBIDDEN_OP; #if defined(_WIN32) || defined(WIN32) wchar_t *z = sqlite3_win32_utf8_to_unicode(azArg[1]); rc = !SetCurrentDirectoryW(z); @@ -8125,7 +8114,7 @@ DISPATCHABLE_COMMAND( check 3 0 0 ){ */ char *zRes = 0; int rc=0; - output_reset(p); + output_reset(ISS(p)); if( nArg!=2 ){ return SHELL_INVALID_ARGS; }else if( (zRes = readFile("testcase-out.txt", 0))==0 ){ @@ -8135,59 +8124,60 @@ DISPATCHABLE_COMMAND( check 3 0 0 ){ *pzErr = shellMPrintf(&rc, "testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n", - p->zTestcase, azArg[1], zRes); + ISS(p)->zTestcase, azArg[1], zRes); rc = 1; }else{ - utf8_printf(STD_OUT, "testcase-%s ok\n", p->zTestcase); - p->nCheck++; + utf8_printf(STD_OUT, "testcase-%s ok\n", ISS(p)->zTestcase); + ISS(p)->nCheck++; } sqlite3_free(zRes); return rc; } DISPATCHABLE_COMMAND( clone ? 2 2 ){ - if( p->bSafeMode ) return SHELL_FORBIDDEN_OP; + if( ISS(p)->bSafeMode ) return SHELL_FORBIDDEN_OP; tryToClone(p, azArg[1]); return 0; } DISPATCHABLE_COMMAND( connection ? 1 4 ){ + ShellInState *psi = ISS(p); if( nArg==1 ){ /* List available connections */ int i; - for(i=0; iaAuxDb); i++){ - const char *zFile = p->aAuxDb[i].zDbFilename; - if( p->aAuxDb[i].db==0 && p->pAuxDb!=&p->aAuxDb[i] ){ - zFile = "(not open)"; + for(i=0; iaAuxDb); i++){ + const char *zFile = psi->aAuxDb[i].zDbFilename; + if( psi->aAuxDb[i].db==0 && psi->pAuxDb!=&psi->aAuxDb[i] ){ + zFile = "(not open)"; }else if( zFile==0 ){ - zFile = "(memory)"; + zFile = "(memory)"; }else if( zFile[0]==0 ){ - zFile = "(temporary-file)"; + zFile = "(temporary-file)"; } - if( p->pAuxDb == &p->aAuxDb[i] ){ - utf8_printf(STD_OUT, "ACTIVE %d: %s\n", i, zFile); - }else if( p->aAuxDb[i].db!=0 ){ - utf8_printf(STD_OUT, " %d: %s\n", i, zFile); + if( psi->pAuxDb == &psi->aAuxDb[i] ){ + utf8_printf(STD_OUT, "ACTIVE %d: %s\n", i, zFile); + }else if( psi->aAuxDb[i].db!=0 ){ + utf8_printf(STD_OUT, " %d: %s\n", i, zFile); } } }else if( nArg==2 && IsDigit(azArg[1][0]) && azArg[1][1]==0 ){ int i = azArg[1][0] - '0'; - if( p->pAuxDb != &p->aAuxDb[i] && i>=0 && iaAuxDb) ){ - p->pAuxDb->db = p->db; - p->pAuxDb = &p->aAuxDb[i]; - globalDb = p->db = p->pAuxDb->db; - p->pAuxDb->db = 0; + if( psi->pAuxDb != &psi->aAuxDb[i] && i>=0 && iaAuxDb) ){ + psi->pAuxDb->db = DBI(psi); + psi->pAuxDb = &psi->aAuxDb[i]; + globalDb = DBI(psi) = psi->pAuxDb->db; + psi->pAuxDb->db = 0; } }else if( nArg==3 && strcmp(azArg[1], "close")==0 - && IsDigit(azArg[2][0]) && azArg[2][1]==0 ){ + && IsDigit(azArg[2][0]) && azArg[2][1]==0 ){ int i = azArg[2][0] - '0'; - if( i<0 || i>=ArraySize(p->aAuxDb) ){ + if( i<0 || i>=ArraySize(psi->aAuxDb) ){ /* No-op */ - }else if( p->pAuxDb == &p->aAuxDb[i] ){ + }else if( psi->pAuxDb == &psi->aAuxDb[i] ){ raw_printf(STD_ERR, "cannot close the active database connection\n"); return 1; - }else if( p->aAuxDb[i].db ){ - session_close_all(p, i); - close_db(p->aAuxDb[i].db); - p->aAuxDb[i].db = 0; + }else if( psi->aAuxDb[i].db ){ + session_close_all(psi, i); + close_db(psi->aAuxDb[i].db); + psi->aAuxDb[i].db = 0; } }else{ return SHELL_INVALID_ARGS; @@ -8209,11 +8199,13 @@ DISPATCHABLE_COMMAND( databases 2 1 0 ){ char **azName = 0; int nName = 0; sqlite3_stmt *pStmt; + sqlite3 *db; int i; open_db(p, 0); - rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0); + db = DBX(p); + rc = sqlite3_prepare_v2(db, "PRAGMA database_list", -1, &pStmt, 0); if( rc ){ - *pzErr = shellMPrintf(0,"Error: %s\n", sqlite3_errmsg(p->db)); + *pzErr = shellMPrintf(0,"Error: %s\n", sqlite3_errmsg(db)); rc = 1; }else{ while( sqlite3_step(pStmt)==SQLITE_ROW ){ @@ -8231,10 +8223,10 @@ DISPATCHABLE_COMMAND( databases 2 1 0 ){ } sqlite3_finalize(pStmt); for(i=0; idb, azName[i*2]); - int bRdonly = sqlite3_db_readonly(p->db, azName[i*2]); + int eTxn = sqlite3_txn_state(db, azName[i*2]); + int bRdonly = sqlite3_db_readonly(db, azName[i*2]); const char *z = azName[i*2+1]; - utf8_printf(p->out, "%s: %s %s%s\n", + utf8_printf(ISS(p)->out, "%s: %s %s%s\n", azName[i*2], z && z[0] ? z : "\"\"", bRdonly ? "r/o" : "r/w", @@ -8273,10 +8265,11 @@ DISPATCHABLE_COMMAND( dbconfig 3 1 3 ){ for(ii=0; ii1 && strcmp(azArg[1], aDbConfig[ii].zName)!=0 ) continue; if( nArg>=3 ){ - sqlite3_db_config(p->db, aDbConfig[ii].op, booleanValue(azArg[2]), 0); + sqlite3_db_config(DBX(p), aDbConfig[ii].op, booleanValue(azArg[2]), 0); } - sqlite3_db_config(p->db, aDbConfig[ii].op, -1, &v); - utf8_printf(p->out, "%19s %s\n", aDbConfig[ii].zName, v ? "on" : "off"); + sqlite3_db_config(DBX(p), aDbConfig[ii].op, -1, &v); + utf8_printf(ISS(p)->out, "%19s %s\n", + aDbConfig[ii].zName, v ? "on" : "off"); if( nArg>1 ) break; } if( nArg>1 && ii==ArraySize(aDbConfig) ){ @@ -8315,12 +8308,13 @@ COLLECT_HELP_TEXT[ " trigger Like \"full\" but also show trigger bytecode", ]; DISPATCHABLE_COMMAND( dump ? 1 2 ){ + ShellInState *psi = ISS(p); char *zLike = 0; char *zSchema = "main"; char *zSql; int i; - int savedShowHeader = p->showHeader; - int savedShellFlags = p->shellFlgs; + int savedShowHeader = psi->showHeader; + int savedShellFlags = psi->shellFlgs; ShellClearFlag(p, SHFLG_PreserveRowid|SHFLG_Newlines|SHFLG_Echo |SHFLG_DumpDataOnly|SHFLG_DumpNoSys); @@ -8379,20 +8373,20 @@ DISPATCHABLE_COMMAND( dump ? 1 2 ){ open_db(p, 0); - if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){ + if( (psi->shellFlgs & SHFLG_DumpDataOnly)==0 ){ /* When playing back a "dump", the content might appear in an order ** which causes immediate foreign key constraints to be violated. ** So disable foreign-key constraint enforcement to prevent problems. */ - raw_printf(p->out, "PRAGMA foreign_keys=OFF;\n"); - raw_printf(p->out, "BEGIN TRANSACTION;\n"); + raw_printf(psi->out, "PRAGMA foreign_keys=OFF;\n"); + raw_printf(psi->out, "BEGIN TRANSACTION;\n"); } - p->writableSchema = 0; - p->showHeader = 0; + psi->writableSchema = 0; + psi->showHeader = 0; /* Set writable_schema=ON since doing so forces SQLite to initialize ** as much of the schema as it can even if the sqlite_schema table is ** corrupt. */ - sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0); - p->nErr = 0; + sqlite3_exec(DBX(p), "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0); + psi->nErr = 0; if( zLike==0 ) zLike = sqlite3_mprintf("true"); zSql = sqlite3_mprintf( "SELECT name, type, sql FROM %w.sqlite_schema AS o " @@ -8401,30 +8395,30 @@ DISPATCHABLE_COMMAND( dump ? 1 2 ){ " ORDER BY tbl_name='sqlite_sequence', rowid", zSchema, zLike ); - run_schema_dump_query(p,zSql); + run_schema_dump_query(psi,zSql); sqlite3_free(zSql); - if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){ + if( (psi->shellFlgs & SHFLG_DumpDataOnly)==0 ){ zSql = sqlite3_mprintf( "SELECT sql FROM sqlite_schema AS o " "WHERE (%s) AND sql NOT NULL" " AND type IN ('index','trigger','view')", zLike ); - run_table_dump_query(p, zSql); + run_table_dump_query(psi, zSql); sqlite3_free(zSql); } sqlite3_free(zLike); - if( p->writableSchema ){ - raw_printf(p->out, "PRAGMA writable_schema=OFF;\n"); - p->writableSchema = 0; + if( psi->writableSchema ){ + raw_printf(psi->out, "PRAGMA writable_schema=OFF;\n"); + psi->writableSchema = 0; } - sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); - sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0); - if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){ - raw_printf(p->out, p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n"); + sqlite3_exec(DBX(p), "PRAGMA writable_schema=OFF;", 0, 0, 0); + sqlite3_exec(DBX(p), "RELEASE dump;", 0, 0, 0); + if( (psi->shellFlgs & SHFLG_DumpDataOnly)==0 ){ + raw_printf(psi->out, psi->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n"); } - p->showHeader = savedShowHeader; - p->shellFlgs = savedShellFlags; + psi->showHeader = savedShowHeader; + psi->shellFlgs = savedShellFlags; return 0; } @@ -8433,29 +8427,30 @@ DISPATCHABLE_COMMAND( echo ? 2 2 ){ return 0; } DISPATCHABLE_COMMAND( eqp ? 0 0 ){ + ShellInState *psi = ISS(p); if( nArg==2 ){ - p->autoEQPtest = 0; - if( p->autoEQPtrace ){ - if( p->db ) sqlite3_exec(p->db, "PRAGMA vdbe_trace=OFF;", 0, 0, 0); - p->autoEQPtrace = 0; + psi->autoEQPtest = 0; + if( psi->autoEQPtrace ){ + if( DBX(p) ) sqlite3_exec(DBX(p), "PRAGMA vdbe_trace=OFF;", 0, 0, 0); + psi->autoEQPtrace = 0; } if( strcmp(azArg[1],"full")==0 ){ - p->autoEQP = AUTOEQP_full; + psi->autoEQP = AUTOEQP_full; }else if( strcmp(azArg[1],"trigger")==0 ){ - p->autoEQP = AUTOEQP_trigger; + psi->autoEQP = AUTOEQP_trigger; #ifdef SQLITE_DEBUG }else if( strcmp(azArg[1],"test")==0 ){ - p->autoEQP = AUTOEQP_on; - p->autoEQPtest = 1; + psi->autoEQP = AUTOEQP_on; + psi->autoEQPtest = 1; }else if( strcmp(azArg[1],"trace")==0 ){ - p->autoEQP = AUTOEQP_full; - p->autoEQPtrace = 1; + psi->autoEQP = AUTOEQP_full; + psi->autoEQPtrace = 1; open_db(p, 0); - sqlite3_exec(p->db, "SELECT name FROM sqlite_schema LIMIT 1", 0, 0, 0); - sqlite3_exec(p->db, "PRAGMA vdbe_trace=ON;", 0, 0, 0); + sqlite3_exec(DBX(p), "SELECT name FROM sqlite_schema LIMIT 1", 0, 0, 0); + sqlite3_exec(DBX(p), "PRAGMA vdbe_trace=ON;", 0, 0, 0); #endif }else{ - p->autoEQP = (u8)booleanValue(azArg[1]); + psi->autoEQP = (u8)booleanValue(azArg[1]); } }else{ return SHELL_INVALID_ARGS; @@ -8468,16 +8463,17 @@ DISPATCHABLE_COMMAND( eqp ? 0 0 ){ CONDITION_COMMAND( expert !defined(SQLITE_OMIT_VIRTUALTABLE) ); COLLECT_HELP_TEXT[ ".expert Suggest indexes for queries", - ".explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto", + ".explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto", ]; DISPATCHABLE_COMMAND( expert ? 1 1 ){ open_db(p, 0); - expertDotCommand(p, azArg, nArg); + expertDotCommand(ISS(p), azArg, nArg); return 0; } DISPATCHABLE_COMMAND( explain ? 1 2 ){ /* The ".explain" command is automatic now. It is largely ** pointless, retained purely for backwards compatibility */ + ShellInState *psi = ISS(p); int val = 1; if( nArg>1 ){ if( strcmp(azArg[1],"auto")==0 ){ @@ -8486,16 +8482,16 @@ DISPATCHABLE_COMMAND( explain ? 1 2 ){ val = booleanValue(azArg[1]); } } - if( val==1 && p->mode!=MODE_Explain ){ - p->normalMode = p->mode; - p->mode = MODE_Explain; - p->autoExplain = 0; + if( val==1 && psi->mode!=MODE_Explain ){ + psi->normalMode = psi->mode; + psi->mode = MODE_Explain; + psi->autoExplain = 0; }else if( val==0 ){ - if( p->mode==MODE_Explain ) p->mode = p->normalMode; - p->autoExplain = 0; + if( psi->mode==MODE_Explain ) psi->mode = psi->normalMode; + psi->autoExplain = 0; }else if( val==99 ){ - if( p->mode==MODE_Explain ) p->mode = p->normalMode; - p->autoExplain = 1; + if( psi->mode==MODE_Explain ) psi->mode = psi->normalMode; + psi->autoExplain = 1; } return 0; } @@ -8520,19 +8516,19 @@ COLLECT_HELP_TEXT[ " -e Send output to the system text editor", " -x Send output as CSV to a spreadsheet (same as \".excel\")", ]; -static int outputRedirs(char *[], int, ShellState *, +static int outputRedirs(char *[], int, ShellInState *, char **pzErr, int bOnce, int eMode); DISPATCHABLE_COMMAND( excel ? 1 2 ){ - return outputRedirs(azArg, nArg, p, pzErr, 2, 'x'); + return outputRedirs(azArg, nArg, ISS(p), pzErr, 2, 'x'); } DISPATCHABLE_COMMAND( once ? 1 6 ){ - return outputRedirs(azArg, nArg, p, pzErr, 1, 0); + return outputRedirs(azArg, nArg, ISS(p), pzErr, 1, 0); } DISPATCHABLE_COMMAND( output ? 1 6 ){ - return outputRedirs(azArg, nArg, p, pzErr, 0, 0); + return outputRedirs(azArg, nArg, ISS(p), pzErr, 0, 0); } -static int outputRedirs(char *azArg[], int nArg, ShellState *p, +static int outputRedirs(char *azArg[], int nArg, ShellInState *psi, char **pzErr, int bOnce, int eMode){ /* bOnce => 0: .output, 1: .once, 2: .excel */ /* eMode => 'x' for excel, else 0 */ @@ -8541,7 +8537,7 @@ static int outputRedirs(char *azArg[], int nArg, ShellState *p, int bTxtMode = 0; int i; int bBOM = 0; - if( p->bSafeMode ) return SHELL_FORBIDDEN_OP; + if( psi->bSafeMode ) return SHELL_FORBIDDEN_OP; for(i=1; ioutCount = 2; + psi->outCount = 2; }else{ - p->outCount = 0; + psi->outCount = 0; } - output_reset(p); + output_reset(psi); #ifndef SQLITE_NOHAVE_SYSTEM if( eMode=='e' || eMode=='x' ){ - p->doXdgOpen = 1; - outputModePush(p); + psi->doXdgOpen = 1; + outputModePush(psi); if( eMode=='x' ){ /* spreadsheet mode. Output as CSV. */ - newTempFile(p, "csv"); - ShellClearFlag(p, SHFLG_Echo); - p->mode = MODE_Csv; - sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); + newTempFile(psi, "csv"); + psi->shellFlgs &= ~SHFLG_Echo; + psi->mode = MODE_Csv; + sqlite3_snprintf(sizeof(psi->colSeparator), psi->colSeparator, SEP_Comma); + sqlite3_snprintf(sizeof(psi->rowSeparator), psi->rowSeparator, SEP_CrLf); }else{ /* text editor mode */ - newTempFile(p, "txt"); + newTempFile(psi, "txt"); bTxtMode = 1; } sqlite3_free(zFile); - zFile = sqlite3_mprintf("%s", p->zTempFile); + zFile = sqlite3_mprintf("%s", psi->zTempFile); } #endif /* SQLITE_NOHAVE_SYSTEM */ shell_check_oom(zFile); @@ -8607,30 +8603,30 @@ static int outputRedirs(char *azArg[], int nArg, ShellState *p, #ifdef SQLITE_OMIT_POPEN *pzErr = shellMPrintf(&rc, "Error: pipes are not supported in this OS\n"); rc = 1; - p->out = STD_OUT; + psi->out = STD_OUT; #else - p->out = popen(zFile + 1, "w"); - if( p->out==0 ){ + psi->out = popen(zFile + 1, "w"); + if( psi->out==0 ){ *pzErr = shellMPrintf(&rc, "Error: cannot open pipe \"%s\"\n", zFile + 1); - p->out = STD_OUT; + psi->out = STD_OUT; rc = 1; }else{ - if( bBOM ) fprintf(p->out,"\357\273\277"); - sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); + if( bBOM ) fprintf(psi->out,"\357\273\277"); + sqlite3_snprintf(sizeof(psi->outfile), psi->outfile, "%s", zFile); } #endif }else{ - p->out = output_file_open(zFile, bTxtMode); - if( p->out==0 ){ + psi->out = output_file_open(zFile, bTxtMode); + if( psi->out==0 ){ if( strcmp(zFile,"off")!=0 ){ *pzErr = shellMPrintf (&rc, "Error: cannot write to \"%s\"\n", zFile); } - p->out = STD_OUT; + psi->out = STD_OUT; rc = 1; } else { - if( bBOM ) fprintf(p->out,"\357\273\277"); - sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); + if( bBOM ) fprintf(psi->out,"\357\273\277"); + sqlite3_snprintf(sizeof(psi->outfile), psi->outfile, "%s", zFile); } } sqlite3_free(zFile); @@ -8665,6 +8661,7 @@ DISPATCHABLE_COMMAND( filectrl ? 2 0 ){ { "tempfilename", SQLITE_FCNTL_TEMPFILENAME, "" }, /* { "win32_av_retry", SQLITE_FCNTL_WIN32_AV_RETRY, "COUNT DELAY" },*/ }; + ShellInState *psi = ISS(p); int filectrl = -1; int iCtrl = -1; sqlite3_int64 iRes = 0; /* Integer result to display if rc2==1 */ @@ -8694,14 +8691,14 @@ DISPATCHABLE_COMMAND( filectrl ? 2 0 ){ /* --help lists all file-controls */ if( strcmp(zCmd,"help")==0 ){ - utf8_printf(p->out, "Available file-controls:\n"); + utf8_printf(psi->out, "Available file-controls:\n"); for(i=0; iout, " .filectrl %s %s\n", + utf8_printf(psi->out, " .filectrl %s %s\n", aCtrl[i].zCtrlName, aCtrl[i].zUsage); } return 1; } - + /* Convert filectrl text option to value. Allow any ** unique prefix of the option name, or a numerical value. */ n2 = strlen30(zCmd); @@ -8725,7 +8722,7 @@ DISPATCHABLE_COMMAND( filectrl ? 2 0 ){ case SQLITE_FCNTL_SIZE_LIMIT: { if( nArg!=2 && nArg!=3 ) break; iRes = nArg==3 ? integerValue(azArg[2]) : -1; - sqlite3_file_control(p->db, zSchema, SQLITE_FCNTL_SIZE_LIMIT, &iRes); + sqlite3_file_control(DBX(p), zSchema, SQLITE_FCNTL_SIZE_LIMIT, &iRes); isOk = 1; break; } @@ -8734,7 +8731,7 @@ DISPATCHABLE_COMMAND( filectrl ? 2 0 ){ int x; if( nArg!=3 ) break; x = (int)integerValue(azArg[2]); - sqlite3_file_control(p->db, zSchema, filectrl, &x); + sqlite3_file_control(DBX(p), zSchema, filectrl, &x); isOk = 2; break; } @@ -8743,7 +8740,7 @@ DISPATCHABLE_COMMAND( filectrl ? 2 0 ){ int x; if( nArg!=2 && nArg!=3 ) break; x = nArg==3 ? booleanValue(azArg[2]) : -1; - sqlite3_file_control(p->db, zSchema, filectrl, &x); + sqlite3_file_control(DBX(p), zSchema, filectrl, &x); iRes = x; isOk = 1; break; @@ -8752,7 +8749,7 @@ DISPATCHABLE_COMMAND( filectrl ? 2 0 ){ case SQLITE_FCNTL_HAS_MOVED: { int x; if( nArg!=2 ) break; - sqlite3_file_control(p->db, zSchema, filectrl, &x); + sqlite3_file_control(DBX(p), zSchema, filectrl, &x); iRes = x; isOk = 1; break; @@ -8760,9 +8757,9 @@ DISPATCHABLE_COMMAND( filectrl ? 2 0 ){ case SQLITE_FCNTL_TEMPFILENAME: { char *z = 0; if( nArg!=2 ) break; - sqlite3_file_control(p->db, zSchema, filectrl, &z); + sqlite3_file_control(DBX(p), zSchema, filectrl, &z); if( z ){ - utf8_printf(p->out, "%s\n", z); + utf8_printf(psi->out, "%s\n", z); sqlite3_free(z); } isOk = 2; @@ -8772,32 +8769,37 @@ DISPATCHABLE_COMMAND( filectrl ? 2 0 ){ int x; if( nArg>=3 ){ x = atoi(azArg[2]); - sqlite3_file_control(p->db, zSchema, filectrl, &x); + sqlite3_file_control(DBX(p), zSchema, filectrl, &x); } x = -1; - sqlite3_file_control(p->db, zSchema, filectrl, &x); - utf8_printf(p->out,"%d\n", x); + sqlite3_file_control(DBX(p), zSchema, filectrl, &x); + utf8_printf(psi->out,"%d\n", x); isOk = 2; break; } } } if( isOk==0 && iCtrl>=0 ){ - utf8_printf(p->out, "Usage: .filectrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); + utf8_printf(psi->out, "Usage: .filectrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); return 1; }else if( isOk==1 ){ char zBuf[100]; sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", iRes); - raw_printf(p->out, "%s\n", zBuf); + raw_printf(psi->out, "%s\n", zBuf); } return 0; } DISPATCHABLE_COMMAND( fullschema ? 1 2 ){ int rc; - ShellState data; + ShellInState data; + ShellExState datax; int doStats = 0; - memcpy(&data, p, sizeof(data)); + /* Consider some refactoring to avoid this wholesale copying. */ + memcpy(&data, ISS(p), sizeof(data)); + memcpy(&datax, p, sizeof(datax)); + data.pSXS = &datax; + datax.pSIS = &data; data.showHeader = 0; data.cMode = data.mode = MODE_Semi; if( nArg==2 && optionMatch(azArg[1], "indent") ){ @@ -8808,18 +8810,18 @@ DISPATCHABLE_COMMAND( fullschema ? 1 2 ){ return SHELL_INVALID_ARGS; } open_db(p, 0); - rc = sqlite3_exec(p->db, + rc = sqlite3_exec(datax.dbUser, "SELECT sql FROM" " (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x" " FROM sqlite_schema UNION ALL" " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_schema) " "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%' " "ORDER BY x", - callback, &data, 0 + callback, &datax, 0 ); if( rc==SQLITE_OK ){ sqlite3_stmt *pStmt; - rc = sqlite3_prepare_v2(p->db, + rc = sqlite3_prepare_v2(datax.dbUser, "SELECT rowid FROM sqlite_schema" " WHERE name GLOB 'sqlite_stat[134]'", -1, &pStmt, 0); @@ -8827,15 +8829,15 @@ DISPATCHABLE_COMMAND( fullschema ? 1 2 ){ sqlite3_finalize(pStmt); } if( doStats==0 ){ - raw_printf(p->out, "/* No STAT tables available */\n"); + raw_printf(data.out, "/* No STAT tables available */\n"); }else{ - raw_printf(p->out, "ANALYZE sqlite_schema;\n"); + raw_printf(data.out, "ANALYZE sqlite_schema;\n"); data.cMode = data.mode = MODE_Insert; - data.zDestTable = "sqlite_stat1"; - shell_exec(&data, "SELECT * FROM sqlite_stat1", 0); - data.zDestTable = "sqlite_stat4"; - shell_exec(&data, "SELECT * FROM sqlite_stat4", 0); - raw_printf(p->out, "ANALYZE sqlite_schema;\n"); + datax.zDestTable = "sqlite_stat1"; + shell_exec(&datax, "SELECT * FROM sqlite_stat1", 0); + datax.zDestTable = "sqlite_stat4"; + shell_exec(&datax, "SELECT * FROM sqlite_stat4", 0); + raw_printf(data.out, "ANALYZE sqlite_schema;\n"); } return rc > 0; } @@ -8847,8 +8849,8 @@ COLLECT_HELP_TEXT[ ".headers on|off Turn display of headers on or off", ]; DISPATCHABLE_COMMAND( headers 6 2 2 ){ - p->showHeader = booleanValue(azArg[1]); - p->shellFlgs |= SHFLG_HeaderSet; + ISS(p)->showHeader = booleanValue(azArg[1]); + ISS(p)->shellFlgs |= SHFLG_HeaderSet; return 0; } @@ -8871,8 +8873,8 @@ DISPATCHABLE_COMMAND( help 3 1 2 ){ zPat = z; } } - if( showHelp(p->out, zPat)==0 ){ - utf8_printf(p->out, "Nothing matches '%s'\n", azArg[1]); + if( showHelp(ISS(p)->out, zPat)==0 ){ + utf8_printf(ISS(p)->out, "Nothing matches '%s'\n", azArg[1]); } /* Help pleas never fail! */ return 0; @@ -8906,22 +8908,24 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){ int nByte; /* Number of bytes in an SQL string */ int i, j; /* Loop counters */ int needCommit; /* True to COMMIT or ROLLBACK at end */ - int nSep; /* Number of bytes in p->colSeparator[] */ + int nSep; /* Number of bytes in psi->colSeparator[] */ char *zSql; /* An SQL statement */ ImportCtx sCtx; /* Reader context */ char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */ int eVerbose = 0; /* Larger for more console output */ int nSkip = 0; /* Initial lines to skip */ int useOutputMode = 1; /* Use output mode to determine separators */ + FILE *out = ISS(p)->out; /* output stream */ + ShellInState *psi = ISS(p); int rc = 0; - if(p->bSafeMode) return SHELL_FORBIDDEN_OP; + if(psi->bSafeMode) return SHELL_FORBIDDEN_OP; memset(&sCtx, 0, sizeof(sCtx)); if( 0==(sCtx.z = sqlite3_malloc64(120)) ){ shell_out_of_memory(); } - if( p->mode==MODE_Ascii ){ + if( psi->mode==MODE_Ascii ){ xRead = ascii_read_one_field; }else{ xRead = csv_read_one_field; @@ -8970,7 +8974,7 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){ const char *zYap = 0; /* If neither the --csv or --ascii options are specified, then set ** the column and row separator characters from the output mode. */ - nSep = strlen30(p->colSeparator); + nSep = strlen30(psi->colSeparator); if( nSep==0 ){ zYap = "Error: non-null column separator required for import"; } @@ -8978,7 +8982,7 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){ zYap = "Error: multi-character or multi-byte column separators" " not allowed for import"; } - nSep = strlen30(p->rowSeparator); + nSep = strlen30(psi->rowSeparator); if( nSep==0 ){ zYap = "Error: non-null row separator required for import"; } @@ -8986,21 +8990,22 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){ *pzErr = shellMPrintf(0,"%s\n", zYap); return 1; } - if( nSep==2 && p->mode==MODE_Csv && strcmp(p->rowSeparator,SEP_CrLf)==0 ){ + if( nSep==2 && psi->mode==MODE_Csv + && strcmp(psi->rowSeparator,SEP_CrLf)==0 ){ /* When importing CSV (only), if the row separator is set to the ** default output row separator, change it to the default input ** row separator. This avoids having to maintain different input ** and output row separators. */ - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); - nSep = strlen30(p->rowSeparator); + sqlite3_snprintf(sizeof(psi->rowSeparator), psi->rowSeparator, SEP_Row); + nSep = strlen30(psi->rowSeparator); } if( nSep>1 ){ *pzErr = sqlite3_mprintf ("Error: multi-character row separators not allowed for import\n"); return 1; } - sCtx.cColSep = p->colSeparator[0]; - sCtx.cRowSep = p->rowSeparator[0]; + sCtx.cColSep = psi->colSeparator[0]; + sCtx.cRowSep = psi->rowSeparator[0]; } sCtx.zFile = zFile; sCtx.nLine = 1; @@ -9027,12 +9032,12 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){ char zSep[2]; zSep[1] = 0; zSep[0] = sCtx.cColSep; - utf8_printf(p->out, "Column separator "); - output_c_string(p->out, zSep); - utf8_printf(p->out, ", row separator "); + utf8_printf(out, "Column separator "); + output_c_string(out, zSep); + utf8_printf(out, ", row separator "); zSep[0] = sCtx.cRowSep; - output_c_string(p->out, zSep); - utf8_printf(p->out, "\n"); + output_c_string(out, zSep); + utf8_printf(out, "\n"); } while( (nSkip--)>0 ){ while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){} @@ -9043,9 +9048,9 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){ shell_out_of_memory(); } nByte = strlen30(zSql); - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + rc = sqlite3_prepare_v2(DBX(p), zSql, -1, &pStmt, 0); import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */ - if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(p->db))==0 ){ + if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(DBX(p)))==0 ){ char *zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"", zSchema, zTable); sqlite3 *dbCols = 0; @@ -9053,12 +9058,11 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){ char *zColDefs; while( xRead(&sCtx) ){ zAutoColumn(sCtx.z, &dbCols, 0); - if( sCtx.cTerm!=sCtx.cColSep ) break; } zColDefs = zAutoColumn(0, &dbCols, &zRenames); if( zRenames!=0 ){ - FILE *fh = INSOURCE_IS_INTERACTIVE(p->pInSource)? p->out : STD_ERR; + FILE *fh = INSOURCE_IS_INTERACTIVE(psi->pInSource)? out : STD_ERR; utf8_printf(fh, "Columns renamed during .import %s due to duplicates:\n" "%s\n", sCtx.zFile, zRenames); sqlite3_free(zRenames); @@ -9072,22 +9076,22 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){ } zCreate = sqlite3_mprintf("%z%z\n", zCreate, zColDefs); if( eVerbose>=1 ){ - utf8_printf(p->out, "%s\n", zCreate); + utf8_printf(out, "%s\n", zCreate); } - rc = sqlite3_exec(p->db, zCreate, 0, 0, 0); + rc = sqlite3_exec(DBX(p), zCreate, 0, 0, 0); if( rc ){ - utf8_printf(STD_ERR, "%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db)); + utf8_printf(STD_ERR, "%s failed:\n%s\n", zCreate, sqlite3_errmsg(DBX(p))); sqlite3_free(zCreate); import_cleanup(&sCtx); return 1; } sqlite3_free(zCreate); - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + rc = sqlite3_prepare_v2(DBX(p), zSql, -1, &pStmt, 0); } sqlite3_free(zSql); if( rc ){ if (pStmt) sqlite3_finalize(pStmt); - *pzErr = shellMPrintf(0,"Error: %s\n", sqlite3_errmsg(p->db)); + *pzErr = shellMPrintf(0,"Error: %s\n", sqlite3_errmsg(DBX(p))); import_cleanup(&sCtx); return 1; } @@ -9110,18 +9114,18 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){ zSql[j++] = ')'; zSql[j] = 0; if( eVerbose>=2 ){ - utf8_printf(p->out, "Insert using: %s\n", zSql); + utf8_printf(psi->out, "Insert using: %s\n", zSql); } - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + rc = sqlite3_prepare_v2(DBX(p), zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rc ){ - *pzErr = shellMPrintf(0,"Error: %s\n", sqlite3_errmsg(p->db)); + *pzErr = shellMPrintf(0,"Error: %s\n", sqlite3_errmsg(DBX(p))); if (pStmt) sqlite3_finalize(pStmt); import_cleanup(&sCtx); return 1; } - needCommit = sqlite3_get_autocommit(p->db); - if( needCommit ) sqlite3_exec(p->db, "BEGIN", 0, 0, 0); + needCommit = sqlite3_get_autocommit(DBX(p)); + if( needCommit ) sqlite3_exec(DBX(p), "BEGIN", 0, 0, 0); do{ int startLine = sCtx.nLine; for(i=0; imode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break; + if( psi->mode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break; sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); if( idb)); + startLine, sqlite3_errmsg(DBX(p))); sCtx.nErr++; }else{ sCtx.nRow++; @@ -9170,9 +9174,9 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){ import_cleanup(&sCtx); sqlite3_finalize(pStmt); - if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); + if( needCommit ) sqlite3_exec(DBX(p), "COMMIT", 0, 0, 0); if( eVerbose>0 ){ - utf8_printf(p->out, + utf8_printf(out, "Added %d rows with %d errors using %d lines of input\n", sCtx.nRow, sCtx.nErr, sCtx.nLine-1); } @@ -9187,6 +9191,7 @@ COLLECT_HELP_TEXT[ ".keyword ?KW? List keywords, or say whether KW is one.", ]; DISPATCHABLE_COMMAND( keyword ? 1 2 ){ + FILE *out = ISS(p)->out; if( nArg<2 ){ int i = 0; int nk = sqlite3_keyword_count(); @@ -9204,15 +9209,15 @@ DISPATCHABLE_COMMAND( keyword ? 1 2 ){ } memcpy(kwBuf, zKW, szKW); kwBuf[szKW] = 0; - utf8_printf(p->out, "%s%s", kwBuf, zSep); + utf8_printf(out, "%s%s", kwBuf, zSep); } } } - if( nCol>0 ) utf8_printf(p->out, "\n"); + if( nCol>0 ) utf8_printf(out, "\n"); }else{ int szKW = strlen30(azArg[1]); int isKeyword = sqlite3_keyword_check(azArg[1], szKW); - utf8_printf(p->out, "%s is%s a keyword\n", + utf8_printf(out, "%s is%s a keyword\n", azArg[1], (isKeyword)? "" : " not"); } return 0; @@ -9232,6 +9237,9 @@ COLLECT_HELP_TEXT[ " Options:", " fkey-indexes Find missing foreign key indexes", ".load FILE ?ENTRY? Load an extension library", +#if SHELL_DYNAMIC_EXTENSION + " Option -shellext will load the library as a shell extension.", +#endif ".log FILE|off Turn logging on or off. FILE can be stderr/stdout", ]; DISPATCHABLE_COMMAND( imposter ? 3 3 ){ @@ -9239,6 +9247,7 @@ DISPATCHABLE_COMMAND( imposter ? 3 3 ){ char *zSql; char *zCollist = 0; sqlite3_stmt *pStmt; + sqlite3 *db; int tnum = 0; int isWO = 0; /* True if making an imposter of a WITHOUT ROWID table */ int lenPK = 0; /* Length of the PRIMARY KEY string for isWO tables */ @@ -9256,8 +9265,9 @@ DISPATCHABLE_COMMAND( imposter ? 3 3 ){ return 1; } open_db(p, 0); + db = DBX(p); if( nArg==2 ){ - sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 1); + sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, "main", 0, 1); return 0; } zSql = sqlite3_mprintf( @@ -9269,7 +9279,7 @@ DISPATCHABLE_COMMAND( imposter ? 3 3 ){ " AND sql LIKE '%%without%%rowid%%'", azArg[1], azArg[1] ); - sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( sqlite3_step(pStmt)==SQLITE_ROW ){ tnum = sqlite3_column_int(pStmt, 0); @@ -9277,7 +9287,7 @@ DISPATCHABLE_COMMAND( imposter ? 3 3 ){ } sqlite3_finalize(pStmt); zSql = sqlite3_mprintf("PRAGMA index_xinfo='%q'", azArg[1]); - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); i = 0; while( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ @@ -9308,21 +9318,20 @@ DISPATCHABLE_COMMAND( imposter ? 3 3 ){ return 1; } if( lenPK==0 ) lenPK = 100000; - zSql = sqlite3_mprintf( - "CREATE TABLE \"%w\"(%s,PRIMARY KEY(%.*s))WITHOUT ROWID", - azArg[2], zCollist, lenPK, zCollist); + zSql = sqlite3_mprintf("CREATE TABLE \"%w\"(%s,PRIMARY KEY(%.*s))" + "WITHOUT ROWID", azArg[2], zCollist, lenPK, zCollist); sqlite3_free(zCollist); - rc = sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 1, tnum); + rc = sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, "main", 1, tnum); if( rc==SQLITE_OK ){ - rc = sqlite3_exec(p->db, zSql, 0, 0, 0); - sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 0); + rc = sqlite3_exec(db, zSql, 0, 0, 0); + sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, "main", 0, 0); if( rc ){ *pzErr = shellMPrintf(0,"Error in [%s]: %s\n", - zSql, sqlite3_errmsg(p->db)); + zSql, sqlite3_errmsg(db)); }else{ utf8_printf(STD_OUT, "%s;\n", zSql); - raw_printf(STD_OUT, - "WARNING: writing to an imposter table will corrupt the \"%s\" %s!\n", + raw_printf(STD_OUT, "WARNING: " + "writing to an imposter table will corrupt the \"%s\" %s!\n", azArg[1], isWO ? "table" : "index" ); } @@ -9376,7 +9385,7 @@ DISPATCHABLE_COMMAND( limits 5 1 3 ){ if( nArg==1 ){ for(i=0; idb, aLimit[i].limitCode, -1)); + sqlite3_limit(DBX(p), aLimit[i].limitCode, -1)); } }else if( nArg>3 ){ return SHELL_INVALID_ARGS; @@ -9401,50 +9410,231 @@ DISPATCHABLE_COMMAND( limits 5 1 3 ){ return 1; } if( nArg==3 ){ - sqlite3_limit(p->db, aLimit[iLimit].limitCode, + sqlite3_limit(DBX(p), aLimit[iLimit].limitCode, (int)integerValue(azArg[2])); } fprintf(STD_OUT, "%20s %d\n", aLimit[iLimit].zLimitName, - sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1)); + sqlite3_limit(DBX(p), aLimit[iLimit].limitCode, -1)); } return 0; } DISPATCHABLE_COMMAND( lint 3 1 0 ){ - open_db(p, 0); - int n = (nArg>=2 ? strlen30(azArg[1]) : 0); - if( n>0 && !sqlite3_strnicmp(azArg[1], "fkey-indexes", n) ){ - return lintFkeyIndexes(p, azArg, nArg); - } - *pzErr = sqlite3_mprintf - ("Usage %s sub-command ?switches...?\n" - "Where sub-commands are:\n" - " fkey-indexes\n", azArg[0]); - return 1; -} + sqlite3 *db; /* Database handle to query "main" db of */ + FILE *out = ISS(p)->out; /* Stream to write non-error output to */ + int bVerbose = 0; /* If -verbose is present */ + int bGroupByParent = 0; /* If -groupbyparent is present */ + int i; /* To iterate through azArg[] */ + const char *zIndent = ""; /* How much to indent CREATE INDEX by */ + int rc; /* Return code */ + sqlite3_stmt *pSql = 0; /* Compiled version of SQL statement below */ -DISPATCHABLE_COMMAND( load ? 2 3 ){ - const char *zFile, *zProc; - char *zErrMsg = 0; - if( p->bSafeMode ) return SHELL_FORBIDDEN_OP; - zFile = azArg[1]; - zProc = nArg>=3 ? azArg[2] : 0; - open_db(p, 0); - if( SQLITE_OK!=sqlite3_load_extension(p->db, zFile, zProc, pzErr) ){ + i = (nArg>=2 ? strlen30(azArg[1]) : 0); + if( i==0 || 0!=sqlite3_strnicmp(azArg[1], "fkey-indexes", i) ){ + *pzErr = sqlite3_mprintf + ("Usage %s sub-command ?switches...?\n" + "Where sub-commands are:\n" + " fkey-indexes\n", azArg[0]); return 1; } - return 0; + open_db(p, 0); + db = DBX(p); + + /* + ** This SELECT statement returns one row for each foreign key constraint + ** in the schema of the main database. The column values are: + ** + ** 0. The text of an SQL statement similar to: + ** + ** "EXPLAIN QUERY PLAN SELECT 1 FROM child_table WHERE child_key=?" + ** + ** This SELECT is similar to the one that the foreign keys implementation + ** needs to run internally on child tables. If there is an index that can + ** be used to optimize this query, then it can also be used by the FK + ** implementation to optimize DELETE or UPDATE statements on the parent + ** table. + ** + ** 1. A GLOB pattern suitable for sqlite3_strglob(). If the plan output by + ** the EXPLAIN QUERY PLAN command matches this pattern, then the schema + ** contains an index that can be used to optimize the query. + ** + ** 2. Human readable text that describes the child table and columns. e.g. + ** + ** "child_table(child_key1, child_key2)" + ** + ** 3. Human readable text that describes the parent table and columns. e.g. + ** + ** "parent_table(parent_key1, parent_key2)" + ** + ** 4. A full CREATE INDEX statement for an index that could be used to + ** optimize DELETE or UPDATE statements on the parent table. e.g. + ** + ** "CREATE INDEX child_table_child_key ON child_table(child_key)" + ** + ** 5. The name of the parent table. + ** + ** These six values are used by the C logic below to generate the report. + */ + const char *zSql = + "SELECT " + " 'EXPLAIN QUERY PLAN SELECT 1 FROM ' || quote(s.name) || ' WHERE '" + " || group_concat(quote(s.name) || '.' || quote(f.[from]) || '=?' " + " || fkey_collate_clause(" + " f.[table], COALESCE(f.[to], p.[name]), s.name, f.[from]),' AND ')" + ", " + " 'SEARCH ' || s.name || ' USING COVERING INDEX*('" + " || group_concat('*=?', ' AND ') || ')'" + ", " + " s.name || '(' || group_concat(f.[from], ', ') || ')'" + ", " + " f.[table] || '(' || group_concat(COALESCE(f.[to], p.[name])) || ')'" + ", " + " 'CREATE INDEX ' || quote(s.name ||'_'|| group_concat(f.[from], '_'))" + " || ' ON ' || quote(s.name) || '('" + " || group_concat(quote(f.[from]) ||" + " fkey_collate_clause(" + " f.[table], COALESCE(f.[to], p.[name]), s.name, f.[from]), ', ')" + " || ');'" + ", " + " f.[table] " + "FROM sqlite_schema AS s, pragma_foreign_key_list(s.name) AS f " + "LEFT JOIN pragma_table_info AS p ON (pk-1=seq AND p.arg=f.[table]) " + "GROUP BY s.name, f.id " + "ORDER BY (CASE WHEN ? THEN f.[table] ELSE s.name END)" + ; + const char *zGlobIPK = "SEARCH * USING INTEGER PRIMARY KEY (rowid=?)"; + + for(i=2; i1 && sqlite3_strnicmp("-verbose", azArg[i], n)==0 ){ + bVerbose = 1; + } + else if( n>1 && sqlite3_strnicmp("-groupbyparent", azArg[i], n)==0 ){ + bGroupByParent = 1; + zIndent = " "; + } + else{ + raw_printf(STD_ERR, "Usage: %s %s ?-verbose? ?-groupbyparent?\n", + azArg[0], azArg[1] + ); + return SQLITE_ERROR; + } + } + + /* Register the fkey_collate_clause() SQL function */ + rc = sqlite3_create_function(db, "fkey_collate_clause", 4, SQLITE_UTF8, + 0, shellFkeyCollateClause, 0, 0 + ); + + if( rc==SQLITE_OK ){ + rc = sqlite3_prepare_v2(db, zSql, -1, &pSql, 0); + } + if( rc==SQLITE_OK ){ + sqlite3_bind_int(pSql, 1, bGroupByParent); + } + + if( rc==SQLITE_OK ){ + int rc2; + char *zPrev = 0; + while( SQLITE_ROW==sqlite3_step(pSql) ){ + int res = -1; + sqlite3_stmt *pExplain = 0; + const char *zEQP = (const char*)sqlite3_column_text(pSql, 0); + const char *zGlob = (const char*)sqlite3_column_text(pSql, 1); + const char *zFrom = (const char*)sqlite3_column_text(pSql, 2); + const char *zTarget = (const char*)sqlite3_column_text(pSql, 3); + const char *zCI = (const char*)sqlite3_column_text(pSql, 4); + const char *zParent = (const char*)sqlite3_column_text(pSql, 5); + + if( zEQP==0 || zGlob==0 ) continue; + rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0); + if( rc!=SQLITE_OK ) break; + if( SQLITE_ROW==sqlite3_step(pExplain) ){ + const char *zPlan = (const char*)sqlite3_column_text(pExplain, 3); + res = zPlan!=0 && ( 0==sqlite3_strglob(zGlob, zPlan) + || 0==sqlite3_strglob(zGlobIPK, zPlan)); + } + rc = sqlite3_finalize(pExplain); + if( rc!=SQLITE_OK ) break; + + if( res<0 ){ + raw_printf(STD_ERR, "Error: internal error"); + break; + }else{ + if( bGroupByParent + && (bVerbose || res==0) + && (zPrev==0 || sqlite3_stricmp(zParent, zPrev)) + ){ + raw_printf(out, "-- Parent table %s\n", zParent); + sqlite3_free(zPrev); + zPrev = sqlite3_mprintf("%s", zParent); + } + + if( res==0 ){ + raw_printf(out, "%s%s --> %s\n", zIndent, zCI, zTarget); + }else if( bVerbose ){ + raw_printf(out, "%s/* no extra indexes required for %s -> %s */\n", + zIndent, zFrom, zTarget); + } + } + } + sqlite3_free(zPrev); + + if( rc!=SQLITE_OK ){ + raw_printf(STD_ERR, "%s\n", sqlite3_errmsg(db)); + } + + rc2 = sqlite3_finalize(pSql); + if( rc==SQLITE_OK && rc2!=SQLITE_OK ){ + rc = rc2; + raw_printf(STD_ERR, "%s\n", sqlite3_errmsg(db)); + } + }else{ + raw_printf(STD_ERR, "%s\n", sqlite3_errmsg(db)); + } + + return rc; +} + +DISPATCHABLE_COMMAND( load ? 2 4 ){ + const char *zFile = 0, *zProc = 0; + char *zErrMsg = 0; + int ai = 1, rc; +#if SHELL_DYNAMIC_EXTENSION + u8 bLoadExt = 0; +#endif + if( ISS(p)->bSafeMode ) return SHELL_FORBIDDEN_OP; + while( aibSafeMode ) return SHELL_FORBIDDEN_OP; - output_file_close(p->pLog); - p->pLog = output_file_open(zFile, 0); + if( ISS(p)->bSafeMode ) return SHELL_FORBIDDEN_OP; + output_file_close(ISS(p)->pLog); + ISS(p)->pLog = output_file_open(zFile, 0); return 0; } -static void effectMode(ShellState *p, u8 modeRequest, u8 modeNominal){ +static void effectMode(ShellInState *psi, u8 modeRequest, u8 modeNominal){ /* Effect the specified mode change. */ const char *zColSep = 0, *zRowSep = 0; assert(modeNominal!=MODE_COUNT_OF); @@ -9453,8 +9643,8 @@ static void effectMode(ShellState *p, u8 modeRequest, u8 modeNominal){ zRowSep = SEP_Row; break; case MODE_Column: - if( (p->shellFlgs & SHFLG_HeaderSet)==0 ){ - p->showHeader = 1; + if( (psi->shellFlgs & SHFLG_HeaderSet)==0 ){ + psi->showHeader = 1; } zRowSep = SEP_Row; break; @@ -9502,12 +9692,12 @@ static void effectMode(ShellState *p, u8 modeRequest, u8 modeNominal){ return; } if( zRowSep!=0 ){ - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, zRowSep); + sqlite3_snprintf(sizeof(psi->rowSeparator), psi->rowSeparator, zRowSep); } if( zColSep!=0 ){ - sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, zColSep); + sqlite3_snprintf(sizeof(psi->colSeparator), psi->colSeparator, zColSep); } - p->mode = modeNominal; + psi->mode = modeNominal; } /***************** @@ -9540,6 +9730,7 @@ COLLECT_HELP_TEXT[ " TABLE The name of SQL table used for \"insert\" mode", ]; DISPATCHABLE_COMMAND( mode ? 1 0 ){ + ShellInState *psi = ISS(p); const char *zTabname = 0; const char *zArg; int i; @@ -9583,30 +9774,31 @@ DISPATCHABLE_COMMAND( mode ? 1 0 ){ } } /* Arg loop */ if( foundMode==MODE_COUNT_OF ){ + FILE *out = psi->out; const char *zMode; int nms; - i = p->mode; + i = psi->mode; assert(i>=0 && imode) ){ + if( MODE_IS_COLUMNAR(psi->mode) ){ raw_printf - (p->out, "current output mode: %.*s --wrap %d --wordwrap %s --%squote\n", - nms, zMode, p->cmOpts.iWrap, - p->cmOpts.bWordWrap ? "on" : "off", - p->cmOpts.bQuote ? "" : "no"); + (out, "current output mode: %.*s --wrap %d --wordwrap %s --%squote\n", + nms, zMode, psi->cmOpts.iWrap, + psi->cmOpts.bWordWrap ? "on" : "off", + psi->cmOpts.bQuote ? "" : "no"); }else{ - raw_printf(p->out, "current output mode: %.*s\n", nms, zMode); + raw_printf(out, "current output mode: %.*s\n", nms, zMode); } }else{ - effectMode(p, foundMode, setMode); - if( MODE_IS_COLUMNAR(setMode) ) p->cmOpts = cmOpts; + effectMode(psi, foundMode, setMode); + if( MODE_IS_COLUMNAR(setMode) ) psi->cmOpts = cmOpts; else if( setMode==MODE_Insert ){ set_table_name(p, zTabname ? zTabname : "table"); } } - p->cMode = p->mode; + psi->cMode = psi->mode; return 0; flag_unknown: utf8_printf(STD_ERR, "Error: Unknown .mode option: %s\nValid options:\n%s", @@ -9647,11 +9839,14 @@ COLLECT_HELP_TEXT[ ".nullvalue STRING Use STRING in place of NULL values", ]; DISPATCHABLE_COMMAND( open 3 1 0 ){ + ShellInState *psi = ISS(p); const char *zFN = 0; /* Pointer to constant filename */ char *zNewFilename = 0; /* Name of the database file to open */ int iName = 1; /* Index in azArg[] of the filename */ int newFlag = 0; /* True to delete file before opening */ u8 openMode = SHELL_OPEN_UNSPEC; + int openFlags = 0; + sqlite3_int64 szMax = 0; int rc = 0; /* Check for command-line arguments */ for(iName=1; iNameopenFlags |= SQLITE_OPEN_NOFOLLOW; + openFlags |= SQLITE_OPEN_NOFOLLOW; #ifndef SQLITE_OMIT_DESERIALIZE }else if( optionMatch(z, "deserialize") ){ openMode = SHELL_OPEN_DESERIALIZE; }else if( optionMatch(z, "hexdb") ){ openMode = SHELL_OPEN_HEXDB; }else if( optionMatch(z, "maxsize") && iName+1szMax = integerValue(azArg[++iName]); + szMax = integerValue(azArg[++iName]); #endif /* SQLITE_OMIT_DESERIALIZE */ }else if( z[0]=='-' ){ *pzErr = shellMPrintf(0,"unknown option: %s\n", z); @@ -9688,21 +9883,21 @@ DISPATCHABLE_COMMAND( open 3 1 0 ){ } /* Close the existing database */ - session_close_all(p, -1); - close_db(p->db); - p->db = 0; - p->pAuxDb->zDbFilename = 0; - sqlite3_free(p->pAuxDb->zFreeOnClose); - p->pAuxDb->zFreeOnClose = 0; - p->openMode = openMode; - p->openFlags = 0; - p->szMax = 0; + session_close_all(psi, -1); + close_db(DBX(p)); + DBX(p) = 0; + psi->pAuxDb->zDbFilename = 0; + sqlite3_free(psi->pAuxDb->zFreeOnClose); + psi->pAuxDb->zFreeOnClose = 0; + psi->openMode = openMode; + psi->openFlags = 0; + psi->szMax = 0; /* If a filename is specified, try to open it first */ - if( zFN || p->openMode==SHELL_OPEN_HEXDB ){ - if( newFlag && zFN && !p->bSafeMode ) shellDeleteFile(zFN); - if( p->bSafeMode - && p->openMode!=SHELL_OPEN_HEXDB + if( zFN || psi->openMode==SHELL_OPEN_HEXDB ){ + if( newFlag && zFN && !psi->bSafeMode ) shellDeleteFile(zFN); + if( psi->bSafeMode + && psi->openMode!=SHELL_OPEN_HEXDB && zFN && strcmp(zFN,":memory:")!=0 ){ @@ -9715,38 +9910,41 @@ DISPATCHABLE_COMMAND( open 3 1 0 ){ }else{ zNewFilename = 0; } - p->pAuxDb->zDbFilename = zNewFilename; + psi->pAuxDb->zDbFilename = zNewFilename; + psi->openFlags = openFlags; + psi->szMax = szMax; open_db(p, OPEN_DB_KEEPALIVE); - if( p->db==0 ){ + if( DBX(p)==0 ){ *pzErr = shellMPrintf(0,"Error: cannot open '%s'\n", zNewFilename); sqlite3_free(zNewFilename); rc = 1; }else{ - p->pAuxDb->zFreeOnClose = zNewFilename; + psi->pAuxDb->zFreeOnClose = zNewFilename; } } - if( p->db==0 ){ + if( DBX(p)==0 ){ /* As a fall-back open a TEMP database */ - p->pAuxDb->zDbFilename = 0; + psi->pAuxDb->zDbFilename = 0; open_db(p, 0); } return rc; } DISPATCHABLE_COMMAND( nonce ? 2 2 ){ - if( p->zNonce==0 || strcmp(azArg[1],p->zNonce)!=0 ){ + ShellInState *psi = ISS(p); + if( psi->zNonce==0 || strcmp(azArg[1],psi->zNonce)!=0 ){ raw_printf(STD_ERR, "line %d: incorrect nonce: \"%s\"\n", - p->pInSource->lineno, azArg[1]); + psi->pInSource->lineno, azArg[1]); exit(1); } /* Suspend safe mode for 1 meta-command after this. */ - p->bSafeModeFuture = 2; + psi->bSafeModeFuture = 2; return 0; } DISPATCHABLE_COMMAND( nullvalue ? 2 2 ){ - sqlite3_snprintf(sizeof(p->nullValue), p->nullValue, - "%.*s", (int)ArraySize(p->nullValue)-1, azArg[1]); + sqlite3_snprintf(sizeof(ISS(p)->nullValue), ISS(p)->nullValue, "%.*s", + (int)ArraySize(ISS(p)->nullValue)-1, azArg[1]); return 0; } @@ -10106,10 +10304,11 @@ static int param_set(sqlite3 *db, char cCast, } /* list or ls subcommand for .parameter dot-command */ -static void list_params(ShellState *p, ParamTableUse ptu, u8 bShort, +static void list_params(ShellExState *psx, ParamTableUse ptu, u8 bShort, char **pzArgs, int nArg){ sqlite3_stmt *pStmt = 0; - sqlite3_str *sbList = sqlite3_str_new(p->db); + sqlite3 *db = DBX(psx); + sqlite3_str *sbList = sqlite3_str_new(db); int len = 0, rc; char *zFromWhere = 0; char *zSql = 0; @@ -10121,7 +10320,7 @@ static void list_params(ShellState *p, ParamTableUse ptu, u8 bShort, shell_check_oom(zFromWhere); zSql = sqlite3_mprintf("SELECT max(length(key)) %s", zFromWhere); shell_check_oom(zSql); - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); if( rc==SQLITE_OK ){ sqlite3_bind_int(pStmt, 1, ptu); if( sqlite3_step(pStmt)==SQLITE_ROW ){ @@ -10133,30 +10332,31 @@ static void list_params(ShellState *p, ParamTableUse ptu, u8 bShort, sqlite3_finalize(pStmt); pStmt = 0; if( len ){ + FILE *out = ISS(psx)->out; sqlite3_free(zSql); if( !bShort ){ int nBindings = 0, nScripts = 0; zSql = sqlite3_mprintf("SELECT key, uses, iif(uses, value, quote(value))" " %z ORDER BY uses, key", zFromWhere); shell_check_oom(zSql); - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); sqlite3_bind_int(pStmt, 1, ptu); while( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ ParamTableUse ptux = sqlite3_column_int(pStmt,1); switch( ptux ){ case PTU_Binding: if( nBindings++ == 0 ){ - utf8_printf(p->out, "Binding Values:\n%-*s %s\n", + utf8_printf(out, "Binding Values:\n%-*s %s\n", len, "name", "value"); } - utf8_printf(p->out, "%-*s %s\n", len, sqlite3_column_text(pStmt,0), + utf8_printf(out, "%-*s %s\n", len, sqlite3_column_text(pStmt,0), sqlite3_column_text(pStmt,2)); break; case PTU_Script: if( nScripts++ == 0 ){ - utf8_printf(p->out, "Scripts\n%-*s %s\n", len, "name", "value"); + utf8_printf(out, "Scripts\n%-*s %s\n", len, "name", "value"); } - utf8_printf(p->out, "%-*s %s\n", len, sqlite3_column_text(pStmt,0), + utf8_printf(out, "%-*s %s\n", len, sqlite3_column_text(pStmt,0), sqlite3_column_text(pStmt,2)); break; default: break; /* Ignore */ @@ -10166,13 +10366,13 @@ static void list_params(ShellState *p, ParamTableUse ptu, u8 bShort, int nc = 0, ncw = 78/(len+2); zSql = sqlite3_mprintf("SELECT key %z ORDER BY key", zFromWhere); shell_check_oom(zSql); - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); sqlite3_bind_int(pStmt, 1, ptu); while( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ - utf8_printf(p->out, "%s %-*s", ((++nc%ncw==0)? "\n" : ""), + utf8_printf(out, "%s %-*s", ((++nc%ncw==0)? "\n" : ""), len, sqlite3_column_text(pStmt,0)); } - if( nc>0 ) utf8_printf(p->out, "\n"); + if( nc>0 ) utf8_printf(out, "\n"); } sqlite3_finalize(pStmt); }else{ @@ -10232,7 +10432,7 @@ COLLECT_HELP_TEXT[ " clear ?NAMES? Erase all or only given named parameters", #ifndef SQLITE_NOHAVE_SYSTEM " edit ?OPT? NAME ... Use edit() to create or alter parameter NAME", - " OPT may be -t or -e to use edited value as text or evaluate it first.", + " OPT may be -t to use edited value as text or -e to evaluate it.", #endif " init Initialize TEMP table for bindings and scripts", " list ?PATTERNS? List parameters table binding and script values", @@ -10243,7 +10443,7 @@ COLLECT_HELP_TEXT[ " If FILE missing, empty or '~', it defaults to ~/sqlite_params.sdb", " set ?TOPT? NAME VALUE Give SQL parameter NAME a value of VALUE", " NAME must begin with one of $,:,@,? for bindings, or with a letter", - " to be executable; the value is following argument list, space-joined.", + " to be executable; value is the space-joined argument list.", " Option TOPT may be one of {-b -i -n -r -t} to cast effective value", " to BLOB, INT, NUMERIC, REAL or TEXT respectively.", " unset ?NAMES? Remove named parameter(s) from parameters table", @@ -10251,14 +10451,15 @@ COLLECT_HELP_TEXT[ DISPATCHABLE_COMMAND( parameter 2 2 0 ){ int rc = 0; open_db(p,0); + sqlite3 *db = DBX(p); /* .parameter clear and .parameter unset ?NAMES? ** Delete some or all parameters from the TEMP table that holds them. ** Without any arguments, clear deletes them all and unset does nothing. */ if( strcmp(azArg[1],"clear")==0 || strcmp(azArg[1],"unset")==0 ){ - if( param_table_exists(p->db) && (nArg>2 || azArg[1][0]=='c') ){ - sqlite3_str *sbZap = sqlite3_str_new(p->db); + if( param_table_exists(db) && (nArg>2 || azArg[1][0]=='c') ){ + sqlite3_str *sbZap = sqlite3_str_new(db); char *zSql; sqlite3_str_appendf (sbZap, "DELETE FROM "PARAM_TABLE_SNAME" WHERE key "); @@ -10266,7 +10467,7 @@ DISPATCHABLE_COMMAND( parameter 2 2 0 ){ (const char **)&azArg[2], (const char **)&azArg[nArg]); zSql = sqlite3_str_finish(sbZap); shell_check_oom(zSql); - sqlite3_exec(p->db, zSql, 0, 0, 0); + sqlite3_exec(db, zSql, 0, 0, 0); sqlite3_free(zSql); } }else @@ -10276,28 +10477,29 @@ DISPATCHABLE_COMMAND( parameter 2 2 0 ){ ** New ones get a uses tag auto-selected by their leading char. */ if( strcmp(azArg[1],"edit")==0 ){ + ShellInState *psi = ISS(p); int ia = 2; int eval = 0; - if( !INSOURCE_IS_INTERACTIVE(p->pInSource) ){ + if( !INSOURCE_IS_INTERACTIVE(psi->pInSource) ){ utf8_printf(STD_ERR, "Error: " ".parameter edit can only be used interactively.\n"); return 1; } - param_table_init(p); - if( p->zEditor==0 ){ + param_table_init(db); + if( psi->zEditor==0 ){ const char *zE = getenv("VISUAL"); - if( zE!=0 ) p->zEditor = sqlite3_mprintf("%s", zE); + if( zE!=0 ) psi->zEditor = sqlite3_mprintf("%s", zE); } if( nArg>=3 && azArg[2][0]=='-' ){ char *zArg = (azArg[2][1]=='-')? azArg[2]+2 : azArg[2]+1; if( strncmp(zArg,"editor=",7)==0 ){ - sqlite3_free(p->zEditor); + sqlite3_free(psi->zEditor); /* Accept an initial -editor=? option. */ - p->zEditor = sqlite3_mprintf("%s", zArg+7); + psi->zEditor = sqlite3_mprintf("%s", zArg+7); ++ia; } } - if( p->zEditor==0 ){ + if( psi->zEditor==0 ){ utf8_printf(STD_ERR, "Either set env-var VISUAL to name an" " editor and restart, or rerun\n " @@ -10328,7 +10530,7 @@ DISPATCHABLE_COMMAND( parameter 2 2 0 ){ " parameter name.\n", azArg[ia]); return 1; } - rc = edit_one_param(p->db, azArg[ia], eval, ptu, p->zEditor); + rc = edit_one_param(db, azArg[ia], eval, ptu, psi->zEditor); ++ia; if( rc!=0 ) return rc; } @@ -10340,7 +10542,7 @@ DISPATCHABLE_COMMAND( parameter 2 2 0 ){ ** Create it if necessary. */ if( nArg==2 && strcmp(azArg[1],"init")==0 ){ - param_table_init(p); + param_table_init(db); }else /* .parameter list|ls @@ -10357,15 +10559,15 @@ DISPATCHABLE_COMMAND( parameter 2 2 0 ){ ** Load all or named parameters from specified or default (DB) file. */ if( strcmp(azArg[1],"load")==0 ){ - param_table_init(p); - rc = parameters_load(p->db, (const char **)azArg+1, nArg-1); + param_table_init(db); + rc = parameters_load(db, (const char **)azArg+1, nArg-1); }else /* .parameter save ** Save all or named parameters into specified or default (DB) file. */ if( strcmp(azArg[1],"save")==0 ){ - rc = parameters_save(p->db, (const char **)azArg+1, nArg-1); + rc = parameters_save(db, (const char **)azArg+1, nArg-1); }else /* .parameter set NAME VALUE @@ -10383,18 +10585,18 @@ DISPATCHABLE_COMMAND( parameter 2 2 0 ){ "Error: %s is not a usable parameter name.\n", azArg[inv]); rc = 1; }else{ - param_table_init(p); - rc = param_set(p->db, cCast, azArg[inv], + param_table_init(db); + rc = param_set(db, cCast, azArg[inv], &azArg[inv+1], &azArg[nArg], ptu); if( rc!=SQLITE_OK ){ - utf8_printf(p->out, "Error: %s\n", sqlite3_errmsg(p->db)); + utf8_printf(STD_ERR, "Error: %s\n", sqlite3_errmsg(db)); rc = 1; } } }else { /* If no command name and arg count matches, show a syntax error */ - showHelp(p->out, "parameter"); + showHelp(ISS(p)->out, "parameter"); return 1; } @@ -10417,33 +10619,32 @@ COLLECT_HELP_TEXT[ DISPATCHABLE_COMMAND( print 3 1 0 ){ int i; for(i=1; i1 ) raw_printf(p->out, " "); - utf8_printf(p->out, "%s", azArg[i]); + utf8_printf(ISS(p)->out, "%s%s", azArg[i], (i==nArg-1)? "\n" : " "); } - raw_printf(p->out, "\n"); return 0; } DISPATCHABLE_COMMAND( progress 3 2 0 ){ + ShellInState *psi = ISS(p); int i; int nn = 0; - p->flgProgress = 0; - p->mxProgress = 0; - p->nProgress = 0; + psi->flgProgress = 0; + psi->mxProgress = 0; + psi->nProgress = 0; for(i=1; iflgProgress |= SHELL_PROGRESS_QUIET; + psi->flgProgress |= SHELL_PROGRESS_QUIET; continue; } if( strcmp(z,"reset")==0 ){ - p->flgProgress |= SHELL_PROGRESS_RESET; + psi->flgProgress |= SHELL_PROGRESS_RESET; continue; } if( strcmp(z,"once")==0 ){ - p->flgProgress |= SHELL_PROGRESS_ONCE; + psi->flgProgress |= SHELL_PROGRESS_ONCE; continue; } if( strcmp(z,"limit")==0 ){ @@ -10451,18 +10652,18 @@ DISPATCHABLE_COMMAND( progress 3 2 0 ){ *pzErr = shellMPrintf(0,"Error: missing argument on --limit\n"); return SHELL_INVALID_ARGS; }else{ - p->mxProgress = (int)integerValue(azArg[++i]); + psi->mxProgress = (int)integerValue(azArg[++i]); } continue; } - *pzErr = shellMPrintf(0,"Error: unknown option: \"%s\"\n", azArg[i]); + *pzErr = shellMPrintf(0, "Error: unknown option: \"%s\"\n", azArg[i]); return SHELL_INVALID_ARGS; }else{ nn = (int)integerValue(z); } } open_db(p, 0); - sqlite3_progress_handler(p->db, nn, progress_handler, p); + sqlite3_progress_handler(DBX(p), nn, progress_handler, psi); return 0; } /* Allow too few arguments by tradition, (a form of no-op.) */ @@ -10495,12 +10696,12 @@ DISPATCHABLE_COMMAND( read 3 2 2 ){ int rc = 0; FILE *inUse = 0; int (*fCloser)(FILE *) = 0; - if( p->bSafeMode ) return SHELL_FORBIDDEN_OP; + if( ISS(p)->bSafeMode ) return SHELL_FORBIDDEN_OP; if( azArg[1][0]=='|' ){ #ifdef SQLITE_OMIT_POPEN *pzErr = shellMPrintf(0,"Error: pipes are not supported in this OS\n"); rc = 1; - p->out = STD_OUT; /* This is likely not needed. To be investigated. */ + /* p->out = STD_OUT; This was likely not needed. To be investigated. */ #else inUse = popen(azArg[1]+1, "r"); if( inUse==0 ){ @@ -10518,12 +10719,12 @@ DISPATCHABLE_COMMAND( read 3 2 2 ){ } if( inUse!=0 ){ InSource inSourceRedir - = INSOURCE_FILE_REDIR(inUse, azArg[1], p->pInSource); - p->pInSource = &inSourceRedir; - rc = process_input(p); + = INSOURCE_FILE_REDIR(inUse, azArg[1], ISS(p)->pInSource); + ISS(p)->pInSource = &inSourceRedir; + rc = process_input(ISS(p)); assert(fCloser!=0); fCloser(inUse); - p->pInSource = p->pInSource->pFrom; + ISS(p)->pInSource = inSourceRedir.pFrom; } return rc; } @@ -10534,7 +10735,8 @@ DISPATCHABLE_COMMAND( read 3 2 2 ){ ** on stream pState->out. */ DISPATCHABLE_COMMAND( recover ? 1 7 ){ - open_db(p, 0); + FILE *out = ISS(p)->out; + sqlite3 *db; int rc = SQLITE_OK; sqlite3_stmt *pLoop = 0; /* Loop through all root pages */ sqlite3_stmt *pPages = 0; /* Loop through all pages in a group */ @@ -10545,6 +10747,9 @@ DISPATCHABLE_COMMAND( recover ? 1 7 ){ int nOrphan = -1; RecoverTable *pOrphan = 0; + open_db(p, 0); + db = DBX(p); + int bFreelist = 1; /* 0 if --freelist-corrupt is specified */ int bRowids = 1; /* 0 if --no-rowids */ for(i=1; iout, azArg[0]); + showHelp(out, azArg[0]); return 1; } } - shellExecPrintf(p->db, &rc, + shellExecPrintf(db, &rc, /* Attach an in-memory database named 'recovery'. Create an indexed ** cache of the sqlite_dbptr virtual table. */ "PRAGMA writable_schema = on;" @@ -10586,7 +10791,7 @@ DISPATCHABLE_COMMAND( recover ? 1 7 ){ ); if( bFreelist ){ - shellExec(p->db, &rc, + shellExec(db, &rc, "WITH trunk(pgno) AS (" " SELECT shell_int32(" " (SELECT data FROM sqlite_dbpage WHERE pgno=1), 8) AS x " @@ -10610,7 +10815,7 @@ DISPATCHABLE_COMMAND( recover ? 1 7 ){ /* If this is an auto-vacuum database, add all pointer-map pages to ** the freelist table. Do this regardless of whether or not ** --freelist-corrupt was specified. */ - shellExec(p->db, &rc, + shellExec(db, &rc, "WITH ptrmap(pgno) AS (" " SELECT 2 WHERE shell_int32(" " (SELECT data FROM sqlite_dbpage WHERE pgno=1), 13" @@ -10622,7 +10827,7 @@ DISPATCHABLE_COMMAND( recover ? 1 7 ){ "REPLACE INTO recovery.freelist SELECT pgno FROM ptrmap" ); - shellExec(p->db, &rc, + shellExec(db, &rc, "CREATE TABLE recovery.dbptr(" " pgno, child, PRIMARY KEY(child, pgno)" ") WITHOUT ROWID;" @@ -10699,25 +10904,23 @@ DISPATCHABLE_COMMAND( recover ? 1 7 ){ ** foreign key constraints to be violated. So disable foreign-key ** constraint enforcement to prevent problems when running the output ** script. */ - raw_printf(p->out, "PRAGMA foreign_keys=OFF;\n"); - raw_printf(p->out, "BEGIN;\n"); - raw_printf(p->out, "PRAGMA writable_schema = on;\n"); - shellPrepare(p->db, &rc, + raw_printf(out, "PRAGMA foreign_keys=OFF;\n"); + raw_printf(out, "BEGIN;\n"); + raw_printf(out, "PRAGMA writable_schema = on;\n"); + shellPrepare(db, &rc, "SELECT sql FROM recovery.schema " "WHERE type='table' AND sql LIKE 'create table%'", &pStmt ); while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ const char *zCreateTable = (const char*)sqlite3_column_text(pStmt, 0); - raw_printf(p->out, "CREATE TABLE IF NOT EXISTS %s;\n", - &zCreateTable[12] - ); + raw_printf(out, "CREATE TABLE IF NOT EXISTS %s;\n", &zCreateTable[12]); } shellFinalize(&rc, pStmt); } /* Figure out if an orphan table will be required. And if so, how many ** user columns it should contain */ - shellPrepare(p->db, &rc, + shellPrepare(db, &rc, "SELECT coalesce(max(maxlen), -2) FROM recovery.map WHERE root>1" , &pLoop ); @@ -10727,11 +10930,11 @@ DISPATCHABLE_COMMAND( recover ? 1 7 ){ shellFinalize(&rc, pLoop); pLoop = 0; - shellPrepare(p->db, &rc, + shellPrepare(db, &rc, "SELECT pgno FROM recovery.map WHERE root=?", &pPages ); - shellPrepare(p->db, &rc, + shellPrepare(db, &rc, "SELECT max(field), group_concat(shell_escape_crnl(quote" "(case when (? AND field<0) then NULL else value end)" "), ', ')" @@ -10741,7 +10944,7 @@ DISPATCHABLE_COMMAND( recover ? 1 7 ){ ); /* Loop through each root page. */ - shellPrepare(p->db, &rc, + shellPrepare(db, &rc, "SELECT root, intkey, max(maxlen) FROM recovery.map" " WHERE root>1 GROUP BY root, intkey ORDER BY root=(" " SELECT rootpage FROM recovery.schema WHERE name='sqlite_sequence'" @@ -10755,18 +10958,18 @@ DISPATCHABLE_COMMAND( recover ? 1 7 ){ RecoverTable *pTab; assert( bIntkey==0 || bIntkey==1 ); - pTab = recoverFindTable(p, &rc, iRoot, bIntkey, nCol, &bNoop); + pTab = recoverFindTable(db, &rc, iRoot, bIntkey, nCol, &bNoop); if( bNoop || rc ) continue; if( pTab==0 ){ if( pOrphan==0 ){ - pOrphan = recoverOrphanTable(p, &rc, zLostAndFound, nOrphan); + pOrphan = recoverOrphanTable(db, out, &rc, zLostAndFound, nOrphan); } pTab = pOrphan; if( pTab==0 ) break; } if( 0==sqlite3_stricmp(pTab->zQuoted, "\"sqlite_sequence\"") ){ - raw_printf(p->out, "DELETE FROM sqlite_sequence;\n"); + raw_printf(out, "DELETE FROM sqlite_sequence;\n"); } sqlite3_bind_int(pPages, 1, iRoot); if( bRowids==0 && pTab->iPk<0 ){ @@ -10787,7 +10990,7 @@ DISPATCHABLE_COMMAND( recover ? 1 7 ){ RecoverTable *pTab2 = pTab; if( pTab!=pOrphan && (iMin<0)!=bIntkey ){ if( pOrphan==0 ){ - pOrphan = recoverOrphanTable(p, &rc, zLostAndFound, nOrphan); + pOrphan = recoverOrphanTable(db, out, &rc, zLostAndFound, nOrphan); } pTab2 = pOrphan; if( pTab2==0 ) break; @@ -10795,13 +10998,13 @@ DISPATCHABLE_COMMAND( recover ? 1 7 ){ nField = nField+1; if( pTab2==pOrphan ){ - raw_printf(p->out, + raw_printf(out, "INSERT INTO %s VALUES(%d, %d, %d, %s%s%s);\n", pTab2->zQuoted, iRoot, iPgno, nField, iMin<0 ? "" : "NULL, ", zVal, pTab2->azlCol[nField] ); }else{ - raw_printf(p->out, "INSERT INTO %s(%s) VALUES( %s );\n", + raw_printf(out, "INSERT INTO %s(%s) VALUES( %s );\n", pTab2->zQuoted, pTab2->azlCol[nField], zVal ); } @@ -10819,7 +11022,7 @@ DISPATCHABLE_COMMAND( recover ? 1 7 ){ /* The rest of the schema */ if( rc==SQLITE_OK ){ sqlite3_stmt *pStmt = 0; - shellPrepare(p->db, &rc, + shellPrepare(db, &rc, "SELECT sql, name FROM recovery.schema " "WHERE sql NOT LIKE 'create table%'", &pStmt ); @@ -10831,20 +11034,20 @@ DISPATCHABLE_COMMAND( recover ? 1 7 ){ "INSERT INTO sqlite_schema VALUES('table', %Q, %Q, 0, %Q)", zName, zName, zSql ); - raw_printf(p->out, "%s;\n", zPrint); + raw_printf(out, "%s;\n", zPrint); sqlite3_free(zPrint); }else{ - raw_printf(p->out, "%s;\n", zSql); + raw_printf(out, "%s;\n", zSql); } } shellFinalize(&rc, pStmt); } if( rc==SQLITE_OK ){ - raw_printf(p->out, "PRAGMA writable_schema = off;\n"); - raw_printf(p->out, "COMMIT;\n"); + raw_printf(out, "PRAGMA writable_schema = off;\n"); + raw_printf(out, "COMMIT;\n"); } - sqlite3_exec(p->db, "DETACH recovery", 0, 0, 0); + sqlite3_exec(db, "DETACH recovery", 0, 0, 0); return rc; } @@ -10856,7 +11059,7 @@ DISPATCHABLE_COMMAND( restore ? 2 3 ){ sqlite3_backup *pBackup; int nTimeout = 0; - if( p->bSafeMode ) return SHELL_FORBIDDEN_OP; + if( ISS(p)->bSafeMode ) return SHELL_FORBIDDEN_OP; if( nArg==2 ){ zSrcFile = azArg[1]; zDb = "main"; @@ -10873,9 +11076,9 @@ DISPATCHABLE_COMMAND( restore ? 2 3 ){ return 1; } open_db(p, 0); - pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main"); + pBackup = sqlite3_backup_init(DBX(p), zDb, pSrc, "main"); if( pBackup==0 ){ - *pzErr = shellMPrintf(0,"Error: %s\n", sqlite3_errmsg(p->db)); + *pzErr = shellMPrintf(0,"Error: %s\n", sqlite3_errmsg(DBX(p))); close_db(pSrc); return 1; } @@ -10893,7 +11096,7 @@ DISPATCHABLE_COMMAND( restore ? 2 3 ){ *pzErr = shellMPrintf(0,"Error: source database is busy\n"); rc = 1; }else{ - *pzErr = shellMPrintf(0,"Error: %s\n", sqlite3_errmsg(p->db)); + *pzErr = shellMPrintf(0,"Error: %s\n", sqlite3_errmsg(DBX(p))); rc = 1; } close_db(pSrc); @@ -10911,16 +11114,17 @@ COLLECT_HELP_TEXT[ " --nosys Omit objects whose names start with \"sqlite_\"", ]; DISPATCHABLE_COMMAND( scanstats ? 2 2 ){ - p->scanstatsOn = (u8)booleanValue(azArg[1]); + ISS(p)->scanstatsOn = (u8)booleanValue(azArg[1]); #ifndef SQLITE_ENABLE_STMT_SCANSTATUS - raw_printf(STD_ERR, "Warning: .scanstats not available in this build.\n"); + raw_printf(STD_ERR, "Warning: .scanstats not available in this build.\n"); #endif return 0; } DISPATCHABLE_COMMAND( schema ? 1 2 ){ int rc; ShellText sSelect; - ShellState data; + ShellInState data; + ShellExState datax; char *zErrMsg = 0; const char *zDiv = "("; const char *zName = 0; @@ -10930,7 +11134,12 @@ DISPATCHABLE_COMMAND( schema ? 1 2 ){ int ii; open_db(p, 0); - memcpy(&data, p, sizeof(data)); + /* Consider some refactoring to avoid duplicative wholesale copying. */ + memcpy(&data, ISS(p), sizeof(data)); + memcpy(&datax, p, sizeof(datax)); + data.pSXS = &datax; + datax.pSIS = &data; + data.showHeader = 0; data.cMode = data.mode = MODE_Semi; initText(&sSelect); @@ -10969,16 +11178,17 @@ DISPATCHABLE_COMMAND( schema ? 1 2 ){ new_argv[1] = 0; new_colv[0] = "sql"; new_colv[1] = 0; - callback(&data, 1, new_argv, new_colv); + callback(&datax, 1, new_argv, new_colv); sqlite3_free(new_argv[0]); } } if( zDiv ){ sqlite3_stmt *pStmt = 0; - rc = sqlite3_prepare_v2(p->db, "SELECT name FROM pragma_database_list", + rc = sqlite3_prepare_v2(datax.dbUser, + "SELECT name FROM pragma_database_list", -1, &pStmt, 0); if( rc ){ - *pzErr = shellMPrintf(0,"Error: %s\n", sqlite3_errmsg(p->db)); + *pzErr = shellMPrintf(0,"Error: %s\n", sqlite3_errmsg(datax.dbUser)); sqlite3_finalize(pStmt); return 1; } @@ -11040,22 +11250,21 @@ DISPATCHABLE_COMMAND( schema ? 1 2 ){ appendText(&sSelect, "sql IS NOT NULL" " ORDER BY snum, rowid", 0); if( bDebug ){ - utf8_printf(p->out, "SQL: %s;\n", sSelect.z); + utf8_printf(data.out, "SQL: %s;\n", sSelect.z); }else{ - rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg); + rc = sqlite3_exec(datax.dbUser, sSelect.z, callback, &datax, &zErrMsg); } freeText(&sSelect); } if( zErrMsg ){ *pzErr = zErrMsg; - rc = 1; + return 1; }else if( rc != SQLITE_OK ){ *pzErr = shellMPrintf(0,"Error: querying schema information\n"); - rc = 1; + return 1; }else{ - rc = 0; + return 0; } - return rc; } /***************** @@ -11093,19 +11302,20 @@ DISPATCHABLE_COMMAND( selecttrace ? 1 0 ){ } DISPATCHABLE_COMMAND( separator ? 2 3 ){ if( nArg>=2 ){ - sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, - "%.*s", (int)ArraySize(p->colSeparator)-1, azArg[1]); + sqlite3_snprintf(sizeof(ISS(p)->colSeparator), ISS(p)->colSeparator, + "%.*s", (int)ArraySize(ISS(p)->colSeparator)-1, azArg[1]); } if( nArg>=3 ){ - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, - "%.*s", (int)ArraySize(p->rowSeparator)-1, azArg[2]); + sqlite3_snprintf(sizeof(ISS(p)->rowSeparator), ISS(p)->rowSeparator, + "%.*s", (int)ArraySize(ISS(p)->rowSeparator)-1, azArg[2]); } return 0; } DISPATCHABLE_COMMAND( session 3 2 0 ){ int rc = 0; - struct AuxDb *pAuxDb = p->pAuxDb; + struct AuxDb *pAuxDb = ISS(p)->pAuxDb; OpenSession *pSession = &pAuxDb->aSession[0]; + FILE *out = ISS(p)->out; char **azCmd = &azArg[1]; int iSes = 0; int nCmd = nArg - 1; @@ -11148,15 +11358,15 @@ DISPATCHABLE_COMMAND( session 3 2 0 ){ ** Write a changeset or patchset into a file. The file is overwritten. */ if( strcmp(azCmd[0],"changeset")==0 || strcmp(azCmd[0],"patchset")==0 ){ - FILE *out = 0; + FILE *cs_out = 0; if( failIfSafeMode (p, "cannot run \".session %s\" in safe mode", azCmd[0]) ){ rc = SHELL_FORBIDDEN_OP; }else{ if( nCmd!=2 ) goto session_syntax_error; if( pSession->p==0 ) goto session_not_open; - out = fopen(azCmd[1], "wb"); - if( out==0 ){ + cs_out = fopen(azCmd[1], "wb"); + if( cs_out==0 ){ *pzErr = sqlite3_mprintf ("ERROR: cannot open \"%s\" for writing\n", azCmd[1]); rc = 1; @@ -11169,15 +11379,15 @@ DISPATCHABLE_COMMAND( session 3 2 0 ){ rc = sqlite3session_patchset(pSession->p, &szChng, &pChng); } if( rc ){ - fprintf(STD_OUT, "Error: error code %d\n", rc); + fprintf(out, "Error: error code %d\n", rc); rc = 0; } - if( pChng && fwrite(pChng, szChng, 1, out)!=1 ){ + if( pChng && fwrite(pChng, szChng, 1, cs_out)!=1 ){ raw_printf(STD_ERR, "ERROR: Failed to write entire %d-byte output\n", szChng); } sqlite3_free(pChng); - fclose(out); + fclose(cs_out); } } }else @@ -11202,7 +11412,7 @@ DISPATCHABLE_COMMAND( session 3 2 0 ){ ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); if( pAuxDb->nSession ){ ii = sqlite3session_enable(pSession->p, ii); - utf8_printf(p->out, "session %s enable flag = %d\n", + utf8_printf(out, "session %s enable flag = %d\n", pSession->zName, ii); } }else @@ -11240,7 +11450,7 @@ DISPATCHABLE_COMMAND( session 3 2 0 ){ ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); if( pAuxDb->nSession ){ ii = sqlite3session_indirect(pSession->p, ii); - utf8_printf(p->out, "session %s indirect flag = %d\n", + utf8_printf(out, "session %s indirect flag = %d\n", pSession->zName, ii); } }else @@ -11253,7 +11463,7 @@ DISPATCHABLE_COMMAND( session 3 2 0 ){ if( nCmd!=1 ) goto session_syntax_error; if( pAuxDb->nSession ){ ii = sqlite3session_isempty(pSession->p); - utf8_printf(p->out, "session %s isempty flag = %d\n", + utf8_printf(out, "session %s isempty flag = %d\n", pSession->zName, ii); } }else @@ -11263,7 +11473,7 @@ DISPATCHABLE_COMMAND( session 3 2 0 ){ */ if( strcmp(azCmd[0],"list")==0 ){ for(i=0; inSession; i++){ - utf8_printf(p->out, "%d %s\n", i, pAuxDb->aSession[i].zName); + utf8_printf(out, "%d %s\n", i, pAuxDb->aSession[i].zName); } }else @@ -11288,7 +11498,7 @@ DISPATCHABLE_COMMAND( session 3 2 0 ){ return rc; } pSession = &pAuxDb->aSession[pAuxDb->nSession]; - rc = sqlite3session_create(p->db, azCmd[1], &pSession->p); + rc = sqlite3session_create(DBX(p), azCmd[1], &pSession->p); if( rc ){ *pzErr = sqlite3_mprintf ("Cannot open session: error code=%d\n", rc); @@ -11302,7 +11512,7 @@ DISPATCHABLE_COMMAND( session 3 2 0 ){ /* If no command name matches, show a syntax error */ session_syntax_error: - showHelp(p->out, "session"); + showHelp(out, "session"); return 1; } return rc; @@ -11360,7 +11570,7 @@ DISPATCHABLE_COMMAND( sha3sum 4 1 1 ){ " AND name NOT LIKE 'sqlite_%'" " ORDER BY 1 collate nocase"; } - sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + sqlite3_prepare_v2(DBX(p), zSql, -1, &pStmt, 0); initText(&sQuery); initText(&sSql); appendText(&sSql, "WITH [sha3sum$query](a,b) AS(",0); @@ -11412,7 +11622,7 @@ DISPATCHABLE_COMMAND( sha3sum 4 1 1 ){ freeText(&sQuery); freeText(&sSql); if( bDebug ){ - utf8_printf(p->out, "%s\n", zSql); + utf8_printf(ISS(p)->out, "%s\n", zSql); }else{ shell_exec(p, zSql, 0); } @@ -11444,6 +11654,7 @@ COLLECT_HELP_TEXT[ ]; DISPATCHABLE_COMMAND( selftest 4 0 0 ){ int rc; + ShellInState *psi = ISS(p); int bIsInit = 0; /* True to initialize the SELFTEST table */ int bVerbose = 0; /* Verbose output */ int bSelftestExists; /* True if SELFTEST already exists */ @@ -11453,7 +11664,6 @@ DISPATCHABLE_COMMAND( selftest 4 0 0 ){ ShellText str; /* Answer for a query */ sqlite3_stmt *pStmt = 0; /* Query against the SELFTEST table */ - open_db(p,0); for(i=1; idb,"main","selftest",0,0,0,0,0,0) + open_db(p,0); + if( sqlite3_table_column_metadata(DBX(p),"main","selftest",0,0,0,0,0,0) != SQLITE_OK ){ bSelftestExists = 0; }else{ bSelftestExists = 1; } if( bIsInit ){ - createSelftestTable(p); + createSelftestTable(ISS(p)); bSelftestExists = 1; } initText(&str); appendText(&str, "x", 0); for(k=bSelftestExists; k>=0; k--){ if( k==1 ){ - rc = sqlite3_prepare_v2(p->db, + rc = sqlite3_prepare_v2(DBX(p), "SELECT tno,op,cmd,ans FROM selftest ORDER BY tno", -1, &pStmt, 0); }else{ - rc = sqlite3_prepare_v2(p->db, - "VALUES(0,'memo','Missing SELFTEST table - default checks only','')," - " (1,'run','PRAGMA integrity_check','ok')", - -1, &pStmt, 0); + rc = sqlite3_prepare_v2(DBX(p), + "VALUES(0,'memo','Missing SELFTEST table - default checks only','')," + " (1,'run','PRAGMA integrity_check','ok')", + -1, &pStmt, 0); } if( rc ){ *pzErr = shellMPrintf(0,"Error querying the selftest table\n"); @@ -11507,29 +11718,30 @@ DISPATCHABLE_COMMAND( selftest 4 0 0 ){ if( zOp==0 || zSql==0 || zAns==0 ) continue; k = 0; if( bVerbose>0 ){ + /* This unusually directed output is for test purposes. */ fprintf(STD_OUT, "%d: %s %s\n", tno, zOp, zSql); } if( strcmp(zOp,"memo")==0 ){ - utf8_printf(p->out, "%s\n", zSql); + utf8_printf(psi->out, "%s\n", zSql); }else if( strcmp(zOp,"run")==0 ){ char *zErrMsg = 0; str.n = 0; str.z[0] = 0; - rc = sqlite3_exec(p->db, zSql, captureOutputCallback, &str, &zErrMsg); + rc = sqlite3_exec(DBX(p), zSql, captureOutputCallback, &str, &zErrMsg); nTest++; if( bVerbose ){ - utf8_printf(p->out, "Result: %s\n", str.z); + utf8_printf(psi->out, "Result: %s\n", str.z); } if( rc || zErrMsg ){ nErr++; rc = 1; - utf8_printf(p->out, "%d: error-code-%d: %s\n", tno, rc, zErrMsg); + utf8_printf(psi->out, "%d: error-code-%d: %s\n", tno, rc, zErrMsg); sqlite3_free(zErrMsg); }else if( strcmp(zAns,str.z)!=0 ){ nErr++; rc = 1; - utf8_printf(p->out, "%d: Expected: [%s]\n", tno, zAns); - utf8_printf(p->out, "%d: Got: [%s]\n", tno, str.z); + utf8_printf(psi->out, "%d: Expected: [%s]\n", tno, zAns); + utf8_printf(psi->out, "%d: Got: [%s]\n", tno, str.z); } }else{ *pzErr = sqlite3_mprintf @@ -11541,15 +11753,14 @@ DISPATCHABLE_COMMAND( selftest 4 0 0 ){ sqlite3_finalize(pStmt); } /* End loop over k */ freeText(&str); - utf8_printf(p->out, "%d errors out of %d tests\n", nErr, nTest); + utf8_printf(psi->out, "%d errors out of %d tests\n", nErr, nTest); return rc > 0; } -#ifndef SQLITE_NOHAVE_SYSTEM -static int shellOut(char *azArg[], int nArg, ShellState *p, char **pzErr){ +DISPATCHABLE_COMMAND( shell ? 2 0 ){ char *zCmd; int i, x; - if( p->bSafeMode ) return SHELL_FORBIDDEN_OP; + if( ISS(p)->bSafeMode ) return SHELL_FORBIDDEN_OP; zCmd = sqlite3_mprintf(strchr(azArg[1],' ')==0?"%s":"\"%s\"", azArg[1]); shell_check_oom(zCmd); for(i=2; i1 ){ for( ia=1; iabExtendedDotCmds |= shopts[io].mask; - else p->bExtendedDotCmds &= ~shopts[io].mask; + if( cs=='+' ) psi->bExtendedDotCmds |= shopts[io].mask; + else psi->bExtendedDotCmds &= ~shopts[io].mask; break; } } @@ -11603,13 +11811,13 @@ DISPATCHABLE_COMMAND( shxopts 3 0 0 ){ } } }else{ - raw_printf(p->out, + raw_printf(psi->out, " name value \"-shxopts set\"\n" " -------- ----- ---------------\n"); for( io=0; iobExtendedDotCmds & m) == m)? 1 : 0; - raw_printf(p->out, + unsigned v = ((psi->bExtendedDotCmds & m) == m)? 1 : 0; + raw_printf(psi->out, " %9s %2d \"-shxopts 0x%02X\"\n", shopts[io].name, v, m); } @@ -11620,67 +11828,71 @@ DISPATCHABLE_COMMAND( shxopts 3 0 0 ){ return 1; } DISPATCHABLE_COMMAND( system ? 2 0 ){ - return shellOut(azArg, nArg, p, pzErr); + return shellCommand(azArg, nArg, p, pzErr); } DISPATCHABLE_COMMAND( show ? 1 1 ){ static const char *azBool[] = { "off", "on", "trigger", "full"}; const char *zOut; + ShellInState *psi = ISS(p); + FILE *out = psi->out; int i; - utf8_printf(p->out, "%12.12s: %s\n","echo", + utf8_printf(out, "%12.12s: %s\n","echo", azBool[ShellHasFlag(p, SHFLG_Echo)]); - utf8_printf(p->out, "%12.12s: %s\n","eqp", azBool[p->autoEQP&3]); - utf8_printf(p->out, "%12.12s: %s\n","explain", - p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off"); - utf8_printf(p->out,"%12.12s: %s\n","headers", azBool[p->showHeader!=0]); - zOut = modeDescr[p->mode].zModeName; - i = strlen30(zOut) - modeDescr[p->mode].bDepluralize; - if( MODE_IS_COLUMNAR(p->mode) ){ + utf8_printf(out, "%12.12s: %s\n","eqp", azBool[psi->autoEQP&3]); + utf8_printf(out, "%12.12s: %s\n","explain", + psi->mode==MODE_Explain + ? "on" : psi->autoExplain ? "auto" : "off"); + utf8_printf(out,"%12.12s: %s\n","headers", azBool[psi->showHeader!=0]); + zOut = modeDescr[psi->mode].zModeName; + i = strlen30(zOut) - modeDescr[psi->mode].bDepluralize; + if( MODE_IS_COLUMNAR(psi->mode) ){ utf8_printf - (p->out, "%12.12s: %.*s --wrap %d --wordwrap %s --%squote\n", "mode", - i, zOut, p->cmOpts.iWrap, - p->cmOpts.bWordWrap ? "on" : "off", - p->cmOpts.bQuote ? "" : "no"); + (out, "%12.12s: %.*s --wrap %d --wordwrap %s --%squote\n", "mode", + i, zOut, psi->cmOpts.iWrap, + psi->cmOpts.bWordWrap ? "on" : "off", + psi->cmOpts.bQuote ? "" : "no"); }else{ - utf8_printf(p->out, "%12.12s: %.*s\n","mode", i, zOut); - } - utf8_printf(p->out, "%12.12s: ", "nullvalue"); - output_c_string(p->out, p->nullValue); - raw_printf(p->out, "\n"); - utf8_printf(p->out,"%12.12s: %s\n","output", - strlen30(p->outfile) ? p->outfile : "stdout"); - utf8_printf(p->out,"%12.12s: ", "colseparator"); - output_c_string(p->out, p->colSeparator); - raw_printf(p->out, "\n"); - utf8_printf(p->out,"%12.12s: ", "rowseparator"); - output_c_string(p->out, p->rowSeparator); - raw_printf(p->out, "\n"); - switch( p->statsOn ){ + utf8_printf(out, "%12.12s: %.*s\n","mode", i, zOut); + } + utf8_printf(out, "%12.12s: ", "nullvalue"); + output_c_string(out, psi->nullValue); + raw_printf(out, "\n"); + utf8_printf(out,"%12.12s: %s\n","output", + strlen30(psi->outfile) ? psi->outfile : "stdout"); + utf8_printf(out,"%12.12s: ", "colseparator"); + output_c_string(out, psi->colSeparator); + raw_printf(out, "\n"); + utf8_printf(out,"%12.12s: ", "rowseparator"); + output_c_string(out, psi->rowSeparator); + raw_printf(out, "\n"); + switch( psi->statsOn ){ case 0: zOut = "off"; break; default: zOut = "on"; break; case 2: zOut = "stmt"; break; case 3: zOut = "vmstep"; break; } - utf8_printf(p->out, "%12.12s: %s\n","stats", zOut); - utf8_printf(p->out, "%12.12s: ", "width"); - for (i=0;inWidth;i++) { - raw_printf(p->out, "%d ", p->colWidth[i]); + utf8_printf(out, "%12.12s: %s\n","stats", zOut); + utf8_printf(out, "%12.12s: ", "width"); + for (i=0;inumWidths;i++) { + raw_printf(out, "%d ", p->pSpecWidths[i]); } - raw_printf(p->out, "\n"); - utf8_printf(p->out, "%12.12s: %s\n", "filename", - p->pAuxDb->zDbFilename ? p->pAuxDb->zDbFilename : ""); + raw_printf(out, "\n"); + utf8_printf(out, "%12.12s: %s\n", "filename", + psi->pAuxDb->zDbFilename ? psi->pAuxDb->zDbFilename : ""); return 0; } DISPATCHABLE_COMMAND( stats ? 0 0 ){ + ShellInState *psi = ISS(p); if( nArg==2 ){ if( strcmp(azArg[1],"stmt")==0 ){ - p->statsOn = 2; + psi->statsOn = 2; }else if( strcmp(azArg[1],"vmstep")==0 ){ - p->statsOn = 3; + psi->statsOn = 3; }else{ - p->statsOn = (u8)booleanValue(azArg[1]); + psi->statsOn = (u8)booleanValue(azArg[1]); } }else if( nArg==1 ){ - display_stats(p->db, p, 0); + display_stats(DBX(p), psi, 0); }else{ *pzErr = shellMPrintf(0,"Usage: .stats ?on|off|stmt|vmstep?\n"); return 1; @@ -11697,7 +11909,7 @@ COLLECT_HELP_TEXT[ " If TABLE is specified, only show indexes for", " tables matching TABLE using the LIKE operator.", ]; -static int showTableLike(char *azArg[], int nArg, ShellState *p, +static int showTableLike(char *azArg[], int nArg, ShellExState *p, char **pzErr, char ot){ int rc; sqlite3_stmt *pStmt; @@ -11707,10 +11919,10 @@ static int showTableLike(char *azArg[], int nArg, ShellState *p, ShellText s; initText(&s); open_db(p, 0); - rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0); + rc = sqlite3_prepare_v2(DBX(p), "PRAGMA database_list", -1, &pStmt, 0); if( rc ){ sqlite3_finalize(pStmt); - return shellDatabaseError(p->db); + return shellDatabaseError(DBX(p)); } if( nArg>2 && ot=='i' ){ @@ -11765,10 +11977,10 @@ static int showTableLike(char *azArg[], int nArg, ShellState *p, rc = sqlite3_finalize(pStmt); if( rc==SQLITE_OK ){ appendText(&s, " ORDER BY 1", 0); - rc = sqlite3_prepare_v2(p->db, s.z, -1, &pStmt, 0); + rc = sqlite3_prepare_v2(DBX(p), s.z, -1, &pStmt, 0); } freeText(&s); - if( rc ) return shellDatabaseError(p->db); + if( rc ) return shellDatabaseError(DBX(p)); /* Run the SQL statement prepared by the above block. Store the results ** as an array of nul-terminated strings in azResult[]. */ @@ -11793,7 +12005,7 @@ static int showTableLike(char *azArg[], int nArg, ShellState *p, nRow++; } if( sqlite3_finalize(pStmt)!=SQLITE_OK ){ - rc = shellDatabaseError(p->db); + rc = shellDatabaseError(DBX(p)); } /* Pretty-print the contents of array azResult[] to the output */ @@ -11811,10 +12023,10 @@ static int showTableLike(char *azArg[], int nArg, ShellState *p, for(i=0; iout, "%s%-*s", zSp, maxlen, + utf8_printf(ISS(p)->out, "%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:""); } - raw_printf(p->out, "\n"); + raw_printf(ISS(p)->out, "\n"); } } @@ -11859,33 +12071,6 @@ DISPATCHABLE_COMMAND( indices 3 1 2 ){ return showTableLike(azArg, nArg, p, pzErr, 'i'); } -/***************** - * The .unmodule command - */ -CONDITION_COMMAND( unmodule defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_VIRTUALTABLE) ); -COLLECT_HELP_TEXT[ - ".unmodule NAME ... Unregister virtual table modules", - " --allexcept Unregister everything except those named", -]; -DISPATCHABLE_COMMAND( unmodule ? 2 0 ){ - int ii; - int lenOpt; - char *zOpt; - open_db(p, 0); - zOpt = azArg[1]; - if( zOpt[0]=='-' && zOpt[1]=='-' && zOpt[2]!=0 ) zOpt++; - lenOpt = (int)strlen(zOpt); - if( lenOpt>=3 && strncmp(zOpt, "-allexcept",lenOpt)==0 ){ - assert( azArg[nArg]==0 ); - sqlite3_drop_modules(p->db, nArg>2 ? (const char**)(azArg+2) : 0); - }else{ - for(ii=1; iidb, azArg[ii], 0, 0); - } - } - return 0; -} - /***************** * The .testcase, .testctrl, .timeout, .timer and .trace commands */ @@ -11915,18 +12100,21 @@ COLLECT_HELP_TEXT[ /* Begin redirecting output to the file "testcase-out.txt" */ DISPATCHABLE_COMMAND( testcase ? 0 0 ){ - output_reset(p); - p->out = output_file_open("testcase-out.txt", 0); - if( p->out==0 ){ + ShellInState *psi = ISS(p); + output_reset(psi); + psi->out = output_file_open("testcase-out.txt", 0); + if( psi->out==0 ){ raw_printf(STD_ERR, "Error: cannot open 'testcase-out.txt'\n"); } if( nArg>=2 ){ - sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]); + sqlite3_snprintf(sizeof(psi->zTestcase), psi->zTestcase, "%s", azArg[1]); }else{ - sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "?"); + sqlite3_snprintf(sizeof(psi->zTestcase), psi->zTestcase, "?"); } + return 0; } DISPATCHABLE_COMMAND( testctrl ? 0 0 ){ + FILE *out = ISS(p)->out; static const struct { const char *zCtrlName; /* Name of a test-control option */ int ctrlCode; /* Integer code for that option */ @@ -11974,9 +12162,9 @@ DISPATCHABLE_COMMAND( testctrl ? 0 0 ){ /* --help lists all test-controls */ if( strcmp(zCmd,"help")==0 ){ - utf8_printf(p->out, "Available test-controls:\n"); + utf8_printf(out, "Available test-controls:\n"); for(i=0; iout, " .testctrl %s %s\n", + utf8_printf(out, " .testctrl %s %s\n", aCtrl[i].zCtrlName, aCtrl[i].zUsage); } return 1; @@ -12001,10 +12189,10 @@ DISPATCHABLE_COMMAND( testctrl ? 0 0 ){ if( testctrl<0 ){ utf8_printf(STD_ERR,"Error: unknown test-control: %s\n" "Use \".testctrl --help\" for help\n", zCmd); - }else if( aCtrl[iCtrl].unSafe && p->bSafeMode ){ + }else if( aCtrl[iCtrl].unSafe && ISS(p)->bSafeMode ){ utf8_printf(STD_ERR, - "line %d: \".testctrl %s\" may not be used in safe mode\n", - p->pInSource->lineno, aCtrl[iCtrl].zCtrlName); + "line %d: \".testctrl %s\" may not be used in safe mode\n", + ISS(p)->pInSource->lineno, aCtrl[iCtrl].zCtrlName); exit(1); }else{ switch(testctrl){ @@ -12013,7 +12201,7 @@ DISPATCHABLE_COMMAND( testctrl ? 0 0 ){ case SQLITE_TESTCTRL_OPTIMIZATIONS: if( nArg==3 ){ unsigned int opt = (unsigned int)strtol(azArg[2], 0, 0); - rc2 = sqlite3_test_control(testctrl, p->db, opt); + rc2 = sqlite3_test_control(testctrl, DBX(p), opt); isOk = 3; } break; @@ -12049,7 +12237,7 @@ DISPATCHABLE_COMMAND( testctrl ? 0 0 ){ if( nArg==3 ){ db = 0; }else{ - db = p->db; + db = DBX(p); /* Make sure the schema has been loaded */ sqlite3_table_column_metadata(db, 0, "x", 0, 0, 0, 0, 0, 0); } @@ -12080,13 +12268,13 @@ DISPATCHABLE_COMMAND( testctrl ? 0 0 ){ /* sqlite3_test_control(sqlite3*) */ case SQLITE_TESTCTRL_INTERNAL_FUNCTIONS: - rc2 = sqlite3_test_control(testctrl, p->db); + rc2 = sqlite3_test_control(testctrl, DBX(p)); isOk = 3; break; case SQLITE_TESTCTRL_IMPOSTER: if( nArg==5 ){ - rc2 = sqlite3_test_control(testctrl, p->db, + rc2 = sqlite3_test_control(testctrl, DBX(p), azArg[2], integerValue(azArg[3]), integerValue(azArg[4])); @@ -12096,8 +12284,8 @@ DISPATCHABLE_COMMAND( testctrl ? 0 0 ){ case SQLITE_TESTCTRL_SEEK_COUNT: { u64 x = 0; - rc2 = sqlite3_test_control(testctrl, p->db, &x); - utf8_printf(p->out, "%llu\n", x); + rc2 = sqlite3_test_control(testctrl, DBX(p), &x); + utf8_printf(out, "%llu\n", x); isOk = 3; break; } @@ -12105,7 +12293,7 @@ DISPATCHABLE_COMMAND( testctrl ? 0 0 ){ #ifdef YYCOVERAGE case SQLITE_TESTCTRL_PARSER_COVERAGE: { if( nArg==2 ){ - sqlite3_test_control(testctrl, p->out); + sqlite3_test_control(testctrl, out); isOk = 3; } break; @@ -12128,11 +12316,11 @@ DISPATCHABLE_COMMAND( testctrl ? 0 0 ){ int val = 0; rc2 = sqlite3_test_control(testctrl, -id, &val); if( rc2!=SQLITE_OK ) break; - if( id>1 ) utf8_printf(p->out, " "); - utf8_printf(p->out, "%d: %d", id, val); + if( id>1 ) utf8_printf(out, " "); + utf8_printf(out, "%d: %d", id, val); id++; } - if( id>1 ) utf8_printf(p->out, "\n"); + if( id>1 ) utf8_printf(out, "\n"); isOk = 3; } break; @@ -12141,18 +12329,18 @@ DISPATCHABLE_COMMAND( testctrl ? 0 0 ){ } } if( isOk==0 && iCtrl>=0 ){ - utf8_printf(p->out, "Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); + utf8_printf(out, "Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); return 1; }else if( isOk==1 ){ - raw_printf(p->out, "%d\n", rc2); + raw_printf(out, "%d\n", rc2); }else if( isOk==2 ){ - raw_printf(p->out, "0x%08x\n", rc2); + raw_printf(out, "0x%08x\n", rc2); } return 0; } DISPATCHABLE_COMMAND( timeout 4 1 2 ){ open_db(p, 0); - sqlite3_busy_timeout(p->db, nArg>=2 ? (int)integerValue(azArg[1]) : 0); + sqlite3_busy_timeout(DBX(p), nArg>=2 ? (int)integerValue(azArg[1]) : 0); return 0; } DISPATCHABLE_COMMAND( timer ? 2 2 ){ @@ -12164,6 +12352,7 @@ DISPATCHABLE_COMMAND( timer ? 2 2 ){ return 0; } DISPATCHABLE_COMMAND( trace ? 0 0 ){ + ShellInState *psi = ISS(p); int mType = 0; int jj; open_db(p, 0); @@ -12171,15 +12360,15 @@ DISPATCHABLE_COMMAND( trace ? 0 0 ){ const char *z = azArg[jj]; if( z[0]=='-' ){ if( optionMatch(z, "expanded") ){ - p->eTraceType = SHELL_TRACE_EXPANDED; + psi->eTraceType = SHELL_TRACE_EXPANDED; } #ifdef SQLITE_ENABLE_NORMALIZE else if( optionMatch(z, "normalized") ){ - p->eTraceType = SHELL_TRACE_NORMALIZED; + psi->eTraceType = SHELL_TRACE_NORMALIZED; } #endif else if( optionMatch(z, "plain") ){ - p->eTraceType = SHELL_TRACE_PLAIN; + psi->eTraceType = SHELL_TRACE_PLAIN; } else if( optionMatch(z, "profile") ){ mType |= SQLITE_TRACE_PROFILE; @@ -12198,15 +12387,42 @@ DISPATCHABLE_COMMAND( trace ? 0 0 ){ return 1; } }else{ - output_file_close(p->traceOut); - p->traceOut = output_file_open(azArg[1], 0); + output_file_close(psi->traceOut); + psi->traceOut = output_file_open(azArg[1], 0); } } - if( p->traceOut==0 ){ - sqlite3_trace_v2(p->db, 0, 0, 0); + if( psi->traceOut==0 ){ + sqlite3_trace_v2(DBX(p), 0, 0, 0); }else{ if( mType==0 ) mType = SQLITE_TRACE_STMT; - sqlite3_trace_v2(p->db, mType, sql_trace_callback, p); + sqlite3_trace_v2(DBX(p), mType, sql_trace_callback, psi); + } + return 0; +} + +/***************** + * The .unmodule command + */ +CONDITION_COMMAND( unmodule defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_VIRTUALTABLE) ); +COLLECT_HELP_TEXT[ + ".unmodule NAME ... Unregister virtual table modules", + " --allexcept Unregister everything except those named", +]; +DISPATCHABLE_COMMAND( unmodule ? 2 0 ){ + int ii; + int lenOpt; + char *zOpt; + open_db(p, 0); + zOpt = azArg[1]; + if( zOpt[0]=='-' && zOpt[1]=='-' && zOpt[2]!=0 ) zOpt++; + lenOpt = (int)strlen(zOpt); + if( lenOpt>=3 && strncmp(zOpt, "-allexcept",lenOpt)==0 ){ + assert( azArg[nArg]==0 ); + sqlite3_drop_modules(DBX(p), nArg>2 ? (const char**)(azArg+2) : 0); + }else{ + for(ii=1; iidb, azArg[2], azArg[3], + rc = sqlite3_user_authenticate(DBX(p), azArg[2], azArg[3], strlen30(azArg[3])); if( rc ){ *pzErr = shellMPrintf(0,"Authentication failed for user %s\n", azArg[2]); @@ -12246,7 +12462,7 @@ DISPATCHABLE_COMMAND( user ? 0 0 ){ if( nArg!=5 ){ goto teach_fail; } - rc = sqlite3_user_add(p->db, azArg[2], azArg[3], strlen30(azArg[3]), + rc = sqlite3_user_add(DBX(p), azArg[2], azArg[3], strlen30(azArg[3]), booleanValue(azArg[4])); if( rc ){ *pzErr = shellMPrintf(0,"User-Add failed: %d\n", rc); @@ -12256,7 +12472,7 @@ DISPATCHABLE_COMMAND( user ? 0 0 ){ if( nArg!=5 ){ goto teach_fail; } - rc = sqlite3_user_change(p->db, azArg[2], azArg[3], strlen30(azArg[3]), + rc = sqlite3_user_change(DBX(p), azArg[2], azArg[3], strlen30(azArg[3]), booleanValue(azArg[4])); if( rc ){ *pzErr = shellMPrintf(0,"User-Edit failed: %d\n", rc); @@ -12266,7 +12482,7 @@ DISPATCHABLE_COMMAND( user ? 0 0 ){ if( nArg!=3 ){ goto teach_fail; } - rc = sqlite3_user_delete(p->db, azArg[2]); + rc = sqlite3_user_delete(DBX(p), azArg[2]); if( rc ){ *pzErr = shellMPrintf(0,"User-Delete failed: %d\n", rc); return 1; @@ -12287,34 +12503,36 @@ COLLECT_HELP_TEXT[ ".vfsname ?AUX? Print the name of the VFS stack", ]; DISPATCHABLE_COMMAND( version ? 1 1 ){ - utf8_printf(p->out, "SQLite %s %s\n" /*extra-version-info*/, + FILE *out = ISS(p)->out; + utf8_printf(out, "SQLite %s %s\n" /*extra-version-info*/, sqlite3_libversion(), sqlite3_sourceid()); #if SQLITE_HAVE_ZLIB - utf8_printf(p->out, "zlib version %s\n", zlibVersion()); + utf8_printf(out, "zlib version %s\n", zlibVersion()); #endif #define CTIMEOPT_VAL_(opt) #opt #define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) #if defined(__clang__) && defined(__clang_major__) - utf8_printf(p->out, "clang-" CTIMEOPT_VAL(__clang_major__) "." + utf8_printf(out, "clang-" CTIMEOPT_VAL(__clang_major__) "." CTIMEOPT_VAL(__clang_minor__) "." CTIMEOPT_VAL(__clang_patchlevel__) "\n"); #elif defined(_MSC_VER) - utf8_printf(p->out, "msvc-" CTIMEOPT_VAL(_MSC_VER) "\n"); + utf8_printf(out, "msvc-" CTIMEOPT_VAL(_MSC_VER) "\n"); #elif defined(__GNUC__) && defined(__VERSION__) - utf8_printf(p->out, "gcc-" __VERSION__ "\n"); + utf8_printf(out, "gcc-" __VERSION__ "\n"); #endif return 0; } DISPATCHABLE_COMMAND( vfsinfo ? 1 2 ){ const char *zDbName = nArg==2 ? azArg[1] : "main"; sqlite3_vfs *pVfs = 0; - if( p->db ){ - sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFS_POINTER, &pVfs); + if( DBX(p) ){ + sqlite3_file_control(DBX(p), zDbName, SQLITE_FCNTL_VFS_POINTER, &pVfs); if( pVfs ){ - utf8_printf(p->out, "vfs.zName = \"%s\"\n", pVfs->zName); - raw_printf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); - raw_printf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); - raw_printf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); + FILE *out = ISS(p)->out; + utf8_printf(out, "vfs.zName = \"%s\"\n", pVfs->zName); + raw_printf(out, "vfs.iVersion = %d\n", pVfs->iVersion); + raw_printf(out, "vfs.szOsFile = %d\n", pVfs->szOsFile); + raw_printf(out, "vfs.mxPathname = %d\n", pVfs->mxPathname); } } return 0; @@ -12322,17 +12540,18 @@ DISPATCHABLE_COMMAND( vfsinfo ? 1 2 ){ DISPATCHABLE_COMMAND( vfslist ? 1 1 ){ sqlite3_vfs *pVfs; sqlite3_vfs *pCurrent = 0; - if( p->db ){ - sqlite3_file_control(p->db, "main", SQLITE_FCNTL_VFS_POINTER, &pCurrent); + FILE *out = ISS(p)->out; + if( DBX(p) ){ + sqlite3_file_control(DBX(p), "main", SQLITE_FCNTL_VFS_POINTER, &pCurrent); } for(pVfs=sqlite3_vfs_find(0); pVfs; pVfs=pVfs->pNext){ - utf8_printf(p->out, "vfs.zName = \"%s\"%s\n", pVfs->zName, + utf8_printf(out, "vfs.zName = \"%s\"%s\n", pVfs->zName, pVfs==pCurrent ? " <--- CURRENT" : ""); - raw_printf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); - raw_printf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); - raw_printf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); + raw_printf(out, "vfs.iVersion = %d\n", pVfs->iVersion); + raw_printf(out, "vfs.szOsFile = %d\n", pVfs->szOsFile); + raw_printf(out, "vfs.mxPathname = %d\n", pVfs->mxPathname); if( pVfs->pNext ){ - raw_printf(p->out, "-----------------------------------\n"); + raw_printf(out, "-----------------------------------\n"); } } return 0; @@ -12340,10 +12559,10 @@ DISPATCHABLE_COMMAND( vfslist ? 1 1 ){ DISPATCHABLE_COMMAND( vfsname ? 0 0 ){ const char *zDbName = nArg==2 ? azArg[1] : "main"; char *zVfsName = 0; - if( p->db ){ - sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFSNAME, &zVfsName); + if( DBX(p) ){ + sqlite3_file_control(DBX(p), zDbName, SQLITE_FCNTL_VFSNAME, &zVfsName); if( zVfsName ){ - utf8_printf(p->out, "%s\n", zVfsName); + utf8_printf(ISS(p)->out, "%s\n", zVfsName); sqlite3_free(zVfsName); } } @@ -12354,19 +12573,25 @@ DISPATCHABLE_COMMAND( vfsname ? 0 0 ){ * The .width and .wheretrace commands * The .wheretrace command has no help. */ +static void setColumnWidths(ShellExState *p, char *azWidths[], int nWidths){ + int j; + p->numWidths = nWidths; + p->pSpecWidths = realloc(p->pSpecWidths, (nWidths+1)*sizeof(int)*2); + if( nWidths>0 ){ + shell_check_oom(p->pSpecWidths); + p->pHaveWidths = &p->pSpecWidths[nWidths]; + for(j=0; jpSpecWidths[j] = (int)integerValue(azWidths[j]); + p->pHaveWidths[j] = 0; + } + }else p->pHaveWidths = p->pSpecWidths; +} COLLECT_HELP_TEXT[ ".width NUM1 NUM2 ... Set minimum column widths for columnar output", " Negative values right-justify", ]; DISPATCHABLE_COMMAND( width ? 1 0 ){ - int j; - p->nWidth = nArg-1; - p->colWidth = realloc(p->colWidth, (p->nWidth+1)*sizeof(int)*2); - if( p->colWidth==0 && p->nWidth>0 ) shell_out_of_memory(); - if( p->nWidth ) p->actualWidth = &p->colWidth[p->nWidth]; - for(j=1; jcolWidth[j-1] = (int)integerValue(azArg[j]); - } + setColumnWidths(p, azArg+1, nArg-1); return 0; } DISPATCHABLE_COMMAND( wheretrace ? 1 2 ){ @@ -12385,21 +12610,22 @@ COLLECT_HELP_TEXT[ DISPATCHABLE_COMMAND( x ? 1 0 ){ int ia, rc, nErrors = 0; sqlite3_stmt *pStmt = 0; + sqlite3 *db; + open_db(p, 0); - if( p->db==0 ){ + db = DBX(p); + if( db==0 ){ utf8_printf(STD_ERR, ".x can only be done with a database open.\n"); return 1; } - if( sqlite3_table_column_metadata(p->db, PARAM_TABLE_SCHEMA, + if( sqlite3_table_column_metadata(db, PARAM_TABLE_SCHEMA, PARAM_TABLE_NAME, "key", 0, 0, 0, 0, 0)!=SQLITE_OK ){ utf8_printf(STD_ERR, "No "PARAM_TABLE_SNAME" table exists.\n"); return 1; } - rc = sqlite3_prepare_v2 - (p->db, "SELECT value FROM "PARAM_TABLE_SNAME - " WHERE key=$1 AND uses=1", - -1, &pStmt, 0); + rc = sqlite3_prepare_v2(db, "SELECT value FROM "PARAM_TABLE_SNAME + " WHERE key=$1 AND uses=1", -1, &pStmt, 0); if( rc!=SQLITE_OK ){ utf8_printf(STD_ERR, PARAM_TABLE_SNAME" is wrongly created.\n"); return 1; @@ -12422,13 +12648,14 @@ DISPATCHABLE_COMMAND( x ? 1 0 ){ */ int nle = zValue[nb-1]=='\n'; char *zSubmit = sqlite3_mprintf( "%.*s%s", nb, zValue, "\n"+nle ); + ShellInState *psi = ISS(p); InSource inRedir - = INSOURCE_STR_REDIR(zSubmit, azArg[ia], p->pInSource); + = INSOURCE_STR_REDIR(zSubmit, azArg[ia], psi->pInSource); shell_check_oom(zSubmit); - p->pInSource = &inRedir; - rc = process_input(p); + psi->pInSource = &inRedir; + rc = process_input(psi); sqlite3_free(zSubmit); - p->pInSource = inRedir.pFrom; + psi->pInSource = inRedir.pFrom; }else{ continue; /* All white, ignore. */ } @@ -12451,17 +12678,40 @@ DISPATCHABLE_COMMAND( x ? 1 0 ){ /* End of published, standard meta-command implementation functions COMMENT Build-time overrides of above meta-commands or new meta-commands may be COMMENT incorporated into shell.c via: -it COMMAND_CUSTOMIZE= -COMMENT where names a file using the methodology of the above -COMMENT section to define new or altered meta-commands and their help text. +COMMENT where names a file using the above methodology to +COMMENT define new or altered meta-commands and their help text. */ INCLUDE( COMMAND_CUSTOMIZE ); -typedef struct MetaCommand MetaCommand; +COMMENT This help text is set seperately from meta-command definition section +COMMENT for the always-built-in, non-customizable commands with visible help. +COLLECT_HELP_TEXT[ + ".exit ?CODE? Exit this program with return-code CODE or 0", + ".quit Exit this program", +]; + +static void MetaCommand_dtor(MetaCommand *); +static const char * MetaCommand_name(MetaCommand *); +static const char * MetaCommand_help(MetaCommand *, int); +static int MetaCommand_argsCheck(MetaCommand *, char **, int, char *azArgs[]); +static int MetaCommand_execute(MetaCommand *, ShellExState *, + char **, int, char *azArgs[]); + +static VTABLE_NAME(MetaCommand) meta_cmd_VtabBuiltIn = { + MetaCommand_dtor, + MetaCommand_name, + MetaCommand_help, + MetaCommand_argsCheck, + MetaCommand_execute +}; /* Define and populate command dispatch table. */ static struct CommandInfo { +#if OBJECTIFY_COMMANDS + VTABLE_NAME(MetaCommand) *mcVtabBuiltIn; +#endif const char * cmdName; - int (*cmdDoer)(char *azArg[], int nArg, ShellState *, char **pzErr); + int (*cmdDoer)(char *azArg[], int nArg, ShellExState *, char **pzErr); unsigned char minLen, minArgs, maxArgs; #if OBJECTIFY_COMMANDS const char *azHelp[2]; /* primary and secondary help text */ @@ -12469,18 +12719,49 @@ static struct CommandInfo { #endif } command_table[] = { COMMENT Emit the dispatch table entries generated and collected above. +#if OBJECTIFY_COMMANDS +# define META_CMD_INFO(cmd, nlenMin, minArgs, maxArgs, ht0, ht1 ) \ + { &meta_cmd_VtabBuiltIn, #cmd, cmd ## Command, \ + nlenMin, minArgs, maxArgs, { ht0, ht1 }, 0 \ + } + EMIT_METACMD_INIT(2); + { 0, 0, 0, 0, ~0, ~0, {0,0}, 0 } +#else EMIT_DISPATCH(2); - { 0, 0, 0, -1, -1 } + { 0, 0, 0, ~0, ~0 } +#endif }; static unsigned numCommands = sizeof(command_table)/sizeof(struct CommandInfo) - 1; -COMMENT This help text is set seperately from meta-command definition section -COMMENT for the always-built-in, non-customizable commands with visible help. -COLLECT_HELP_TEXT[ - ".exit ?CODE? Exit this program with return-code CODE or 0", - ".quit Exit this program", -]; +static void MetaCommand_dtor(MetaCommand *pMe){ + UNUSED_PARAMETER(pMe); +} + +static const char * MetaCommand_name(MetaCommand *pMe){ + return ((struct CommandInfo *)pMe)->cmdName; +} + +static const char * MetaCommand_help(MetaCommand *pMe, int more){ + if( more>=0 && more<2 ) return ((struct CommandInfo *)pMe)->azHelp[more]; + else return 0; +} + +static int MetaCommand_argsCheck(MetaCommand *pMe, + char **pzErrMsg, int nArgs, char *azArgs[]){ + struct CommandInfo *pci = (struct CommandInfo *)pMe; + UNUSED_PARAMETER(pzErrMsg); /* Future: Make this more informative. */ + UNUSED_PARAMETER(nArgs); + UNUSED_PARAMETER(azArgs); + if( pci->minArgs > nArgs||(pci->maxArgs > 0 && pci->maxArgs < nArgs) ){ + return SHELL_INVALID_ARGS; + }else return 0; +} + +static int MetaCommand_execute(MetaCommand *pMe, ShellExState *pssx, + char **pzErrMsg, int nArgs, char *azArgs[]){ + return (((struct CommandInfo *)pMe)->cmdDoer)(azArgs, nArgs, pssx, pzErrMsg); +} /* ** Text of help messages. @@ -12517,7 +12798,7 @@ static const char *(azHelp[]) = { ** The return is either a dispatch error or whatever the dispatched ** meta-command returns. */ -int dispatchCommand(char *azArg[], int nArg, ShellState *pSS, char **pzErr){ +int dispatchCommand(char *azArg[], int nArg, ShellExState *psx, char **pzErr){ const char *cmdName = azArg[0]; int cmdLen = strlen30(cmdName); struct CommandInfo *pci = 0; @@ -12545,7 +12826,7 @@ int dispatchCommand(char *azArg[], int nArg, ShellState *pSS, char **pzErr){ } /* Replace any user-shortened command name with its whole name. */ azArg[0] = (char *)pci->cmdName; - return (pci->cmdDoer)(azArg, nArg, pSS, pzErr); + return (pci->cmdDoer)(azArg, nArg, psx, pzErr); } /* @@ -12554,7 +12835,7 @@ int dispatchCommand(char *azArg[], int nArg, ShellState *pSS, char **pzErr){ ** ** Return 1 on error, 2 to exit, and 0 otherwise. */ -static int do_meta_command(char *zLine, ShellState *p){ +static int do_meta_command(char *zLine, ShellExState *psx){ int h = 1; int nArg = 0; int n, c; @@ -12562,12 +12843,12 @@ static int do_meta_command(char *zLine, ShellState *p){ char *azArg[52]; #if SHELL_VARIABLE_EXPANSION int ncLineIn = strlen30(zLine); - u8 bExpVars = SHEXT_VAREXP(p); + u8 bExpVars = SHEXT_VAREXP(ISS(psx)); #endif #ifndef SQLITE_OMIT_VIRTUALTABLE - if( p->expert.pExpert ){ - expertFinish(p, 1, 0); + if( ISS(psx)->expert.pExpert ){ + expertFinish(ISS(psx), 1, 0); } #endif @@ -12601,14 +12882,14 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nArg==0 ) return 0; /* no tokens, no error */ n = strlen30(azArg[0]); c = azArg[0][0]; - clearTempFile(p); + clearTempFile(ISS(psx)); /* Check for the special, non-dispatched meta-commands. */ if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){ if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) - p->abruptExit = rc; + psx->shellAbruptExit = rc; rc = 2; }else @@ -12626,7 +12907,7 @@ static int do_meta_command(char *zLine, ShellState *p){ int i, v; for(i=1; iout, "%s: %d 0x%x\n", azArg[i], v, v); + utf8_printf(ISS(psx)->out, "%s: %d 0x%x\n", azArg[i], v, v); } } if( strncmp(azArg[0]+9, "integer", n-9)==0 ){ @@ -12635,7 +12916,7 @@ static int do_meta_command(char *zLine, ShellState *p){ char zBuf[200]; v = integerValue(azArg[i]); sqlite3_snprintf(sizeof(zBuf),zBuf,"%s: %lld 0x%llx\n", azArg[i],v,v); - utf8_printf(p->out, "%s", zBuf); + utf8_printf(ISS(psx)->out, "%s", zBuf); } } }else @@ -12643,8 +12924,8 @@ static int do_meta_command(char *zLine, ShellState *p){ /* The meta-command is not among the specially handled ones. Dispatch it. */ { char *zErr = 0; - int dispatchResult = dispatchCommand(azArg, nArg, p, &zErr); - if( p->abruptExit!=0 ){ + int dispatchResult = dispatchCommand(azArg, nArg, psx, &zErr); + if( psx->shellAbruptExit!=0 ){ dispatchResult = SHELL_FORBIDDEN_OP; } switch( dispatchResult ){ @@ -12676,7 +12957,7 @@ static int do_meta_command(char *zLine, ShellState *p){ utf8_printf(STD_ERR, "Error: \".%s\" forbidden in --safe mode\n", azArg[0]); } - p->abruptExit = 3; + psx->shellAbruptExit = 3; rc = 2; default: if( 0!=dispatchResult ) rc = 1; @@ -12688,11 +12969,11 @@ static int do_meta_command(char *zLine, ShellState *p){ } meta_command_exit: - if( p->outCount ){ - p->outCount--; - if( p->outCount==0 ) output_reset(p); + if( ISS(psx)->outCount ){ + ISS(psx)->outCount--; + if( ISS(psx)->outCount==0 ) output_reset(ISS(psx)); } - updateSafeMode(p); + updateSafeMode(ISS(psx)); #if SHELL_VARIABLE_EXPANSION if( bExpVars ){ /* Free any arguments that are allocated rather than tokenized in place. */ @@ -12851,15 +13132,17 @@ static int line_is_complete(char *zSql, int nSql){ ** Run a single line of SQL. Return the number of errors. ** bAltIn indicates that input has been redirected in some way. */ -static int runOneSqlLine(ShellState *p, char *zSql, int bAltIn, int startline){ +static int runOneSqlLine(ShellExState *psx, char *zSql, + int bAltIn, int startline){ int rc; char *zErrMsg = 0; - open_db(p, 0); - if( ShellHasFlag(p,SHFLG_Backslash) ) resolve_backslashes(zSql); - if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; + open_db(psx, 0); + if( ShellHasFlag(psx,SHFLG_Backslash) ) resolve_backslashes(zSql); + if( ISS(psx)->flgProgress & SHELL_PROGRESS_RESET ) ISS(psx)->nProgress = 0; BEGIN_TIMER; - rc = shell_exec(p, zSql, &zErrMsg); + /* This shell_exec call will also echo the submitted SQL when echo is on. */ + rc = shell_exec(psx, zSql, &zErrMsg); END_TIMER; if( rc || zErrMsg ){ char zPrefix[100]; @@ -12867,7 +13150,7 @@ static int runOneSqlLine(ShellState *p, char *zSql, int bAltIn, int startline){ const char *zErrorType; if( zErrMsg==0 ){ zErrorType = "Error"; - zErrorTail = sqlite3_errmsg(p->db); + zErrorTail = sqlite3_errmsg(DBX(psx)); }else if( strncmp(zErrMsg, "in prepare, ",12)==0 ){ zErrorType = "Parse error"; zErrorTail = &zErrMsg[12]; @@ -12887,12 +13170,12 @@ static int runOneSqlLine(ShellState *p, char *zSql, int bAltIn, int startline){ utf8_printf(STD_ERR, "%s %s\n", zPrefix, zErrorTail); sqlite3_free(zErrMsg); return 1; - }else if( ShellHasFlag(p, SHFLG_CountChanges) ){ + }else if( ShellHasFlag(psx, SHFLG_CountChanges) ){ char zLineBuf[2000]; sqlite3_snprintf(sizeof(zLineBuf), zLineBuf, "changes: %lld total_changes: %lld", - sqlite3_changes64(p->db), sqlite3_total_changes64(p->db)); - raw_printf(p->out, "%s\n", zLineBuf); + sqlite3_changes64(DBX(psx)), sqlite3_total_changes64(DBX(psx))); + raw_printf(ISS(psx)->out, "%s\n", zLineBuf); } return 0; } @@ -13051,7 +13334,7 @@ static void grow_line_buffer(char **pz, int *pna, int ncNeed){ ** 2 => exit demanded, no errors. ** 3 => exit demanded, errors>0 */ -static int process_input(ShellState *p){ +static int process_input(ShellInState *psi){ char *zLineInput = 0; /* a line-at-a-time input buffer or usable result */ char *zLineAccum = 0; /* accumulation buffer, used for multi-line input */ /* Above two pointers could be local to the group handling loop, but are @@ -13061,12 +13344,12 @@ static int process_input(ShellState *p){ /* Some flags for ending the overall group processing loop, always 1 or 0 */ u8 bErrorBail=0, bInputEnd=0, bExitDemand=0, bInterrupted=0; /* Flag to affect prompting and interrupt action */ - u8 bInteractive = (p->pInSource==&stdInSource && stdin_is_interactive); + u8 bInteractive = INSOURCE_IS_INTERACTIVE(psi->pInSource); int nErrors = 0; /* count of errors during execution or its prep */ /* Block overly-recursive or absurdly nested input redirects. */ - if( p->inputNesting==MAX_INPUT_NESTING ){ - InSource *pInSrc = p->pInSource->pFrom; + if( psi->inputNesting>=MAX_INPUT_NESTING ){ + InSource *pInSrc = psi->pInSource->pFrom; const char *zLead = "Input nesting limit (" SHELL_STRINGIFY(MAX_INPUT_NESTING)") reached,"; int i = 3; @@ -13081,7 +13364,7 @@ static int process_input(ShellState *p){ utf8_printf(STD_ERR, " ...\nERROR: Check recursion.\n"); return 1; } - ++p->inputNesting; + ++psi->inputNesting; /* line-group processing loop (per SQL block, dot-command or comment) */ while( !bErrorBail && !bInputEnd && !bExitDemand && !bInterrupted ){ @@ -13111,8 +13394,8 @@ static int process_input(ShellState *p){ int *pncLineUse = &ncLineIn; /* ref that line's char count */ int iStartline = 0; /* starting line number of group */ - fflush(p->out); - zLineInput = one_input_line(p->pInSource, zLineInput, nGroupLines>0); + fflush(psi->out); + zLineInput = one_input_line(psi->pInSource, zLineInput, nGroupLines>0); if( zLineInput==0 ){ bInputEnd = 1; inKind = Eof; @@ -13120,17 +13403,17 @@ static int process_input(ShellState *p){ if( bInteractive ) printf("\n"); }else{ ++nGroupLines; - iStartline = p->pInSource->lineno; + iStartline = psi->pInSource->lineno; ncLineIn = strlen30(zLineInput); if( seenInterrupt ){ - if( p->pInSource!=0 ) break; + if( psi->pInSource!=0 ) break; bInterrupted = 1; /* This will be honored, or not, later. */ seenInterrupt = 0; disposition = Dumpable; } /* Classify and check for single-line dispositions, prep for more. */ #if SHELL_EXTENDED_PARSING - ndcLeadWhite = (SHEXT_PARSING(p)) + ndcLeadWhite = (SHEXT_PARSING(psi)) ? skipWhite(zLineInput)-zLineInput : 0; /* Disallow leading whitespace for . or # in legacy mode. */ #endif @@ -13171,7 +13454,7 @@ static int process_input(ShellState *p){ disposition = Dumpable; case Cmd: #if SHELL_EXTENDED_PARSING - if( SHEXT_PARSING(p) ){ + if( SHEXT_PARSING(psi) ){ if( line_join_ends(dcScanState, *pzLineUse, pncLineUse, &cLineEnd) ){ disposition = Runnable; } @@ -13214,7 +13497,7 @@ static int process_input(ShellState *p){ pncLineUse = &ncLineAcc; } /* Read in next line of group, (if available.) */ - zLineInput = one_input_line(p->pInSource, zLineInput, nGroupLines>0); + zLineInput = one_input_line(psi->pInSource, zLineInput, nGroupLines>0); if( zLineInput==0 ){ bInputEnd = 1; inKind = Eof; @@ -13248,20 +13531,20 @@ static int process_input(ShellState *p){ /* Here, the group is fully collected or known to be incomplete forever. */ switch( disposition ){ case Dumpable: - echo_group_input(p, *pzLineUse); + echo_group_input(psi, *pzLineUse); break; case Runnable: switch( inKind ){ case Sql: /* runOneSqlLine() does its own echo when requested. */ - nErrors += runOneSqlLine(p, *pzLineUse, - INSOURCE_IS_INTERACTIVE(p->pInSource), + nErrors += runOneSqlLine(XSS(psi), *pzLineUse, + INSOURCE_IS_INTERACTIVE(psi->pInSource), iStartline); if( bail_on_error && nErrors>0 ) bErrorBail = 1; break; case Cmd: - echo_group_input(p, *pzLineUse); - switch( do_meta_command(*pzLineUse+ndcLeadWhite, p) ){ + echo_group_input(psi, *pzLineUse); + switch( do_meta_command(*pzLineUse+ndcLeadWhite, XSS(psi)) ){ default: ++nErrors; /* fall thru */ case 0: break; case 2: bExitDemand = 1; break; @@ -13271,11 +13554,11 @@ static int process_input(ShellState *p){ assert(inKind!=Tbd); break; } - if( p->abruptExit!=0 ) bExitDemand = 1; + if( XSS(psi)->shellAbruptExit!=0 ) bExitDemand = 1; break; case Erroneous: utf8_printf(STD_ERR, "Error: Input incomplete at line %d of \"%s\"\n", - p->pInSource->lineno, p->pInSource->zSourceSay); + psi->pInSource->lineno, psi->pInSource->zSourceSay); ++nErrors; break; case Ignore: @@ -13366,11 +13649,11 @@ static char *find_home_dir(int clearFlag){ ** This is a convenience function, used in several places, all of ** demand a shell exit upon failure, which leads to return 2. */ -static int run_single_query(ShellState *p, char *zSql){ +static int run_single_query(ShellExState *psx, char *zSql){ char *zErrMsg = 0; int rc; - open_db(p, 0); - rc = shell_exec(p, zSql, &zErrMsg); + open_db(psx, 0); + rc = shell_exec(psx, zSql, &zErrMsg); if( zErrMsg!=0 || rc>0 ){ /* Some kind of error, to result in exit before REPL. */ if( zErrMsg!=0 ){ @@ -13379,7 +13662,7 @@ static int run_single_query(ShellState *p, char *zSql){ }else{ utf8_printf(STD_ERR,"Error: unable to process SQL \"%s\"\n", zSql); } - p->abruptExit = (rc==0)? 1 : rc; + psx->shellAbruptExit = (rc==0)? 1 : rc; rc = 2; } return rc; @@ -13392,7 +13675,7 @@ static int run_single_query(ShellState *p, char *zSql){ ** The return is similar to process_input() (0 success, 1 error, x abort) */ static void process_sqliterc( - ShellState *p, /* Configuration data */ + ShellInState *psi, /* shell state (internal) */ const char *sqliterc_override /* Name of config file. NULL to use default */ ){ char *home_dir = NULL; @@ -13414,19 +13697,19 @@ static void process_sqliterc( inUse = fopen(sqliterc,"rb"); if( inUse!=0 ){ InSource inSourceRedir - = INSOURCE_FILE_REDIR(inUse, sqliterc, p->pInSource); + = INSOURCE_FILE_REDIR(inUse, sqliterc, psi->pInSource); int rc; - p->pInSource = &inSourceRedir; + psi->pInSource = &inSourceRedir; if( stdin_is_interactive ){ utf8_printf(STD_ERR,"-- Loading resources from %s\n",sqliterc); } - rc = process_input(p); + rc = process_input(psi); fclose(inUse); - p->pInSource = inSourceRedir.pFrom; - if( rc!=0 && bail_on_error ) p->abruptExit = rc; + psi->pInSource = inSourceRedir.pFrom; + if( rc!=0 && bail_on_error ) XSS(psi)->shellAbruptExit = rc; }else if( sqliterc_override!=0 ){ utf8_printf(STD_ERR,"cannot open: \"%s\"\n", sqliterc); - if( bail_on_error ) exit(1); + if( bail_on_error ) exit(1); /* Future: Exit shell rather than process. */ } sqlite3_free(zBuf); } @@ -13523,21 +13806,30 @@ static void verify_uninitialized(void){ } /* -** Initialize the state information in data -*/ -static void main_init(ShellState *data) { - memset(data, 0, sizeof(*data)); - data->out = STD_OUT; - data->normalMode = data->cMode = data->mode = MODE_List; - data->autoExplain = 1; - data->pAuxDb = &data->aAuxDb[0]; - memcpy(data->colSeparator,SEP_Column, 2); - memcpy(data->rowSeparator,SEP_Row, 2); - data->showHeader = 0; - data->shellFlgs = SHFLG_Lookaside; +** Initialize the state information in data and datax +*/ + static void main_init(ShellInState *pData, ShellExState *pDatax) { + memset(pData, 0, sizeof(*pData)); + memset(pDatax, 0, sizeof(*pDatax)); + pDatax->sizeofThis = sizeof(*pDatax); + pDatax->pSIS = pData; + pData->pSXS = pDatax; + pDatax->pShowHeader = &pData->showHeader; + pDatax->zFieldSeparator = &pData->colSeparator[0]; + pDatax->zRecordSeparator = &pData->rowSeparator[0]; + pDatax->zNullValue = &pData->nullValue[0]; + pData->out = STD_OUT; + pDatax->ppCurrentOutput = &pData->out; + pData->normalMode = pData->cMode = pData->mode = MODE_List; + pData->autoExplain = 1; + pData->pAuxDb = &pData->aAuxDb[0]; + memcpy(pData->colSeparator,SEP_Column, 2); + memcpy(pData->rowSeparator,SEP_Row, 2); + pData->showHeader = 0; + pData->shellFlgs = SHFLG_Lookaside; verify_uninitialized(); sqlite3_config(SQLITE_CONFIG_URI, 1); - sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); + sqlite3_config(SQLITE_CONFIG_LOG, shellLog, pData); sqlite3_config(SQLITE_CONFIG_MULTITHREAD); sqlite3_snprintf(sizeof(mainPrompt), mainPrompt,"sqlite> "); sqlite3_snprintf(sizeof(continuePrompt), continuePrompt," ...> "); @@ -13604,10 +13896,11 @@ int SQLITE_CDECL SHELL_MAIN(int argc, char **argv){ int SQLITE_CDECL SHELL_MAIN(int argc, wchar_t **wargv){ char **argv; #endif - ShellState data; + ShellInState data; + ShellExState datax; const char *zInitFile = 0; int bQuiet = 0; /* for testing, to suppress banner and history actions */ - int i; + int i, aec; int rc = 0; int warnInmemoryDb = 0; int readStdin = 1; @@ -13651,7 +13944,7 @@ int SQLITE_CDECL SHELL_MAIN(int argc, wchar_t **wargv){ exit(1); } #endif - main_init(&data); + main_init(&data,&datax); /* On Windows, we must translate command-line arguments into UTF-8. ** The SQLite memory allocator subsystem has to be enabled in order to @@ -13893,7 +14186,7 @@ int SQLITE_CDECL SHELL_MAIN(int argc, wchar_t **wargv){ ** to the sqlite command-line tool. */ if( access(data.pAuxDb->zDbFilename, 0)==0 ){ - open_db(&data, 0); + open_db(&datax, 0); } /* Process the initialization file if there is one. If no -init option @@ -13965,12 +14258,12 @@ int SQLITE_CDECL SHELL_MAIN(int argc, wchar_t **wargv){ "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-header")==0 ){ data.showHeader = 1; - ShellSetFlag(&data, SHFLG_HeaderSet); + ShellSetFlag(&datax, SHFLG_HeaderSet); }else if( strcmp(z,"-noheader")==0 ){ data.showHeader = 0; - ShellSetFlag(&data, SHFLG_HeaderSet); + ShellSetFlag(&datax, SHFLG_HeaderSet); }else if( strcmp(z,"-echo")==0 ){ - ShellSetFlag(&data, SHFLG_Echo); + ShellSetFlag(&datax, SHFLG_Echo); }else if( strcmp(z,"-eqp")==0 ){ data.autoEQP = AUTOEQP_on; }else if( strcmp(z,"-eqpfull")==0 ){ @@ -13985,7 +14278,7 @@ int SQLITE_CDECL SHELL_MAIN(int argc, wchar_t **wargv){ ** prior to sending the SQL into SQLite. Useful for injecting ** crazy bytes in the middle of SQL statements for testing and debugging. */ - ShellSetFlag(&data, SHFLG_Backslash); + ShellSetFlag(&datax, SHFLG_Backslash); }else if( strcmp(z,"-bail")==0 ){ /* No-op. The bail_on_error flag should already be set. */ #if SHELL_EXTENSIONS @@ -14037,9 +14330,9 @@ int SQLITE_CDECL SHELL_MAIN(int argc, wchar_t **wargv){ if( i==argc-1 ) break; z = cmdline_option_value(argc,argv,++i); if( z[0]=='.' ){ - rc = do_meta_command(z, &data); + rc = do_meta_command(z, &datax); }else{ - rc = run_single_query(&data, z); + rc = run_single_query(&datax, z); } if( rc && bail_on_error ){ if( rc==2 ) rc = 0; @@ -14053,12 +14346,12 @@ int SQLITE_CDECL SHELL_MAIN(int argc, wchar_t **wargv){ rc = 1; goto shell_bail; } - open_db(&data, OPEN_DB_ZIPFILE); + open_db(&datax, OPEN_DB_ZIPFILE); if( z[2] ){ argv[i] = &z[2]; - rc = arDotCommand(&data, 1, argv+(i-1), argc-(i-1)); + rc = arDotCommand(&datax, 1, argv+(i-1), argc-(i-1)); }else{ - rc = arDotCommand(&data, 1, argv+i, argc-i); + rc = arDotCommand(&datax, 1, argv+i, argc-i); } readStdin = 0; break; @@ -14074,7 +14367,7 @@ int SQLITE_CDECL SHELL_MAIN(int argc, wchar_t **wargv){ } if( zModeSet!=0 ){ char *azModeCmd[] = { ".mode", zModeSet+1 }; - modeCommand(azModeCmd, 2, &data, 0); + modeCommand(azModeCmd, 2, &datax, 0); data.cMode = data.mode; } } @@ -14086,9 +14379,9 @@ int SQLITE_CDECL SHELL_MAIN(int argc, wchar_t **wargv){ */ for(i=0; i2 || data.abruptExit>1 ){ - rc = (rc2 || aec>1 ){ + rc = (rc255 ) rc = 255; /* Clamp to valid error exit range. */ + raw_printf(STD_ERR, "Abnormal exit (%d)\n", rc); }else{ /* rc is one of 0,1,2, mapping to 0,1,0 shellexit codes. */ rc &= ~2; diff --git a/src/shext_linkage.h b/src/shext_linkage.h index d8f832595b..a8496f4a44 100644 --- a/src/shext_linkage.h +++ b/src/shext_linkage.h @@ -1,5 +1,10 @@ #ifndef SQLITE3SHX_H #define SQLITE3SHX_H +#include +#include +#include +#include + #include "sqlite3ext.h" #include "obj_interfaces.h" @@ -9,63 +14,68 @@ extern "C" { #endif /* Convey data to, from and/or between I/O handlers and meta-commands. */ -typedef struct { +typedef struct ShellExState { + /* A sizeof(*) to permit extensions to guard against too-old hosts */ + int sizeofThis; + /* A semi-transient holder of arbitrary data used during operations * not interrupted by meta-command invocations. Any not-null pointer * left after a meta-command has completed is, by contract, to be * freeable using sqlite3_free(). It is otherwise unconstrained. */ void *pvHandlerData; - /* The user's currently open and primary DB connection */ - sqlite3 *db; - /* The DB connection used for shell's dynamical data */ + /* The user's currently open and primary DB connection + * Extensions may use this DB, but must not modify this pointer. + */ + sqlite3 *dbUser; + /* DB connection for shell dynamical data and extension management + * Extensions may use this DB, but should not alter content created + * by the shell nor depend upon its schema. Names with prefix "shell_" + * or "shext_" are reserved for the shell's use. + */ sqlite3 *dbShell; - /* Input stream providing shell's command or query input */ - FILE *pCurrentInputStream; - /* Output stream to which shell's text output to be written */ - FILE *pCurrentOutputStream; + /* Output stream to which shell's text output to be written (reference) */ + FILE **ppCurrentOutput; /* Whether to exit as command completes. * 0 => no exit * ~0 => a non-error (0) exit * other => exit with process exit code other - * For embedded shell, "exit" means "return from REPL". + * For embedded shell, "exit" means "return from REPL function". */ - int shellExit; + int shellAbruptExit; /* Number of lines written during a query result output */ int resultCount; - /* Whether to show column names for certain output modes */ - int showHeader; - /* Column separator character for some modes */ + /* Whether to show column names for certain output modes (reference) */ + u8 *pShowHeader; + /* Column separator character for some modes, read-only */ char *zFieldSeparator; - /* Row separator character for some modes (MODE_Ascii) */ + /* Row separator character for some modes (MODE_Ascii), read-only */ char *zRecordSeparator; - /* Row set prefix for some modes */ + /* Row set prefix for some modes if not 0 */ char *zRecordLead; - /* Row set suffix for some modes */ - char *zRecordTrail; - /* Text to represent a NULL in external data formats */ + /* Row set suffix for some modes if not 0 */ + char *zRecordTail; + /* Text to represent a NULL in external data formats, read-only */ char *zNullValue; - /* Number of column widths presently desired or tracked */ + /* Name of table for which inserts are to be written or performed */ + const char *zDestTable; + /* Next 3 members should be set and/or allocated by .width meta-command. + * The values of pSpecWidths[i] and pHaveWidths[i] can be modified or + * used by extensions, but setColumnWidths(...) must resize those lists. + */ + /* Number of column widths presently desired or tracked, read-only */ int numWidths; /* known allocation count of next 2 members */ /* The column widths last specified via .width command */ - int *pWantWidths; - /* The column widths last observed in query results */ + int *pSpecWidths; + /* The column widths last observed in query results, read-only */ int *pHaveWidths; + /* Internal and opaque shell state, not for use by extensions */ + struct ShellInState *pSIS; /* Offset of this member is NOT STABLE. */ } ShellExState; -/* The shell's state, shared among meta-command implementations. - * The ShellStateX object includes a private partition whose content - * and usage are opaque to shell extensions compiled separately - * from the shell.c core. (As defined here, it is wholly opaque.) - */ -typedef struct ShellStateX { - ShellExState sxs; /* sizeof(ShellExState) will never shrink. */ - struct ShellState *pSS; /* The offset of this member is NOT STABLE. */ -} ShellStateX; - /* This function pointer has the same signature as the sqlite3_X_init() * function that is called as SQLite3 completes loading an extension. */ @@ -83,10 +93,10 @@ typedef int (*ExtensionId) INTERFACE_BEGIN( MetaCommand ); PURE_VMETHOD(const char *, name, MetaCommand, 0,()); PURE_VMETHOD(const char *, help, MetaCommand, 1,(int more)); -PURE_VMETHOD(struct {unsigned minArgs; unsigned maxArgs;}, - argsRange, MetaCommand, 0,()); +PURE_VMETHOD(int, argsCheck, MetaCommand, + 3, (char **pzErrMsg, int nArgs, char *azArgs[])); PURE_VMETHOD(int, execute, MetaCommand, - 4,(ShellStateX *, char **pzErrMsg, int nArgs, char *azArgs[])); + 4,(ShellExState *, char **pzErrMsg, int nArgs, char *azArgs[])); INTERFACE_END( MetaCommand ); /* Define error codes to be returned either by a meta-command during @@ -134,26 +144,60 @@ PURE_VMETHOD(void, closeDataInStream, ImportHandler, INTERFACE_END( ImportHandlerVtable ); typedef struct { - int helperCount; + int helperCount; /* Helper count, not including sentinel */ union ExtHelp { struct { - void (*failIfSafeMode)(ShellStateX *p, const char *zErrMsg, ...); + int (*failIfSafeMode)(ShellExState *p, const char *zErrMsg, ...); + FILE * (*currentOutputFile)(ShellExState *p); + struct InSource * (*currentInputSource)(ShellExState *p); + char * (*strLineGet)(char *zBuf, int ncMax, struct InSource *pInSrc); + void (*setColumnWidths)(ShellExState *p, char *azWidths[], int nWidths); + int (*nowInteractive)(ShellExState *p); + void (*sentinel)(void); } named ; - void (*nameless[2])(); /* Same as named but anonymous plus a sentinel. */ + void (*nameless[5+1])(); /* Same as named but anonymous plus a sentinel. */ } helpers; } ExtensionHelpers; -#define SHELLEXT_VALIDITY_MARK "ExtensibleShell" +/* Various shell extension helpers and feature registration functions */ +typedef struct ShellExtensionAPI { + /* Utility functions for use by extensions */ + ExtensionHelpers * pExtHelp; + /* Functions for extension to register its implementors with shell */ + const int numRegistrars; /* 3 for this version */ + union { + struct ShExtAPI { + /* Register a meta-command */ + int (*registerMetaCommand)(ShellExState *p, + ExtensionId eid, MetaCommand *pMC); + /* Register query result data display (or other disposition) mode */ + int (*registerOutMode)(ShellExState *p, + ExtensionId eid, OutModeHandler *pOMH); + /* Register an import variation from (various sources) for .import */ + int (*registerImporter)(ShellExState *p, + ExtensionId eid, ImportHandler *pIH); + /* Preset to 0 at extension load, a sentinel for expansion */ + void (*sentinel)(void); + } named; + void (*pFunctions[4])(); /* 0-terminated sequence of function pointers */ + } api; +} ShellExtensionAPI; + +/* Struct passed to extension init function to establish linkage. The + * lifetime of instances spans only the init call itself. Extensions + * should make a copy, if needed, of pShellExtensionAPI for later use. + * Its referant is static, persisting for the process duration. + */ typedef struct ShellExtensionLink { - char validityMark[16]; /* Preset to contain "ExtensibleShell\x00" */ - char *zErrMsg; /* Extension puts error message here if any. */ - int sizeOfThis; /* sizeof(struct ShellExtensionLink) */ - const char *shellVersion; /* Preset to "3.??.??\x00" or similar */ + int sizeOfThis; /* sizeof(ShellExtensionLink) for expansion */ + ShellExtensionAPI *pShellExtensionAPI; + ShellExState *pSSX; /* For use in extension feature registrations */ + char *zErrMsg; /* Extension error messages land here, if any. */ /* An init "out" parameter, used as the loaded extension ID. Unless * this is set within sqlite3_X_init() prior to register*() calls, - * the extension cannot be unloaded. + * the extension cannot be unloaded. */ ExtensionId eid; @@ -162,55 +206,23 @@ typedef struct ShellExtensionLink { */ void (*extensionDestruct)(void *); - /* Various shell extension helpers and feature registration functions - */ - ExtensionHelpers * pExtHelp; - - union ShellExtensionAPI { - struct ShExtAPI { - /* Register a meta-command */ - int (*registerMetaCommand)(ExtensionId eid, MetaCommand *pMC); - /* Register an output data display (or other disposition) mode */ - int (*registerOutMode)(ExtensionId eid, OutModeHandler *pOMH); - /* Register an import variation from (various sources) for .import */ - int (*registerImporter)(ExtensionId eid, ImportHandler *pIH); - /* Preset to 0 at extension load, a sentinel for expansion */ - void (*pExtra)(void); - } named; - void (*pFunctions[4])(); /* 0-terminated sequence of function pointers */ - } api; } ShellExtensionLink; -/* Test whether a char ** references a ShellExtensionLink instance's - * validityMark, and if so return the instance's address, else return 0. - * This macro may be used by a shell extension's sqlite3_X_init() function - * to obtain a pointer to the ShellExtensionLink struct, derived from the - * error message pointer (pzErrMsg) passed as the 2nd argument. This enables - * the extension to incorporate its features into a running shell process. - */ -#define EXTENSION_LINKAGE_PTR(pzem) ( \ - pzem != 0 && *pzem != 0 && strcmp(*pzem, SHELLEXT_VALIDITY_MARK) == 0 \ - && *pzem == (char *)pzem \ - + offsetof(ShellExtensionLink, validityMark) \ - - offsetof(ShellExtensionLink, zErrMsg) ) \ - ? (ShellExtensionLink *) \ - ((char *)pzem-offsetof(ShellExtensionLink,zErrMsg)) \ - : 0 - -/* String used with SQLite "Pointer Passing Interfaces" as a type marker. +/* String used with SQLite "Pointer Passing Interfaces" as a type marker. * That API subset is used by the shell to pass its extension API to the - * sqlite3_X_init() function of extensions, via the DB parameter. + * sqlite3_X_init() function of shell extensions, via the DB parameter. */ #define SHELLEXT_API_POINTERS "shellext_api_pointers" /* Pre-write a function to retrieve a ShellExtensionLink pointer from the - * shell's DB. This is an alternative to use of the EXTENSION_LINKAGE_PTR - * macro above. It takes some more code, replicated across extensions. + * shell's DB. This macro defines a function which will return either a + * pointer to a ShellExtensionLink instance during an extension's *init*() + * call (during shell extension load) or 0 (during SQLite extension load.) */ #define DEFINE_SHDB_TO_SHEXT_API(func_name) \ static ShellExtensionLink * func_name(sqlite3 * db){ \ ShellExtensionLink *rv = 0; sqlite3_stmt *pStmt = 0; \ - if( SQLITE_OK==sqlite3_prepare(db,"SELECT shext_pointer(0)",-1,&pStmt,0) \ + if( SQLITE_OK==sqlite3_prepare_v2(db,"SELECT shext_pointer(0)",-1,&pStmt,0) \ && SQLITE_ROW == sqlite3_step(pStmt) ) \ rv = (ShellExtensionLink *)sqlite3_value_pointer \ (sqlite3_column_value(pStmt, 0), SHELLEXT_API_POINTERS); \ diff --git a/src/test_shellext.c b/src/test_shellext.c new file mode 100644 index 0000000000..1777ae0bf1 --- /dev/null +++ b/src/test_shellext.c @@ -0,0 +1,43 @@ +/* +** 2022 Feb 28 +** +** 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. +** +************************************************************************* +** Test extension for testing the shell's .load -shellext ... function. +** gcc -shared -fPIC -Wall -I$srcdir -I.. -g test_shellext.c -o test_shellext.so +*/ +#include +#include "shext_linkage.h" + +SQLITE_EXTENSION_INIT1; + +DEFINE_SHDB_TO_SHEXT_API(shext_api); + +/* +** Extension load function. +*/ +#ifdef _WIN32 +__declspec(dllexport) +#endif +int sqlite3_testshellext_init( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pApi +){ + int nErr = 0; + ShellExtensionLink *papi; + SQLITE_EXTENSION_INIT2(pApi); + papi = shext_api(db); + if( papi ){ + printf("Got papi, equality=%d\n", &papi->zErrMsg==pzErrMsg); + } + else + printf("No papi pointer.\n"); + return nErr ? SQLITE_ERROR : SQLITE_OK; +} diff --git a/tool/mkshellc.tcl b/tool/mkshellc.tcl index cdb7d894a7..0da8fb267e 100644 --- a/tool/mkshellc.tcl +++ b/tool/mkshellc.tcl @@ -200,6 +200,7 @@ proc emit_sync { lines ostrm precLines {fromFile ""} } { array set ::cmd_help {} array set ::cmd_dispatch {} array set ::cmd_condition {} +array set ::metacmd_init {} array set ::inc_type_files {} set ::iShuffleErrors 0 # Ease use of { and } in literals. Instead, $::lb and $::rb can be used. @@ -214,9 +215,6 @@ set ::parametersHelp { ARGS_SIGNATURE sets the formal argument list for the dispatchable functions. DISPATCH_ENTRY sets the text of each entry line in emitted dispatch table. DISPATCHEE_NAME sets the name to be generated for dispatchable functions. - CMD_CAPTURE_RE sets a regular expression to be used for capturing the name - to be used for meta-commands within a line passed into COLLECT_DISPATCH, - (which is needed to permit them to be emitted in lexical order by name.) DC_ARG_COUNT sets the effective argument count for DISPATCHABLE_COMMAND(). DC_ARG#_DEFAULT sets a default value, DISPATCHABLE_COMMAND() #'th argument. HELP_COALESCE sets whether to coalesce secondary help text and add newlines. @@ -236,8 +234,9 @@ array set ::dispCfg [list \ DISPATCH_ENTRY \ "{ \"\$cmd\", \${cmd}Command, \$arg1,\$arg2,\$arg3 }," \ DISPATCHEE_NAME {${cmd}Command} \ - CMD_CAPTURE_RE "^\\s*$::lb\\s*\"(\\w+)\"" \ HELP_COALESCE 0 \ + METACMD_INIT \ + "META_CMD_INFO( \${cmd}, \$arg1,\$arg2,\$arg3,\n ,\n )," \ ] # Other config keys: # DC_ARG_COUNT= @@ -337,7 +336,6 @@ proc chunkify_help {htin} { } array set ::macroTailREs [list \ - COLLECT_DISPATCH {^\(\s*([\w\*]+)\s*\)\[} \ COLLECT_HELP_TEXT {^\[} \ COMMENT {\s+(.*)$} \ CONDITION_COMMAND {^\(\s*(\w+)\s+([^;]+)\);} \ @@ -345,6 +343,7 @@ array set ::macroTailREs [list \ DISPATCHABLE_COMMAND {^\(([\w\? ]+)\)(\S)\s*$} \ EMIT_DISPATCH {^\((\d*)\)} \ EMIT_HELP_TEXT {^\((\d*)\)} \ + EMIT_METACMD_INIT {^\((\d*)\)} \ INCLUDE {^(?:\(\s*(\w+)\s*\))|(?:\s+([\w./\\]+)\M)} \ IGNORE_COMMANDS {^\(\s*([-+\w ]*)\)\s*;\s*} \ ] @@ -352,16 +351,15 @@ array set ::macroTailREs [list \ # COMMENT tailCapture_Commentary # CONDITION_COMMAND tailCapture_Cmd_Condition # CONFIGURE_DISPATCH tailCapture_Empty -# COLLECT_DISPATCH tailCapture_Cmd # COLLECT_HELP_TEXT tailCapture_Empty # DISPATCHABLE_COMMAND tailCapture_ArgsGlom_TrailChar # EMIT_DISPATCH tailCapture_Indent # EMIT_HELP_TEXT tailCapture_Indent +# EMIT_METACMD_INIT tailCapture_Indent # IGNORED_COMMANDS tailCapture_SignedCmdGlom # INCLUDE tailCapture_IncType_Filename array set ::macroUsages [list \ - COLLECT_DISPATCH "\[\n \n \];" \ COLLECT_HELP_TEXT "\[\n \n \];" \ COMMENT " " \ CONDITION_COMMAND "( name pp_expr );" \ @@ -370,6 +368,7 @@ array set ::macroUsages [list \ "( name args... ){\n \n }" \ EMIT_DISPATCH "( indent );" \ EMIT_HELP_TEXT "( indent );" \ + EMIT_METACMD_INIT "( indent );" \ INCLUDE {( )} \ SKIP_COMMANDS "( );" \ ] @@ -405,32 +404,6 @@ proc IGNORED_COMMANDS {inSrc tcSignedCmdGlom ostrm} { return 1 } -proc COLLECT_DISPATCH {inSrc tailCaptureCmdOrStar ostrm} { - # Collect dispatch table entries, along with cmd(s) as ordering info. - foreach {infile istrm inLineNum} $inSrc {} - foreach {cmd} $tailCaptureCmdOrStar {} - set iAte 2 - set lx [gets $istrm] - set disp_frag {} - while {![eof $istrm] && ![regexp {^\s*\];} $lx]} { - lappend disp_frag $lx - set grabCmd $::dispCfg(CMD_CAPTURE_RE) - if {![regexp $grabCmd $lx ma dcmd]} { - puts stderr "malformed dispatch element:\n $lx" - incr ::iShuffleErrors - } elseif {$cmd ne "*" && $dcmd ne $cmd} { - puts stderr "misdeclared dispatch element:\n $lx" - incr ::iShuffleErrors - } else { - set ::cmd_dispatch($dcmd) [list $lx] - set_src_tags dispatch $dcmd $inSrc - } - set lx [gets $istrm] - incr iAte - } - return $iAte -} - proc COMMENT {inSrc tailCaptureIgnore ostrm} { # Allow comments in an input file which have no effect on output. return 1 @@ -572,8 +545,10 @@ proc DISPATCHABLE_COMMAND {inSrc tailCapture ostrm} { set fname [subst $::dispCfg(DISPATCHEE_NAME)] set funcOpen "$rsct $fname\($argexp\)$::lb" set dispEntry [subst $::dispCfg(DISPATCH_ENTRY)] + set mcInit [subst $::dispCfg(METACMD_INIT)] emit_conditionally $cmd [linsert $body 0 $funcOpen] $inSrc $ostrm set ::cmd_dispatch($cmd) [list $dispEntry] + set ::metacmd_init($cmd) $mcInit set_src_tags dispatch $cmd $inSrc } } @@ -589,6 +564,42 @@ proc EMIT_DISPATCH {inSrc tailCap ostrm} { return 1 } +proc EMIT_METACMD_INIT {inSrc tailCap ostrm} { + # Emit the collected metacommand init table entries, in command order, maybe + # wrapped with a conditional construct as set by CONDITION_COMMAND(). Prior + # to the emit, substitute markers with help text for the command. + foreach cmd [lsort [array names ::metacmd_init]] { + set initem $::metacmd_init($cmd) + set ht0i -1 + if {[info exists ::cmd_help($cmd)]} { + set ht $::cmd_help($cmd) ; # ht is a list. + # HT0 is its content through first trailing comma. + # HT1 is the remainder. + for {set itc 0} {$itc < [llength $ht]} {incr itc} { + if {[regexp {,\w*$} [lindex $ht $itc]]} { + set ht0i $itc + break + } + } + } + if {$ht0i != -1} { + set ht0 [regsub {,\w*$} [join [lrange $ht 0 $ht0i] "\n"] "\n"] + incr ht0i + set ht1 [regsub {,\w*$} [join [lrange $ht $ht0i end] "\n"] "\n"] + set ht0 "\n$ht0" + set ht1 "\n$ht1" + } else { + set ht0 {0 /* help or commas missing */} + set ht1 0 + } + set initem [regsub {} $initem $ht0] + set initem [regsub {} $initem $ht1] + set initem [split $initem "\n"] + emit_conditionally $cmd $initem $inSrc $ostrm $tailCap + } + return 1 +} + proc EMIT_HELP_TEXT {inSrc tailCap ostrm} { # Emit the collected help text table entries, in command order, maybe # wrapped with a conditional construct as set by CONDITION_COMMAND().