In the command-line shell, in CSV output mode, terminate rows with CRNL but

do not expand NL characters in data into CRNL.  Provide the extra -newline
command-line option and the extra argument to .separator to designate an
alternative newline character sequence for CSV output.

FossilOrigin-Name: 16c8ce10e1530731441e6c4538691b71564684ed
This commit is contained in:
drh 2014-07-24 12:09:47 +00:00
parent dbd9486d5b
commit 6976c2123b
5 changed files with 59 additions and 28 deletions

View File

@ -1,5 +1,5 @@
C Add\sexperimental\s"costmult"\slogic.\s\sOnly\senabled\swhen\scompiled\swith\n-DSQLITE_ENABLE_COSTMULT. C In\sthe\scommand-line\sshell,\sin\sCSV\soutput\smode,\sterminate\srows\swith\sCRNL\sbut\ndo\snot\sexpand\sNL\scharacters\sin\sdata\sinto\sCRNL.\s\sProvide\sthe\sextra\s-newline\ncommand-line\soption\sand\sthe\sextra\sargument\sto\s.separator\sto\sdesignate\san\nalternative\snewline\scharacter\ssequence\sfor\sCSV\soutput.
D 2014-07-23T23:57:42.403 D 2014-07-24T12:09:47.370
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 5eb79e334a5de69c87740edd56af6527dd219308 F Makefile.in 5eb79e334a5de69c87740edd56af6527dd219308
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@ -223,7 +223,7 @@ F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece
F src/resolve.c 5fc110baeacf120a73fe34e103f052632ff11a02 F src/resolve.c 5fc110baeacf120a73fe34e103f052632ff11a02
F src/rowset.c a9c9aae3234b44a6d7c6f5a3cadf90dce1e627be F src/rowset.c a9c9aae3234b44a6d7c6f5a3cadf90dce1e627be
F src/select.c 6762c62e11b504aa014edceab8886495165e3a77 F src/select.c 6762c62e11b504aa014edceab8886495165e3a77
F src/shell.c 566aee8213372a2e81ba0eb34e9759f7b2574009 F src/shell.c cca6ea15719f2a3f41b8a1e0030d0b67a8aae3ca
F src/sqlite.h.in ac4451c9da2771d2f4d702ef89722407242906d9 F src/sqlite.h.in ac4451c9da2771d2f4d702ef89722407242906d9
F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e
F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
@ -819,11 +819,11 @@ F test/shared9.test 5f2a8f79b4d6c7d107a01ffa1ed05ae7e6333e21
F test/sharedA.test 0cdf1a76dfa00e6beee66af5b534b1e8df2720f5 F test/sharedA.test 0cdf1a76dfa00e6beee66af5b534b1e8df2720f5
F test/shared_err.test 0079c05c97d88cfa03989b7c20a8b266983087aa F test/shared_err.test 0079c05c97d88cfa03989b7c20a8b266983087aa
F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304 F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
F test/shell1.test fb080d67c81e8a80a79ea04b36f127209b5bd112 F test/shell1.test d60946b5fde4d85fe06db7331dfe89011f564350
F test/shell2.test c57da3a381c099b02c813ba156298d5c2f5c93a3 F test/shell2.test c57da3a381c099b02c813ba156298d5c2f5c93a3
F test/shell3.test 5e8545ec72c4413a0e8d4c6be56496e3c257ca29 F test/shell3.test 5e8545ec72c4413a0e8d4c6be56496e3c257ca29
F test/shell4.test 8a9c08976291e6c6c808b4d718f4a8b299f339f5 F test/shell4.test 8a9c08976291e6c6c808b4d718f4a8b299f339f5
F test/shell5.test ef0c52952a4a96dc1d9ec3b1fa81ec897ca48154 F test/shell5.test 15a419cc1df21c892ed64f5596ae7a501f2816f2
F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3 F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3
F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5 F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5
F test/shrink.test 8c70f62b6e8eb4d54533de6d65bd06b1b9a17868 F test/shrink.test 8c70f62b6e8eb4d54533de6d65bd06b1b9a17868
@ -1183,7 +1183,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P e6225a7bf77a700b318563b1a854b4b3a9e031e1 P 729ece40885ed7f52c5981364833fc62281a388b
R 07ca3465b9cbcf38e018d96848d50309 R d4352ef214ae9bc2c731800627aed3f1
U drh U drh
Z 5f4178ba82b72065a3c11af477d1e9bb Z ddfb7b81152d55231f21f3e3700ae2bf

View File

@ -1 +1 @@
729ece40885ed7f52c5981364833fc62281a388b 16c8ce10e1530731441e6c4538691b71564684ed

View File

@ -64,6 +64,7 @@
#if defined(_WIN32) || defined(WIN32) #if defined(_WIN32) || defined(WIN32)
# include <io.h> # include <io.h>
# include <fcntl.h>
#define isatty(h) _isatty(h) #define isatty(h) _isatty(h)
#ifndef access #ifndef access
# define access(f,m) _access((f),(m)) # 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 */ int showHeader; /* True to show column names in List or Column mode */
char *zDestTable; /* Name of destination table when MODE_Insert */ char *zDestTable; /* Name of destination table when MODE_Insert */
char separator[20]; /* Separator character for MODE_List */ 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 colWidth[100]; /* Requested width of each column when in column mode*/
int actualWidth[100]; /* Actual width of each column */ int actualWidth[100]; /* Actual width of each column */
char nullvalue[20]; /* The text to print when a NULL comes back from 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 ** 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 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){ static void output_csv(struct callback_data *p, const char *z, int bSep){
FILE *out = p->out; FILE *out = p->out;
@ -855,17 +858,26 @@ static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int
break; break;
} }
case MODE_Csv: { case MODE_Csv: {
#if defined(WIN32) || defined(_WIN32)
fflush(p->out);
_setmode(_fileno(p->out), _O_BINARY);
#endif
if( p->cnt++==0 && p->showHeader ){ if( p->cnt++==0 && p->showHeader ){
for(i=0; i<nArg; i++){ for(i=0; i<nArg; i++){
output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1); output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1);
} }
fprintf(p->out,"\n"); fprintf(p->out,"%s",p->newline);
} }
if( azArg==0 ) break; if( azArg>0 ){
for(i=0; i<nArg; i++){ for(i=0; i<nArg; i++){
output_csv(p, azArg[i], i<nArg-1); 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; break;
} }
case MODE_Insert: { case MODE_Insert: {
@ -1619,7 +1631,8 @@ static char zHelp[] =
".schema ?TABLE? Show the CREATE statements\n" ".schema ?TABLE? Show the CREATE statements\n"
" If TABLE specified, only show tables matching\n" " If TABLE specified, only show tables matching\n"
" LIKE pattern TABLE.\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" ".shell CMD ARGS... Run CMD ARGS... in a system shell\n"
".show Show the current values for various settings\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"
@ -2751,6 +2764,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){
p->mode = MODE_Csv; p->mode = MODE_Csv;
sqlite3_snprintf(sizeof(p->separator), p->separator, ","); 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 ){ }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){
p->mode = MODE_List; p->mode = MODE_List;
sqlite3_snprintf(sizeof(p->separator), p->separator, "\t"); sqlite3_snprintf(sizeof(p->separator), p->separator, "\t");
@ -3029,13 +3043,16 @@ static int do_meta_command(char *zLine, struct callback_data *p){
#endif #endif
if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){ if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){
if( nArg==2 ){ if( nArg<2 || nArg>3 ){
sqlite3_snprintf(sizeof(p->separator), p->separator, fprintf(stderr, "Usage: .separator SEPARATOR ?NEWLINE?\n");
"%.*s", (int)sizeof(p->separator)-1, azArg[1]);
}else{
fprintf(stderr, "Usage: .separator STRING\n");
rc = 1; 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 }else
if( c=='s' if( c=='s'
@ -3076,6 +3093,8 @@ static int do_meta_command(char *zLine, struct callback_data *p){
strlen30(p->outfile) ? p->outfile : "stdout"); strlen30(p->outfile) ? p->outfile : "stdout");
fprintf(p->out,"%9.9s: ", "separator"); fprintf(p->out,"%9.9s: ", "separator");
output_c_string(p->out, p->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, "\n");
fprintf(p->out,"%9.9s: %s\n","stats", p->statsOn ? "on" : "off"); fprintf(p->out,"%9.9s: %s\n","stats", p->statsOn ? "on" : "off");
fprintf(p->out,"%9.9s: ","width"); fprintf(p->out,"%9.9s: ","width");
@ -3692,6 +3711,7 @@ static const char zOptions[] =
#ifdef SQLITE_ENABLE_MULTIPLEX #ifdef SQLITE_ENABLE_MULTIPLEX
" -multiplex enable the multiplexor VFS\n" " -multiplex enable the multiplexor VFS\n"
#endif #endif
" -newline SEP set newline character(s) for CSV\n"
" -nullvalue TEXT set text string for NULL values. Default ''\n" " -nullvalue TEXT set text string for NULL values. Default ''\n"
" -separator SEP set output field separator. Default: '|'\n" " -separator SEP set output field separator. Default: '|'\n"
" -stats print memory stats before each finalize\n" " -stats print memory stats before each finalize\n"
@ -3721,6 +3741,7 @@ static void main_init(struct callback_data *data) {
memset(data, 0, sizeof(*data)); memset(data, 0, sizeof(*data));
data->mode = MODE_List; data->mode = MODE_List;
memcpy(data->separator,"|", 2); memcpy(data->separator,"|", 2);
memcpy(data->newline,"\r\n", 3);
data->showHeader = 0; data->showHeader = 0;
sqlite3_config(SQLITE_CONFIG_URI, 1); sqlite3_config(SQLITE_CONFIG_URI, 1);
sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data);
@ -3813,6 +3834,7 @@ int main(int argc, char **argv){
if( z[1]=='-' ) z++; if( z[1]=='-' ) z++;
if( strcmp(z,"-separator")==0 if( strcmp(z,"-separator")==0
|| strcmp(z,"-nullvalue")==0 || strcmp(z,"-nullvalue")==0
|| strcmp(z,"-newline")==0
|| strcmp(z,"-cmd")==0 || strcmp(z,"-cmd")==0
){ ){
(void)cmdline_option_value(argc, argv, ++i); (void)cmdline_option_value(argc, argv, ++i);
@ -3922,6 +3944,9 @@ int main(int argc, char **argv){
}else if( strcmp(z,"-separator")==0 ){ }else if( strcmp(z,"-separator")==0 ){
sqlite3_snprintf(sizeof(data.separator), data.separator, sqlite3_snprintf(sizeof(data.separator), data.separator,
"%s",cmdline_option_value(argc,argv,++i)); "%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 ){ }else if( strcmp(z,"-nullvalue")==0 ){
sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue, sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue,
"%s",cmdline_option_value(argc,argv,++i)); "%s",cmdline_option_value(argc,argv,++i));

View File

@ -588,14 +588,17 @@ db eval {DROP VIEW v1; DROP VIEW v2; DROP TABLE t1;}
# .separator STRING Change separator used by output mode and .import # .separator STRING Change separator used by output mode and .import
do_test shell1-3.22.1 { do_test shell1-3.22.1 {
catchcmd "test.db" ".separator" catchcmd "test.db" ".separator"
} {1 {Usage: .separator STRING}} } {1 {Usage: .separator SEPARATOR ?NEWLINE?}}
do_test shell1-3.22.2 { do_test shell1-3.22.2 {
catchcmd "test.db" ".separator FOO" catchcmd "test.db" ".separator FOO"
} {0 {}} } {0 {}}
do_test shell1-3.22.3 { do_test shell1-3.22.3 {
catchcmd "test.db" ".separator ABC XYZ"
} {0 {}}
do_test shell1-3.22.4 {
# too many arguments # too many arguments
catchcmd "test.db" ".separator FOO BAD" catchcmd "test.db" ".separator FOO BAD BAD2"
} {1 {Usage: .separator STRING}} } {1 {Usage: .separator SEPARATOR ?NEWLINE?}}
# .show Show the current values for various settings # .show Show the current values for various settings
do_test shell1-3.23.1 { do_test shell1-3.23.1 {

View File

@ -55,14 +55,17 @@ do_test shell5-1.1.3 {
# .separator STRING Change separator used by output mode and .import # .separator STRING Change separator used by output mode and .import
do_test shell5-1.2.1 { do_test shell5-1.2.1 {
catchcmd "test.db" ".separator" catchcmd "test.db" ".separator"
} {1 {Usage: .separator STRING}} } {1 {Usage: .separator SEPARATOR ?NEWLINE?}}
do_test shell5-1.2.2 { do_test shell5-1.2.2 {
catchcmd "test.db" ".separator FOO" catchcmd "test.db" ".separator ONE"
} {0 {}} } {0 {}}
do_test shell5-1.2.3 { do_test shell5-1.2.3 {
catchcmd "test.db" ".separator ONE TWO"
} {0 {}}
do_test shell5-1.2.4 {
# too many arguments # too many arguments
catchcmd "test.db" ".separator FOO BAD" catchcmd "test.db" ".separator ONE TWO THREE"
} {1 {Usage: .separator STRING}} } {1 {Usage: .separator SEPARATOR ?NEWLINE?}}
# separator should default to "|" # separator should default to "|"
do_test shell5-1.3.1 { do_test shell5-1.3.1 {