# include <sys/types.h>
#endif
-#ifdef HAVE_EDITLINE
-# include <editline/editline.h>
-#endif
-#if defined(HAVE_READLINE) && HAVE_READLINE==1
+#if defined(HAVE_READLINE) && HAVE_READLINE!=0
# include <readline/readline.h>
# include <readline/history.h>
+#else
+# undef HAVE_READLINE
+#endif
+#if defined(HAVE_EDITLINE) && !defined(HAVE_READLINE)
+# define HAVE_READLINE 1
+# include <editline/readline.h>
#endif
-#if !defined(HAVE_EDITLINE) && (!defined(HAVE_READLINE) || HAVE_READLINE!=1)
+#if !defined(HAVE_READLINE)
# define add_history(X)
# define read_history(X)
# define write_history(X)
#if defined(_WIN32) || defined(WIN32)
# include <io.h>
#define isatty(h) _isatty(h)
-#define access(f,m) _access((f),(m))
+#ifndef access
+# define access(f,m) _access((f),(m))
+#endif
#undef popen
#define popen _popen
#undef pclose
zResult = local_getline(zPrior, in);
}else{
zPrompt = isContinuation ? continuePrompt : mainPrompt;
-#if defined(HAVE_READLINE) && HAVE_READLINE==1
+#if defined(HAVE_READLINE)
free(zPrior);
zResult = readline(zPrompt);
if( zResult && *zResult ) add_history(zResult);
struct callback_data {
sqlite3 *db; /* The database */
int echoOn; /* True to echo input commands */
+ int autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */
int statsOn; /* True to display memory stats before each finalize */
+ int outCount; /* Revert to stdout when reaching zero */
int cnt; /* Number of records displayed so far */
FILE *out; /* Write results here */
FILE *traceOut; /* Output for sqlite3_trace() */
*/
static void output_html_string(FILE *out, const char *z){
int i;
+ if( z==0 ) z = "";
while( *z ){
for(i=0; z[i]
&& z[i]!='<'
*/
static void interrupt_handler(int NotUsed){
UNUSED_PARAMETER(NotUsed);
- seenInterrupt = 1;
+ seenInterrupt++;
+ if( seenInterrupt>2 ) exit(1);
if( db ) sqlite3_interrupt(db);
}
#endif
}else if( aiType && aiType[i]==SQLITE_TEXT ){
if( zSep[0] ) fprintf(p->out,"%s",zSep);
output_quoted_string(p->out, azArg[i]);
- }else if( aiType && (aiType[i]==SQLITE_INTEGER || aiType[i]==SQLITE_FLOAT) ){
+ }else if( aiType && (aiType[i]==SQLITE_INTEGER
+ || aiType[i]==SQLITE_FLOAT) ){
fprintf(p->out,"%s%s",zSep, azArg[i]);
}else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){
const void *pBlob = sqlite3_column_blob(p->pStmt, i);
int nResult;
int i;
const char *z;
- rc = sqlite3_prepare(p->db, zSelect, -1, &pSelect, 0);
+ rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0);
if( rc!=SQLITE_OK || !pSelect ){
fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db));
if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++;
**
** * For each "Goto", if the jump destination is earlier in the program
** and ends on one of:
-** Yield SeekGt SeekLt RowSetRead
+** Yield SeekGt SeekLt RowSetRead Rewind
+** or if the P1 parameter is one instead of zero,
** then indent all opcodes between the earlier instruction
** and "Goto" by 2 spaces.
*/
int nAlloc = 0; /* Allocated size of p->aiIndent[], abYield */
int iOp; /* Index of operation in p->aiIndent[] */
- const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext", 0 };
- const char *azYield[] = { "Yield", "SeekLt", "SeekGt", "RowSetRead", 0 };
+ const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext",
+ "NextIfOpen", "PrevIfOpen", 0 };
+ const char *azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead", "Rewind", 0 };
const char *azGoto[] = { "Goto", 0 };
/* Try to figure out if this is really an EXPLAIN statement. If this
if( str_in_array(zOp, azNext) ){
for(i=p2op; i<iOp; i++) p->aiIndent[i] += 2;
}
- if( str_in_array(zOp, azGoto) && p2op<p->nIndent && abYield[p2op] ){
- for(i=p2op; i<iOp; i++) p->aiIndent[i] += 2;
+ if( str_in_array(zOp, azGoto) && p2op<p->nIndent
+ && (abYield[p2op] || sqlite3_column_int(pSql, 2))
+ ){
+ for(i=p2op+1; i<iOp; i++) p->aiIndent[i] += 2;
}
}
fprintf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql);
}
+ /* Show the EXPLAIN QUERY PLAN if .eqp is on */
+ if( pArg && pArg->autoEQP ){
+ sqlite3_stmt *pExplain;
+ char *zEQP = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", sqlite3_sql(pStmt));
+ rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0);
+ if( rc==SQLITE_OK ){
+ while( sqlite3_step(pExplain)==SQLITE_ROW ){
+ fprintf(pArg->out,"--EQP-- %d,", sqlite3_column_int(pExplain, 0));
+ fprintf(pArg->out,"%d,", sqlite3_column_int(pExplain, 1));
+ fprintf(pArg->out,"%d,", sqlite3_column_int(pExplain, 2));
+ fprintf(pArg->out,"%s\n", sqlite3_column_text(pExplain, 3));
+ }
+ }
+ sqlite3_finalize(pExplain);
+ sqlite3_free(zEQP);
+ }
+
/* Output TESTCTRL_EXPLAIN text of requested */
if( pArg && pArg->mode==MODE_Explain ){
const char *zExplain = 0;
zTableInfo = appendText(zTableInfo, zTable, '"');
zTableInfo = appendText(zTableInfo, ");", 0);
- rc = sqlite3_prepare(p->db, zTableInfo, -1, &pTableInfo, 0);
+ rc = sqlite3_prepare_v2(p->db, zTableInfo, -1, &pTableInfo, 0);
free(zTableInfo);
if( rc!=SQLITE_OK || !pTableInfo ){
return 1;
*/
static char zHelp[] =
".backup ?DB? FILE Backup DB (default \"main\") to FILE\n"
- ".bail ON|OFF Stop after hitting an error. Default OFF\n"
+ ".bail on|off Stop after hitting an error. Default OFF\n"
+ ".clone NEWDB Clone data into NEWDB from the existing database\n"
".databases List names and files of attached databases\n"
".dump ?TABLE? ... Dump the database in an SQL text format\n"
" If TABLE specified, only dump tables matching\n"
" LIKE pattern TABLE.\n"
- ".echo ON|OFF Turn command echo on or off\n"
+ ".echo on|off Turn command echo on or off\n"
".exit Exit this program\n"
- ".explain ?ON|OFF? Turn output mode suitable for EXPLAIN on or off.\n"
+ ".explain ?on|off? Turn output mode suitable for EXPLAIN on or off.\n"
" With no args, it turns EXPLAIN on.\n"
- ".header(s) ON|OFF Turn display of headers on or off\n"
+ ".headers on|off Turn display of headers on or off\n"
".help Show this message\n"
".import FILE TABLE Import data from FILE into TABLE\n"
".indices ?TABLE? Show names of all indices\n"
" tabs Tab-separated values\n"
" tcl TCL list elements\n"
".nullvalue STRING Use STRING in place of NULL values\n"
+ ".once FILENAME Output for the next SQL command only to FILENAME\n"
".open ?FILENAME? Close existing database and reopen FILENAME\n"
- ".output FILENAME Send output to FILENAME\n"
- ".output stdout Send output to the screen\n"
+ ".output ?FILENAME? Send output to FILENAME or stdout\n"
".print STRING... Print literal STRING\n"
".prompt MAIN CONTINUE Replace the standard prompts\n"
".quit Exit this program\n"
".read FILENAME Execute SQL in FILENAME\n"
".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n"
+ ".save FILE Write in-memory database into FILE\n"
".schema ?TABLE? Show the CREATE statements\n"
" If TABLE specified, only show tables matching\n"
" LIKE pattern TABLE.\n"
".separator STRING Change separator used by output mode and .import\n"
+ ".shell CMD ARGS... Run CMD ARGS... in a system shell\n"
".show Show the current values for various settings\n"
- ".stats ON|OFF Turn stats on or off\n"
+ ".stats on|off Turn stats on or off\n"
+ ".system CMD ARGS... Run CMD ARGS... in a system shell\n"
".tables ?TABLE? List names of tables\n"
" If TABLE specified, only list tables matching\n"
" LIKE pattern TABLE.\n"
".timeout MS Try opening locked tables for MS milliseconds\n"
+ ".timer on|off Turn SQL timer on or off\n"
".trace FILE|off Output each SQL statement as it is run\n"
".vfsname ?AUX? Print the name of the VFS stack\n"
".width NUM1 NUM2 ... Set column widths for \"column\" mode\n"
-;
-
-static char zTimerHelp[] =
- ".timer ON|OFF Turn the CPU timer measurement on or off\n"
+ " Negative values right-justify\n"
;
/* Forward reference */
static void resolve_backslashes(char *z){
int i, j;
char c;
+ while( *z && *z!='\\' ) z++;
for(i=j=0; (c = z[i])!=0; i++, j++){
if( c=='\\' ){
c = z[++i];
}
z[j] = c;
}
- z[j] = 0;
+ if( j<i ) z[j] = 0;
}
/*
** + Report syntax errors on stderr
*/
static char *csv_read_one_field(CSVReader *p){
- int c, pc;
+ int c, pc, ppc;
int cSep = p->cSeparator;
p->n = 0;
c = fgetc(p->in);
if( c=='"' ){
int startLine = p->nLine;
int cQuote = c;
- pc = 0;
+ pc = ppc = 0;
while( 1 ){
c = fgetc(p->in);
if( c=='\n' ) p->nLine++;
}
if( (c==cSep && pc==cQuote)
|| (c=='\n' && pc==cQuote)
- || (c=='\n' && pc=='\r' && p->n>=2 && p->z[p->n-2]==cQuote)
+ || (c=='\n' && pc=='\r' && ppc==cQuote)
|| (c==EOF && pc==cQuote)
){
do{ p->n--; }while( p->z[p->n]!=cQuote );
break;
}
csv_append_char(p, c);
+ ppc = pc;
pc = c;
}
}else{
}
if( c=='\n' ){
p->nLine++;
- if( p->n>1 && p->z[p->n-1]=='\r' ) p->n--;
+ if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--;
}
p->cTerm = c;
}
return p->z;
}
+/*
+** Try to transfer data for table zTable. If an error is seen while
+** moving forward, try to go backwards. The backwards movement won't
+** work for WITHOUT ROWID tables.
+*/
+static void tryToCloneData(
+ struct callback_data *p,
+ sqlite3 *newDb,
+ const char *zTable
+){
+ sqlite3_stmt *pQuery = 0;
+ sqlite3_stmt *pInsert = 0;
+ char *zQuery = 0;
+ char *zInsert = 0;
+ int rc;
+ int i, j, n;
+ int nTable = (int)strlen(zTable);
+ int k = 0;
+ int cnt = 0;
+ const int spinRate = 10000;
+
+ zQuery = sqlite3_mprintf("SELECT * FROM \"%w\"", zTable);
+ rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
+ if( rc ){
+ fprintf(stderr, "Error %d: %s on [%s]\n",
+ sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db),
+ zQuery);
+ goto end_data_xfer;
+ }
+ n = sqlite3_column_count(pQuery);
+ zInsert = sqlite3_malloc(200 + nTable + n*3);
+ if( zInsert==0 ){
+ fprintf(stderr, "out of memory\n");
+ goto end_data_xfer;
+ }
+ sqlite3_snprintf(200+nTable,zInsert,
+ "INSERT OR IGNORE INTO \"%s\" VALUES(?", zTable);
+ i = (int)strlen(zInsert);
+ for(j=1; j<n; j++){
+ memcpy(zInsert+i, ",?", 2);
+ i += 2;
+ }
+ memcpy(zInsert+i, ");", 3);
+ rc = sqlite3_prepare_v2(newDb, zInsert, -1, &pInsert, 0);
+ if( rc ){
+ fprintf(stderr, "Error %d: %s on [%s]\n",
+ sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb),
+ zQuery);
+ goto end_data_xfer;
+ }
+ for(k=0; k<2; k++){
+ while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){
+ for(i=0; i<n; i++){
+ switch( sqlite3_column_type(pQuery, i) ){
+ case SQLITE_NULL: {
+ sqlite3_bind_null(pInsert, i+1);
+ break;
+ }
+ case SQLITE_INTEGER: {
+ sqlite3_bind_int64(pInsert, i+1, sqlite3_column_int64(pQuery,i));
+ break;
+ }
+ case SQLITE_FLOAT: {
+ sqlite3_bind_double(pInsert, i+1, sqlite3_column_double(pQuery,i));
+ break;
+ }
+ case SQLITE_TEXT: {
+ sqlite3_bind_text(pInsert, i+1,
+ (const char*)sqlite3_column_text(pQuery,i),
+ -1, SQLITE_STATIC);
+ break;
+ }
+ case SQLITE_BLOB: {
+ sqlite3_bind_blob(pInsert, i+1, sqlite3_column_blob(pQuery,i),
+ sqlite3_column_bytes(pQuery,i),
+ SQLITE_STATIC);
+ break;
+ }
+ }
+ } /* End for */
+ rc = sqlite3_step(pInsert);
+ if( rc!=SQLITE_OK && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){
+ fprintf(stderr, "Error %d: %s\n", sqlite3_extended_errcode(newDb),
+ sqlite3_errmsg(newDb));
+ }
+ sqlite3_reset(pInsert);
+ cnt++;
+ if( (cnt%spinRate)==0 ){
+ printf("%c\b", "|/-\\"[(cnt/spinRate)%4]);
+ fflush(stdout);
+ }
+ } /* End while */
+ if( rc==SQLITE_DONE ) break;
+ sqlite3_finalize(pQuery);
+ sqlite3_free(zQuery);
+ zQuery = sqlite3_mprintf("SELECT * FROM \"%w\" ORDER BY rowid DESC;",
+ zTable);
+ rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
+ if( rc ){
+ fprintf(stderr, "Warning: cannot step \"%s\" backwards", zTable);
+ break;
+ }
+ } /* End for(k=0...) */
+
+end_data_xfer:
+ sqlite3_finalize(pQuery);
+ sqlite3_finalize(pInsert);
+ sqlite3_free(zQuery);
+ sqlite3_free(zInsert);
+}
+
+
+/*
+** Try to transfer all rows of the schema that match zWhere. For
+** each row, invoke xForEach() on the object defined by that row.
+** If an error is encountered while moving forward through the
+** sqlite_master table, try again moving backwards.
+*/
+static void tryToCloneSchema(
+ struct callback_data *p,
+ sqlite3 *newDb,
+ const char *zWhere,
+ void (*xForEach)(struct callback_data*,sqlite3*,const char*)
+){
+ sqlite3_stmt *pQuery = 0;
+ char *zQuery = 0;
+ int rc;
+ const unsigned char *zName;
+ const unsigned char *zSql;
+ char *zErrMsg = 0;
+
+ zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_master"
+ " WHERE %s", zWhere);
+ rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
+ if( rc ){
+ fprintf(stderr, "Error: (%d) %s on [%s]\n",
+ sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db),
+ zQuery);
+ goto end_schema_xfer;
+ }
+ while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){
+ zName = sqlite3_column_text(pQuery, 0);
+ zSql = sqlite3_column_text(pQuery, 1);
+ printf("%s... ", zName); fflush(stdout);
+ sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg);
+ if( zErrMsg ){
+ fprintf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql);
+ sqlite3_free(zErrMsg);
+ zErrMsg = 0;
+ }
+ if( xForEach ){
+ xForEach(p, newDb, (const char*)zName);
+ }
+ printf("done\n");
+ }
+ if( rc!=SQLITE_DONE ){
+ sqlite3_finalize(pQuery);
+ sqlite3_free(zQuery);
+ zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_master"
+ " WHERE %s ORDER BY rowid DESC", zWhere);
+ rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
+ if( rc ){
+ fprintf(stderr, "Error: (%d) %s on [%s]\n",
+ sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db),
+ zQuery);
+ goto end_schema_xfer;
+ }
+ while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){
+ zName = sqlite3_column_text(pQuery, 0);
+ zSql = sqlite3_column_text(pQuery, 1);
+ printf("%s... ", zName); fflush(stdout);
+ sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg);
+ if( zErrMsg ){
+ fprintf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql);
+ sqlite3_free(zErrMsg);
+ zErrMsg = 0;
+ }
+ if( xForEach ){
+ xForEach(p, newDb, (const char*)zName);
+ }
+ printf("done\n");
+ }
+ }
+end_schema_xfer:
+ sqlite3_finalize(pQuery);
+ sqlite3_free(zQuery);
+}
+
+/*
+** Open a new database file named "zNewDb". Try to recover as much information
+** as possible out of the main database (which might be corrupt) and write it
+** into zNewDb.
+*/
+static void tryToClone(struct callback_data *p, const char *zNewDb){
+ int rc;
+ sqlite3 *newDb = 0;
+ if( access(zNewDb,0)==0 ){
+ fprintf(stderr, "File \"%s\" already exists.\n", zNewDb);
+ return;
+ }
+ rc = sqlite3_open(zNewDb, &newDb);
+ if( rc ){
+ fprintf(stderr, "Cannot create output database: %s\n",
+ sqlite3_errmsg(newDb));
+ }else{
+ sqlite3_exec(p->db, "PRAGMA writable_schema=ON;", 0, 0, 0);
+ sqlite3_exec(newDb, "BEGIN EXCLUSIVE;", 0, 0, 0);
+ tryToCloneSchema(p, newDb, "type='table'", tryToCloneData);
+ tryToCloneSchema(p, newDb, "type!='table'", 0);
+ sqlite3_exec(newDb, "COMMIT;", 0, 0, 0);
+ sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0);
+ }
+ sqlite3_close(newDb);
+}
+
+/*
+** Change the output file back to stdout
+*/
+static void output_reset(struct callback_data *p){
+ if( p->outfile[0]=='|' ){
+ pclose(p->out);
+ }else{
+ output_file_close(p->out);
+ }
+ p->outfile[0] = 0;
+ p->out = stdout;
+}
+
/*
** If an input line begins with "." then invoke this routine to
** process that line.
if( nArg==0 ) return 0; /* no tokens, no error */
n = strlen30(azArg[0]);
c = azArg[0][0];
- if( c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0 ){
+ if( (c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0)
+ || (c=='s' && n>=3 && strncmp(azArg[0], "save", n)==0)
+ ){
const char *zDestFile = 0;
const char *zDb = 0;
sqlite3 *pDest;
sqlite3_close(pDest);
}else
- if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 && nArg>1 && nArg<3 ){
- bail_on_error = booleanValue(azArg[1]);
+ if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 ){
+ if( nArg==2 ){
+ bail_on_error = booleanValue(azArg[1]);
+ }else{
+ fprintf(stderr, "Usage: .bail on|off\n");
+ rc = 1;
+ }
}else
/* The undocumented ".breakpoint" command causes a call to the no-op
test_breakpoint();
}else
- if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 && nArg==1 ){
+ if( c=='c' && strncmp(azArg[0], "clone", n)==0 ){
+ if( nArg==2 ){
+ tryToClone(p, azArg[1]);
+ }else{
+ fprintf(stderr, "Usage: .clone FILENAME\n");
+ rc = 1;
+ }
+ }else
+
+ if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 ){
struct callback_data data;
char *zErrMsg = 0;
open_db(p, 0);
}
}else
- if( c=='d' && strncmp(azArg[0], "dump", n)==0 && nArg<3 ){
+ if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){
open_db(p, 0);
/* When playing back a "dump", the content might appear in an order
** which causes immediate foreign key constraints to be violated.
** So disable foreign-key constraint enforcement to prevent problems. */
+ if( nArg!=1 && nArg!=2 ){
+ fprintf(stderr, "Usage: .dump ?LIKE-PATTERN?\n");
+ rc = 1;
+ goto meta_command_exit;
+ }
fprintf(p->out, "PRAGMA foreign_keys=OFF;\n");
fprintf(p->out, "BEGIN TRANSACTION;\n");
p->writableSchema = 0;
fprintf(p->out, p->nErr ? "ROLLBACK; -- due to errors\n" : "COMMIT;\n");
}else
- if( c=='e' && strncmp(azArg[0], "echo", n)==0 && nArg>1 && nArg<3 ){
- p->echoOn = booleanValue(azArg[1]);
+ if( c=='e' && strncmp(azArg[0], "echo", n)==0 ){
+ if( nArg==2 ){
+ p->echoOn = booleanValue(azArg[1]);
+ }else{
+ fprintf(stderr, "Usage: .echo on|off\n");
+ rc = 1;
+ }
+ }else
+
+ if( c=='e' && strncmp(azArg[0], "eqp", n)==0 ){
+ if( nArg==2 ){
+ p->autoEQP = booleanValue(azArg[1]);
+ }else{
+ fprintf(stderr, "Usage: .eqp on|off\n");
+ rc = 1;
+ }
}else
if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){
rc = 2;
}else
- if( c=='e' && strncmp(azArg[0], "explain", n)==0 && nArg<3 ){
+ if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){
int val = nArg>=2 ? booleanValue(azArg[1]) : 1;
if(val == 1) {
if(!p->explainPrev.valid) {
}
}else
- if( c=='h' && (strncmp(azArg[0], "header", n)==0 ||
- strncmp(azArg[0], "headers", n)==0) && nArg>1 && nArg<3 ){
- p->showHeader = booleanValue(azArg[1]);
+ if( c=='h' && strncmp(azArg[0], "headers", n)==0 ){
+ if( nArg==2 ){
+ p->showHeader = booleanValue(azArg[1]);
+ }else{
+ fprintf(stderr, "Usage: .headers on|off\n");
+ rc = 1;
+ }
}else
if( c=='h' && strncmp(azArg[0], "help", n)==0 ){
- fprintf(stderr,"%s",zHelp);
- if( HAS_TIMER ){
- fprintf(stderr,"%s",zTimerHelp);
- }
+ fprintf(p->out, "%s", zHelp);
}else
- if( c=='i' && strncmp(azArg[0], "import", n)==0 && nArg==3 ){
- char *zTable = azArg[2]; /* Insert data into this table */
- char *zFile = azArg[1]; /* Name of file to extra content from */
+ if( c=='i' && strncmp(azArg[0], "import", n)==0 ){
+ char *zTable; /* Insert data into this table */
+ char *zFile; /* Name of file to extra content from */
sqlite3_stmt *pStmt = NULL; /* A statement */
int nCol; /* Number of columns in the table */
int nByte; /* Number of bytes in an SQL string */
CSVReader sCsv; /* Reader context */
int (*xCloser)(FILE*); /* Procedure to close th3 connection */
+ if( nArg!=3 ){
+ fprintf(stderr, "Usage: .import FILE TABLE\n");
+ goto meta_command_exit;
+ }
+ zFile = azArg[1];
+ zTable = azArg[2];
seenInterrupt = 0;
memset(&sCsv, 0, sizeof(sCsv));
open_db(p, 0);
return 1;
}
nByte = strlen30(zSql);
- rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0);
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+ csv_append_char(&sCsv, 0); /* To ensure sCsv.z is allocated */
if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(db))==0 ){
char *zCreate = sqlite3_mprintf("CREATE TABLE %s", zTable);
char cSep = '(';
xCloser(sCsv.in);
return 1;
}
- rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0);
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
}
sqlite3_free(zSql);
if( rc ){
}
zSql[j++] = ')';
zSql[j] = 0;
- rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0);
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
if( rc ){
fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db));
if( needCommit ) sqlite3_exec(db, "COMMIT", 0, 0, 0);
}else
- if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg<3 ){
+ if( c=='i' && strncmp(azArg[0], "indices", n)==0 ){
struct callback_data data;
char *zErrMsg = 0;
open_db(p, 0);
"ORDER BY 1",
callback, &data, &zErrMsg
);
- }else{
+ }else if( nArg==2 ){
zShellStatic = azArg[1];
rc = sqlite3_exec(p->db,
"SELECT name FROM sqlite_master "
callback, &data, &zErrMsg
);
zShellStatic = 0;
+ }else{
+ fprintf(stderr, "Usage: .indices ?LIKE-PATTERN?\n");
+ rc = 1;
+ goto meta_command_exit;
}
if( zErrMsg ){
fprintf(stderr,"Error: %s\n", zErrMsg);
#endif
#ifndef SQLITE_OMIT_LOAD_EXTENSION
- if( c=='l' && strncmp(azArg[0], "load", n)==0 && nArg>=2 ){
+ if( c=='l' && strncmp(azArg[0], "load", n)==0 ){
const char *zFile, *zProc;
char *zErrMsg = 0;
+ if( nArg<2 ){
+ fprintf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n");
+ rc = 1;
+ goto meta_command_exit;
+ }
zFile = azArg[1];
zProc = nArg>=3 ? azArg[2] : 0;
open_db(p, 0);
}else
#endif
- if( c=='l' && strncmp(azArg[0], "log", n)==0 && nArg>=2 ){
- const char *zFile = azArg[1];
- output_file_close(p->pLog);
- p->pLog = output_file_open(zFile);
+ if( c=='l' && strncmp(azArg[0], "log", n)==0 ){
+ if( nArg!=2 ){
+ fprintf(stderr, "Usage: .log FILENAME\n");
+ rc = 1;
+ }else{
+ const char *zFile = azArg[1];
+ output_file_close(p->pLog);
+ p->pLog = output_file_open(zFile);
+ }
}else
- if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==2 ){
- int n2 = strlen30(azArg[1]);
- if( (n2==4 && strncmp(azArg[1],"line",n2)==0)
- ||
- (n2==5 && strncmp(azArg[1],"lines",n2)==0) ){
+ if( c=='m' && strncmp(azArg[0], "mode", n)==0 ){
+ const char *zMode = nArg>=2 ? azArg[1] : "";
+ int n2 = (int)strlen(zMode);
+ int c2 = zMode[0];
+ if( c2=='l' && n2>2 && strncmp(azArg[1],"lines",n2)==0 ){
p->mode = MODE_Line;
- }else if( (n2==6 && strncmp(azArg[1],"column",n2)==0)
- ||
- (n2==7 && strncmp(azArg[1],"columns",n2)==0) ){
+ }else if( c2=='c' && strncmp(azArg[1],"columns",n2)==0 ){
p->mode = MODE_Column;
- }else if( n2==4 && strncmp(azArg[1],"list",n2)==0 ){
+ }else if( c2=='l' && n2>2 && strncmp(azArg[1],"list",n2)==0 ){
p->mode = MODE_List;
- }else if( n2==4 && strncmp(azArg[1],"html",n2)==0 ){
+ }else if( c2=='h' && strncmp(azArg[1],"html",n2)==0 ){
p->mode = MODE_Html;
- }else if( n2==3 && strncmp(azArg[1],"tcl",n2)==0 ){
+ }else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){
p->mode = MODE_Tcl;
sqlite3_snprintf(sizeof(p->separator), p->separator, " ");
- }else if( n2==3 && strncmp(azArg[1],"csv",n2)==0 ){
+ }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){
p->mode = MODE_Csv;
sqlite3_snprintf(sizeof(p->separator), p->separator, ",");
- }else if( n2==4 && strncmp(azArg[1],"tabs",n2)==0 ){
+ }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){
p->mode = MODE_List;
sqlite3_snprintf(sizeof(p->separator), p->separator, "\t");
- }else if( n2==6 && strncmp(azArg[1],"insert",n2)==0 ){
+ }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){
p->mode = MODE_Insert;
- set_table_name(p, "table");
+ set_table_name(p, nArg>=3 ? azArg[2] : "table");
}else {
fprintf(stderr,"Error: mode should be one of: "
"column csv html insert line list tabs tcl\n");
}
}else
- if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==3 ){
- int n2 = strlen30(azArg[1]);
- if( n2==6 && strncmp(azArg[1],"insert",n2)==0 ){
- p->mode = MODE_Insert;
- set_table_name(p, azArg[2]);
- }else {
- fprintf(stderr, "Error: invalid arguments: "
- " \"%s\". Enter \".help\" for help\n", azArg[2]);
+ if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){
+ if( nArg==2 ){
+ sqlite3_snprintf(sizeof(p->nullvalue), p->nullvalue,
+ "%.*s", (int)ArraySize(p->nullvalue)-1, azArg[1]);
+ }else{
+ fprintf(stderr, "Usage: .nullvalue STRING\n");
rc = 1;
}
}else
- if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 && nArg==2 ) {
- sqlite3_snprintf(sizeof(p->nullvalue), p->nullvalue,
- "%.*s", (int)ArraySize(p->nullvalue)-1, azArg[1]);
- }else
-
if( c=='o' && strncmp(azArg[0], "open", n)==0 && n>=2 ){
sqlite3 *savedDb = p->db;
const char *zSavedFilename = p->zDbFilename;
}
}else
- if( c=='o' && strncmp(azArg[0], "output", n)==0 && nArg==2 ){
- if( p->outfile[0]=='|' ){
- pclose(p->out);
+ if( c=='o'
+ && (strncmp(azArg[0], "output", n)==0 || strncmp(azArg[0], "once", n)==0)
+ ){
+ const char *zFile = nArg>=2 ? azArg[1] : "stdout";
+ if( nArg>2 ){
+ fprintf(stderr, "Usage: .%s FILE\n", azArg[0]);
+ rc = 1;
+ goto meta_command_exit;
+ }
+ if( n>1 && strncmp(azArg[0], "once", n)==0 ){
+ if( nArg<2 ){
+ fprintf(stderr, "Usage: .once FILE\n");
+ rc = 1;
+ goto meta_command_exit;
+ }
+ p->outCount = 2;
}else{
- output_file_close(p->out);
+ p->outCount = 0;
}
- p->outfile[0] = 0;
- if( azArg[1][0]=='|' ){
- p->out = popen(&azArg[1][1], "w");
+ output_reset(p);
+ if( zFile[0]=='|' ){
+ p->out = popen(zFile + 1, "w");
if( p->out==0 ){
- fprintf(stderr,"Error: cannot open pipe \"%s\"\n", &azArg[1][1]);
+ fprintf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1);
p->out = stdout;
rc = 1;
}else{
- sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]);
+ sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
}
}else{
- p->out = output_file_open(azArg[1]);
+ p->out = output_file_open(zFile);
if( p->out==0 ){
- if( strcmp(azArg[1],"off")!=0 ){
- fprintf(stderr,"Error: cannot write to \"%s\"\n", azArg[1]);
+ if( strcmp(zFile,"off")!=0 ){
+ fprintf(stderr,"Error: cannot write to \"%s\"\n", zFile);
}
p->out = stdout;
rc = 1;
} else {
- sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]);
+ sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
}
}
}else
fprintf(p->out, "\n");
}else
- if( c=='p' && strncmp(azArg[0], "prompt", n)==0 && (nArg==2 || nArg==3)){
+ if( c=='p' && strncmp(azArg[0], "prompt", n)==0 ){
if( nArg >= 2) {
strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1);
}
}
}else
- if( c=='q' && strncmp(azArg[0], "quit", n)==0 && nArg==1 ){
+ if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){
rc = 2;
}else
- if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 && nArg==2 ){
- FILE *alt = fopen(azArg[1], "rb");
+ if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 ){
+ FILE *alt;
+ if( nArg!=2 ){
+ fprintf(stderr, "Usage: .read FILE\n");
+ rc = 1;
+ goto meta_command_exit;
+ }
+ alt = fopen(azArg[1], "rb");
if( alt==0 ){
fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
rc = 1;
}
}else
- if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 && nArg>1 && nArg<4){
+ if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 ){
const char *zSrcFile;
const char *zDb;
sqlite3 *pSrc;
if( nArg==2 ){
zSrcFile = azArg[1];
zDb = "main";
- }else{
+ }else if( nArg==3 ){
zSrcFile = azArg[2];
zDb = azArg[1];
+ }else{
+ fprintf(stderr, "Usage: .restore ?DB? FILE\n");
+ rc = 1;
+ goto meta_command_exit;
}
rc = sqlite3_open(zSrcFile, &pSrc);
if( rc!=SQLITE_OK ){
sqlite3_close(pSrc);
}else
- if( c=='s' && strncmp(azArg[0], "schema", n)==0 && nArg<3 ){
+ if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){
struct callback_data data;
char *zErrMsg = 0;
open_db(p, 0);
memcpy(&data, p, sizeof(data));
data.showHeader = 0;
data.mode = MODE_Semi;
- if( nArg>1 ){
+ if( nArg==2 ){
int i;
for(i=0; azArg[1][i]; i++) azArg[1][i] = ToLower(azArg[1][i]);
if( strcmp(azArg[1],"sqlite_master")==0 ){
callback, &data, &zErrMsg);
zShellStatic = 0;
}
- }else{
+ }else if( nArg==1 ){
rc = sqlite3_exec(p->db,
"SELECT sql FROM "
" (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x"
"ORDER BY rowid",
callback, &data, &zErrMsg
);
+ }else{
+ fprintf(stderr, "Usage: .schema ?LIKE-PATTERN?\n");
+ rc = 1;
+ goto meta_command_exit;
}
if( zErrMsg ){
fprintf(stderr,"Error: %s\n", zErrMsg);
for(i=1; i<nArg; i++){
char zBuf[200];
v = integerValue(azArg[i]);
- sqlite3_snprintf(sizeof(zBuf), zBuf, "%s: %lld 0x%llx\n", azArg[i], v, v);
+ sqlite3_snprintf(sizeof(zBuf),zBuf,"%s: %lld 0x%llx\n", azArg[i],v,v);
fprintf(p->out, "%s", zBuf);
}
}
}else
#endif
- if( c=='s' && strncmp(azArg[0], "separator", n)==0 && nArg==2 ){
- sqlite3_snprintf(sizeof(p->separator), p->separator,
- "%.*s", (int)sizeof(p->separator)-1, azArg[1]);
+ if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){
+ if( nArg==2 ){
+ sqlite3_snprintf(sizeof(p->separator), p->separator,
+ "%.*s", (int)sizeof(p->separator)-1, azArg[1]);
+ }else{
+ fprintf(stderr, "Usage: .separator STRING\n");
+ rc = 1;
+ }
+ }else
+
+ if( c=='s'
+ && (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0)
+ ){
+ char *zCmd;
+ int i;
+ if( nArg<2 ){
+ fprintf(stderr, "Usage: .system COMMAND\n");
+ rc = 1;
+ goto meta_command_exit;
+ }
+ zCmd = sqlite3_mprintf(strchr(azArg[1],' ')==0?"%s":"\"%s\"", azArg[1]);
+ for(i=2; i<nArg; i++){
+ zCmd = sqlite3_mprintf(strchr(azArg[i],' ')==0?"%z %s":"%z \"%s\"",
+ zCmd, azArg[i]);
+ }
+ (void)system(zCmd);
+ sqlite3_free(zCmd);
}else
- if( c=='s' && strncmp(azArg[0], "show", n)==0 && nArg==1 ){
+ if( c=='s' && strncmp(azArg[0], "show", n)==0 ){
int i;
+ if( nArg!=1 ){
+ fprintf(stderr, "Usage: .show\n");
+ rc = 1;
+ goto meta_command_exit;
+ }
fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off");
+ fprintf(p->out,"%9.9s: %s\n","eqp", p->autoEQP ? "on" : "off");
fprintf(p->out,"%9.9s: %s\n","explain", p->explainPrev.valid ? "on" :"off");
fprintf(p->out,"%9.9s: %s\n","headers", p->showHeader ? "on" : "off");
fprintf(p->out,"%9.9s: %s\n","mode", modeDescr[p->mode]);
fprintf(p->out,"\n");
}else
- if( c=='s' && strncmp(azArg[0], "stats", n)==0 && nArg>1 && nArg<3 ){
- p->statsOn = booleanValue(azArg[1]);
+ if( c=='s' && strncmp(azArg[0], "stats", n)==0 ){
+ if( nArg==2 ){
+ p->statsOn = booleanValue(azArg[1]);
+ }else{
+ fprintf(stderr, "Usage: .stats on|off\n");
+ rc = 1;
+ }
}else
- if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 && nArg<3 ){
+ if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 ){
sqlite3_stmt *pStmt;
char **azResult;
int nRow, nAlloc;
{ "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS },
{ "iskeyword", SQLITE_TESTCTRL_ISKEYWORD },
{ "scratchmalloc", SQLITE_TESTCTRL_SCRATCHMALLOC },
+ { "byteorder", SQLITE_TESTCTRL_BYTEORDER },
};
int testctrl = -1;
int rc = 0;
break;
/* sqlite3_test_control(int) */
- case SQLITE_TESTCTRL_PRNG_SAVE:
- case SQLITE_TESTCTRL_PRNG_RESTORE:
+ case SQLITE_TESTCTRL_PRNG_SAVE:
+ case SQLITE_TESTCTRL_PRNG_RESTORE:
case SQLITE_TESTCTRL_PRNG_RESET:
+ case SQLITE_TESTCTRL_BYTEORDER:
if( nArg==2 ){
rc = sqlite3_test_control(testctrl);
fprintf(p->out, "%d (0x%08x)\n", rc, rc);
}
}else
- if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 && nArg==2 ){
+ if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 ){
open_db(p, 0);
- sqlite3_busy_timeout(p->db, (int)integerValue(azArg[1]));
+ sqlite3_busy_timeout(p->db, nArg>=2 ? (int)integerValue(azArg[1]) : 0);
}else
- if( HAS_TIMER && c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0
- && nArg==2
- ){
- enableTimer = booleanValue(azArg[1]);
+ if( c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0 ){
+ if( nArg==2 ){
+ enableTimer = booleanValue(azArg[1]);
+ if( enableTimer && !HAS_TIMER ){
+ fprintf(stderr, "Error: timer not available on this system.\n");
+ enableTimer = 0;
+ }
+ }else{
+ fprintf(stderr, "Usage: .timer on|off\n");
+ rc = 1;
+ }
}else
- if( c=='t' && strncmp(azArg[0], "trace", n)==0 && nArg>1 ){
+ if( c=='t' && strncmp(azArg[0], "trace", n)==0 ){
open_db(p, 0);
output_file_close(p->traceOut);
+ if( nArg!=2 ){
+ fprintf(stderr, "Usage: .trace FILE|off\n");
+ rc = 1;
+ goto meta_command_exit;
+ }
p->traceOut = output_file_open(azArg[1]);
#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT)
if( p->traceOut==0 ){
#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE)
if( c=='w' && strncmp(azArg[0], "wheretrace", n)==0 ){
extern int sqlite3WhereTrace;
- sqlite3WhereTrace = booleanValue(azArg[1]);
+ sqlite3WhereTrace = nArg>=2 ? booleanValue(azArg[1]) : 0xff;
}else
#endif
- if( c=='w' && strncmp(azArg[0], "width", n)==0 && nArg>1 ){
+ if( c=='w' && strncmp(azArg[0], "width", n)==0 ){
int j;
assert( nArg<=ArraySize(azArg) );
for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){
rc = 1;
}
+meta_command_exit:
+ if( p->outCount ){
+ p->outCount--;
+ if( p->outCount==0 ) output_reset(p);
+ }
return rc;
}
seenInterrupt = 0;
}
lineno++;
- if( nSql==0 && _all_whitespace(zLine) ) continue;
+ if( nSql==0 && _all_whitespace(zLine) ){
+ if( p->echoOn ) printf("%s\n", zLine);
+ continue;
+ }
if( zLine && zLine[0]=='.' && nSql==0 ){
if( p->echoOn ) printf("%s\n", zLine);
rc = do_meta_command(zLine, p);
errCnt++;
}
nSql = 0;
+ if( p->outCount ){
+ output_reset(p);
+ p->outCount = 0;
+ }
}else if( nSql && _all_whitespace(zSql) ){
+ if( p->echoOn ) printf("%s\n", zSql);
nSql = 0;
}
}
sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
}
+/*
+** Output text to the console in a font that attracts extra attention.
+*/
+#ifdef _WIN32
+static void printBold(const char *zText){
+ HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
+ CONSOLE_SCREEN_BUFFER_INFO defaultScreenInfo;
+ GetConsoleScreenBufferInfo(out, &defaultScreenInfo);
+ SetConsoleTextAttribute(out,
+ FOREGROUND_RED|FOREGROUND_INTENSITY
+ );
+ printf("%s", zText);
+ SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes);
+}
+#else
+static void printBold(const char *zText){
+ printf("\033[1m%s\033[0m", zText);
+}
+#endif
+
/*
** Get the argument to an --option. Throw an error and die if no argument
** is available.
char *zFirstCmd = 0;
int i;
int rc = 0;
+ int warnInmemoryDb = 0;
+#if USE_SYSTEM_SQLITE+0!=1
if( strcmp(sqlite3_sourceid(),SQLITE_SOURCE_ID)!=0 ){
fprintf(stderr, "SQLite header and source version mismatch\n%s\n%s\n",
sqlite3_sourceid(), SQLITE_SOURCE_ID);
exit(1);
}
+#endif
Argv0 = argv[0];
main_init(&data);
stdin_is_interactive = isatty(0);
if( data.zDbFilename==0 ){
#ifndef SQLITE_OMIT_MEMORYDB
data.zDbFilename = ":memory:";
+ warnInmemoryDb = argc==1;
#else
fprintf(stderr,"%s: Error: no database filename specified\n", Argv0);
return 1;
+#endif
+#ifdef SQLITE_SHELL_DBNAME_PROC
+ { extern void SQLITE_SHELL_DBNAME_PROC(const char**);
+ SQLITE_SHELL_DBNAME_PROC(&data.zDbFilename);
+ warnInmemoryDb = 0; }
#endif
}
data.out = stdout;
data.showHeader = 0;
}else if( strcmp(z,"-echo")==0 ){
data.echoOn = 1;
+ }else if( strcmp(z,"-eqp")==0 ){
+ data.autoEQP = 1;
}else if( strcmp(z,"-stats")==0 ){
data.statsOn = 1;
}else if( strcmp(z,"-bail")==0 ){
int nHistory;
printf(
"SQLite version %s %.19s\n" /*extra-version-info*/
- "Enter \".help\" for instructions\n"
- "Enter SQL statements terminated with a \";\"\n",
+ "Enter \".help\" for usage hints.\n",
sqlite3_libversion(), sqlite3_sourceid()
);
+ if( warnInmemoryDb ){
+ printf("Connected to a ");
+ printBold("transient in-memory database");
+ printf(".\nUse \".open FILENAME\" to reopen on a "
+ "persistent database.\n");
+ }
zHome = find_home_dir();
if( zHome ){
nHistory = strlen30(zHome) + 20;
sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
}
}
-#if defined(HAVE_READLINE) && HAVE_READLINE==1
+#if defined(HAVE_READLINE)
if( zHistory ) read_history(zHistory);
#endif
rc = process_input(&data, 0);