diff options
author | drh <drh@noemail.net> | 2014-07-24 16:54:28 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2014-07-24 16:54:28 +0000 |
commit | 2bd2c29a955a2d459b2b300125eda1d79b070e64 (patch) | |
tree | 3dac35daf7fb3c5a48c4e71900dfcff7b4a95311 /src/shell.c | |
parent | 6614181610e4c40e44484a1c7b14b253d512ad77 (diff) | |
parent | ba5b09319e2f79707bcc55a84a5f059ea0949334 (diff) | |
download | sqlite-2bd2c29a955a2d459b2b300125eda1d79b070e64.tar.gz sqlite-2bd2c29a955a2d459b2b300125eda1d79b070e64.zip |
Merge all recent trunk changes into the threads branch.
FossilOrigin-Name: 770685892c8f09b9cddb2fbb2877cfb291e19425
Diffstat (limited to 'src/shell.c')
-rw-r--r-- | src/shell.c | 159 |
1 files changed, 132 insertions, 27 deletions
diff --git a/src/shell.c b/src/shell.c index 770b4c318..76bb89970 100644 --- a/src/shell.c +++ b/src/shell.c @@ -64,6 +64,7 @@ #if defined(_WIN32) || defined(WIN32) # include <io.h> +# include <fcntl.h> #define isatty(h) _isatty(h) #ifndef access # define access(f,m) _access((f),(m)) @@ -458,6 +459,7 @@ struct callback_data { int showHeader; /* True to show column names in List or Column mode */ char *zDestTable; /* Name of destination table when MODE_Insert */ char separator[20]; /* Separator character for MODE_List */ + char newline[20]; /* Record separator in MODE_Csv */ int colWidth[100]; /* Requested width of each column when in column mode*/ int actualWidth[100]; /* Actual width of each column */ char nullvalue[20]; /* The text to print when a NULL comes back from @@ -659,7 +661,8 @@ static const char needCsvQuote[] = { /* ** Output a single term of CSV. Actually, p->separator is used for ** the separator, which may or may not be a comma. p->nullvalue is -** the null value. Strings are quoted if necessary. +** the null value. Strings are quoted if necessary. The separator +** is only issued if bSep is true. */ static void output_csv(struct callback_data *p, const char *z, int bSep){ FILE *out = p->out; @@ -855,17 +858,26 @@ static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int break; } case MODE_Csv: { +#if defined(WIN32) || defined(_WIN32) + fflush(p->out); + _setmode(_fileno(p->out), _O_BINARY); +#endif if( p->cnt++==0 && p->showHeader ){ for(i=0; i<nArg; i++){ output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1); } - fprintf(p->out,"\n"); + fprintf(p->out,"%s",p->newline); } - if( azArg==0 ) break; - for(i=0; i<nArg; i++){ - output_csv(p, azArg[i], i<nArg-1); + if( azArg>0 ){ + for(i=0; i<nArg; i++){ + output_csv(p, azArg[i], i<nArg-1); + } + fprintf(p->out,"%s",p->newline); } - fprintf(p->out,"\n"); +#if defined(WIN32) || defined(_WIN32) + fflush(p->out); + _setmode(_fileno(p->out), _O_TEXT); +#endif break; } case MODE_Insert: { @@ -1619,7 +1631,8 @@ static char zHelp[] = ".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" + ".separator STRING ?NL? Change separator used by output mode and .import\n" + " NL is the end-of-line mark for CSV\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" @@ -1637,6 +1650,69 @@ static char zHelp[] = /* Forward reference */ static int process_input(struct callback_data *p, FILE *in); +/* +** Implementation of the "readfile(X)" SQL function. The entire content +** of the file named X is read and returned as a BLOB. NULL is returned +** if the file does not exist or is unreadable. +*/ +static void readfileFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + const char *zName; + FILE *in; + long nIn; + void *pBuf; + + zName = (const char*)sqlite3_value_text(argv[0]); + if( zName==0 ) return; + in = fopen(zName, "rb"); + if( in==0 ) return; + fseek(in, 0, SEEK_END); + nIn = ftell(in); + rewind(in); + pBuf = sqlite3_malloc( nIn ); + if( pBuf && 1==fread(pBuf, nIn, 1, in) ){ + sqlite3_result_blob(context, pBuf, nIn, sqlite3_free); + }else{ + sqlite3_free(pBuf); + } + fclose(in); +} + +/* +** Implementation of the "writefile(X,Y)" SQL function. The argument Y +** is written into file X. The number of bytes written is returned. Or +** NULL is returned if something goes wrong, such as being unable to open +** file X for writing. +*/ +static void writefileFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + FILE *out; + const char *z; + int n; + sqlite3_int64 rc; + const char *zFile; + + zFile = (const char*)sqlite3_value_text(argv[0]); + if( zFile==0 ) return; + out = fopen(zFile, "wb"); + if( out==0 ) return; + z = (const char*)sqlite3_value_blob(argv[1]); + if( z==0 ){ + n = 0; + rc = 0; + }else{ + n = sqlite3_value_bytes(argv[1]); + rc = fwrite(z, 1, n, out); + } + fclose(out); + sqlite3_result_int64(context, rc); +} /* ** Make sure the database is open. If it is not, then open it. If @@ -1660,6 +1736,10 @@ static void open_db(struct callback_data *p, int keepAlive){ #ifndef SQLITE_OMIT_LOAD_EXTENSION sqlite3_enable_load_extension(p->db, 1); #endif + sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0, + readfileFunc, 0, 0); + sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0, + writefileFunc, 0, 0); } } @@ -2416,6 +2496,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){ if( c=='f' && strncmp(azArg[0], "fullschema", n)==0 ){ struct callback_data data; char *zErrMsg = 0; + int doStats = 0; if( nArg!=1 ){ fprintf(stderr, "Usage: .fullschema\n"); rc = 1; @@ -2434,21 +2515,33 @@ static int do_meta_command(char *zLine, struct callback_data *p){ "ORDER BY rowid", callback, &data, &zErrMsg ); - sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_master;'", - callback, &data, &zErrMsg); - data.mode = MODE_Insert; - data.zDestTable = "sqlite_stat1"; - shell_exec(p->db, "SELECT * FROM sqlite_stat1", - shell_callback, &data,&zErrMsg); - data.zDestTable = "sqlite_stat3"; - shell_exec(p->db, "SELECT * FROM sqlite_stat3", - shell_callback, &data,&zErrMsg); - data.zDestTable = "sqlite_stat4"; - shell_exec(p->db, "SELECT * FROM sqlite_stat4", - shell_callback, &data, &zErrMsg); - data.mode = MODE_Semi; - shell_exec(p->db, "SELECT 'ANALYZE sqlite_master;'", - shell_callback, &data, &zErrMsg); + if( rc==SQLITE_OK ){ + sqlite3_stmt *pStmt; + rc = sqlite3_prepare_v2(p->db, + "SELECT rowid FROM sqlite_master" + " WHERE name GLOB 'sqlite_stat[134]'", + -1, &pStmt, 0); + doStats = sqlite3_step(pStmt)==SQLITE_ROW; + sqlite3_finalize(pStmt); + } + if( doStats==0 ){ + fprintf(p->out, "/* No STAT tables available */\n"); + }else{ + fprintf(p->out, "ANALYZE sqlite_master;\n"); + sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_master'", + callback, &data, &zErrMsg); + data.mode = MODE_Insert; + data.zDestTable = "sqlite_stat1"; + shell_exec(p->db, "SELECT * FROM sqlite_stat1", + shell_callback, &data,&zErrMsg); + data.zDestTable = "sqlite_stat3"; + shell_exec(p->db, "SELECT * FROM sqlite_stat3", + shell_callback, &data,&zErrMsg); + data.zDestTable = "sqlite_stat4"; + shell_exec(p->db, "SELECT * FROM sqlite_stat4", + shell_callback, &data, &zErrMsg); + fprintf(p->out, "ANALYZE sqlite_master;\n"); + } }else if( c=='h' && strncmp(azArg[0], "headers", n)==0 ){ @@ -2738,6 +2831,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){ }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ p->mode = MODE_Csv; sqlite3_snprintf(sizeof(p->separator), p->separator, ","); + sqlite3_snprintf(sizeof(p->newline), p->newline, "\r\n"); }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){ p->mode = MODE_List; sqlite3_snprintf(sizeof(p->separator), p->separator, "\t"); @@ -3016,13 +3110,16 @@ static int do_meta_command(char *zLine, struct callback_data *p){ #endif 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"); + if( nArg<2 || nArg>3 ){ + fprintf(stderr, "Usage: .separator SEPARATOR ?NEWLINE?\n"); rc = 1; } + if( nArg>=2 ){ + sqlite3_snprintf(sizeof(p->separator), p->separator, azArg[1]); + } + if( nArg>=3 ){ + sqlite3_snprintf(sizeof(p->newline), p->newline, azArg[2]); + } }else if( c=='s' @@ -3063,6 +3160,8 @@ static int do_meta_command(char *zLine, struct callback_data *p){ strlen30(p->outfile) ? p->outfile : "stdout"); fprintf(p->out,"%9.9s: ", "separator"); output_c_string(p->out, p->separator); + fprintf(p->out," "); + output_c_string(p->out, p->newline); fprintf(p->out, "\n"); fprintf(p->out,"%9.9s: %s\n","stats", p->statsOn ? "on" : "off"); fprintf(p->out,"%9.9s: ","width"); @@ -3679,6 +3778,7 @@ static const char zOptions[] = #ifdef SQLITE_ENABLE_MULTIPLEX " -multiplex enable the multiplexor VFS\n" #endif + " -newline SEP set newline character(s) for CSV\n" " -nullvalue TEXT set text string for NULL values. Default ''\n" " -separator SEP set output field separator. Default: '|'\n" " -stats print memory stats before each finalize\n" @@ -3708,6 +3808,7 @@ static void main_init(struct callback_data *data) { memset(data, 0, sizeof(*data)); data->mode = MODE_List; memcpy(data->separator,"|", 2); + memcpy(data->newline,"\r\n", 3); data->showHeader = 0; sqlite3_config(SQLITE_CONFIG_URI, 1); sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); @@ -3801,6 +3902,7 @@ int main(int argc, char **argv){ if( z[1]=='-' ) z++; if( strcmp(z,"-separator")==0 || strcmp(z,"-nullvalue")==0 + || strcmp(z,"-newline")==0 || strcmp(z,"-cmd")==0 ){ (void)cmdline_option_value(argc, argv, ++i); @@ -3910,6 +4012,9 @@ int main(int argc, char **argv){ }else if( strcmp(z,"-separator")==0 ){ sqlite3_snprintf(sizeof(data.separator), data.separator, "%s",cmdline_option_value(argc,argv,++i)); + }else if( strcmp(z,"-newline")==0 ){ + sqlite3_snprintf(sizeof(data.newline), data.newline, + "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-nullvalue")==0 ){ sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue, "%s",cmdline_option_value(argc,argv,++i)); |