#define _CRT_SECURE_NO_WARNINGS
#endif
+/*
+** Optionally #include a user-defined header, whereby compilation options
+** 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.
+*/
+#ifdef SQLITE_CUSTOM_INCLUDE
+# define INC_STRINGIFY_(f) #f
+# define INC_STRINGIFY(f) INC_STRINGIFY_(f)
+# include INC_STRINGIFY(SQLITE_CUSTOM_INCLUDE)
+#endif
+
/*
** Determine if we are dealing with WinRT, which provides only a subset of
** the full Win32 API.
}
/*
-** Return true if zFile does not exist or if it is not an ordinary file.
+** Return open FILE * if zFile exists, can be opened for read
+** and is an ordinary file or a character stream source.
+** Otherwise return 0.
*/
+static FILE * openChrSource(const char *zFile){
#ifdef _WIN32
-# define notNormalFile(X) 0
+ struct _stat x = {0};
+# define STAT_CHR_SRC(mode) ((mode & (_S_IFCHR|_S_IFIFO|_S_IFREG))!=0)
+ /* On Windows, open first, then check the stream nature. This order
+ ** is necessary because _stat() and sibs, when checking a named pipe,
+ ** effectively break the pipe as its supplier sees it. */
+ FILE *rv = fopen(zFile, "rb");
+ if( rv==0 ) return 0;
+ if( _fstat(_fileno(rv), &x) != 0
+ || !STAT_CHR_SRC(x.st_mode)){
+ fclose(rv);
+ rv = 0;
+ }
+ return rv;
#else
-static int notNormalFile(const char *zFile){
- struct stat x;
- int rc;
- memset(&x, 0, sizeof(x));
- rc = stat(zFile, &x);
- return rc || !S_ISREG(x.st_mode);
-}
+ struct stat x = {0};
+ int rc = stat(zFile, &x);
+# define STAT_CHR_SRC(mode) (S_ISREG(mode)||S_ISFIFO(mode)||S_ISCHR(mode))
+ if( rc!=0 ) return 0;
+ if( STAT_CHR_SRC(x.st_mode) ){
+ return fopen(zFile, "rb");
+ }else{
+ return 0;
+ }
#endif
+#undef STAT_CHR_SRC
+}
/*
** This routine reads a line of text from FILE in, stores
}
}
- if( p->n+len>=p->nAlloc ){
+ if( p->z==0 || p->n+len>=p->nAlloc ){
p->nAlloc = p->nAlloc*2 + len + 20;
p->z = realloc(p->z, p->nAlloc);
if( p->z==0 ) shell_out_of_memory();
sqlite3 *db = sqlite3_context_db_handle(pCtx);
UNUSED_PARAMETER(nVal);
if( zIn!=0 && strncmp(zIn, "CREATE ", 7)==0 ){
- for(i=0; i<(int)(sizeof(aPrefix)/sizeof(aPrefix[0])); i++){
+ for(i=0; i<ArraySize(aPrefix); i++){
int n = strlen30(aPrefix[i]);
if( strncmp(zIn+7, aPrefix[i], n)==0 && zIn[n+7]==' ' ){
char *z = 0;
** $path is a relative path, then $path is interpreted relative to $dir.
** And the paths returned in the "name" column of the table are also
** relative to directory $dir.
+**
+** Notes on building this extension for Windows:
+** Unless linked statically with the SQLite library, a preprocessor
+** symbol, FILEIO_WIN32_DLL, must be #define'd to create a stand-alone
+** DLL form of this extension for WIN32. See its use below for details.
*/
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
return (fileIntervals.QuadPart - epochIntervals.QuadPart) / 10000000;
}
+
+#if defined(FILEIO_WIN32_DLL) && (defined(_WIN32) || defined(WIN32))
+# /* To allow a standalone DLL, use this next replacement function: */
+# undef sqlite3_win32_utf8_to_unicode
+# define sqlite3_win32_utf8_to_unicode utf8_to_utf16
+#
+LPWSTR utf8_to_utf16(const char *z){
+ int nAllot = MultiByteToWideChar(CP_UTF8, 0, z, -1, NULL, 0);
+ LPWSTR rv = sqlite3_malloc(nAllot * sizeof(WCHAR));
+ if( rv!=0 && 0 < MultiByteToWideChar(CP_UTF8, 0, z, -1, rv, nAllot) )
+ return rv;
+ sqlite3_free(rv);
+ return 0;
+}
+#endif
+
/*
** This function attempts to normalize the time values found in the stat()
** buffer to UTC. This is necessary on Win32, where the runtime library
return rc;
}
+#if defined(FILEIO_WIN32_DLL) && (defined(_WIN32) || defined(WIN32))
+/* To allow a standalone DLL, make test_windirent.c use the same
+ * redefined SQLite API calls as the above extension code does.
+ * Just pull in this .c to accomplish this. As a beneficial side
+ * effect, this extension becomes a single translation unit. */
+# include "test_windirent.c"
+#endif
+
/************************* End ../ext/misc/fileio.c ********************/
/************************* Begin ../ext/misc/completion.c ******************/
/*
e += 1075;
if( e<=0 ){
/* Subnormal */
- m >>= 1-e;
+ if( 1-e >= 64 ){
+ m = 0;
+ }else{
+ m >>= 1-e;
+ }
e = 0;
}else if( e>0x7ff ){
e = 0x7ff;
** (8) output in descending order
*/
static int seriesBestIndex(
- sqlite3_vtab *tabUnused,
+ sqlite3_vtab *pVTab,
sqlite3_index_info *pIdxInfo
){
int i, j; /* Loop over constraints */
int idxNum = 0; /* The query plan bitmask */
+ int bStartSeen = 0; /* EQ constraint seen on the START column */
int unusableMask = 0; /* Mask of unusable constraints */
int nArg = 0; /* Number of arguments that seriesFilter() expects */
int aIdx[3]; /* Constraints on start, stop, and step */
** are the last three columns in the virtual table. */
assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 );
assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 );
- (void)tabUnused;
+
aIdx[0] = aIdx[1] = aIdx[2] = -1;
pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
iCol = pConstraint->iColumn - SERIES_COLUMN_START;
assert( iCol>=0 && iCol<=2 );
iMask = 1 << iCol;
+ if( iCol==0 ) bStartSeen = 1;
if( pConstraint->usable==0 ){
unusableMask |= iMask;
continue;
pIdxInfo->aConstraintUsage[j].omit = !SQLITE_SERIES_CONSTRAINT_VERIFY;
}
}
+ /* The current generate_column() implementation requires at least one
+ ** argument (the START value). Legacy versions assumed START=0 if the
+ ** first argument was omitted. Compile with -DZERO_ARGUMENT_GENERATE_SERIES
+ ** to obtain the legacy behavior */
+#ifndef ZERO_ARGUMENT_GENERATE_SERIES
+ if( !bStartSeen ){
+ sqlite3_free(pVTab->zErrMsg);
+ pVTab->zErrMsg = sqlite3_mprintf(
+ "first argument to \"generate_series()\" missing or unusable");
+ return SQLITE_ERROR;
+ }
+#endif
if( (unusableMask & ~idxNum)!=0 ){
/* The start, stop, and step columns are inputs. Therefore if there
** are unusable constraints on any of start, stop, or step then
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( sqlite3_libversion_number()<3008012 ){
+ if( sqlite3_libversion_number()<3008012 && pzErrMsg!=0 ){
*pzErrMsg = sqlite3_mprintf(
"generate_series() requires SQLite 3.8.12 or later");
return SQLITE_ERROR;
}
case RE_OP_CC_EXC: {
if( c==0 ) break;
- /* fall-through */
+ /* fall-through */ goto re_op_cc_inc;
}
- case RE_OP_CC_INC: {
+ case RE_OP_CC_INC: re_op_cc_inc: {
int j = 1;
int n = pRe->aArg[x];
int hit = 0;
#define MIN(a,b) ((a)<(b) ? (a) : (b))
#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
+# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1
+#endif
+#if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS)
# define ALWAYS(X) (1)
# define NEVER(X) (0)
#elif !defined(NDEBUG)
** Read and return a 32-bit little-endian unsigned integer from buffer aBuf.
*/
static u32 zipfileGetU32(const u8 *aBuf){
+ if( aBuf==0 ) return 0;
return ((u32)(aBuf[3]) << 24)
+ ((u32)(aBuf[2]) << 16)
+ ((u32)(aBuf[1]) << 8)
aRead = (u8*)&aBlob[pNew->cds.iOffset];
}
- rc = zipfileReadLFH(aRead, &lfh);
+ if( rc==SQLITE_OK ) rc = zipfileReadLFH(aRead, &lfh);
if( rc==SQLITE_OK ){
pNew->iDataOff = pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ;
pNew->iDataOff += lfh.nFile + lfh.nExtra;
int nRead; /* Bytes to read from file */
int rc = SQLITE_OK;
+ memset(pEOCD, 0, sizeof(ZipfileEOCD));
if( aBlob==0 ){
i64 iOff; /* Offset to read from */
i64 szFile; /* Total size of file in bytes */
fseek(pFile, 0, SEEK_END);
szFile = (i64)ftell(pFile);
if( szFile==0 ){
- memset(pEOCD, 0, sizeof(ZipfileEOCD));
return SQLITE_OK;
}
nRead = (int)(MIN(szFile, ZIPFILE_BUFFER_SIZE));
#include <string.h>
#include <stdio.h>
+#if !defined(SQLITE_AMALGAMATION)
+#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
+# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1
+#endif
+#if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS)
+# define ALWAYS(X) (1)
+# define NEVER(X) (0)
+#elif !defined(NDEBUG)
+# define ALWAYS(X) ((X)?1:(assert(0),0))
+# define NEVER(X) ((X)?(assert(0),1):0)
+#else
+# define ALWAYS(X) (X)
+# define NEVER(X) (X)
+#endif
+#endif /* !defined(SQLITE_AMALGAMATION) */
+
+
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* typedef sqlite3_int64 i64; */
rc = idxPrintfPrepareStmt(db, &p1, pzErrmsg, "PRAGMA table_xinfo=%Q", zTab);
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(p1) ){
const char *zCol = (const char*)sqlite3_column_text(p1, 1);
+ const char *zColSeq = 0;
nByte += 1 + STRLEN(zCol);
rc = sqlite3_table_column_metadata(
- db, "main", zTab, zCol, 0, &zCol, 0, 0, 0
+ db, "main", zTab, zCol, 0, &zColSeq, 0, 0, 0
);
- nByte += 1 + STRLEN(zCol);
+ if( zColSeq==0 ) zColSeq = "binary";
+ nByte += 1 + STRLEN(zColSeq);
nCol++;
nPk += (sqlite3_column_int(p1, 5)>0);
}
nCol = 0;
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(p1) ){
const char *zCol = (const char*)sqlite3_column_text(p1, 1);
+ const char *zColSeq = 0;
int nCopy = STRLEN(zCol) + 1;
pNew->aCol[nCol].zName = pCsr;
pNew->aCol[nCol].iPk = (sqlite3_column_int(p1, 5)==1 && nPk==1);
pCsr += nCopy;
rc = sqlite3_table_column_metadata(
- db, "main", zTab, zCol, 0, &zCol, 0, 0, 0
+ db, "main", zTab, zCol, 0, &zColSeq, 0, 0, 0
);
if( rc==SQLITE_OK ){
- nCopy = STRLEN(zCol) + 1;
+ if( zColSeq==0 ) zColSeq = "binary";
+ nCopy = STRLEN(zColSeq) + 1;
pNew->aCol[nCol].zColl = pCsr;
- memcpy(pCsr, zCol, nCopy);
+ memcpy(pCsr, zColSeq, nCopy);
pCsr += nCopy;
}
if( rc!=SQLITE_OK ){
sqlite3_free(pNew);
pNew = 0;
- }else{
+ }else if( ALWAYS(pNew!=0) ){
pNew->zName = pCsr;
- memcpy(pNew->zName, zTab, nTab+1);
+ if( ALWAYS(pNew->zName!=0) ) memcpy(pNew->zName, zTab, nTab+1);
}
*ppOut = pNew;
return 0;
}
+/* Callback for sqlite3_exec() with query with leading count(*) column.
+ * The first argument is expected to be an int*, referent to be incremented
+ * if that leading column is not exactly '0'.
+ */
+static int countNonzeros(void* pCount, int nc,
+ char* azResults[], char* azColumns[]){
+ (void)azColumns; /* Suppress unused parameter warning */
+ if( nc>0 && (azResults[0][0]!='0' || azResults[0][1]!=0) ){
+ *((int *)pCount) += 1;
+ }
+ return 0;
+}
+
static int idxCreateFromCons(
sqlite3expert *p,
IdxScan *pScan,
if( rc==SQLITE_OK ){
/* Hash the list of columns to come up with a name for the index */
const char *zTable = pScan->pTab->zName;
- char *zName; /* Index name */
- int i;
- for(i=0; zCols[i]; i++){
- h += ((h<<3) + zCols[i]);
- }
- zName = sqlite3_mprintf("%s_idx_%08x", zTable, h);
- if( zName==0 ){
+ int quoteTable = idxIdentifierRequiresQuotes(zTable);
+ char *zName = 0; /* Index name */
+ int collisions = 0;
+ do{
+ int i;
+ char *zFind;
+ for(i=0; zCols[i]; i++){
+ h += ((h<<3) + zCols[i]);
+ }
+ sqlite3_free(zName);
+ zName = sqlite3_mprintf("%s_idx_%08x", zTable, h);
+ if( zName==0 ) break;
+ /* Is is unique among table, view and index names? */
+ zFmt = "SELECT count(*) FROM sqlite_schema WHERE name=%Q"
+ " AND type in ('index','table','view')";
+ zFind = sqlite3_mprintf(zFmt, zName);
+ i = 0;
+ rc = sqlite3_exec(dbm, zFind, countNonzeros, &i, 0);
+ assert(rc==SQLITE_OK);
+ sqlite3_free(zFind);
+ if( i==0 ){
+ collisions = 0;
+ break;
+ }
+ ++collisions;
+ }while( collisions<50 && zName!=0 );
+ if( collisions ){
+ /* This return means "Gave up trying to find a unique index name." */
+ rc = SQLITE_BUSY_TIMEOUT;
+ }else if( zName==0 ){
rc = SQLITE_NOMEM;
}else{
- if( idxIdentifierRequiresQuotes(zTable) ){
- zFmt = "CREATE INDEX '%q' ON %Q(%s)";
+ if( quoteTable ){
+ zFmt = "CREATE INDEX \"%w\" ON \"%w\"(%s)";
}else{
zFmt = "CREATE INDEX %s ON %s(%s)";
}
rc = SQLITE_NOMEM;
}else{
rc = sqlite3_exec(dbm, zIdx, 0, 0, p->pzErrmsg);
- idxHashAdd(&rc, &p->hIdx, zName, zIdx);
+ if( rc!=SQLITE_OK ){
+ rc = SQLITE_BUSY_TIMEOUT;
+ }else{
+ idxHashAdd(&rc, &p->hIdx, zName, zIdx);
+ }
}
sqlite3_free(zName);
sqlite3_free(zIdx);
/* Create candidate indexes within the in-memory database file */
if( rc==SQLITE_OK ){
rc = idxCreateCandidates(p);
+ }else if ( rc==SQLITE_BUSY_TIMEOUT ){
+ if( pzErr )
+ *pzErr = sqlite3_mprintf("Cannot find a unique index name to propose.");
+ return rc;
}
/* Generate the stat1 data */
u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */
u8 nEqpLevel; /* Depth of the EQP output graph */
u8 eTraceType; /* SHELL_TRACE_* value for type of trace */
+ u8 bSafeMode; /* True to prohibit unsafe operations */
+ u8 bSafeModePersist; /* The long-term value of bSafeMode */
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 */
char nullValue[20]; /* The text to print when a NULL comes back from
** the database */
char outfile[FILENAME_MAX]; /* Filename for *out */
- const char *zDbFilename; /* name of the database file */
- char *zFreeOnClose; /* Filename to free when closing */
- const char *zVfs; /* Name of VFS to use */
sqlite3_stmt *pStmt; /* Current statement if any. */
FILE *pLog; /* Write log output here */
+ struct AuxDb { /* Storage space for auxiliary database connections */
+ sqlite3 *db; /* Connection pointer */
+ const char *zDbFilename; /* Filename used to open the connection */
+ char *zFreeOnClose; /* Free this memory allocation on close */
+#if defined(SQLITE_ENABLE_SESSION)
+ int nSession; /* Number of active sessions */
+ OpenSession aSession[4]; /* Array of sessions. [0] is in focus. */
+#endif
+ } aAuxDb[5], /* Array of all database connections */
+ *pAuxDb; /* Currently active database connection */
int *aiIndent; /* Array of indents used in MODE_Explain */
int nIndent; /* Size of array aiIndent[] */
int iIndent; /* Index of current op in aiIndent[] */
+ char *zNonce; /* Nonce for temporary safe-mode excapes */
EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */
-#if defined(SQLITE_ENABLE_SESSION)
- int nSession; /* Number of active sessions */
- OpenSession aSession[4]; /* Array of sessions. [0] is in focus. */
-#endif
- ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */
+ ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */
};
#define SHFLG_Newlines 0x00000010 /* .dump --newline flag */
#define SHFLG_CountChanges 0x00000020 /* .changes setting */
#define SHFLG_Echo 0x00000040 /* .echo or --echo setting */
-#define SHFLG_HeaderSet 0x00000080 /* .header has been used */
+#define SHFLG_HeaderSet 0x00000080 /* showHeader has been specified */
#define SHFLG_DumpDataOnly 0x00000100 /* .dump show data only */
#define SHFLG_DumpNoSys 0x00000200 /* .dump omits system tables */
sqlite3_result_value(pCtx, apVal[0]);
}
+/*
+** If in safe mode, print an error message described by the arguments
+** and exit immediately.
+*/
+static void failIfSafeMode(
+ ShellState *p,
+ const char *zErrMsg,
+ ...
+){
+ if( p->bSafeMode ){
+ va_list ap;
+ char *zMsg;
+ va_start(ap, zErrMsg);
+ zMsg = sqlite3_vmprintf(zErrMsg, ap);
+ va_end(ap);
+ raw_printf(stderr, "line %d: ", p->lineno);
+ utf8_printf(stderr, "%s\n", zMsg);
+ exit(1);
+ }
+}
+
/*
** SQL function: edit(VALUE)
** edit(VALUE,EDITOR)
sqlite3_int64 i, j;
if( hasCRNL ){
/* If the original contains \r\n then do no conversions back to \n */
- j = sz;
}else{
/* If the file did not originally contain \r\n then convert any new
** \r\n back into \n */
if( z==0 ){
utf8_printf(out,"%s",p->nullValue);
}else{
- int i;
- int nSep = strlen30(p->colSeparator);
+ unsigned i;
for(i=0; z[i]; i++){
- if( needCsvQuote[((unsigned char*)z)[i]]
- || (z[i]==p->colSeparator[0] &&
- (nSep==1 || memcmp(z, p->colSeparator, nSep)==0)) ){
+ if( needCsvQuote[((unsigned char*)z)[i]] ){
i = 0;
break;
}
}
- if( i==0 ){
+ if( i==0 || strstr(z, p->colSeparator)!=0 ){
char *zQuoted = sqlite3_mprintf("\"%w\"", z);
utf8_printf(out, "%s", zQuoted);
sqlite3_free(zQuoted);
#endif
#ifndef SQLITE_OMIT_AUTHORIZATION
+/*
+** This authorizer runs in safe mode.
+*/
+static int safeModeAuth(
+ void *pClientData,
+ int op,
+ const char *zA1,
+ const char *zA2,
+ const char *zA3,
+ const char *zA4
+){
+ ShellState *p = (ShellState*)pClientData;
+ static const char *azProhibitedFunctions[] = {
+ "edit",
+ "fts3_tokenizer",
+ "load_extension",
+ "readfile",
+ "writefile",
+ "zipfile",
+ "zipfile_cds",
+ };
+ UNUSED_PARAMETER(zA2);
+ UNUSED_PARAMETER(zA3);
+ UNUSED_PARAMETER(zA4);
+ switch( op ){
+ case SQLITE_ATTACH: {
+ failIfSafeMode(p, "cannot run ATTACH in safe mode");
+ break;
+ }
+ case SQLITE_FUNCTION: {
+ int i;
+ for(i=0; i<ArraySize(azProhibitedFunctions); i++){
+ if( sqlite3_stricmp(zA1, azProhibitedFunctions[i])==0 ){
+ failIfSafeMode(p, "cannot use the %s() function in safe mode",
+ azProhibitedFunctions[i]);
+ }
+ }
+ break;
+ }
+ }
+ return SQLITE_OK;
+}
+
/*
** When the ".auth ON" is set, the following authorizer callback is
** invoked. It always returns SQLITE_OK.
}
}
raw_printf(p->out, "\n");
+ if( p->bSafeMode ) (void)safeModeAuth(pClientData, op, zA1, zA2, zA3, zA4);
return SQLITE_OK;
}
#endif
}
/*
-** Allocate space and save off current error string.
+** Allocate space and save off string indicating current error.
*/
static char *save_err_msg(
- sqlite3 *db /* Database to query */
+ sqlite3 *db, /* Database to query */
+ const char *zWhen, /* Qualifier (format) wrapper */
+ int rc /* Error code returned from API */
){
- int nErrMsg = 1+strlen30(sqlite3_errmsg(db));
- char *zErrMsg = sqlite3_malloc64(nErrMsg);
- if( zErrMsg ){
- memcpy(zErrMsg, sqlite3_errmsg(db), nErrMsg);
- }
- return zErrMsg;
+ if( zWhen==0 )
+ zWhen = "%s (%d)";
+ return sqlite3_mprintf(zWhen, sqlite3_errmsg(db), rc);
}
#ifdef __linux__
z = (const char*)sqlite3_column_text(pStmt,i);
azData[nRow*nColumn + i] = z ? strdup(z) : 0;
}
- }while( (rc = sqlite3_step(pStmt))==SQLITE_ROW );
+ }while( sqlite3_step(pStmt)==SQLITE_ROW );
if( nColumn>p->nWidth ){
- p->colWidth = realloc(p->colWidth, nColumn*2*sizeof(int));
+ p->colWidth = realloc(p->colWidth, (nColumn+1)*2*sizeof(int));
if( p->colWidth==0 ) shell_out_of_memory();
for(i=p->nWidth; i<nColumn; i++) p->colWidth[i] = 0;
p->nWidth = nColumn;
int nCol = sqlite3_column_count(pStmt);
void *pData = sqlite3_malloc64(3*nCol*sizeof(const char*) + 1);
if( !pData ){
- rc = SQLITE_NOMEM;
+ shell_out_of_memory();
}else{
char **azCols = (char **)pData; /* Names of result columns */
char **azVals = &azCols[nCol]; /* Results */
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover);
if( SQLITE_OK != rc ){
if( pzErrMsg ){
- *pzErrMsg = save_err_msg(db);
+ *pzErrMsg = save_err_msg(db, "in prepare, %s (%d)", rc);
}
}else{
if( !pStmt ){
zSql = zLeftover;
while( IsSpace(zSql[0]) ) zSql++;
}else if( pzErrMsg ){
- *pzErrMsg = save_err_msg(db);
+ *pzErrMsg = save_err_msg(db, "stepping, %s (%d)", rc);
}
/* clear saved stmt handle */
" -c, --create Create a new archive",
" -u, --update Add or update files with changed mtime",
" -i, --insert Like -u but always add even if unchanged",
+ " -r, --remove Remove files from archive",
" -t, --list List contents of archive",
" -x, --extract Extract files from archive",
" Optional arguments:",
" -f FILE, --file FILE Use archive FILE (default is current db)",
" -a FILE, --append FILE Open FILE using the apndvfs VFS",
" -C DIR, --directory DIR Read/extract files from directory DIR",
+ " -g, --glob Use glob matching for names in archive",
" -n, --dryrun Show the SQL that would have occurred",
" Examples:",
" .ar -cf ARCHIVE foo bar # Create ARCHIVE from files foo and bar",
".changes on|off Show number of rows changed by SQL",
".check GLOB Fail if output since .testcase does not match",
".clone NEWDB Clone data into NEWDB from the existing database",
+ ".connection [close] [#] Open or close an auxiliary database connection",
".databases List names and files of attached databases",
".dbconfig ?op? ?val? List or change sqlite3_db_config() options",
".dbinfo ?DB? Show status information about the database",
" table ASCII-art table",
" tabs Tab-separated values",
" tcl TCL list elements",
+ ".nonce STRING Disable safe mode for one command if the nonce matches",
".nullvalue STRING Use STRING in place of NULL values",
".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE",
" If FILE begins with '|' then open as a pipe",
** Close all OpenSession objects and release all associated resources.
*/
#if defined(SQLITE_ENABLE_SESSION)
-static void session_close_all(ShellState *p){
- int i;
- for(i=0; i<p->nSession; i++){
- session_close(&p->aSession[i]);
+static void session_close_all(ShellState *p, int i){
+ int j;
+ struct AuxDb *pAuxDb = i<0 ? p->pAuxDb : &p->aAuxDb[i];
+ for(j=0; j<pAuxDb->nSession; j++){
+ session_close(&pAuxDb->aSession[j]);
}
- p->nSession = 0;
+ pAuxDb->nSession = 0;
}
#else
-# define session_close_all(X)
+# define session_close_all(X,Y)
#endif
/*
#ifndef SQLITE_OMIT_DESERIALIZE
/*
** Reconstruct an in-memory database using the output from the "dbtotxt"
-** program. Read content from the file in p->zDbFilename. If p->zDbFilename
-** is 0, then read from standard input.
+** program. Read content from the file in p->aAuxDb[].zDbFilename.
+** If p->aAuxDb[].zDbFilename is 0, then read from standard input.
*/
static unsigned char *readHexDb(ShellState *p, int *pnData){
unsigned char *a = 0;
int j, k;
int rc;
FILE *in;
+ const char *zDbFilename = p->pAuxDb->zDbFilename;
unsigned int x[16];
char zLine[1000];
- if( p->zDbFilename ){
- in = fopen(p->zDbFilename, "r");
+ if( zDbFilename ){
+ in = fopen(zDbFilename, "r");
if( in==0 ){
- utf8_printf(stderr, "cannot open \"%s\" for reading\n", p->zDbFilename);
+ utf8_printf(stderr, "cannot open \"%s\" for reading\n", zDbFilename);
return 0;
}
nLine = 0;
&x[8], &x[9], &x[10], &x[11], &x[12], &x[13], &x[14], &x[15]);
if( rc==17 ){
k = iOffset+j;
- if( k+16<=n ){
+ if( k+16<=n && k>=0 ){
int ii;
for(ii=0; ii<16; ii++) a[k+ii] = x[ii]&0xff;
}
*/
static void open_db(ShellState *p, int openFlags){
if( p->db==0 ){
+ const char *zDbFilename = p->pAuxDb->zDbFilename;
if( p->openMode==SHELL_OPEN_UNSPEC ){
- if( p->zDbFilename==0 || p->zDbFilename[0]==0 ){
+ if( zDbFilename==0 || zDbFilename[0]==0 ){
p->openMode = SHELL_OPEN_NORMAL;
}else{
- p->openMode = (u8)deduceDatabaseType(p->zDbFilename,
+ p->openMode = (u8)deduceDatabaseType(zDbFilename,
(openFlags & OPEN_DB_ZIPFILE)!=0);
}
}
switch( p->openMode ){
case SHELL_OPEN_APPENDVFS: {
- sqlite3_open_v2(p->zDbFilename, &p->db,
+ sqlite3_open_v2(zDbFilename, &p->db,
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, "apndvfs");
break;
}
break;
}
case SHELL_OPEN_READONLY: {
- sqlite3_open_v2(p->zDbFilename, &p->db,
+ sqlite3_open_v2(zDbFilename, &p->db,
SQLITE_OPEN_READONLY|p->openFlags, 0);
break;
}
case SHELL_OPEN_UNSPEC:
case SHELL_OPEN_NORMAL: {
- sqlite3_open_v2(p->zDbFilename, &p->db,
+ sqlite3_open_v2(zDbFilename, &p->db,
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, 0);
break;
}
globalDb = p->db;
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n",
- p->zDbFilename, sqlite3_errmsg(p->db));
+ zDbFilename, sqlite3_errmsg(p->db));
if( openFlags & OPEN_DB_KEEPALIVE ){
sqlite3_open(":memory:", &p->db);
return;
#endif
if( p->openMode==SHELL_OPEN_ZIPFILE ){
char *zSql = sqlite3_mprintf(
- "CREATE VIRTUAL TABLE zip USING zipfile(%Q);", p->zDbFilename);
+ "CREATE VIRTUAL TABLE zip USING zipfile(%Q);", zDbFilename);
sqlite3_exec(p->db, zSql, 0, 0, 0);
sqlite3_free(zSql);
}
int nData = 0;
unsigned char *aData;
if( p->openMode==SHELL_OPEN_DESERIALIZE ){
- aData = (unsigned char*)readFile(p->zDbFilename, &nData);
+ aData = (unsigned char*)readFile(zDbFilename, &nData);
}else{
aData = readHexDb(p, &nData);
if( aData==0 ){
}
#endif
}
+ if( p->bSafeModePersist && p->db!=0 ){
+ sqlite3_set_authorizer(p->db, safeModeAuth, p);
+ }
}
/*
zQuery);
goto end_schema_xfer;
}
- while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){
+ while( sqlite3_step(pQuery)==SQLITE_ROW ){
zName = sqlite3_column_text(pQuery, 0);
zSql = sqlite3_column_text(pQuery, 1);
printf("%s... ", zName); fflush(stdout);
p->zTempFile = sqlite3_mprintf("%z.%s", p->zTempFile, zSuffix);
}
if( p->zTempFile==0 ){
- raw_printf(stderr, "out of memory\n");
- exit(1);
+ shell_out_of_memory();
}
}
u8 bZip; /* True if the archive is a ZIP */
u8 bDryRun; /* True if --dry-run */
u8 bAppend; /* True if --append */
+ u8 bGlob; /* True if --glob */
u8 fromCmdLine; /* Run from -A instead of .archive */
int nArg; /* Number of command arguments */
char *zSrcTable; /* "sqlar", "zipfile($file)" or "zip" */
#define AR_CMD_EXTRACT 4
#define AR_CMD_LIST 5
#define AR_CMD_HELP 6
+#define AR_CMD_REMOVE 7
/*
** Other (non-command) switches.
*/
-#define AR_SWITCH_VERBOSE 7
-#define AR_SWITCH_FILE 8
-#define AR_SWITCH_DIRECTORY 9
-#define AR_SWITCH_APPEND 10
-#define AR_SWITCH_DRYRUN 11
+#define AR_SWITCH_VERBOSE 8
+#define AR_SWITCH_FILE 9
+#define AR_SWITCH_DIRECTORY 10
+#define AR_SWITCH_APPEND 11
+#define AR_SWITCH_DRYRUN 12
+#define AR_SWITCH_GLOB 13
static int arProcessSwitch(ArCommand *pAr, int eSwitch, const char *zArg){
switch( eSwitch ){
case AR_CMD_CREATE:
case AR_CMD_EXTRACT:
case AR_CMD_LIST:
+ case AR_CMD_REMOVE:
case AR_CMD_UPDATE:
case AR_CMD_INSERT:
case AR_CMD_HELP:
case AR_SWITCH_DRYRUN:
pAr->bDryRun = 1;
break;
+ case AR_SWITCH_GLOB:
+ pAr->bGlob = 1;
+ break;
case AR_SWITCH_VERBOSE:
pAr->bVerbose = 1;
break;
{ "extract", 'x', AR_CMD_EXTRACT, 0 },
{ "insert", 'i', AR_CMD_INSERT, 0 },
{ "list", 't', AR_CMD_LIST, 0 },
+ { "remove", 'r', AR_CMD_REMOVE, 0 },
{ "update", 'u', AR_CMD_UPDATE, 0 },
{ "help", 'h', AR_CMD_HELP, 0 },
{ "verbose", 'v', AR_SWITCH_VERBOSE, 0 },
{ "append", 'a', AR_SWITCH_APPEND, 1 },
{ "directory", 'C', AR_SWITCH_DIRECTORY, 1 },
{ "dryrun", 'n', AR_SWITCH_DRYRUN, 0 },
+ { "glob", 'g', AR_SWITCH_GLOB, 0 },
};
int nSwitch = sizeof(aSwitch) / sizeof(struct ArSwitch);
struct ArSwitch *pEnd = &aSwitch[nSwitch];
/*
** This function assumes that all arguments within the ArCommand.azArg[]
-** array refer to archive members, as for the --extract or --list commands.
-** It checks that each of them are present. If any specified file is not
-** present in the archive, an error is printed to stderr and an error
-** code returned. Otherwise, if all specified arguments are present in
-** the archive, SQLITE_OK is returned.
+** array refer to archive members, as for the --extract, --list or --remove
+** commands. It checks that each of them are "present". If any specified
+** file is not present in the archive, an error is printed to stderr and an
+** error code returned. Otherwise, if all specified arguments are present
+** in the archive, SQLITE_OK is returned. Here, "present" means either an
+** exact equality when pAr->bGlob is false or a "name GLOB pattern" match
+** when pAr->bGlob is true.
**
** This function strips any trailing '/' characters from each argument.
** This is consistent with the way the [tar] command seems to work on
if( pAr->nArg ){
int i, j;
sqlite3_stmt *pTest = 0;
+ const char *zSel = (pAr->bGlob)
+ ? "SELECT name FROM %s WHERE glob($name,name)"
+ : "SELECT name FROM %s WHERE name=$name";
- shellPreparePrintf(pAr->db, &rc, &pTest,
- "SELECT name FROM %s WHERE name=$name",
- pAr->zSrcTable
- );
+ shellPreparePrintf(pAr->db, &rc, &pTest, zSel, pAr->zSrcTable);
j = sqlite3_bind_parameter_index(pTest, "$name");
for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){
char *z = pAr->azArg[i];
** identify all archive members that match the command arguments held
** in (*pAr). Leave this WHERE clause in (*pzWhere) before returning.
** The caller is responsible for eventually calling sqlite3_free() on
-** any non-NULL (*pzWhere) value.
+** any non-NULL (*pzWhere) value. Here, "match" means strict equality
+** when pAr->bGlob is false and GLOB match when pAr->bGlob is true.
*/
static void arWhereClause(
int *pRc,
- ArCommand *pAr,
+ ArCommand *pAr,
char **pzWhere /* OUT: New WHERE clause */
){
char *zWhere = 0;
+ const char *zSameOp = (pAr->bGlob)? "GLOB" : "=";
if( *pRc==SQLITE_OK ){
if( pAr->nArg==0 ){
zWhere = sqlite3_mprintf("1");
for(i=0; i<pAr->nArg; i++){
const char *z = pAr->azArg[i];
zWhere = sqlite3_mprintf(
- "%z%s name = '%q' OR substr(name,1,%d) = '%q/'",
- zWhere, zSep, z, strlen30(z)+1, z
+ "%z%s name %s '%q' OR substr(name,1,%d) %s '%q/'",
+ zWhere, zSep, zSameOp, z, strlen30(z)+1, zSameOp, z
);
if( zWhere==0 ){
*pRc = SQLITE_NOMEM;
}
+/*
+** Implementation of .ar "Remove" command.
+*/
+static int arRemoveCommand(ArCommand *pAr){
+ int rc = 0;
+ char *zSql = 0;
+ char *zWhere = 0;
+
+ if( pAr->nArg ){
+ /* Verify that args actually exist within the archive before proceeding.
+ ** And formulate a WHERE clause to match them. */
+ rc = arCheckEntries(pAr);
+ arWhereClause(&rc, pAr, &zWhere);
+ }
+ if( rc==SQLITE_OK ){
+ zSql = sqlite3_mprintf("DELETE FROM %s WHERE %s;",
+ pAr->zSrcTable, zWhere);
+ if( pAr->bDryRun ){
+ utf8_printf(pAr->p->out, "%s\n", zSql);
+ }else{
+ char *zErr = 0;
+ rc = sqlite3_exec(pAr->db, "SAVEPOINT ar;", 0, 0, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_exec(pAr->db, zSql, 0, 0, &zErr);
+ if( rc!=SQLITE_OK ){
+ sqlite3_exec(pAr->db, "ROLLBACK TO ar; RELEASE ar;", 0, 0, 0);
+ }else{
+ rc = sqlite3_exec(pAr->db, "RELEASE ar;", 0, 0, 0);
+ }
+ }
+ if( zErr ){
+ utf8_printf(stdout, "ERROR: %s\n", zErr);
+ sqlite3_free(zErr);
+ }
+ }
+ }
+ sqlite3_free(zWhere);
+ sqlite3_free(zSql);
+ return rc;
+}
+
/*
** Implementation of .ar "eXtract" command.
*/
int flags;
if( cmd.bAppend ) eDbType = SHELL_OPEN_APPENDVFS;
if( cmd.eCmd==AR_CMD_CREATE || cmd.eCmd==AR_CMD_INSERT
- || cmd.eCmd==AR_CMD_UPDATE ){
+ || cmd.eCmd==AR_CMD_REMOVE || cmd.eCmd==AR_CMD_UPDATE ){
flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
}else{
flags = SQLITE_OPEN_READONLY;
rc = arCreateOrUpdateCommand(&cmd, 1, 0);
break;
+ case AR_CMD_REMOVE:
+ rc = arRemoveCommand(&cmd);
+ break;
+
default:
assert( cmd.eCmd==AR_CMD_UPDATE );
rc = arCreateOrUpdateCommand(&cmd, 1, 1);
}
#endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */
-
/*
** If an input line begins with "." then invoke this routine to
** process that line.
open_db(p, 0);
if( booleanValue(azArg[1]) ){
sqlite3_set_authorizer(p->db, shellAuth, p);
+ }else if( p->bSafeModePersist ){
+ sqlite3_set_authorizer(p->db, safeModeAuth, p);
}else{
sqlite3_set_authorizer(p->db, 0, 0);
}
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
if( c=='a' && strncmp(azArg[0], "archive", n)==0 ){
open_db(p, 0);
+ failIfSafeMode(p, "cannot run .archive in safe mode");
rc = arDotCommand(p, 0, azArg, nArg);
}else
#endif
int j;
int bAsync = 0;
const char *zVfs = 0;
+ failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]);
for(j=1; j<nArg; j++){
const char *z = azArg[j];
if( z[0]=='-' ){
}
}else
+ /* The undocumented ".breakpoint" command causes a call to the no-op
+ ** routine named test_breakpoint().
+ */
+ if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){
+ test_breakpoint();
+ }else
+
if( c=='c' && strcmp(azArg[0],"cd")==0 ){
+ failIfSafeMode(p, "cannot run .cd in safe mode");
if( nArg==2 ){
#if defined(_WIN32) || defined(WIN32)
wchar_t *z = sqlite3_win32_utf8_to_unicode(azArg[1]);
}
}else
- /* The undocumented ".breakpoint" command causes a call to the no-op
- ** routine named test_breakpoint().
- */
- if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){
- test_breakpoint();
- }else
-
if( c=='c' && n>=3 && strncmp(azArg[0], "changes", n)==0 ){
if( nArg==2 ){
setOrClearFlag(p, SHFLG_CountChanges, azArg[1]);
}else
if( c=='c' && strncmp(azArg[0], "clone", n)==0 ){
+ failIfSafeMode(p, "cannot run .clone in safe mode");
if( nArg==2 ){
tryToClone(p, azArg[1]);
}else{
}
}else
+ if( c=='c' && strncmp(azArg[0], "connection", n)==0 ){
+ if( nArg==1 ){
+ /* List available connections */
+ int i;
+ for(i=0; i<ArraySize(p->aAuxDb); i++){
+ const char *zFile = p->aAuxDb[i].zDbFilename;
+ if( p->aAuxDb[i].db==0 && p->pAuxDb!=&p->aAuxDb[i] ){
+ zFile = "(not open)";
+ }else if( zFile==0 ){
+ zFile = "(memory)";
+ }else if( zFile[0]==0 ){
+ zFile = "(temporary-file)";
+ }
+ if( p->pAuxDb == &p->aAuxDb[i] ){
+ utf8_printf(stdout, "ACTIVE %d: %s\n", i, zFile);
+ }else if( p->aAuxDb[i].db!=0 ){
+ utf8_printf(stdout, " %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 && i<ArraySize(p->aAuxDb) ){
+ p->pAuxDb->db = p->db;
+ p->pAuxDb = &p->aAuxDb[i];
+ globalDb = p->db = p->pAuxDb->db;
+ p->pAuxDb->db = 0;
+ }
+ }else if( nArg==3 && strcmp(azArg[1], "close")==0
+ && IsDigit(azArg[2][0]) && azArg[2][1]==0 ){
+ int i = azArg[2][0] - '0';
+ if( i<0 || i>=ArraySize(p->aAuxDb) ){
+ /* No-op */
+ }else if( p->pAuxDb == &p->aAuxDb[i] ){
+ raw_printf(stderr, "cannot close the active database connection\n");
+ rc = 1;
+ }else if( p->aAuxDb[i].db ){
+ session_close_all(p, i);
+ close_db(p->aAuxDb[i].db);
+ p->aAuxDb[i].db = 0;
+ }
+ }else{
+ raw_printf(stderr, "Usage: .connection [close] [CONNECTION-NUMBER]\n");
+ rc = 1;
+ }
+ }else
+
if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 ){
char **azName = 0;
int nName = 0;
" 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 rowid",
+ "ORDER BY x",
callback, &data, 0
);
if( rc==SQLITE_OK ){
raw_printf(p->out, "/* No STAT tables available */\n");
}else{
raw_printf(p->out, "ANALYZE sqlite_schema;\n");
- sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_schema'",
- callback, &data, 0);
data.cMode = data.mode = MODE_Insert;
data.zDestTable = "sqlite_stat1";
shell_exec(&data, "SELECT * FROM sqlite_stat1", 0);
int nSkip = 0; /* Initial lines to skip */
int useOutputMode = 1; /* Use output mode to determine separators */
+ failIfSafeMode(p, "cannot run .import in safe mode");
memset(&sCtx, 0, sizeof(sCtx));
+ sCtx.z = sqlite3_malloc64(120);
+ if( sCtx.z==0 ){
+ import_cleanup(&sCtx);
+ shell_out_of_memory();
+ }
if( p->mode==MODE_Ascii ){
xRead = ascii_read_one_field;
}else{
if( sCtx.in==0 ){
utf8_printf(stderr, "Error: cannot open \"%s\"\n", zFile);
rc = 1;
+ import_cleanup(&sCtx);
goto meta_command_exit;
}
if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
i = 0;
- while( sqlite3_step(pStmt)==SQLITE_ROW ){
+ while( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
char zLabel[20];
const char *zCol = (const char*)sqlite3_column_text(pStmt,2);
i++;
if( c=='l' && strncmp(azArg[0], "load", n)==0 ){
const char *zFile, *zProc;
char *zErrMsg = 0;
+ failIfSafeMode(p, "cannot run .load in safe mode");
if( nArg<2 ){
raw_printf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n");
rc = 1;
#endif
if( c=='l' && strncmp(azArg[0], "log", n)==0 ){
+ failIfSafeMode(p, "cannot run .log in safe mode");
if( nArg!=2 ){
raw_printf(stderr, "Usage: .log FILENAME\n");
rc = 1;
p->cMode = p->mode;
}else
+ if( c=='n' && strcmp(azArg[0], "nonce")==0 ){
+ if( nArg!=2 ){
+ raw_printf(stderr, "Usage: .nonce NONCE\n");
+ rc = 1;
+ }else if( p->zNonce==0 || strcmp(azArg[1],p->zNonce)!=0 ){
+ raw_printf(stderr, "line %d: incorrect nonce: \"%s\"\n", p->lineno, azArg[1]);
+ exit(1);
+ }else{
+ p->bSafeMode = 0;
+ return 0; /* Return immediately to bypass the safe mode reset
+ ** at the end of this procedure */
+ }
+ }else
+
if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){
if( nArg==2 ){
sqlite3_snprintf(sizeof(p->nullValue), p->nullValue,
int iName = 1; /* Index in azArg[] of the filename */
int newFlag = 0; /* True to delete file before opening */
/* Close the existing database */
- session_close_all(p);
+ session_close_all(p, -1);
close_db(p->db);
p->db = 0;
- p->zDbFilename = 0;
- sqlite3_free(p->zFreeOnClose);
- p->zFreeOnClose = 0;
+ p->pAuxDb->zDbFilename = 0;
+ sqlite3_free(p->pAuxDb->zFreeOnClose);
+ p->pAuxDb->zFreeOnClose = 0;
p->openMode = SHELL_OPEN_UNSPEC;
p->openFlags = 0;
p->szMax = 0;
}
/* If a filename is specified, try to open it first */
if( zNewFilename || p->openMode==SHELL_OPEN_HEXDB ){
- if( newFlag ) shellDeleteFile(zNewFilename);
- p->zDbFilename = zNewFilename;
+ if( newFlag && !p->bSafeMode ) shellDeleteFile(zNewFilename);
+ if( p->bSafeMode
+ && p->openMode!=SHELL_OPEN_HEXDB
+ && zNewFilename
+ && strcmp(zNewFilename,":memory:")!=0
+ ){
+ failIfSafeMode(p, "cannot open disk-based database files in safe mode");
+ }
+ p->pAuxDb->zDbFilename = zNewFilename;
open_db(p, OPEN_DB_KEEPALIVE);
if( p->db==0 ){
utf8_printf(stderr, "Error: cannot open '%s'\n", zNewFilename);
sqlite3_free(zNewFilename);
}else{
- p->zFreeOnClose = zNewFilename;
+ p->pAuxDb->zFreeOnClose = zNewFilename;
}
}
if( p->db==0 ){
/* As a fall-back open a TEMP database */
- p->zDbFilename = 0;
+ p->pAuxDb->zDbFilename = 0;
open_db(p, 0);
}
}else
int bBOM = 0;
int bOnce = 0; /* 0: .output, 1: .once, 2: .excel */
+ failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]);
if( c=='e' ){
eMode = 'x';
bOnce = 2;
rx = sqlite3_prepare_v2(p->db,
"SELECT key, quote(value) "
"FROM temp.sqlite_parameters;", -1, &pStmt, 0);
- while( sqlite3_step(pStmt)==SQLITE_ROW ){
+ while( rx==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
utf8_printf(p->out, "%-*s %s\n", len, sqlite3_column_text(pStmt,0),
sqlite3_column_text(pStmt,1));
}
if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 ){
FILE *inSaved = p->in;
int savedLineno = p->lineno;
+ failIfSafeMode(p, "cannot run .read in safe mode");
if( nArg!=2 ){
raw_printf(stderr, "Usage: .read FILE\n");
rc = 1;
pclose(p->in);
}
#endif
- }else if( notNormalFile(azArg[1]) || (p->in = fopen(azArg[1], "rb"))==0 ){
+ }else if( (p->in = openChrSource(azArg[1]))==0 ){
utf8_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
rc = 1;
}else{
sqlite3_backup *pBackup;
int nTimeout = 0;
+ failIfSafeMode(p, "cannot run .restore in safe mode");
if( nArg==2 ){
zSrcFile = azArg[1];
zDb = "main";
#if defined(SQLITE_ENABLE_SESSION)
if( c=='s' && strncmp(azArg[0],"session",n)==0 && n>=3 ){
- OpenSession *pSession = &p->aSession[0];
+ struct AuxDb *pAuxDb = p->pAuxDb;
+ OpenSession *pSession = &pAuxDb->aSession[0];
char **azCmd = &azArg[1];
int iSes = 0;
int nCmd = nArg - 1;
if( nArg<=1 ) goto session_syntax_error;
open_db(p, 0);
if( nArg>=3 ){
- for(iSes=0; iSes<p->nSession; iSes++){
- if( strcmp(p->aSession[iSes].zName, azArg[1])==0 ) break;
+ for(iSes=0; iSes<pAuxDb->nSession; iSes++){
+ if( strcmp(pAuxDb->aSession[iSes].zName, azArg[1])==0 ) break;
}
- if( iSes<p->nSession ){
- pSession = &p->aSession[iSes];
+ if( iSes<pAuxDb->nSession ){
+ pSession = &pAuxDb->aSession[iSes];
azCmd++;
nCmd--;
}else{
- pSession = &p->aSession[0];
+ pSession = &pAuxDb->aSession[0];
iSes = 0;
}
}
*/
if( strcmp(azCmd[0],"changeset")==0 || strcmp(azCmd[0],"patchset")==0 ){
FILE *out = 0;
+ failIfSafeMode(p, "cannot run \".session %s\" in safe mode", azCmd[0]);
if( nCmd!=2 ) goto session_syntax_error;
if( pSession->p==0 ) goto session_not_open;
out = fopen(azCmd[1], "wb");
*/
if( strcmp(azCmd[0], "close")==0 ){
if( nCmd!=1 ) goto session_syntax_error;
- if( p->nSession ){
+ if( pAuxDb->nSession ){
session_close(pSession);
- p->aSession[iSes] = p->aSession[--p->nSession];
+ pAuxDb->aSession[iSes] = pAuxDb->aSession[--pAuxDb->nSession];
}
}else
int ii;
if( nCmd>2 ) goto session_syntax_error;
ii = nCmd==1 ? -1 : booleanValue(azCmd[1]);
- if( p->nSession ){
+ if( pAuxDb->nSession ){
ii = sqlite3session_enable(pSession->p, ii);
utf8_printf(p->out, "session %s enable flag = %d\n",
pSession->zName, ii);
if( strcmp(azCmd[0], "filter")==0 ){
int ii, nByte;
if( nCmd<2 ) goto session_syntax_error;
- if( p->nSession ){
+ if( pAuxDb->nSession ){
for(ii=0; ii<pSession->nFilter; ii++){
sqlite3_free(pSession->azFilter[ii]);
}
int ii;
if( nCmd>2 ) goto session_syntax_error;
ii = nCmd==1 ? -1 : booleanValue(azCmd[1]);
- if( p->nSession ){
+ if( pAuxDb->nSession ){
ii = sqlite3session_indirect(pSession->p, ii);
utf8_printf(p->out, "session %s indirect flag = %d\n",
pSession->zName, ii);
if( strcmp(azCmd[0], "isempty")==0 ){
int ii;
if( nCmd!=1 ) goto session_syntax_error;
- if( p->nSession ){
+ if( pAuxDb->nSession ){
ii = sqlite3session_isempty(pSession->p);
utf8_printf(p->out, "session %s isempty flag = %d\n",
pSession->zName, ii);
** List all currently open sessions
*/
if( strcmp(azCmd[0],"list")==0 ){
- for(i=0; i<p->nSession; i++){
- utf8_printf(p->out, "%d %s\n", i, p->aSession[i].zName);
+ for(i=0; i<pAuxDb->nSession; i++){
+ utf8_printf(p->out, "%d %s\n", i, pAuxDb->aSession[i].zName);
}
}else
if( nCmd!=3 ) goto session_syntax_error;
zName = azCmd[2];
if( zName[0]==0 ) goto session_syntax_error;
- for(i=0; i<p->nSession; i++){
- if( strcmp(p->aSession[i].zName,zName)==0 ){
+ for(i=0; i<pAuxDb->nSession; i++){
+ if( strcmp(pAuxDb->aSession[i].zName,zName)==0 ){
utf8_printf(stderr, "Session \"%s\" already exists\n", zName);
goto meta_command_exit;
}
}
- if( p->nSession>=ArraySize(p->aSession) ){
- raw_printf(stderr, "Maximum of %d sessions\n", ArraySize(p->aSession));
+ if( pAuxDb->nSession>=ArraySize(pAuxDb->aSession) ){
+ raw_printf(stderr, "Maximum of %d sessions\n", ArraySize(pAuxDb->aSession));
goto meta_command_exit;
}
- pSession = &p->aSession[p->nSession];
+ pSession = &pAuxDb->aSession[pAuxDb->nSession];
rc = sqlite3session_create(p->db, azCmd[1], &pSession->p);
if( rc ){
raw_printf(stderr, "Cannot open session: error code=%d\n", rc);
}
pSession->nFilter = 0;
sqlite3session_table_filter(pSession->p, session_filter, pSession);
- p->nSession++;
+ pAuxDb->nSession++;
pSession->zName = sqlite3_mprintf("%s", zName);
}else
/* If no command name matches, show a syntax error */
){
char *zCmd;
int i, x;
+ failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]);
if( nArg<2 ){
raw_printf(stderr, "Usage: .system COMMAND\n");
rc = 1;
}
raw_printf(p->out, "\n");
utf8_printf(p->out, "%12.12s: %s\n", "filename",
- p->zDbFilename ? p->zDbFilename : "");
+ p->pAuxDb->zDbFilename ? p->pAuxDb->zDbFilename : "");
}else
if( c=='s' && strncmp(azArg[0], "stats", n)==0 ){
}
}
rc = sqlite3_finalize(pStmt);
- appendText(&s, " ORDER BY 1", 0);
- rc = sqlite3_prepare_v2(p->db, s.z, -1, &pStmt, 0);
+ if( rc==SQLITE_OK ){
+ appendText(&s, " ORDER BY 1", 0);
+ rc = sqlite3_prepare_v2(p->db, s.z, -1, &pStmt, 0);
+ }
freeText(&s);
if( rc ) return shellDatabaseError(p->db);
{ "prng_save", SQLITE_TESTCTRL_PRNG_SAVE, "" },
{ "prng_seed", SQLITE_TESTCTRL_PRNG_SEED, "SEED ?db?" },
{ "seek_count", SQLITE_TESTCTRL_SEEK_COUNT, "" },
+ { "sorter_mmap", SQLITE_TESTCTRL_SORTER_MMAP, "NMAX" },
{ "tune", SQLITE_TESTCTRL_TUNE, "ID VALUE" },
};
int testctrl = -1;
break;
}
#endif
+ case SQLITE_TESTCTRL_SORTER_MMAP:
+ if( nArg==3 ){
+ int opt = (unsigned int)integerValue(azArg[2]);
+ rc2 = sqlite3_test_control(testctrl, p->db, opt);
+ isOk = 3;
+ }
+ break;
}
}
if( isOk==0 && iCtrl>=0 ){
int j;
assert( nArg<=ArraySize(azArg) );
p->nWidth = nArg-1;
- p->colWidth = realloc(p->colWidth, p->nWidth*sizeof(int)*2);
+ 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; j<nArg; j++){
p->outCount--;
if( p->outCount==0 ) output_reset(p);
}
+ p->bSafeMode = p->bSafeModePersist;
return rc;
}
-/*
-** Return TRUE if a semicolon occurs anywhere in the first N characters
-** of string z[].
+/* Line scan result and intermediate states (supporting scan resumption)
*/
-static int line_contains_semicolon(const char *z, int N){
- int i;
- for(i=0; i<N; i++){ if( z[i]==';' ) return 1; }
- return 0;
-}
-
-/*
-** Test to see if a line consists entirely of whitespace.
-*/
-static int _all_whitespace(const char *z){
- for(; *z; z++){
- if( IsSpace(z[0]) ) continue;
- if( *z=='/' && z[1]=='*' ){
- z += 2;
- while( *z && (*z!='*' || z[1]!='/') ){ z++; }
- if( *z==0 ) return 0;
- z++;
- continue;
+#ifndef CHAR_BIT
+# define CHAR_BIT 8
+#endif
+typedef enum {
+ QSS_HasDark = 1<<CHAR_BIT, QSS_EndingSemi = 2<<CHAR_BIT,
+ QSS_CharMask = (1<<CHAR_BIT)-1, QSS_ScanMask = 3<<CHAR_BIT,
+ QSS_Start = 0
+} QuickScanState;
+#define QSS_SETV(qss, newst) ((newst) | ((qss) & QSS_ScanMask))
+#define QSS_INPLAIN(qss) (((qss)&QSS_CharMask)==QSS_Start)
+#define QSS_PLAINWHITE(qss) (((qss)&~QSS_EndingSemi)==QSS_Start)
+#define QSS_PLAINDARK(qss) (((qss)&~QSS_EndingSemi)==QSS_HasDark)
+#define QSS_SEMITERM(qss) (((qss)&~QSS_HasDark)==QSS_EndingSemi)
+
+/*
+** Scan line for classification to guide shell's handling.
+** The scan is resumable for subsequent lines when prior
+** return values are passed as the 2nd argument.
+*/
+static QuickScanState quickscan(char *zLine, QuickScanState qss){
+ char cin;
+ char cWait = (char)qss; /* intentional narrowing loss */
+ if( cWait==0 ){
+ PlainScan:
+ assert( cWait==0 );
+ while( (cin = *zLine++)!=0 ){
+ if( IsSpace(cin) )
+ continue;
+ switch (cin){
+ case '-':
+ if( *zLine!='-' )
+ break;
+ while((cin = *++zLine)!=0 )
+ if( cin=='\n')
+ goto PlainScan;
+ return qss;
+ case ';':
+ qss |= QSS_EndingSemi;
+ continue;
+ case '/':
+ if( *zLine=='*' ){
+ ++zLine;
+ cWait = '*';
+ qss = QSS_SETV(qss, cWait);
+ goto TermScan;
+ }
+ break;
+ case '[':
+ cin = ']';
+ /* fall thru */
+ case '`': case '\'': case '"':
+ cWait = cin;
+ qss = QSS_HasDark | cWait;
+ goto TermScan;
+ default:
+ break;
+ }
+ qss = (qss & ~QSS_EndingSemi) | QSS_HasDark;
}
- if( *z=='-' && z[1]=='-' ){
- z += 2;
- while( *z && *z!='\n' ){ z++; }
- if( *z==0 ) return 1;
- continue;
+ }else{
+ TermScan:
+ while( (cin = *zLine++)!=0 ){
+ if( cin==cWait ){
+ switch( cWait ){
+ case '*':
+ if( *zLine != '/' )
+ continue;
+ ++zLine;
+ cWait = 0;
+ qss = QSS_SETV(qss, 0);
+ goto PlainScan;
+ case '`': case '\'': case '"':
+ if(*zLine==cWait){
+ ++zLine;
+ continue;
+ }
+ /* fall thru */
+ case ']':
+ cWait = 0;
+ qss = QSS_SETV(qss, 0);
+ goto PlainScan;
+ default: assert(0);
+ }
+ }
}
- return 0;
}
- return 1;
+ return qss;
}
/*
** than a semi-colon. The SQL Server style "go" command is understood
** as is the Oracle "/".
*/
-static int line_is_command_terminator(const char *zLine){
+static int line_is_command_terminator(char *zLine){
while( IsSpace(zLine[0]) ){ zLine++; };
- if( zLine[0]=='/' && _all_whitespace(&zLine[1]) ){
- return 1; /* Oracle */
- }
- if( ToLower(zLine[0])=='g' && ToLower(zLine[1])=='o'
- && _all_whitespace(&zLine[2]) ){
- return 1; /* SQL Server */
- }
- return 0;
+ if( zLine[0]=='/' )
+ zLine += 1; /* Oracle */
+ else if ( ToLower(zLine[0])=='g' && ToLower(zLine[1])=='o' )
+ zLine += 2; /* SQL Server */
+ else
+ return 0;
+ return quickscan(zLine,QSS_Start)==QSS_Start;
}
/*
}
return 1;
}else if( ShellHasFlag(p, SHFLG_CountChanges) ){
- raw_printf(p->out, "changes: %3d total_changes: %d\n",
- sqlite3_changes(p->db), sqlite3_total_changes(p->db));
+ 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);
}
return 0;
}
int nLine; /* Length of current line */
int nSql = 0; /* Bytes of zSql[] used */
int nAlloc = 0; /* Allocated zSql[] space */
- int nSqlPrior = 0; /* Bytes of zSql[] used by prior line */
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) */
p->lineno = 0;
while( errCnt==0 || !bail_on_error || (p->in==0 && stdin_is_interactive) ){
seenInterrupt = 0;
}
p->lineno++;
- if( nSql==0 && _all_whitespace(zLine) ){
- if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zLine);
+ if( QSS_INPLAIN(qss)
+ && line_is_command_terminator(zLine)
+ && line_is_complete(zSql, nSql) ){
+ memcpy(zLine,";",2);
+ }
+ qss = quickscan(zLine, qss);
+ if( QSS_PLAINWHITE(qss) && nSql==0 ){
+ if( ShellHasFlag(p, SHFLG_Echo) )
+ printf("%s\n", zLine);
+ /* Just swallow single-line whitespace */
+ qss = QSS_Start;
continue;
}
if( zLine && (zLine[0]=='.' || zLine[0]=='#') && nSql==0 ){
errCnt++;
}
}
+ qss = QSS_Start;
continue;
}
- if( line_is_command_terminator(zLine) && line_is_complete(zSql, nSql) ){
- memcpy(zLine,";",2);
- }
+ /* No single-line dispositions remain; accumulate line(s). */
nLine = strlen30(zLine);
if( nSql+nLine+2>=nAlloc ){
- nAlloc = nSql+nLine+100;
+ /* Grow buffer by half-again increments when big. */
+ nAlloc = nSql+(nSql>>1)+nLine+100;
zSql = realloc(zSql, nAlloc);
if( zSql==0 ) shell_out_of_memory();
}
- nSqlPrior = nSql;
if( nSql==0 ){
int i;
for(i=0; zLine[i] && IsSpace(zLine[i]); i++){}
memcpy(zSql+nSql, zLine, nLine+1);
nSql += nLine;
}
- if( nSql && line_contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior)
- && sqlite3_complete(zSql) ){
+ if( nSql && QSS_SEMITERM(qss) && sqlite3_complete(zSql) ){
errCnt += runOneSqlLine(p, zSql, p->in, startline);
nSql = 0;
if( p->outCount ){
}else{
clearTempFile(p);
}
- }else if( nSql && _all_whitespace(zSql) ){
+ p->bSafeMode = p->bSafeModePersist;
+ qss = QSS_Start;
+ }else if( nSql && QSS_PLAINWHITE(qss) ){
if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zSql);
nSql = 0;
+ qss = QSS_Start;
}
}
- if( nSql && !_all_whitespace(zSql) ){
+ if( nSql && QSS_PLAINDARK(qss) ){
errCnt += runOneSqlLine(p, zSql, p->in, startline);
}
free(zSql);
#endif
" -newline SEP set output row separator. Default: '\\n'\n"
" -nofollow refuse to open symbolic links to database files\n"
+ " -nonce STRING set the safe-mode escape nonce\n"
" -nullvalue TEXT set text string for NULL values. Default ''\n"
" -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n"
" -quote set output mode to 'quote'\n"
" -readonly open the database read-only\n"
+ " -safe enable safe-mode\n"
" -separator SEP set output column separator. Default: '|'\n"
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
" -sorterref SIZE sorter references threshold size\n"
memset(data, 0, sizeof(*data));
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;
** this compile-time option to embed this shell program in larger
** applications. */
extern void SQLITE_SHELL_DBNAME_PROC(const char**);
- SQLITE_SHELL_DBNAME_PROC(&data.zDbFilename);
+ SQLITE_SHELL_DBNAME_PROC(&data.pAuxDb->zDbFilename);
warnInmemoryDb = 0;
}
#endif
char *z;
z = argv[i];
if( z[0]!='-' ){
- if( data.zDbFilename==0 ){
- data.zDbFilename = z;
+ if( data.aAuxDb->zDbFilename==0 ){
+ data.aAuxDb->zDbFilename = z;
}else{
/* Excesss arguments are interpreted as SQL (or dot-commands) and
** mean that nothing is read from stdin */
if( n<0 ) n = 0;
sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, n);
if( sz*n==0 ) data.shellFlgs &= ~SHFLG_Lookaside;
+ }else if( strcmp(z,"-threadsafe")==0 ){
+ int n;
+ n = (int)integerValue(cmdline_option_value(argc,argv,++i));
+ switch( n ){
+ case 0: sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); break;
+ case 2: sqlite3_config(SQLITE_CONFIG_MULTITHREAD); break;
+ default: sqlite3_config(SQLITE_CONFIG_SERIALIZED); break;
+ }
#ifdef SQLITE_ENABLE_VFSTRACE
}else if( strcmp(z,"-vfstrace")==0 ){
extern int vfstrace_register(
sqlite3MemTraceActivate(stderr);
}else if( strcmp(z,"-bail")==0 ){
bail_on_error = 1;
+ }else if( strcmp(z,"-nonce")==0 ){
+ free(data.zNonce);
+ data.zNonce = strdup(argv[++i]);
+ }else if( strcmp(z,"-safe")==0 ){
+ /* no-op - catch this on the second pass */
}
}
verify_uninitialized();
}
}
- if( data.zDbFilename==0 ){
+ if( data.pAuxDb->zDbFilename==0 ){
#ifndef SQLITE_OMIT_MEMORYDB
- data.zDbFilename = ":memory:";
+ data.pAuxDb->zDbFilename = ":memory:";
warnInmemoryDb = argc==1;
#else
utf8_printf(stderr,"%s: Error: no database filename specified\n", Argv0);
** files from being created if a user mistypes the database name argument
** to the sqlite command-line tool.
*/
- if( access(data.zDbFilename, 0)==0 ){
+ if( access(data.pAuxDb->zDbFilename, 0)==0 ){
open_db(&data, 0);
}
"%s",cmdline_option_value(argc,argv,++i));
}else if( strcmp(z,"-header")==0 ){
data.showHeader = 1;
- }else if( strcmp(z,"-noheader")==0 ){
+ ShellSetFlag(&data, SHFLG_HeaderSet);
+ }else if( strcmp(z,"-noheader")==0 ){
data.showHeader = 0;
+ ShellSetFlag(&data, SHFLG_HeaderSet);
}else if( strcmp(z,"-echo")==0 ){
ShellSetFlag(&data, SHFLG_Echo);
}else if( strcmp(z,"-eqp")==0 ){
i+=2;
}else if( strcmp(z,"-lookaside")==0 ){
i+=2;
+ }else if( strcmp(z,"-threadsafe")==0 ){
+ i+=2;
+ }else if( strcmp(z,"-nonce")==0 ){
+ i += 2;
}else if( strcmp(z,"-mmap")==0 ){
i++;
}else if( strcmp(z,"-memtrace")==0 ){
readStdin = 0;
break;
#endif
+ }else if( strcmp(z,"-safe")==0 ){
+ data.bSafeMode = data.bSafeModePersist = 1;
}else{
utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
raw_printf(stderr,"Use -help for a list of options.\n");
free(azCmd);
set_table_name(&data, 0);
if( data.db ){
- session_close_all(&data);
+ session_close_all(&data, -1);
close_db(data.db);
}
- sqlite3_free(data.zFreeOnClose);
+ for(i=0; i<ArraySize(data.aAuxDb); i++){
+ sqlite3_free(data.aAuxDb[i].zFreeOnClose);
+ if( data.aAuxDb[i].db ){
+ session_close_all(&data, i);
+ close_db(data.aAuxDb[i].db);
+ }
+ }
find_home_dir(1);
output_reset(&data);
data.doXdgOpen = 0;
free(argvToFree);
#endif
free(data.colWidth);
+ free(data.zNonce);
/* Clear the global data structure so that valgrind will detect memory
** leaks */
memset(&data, 0, sizeof(data));