#define _CRT_SECURE_NO_WARNINGS
#endif
+/* Define stringification of a bare word (or anything without commas.) */
+# define SHELL_STRINGIFY_(f) #f
+# define SHELL_STRINGIFY(f) SHELL_STRINGIFY_(f)
+
/*
** Optionally #include a user-defined header, whereby compilation options
--** may be set prior to where they take effect, but after platform setup.
++** may be set prior to where they take effect, but after platform setup.
** If SQLITE_CUSTOM_INCLUDE=? is defined, its value names the #include
** file. Note that this macro has a like effect on sqlite3.c compilation.
*/
#define ColModeOpts_default { 60, 0, 0 }
#define ColModeOpts_default_qbox { 60, 1, 0 }
- ** Accepted by:
+/*
+** Stored output mode state, for partial save and later restore.
+** Returned by:
+** outputModeSave *outputModeSave(ShellState *p, SaveModeWhat ws).
++** Accepted by:
+** outputModeRestore(ShellState *p, OutputModeSave *pSaved).
+** See enum SaveWhatMode regarding what to save and restore.
+** Also see outputModePush(...), outputModePushSome(...) and
+** outputModePop(...) for usages spanning more than one call.
+*/
+typedef struct OutputModeSave{
+ u16 what; /* Set upon creation. See SaveWhatMode for values. */
+ char itsValues[1]; /* This size is inaccurate unless nothing is saved. */
+} OutputModeSave;
+#define MODE_STACK_MAX 3 /* How many levels of saved output mode to allow. */
+
/*
** State information about the database connection is contained in an
** instance of the following structure.
}
sz = j;
p[sz] = 0;
-- }
++ }
sqlite3_result_text64(context, (const char*)p, sz,
sqlite3_free, SQLITE_UTF8);
}
#endif /* SQLITE_NOHAVE_SYSTEM */
/*
-** Save or restore the current output mode
+** Save or restore the current output mode, partially per spec. (OM_STATE)
+*/
+typedef enum {
+ SWM_showHeader = 1, SWM_shellFlags = 2, SWM_mode = 4, SWM_cmOpts = 8,
+ SWM_colSeparator = 0x10, SWM_rowSeparator = 0x20, SWM_everything = 0x3F,
+ SWM_CountOf = 6
+} SaveWhatMode;
+
+/* This is available in most C89+ C compilers as offsetof(...), but since we
+ * cater to the most arcane C89-like compilers around, define it for sure:
+ */
+#define MEMBER_OFFSET(stype, member) ((size_t)&(((stype*)0)->member))
+#define MEMBER_SIZEOF(stype, member) (sizeof(((stype*)0)->member))
+static struct {
+ size_t offset;
+ size_t size;
+} outputModeCopy[] = {
+#define SS_MEMBER_COPY(mn) \
+ { MEMBER_OFFSET(ShellState,mn), MEMBER_SIZEOF(ShellState,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)
+#undef SS_MEMBER_COPY
+};
+
+/* Allocate a buffer, copy requested output mode data to it, and return it. */
+static OutputModeSave *outputModeSave(ShellState *p, SaveWhatMode w){
+ u16 what = (u16)w;
+ int i, nAlloc = sizeof(what)+1, mask = 1;
+ char *pSaved = 0, *pFill;
+ for( i=0; i<SWM_CountOf; mask<<=1, ++i ){
+ if( (what & mask)!=0 ) nAlloc += (int)outputModeCopy[i].size;
+ }
+ assert(i==ArraySize(outputModeCopy));
+ pSaved = sqlite3_malloc(nAlloc);
+ if( pSaved==0 ) return 0;
+ *(u16 *)pSaved = what;
+ pFill = pSaved + sizeof(what);
+ for( mask=1, i=0; i<SWM_CountOf; mask<<=1, ++i ){
+ if( (what & mask)!=0 ){
+ size_t nb = outputModeCopy[i].size;
+ memcpy(pFill, (char*)p+outputModeCopy[i].offset, nb);
+ pFill += nb;
+ }
+ }
+ *pFill = 0xA5;
+ return (OutputModeSave *)pSaved;
+}
+
+/* From a buffer returned by outputModeSave, restore output mode data.
- * The buffer is freed and its pointer is invalidated.
++ * The buffer is freed and its pointer is invalidated.
+ * If called with some other buffer, results are undefined, likely bad.
+ */
+static void outputModeRestore(ShellState *p, OutputModeSave *pSaved){
+ sqlite3_uint64 nA = sqlite3_msize(pSaved);
+ u16 what = (nA>sizeof(what))? *((u16 *)pSaved) : 0;
+ int i, nAlloc = sizeof(what)+1, mask = 1;
+ char *pTake = (char *)pSaved + sizeof(what);
+ for( i=0; i<SWM_CountOf && nAlloc<nA; mask<<=1, ++i ){
+ if( (what & mask)!=0 ){
+ size_t nb = outputModeCopy[i].size;
+ memcpy((char*)p+outputModeCopy[i].offset, pTake, nb);
+ pTake += nb;
+ }
+ }
+ assert(*pTake==0xA5);
+ sqlite3_free(pSaved);
+}
+
+/*
+** Save or restore the current output mode, in whole or in part.
*/
+static void outputModePushSome(ShellState *p, SaveWhatMode w){
+ OutputModeSave *pOMS;
+ assert(p->nSavedModes<MODE_STACK_MAX); /* Fail hard for this logic error. */
+ if( p->nSavedModes>=MODE_STACK_MAX ) return;
+ pOMS = outputModeSave(p, w);
+ shell_check_oom(pOMS);
+ p->pModeStack[p->nSavedModes++] = pOMS;
+}
static void outputModePush(ShellState *p){
- p->modePrior = p->mode;
- p->priorShFlgs = p->shellFlgs;
- memcpy(p->colSepPrior, p->colSeparator, sizeof(p->colSeparator));
- memcpy(p->rowSepPrior, p->rowSeparator, sizeof(p->rowSeparator));
+ outputModePushSome(p, SWM_everything);
}
static void outputModePop(ShellState *p){
- p->mode = p->modePrior;
- p->shellFlgs = p->priorShFlgs;
- memcpy(p->colSeparator, p->colSepPrior, sizeof(p->colSeparator));
- memcpy(p->rowSeparator, p->rowSepPrior, sizeof(p->rowSeparator));
+ OutputModeSave *pOMS;
+ assert(p->nSavedModes>0); /* Should not be here unless something pushed. */
+ if( p->nSavedModes==0 ) return;
+ pOMS = p->pModeStack[--p->nSavedModes];
+ assert(pOMS!=0);
+ p->pModeStack[p->nSavedModes] = 0;
+ outputModeRestore(p, pOMS);
}
/*
** characters
*/
static void print_box_line(FILE *out, int N){
-- const char zDash[] =
++ const char zDash[] =
BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24
BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24;
const int nDash = sizeof(zDash) - 1;
break;
}
zOut[j] = 0;
-- return (char*)zOut;
++ return (char*)zOut;
}
/* Extract the value of the i-th current column for pStmt as an SQL literal
** caller to eventually free this buffer using sqlite3_free().
*/
static int expertHandleSQL(
-- ShellState *pState,
-- const char *zSql,
++ ShellState *pState,
++ const char *zSql,
char **pzErr
){
assert( pState->expert.pExpert );
/*
** This function is called either to silently clean up the object
--** created by the ".expert" command (if bCancel==1), or to generate a
++** created by the ".expert" command (if bCancel==1), or to generate a
** report from it and then clean it up (if bCancel==0).
**
** If successful, SQLITE_OK is returned. Otherwise, an SQLite error
rc2 = sqlite3_finalize(pStmt);
if( rc!=SQLITE_NOMEM ) rc = rc2;
if( rc==SQLITE_OK ){
- zSql = zLeftover;
- while( IsSpace(zSql[0]) ) zSql++;
+ zSql = skipWhite(zLeftover);
}else if( pzErrMsg ){
- *pzErrMsg = save_err_msg(db, "stepping, %s (%d)", rc, 0);
+ *pzErrMsg = save_err_msg(db, "stepping", rc, 0);
}
/* clear saved stmt handle */
}
}
fclose(f);
-- return rc;
++ return rc;
}
#ifndef SQLITE_OMIT_DESERIALIZE
** offset (4*<arg2>) of the blob.
*/
static void shellInt32(
-- sqlite3_context *context,
-- int argc,
++ sqlite3_context *context,
++ int argc,
sqlite3_value **argv
){
const unsigned char *pBlob;
** using "..." with internal double-quote characters doubled.
*/
static void shellIdQuote(
-- sqlite3_context *context,
-- int argc,
++ sqlite3_context *context,
++ int argc,
sqlite3_value **argv
){
const char *zName = (const char*)sqlite3_value_text(argv[0]);
** Scalar function "usleep(X)" invokes sqlite3_sleep(X) and returns X.
*/
static void shellUSleepFunc(
-- sqlite3_context *context,
-- int argcUnused,
++ sqlite3_context *context,
++ int argcUnused,
sqlite3_value **argv
){
int sleep = sqlite3_value_int(argv[0]);
/*
** Scalar function "shell_escape_crnl" used by the .recover command.
** The argument passed to this function is the output of built-in
--** function quote(). If the first character of the input is "'",
++** function quote(). If the first character of the input is "'",
** indicating that the value passed to quote() was a text value,
** then this function searches the input for "\n" and "\r" characters
** and adds a wrapper similar to the following:
** of the input is returned.
*/
static void shellEscapeCrnl(
-- sqlite3_context *context,
-- int argc,
++ sqlite3_context *context,
++ int argc,
sqlite3_value **argv
){
const char *zText = (const char*)sqlite3_value_text(argv[0]);
}
switch( p->openMode ){
case SHELL_OPEN_APPENDVFS: {
-- sqlite3_open_v2(zDbFilename, &p->db,
++ sqlite3_open_v2(zDbFilename, &p->db,
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, "apndvfs");
break;
}
if( rc ){
utf8_printf(stderr, "Error: sqlite3_close() returns %d: %s\n",
rc, sqlite3_errmsg(db));
-- }
++ }
}
#if HAVE_READLINE || HAVE_EDITLINE
#if !defined SQLITE_OMIT_VIRTUALTABLE
static void shellPrepare(
-- sqlite3 *db,
-- int *pRc,
-- const char *zSql,
++ sqlite3 *db,
++ int *pRc,
++ const char *zSql,
sqlite3_stmt **ppStmt
){
*ppStmt = 0;
if( *pRc==SQLITE_OK ){
int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0);
if( rc!=SQLITE_OK ){
-- raw_printf(stderr, "sql error: %s (%d)\n",
++ raw_printf(stderr, "sql error: %s (%d)\n",
sqlite3_errmsg(db), sqlite3_errcode(db)
);
*pRc = rc;
** nuisance compiler warnings about "defined but not used".
*/
void shellPreparePrintf(
-- sqlite3 *db,
-- int *pRc,
++ sqlite3 *db,
++ int *pRc,
sqlite3_stmt **ppStmt,
-- const char *zFmt,
++ const char *zFmt,
...
){
*ppStmt = 0;
** nuisance compiler warnings about "defined but not used".
*/
void shellFinalize(
-- int *pRc,
++ int *pRc,
sqlite3_stmt *pStmt
){
if( pStmt ){
** nuisance compiler warnings about "defined but not used".
*/
void shellReset(
-- int *pRc,
++ int *pRc,
sqlite3_stmt *pStmt
){
int rc = sqlite3_reset(pStmt);
}
/*
--** Print an error message for the .ar command to stderr and return
++** Print an error message for the .ar command to stderr and return
** SQLITE_ERROR.
*/
static int arErrorMsg(ArCommand *pAr, const char *zFmt, ...){
/*
** Parse the command line for an ".ar" command. The results are written into
** structure (*pAr). SQLITE_OK is returned if the command line is parsed
--** successfully, otherwise an error message is written to stderr and
++** successfully, otherwise an error message is written to stderr and
** SQLITE_ERROR returned.
*/
static int arParseCommand(
** when pAr->bGlob is false and GLOB match when pAr->bGlob is true.
*/
static void arWhereClause(
-- int *pRc,
++ int *pRc,
ArCommand *pAr,
char **pzWhere /* OUT: New WHERE clause */
){
for(i=0; i<pAr->nArg; i++){
const char *z = pAr->azArg[i];
zWhere = sqlite3_mprintf(
-- "%z%s name %s '%q' OR substr(name,1,%d) %s '%q/'",
++ "%z%s name %s '%q' OR substr(name,1,%d) %s '%q/'",
zWhere, zSep, zSameOp, z, strlen30(z)+1, zSameOp, z
);
if( zWhere==0 ){
}
/*
--** Implementation of .ar "lisT" command.
++** Implementation of .ar "lisT" command.
*/
static int arListCommand(ArCommand *pAr){
-- const char *zSql = "SELECT %s FROM %s WHERE %s";
++ const char *zSql = "SELECT %s FROM %s WHERE %s";
const char *azCols[] = {
"name",
"lsmode(mode), sz, datetime(mtime, 'unixepoch'), name"
if( pAr->bVerbose ){
utf8_printf(pAr->p->out, "%s % 10d %s %s\n",
sqlite3_column_text(pSql, 0),
-- sqlite3_column_int(pSql, 1),
++ sqlite3_column_int(pSql, 1),
sqlite3_column_text(pSql, 2),
sqlite3_column_text(pSql, 3)
);
}
/*
--** Implementation of .ar "eXtract" command.
++** Implementation of .ar "eXtract" command.
*/
static int arExtractCommand(ArCommand *pAr){
-- const char *zSql1 =
++ const char *zSql1 =
"SELECT "
" ($dir || name),"
" writefile(($dir || name), %s, mode, mtime) "
"FROM %s WHERE (%s) AND (data IS NULL OR $dirOnly = 0)"
" AND name NOT GLOB '*..[/\\]*'";
-- const char *azExtraArg[] = {
++ const char *azExtraArg[] = {
"sqlar_uncompress(data, sz)",
"data"
};
if( zDir==0 ) rc = SQLITE_NOMEM;
}
-- shellPreparePrintf(pAr->db, &rc, &pSql, zSql1,
++ shellPreparePrintf(pAr->db, &rc, &pSql, zSql1,
azExtraArg[pAr->bZip], pAr->zSrcTable, zWhere
);
int bUpdate, /* true for a --create. */
int bOnlyIfChanged /* Only update if file has changed */
){
-- const char *zCreate =
++ const char *zCreate =
"CREATE TABLE IF NOT EXISTS sqlar(\n"
" name TEXT PRIMARY KEY, -- name of the file\n"
" mode INT, -- access permissions\n"
arExecSql(pAr, "PRAGMA page_size=512");
rc = arExecSql(pAr, "SAVEPOINT ar;");
if( rc!=SQLITE_OK ) return rc;
-- zTemp[0] = 0;
++ zTemp[0] = 0;
if( pAr->bZip ){
/* Initialize the zipfile virtual table, if necessary */
if( pAr->zFile ){
}else if( cmd.zFile ){
int flags;
if( cmd.bAppend ) eDbType = SHELL_OPEN_APPENDVFS;
-- if( cmd.eCmd==AR_CMD_CREATE || cmd.eCmd==AR_CMD_INSERT
++ if( cmd.eCmd==AR_CMD_CREATE || cmd.eCmd==AR_CMD_INSERT
|| cmd.eCmd==AR_CMD_REMOVE || cmd.eCmd==AR_CMD_UPDATE ){
flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
}else{
utf8_printf(pState->out, "-- open database '%s'%s\n", cmd.zFile,
eDbType==SHELL_OPEN_APPENDVFS ? " using 'apndvfs'" : "");
}
-- rc = sqlite3_open_v2(cmd.zFile, &cmd.db, flags,
++ rc = sqlite3_open_v2(cmd.zFile, &cmd.db, flags,
eDbType==SHELL_OPEN_APPENDVFS ? "apndvfs" : 0);
if( rc!=SQLITE_OK ){
-- utf8_printf(stderr, "cannot open file: %s (%s)\n",
++ utf8_printf(stderr, "cannot open file: %s (%s)\n",
cmd.zFile, sqlite3_errmsg(cmd.db)
);
goto end_ar_command;
/*
** If *pRc is not SQLITE_OK when this function is called, it is a no-op.
** Otherwise, zFmt is treated as a printf() style string. The result of
--** formatting it along with any trailing arguments is written into a
++** formatting it along with any trailing arguments is written into a
** buffer obtained from sqlite3_malloc(), and pointer to which is returned.
** It is the responsibility of the caller to eventually free this buffer
** using a call to sqlite3_free().
--**
--** If an OOM error occurs, (*pRc) is set to SQLITE_NOMEM and a NULL
++**
++** If an OOM error occurs, (*pRc) is set to SQLITE_NOMEM and a NULL
** pointer returned.
*/
static char *shellMPrintf(int *pRc, const char *zFmt, ...){
int *pRc, /* IN/OUT: Error code */
const char *zName, /* Name of table */
const char *zSql, /* CREATE TABLE statement */
-- int bIntkey,
++ int bIntkey,
int nCol
){
sqlite3 *dbtmp = 0; /* sqlite3 handle for testing CREATE TABLE */
int nSqlCol = 0;
int bSqlIntkey = 0;
sqlite3_stmt *pStmt = 0;
--
++
rc = sqlite3_open("", &dbtmp);
if( rc==SQLITE_OK ){
sqlite3_create_function(dbtmp, "shell_idquote", 1, SQLITE_UTF8, 0,
goto finished;
}
}
-- shellPreparePrintf(dbtmp, &rc, &pStmt,
++ shellPreparePrintf(dbtmp, &rc, &pStmt,
"SELECT count(*) FROM pragma_table_info(%Q)", zName
);
if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
goto finished;
}
-- shellPreparePrintf(dbtmp, &rc, &pStmt,
++ shellPreparePrintf(dbtmp, &rc, &pStmt,
"SELECT ("
" SELECT substr(data,1,1)==X'0D' FROM sqlite_dbpage WHERE pgno=rootpage"
") FROM sqlite_schema WHERE name = %Q", zName
** leave zPk as "_rowid_" and pTab->iPk at -2. */
pTab->iPk = -2;
if( bIntkey ){
-- shellPreparePrintf(dbtmp, &rc, &pPkFinder,
++ shellPreparePrintf(dbtmp, &rc, &pPkFinder,
"SELECT cid, name FROM pragma_table_info(%Q) "
" WHERE pk=1 AND type='integer' COLLATE nocase"
" AND NOT EXISTS (SELECT cid FROM pragma_table_info(%Q) WHERE pk=2)"
pTab->azlCol[0] = shellMPrintf(&rc, "");
}
i = 1;
-- shellPreparePrintf(dbtmp, &rc, &pStmt,
++ shellPreparePrintf(dbtmp, &rc, &pStmt,
"SELECT %Q || group_concat(shell_idquote(name), ', ') "
" FILTER (WHERE cid!=%d) OVER (ORDER BY %s cid) "
-- "FROM pragma_table_info(%Q)",
-- bIntkey ? ", " : "", pTab->iPk,
++ "FROM pragma_table_info(%Q)",
++ bIntkey ? ", " : "", pTab->iPk,
bIntkey ? "" : "(CASE WHEN pk=0 THEN 1000000 ELSE pk END), ",
zName
);
** those.
**
** If a table is found, a (RecoverTable*) object is returned. Or, if
--** no such table is found, but bIntkey is false and iRoot is the
++** no such table is found, but bIntkey is false and iRoot is the
** root page of an index in the recovered schema, then (*pbNoop) is
** set to true and NULL returned. Or, if there is no such table or
** index, NULL is returned and (*pbNoop) set to 0, indicating that
recoverFreeTable(pTab);
pTab = 0;
}else{
-- raw_printf(pState->out,
++ raw_printf(pState->out,
"CREATE TABLE %s(rootpgno INTEGER, "
"pgno INTEGER, nfield INTEGER, id INTEGER", pTab->zQuoted
);
bRowids = 0;
}
else{
-- utf8_printf(stderr, "unexpected option: %s\n", azArg[i]);
++ utf8_printf(stderr, "unexpected option: %s\n", azArg[i]);
showHelp(pState->out, azArg[0]);
return 1;
}
}
shellExecPrintf(pState->db, &rc,
-- /* Attach an in-memory database named 'recovery'. Create an indexed
++ /* Attach an in-memory database named 'recovery'. Create an indexed
** cache of the sqlite_dbptr virtual table. */
"PRAGMA writable_schema = on;"
"ATTACH %Q AS recovery;"
}
/* If this is an auto-vacuum database, add all pointer-map pages to
-- ** the freelist table. Do this regardless of whether or not
++ ** the freelist table. Do this regardless of whether or not
** --freelist-corrupt was specified. */
-- shellExec(pState->db, &rc,
++ shellExec(pState->db, &rc,
"WITH ptrmap(pgno) AS ("
" SELECT 2 WHERE shell_int32("
" (SELECT data FROM sqlite_dbpage WHERE pgno=1), 13"
"REPLACE INTO recovery.freelist SELECT pgno FROM ptrmap"
);
-- shellExec(pState->db, &rc,
++ shellExec(pState->db, &rc,
"CREATE TABLE recovery.dbptr("
" pgno, child, PRIMARY KEY(child, pgno)"
") WITHOUT ROWID;"
");"
/* Create the "map" table that will (eventually) contain instructions
-- ** for dealing with each page in the db that contains one or more
++ ** for dealing with each page in the db that contains one or more
** records. */
"CREATE TABLE recovery.map("
"pgno INTEGER PRIMARY KEY, maxlen INT, intkey, root INT"
"CREATE INDEX recovery.schema_rootpage ON schema(rootpage);"
);
-- /* Open a transaction, then print out all non-virtual, non-"sqlite_%"
++ /* Open a transaction, then print out all non-virtual, non-"sqlite_%"
** CREATE TABLE statements that extracted from the existing schema. */
if( rc==SQLITE_OK ){
sqlite3_stmt *pStmt = 0;
);
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
const char *zCreateTable = (const char*)sqlite3_column_text(pStmt, 0);
-- raw_printf(pState->out, "CREATE TABLE IF NOT EXISTS %s;\n",
++ raw_printf(pState->out, "CREATE TABLE IF NOT EXISTS %s;\n",
&zCreateTable[12]
);
}
/* Figure out if an orphan table will be required. And if so, how many
** user columns it should contain */
-- shellPrepare(pState->db, &rc,
++ shellPrepare(pState->db, &rc,
"SELECT coalesce(max(maxlen), -2) FROM recovery.map WHERE root>1"
, &pLoop
);
);
/* Loop through each root page. */
-- shellPrepare(pState->db, &rc,
-- "SELECT root, intkey, max(maxlen) FROM recovery.map"
++ shellPrepare(pState->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'"
")", &pLoop
nField = nField+1;
if( pTab2==pOrphan ){
-- raw_printf(pState->out,
++ raw_printf(pState->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(pState->out, "INSERT INTO %s(%s) VALUES( %s );\n",
++ raw_printf(pState->out, "INSERT INTO %s(%s) VALUES( %s );\n",
pTab2->zQuoted, pTab2->azlCol[nField], zVal
);
}
/* The rest of the schema */
if( rc==SQLITE_OK ){
sqlite3_stmt *pStmt = 0;
-- shellPrepare(pState->db, &rc,
++ shellPrepare(pState->db, &rc,
"SELECT sql, name FROM recovery.schema "
"WHERE sql NOT LIKE 'create table%'", &pStmt
);
const char *zSql = (const char*)sqlite3_column_text(pStmt, 0);
if( sqlite3_strnicmp(zSql, "create virt", 11)==0 ){
const char *zName = (const char*)sqlite3_column_text(pStmt, 1);
-- char *zPrint = shellMPrintf(&rc,
++ char *zPrint = shellMPrintf(&rc,
"INSERT INTO sqlite_schema VALUES('table', %Q, %Q, 0, %Q)",
zName, zName, zSql
);
}
#endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */
- /*
++/*
+** The .shxopts command, for setting or listing shell extension options.
+ */
+#if SHELL_EXTENSIONS
+static int shxoptsCommand(char *azArg[], int nArg, ShellState *p, char **pzE){
+ static struct { const char *name; u8 mask; } shopts[] = {
+#if SHELL_DYNAMIC_COMMANDS
+ {"dyn_cmds", 1<<SHEXT_DYNCMDS_BIT},
+#endif
+#if SHELL_EXTENDED_PARSING
+ {"parsing", 1<<SHEXT_PARSING_BIT},
+#endif
+#if SHELL_VARIABLE_EXPANSION
+ {"dot_vars", 1<<SHEXT_VAREXP_BIT},
+#endif
+ {"all_opts", SHELL_ALL_EXTENSIONS}
+ };
+ const char *zMoan = 0, *zAbout = 0;
+ int ia, io;
+ if( nArg>1 ){
+ for( ia=1; ia<nArg; ++ia ){
+ char cs = azArg[ia][0];
+ if( cs!='+' && cs!='-' ){
+ zMoan = "arguments must have a sign prefix.";
+ zAbout = azArg[0];
+ goto moan_error;
+ }
+ for( io=0; io<ArraySize(shopts); ++io ){
+ if( strcmp(azArg[ia]+1, shopts[io].name)==0 ){
+ if( cs=='+' ) p->bExtendedDotCmds |= shopts[io].mask;
+ else p->bExtendedDotCmds &= ~shopts[io].mask;
+ break;
+ }
+ }
+ if( io==ArraySize(shopts) ){
+ zAbout = azArg[ia];
+ zMoan = "is not a recognized option name";
+ goto moan_error;
+ }
+ }
+ }else{
+ raw_printf(p->out,
+ " name value \"-shxopts set\"\n"
+ " -------- ----- ---------------\n");
+ for( io=0; io<ArraySize(shopts); ++io ){
+ unsigned m = shopts[io].mask;
+ unsigned v = ((p->bExtendedDotCmds & m) == m)? 1 : 0;
+ raw_printf(p->out,
+ " %9s %2d \"-shxopts 0x%02X\"\n",
+ shopts[io].name, v, m);
+ }
+ }
+ return 0;
+ moan_error:
+ raw_printf(stderr, "Error: %s %s\n", zAbout, zMoan);
+ return 1;
+}
+#endif
+
+static int execute_variables(char *azArg[], int nArg, ShellState *p){
+ int ia, rc, nErrors = 0;
+ sqlite3_stmt *pStmt = 0;
+ open_db(p, 0);
+ if( p->db==0 ){
+ utf8_printf(stderr, ".x can only be done with a database open.\n");
+ return 1;
+ }
+ if( sqlite3_table_column_metadata(p->db, PARAM_TABLE_SCHEMA,
+ PARAM_TABLE_NAME,
+ "key", 0, 0, 0, 0, 0)!=SQLITE_OK ){
+ utf8_printf(stderr, "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);
+ if( rc!=SQLITE_OK ){
+ utf8_printf(stderr, PARAM_TABLE_SNAME" is wrongly created.\n");
+ return 1;
+ }
+ for( ia=1; ia < nArg; ++ia ){
+ if( isalpha(azArg[ia][0]) ){
+ rc = sqlite3_reset(pStmt);
+ rc = sqlite3_bind_text(pStmt, 1, azArg[ia], -1, 0);
+ rc = sqlite3_step(pStmt);
+ if( rc==SQLITE_ROW ){
+ const unsigned char *zValue = sqlite3_column_text(pStmt, 0);
+ int nb = sqlite3_column_bytes(pStmt, 0);
+ while( nb>0 && IsSpace(zValue[nb-1]) ) --nb;
+ if( nb>0 ){
+ /* The trailing newline (or some other placeholder) is important
+ * because one (or some other character) will likely be put in
+ * its place during process_input() line/group handling, along
+ * with a terminating NUL character. Without it, the NULL could
+ * land past the end of the allocation made just below.
+ */
+ int nle = zValue[nb-1]=='\n';
+ char *zSubmit = sqlite3_mprintf( "%.*s%s", nb, zValue, "\n"+nle );
+ InSource inRedir
+ = INSOURCE_STR_REDIR(zSubmit, azArg[ia], p->pInSource);
+ shell_check_oom(zSubmit);
+ p->pInSource = &inRedir;
+ rc = process_input(p);
+ sqlite3_free(zSubmit);
+ p->pInSource = inRedir.pFrom;
+ }else{
+ continue; /* All white, ignore. */
+ }
+ }else{
+ utf8_printf(stderr,
+ "Skipping parameter '%s' (not set and executable.)\n",
+ azArg[ia]);
+ ++nErrors;
+ }
+ }else{
+ utf8_printf(stderr,
+ "Skipping badly named %s. Run \".help x\"\n", azArg[ia]);
+ ++nErrors;
+ }
+ }
+ sqlite3_finalize(pStmt);
+ return (rc==2)? 2 : nErrors>0;
+}
+
+struct param_row { char * value; int uses; int hits; };
+
+static int param_find_callback(void *pData, int nc, char **pV, char **pC){
+ assert(nc>=1);
+ assert(strcmp(pC[0],"value")==0);
+ struct param_row *pParam = (struct param_row *)pData;
+ assert(pParam->value==0); /* key values are supposedly unique. */
+ if( pParam->value!=0 ) sqlite3_free( pParam->value );
+ pParam->value = sqlite3_mprintf("%s", pV[0]); /* source owned by statement */
+ if( nc>1 ) pParam->uses = (int)integerValue(pV[1]);
+ ++pParam->hits;
+ return 0;
+}
+
+static void append_in_clause(sqlite3_str *pStr,
+ const char **azBeg, const char **azLim);
+static char *find_home_dir(int clearFlag);
+
+/* Create a home-relative pathname from ~ prefixed path.
+ * Return it, or 0 for any error.
+ * Caller must sqlite3_free() it.
+ */
+static char *home_based_path( const char *zPath ){
+ char *zHome = find_home_dir(0);
+ char *zErr = 0;
+ assert( zPath[0]=='~' );
+ if( zHome==0 ){
+ zErr = "Cannot find home directory.";
+ }else if( zPath[0]==0 || (zPath[1]!='/'
+#if defined(_WIN32) || defined(WIN32)
+ && zPath[1]!='\\'
+#endif
+ ) ){
+ zErr = "Malformed pathname";
+ }else{
+ return sqlite3_mprintf("%s%s", zHome, zPath+1);
+ }
+ utf8_printf(stderr, "Error: %s\n", zErr);
+ return 0;
+}
+
+/* Transfer selected parameters between two parameter tables, for save/load.
+ * Argument bSaveNotLoad determines transfer direction and other actions.
+ * If it is true, the store DB will be created if not existent, and its
+ * table for keeping parameters will be created. Or failure is returned.
+ * If it is false, the store DB will be opened for read and its presumed
+ * table for keeping parameters will be read. Or failure is returned.
+ *
+ * Arguments azNames and nNames reference the ?NAMES? save/load arguments.
+ * If it is an empty list, all parameters will be saved or loaded.
- * Otherwise, only the named parameters are transferred, if they exist.
++ * Otherwise, only the named parameters are transferred, if they exist.
+ * It is not an error to specify a name that cannot be transferred
+ * because it does not exist in the source table.
+ *
+ * Returns are SQLITE_OK for success, or other codes for failure.
+ */
+static int param_xfr_table(sqlite3 *db, const char *zStoreDbName,
+ int bSaveNotLoad, const char *azNames[], int nNames){
+ int rc = 0;
+ char *zSql = 0; /* to be sqlite3_free()'ed */
+ sqlite3_str *sbCopy = 0;
+ const char *zHere = PARAM_TABLE_SNAME;
+ const char *zThere = PARAM_STORE_SNAME;
+ const char *zTo = (bSaveNotLoad)? zThere : zHere;
+ const char *zFrom = (bSaveNotLoad)? zHere : zThere;
+ sqlite3 *dbStore = 0;
+ int openFlags = (bSaveNotLoad)
+ ? SQLITE_OPEN_URI|SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE
+ : SQLITE_OPEN_READONLY;
+
+ /* Ensure store DB can be opened and/or created appropriately. */
+ rc = sqlite3_open_v2(zStoreDbName, &dbStore, openFlags, 0);
+ if( rc!=SQLITE_OK ){
+ utf8_printf(stderr, "Error: Cannot %s parameter store DB %s\n",
+ bSaveNotLoad? "open/create" : "read", zStoreDbName);
+ return rc;
+ }
+ /* Ensure it has the parameter store table, or handle its absence. */
+ assert(dbStore!=0);
+ if( sqlite3_table_column_metadata
+ (dbStore, "main", PARAM_STORE_NAME, 0, 0, 0, 0, 0, 0)!=SQLITE_OK ){
+ if( !bSaveNotLoad ){
+ utf8_printf(stderr, "Error: No parameters ever stored in DB %s\n",
+ zStoreDbName);
+ rc = 1;
+ }else{
+ /* The saved parameters table is not there yet; create it. */
+ const char *zCT =
+ "CREATE TABLE IF NOT EXISTS "PARAM_STORE_NAME"(\n"
+ " key TEXT PRIMARY KEY,\n"
+ " value,\n"
+ " uses INT\n"
+ ") WITHOUT ROWID;";
+ rc = sqlite3_exec(dbStore, zCT, 0, 0, 0);
+ if( rc!=SQLITE_OK ){
+ utf8_printf(stderr, "Cannot create table %s. Nothing saved.", zThere);
+ }
+ }
+ }
+ sqlite3_close(dbStore);
+ if( rc!=0 ) return rc;
+
+ zSql = sqlite3_mprintf("ATTACH %Q AS %s;", zStoreDbName, PARAM_STORE_SCHEMA);
+ shell_check_oom(zSql);
+ rc = sqlite3_exec(db, zSql, 0, 0, 0);
+ sqlite3_free(zSql);
+ if( rc!=SQLITE_OK ) return rc;
+
+ sbCopy = sqlite3_str_new(db);
+ sqlite3_str_appendf
+ (sbCopy, "INSERT OR REPLACE INTO %s(key,value,uses)"
+ "SELECT key, value, uses FROM %s WHERE key ", zTo, zFrom);
+ append_in_clause(sbCopy, azNames, azNames+nNames);
+ zSql = sqlite3_str_finish(sbCopy);
+ shell_check_oom(zSql);
+ rc = sqlite3_exec(db, zSql, 0, 0, 0);
+ sqlite3_free(zSql);
+
+ sqlite3_exec(db, "DETACH "PARAM_STORE_SCHEMA";", 0, 0, 0);
+ return rc;
+}
+
+/* Default location of parameters store DB for .parameters save/load. */
+static const char *zDefaultParamStore = "~/sqlite_params.sdb";
+
+/* Possibly generate a derived path from input spec, with defaulting
+ * and conversion of leading (or only) tilde as home directory.
+ * The above-set default is used for zSpec NULL, "" or "~".
+ * When return is 0, there is an error; what needs doing cannnot be done.
+ * If the return is exactly the input, it must not be sqlite3_free()'ed.
+ * If the return differs from the input, it must be sqlite3_free()'ed.
+ */
+static const char *params_store_path(const char *zSpec){
+ if( zSpec==0 || zSpec[0]==0 || strcmp(zSpec,"~")==0 ){
+ return home_based_path(zDefaultParamStore);
+ }else if ( zSpec[0]=='~' ){
+ return home_based_path(zSpec);
+ }
+ return zSpec;
+}
+
+/* Load some or all parameters. Arguments are "load FILE ?NAMES?". */
+static int parameters_load(sqlite3 *db, const char *azArg[], int nArg){
+ const char *zStore = params_store_path((nArg>1)? azArg[1] : 0);
+ if( zStore==0 ){
+ utf8_printf(stderr, "Cannot form parameter load path. Nothing loaded.\n");
+ return 1;
+ }else{
+ const char **pzFirst = (nArg>2)? azArg+2 : 0;
+ int nNames = (nArg>2)? nArg-2 : 0;
+ int rc = param_xfr_table(db, zStore, 0, pzFirst, nNames);
+ if( nArg>1 && zStore!=azArg[1] ) sqlite3_free((void*)zStore);
+ return rc;
+ }
+}
+
+/* Save some or all parameters. Arguments are "save FILE ?NAMES?". */
+static int parameters_save(sqlite3 *db, const char *azArg[], int nArg){
+ const char *zStore = params_store_path((nArg>1)? azArg[1] : 0);
+ if( zStore==0 ){
+ utf8_printf(stderr, "Cannot form parameter save path. Nothing saved.\n");
+ return 1;
+ }else{
+ const char **pzFirst = (nArg>2)? azArg+2 : 0;
+ int nNames = (nArg>2)? nArg-2 : 0;
+ int rc = param_xfr_table(db, zStore, 1, pzFirst, nNames);
+ if( nArg>1 && zStore!=azArg[1] ) sqlite3_free((void*)zStore);
+ return rc;
+ }
+}
+
+#ifndef SQLITE_NOHAVE_SYSTEM
+/*
+ * Edit one named parameter in the parameters table. If it does not
+ * yet exist, create it. If eval is true, the value is treated as a
+ * bare expression, otherwise it is a text value. The uses argument
+ * sets the 3rd column in the parameters table, and may also serve
+ * to partition the key namespace. (This is not done now.)
+ */
+static int edit_one_param(sqlite3 *db, char *name, int eval,
+ ParamTableUse uses, const char * zEditor){
+ struct param_row paramVU = {0,0,0};
+ int rc;
+ char * zVal = 0;
+ char * zSql = sqlite3_mprintf
+ ("SELECT value, uses FROM " PARAM_TABLE_SNAME " WHERE key=%Q", name);
+ shell_check_oom(zSql);
+ sqlite3_exec(db, zSql, param_find_callback, ¶mVU, 0);
+ sqlite3_free(zSql);
+ assert(paramVU.hits<2);
+ if( paramVU.hits==1 && paramVU.uses==uses){
+ /* Editing an existing value of same kind. */
+ sqlite3_free(paramVU.value);
+ if( eval!=0 ){
+ zSql = sqlite3_mprintf
+ ("SELECT edit(value, %Q) FROM " PARAM_TABLE_SNAME
+ " WHERE key=%Q AND uses=%d", zEditor, name, uses);
+ zVal = db_text(db, zSql, 1);
+ sqlite3_free(zSql);
+ zSql = sqlite3_mprintf
+ ("UPDATE "PARAM_TABLE_SNAME" SET value=(SELECT %s) WHERE"
+ " key=%Q AND uses=%d", zVal, name, uses);
+ }else{
+ zSql = sqlite3_mprintf
+ ("UPDATE "PARAM_TABLE_SNAME" SET value=edit(value, %Q) WHERE"
+ " key=%Q AND uses=%d", zEditor, name, uses);
+ }
+ }else{
+ /* Editing a new value of same kind. */
+ assert(paramVU.value==0 || paramVU.uses!=uses);
+ if( eval!=0 ){
+ zSql = sqlite3_mprintf
+ ("SELECT edit('-- %q%s', %Q)", name, "\n", zEditor);
+ zVal = db_text(db, zSql, 1);
+ sqlite3_free(zSql);
+ zSql = sqlite3_mprintf
+ ("INSERT INTO "PARAM_TABLE_SNAME"(key,value,uses)"
+ " VALUES (%Q,(SELECT %s LIMIT 1),%d)",
+ name, zVal, uses);
+ }else{
+ zSql = sqlite3_mprintf
+ ("INSERT INTO "PARAM_TABLE_SNAME"(key,value,uses)"
+ " VALUES (%Q,edit('-- %q%s', %Q),%d)",
+ name, name, "\n", zEditor, uses);
+ }
+ }
+ shell_check_oom(zSql);
+ if( eval!=0 ){
+ }
+ rc = sqlite3_exec(db, zSql, 0, 0, 0);
+ sqlite3_free(zSql);
+ sqlite3_free(zVal);
+ return rc!=SQLITE_OK;
+}
+#endif
+
+/* Space-join values in an argument list. *valLim is not included. */
+char *values_join( char **valBeg, char **valLim ){
+ char *z = 0;
+ const char *zSep = 0;
+ while( valBeg < valLim ){
+ z = sqlite3_mprintf("%z%s%s", z, zSep, *valBeg);
+ zSep = " ";
+ ++valBeg;
+ }
+ return z;
+}
+
+/* Get a named parameter value in form of stepped prepared statement,
+ * ready to have its value taken from the 0th column. If the name
+ * cannot be found for the given ParamTableUse, 0 is returned.
+ * The caller is responsible for calling sqlite3_finalize(pStmt),
+ * where pStmt is the return from this function.
+ */
+static sqlite3_stmt *get_param_value(sqlite3 *db, char *name,
+ ParamTableUse ptu){
+ sqlite3_stmt *rv = 0;
+ int rc;
+ char *zSql = sqlite3_mprintf
+ ( "SELECT value FROM "PARAM_TABLE_SNAME
+ " WHERE key=%Q AND uses=%d", name, ptu );
+ shell_check_oom(zSql);
+ rc = sqlite3_prepare_v2(db, zSql, -1, &rv, 0);
+ sqlite3_free(zSql);
+ if( SQLITE_OK==rc ){
+ if( SQLITE_ROW==sqlite3_step(rv) ) return rv;
+ sqlite3_finalize(rv);
+ }
+ return 0;
+}
+
+static struct ParamSetOpts {
+ const char cCast;
+ const char *zTypename;
+ int evalKind;
+} param_set_opts[] = {
+ /* { 'q', 0, 2 }, */
+ /* { 'x', 0, 1 }, */
+ { 'i', "INT", 1 },
+ { 'r', "REAL", 1 },
+ { 'b', "BLOB", 1 },
+ { 't', "TEXT", 0 },
+ { 'n', "NUMERIC", 1 }
+};
+
+/* Return an option character if it is single and prefixed by - or --,
+ * else return 0.
+ */
+static char option_char(char *zArg){
+ if( zArg[0]=='-' ){
+ ++zArg;
+ if( zArg[0]=='-' ) ++zArg;
+ if( zArg[0]!=0 && zArg[1]==0 ) return zArg[0];
+ }
+ return 0;
+}
+
+static int param_set(sqlite3 *db, char cCast,
+ char *name, char **valBeg, char **valLim,
+ ParamTableUse ptu){
+ char *zSql = 0;
+ int rc = SQLITE_OK, retries = 0, needsEval = 1;
+ char *zValGlom = (valLim-valBeg>1)? values_join(valBeg, valLim) : 0;
+ sqlite3_stmt *pStmtSet = 0;
+ const char *zCastTo = 0;
+ char *zValue = (zValGlom==0)? *valBeg : zValGlom;
+ if( cCast ){
+ struct ParamSetOpts *pSO = param_set_opts;
+ for(; pSO-param_set_opts < ArraySize(param_set_opts); ++pSO ){
+ if( cCast==pSO->cCast ){
+ zCastTo = pSO->zTypename;
+ needsEval = pSO->evalKind > 0;
+ break;
+ }
+ }
+ }
+ if( needsEval ){
+ if( zCastTo!=0 ){
+ zSql = sqlite3_mprintf
+ ( "REPLACE INTO "PARAM_TABLE_SNAME"(key,value,uses)"
+ " VALUES(%Q,CAST((%s) AS %s),%d);", name, zValue, zCastTo, ptu );
+ }else{
+ zSql = sqlite3_mprintf
+ ( "REPLACE INTO "PARAM_TABLE_SNAME"(key,value,uses)"
+ "VALUES(%Q,(%s),%d);", name, zValue, ptu );
+ }
+ shell_check_oom(zSql);
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmtSet, 0);
+ sqlite3_free(zSql);
+ }
+ if( !needsEval || rc!=SQLITE_OK ){
+ /* Reach here when value either requested to be cast to text, or must be. */
+ sqlite3_finalize(pStmtSet);
+ pStmtSet = 0;
+ zSql = sqlite3_mprintf
+ ( "REPLACE INTO "PARAM_TABLE_SNAME"(key,value,uses)"
+ "VALUES(%Q,%Q,%d);", name, zValue, ptu );
+ shell_check_oom(zSql);
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmtSet, 0);
+ assert(rc==SQLITE_OK);
+ sqlite3_free(zSql);
+ }
+ sqlite3_step(pStmtSet);
+ sqlite3_finalize(pStmtSet);
+ sqlite3_free(zValGlom);
+ return rc;
+}
+
+/* list subcommand for .parameter dot-command */
+static void list_params(ShellState *p, ParamTableUse ptu){
+ sqlite3_stmt *pStmt = 0;
+ int len = 0;
+ int rc = sqlite3_prepare_v2
+ (p->db, "SELECT max(length(key)) FROM "
+ PARAM_TABLE_SNAME" WHERE ?1=3 OR uses=?1", -1, &pStmt, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int(pStmt, 1, ptu);
+ if( sqlite3_step(pStmt)==SQLITE_ROW ){
+ len = sqlite3_column_int(pStmt, 0);
+ if( len>40 ) len = 40;
+ if( len<4 ) len = 4;
+ }
+ }
+ sqlite3_finalize(pStmt);
+ pStmt = 0;
+ if( len ){
+ utf8_printf(p->out, "%-*s %-8s %s\n", len, "name", "usage", "value");
+ rc = sqlite3_prepare_v2
+ (p->db, "SELECT key, uses, quote(value) FROM " PARAM_TABLE_SNAME
+ " WHERE ?1=3 OR uses=?1 ORDER BY key", -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);
+ const char *zUse;
+ switch( ptux ){
+ case PTU_Binding: zUse = "binding"; break;
+ case PTU_Script: zUse = "script"; break;
+ default: zUse = "unknown";
+ }
+ utf8_printf(p->out, "%-*s %-8s %s\n", len, sqlite3_column_text(pStmt,0),
+ zUse, sqlite3_column_text(pStmt,2));
+ }
+ sqlite3_finalize(pStmt);
+ }
- }
++}
+
+/* Append either an IN clause or an always true test to some SQL.
+ *
+ * An empty IN list is the same as always true (for non-NULL LHS)
+ * for this clause, which assumes a trailing LHS operand and space.
+ * If that is not the right result, guard the call against it.
+ * This is used for ".parameter dostuff ?NAMES?" options,
+ * where a missing list means all the qualifying entries.
+ *
+ * The empty list may be signified by azBeg and azLim both 0.
+ */
+static void append_in_clause(sqlite3_str *pStr,
+ const char **azBeg, const char **azLim){
+ if( azBeg==azLim ) sqlite3_str_appendf(pStr, "NOT NULL");
+ else{
+ char cSep = '(';
+ sqlite3_str_appendf(pStr, "IN");
+ while( azBeg<azLim ){
+ sqlite3_str_appendf(pStr, "%c%Q", cSep, *azBeg);
+ cSep = ',';
+ ++azBeg;
+ }
+ sqlite3_str_appendf(pStr, ")");
+ }
+}
+
+
-/*
++/*
+ * zAutoColumn(zCol, &db, ?) => Maybe init db, add column zCol to it.
+ * zAutoColumn(0, &db, ?) => (db!=0) Form columns spec for CREATE TABLE,
+ * close db and set it to 0, and return the columns spec, to later
+ * be sqlite3_free()'ed by the caller.
+ * The return is 0 when either:
+ * (a) The db was not initialized and zCol==0 (There are no columns.)
+ * (b) zCol!=0 (Column was added, db initialized as needed.)
+ * The 3rd argument, pRenamed, references an out parameter. If the
+ * pointer is non-zero, its referent will be set to a summary of renames
+ * done if renaming was necessary, or set to 0 if none was done. The out
+ * string (if any) must be sqlite3_free()'ed by the caller.
+ */
+ #ifdef SHELL_DEBUG
+ #define rc_err_oom_die(rc) \
+ if( rc==SQLITE_NOMEM ) shell_check_oom(0); \
+ else if(!(rc==SQLITE_OK||rc==SQLITE_DONE)) \
+ fprintf(stderr,"E:%d\n",rc), assert(0)
+ #else
+ static void rc_err_oom_die(int rc){
+ if( rc==SQLITE_NOMEM ) shell_check_oom(0);
+ assert(rc==SQLITE_OK||rc==SQLITE_DONE);
+ }
+ #endif
+
+ #ifdef SHELL_COLFIX_DB /* If this is set, the DB can be in a file. */
+ static char zCOL_DB[] = SHELL_STRINGIFY(SHELL_COLFIX_DB);
+ #else /* Otherwise, memory is faster/better for the transient DB. */
+ static const char *zCOL_DB = ":memory:";
+ #endif
+
+ /* Define character (as C string) to separate generated column ordinal
+ * from protected part of incoming column names. This defaults to "_"
+ * so that incoming column identifiers that did not need not be quoted
+ * remain usable without being quoted. It must be one character.
+ */
+ #ifndef SHELL_AUTOCOLUMN_SEP
+ # define AUTOCOLUMN_SEP "_"
+ #else
+ # define AUTOCOLUMN_SEP SHELL_STRINGIFY(SHELL_AUTOCOLUMN_SEP)
+ #endif
+
+ static char *zAutoColumn(const char *zColNew, sqlite3 **pDb, char **pzRenamed){
+ /* Queries and D{D,M}L used here */
+ static const char * const zTabMake = "\
+ CREATE TABLE ColNames(\
+ cpos INTEGER PRIMARY KEY,\
+ name TEXT, nlen INT, chop INT, reps INT, suff TEXT);\
+ CREATE VIEW RepeatedNames AS \
+ SELECT DISTINCT t.name FROM ColNames t \
+ WHERE t.name COLLATE NOCASE IN (\
+ SELECT o.name FROM ColNames o WHERE o.cpos<>t.cpos\
+ );\
+ ";
+ static const char * const zTabFill = "\
+ INSERT INTO ColNames(name,nlen,chop,reps,suff)\
+ VALUES(iif(length(?1)>0,?1,'?'),max(length(?1),1),0,0,'')\
+ ";
+ static const char * const zHasDupes = "\
+ SELECT count(DISTINCT (substring(name,1,nlen-chop)||suff) COLLATE NOCASE)\
+ <count(name) FROM ColNames\
+ ";
+ #ifdef SHELL_COLUMN_RENAME_CLEAN
+ static const char * const zDedoctor = "\
+ UPDATE ColNames SET chop=iif(\
+ (substring(name,nlen,1) BETWEEN '0' AND '9')\
+ AND (rtrim(name,'0123456790') glob '*"AUTOCOLUMN_SEP"'),\
+ nlen-length(rtrim(name, '"AUTOCOLUMN_SEP"0123456789')),\
+ 0\
+ )\
+ ";
+ #endif
+ static const char * const zSetReps = "\
+ UPDATE ColNames AS t SET reps=\
+ (SELECT count(*) FROM ColNames d \
+ WHERE substring(t.name,1,t.nlen-t.chop)=substring(d.name,1,d.nlen-d.chop)\
+ COLLATE NOCASE\
+ )\
+ ";
+ #ifdef SQLITE_ENABLE_MATH_FUNCTIONS
+ static const char * const zColDigits = "\
+ SELECT CAST(ceil(log(count(*)+0.5)) AS INT) FROM ColNames \
+ ";
+ #endif
+ static const char * const zRenameRank =
+ #ifdef SHELL_COLUMN_RENAME_CLEAN
+ "UPDATE ColNames AS t SET suff="
+ "iif(reps>1, printf('%c%0*d', '"AUTOCOLUMN_SEP"', $1, cpos), '')"
+ #else /* ...RENAME_MINIMAL_ONE_PASS */
+ "WITH Lzn(nlz) AS (" /* Find minimum extraneous leading 0's for uniqueness */
+ " SELECT 0 AS nlz"
+ " UNION"
+ " SELECT nlz+1 AS nlz FROM Lzn"
+ " WHERE EXISTS("
+ " SELECT 1"
+ " FROM ColNames t, ColNames o"
+ " WHERE"
+ " iif(t.name IN (SELECT * FROM RepeatedNames),"
+ " printf('%s"AUTOCOLUMN_SEP"%s',"
+ " t.name, substring(printf('%.*c%0.*d',nlz+1,'0',$1,t.cpos),2)),"
+ " t.name"
+ " )"
+ " ="
+ " iif(o.name IN (SELECT * FROM RepeatedNames),"
+ " printf('%s"AUTOCOLUMN_SEP"%s',"
+ " o.name, substring(printf('%.*c%0.*d',nlz+1,'0',$1,o.cpos),2)),"
+ " o.name"
+ " )"
+ " COLLATE NOCASE"
+ " AND o.cpos<>t.cpos"
+ " GROUP BY t.cpos"
+ " )"
+ ") UPDATE Colnames AS t SET"
+ " chop = 0," /* No chopping, never touch incoming names. */
+ " suff = iif(name IN (SELECT * FROM RepeatedNames),"
+ " printf('"AUTOCOLUMN_SEP"%s', substring("
+ " printf('%.*c%0.*d',(SELECT max(nlz) FROM Lzn)+1,'0',1,t.cpos),2)),"
+ " ''"
+ " )"
+ #endif
+ ;
+ static const char * const zCollectVar = "\
+ SELECT\
+ '('||x'0a'\
+ || group_concat(\
+ cname||' TEXT',\
+ ','||iif((cpos-1)%4>0, ' ', x'0a'||' '))\
+ ||')' AS ColsSpec \
+ FROM (\
+ SELECT cpos, printf('\"%w\"',printf('%.*s%s', nlen-chop,name,suff)) AS cname \
+ FROM ColNames ORDER BY cpos\
+ )";
+ static const char * const zRenamesDone =
+ "SELECT group_concat("
+ " printf('\"%w\" to \"%w\"',name,printf('%.*s%s', nlen-chop, name, suff)),"
+ " ','||x'0a')"
+ "FROM ColNames WHERE suff<>'' OR chop!=0"
+ ;
+ int rc;
+ sqlite3_stmt *pStmt = 0;
+ assert(pDb!=0);
+ if( zColNew ){
+ /* Add initial or additional column. Init db if necessary. */
+ if( *pDb==0 ){
+ if( SQLITE_OK!=sqlite3_open(zCOL_DB, pDb) ) return 0;
+ #ifdef SHELL_COLFIX_DB
+ if(*zCOL_DB!=':')
+ sqlite3_exec(*pDb,"drop table if exists ColNames;"
+ "drop view if exists RepeatedNames;",0,0,0);
+ #endif
+ rc = sqlite3_exec(*pDb, zTabMake, 0, 0, 0);
+ rc_err_oom_die(rc);
+ }
+ assert(*pDb!=0);
+ rc = sqlite3_prepare_v2(*pDb, zTabFill, -1, &pStmt, 0);
+ rc_err_oom_die(rc);
+ rc = sqlite3_bind_text(pStmt, 1, zColNew, -1, 0);
+ rc_err_oom_die(rc);
+ rc = sqlite3_step(pStmt);
+ rc_err_oom_die(rc);
+ sqlite3_finalize(pStmt);
+ return 0;
+ }else if( *pDb==0 ){
+ return 0;
+ }else{
+ /* Formulate the columns spec, close the DB, zero *pDb. */
+ char *zColsSpec = 0;
+ int hasDupes = db_int(*pDb, zHasDupes);
+ #ifdef SQLITE_ENABLE_MATH_FUNCTIONS
+ int nDigits = (hasDupes)? db_int(*pDb, zColDigits) : 0;
+ #else
+ # define nDigits 2
+ #endif
+ if( hasDupes ){
+ #ifdef SHELL_COLUMN_RENAME_CLEAN
+ rc = sqlite3_exec(*pDb, zDedoctor, 0, 0, 0);
+ rc_err_oom_die(rc);
+ #endif
+ rc = sqlite3_exec(*pDb, zSetReps, 0, 0, 0);
+ rc_err_oom_die(rc);
+ rc = sqlite3_prepare_v2(*pDb, zRenameRank, -1, &pStmt, 0);
+ rc_err_oom_die(rc);
+ sqlite3_bind_int(pStmt, 1, nDigits);
+ rc = sqlite3_step(pStmt);
+ sqlite3_finalize(pStmt);
+ assert(rc==SQLITE_DONE);
+ }
+ assert(db_int(*pDb, zHasDupes)==0); /* Consider: remove this */
+ rc = sqlite3_prepare_v2(*pDb, zCollectVar, -1, &pStmt, 0);
+ rc_err_oom_die(rc);
+ rc = sqlite3_step(pStmt);
+ if( rc==SQLITE_ROW ){
+ zColsSpec = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
+ }else{
+ zColsSpec = 0;
+ }
+ if( pzRenamed!=0 ){
+ if( !hasDupes ) *pzRenamed = 0;
+ else{
+ sqlite3_finalize(pStmt);
+ if( SQLITE_OK==sqlite3_prepare_v2(*pDb, zRenamesDone, -1, &pStmt, 0)
+ && SQLITE_ROW==sqlite3_step(pStmt) ){
+ *pzRenamed = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
+ }else
+ *pzRenamed = 0;
+ }
+ }
+ sqlite3_finalize(pStmt);
+ sqlite3_close(*pDb);
+ *pDb = 0;
+ return zColsSpec;
+ }
+ }
+
/*
** If an input line begins with "." then invoke this routine to
** process that line.
return 1;
}
if( zDb==0 ) zDb = "main";
-- rc = sqlite3_open_v2(zDestFile, &pDest,
++ rc = sqlite3_open_v2(zDestFile, &pDest,
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs);
if( rc!=SQLITE_OK ){
utf8_printf(stderr, "Error: cannot open \"%s\"\n", zDestFile);
if( nArg>1 && ii==ArraySize(aDbConfig) ){
utf8_printf(stderr, "Error: unknown dbconfig \"%s\"\n", azArg[1]);
utf8_printf(stderr, "Enter \".dbconfig\" with no arguments for a list\n");
-- }
++ }
}else
if( c=='d' && n>=3 && strncmp(azArg[0], "dbinfo", n)==0 ){
int i;
int savedShowHeader = p->showHeader;
int savedShellFlags = p->shellFlgs;
-- ShellClearFlag(p,
++ ShellClearFlag(p,
SHFLG_PreserveRowid|SHFLG_Newlines|SHFLG_Echo
|SHFLG_DumpDataOnly|SHFLG_DumpNoSys);
for(i=1; i<nArg; i++){
" name LIKE %Q ESCAPE '\\' AND"
" sql LIKE 'CREATE VIRTUAL TABLE%%' AND"
" substr(o.name, 1, length(name)+1) == (name||'_')"
- ")", azArg[i], azArg[i]
+ ")", azArg[i], zSchema, azArg[i]
);
--
++
if( zLike ){
zLike = sqlite3_mprintf("%z OR %z", zLike, zExpr);
}else{
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( c=='e' && strncmp(azArg[0], "expert", n)==0 ){
if( p->bSafeMode ){
-- raw_printf(stderr,
++ raw_printf(stderr,
"Cannot run experimental commands such as \"%s\" in safe mode\n",
azArg[0]);
rc = 1;
} aCtrl[] = {
{ "chunk_size", SQLITE_FCNTL_CHUNK_SIZE, "SIZE" },
{ "data_version", SQLITE_FCNTL_DATA_VERSION, "" },
-- { "has_moved", SQLITE_FCNTL_HAS_MOVED, "" },
++ { "has_moved", SQLITE_FCNTL_HAS_MOVED, "" },
{ "lock_timeout", SQLITE_FCNTL_LOCK_TIMEOUT, "MILLISEC" },
{ "persist_wal", SQLITE_FCNTL_PERSIST_WAL, "[BOOLEAN]" },
/* { "pragma", SQLITE_FCNTL_PRAGMA, "NAME ARG" },*/
open_db(p, 0);
zCmd = nArg>=2 ? azArg[1] : "help";
-- if( zCmd[0]=='-'
++ if( zCmd[0]=='-'
&& (strcmp(zCmd,"--schema")==0 || strcmp(zCmd,"-schema")==0)
&& nArg>=4
){
goto meta_command_exit;
}
if( nSep>1 ){
-- raw_printf(stderr,
++ raw_printf(stderr,
"Error: multi-character column separators not allowed"
" for import\n");
rc = 1;
if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(p->db))==0 ){
char *zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"",
zSchema, zTable);
- char cSep = '(';
+ sqlite3 *dbCols = 0;
+ char *zRenames = 0;
+ char *zColDefs;
while( xRead(&sCtx) ){
- zCreate = sqlite3_mprintf("%z%c\n \"%w\" TEXT", zCreate, cSep, sCtx.z);
- cSep = ',';
+ zAutoColumn(sCtx.z, &dbCols, 0);
if( sCtx.cTerm!=sCtx.cColSep ) break;
}
- if( cSep=='(' ){
+ zColDefs = zAutoColumn(0, &dbCols, &zRenames);
+ if( zRenames!=0 ){
- utf8_printf((stdin_is_interactive && p->in==stdin)? p->out : stderr,
++ utf8_printf(INSOURCE_IS_INTERACTIVE(p->pInSource)? p->out : stderr,
+ "Columns renamed during .import %s due to duplicates:\n"
+ "%s\n", sCtx.zFile, zRenames);
+ sqlite3_free(zRenames);
+ }
+ assert(dbCols==0);
+ if( zColDefs==0 ){
sqlite3_free(zCreate);
import_cleanup(&sCtx);
utf8_printf(stderr,"%s: empty file\n", sCtx.zFile);
** VALUE can be in either SQL literal notation, or if not it will be
** understood to be a text string.
*/
- if( nArg==4 && strcmp(azArg[1],"set")==0 ){
- int rx;
- char *zSql;
- sqlite3_stmt *pStmt;
- const char *zKey = azArg[2];
- const char *zValue = azArg[3];
- bind_table_init(p);
- zSql = sqlite3_mprintf(
- "REPLACE INTO temp.sqlite_parameters(key,value)"
- "VALUES(%Q,%s);", zKey, zValue);
- shell_check_oom(zSql);
- pStmt = 0;
- rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
- sqlite3_free(zSql);
- if( rx!=SQLITE_OK ){
- sqlite3_finalize(pStmt);
- pStmt = 0;
- zSql = sqlite3_mprintf(
- "REPLACE INTO temp.sqlite_parameters(key,value)"
- "VALUES(%Q,%Q);", zKey, zValue);
- shell_check_oom(zSql);
- rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
- sqlite3_free(zSql);
- if( rx!=SQLITE_OK ){
+ if( nArg>=4 && strcmp(azArg[1],"set")==0 ){
+ char cCast = option_char(azArg[2]);
+ int inv = 2 + (cCast != 0);
+ ParamTableUse ptu = classify_param_name(azArg[inv]);
+ if( ptu==PTU_Nil ){
+ utf8_printf(stderr,
+ "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],
- &azArg[inv+1], &azArg[nArg], ptu);
++ &azArg[inv+1], &azArg[nArg], ptu);
+ if( rc!=SQLITE_OK ){
utf8_printf(p->out, "Error: %s\n", sqlite3_errmsg(p->db));
- sqlite3_finalize(pStmt);
- pStmt = 0;
rc = 1;
}
}
}
}else
- }
+ if( c=='s' && n==7 && strncmp(azArg[0], "seeargs", n)==0 ){
+ for( n=1; n<nArg; ++n ){
+ raw_printf(p->out, "%s%s", azArg[n], (n==nArg-1)? "|\n" : "|");
++ }
+ }else
+
if( c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0 ){
- unsigned int x = nArg>=2 ? (unsigned int)integerValue(azArg[1]) : 0xffffffff;
+ unsigned int x
+ = nArg>=2 ? (unsigned int)integerValue(azArg[1]) : 0xffffffff;
sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 1, &x);
}else
/* fall thru */
case ']':
cWait = 0;
- qss = QSS_SETV(qss, 0);
+ sss = SSS_SETV(sss, 0);
goto PlainScan;
-- default: assert(0);
++ default: assert(0);
}
}
}
END_TIMER;
if( rc || zErrMsg ){
char zPrefix[100];
- if( bAltIn || !stdin_is_interactive ){
- sqlite3_snprintf(sizeof(zPrefix), zPrefix,
- "Error: near line %d:", startline);
+ const char *zErrorTail;
+ const char *zErrorType;
+ if( zErrMsg==0 ){
+ zErrorType = "Error";
+ zErrorTail = sqlite3_errmsg(p->db);
+ }else if( strncmp(zErrMsg, "in prepare, ",12)==0 ){
+ zErrorType = "Parse error";
+ zErrorTail = &zErrMsg[12];
+ }else if( strncmp(zErrMsg, "stepping, ", 10)==0 ){
+ zErrorType = "Runtime error";
+ zErrorTail = &zErrMsg[10];
}else{
- sqlite3_snprintf(sizeof(zPrefix), zPrefix, "Error:");
+ zErrorType = "Error";
+ zErrorTail = zErrMsg;
}
- if( zErrMsg!=0 ){
- utf8_printf(stderr, "%s %s\n", zPrefix, zErrMsg);
- sqlite3_free(zErrMsg);
- zErrMsg = 0;
- if( in!=0 || !stdin_is_interactive ){
++ if( bAltIn || !stdin_is_interactive ){
+ sqlite3_snprintf(sizeof(zPrefix), zPrefix,
+ "%s near line %d:", zErrorType, startline);
}else{
- utf8_printf(stderr, "%s %s\n", zPrefix, sqlite3_errmsg(p->db));
+ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%s:", zErrorType);
}
- zErrMsg = 0;
+ utf8_printf(stderr, "%s %s\n", zPrefix, zErrorTail);
+ sqlite3_free(zErrMsg);
return 1;
}else if( ShellHasFlag(p, SHFLG_CountChanges) ){
char zLineBuf[2000];
return 0;
}
- if( (c=*zCmd++)==0 ) goto atEnd;
+#if SHELL_EXTENDED_PARSING
+/* Resumable line classsifier for dot-commands
+**
+** Determines if a dot-command is open, having either an unclosed
+** quoted argument or an escape sequence opener ('\') at its end.
+**
+** The FSM design/behavior assumes/requires that a terminating '\'
+** is not part of the character sequence being classified -- that
+** it represents an escaped newline which is removed as physical
+** lines are spliced to accumulate logical lines.
+**
+** The line or added line-portion is passed as zCmd.
+** The pScanState pointer must reference an (opaque) DCmd_ScanState,
+** which must be set to DCSS_Start to initialize the scanner state.
+** Resumed scanning should always be done with zCmd logically just
+** past the last non-0 char of the text previously passed in, with
+** any previously scanned, trailing newline escape first trimmed.
+** Returns are: 0 => not open (aka complete), 1 => is open (incomplete)
+** The following macros may be applied to the scan state:
+*/
+#define DCSS_InDarkArg(dcss) (((dcss)&argPosMask)==inDqArg)
+#define DCSS_EndEscaped(dcss) (((dcss)&endEscaped)!=0)
+#define DCSS_IsOpen(dcss) (((dcss)&isOpenMask)!=0)
+typedef enum {
+ DCSS_Start = 0,
+ twixtArgs = 0, inSqArg = 1, inDarkArg = 2, inDqArg = 3, /* ordered */
+ endEscaped = 4, /* bit used */
+ argPosMask = 3, /* bits used */
+ isOpenMask = 1|4 /* bit test */
+} DCmd_ScanState;
+
+static void dot_command_scan(char *zCmd, DCmd_ScanState *pScanState){
+ DCmd_ScanState ss = *pScanState & ~endEscaped;
+ char c = (ss&isOpenMask)? 1 : *zCmd++;
+ while( c!=0 ){
+ switch( ss ){
+ case twixtArgs:
+ while( IsSpace(c) ){
- if( (c=*zCmd++)==0 ) goto atEnd;
++ if( (c=*zCmd++)==0 ) goto atEnd;
+ }
+ switch( c ){
+ case '\\':
+ if( *zCmd==0 ){
+ ss |= endEscaped;
+ goto atEnd;
+ }else goto inDark;
+ case '\'': ss = inSqArg; goto inSq;
+ case '"': ss = inDqArg; goto inDq;
+ default: ss = inDarkArg; goto inDark;
+ }
+ inSq:
+ case inSqArg:
+ while( (c=*zCmd++)!='\'' ){
+ if( c==0 ) goto atEnd;
+ if( c=='\\' && *zCmd==0 ){
+ ss |= endEscaped;
+ goto atEnd;
+ }
+ }
+ ss = twixtArgs;
+ c = *zCmd++;
+ continue;
+ inDq:
+ case inDqArg:
+ do {
+ if( (c=*zCmd++)==0 ) goto atEnd;
+ if( c=='\\' ){
+ if( (c=*zCmd++)==0 ){
+ ss |= endEscaped;
+ goto atEnd;
+ }
+ if( (c=*zCmd++)==0 ) goto atEnd;
+ }
+ } while( c!='"' );
+ ss = twixtArgs;
+ c = *zCmd++;
+ continue;
+ inDark:
+ case inDarkArg:
+ while( !IsSpace(c) ){
+ if( c=='\\' && *zCmd==0 ){
+ ss |= endEscaped;
+ goto atEnd;
+ }
- /*
++ if( (c=*zCmd++)==0 ) goto atEnd;
+ }
+ ss = twixtArgs;
+ c = *zCmd++;
+ continue;
+ }
+ }
+ atEnd:
+ *pScanState = ss;
+}
+#else
+# define dot_command_scan(x,y)
+#endif
+
+/* Utility functions for process_input. */
+
+#if SHELL_EXTENDED_PARSING
+/*
+** Process dot-command line with its scan state to:
+** 1. Setup for requested line-splicing; and
+** 2. Say whether it is complete.
+** The last two out parameters are the line's length, which may be
+** adjusted, and the char to be used for joining a subsequent line.
+** This is broken out of process_input() mainly for readability.
+** The return is TRUE for dot-command ready to run, else false.
+*/
+static int line_join_ends(DCmd_ScanState dcss, char *zLine,
+ int *pnLength, char *pcLE){
+ /* It is ready only if has no open argument or escaped newline. */
+ int bOpen = DCSS_IsOpen(dcss);
+ if( !DCSS_EndEscaped(dcss) ){
+ *pcLE = '\n';
+ return !bOpen;
+ }else{
+ *pcLE = (bOpen || DCSS_InDarkArg(dcss))? 0 : ' ';
+ /* Swallow the trailing escape character. */
+ zLine[--*pnLength] = 0;
+ return 0;
+ }
+}
+#endif
+
++/*
+** Grow the accumulation line buffer to accommodate ncNeed chars.
+** In/out parameters pz and pna reference the buffer and its size.
+** The buffer must eventually be sqlite3_free()'ed by the caller.
+*/
+static void grow_line_buffer(char **pz, int *pna, int ncNeed){
+ if( ncNeed > *pna ){
+ *pna += *pna + (*pna>>1) + 100;
+ *pz = sqlite3_realloc(*pz, *pna);
+ shell_check_oom(*pz);
+ }
+}
/*
-** Read input from *in and process it. If *in==0 then input
-** is interactive - the user is typing it it. Otherwise, input
-** is coming from a file or device. A prompt is issued and history
-** is saved only if input is interactive. An interrupt signal will
-** cause this routine to exit immediately, unless input is interactive.
+** Read input from designated source (p->pInSource) and process it.
+** If pInSource==0 then input is interactive - the user is typing it.
+** Otherwise, input is coming from a file, stream device or string.
+** Prompts issue and history is saved only for interactive input.
+** An interrupt signal will cause this routine to exit immediately,
+** with "exit demanded" code returned, unless input is interactive.
**
-** Return the number of errors.
+** Returns:
+** 0 => no errors
+** 1 => errors>0
+** 2 => exit demanded, no errors.
+** 3 => exit demanded, errors>0
*/
static int process_input(ShellState *p){
- char *zLine = 0; /* A single input line */
- char *zSql = 0; /* Accumulated SQL text */
- int nLine; /* Length of current line */
- int nSql = 0; /* Bytes of zSql[] used */
- int nAlloc = 0; /* Allocated zSql[] space */
- int rc; /* Error code */
- int errCnt = 0; /* Number of errors seen */
- int startline = 0; /* Line number for start of current input */
- QuickScanState qss = QSS_Start; /* Accumulated line status (so far) */
-
+ 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
+ * not so that the number of memory allocations can be reduced. They are
+ * reused from one incoming group to another, realloc()'ed as needed. */
+ int naAccum = 0; /* tracking how big zLineAccum buffer has become */
+ /* 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);
+ 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 ){
- /* This will be more informative in a later version. */
- utf8_printf(stderr,"Input nesting limit (%d) reached at line %d."
- " Check recursion.\n", MAX_INPUT_NESTING, p->lineno);
+ InSource *pInSrc = p->pInSource->pFrom;
+ const char *zLead = "Input nesting limit ("
+ SHELL_STRINGIFY(MAX_INPUT_NESTING)") reached,";
+ int i = 3;
+ assert(pInSrc!=0 && MAX_INPUT_NESTING>0);
+ while( i-->0 && pInSrc!=0 ){
+ utf8_printf(stderr,
+ "%s from line %d of \"%s\"",
+ zLead, pInSrc->lineno, pInSrc->zSourceSay);
+ zLead = (i%2==0)? "\n" : "";
+ pInSrc=pInSrc->pFrom;
+ }
+ utf8_printf(stderr, " ...\nERROR: Check recursion.\n");
return 1;
}
++p->inputNesting;