From: drh <>
Date: Mon, 17 Nov 2025 16:44:13 +0000 (+0000)
Subject: CLI comes up in legacy "list" mode if neither stdin or stdout are a tty, or
X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=234d2e51ac984238a676c2d27e30eacb8431cf0b;p=thirdparty%2Fsqlite.git
CLI comes up in legacy "list" mode if neither stdin or stdout are a tty, or
if "--compat 20251114" or earlier is specified, or if "--batch" is used.
New modes "batch" and "tty" select either legacy "list" mode or the newer
"qbox" mode with limits.
Make CVS output compatible with legacy. Break out ".import" into a separate
subroutine in anticipation of forthcoming improvements. General code
cleanup.
FossilOrigin-Name: f6bfcea9a01493af182e9aa0d35df6f81bf9e36220df79139afa287fa43d9aa3
---
diff --git a/ext/qrf/README.md b/ext/qrf/README.md
index b3acf5863c..a8d60f28ef 100644
--- a/ext/qrf/README.md
+++ b/ext/qrf/README.md
@@ -278,7 +278,7 @@ A value of QRF_BLOB_Sql means that BLOB values are shown as SQL BLOB
literals: a prefix "`x'`" following by hexadecimal and ending with a
final "`'`".
-A value of QRF_BLOB_Hex means that BLOB values are shown as pure
+A value of QRF_BLOB_Hex means that BLOB values are shown as
hexadecimal text with no delimiters.
A value of QRF_BLOB_Tcl means that BLOB values are shown as a
@@ -604,10 +604,16 @@ Except the eEsp mode defaults to `QRF_ESC_On`, so that control
characters are escaped, for safety.
The **Csv** and **Quote** styles are simply variations on **List**
-with different defaults.
-**Csv** sets things up to generate valid CSV file output.
-**Quote** displays a comma-separated list of SQL
-value literals.
+with hard-coded values for some of the sqlite3_qrf_spec settings:
+
+
+| | Quote | Csv
+ |
|---|
| zColumnSep | "," | ","
+ |
| zRowSep | "\\n" | "\\r\\n"
+ |
| zNull | "NULL" | ""
+ |
| eText | QRF_TEXT_Sql | QRF_TEXT_Csv
+ |
| eBlob | QRF_BLOB_Sql | QRF_BLOB_Text
+ |
The **Html** style generates HTML table content, just without
the `` around the outside.
diff --git a/ext/qrf/qrf.c b/ext/qrf/qrf.c
index ca3eb7d812..fdee93a86f 100644
--- a/ext/qrf/qrf.c
+++ b/ext/qrf/qrf.c
@@ -2261,9 +2261,10 @@ qrf_reinit:
case QRF_STYLE_Csv: {
p->spec.eStyle = QRF_STYLE_List;
p->spec.eText = QRF_TEXT_Csv;
- p->spec.eBlob = QRF_BLOB_Tcl;
+ p->spec.eBlob = QRF_BLOB_Text;
p->spec.zColumnSep = ",";
p->spec.zRowSep = "\r\n";
+ p->spec.zNull = "";
break;
}
case QRF_STYLE_Quote: {
diff --git a/manifest b/manifest
index 7cb99c345f..6dc92154c2 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\stest\scases\simpacted\sby\sthe\suse\sof\s~/.sqliterc
-D 2025-11-17T00:15:14.616
+C CLI\scomes\sup\sin\slegacy\s"list"\smode\sif\sneither\sstdin\sor\sstdout\sare\sa\stty,\sor\nif\s"--compat\s20251114"\sor\searlier\sis\sspecified,\sor\sif\s"--batch"\sis\sused.\nNew\smodes\s"batch"\sand\s"tty"\sselect\seither\slegacy\s"list"\smode\sor\sthe\snewer\n"qbox"\smode\swith\slimits.\nMake\sCVS\soutput\scompatible\swith\slegacy.\s\sBreak\sout\s".import"\sinto\sa\sseparate\nsubroutine\sin\santicipation\sof\sforthcoming\simprovements.\s\sGeneral\scode\ncleanup.
+D 2025-11-17T16:44:13.271
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -416,8 +416,8 @@ F ext/misc/wholenumber.c 0fa0c082676b7868bf2fa918e911133f2b349bcdceabd1198bba5f6
F ext/misc/windirent.h 02211ce51f3034c675f2dbf4d228194d51b3ee05734678bad5106fff6292e60c
F ext/misc/zipfile.c 09e6e3a3ff40a99677de3c0bc6569bd5f4709b1844ac3d1c1452a456c5a62f1c
F ext/misc/zorder.c bddff2e1b9661a90c95c2a9a9c7ecd8908afab5763256294dd12d609d4664eee
-F ext/qrf/README.md 09dd538966d8ee32598fc010e7fe6755bd7190494953a02960a9c81197d20cf3
-F ext/qrf/qrf.c 290b95fa8613e11a90d5a5a92c32fa22ce415b01fa9578ff5070427b057ac02e
+F ext/qrf/README.md dd565fd1ca0c46ea37dbf4d496e368b9ecade768c92669640bc106e039629016
+F ext/qrf/qrf.c b62a16a4d380223c6fe90ae28c33aa27b44af128a2e1e39c496d3452ac6ae14f
F ext/qrf/qrf.h b4b3489b3b3683523fd248d15cf5945830643b036943efacdb772a3e00367aa2
F ext/rbu/rbu.c 801450b24eaf14440d8fd20385aacc751d5c9d6123398df41b1b5aa804bf4ce8
F ext/rbu/rbu1.test 25870dd7db7eb5597e2b4d6e29e7a7e095abf332660f67d89959552ce8f8f255
@@ -735,7 +735,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
F src/resolve.c 5616fbcf3b833c7c705b24371828215ad0925d0c0073216c4f153348d5753f0a
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
F src/select.c ba9cd07ffa3277883c1986085f6ddc4320f4d35d5f212ab58df79a7ecc1a576a
-F src/shell.c.in 64f777047c972c46df3f04597367980732a58aa0b58a21e4862812a225edee12
+F src/shell.c.in 4cae6e0560037d4916f2819e3ca576e71e4a40adee9fc273f320cd28644e99c6
F src/sqlite.h.in 795ce84cc136b4e74d882cf4fab56d2927c20b9af9fd2fcea27760a6fe50851b
F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
F src/sqlite3ext.h 5d5330f5f8461f5ce74960436ddcfa53ecd09c2b8b23901e22ae38aec3243998
@@ -1507,7 +1507,7 @@ F test/printf2.test 3f55c1871a5a65507416076f6eb97e738d5210aeda7595a74ee895f2224c
F test/progress.test ebab27f670bd0d4eb9d20d49cef96e68141d92fb
F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
F test/pushdown.test 46a626ef1c0ca79b85296ff2e078b9da20a50e9b804b38f441590c3987580ddd
-F test/qrf01.test f46f498e447d988b52dfa1cd45c683b7212bb2056741a4eff306df185b9c00b1
+F test/qrf01.test 1caa611b69e07c6c134c3749f633ef014a97640b8f57f03c8fc0ca4d064b6dd2
F test/qrf02.test 39b4afdc000bedccdafc0aecf17638df67a67aaa2d2942865ae6abcc48ba0e92
F test/qrf03.test 9d88aeb5cdd53f050b7ab9bd203281f7c9d063c33f22f8808e441b7ac0874ccf
F test/queryonly.test 5f653159e0f552f0552d43259890c1089391dcca
@@ -1603,11 +1603,11 @@ F test/sharedA.test 64bdd21216dda2c6a3bd3475348ccdc108160f34682c97f2f51c19fc0e21
F test/sharedB.test 1a84863d7a2204e0d42f2e1606577c5e92e4473fa37ea0f5bdf829e4bf8ee707
F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939
F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
-F test/shell1.test 88acfa38c2ecfd5beb25825fdd3a93f4a81573a736c04de82e6e377efa6f6efd
-F test/shell2.test 38c394063f9398bc559fbe5b232c8e7f0c817dd33edd21ee8c4c4c1b650530db
+F test/shell1.test b88af002858e1923f90fccf07f841d853e8fba568cecf38efd29f5172c661735
+F test/shell2.test 103140814bdc7508aa41dd3462413cbc4aa84b4261112cb8d501d74275cb7d48
F test/shell3.test 840192774cc4edf7653520c0434a311c7477b9bc324abbc7bd2887915792fa8c
F test/shell4.test e25580a792b7b54560c3a76b6968bd8189261f38979fe28e6bc6312c5db280db
-F test/shell5.test ed3c2cf47330134ab91e5b16dbdc34a7fb955ff2999ff69bac908cc2238b2b7f
+F test/shell5.test 7a249400bb2af59ac8524f357f8cf2844a62b6ac5ff8ecd69b045ceb688700ae
F test/shell6.test e3b883b61d4916b6906678a35f9d19054861123ad91b856461e0a456273bdbb8
F test/shell7.test 43fd8e511c533bab5232e95c7b4be93b243451709e89582600d4b6e67693d5c3
F test/shell8.test 641cf21a99c59404c24e3062923734951c4099a6b6b6520de00cf7a1249ee871
@@ -2176,8 +2176,8 @@ F tool/version-info.c 33d0390ef484b3b1cb685d59362be891ea162123cea181cb8e6d2cf6dd
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 2aebd7bfecaaf1f75b52b05a0d3009fc0dc61289ae666d24cb4e3ddfaf251645
-R 5f06e6da6beb48984fc94266490a7906
+P 8fc05faef91186429c6c710991fd736b1df9a9af946c29d207db2518d6436b38
+R bb02fe9715521dfd12626a15ebffae99
U drh
-Z 490dbb4a529fbc5b5a8e8da8dff783f8
+Z 405f1a2cbb60052057d2e27df0bf6378
# Remove this line to create a well-formed Fossil manifest.
diff --git a/manifest.uuid b/manifest.uuid
index 891e6da316..437b001ca5 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-8fc05faef91186429c6c710991fd736b1df9a9af946c29d207db2518d6436b38
+f6bfcea9a01493af182e9aa0d35df6f81bf9e36220df79139afa287fa43d9aa3
diff --git a/src/shell.c.in b/src/shell.c.in
index dc1ad60b87..0453c1bd00 100644
--- a/src/shell.c.in
+++ b/src/shell.c.in
@@ -534,12 +534,17 @@ static int bail_on_error = 0;
static int stdin_is_interactive = 1;
/*
-** On Windows systems we need to know if standard output is a console
-** in order to show that UTF-16 translation is done in the sign-on
-** banner. The following variable is true if it is the console.
+** Treat stdout like a TTY if true.
*/
static int stdout_is_console = 1;
+/*
+** Use this value as the width of the output device. Or, figure it
+** out at runtime if the value is negative. Or use a default width
+** if this value is zero.
+*/
+static int stdout_tty_width = -1;
+
/*
** The following is the open SQLite database. We make a pointer
** to this database a static variable so that it can be accessed
@@ -1251,12 +1256,16 @@ typedef struct Mode {
u8 autoEQPtrace; /* autoEQP is in trace mode */
u8 scanstatsOn; /* True to display scan stats before each finalize */
u8 bAutoScreenWidth; /* Using the TTY to determine screen width */
- u8 bEcho; /* True to echo all SQL to output */
+ u8 mFlags; /* MFLG_ECHO and/or MFLG_CRLF */
u8 eMode; /* One of the MODE_ values */
- u8 crlfMode; /* Do NL-to-CRLF translations when enabled (maybe) */
sqlite3_qrf_spec spec; /* Spec to be passed into QRF */
} Mode;
+/* Flags for Mode.mFlags */
+#define MFLG_ECHO 0x01 /* Echo inputs to output */
+#define MFLG_CRLF 0x02 /* Use CR/LF output line endings */
+
+
/*
** State information about the database connection is contained in an
** instance of the following structure.
@@ -1426,8 +1435,14 @@ static const char *qrfQuoteNames[] =
#define MODE_Tcl 19 /* Space-separated list of TCL strings */
#define MODE_Www 20 /* Full web-page output */
+#define MODE_BUILTIN 20 /* Maximum built-in mode */
+#define MODE_BATCH 50 /* Default mode for batch processing */
+#define MODE_TTY 51 /* Default mode for interactive processing */
+#define MODE_USER 75 /* First user-defined mode */
+#define MODE_N_USER 25 /* Maximum number of user-defined modes */
+
/*
-** Information about how each display-mode behaves.
+** Information about built-in display modes
*/
typedef struct ModeInfo ModeInfo;
struct ModeInfo {
@@ -1442,6 +1457,8 @@ struct ModeInfo {
unsigned char eStyle; /* Underlying QRF style */
unsigned char eCx; /* 0: other, 1: line, 2: columnar */
};
+
+/* String constants used by built-in modes */
static const char *aModeStr[] =
/* 0 1 2 3 4 5 6 7 8 */
{ 0, "\n", "|", " ", ",", "\r\n", "\036", "\037", "\t",
@@ -1455,7 +1472,7 @@ static const ModeInfo aModeInfo[] = {
{ "c", 4, 1, 10, 5, 5, 4, 1, 12, 0 },
{ "column", 0, 0, 9, 1, 1, 1, 2, 2, 2 },
{ "count", 0, 0, 0, 0, 0, 0, 0, 3, 0 },
- { "csv", 4, 5, 9, 3, 3, 1, 1, 12, 0 },
+ { "csv", 4, 5, 9, 3, 3, 3, 1, 12, 0 },
{ "html", 0, 0, 9, 4, 4, 1, 2, 7, 0 },
{ "insert", 0, 0, 10, 2, 2, 2, 1, 8, 0 },
{ "jatom", 4, 1, 11, 6, 6, 5, 1, 12, 0 },
@@ -1593,9 +1610,22 @@ static void modeChange(ShellState *p, unsigned char eMode){
pM->spec.eBlob = pI->eBlob;
pM->spec.bTitles = pI->bHdr;
pM->spec.eTitle = pI->eHdr;
- }else if( eMode>=100 && eMode-100nSavedModes ){
+ }else if( eMode>=MODE_USER && eMode-MODE_USERnSavedModes ){
+ modeFree(&p->mode);
+ modeDup(&p->mode, &p->aSavedModes[eMode-MODE_USER].mode);
+ }else if( eMode==MODE_BATCH ){
+ u8 mFlags = p->mode.mFlags;
modeFree(&p->mode);
- modeDup(&p->mode, &p->aSavedModes[eMode-100].mode);
+ modeChange(p, MODE_List);
+ p->mode.mFlags = mFlags;
+ }else if( eMode==MODE_TTY ){
+ u8 mFlags = p->mode.mFlags;
+ modeFree(&p->mode);
+ modeChange(p, MODE_QBox);
+ p->mode.bAutoScreenWidth = 1;
+ p->mode.spec.nCharLimit = 300;
+ p->mode.spec.nLineLimit = 5;
+ p->mode.mFlags = mFlags;
}
}
@@ -1607,13 +1637,10 @@ static void modeChange(ShellState *p, unsigned char eMode){
static void modeDefault(ShellState *p){
p->mode.spec.iVersion = 1;
p->mode.autoExplain = 1;
- if( p->iCompat>=20251115 ){
- modeChange(p, MODE_QBox);
- p->mode.bAutoScreenWidth = 1;
- p->mode.spec.nCharLimit = 300;
- p->mode.spec.nLineLimit = 5;
+ if( p->iCompat>=20251115 && (stdin_is_interactive || stdout_is_console) ){
+ modeChange(p, MODE_TTY);
}else{
- modeChange(p, MODE_List);
+ modeChange(p, MODE_BATCH);
}
}
@@ -1622,7 +1649,12 @@ static void modeDefault(ShellState *p){
** the name does not match any mode.
**
** Saved modes are also searched if p!=NULL. The number returned
-** for a saved mode is the index into the p->aSavedModes[] array plus 100.
+** for a saved mode is the index into the p->aSavedModes[] array
+** plus MODE_USER.
+**
+** Two special mode names are also available: "batch" and "tty".
+** evaluate to the default mode for batch operation and interactive
+** operation on a TTY, respectively.
*/
static int modeFind(ShellState *p, const char *zName){
int i;
@@ -1630,8 +1662,10 @@ static int modeFind(ShellState *p, const char *zName){
if( cli_strcmp(aModeInfo[i].zName,zName)==0 ) return i;
}
for(i=0; inSavedModes; i++){
- if( cli_strcmp(p->aSavedModes[i].zTag,zName)==0 ) return i+100;
+ if( cli_strcmp(p->aSavedModes[i].zTag,zName)==0 ) return i+MODE_USER;
}
+ if( strcmp(zName,"batch")==0 ) return MODE_BATCH;
+ if( strcmp(zName,"tty")==0 ) return MODE_TTY;
return -1;
}
@@ -1901,7 +1935,7 @@ edit_func_end:
*/
static void setCrlfMode(ShellState *p){
#ifdef _WIN32
- if( p->mode.crlfMode ){
+ if( p->mode.mFlags & MFLG_CRLF ){
sqlite3_fsetmode(p->out, _O_TEXT);
}else{
sqlite3_fsetmode(p->out, _O_BINARY);
@@ -2065,7 +2099,8 @@ static void interrupt_handler(int NotUsed){
/* Try to determine the screen width. Use the default if unable.
*/
-int shellScreenWidth(int dfltWidth){
+int shellScreenWidth(void){
+ if( stdout_tty_width>0 ) return stdout_tty_width;
#if defined(TIOCGSIZE)
struct ttysize ts;
if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)>=0
@@ -2091,7 +2126,8 @@ int shellScreenWidth(int dfltWidth){
return csbi.srWindow.Right - csbi.srWindow.Left + 1;
}
#endif
- return dfltWidth;
+#define DEFAULT_SCREEN_WIDTH 80
+ return DEFAULT_SCREEN_WIDTH;
}
#if (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE)
@@ -3142,7 +3178,7 @@ static int shell_exec(
assert( pArg->mode.eMode>=0 && pArg->mode.eModemode.eMode].eStyle;
if( pArg->mode.bAutoScreenWidth ){
- spec.nScreenWidth = shellScreenWidth(80);
+ spec.nScreenWidth = shellScreenWidth();
}
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
@@ -3660,19 +3696,6 @@ static const char *(azHelp[]) = {
".help ?-all? ?PATTERN? Show help text for PATTERN",
#ifndef SQLITE_SHELL_FIDDLE
".import FILE TABLE Import data from FILE into TABLE",
- " Options:",
- " --ascii Use \\037 and \\036 as column and row separators",
- " --csv Use , and \\n as column and row separators",
- " --skip N Skip the first N rows of input",
- " --schema S Target table to be S.TABLE",
- " -v \"Verbose\" - increase auxiliary output",
- " Notes:",
- " * If TABLE does not exist, it is created. The first row of input",
- " determines the column names.",
- " * If neither --csv or --ascii are used, the input mode is derived",
- " from the \".mode\" output mode",
- " * If FILE begins with \"|\" then it is a command that generates the",
- " input text.",
#endif
#ifndef SQLITE_OMIT_TEST_CONTROL
".imposter INDEX TABLE Create imposter table TABLE on index INDEX",
@@ -7217,6 +7240,352 @@ static int pickStr(const char *zArg, char **pzErr, ...){
return -1;
}
+/*
+** DOT-COMMAND: .import
+**
+** USAGE: .import [OPTIONS] FILE TABLE
+**
+** Import CSV or similar text from FILE into TABLE. If TABLE does
+** not exist, it is created using the first row of FILE as the column
+** names. If FILE begins with "|" then it is a command that is run
+** and the output from the command is used as the input data.
+**
+** FILE is assumed to be in a CSV format, unless the current mode
+** is "ascii" or "tabs" or unless one of the options below specify
+** an alternative.
+**
+** Options:
+** --ascii Use \037 and \036 as column and row separators on input
+** --csv Input is standard RFC-4180 CSV.
+** --schema S When creating TABLE, put it in schema S
+** --skip N Ignore the first N rows of input
+** -v Verbose mode
+*/
+static int dotCmdImport(ShellState *p){
+ int nArg = p->dot.nArg; /* Number of arguments */
+ char **azArg = p->dot.azArg;/* Argument list */
+ char *zTable = 0; /* Insert data into this table */
+ char *zSchema = 0; /* Schema of zTable */
+ char *zFile = 0; /* Name of file to extra content from */
+ sqlite3_stmt *pStmt = NULL; /* A statement */
+ int nCol; /* Number of columns in the table */
+ i64 nByte; /* Number of bytes in an SQL string */
+ int i, j; /* Loop counters */
+ int needCommit; /* True to COMMIT or ROLLBACK at end */
+ int nSep; /* Number of bytes in spec.zColumnSep */
+ char *zSql = 0; /* An SQL statement */
+ ImportCtx sCtx; /* Reader context */
+ char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */
+ int eVerbose = 0; /* Larger for more console output */
+ i64 nSkip = 0; /* Initial lines to skip */
+ int useOutputMode = 1; /* Use output mode to determine separators */
+ char *zCreate = 0; /* CREATE TABLE statement text */
+ int rc; /* Result code */
+
+ failIfSafeMode(p, "cannot run .import in safe mode");
+ memset(&sCtx, 0, sizeof(sCtx));
+ if( p->mode.eMode==MODE_Ascii ){
+ xRead = ascii_read_one_field;
+ }else{
+ xRead = csv_read_one_field;
+ }
+ for(i=1; iout, "ERROR: missing %s argument\n",
+ zFile==0 ? "FILE" : "TABLE");
+ return 1;
+ }
+ seenInterrupt = 0;
+ open_db(p, 0);
+ if( useOutputMode ){
+ /* If neither the --csv or --ascii options are specified, then set
+ ** the column and row separator characters from the output mode. */
+ if( p->mode.spec.zColumnSep==0 ){
+ modeSetStr(&p->mode.spec.zColumnSep, ",");
+ nSep = 1;
+ }else if( (nSep = strlen30(p->mode.spec.zColumnSep))==0 ){
+ eputz("Error: non-null column separator required for import\n");
+ return 1;
+ }
+ if( nSep>1 ){
+ eputz("Error: multi-character column separators not allowed"
+ " for import\n");
+ return 1;
+ }
+ if( p->mode.spec.zRowSep==0 ){
+ modeSetStr(&p->mode.spec.zRowSep, "\n");
+ nSep = 1;
+ }else if( (nSep = strlen30(p->mode.spec.zRowSep))==0 ){
+ eputz("Error: non-null row separator required for import\n");
+ return 1;
+ }
+ if( nSep==2 && p->mode.eMode==MODE_Csv
+ && cli_strcmp(p->mode.spec.zRowSep,SEP_CrLf)==0
+ ){
+ /* When importing CSV (only), if the row separator is set to the
+ ** default output row separator, change it to the default input
+ ** row separator. This avoids having to maintain different input
+ ** and output row separators. */
+ modeSetStr(&p->mode.spec.zRowSep, SEP_Row);
+ nSep = strlen30(p->mode.spec.zRowSep);
+ }
+ if( nSep>1 ){
+ eputz("Error: multi-character row separators not allowed"
+ " for import\n");
+ return 1;
+ }
+ sCtx.cColSep = (u8)p->mode.spec.zColumnSep[0];
+ sCtx.cRowSep = (u8)p->mode.spec.zRowSep[0];
+ }
+ sCtx.zFile = zFile;
+ sCtx.nLine = 1;
+ if( sCtx.zFile[0]=='|' ){
+#ifdef SQLITE_OMIT_POPEN
+ eputz("Error: pipes are not supported in this OS\n");
+ return 1;
+#else
+ sCtx.in = sqlite3_popen(sCtx.zFile+1, "r");
+ sCtx.zFile = "";
+ sCtx.xCloser = pclose;
+#endif
+ }else{
+ sCtx.in = sqlite3_fopen(sCtx.zFile, "rb");
+ sCtx.xCloser = fclose;
+ }
+ if( sCtx.in==0 ){
+ cli_printf(stderr,"Error: cannot open \"%s\"\n", zFile);
+ return 1;
+ }
+ if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){
+ char zSep[2];
+ zSep[1] = 0;
+ zSep[0] = sCtx.cColSep;
+ cli_puts("Column separator ", p->out);
+ output_c_string(p->out, zSep);
+ cli_puts(", row separator ", p->out);
+ zSep[0] = sCtx.cRowSep;
+ output_c_string(p->out, zSep);
+ cli_puts("\n", p->out);
+ }
+ sCtx.z = sqlite3_malloc64(120);
+ if( sCtx.z==0 ){
+ import_cleanup(&sCtx);
+ shell_out_of_memory();
+ }
+ /* Below, resources must be freed before exit. */
+ while( nSkip>0 ){
+ nSkip--;
+ while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){}
+ }
+ import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */
+ if( sqlite3_table_column_metadata(p->db, zSchema, zTable,0,0,0,0,0,0)
+ && 0==db_int(p->db, "SELECT count(*) FROM \"%w\".sqlite_schema"
+ " WHERE name=%Q AND type='view'",
+ zSchema ? zSchema : "main", zTable)
+ ){
+ /* Table does not exist. Create it. */
+ sqlite3 *dbCols = 0;
+ char *zRenames = 0;
+ char *zColDefs;
+ zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"",
+ zSchema ? zSchema : "main", zTable);
+ while( xRead(&sCtx) ){
+ zAutoColumn(sCtx.z, &dbCols, 0);
+ if( sCtx.cTerm!=sCtx.cColSep ) break;
+ }
+ zColDefs = zAutoColumn(0, &dbCols, &zRenames);
+ if( zRenames!=0 ){
+ cli_printf((stdin_is_interactive && p->in==stdin)? p->out : stderr,
+ "Columns renamed during .import %s due to duplicates:\n"
+ "%s\n", sCtx.zFile, zRenames);
+ sqlite3_free(zRenames);
+ }
+ assert(dbCols==0);
+ if( zColDefs==0 ){
+ cli_printf(stderr,"%s: empty file\n", sCtx.zFile);
+ import_cleanup(&sCtx);
+ sqlite3_free(zCreate);
+ return 1;
+ }
+ zCreate = sqlite3_mprintf("%z%z\n", zCreate, zColDefs);
+ if( zCreate==0 ){
+ import_cleanup(&sCtx);
+ shell_out_of_memory();
+ }
+ if( eVerbose>=1 ){
+ cli_printf(p->out, "%s\n", zCreate);
+ }
+ rc = sqlite3_exec(p->db, zCreate, 0, 0, 0);
+ if( rc ){
+ cli_printf(stderr,
+ "%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db));
+ }
+ sqlite3_free(zCreate);
+ zCreate = 0;
+ if( rc ){
+ import_cleanup(&sCtx);
+ return 1;
+ }
+ }
+ zSql = sqlite3_mprintf("SELECT count(*) FROM pragma_table_info(%Q,%Q);",
+ zTable, zSchema);
+ if( zSql==0 ){
+ import_cleanup(&sCtx);
+ shell_out_of_memory();
+ }
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+ sqlite3_free(zSql);
+ zSql = 0;
+ if( rc ){
+ if (pStmt) sqlite3_finalize(pStmt);
+ shellDatabaseError(p->db);
+ import_cleanup(&sCtx);
+ return 1;
+ }
+ if( sqlite3_step(pStmt)==SQLITE_ROW ){
+ nCol = sqlite3_column_int(pStmt, 0);
+ }else{
+ nCol = 0;
+ }
+ sqlite3_finalize(pStmt);
+ pStmt = 0;
+ if( nCol==0 ) return 0; /* no columns, no error */
+
+ nByte = 64 /* space for "INSERT INTO", "VALUES(", ")\0" */
+ + (zSchema ? strlen(zSchema)*2 + 2: 0) /* Quoted schema name */
+ + strlen(zTable)*2 + 2 /* Quoted table name */
+ + nCol*2; /* Space for ",?" for each column */
+ zSql = sqlite3_malloc64( nByte );
+ if( zSql==0 ){
+ import_cleanup(&sCtx);
+ shell_out_of_memory();
+ }
+ if( zSchema ){
+ sqlite3_snprintf(nByte, zSql, "INSERT INTO \"%w\".\"%w\" VALUES(?",
+ zSchema, zTable);
+ }else{
+ sqlite3_snprintf(nByte, zSql, "INSERT INTO \"%w\" VALUES(?", zTable);
+ }
+ j = strlen30(zSql);
+ for(i=1; i=2 ){
+ cli_printf(p->out, "Insert using: %s\n", zSql);
+ }
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+ sqlite3_free(zSql);
+ zSql = 0;
+ if( rc ){
+ shellDatabaseError(p->db);
+ if (pStmt) sqlite3_finalize(pStmt);
+ import_cleanup(&sCtx);
+ return 1;
+ }
+ needCommit = sqlite3_get_autocommit(p->db);
+ if( needCommit ) sqlite3_exec(p->db, "BEGIN", 0, 0, 0);
+ do{
+ int startLine = sCtx.nLine;
+ for(i=0; imode.eMode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break;
+ /*
+ ** For CSV mode, per RFC 4180, accept EOF in lieu of final
+ ** record terminator but only for last field of multi-field row.
+ ** (If there are too few fields, it's not valid CSV anyway.)
+ */
+ if( z==0 && (xRead==csv_read_one_field) && i==nCol-1 && i>0 ){
+ z = "";
+ }
+ sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT);
+ if( i=nCol ){
+ sqlite3_step(pStmt);
+ rc = sqlite3_reset(pStmt);
+ if( rc!=SQLITE_OK ){
+ cli_printf(stderr,"%s:%d: INSERT failed: %s\n",
+ sCtx.zFile, startLine, sqlite3_errmsg(p->db));
+ sCtx.nErr++;
+ }else{
+ sCtx.nRow++;
+ }
+ }
+ }while( sCtx.cTerm!=EOF );
+
+ import_cleanup(&sCtx);
+ sqlite3_finalize(pStmt);
+ if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0);
+ if( eVerbose>0 ){
+ cli_printf(p->out,
+ "Added %d rows with %d errors using %d lines of input\n",
+ sCtx.nRow, sCtx.nErr, sCtx.nLine-1);
+ }
+ return 0;
+}
+
+
/*
** This function computes what to show the user about the configured
** titles (or column-names). Output is an integer between 0 and 3:
@@ -7352,12 +7721,6 @@ static int dotCmdMode(ShellState *p){
modeSetStr(&p->mode.spec.zTableName, azArg[i]);
}
chng = 1;
- }else if( z[0]=='2' && strlen(z)==8 && atoi(z)>20000101 ){
- modeFree(&p->mode);
- p->iCompat = atoi(z);
- memset(&p->mode, 0, sizeof(p->mode));
- modeDefault(p);
- chng = 1;
}else if( optionMatch(z,"align") ){
char *zAlign;
int nAlign;
@@ -7442,7 +7805,7 @@ static int dotCmdMode(ShellState *p){
for(ii=0; iinSavedModes; ii++){
cli_printf(p->out, " %s", p->aSavedModes[ii].zTag);
}
- cli_puts("\n", p->out);
+ cli_puts(" batch tty\n", p->out);
}else if( optionMatch(z,"quote") ){
if( i+1nSavedModes > 100 ){
+ if( p->nSavedModes > MODE_N_USER ){
dotCmdError(p, i-1, "cannot add more modes", 0);
return 1;
}
@@ -7983,7 +8346,7 @@ static int dotCmdOutput(ShellState *p){
if( eMode=='x' ){
/* spreadsheet mode. Output as CSV. */
newTempFile(p, "csv");
- p->mode.bEcho = 0;
+ p->mode.mFlags &= ~MFLG_ECHO;
p->mode.eMode = MODE_Csv;
modeSetStr(&p->mode.spec.zColumnSep, SEP_Comma);
modeSetStr(&p->mode.spec.zRowSep, SEP_CrLf);
@@ -8382,12 +8745,17 @@ static int do_meta_command(const char *zLine, ShellState *p){
){
if( nArg==2 ){
#ifdef _WIN32
- p->mode.crlfMode = booleanValue(azArg[1]);
+ if( booleanValue(azArg[1]) ){
+ p->mode.mFlags |= MFLG_CRLF;
+ }else{
+ p->mode.mFlags &= ~MFLG_CRLF;
+ }
#else
- p->mode.crlfMode = 0;
+ p->mode.mFlags &= ~MFLG_CRLF;
#endif
}
- cli_printf(stderr, "crlf is %s\n", p->mode.crlfMode ? "ON" : "OFF");
+ cli_printf(stderr, "crlf is %s\n",
+ (p->mode.mFlags & MFLG_CRLF)!=0 ? "ON" : "OFF");
}else
if( c=='d' && n>1 && cli_strncmp(azArg[0], "databases", n)==0 ){
@@ -8602,7 +8970,11 @@ static int do_meta_command(const char *zLine, ShellState *p){
if( c=='e' && cli_strncmp(azArg[0], "echo", n)==0 ){
if( nArg==2 ){
- p->mode.bEcho = booleanValue(azArg[1]);
+ if( booleanValue(azArg[1]) ){
+ p->mode.mFlags |= MFLG_ECHO;
+ }else{
+ p->mode.mFlags &= ~MFLG_ECHO;
+ }
}else{
eputz("Usage: .echo on|off\n");
rc = 1;
@@ -8910,331 +9282,7 @@ static int do_meta_command(const char *zLine, ShellState *p){
#ifndef SQLITE_SHELL_FIDDLE
if( c=='i' && cli_strncmp(azArg[0], "import", n)==0 ){
- char *zTable = 0; /* Insert data into this table */
- char *zSchema = 0; /* Schema of zTable */
- char *zFile = 0; /* Name of file to extra content from */
- sqlite3_stmt *pStmt = NULL; /* A statement */
- int nCol; /* Number of columns in the table */
- i64 nByte; /* Number of bytes in an SQL string */
- int i, j; /* Loop counters */
- int needCommit; /* True to COMMIT or ROLLBACK at end */
- int nSep; /* Number of bytes in spec.zColumnSep */
- char *zSql = 0; /* An SQL statement */
- ImportCtx sCtx; /* Reader context */
- char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */
- int eVerbose = 0; /* Larger for more console output */
- i64 nSkip = 0; /* Initial lines to skip */
- int useOutputMode = 1; /* Use output mode to determine separators */
- char *zCreate = 0; /* CREATE TABLE statement text */
-
- failIfSafeMode(p, "cannot run .import in safe mode");
- memset(&sCtx, 0, sizeof(sCtx));
- if( p->mode.eMode==MODE_Ascii ){
- xRead = ascii_read_one_field;
- }else{
- xRead = csv_read_one_field;
- }
- rc = 1;
- for(i=1; iout, "ERROR: extra argument: \"%s\". Usage:\n",z);
- showHelp(p->out, "import");
- goto meta_command_exit;
- }
- }else if( cli_strcmp(z,"-v")==0 ){
- eVerbose++;
- }else if( cli_strcmp(z,"-schema")==0 && iout, "ERROR: unknown option: \"%s\". Usage:\n", z);
- showHelp(p->out, "import");
- goto meta_command_exit;
- }
- }
- if( zTable==0 ){
- cli_printf(p->out, "ERROR: missing %s argument. Usage:\n",
- zFile==0 ? "FILE" : "TABLE");
- showHelp(p->out, "import");
- goto meta_command_exit;
- }
- seenInterrupt = 0;
- open_db(p, 0);
- if( useOutputMode ){
- /* If neither the --csv or --ascii options are specified, then set
- ** the column and row separator characters from the output mode. */
- if( p->mode.spec.zColumnSep==0 ){
- modeSetStr(&p->mode.spec.zColumnSep, ",");
- nSep = 1;
- }else if( (nSep = strlen30(p->mode.spec.zColumnSep))==0 ){
- eputz("Error: non-null column separator required for import\n");
- goto meta_command_exit;
- }
- if( nSep>1 ){
- eputz("Error: multi-character column separators not allowed"
- " for import\n");
- goto meta_command_exit;
- }
- if( p->mode.spec.zRowSep==0 ){
- modeSetStr(&p->mode.spec.zRowSep, "\n");
- nSep = 1;
- }else if( (nSep = strlen30(p->mode.spec.zRowSep))==0 ){
- eputz("Error: non-null row separator required for import\n");
- goto meta_command_exit;
- }
- if( nSep==2 && p->mode.eMode==MODE_Csv
- && cli_strcmp(p->mode.spec.zRowSep,SEP_CrLf)==0
- ){
- /* When importing CSV (only), if the row separator is set to the
- ** default output row separator, change it to the default input
- ** row separator. This avoids having to maintain different input
- ** and output row separators. */
- modeSetStr(&p->mode.spec.zRowSep, SEP_Row);
- nSep = strlen30(p->mode.spec.zRowSep);
- }
- if( nSep>1 ){
- eputz("Error: multi-character row separators not allowed"
- " for import\n");
- goto meta_command_exit;
- }
- sCtx.cColSep = (u8)p->mode.spec.zColumnSep[0];
- sCtx.cRowSep = (u8)p->mode.spec.zRowSep[0];
- }
- sCtx.zFile = zFile;
- sCtx.nLine = 1;
- if( sCtx.zFile[0]=='|' ){
-#ifdef SQLITE_OMIT_POPEN
- eputz("Error: pipes are not supported in this OS\n");
- goto meta_command_exit;
-#else
- sCtx.in = sqlite3_popen(sCtx.zFile+1, "r");
- sCtx.zFile = "";
- sCtx.xCloser = pclose;
-#endif
- }else{
- sCtx.in = sqlite3_fopen(sCtx.zFile, "rb");
- sCtx.xCloser = fclose;
- }
- if( sCtx.in==0 ){
- cli_printf(stderr,"Error: cannot open \"%s\"\n", zFile);
- goto meta_command_exit;
- }
- if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){
- char zSep[2];
- zSep[1] = 0;
- zSep[0] = sCtx.cColSep;
- cli_puts("Column separator ", p->out);
- output_c_string(p->out, zSep);
- cli_puts(", row separator ", p->out);
- zSep[0] = sCtx.cRowSep;
- output_c_string(p->out, zSep);
- cli_puts("\n", p->out);
- }
- sCtx.z = sqlite3_malloc64(120);
- if( sCtx.z==0 ){
- import_cleanup(&sCtx);
- shell_out_of_memory();
- }
- /* Below, resources must be freed before exit. */
- while( nSkip>0 ){
- nSkip--;
- while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){}
- }
- import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */
- if( sqlite3_table_column_metadata(p->db, zSchema, zTable,0,0,0,0,0,0)
- && 0==db_int(p->db, "SELECT count(*) FROM \"%w\".sqlite_schema"
- " WHERE name=%Q AND type='view'",
- zSchema ? zSchema : "main", zTable)
- ){
- /* Table does not exist. Create it. */
- sqlite3 *dbCols = 0;
- char *zRenames = 0;
- char *zColDefs;
- zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"",
- zSchema ? zSchema : "main", zTable);
- while( xRead(&sCtx) ){
- zAutoColumn(sCtx.z, &dbCols, 0);
- if( sCtx.cTerm!=sCtx.cColSep ) break;
- }
- zColDefs = zAutoColumn(0, &dbCols, &zRenames);
- if( zRenames!=0 ){
- cli_printf((stdin_is_interactive && p->in==stdin)? p->out : stderr,
- "Columns renamed during .import %s due to duplicates:\n"
- "%s\n", sCtx.zFile, zRenames);
- sqlite3_free(zRenames);
- }
- assert(dbCols==0);
- if( zColDefs==0 ){
- cli_printf(stderr,"%s: empty file\n", sCtx.zFile);
- import_cleanup(&sCtx);
- rc = 1;
- sqlite3_free(zCreate);
- goto meta_command_exit;
- }
- zCreate = sqlite3_mprintf("%z%z\n", zCreate, zColDefs);
- if( zCreate==0 ){
- import_cleanup(&sCtx);
- shell_out_of_memory();
- }
- if( eVerbose>=1 ){
- cli_printf(p->out, "%s\n", zCreate);
- }
- rc = sqlite3_exec(p->db, zCreate, 0, 0, 0);
- if( rc ){
- cli_printf(stderr,
- "%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db));
- }
- sqlite3_free(zCreate);
- zCreate = 0;
- if( rc ){
- import_cleanup(&sCtx);
- rc = 1;
- goto meta_command_exit;
- }
- }
- zSql = sqlite3_mprintf("SELECT count(*) FROM pragma_table_info(%Q,%Q);",
- zTable, zSchema);
- if( zSql==0 ){
- import_cleanup(&sCtx);
- shell_out_of_memory();
- }
- rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
- sqlite3_free(zSql);
- zSql = 0;
- if( rc ){
- if (pStmt) sqlite3_finalize(pStmt);
- shellDatabaseError(p->db);
- import_cleanup(&sCtx);
- rc = 1;
- goto meta_command_exit;
- }
- if( sqlite3_step(pStmt)==SQLITE_ROW ){
- nCol = sqlite3_column_int(pStmt, 0);
- }else{
- nCol = 0;
- }
- sqlite3_finalize(pStmt);
- pStmt = 0;
- if( nCol==0 ) return 0; /* no columns, no error */
-
- nByte = 64 /* space for "INSERT INTO", "VALUES(", ")\0" */
- + (zSchema ? strlen(zSchema)*2 + 2: 0) /* Quoted schema name */
- + strlen(zTable)*2 + 2 /* Quoted table name */
- + nCol*2; /* Space for ",?" for each column */
- zSql = sqlite3_malloc64( nByte );
- if( zSql==0 ){
- import_cleanup(&sCtx);
- shell_out_of_memory();
- }
- if( zSchema ){
- sqlite3_snprintf(nByte, zSql, "INSERT INTO \"%w\".\"%w\" VALUES(?",
- zSchema, zTable);
- }else{
- sqlite3_snprintf(nByte, zSql, "INSERT INTO \"%w\" VALUES(?", zTable);
- }
- j = strlen30(zSql);
- for(i=1; i=2 ){
- cli_printf(p->out, "Insert using: %s\n", zSql);
- }
- rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
- sqlite3_free(zSql);
- zSql = 0;
- if( rc ){
- shellDatabaseError(p->db);
- if (pStmt) sqlite3_finalize(pStmt);
- import_cleanup(&sCtx);
- rc = 1;
- goto meta_command_exit;
- }
- needCommit = sqlite3_get_autocommit(p->db);
- if( needCommit ) sqlite3_exec(p->db, "BEGIN", 0, 0, 0);
- do{
- int startLine = sCtx.nLine;
- for(i=0; imode.eMode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break;
- /*
- ** For CSV mode, per RFC 4180, accept EOF in lieu of final
- ** record terminator but only for last field of multi-field row.
- ** (If there are too few fields, it's not valid CSV anyway.)
- */
- if( z==0 && (xRead==csv_read_one_field) && i==nCol-1 && i>0 ){
- z = "";
- }
- sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT);
- if( i=nCol ){
- sqlite3_step(pStmt);
- rc = sqlite3_reset(pStmt);
- if( rc!=SQLITE_OK ){
- cli_printf(stderr,"%s:%d: INSERT failed: %s\n",
- sCtx.zFile, startLine, sqlite3_errmsg(p->db));
- sCtx.nErr++;
- }else{
- sCtx.nRow++;
- }
- }
- }while( sCtx.cTerm!=EOF );
-
- import_cleanup(&sCtx);
- sqlite3_finalize(pStmt);
- if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0);
- if( eVerbose>0 ){
- cli_printf(p->out,
- "Added %d rows with %d errors using %d lines of input\n",
- sCtx.nRow, sCtx.nErr, sCtx.nLine-1);
- }
+ rc = dotCmdImport(p);
}else
#endif /* !defined(SQLITE_SHELL_FIDDLE) */
@@ -10629,7 +10677,8 @@ static int do_meta_command(const char *zLine, ShellState *p){
rc = 1;
goto meta_command_exit;
}
- cli_printf(p->out, "%12.12s: %s\n","echo", azBool[p->mode.bEcho!=0]);
+ cli_printf(p->out, "%12.12s: %s\n","echo",
+ azBool[(p->mode.mFlags & MFLG_ECHO)!=0]);
cli_printf(p->out, "%12.12s: %s\n","eqp", azBool[p->mode.autoEQP&3]);
cli_printf(p->out, "%12.12s: %s\n","explain",
p->mode.autoExplain ? "auto" : "off");
@@ -10784,7 +10833,7 @@ static int do_meta_command(const char *zLine, ShellState *p){
len = strlen30(azResult[i]);
if( len>maxlen ) maxlen = len;
}
- nPrintCol = 80/(maxlen+2);
+ nPrintCol = shellScreenWidth()/(maxlen+2);
if( nPrintCol<1 ) nPrintCol = 1;
nPrintRow = (nRow + nPrintCol - 1)/nPrintCol;
for(i=0; imode.bEcho ){
+ if( p->mode.mFlags & MFLG_ECHO ){
cli_printf(p->out, "%s\n", zDo);
fflush(p->out);
}
@@ -12175,6 +12224,7 @@ static const char zOptions[] =
" -quote set output mode to 'quote'\n"
" -readonly open the database read-only\n"
" -safe enable safe-mode\n"
+ " -screenwidth N use N as the default screenwidth \n"
" -separator SEP set output column separator. Default: '|'\n"
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
" -sorterref SIZE sorter references threshold size\n"
@@ -12476,6 +12526,15 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
** we do the actual processing of arguments later in a second pass.
*/
stdin_is_interactive = 0;
+ stdout_is_console = 0;
+ modeChange(&data, MODE_BATCH);
+ }else if( cli_strcmp(z,"-screenwidth")==0 ){
+ int n = atoi(cmdline_option_value(argc, argv, ++i));
+ if( n<2 ){
+ sqlite3_fprintf(stderr,"minimum --screenwidth is 2\n");
+ exit(1);
+ }
+ stdout_tty_width = n;
}else if( cli_strcmp(z,"-compat")==0 ){
data.iCompat = atoi(cmdline_option_value(argc, argv, ++i));
modeFree(&data.mode);
@@ -12761,7 +12820,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
}else if( cli_strcmp(z,"-noheader")==0 ){
data.mode.spec.bTitles = QRF_No;
}else if( cli_strcmp(z,"-echo")==0 ){
- data.mode.bEcho = 1;
+ data.mode.mFlags |= MFLG_ECHO;
}else if( cli_strcmp(z,"-eqp")==0 ){
data.mode.autoEQP = AUTOEQP_on;
}else if( cli_strcmp(z,"-eqpfull")==0 ){
@@ -12790,6 +12849,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
stdin_is_interactive = 1;
}else if( cli_strcmp(z,"-batch")==0 ){
/* already handled */
+ }else if( cli_strcmp(z,"-screenwidth")==0 ){
+ i++;
}else if( cli_strcmp(z,"-compat")==0 ){
i++;
}else if( cli_strcmp(z,"-utf8")==0 ){
diff --git a/test/qrf01.test b/test/qrf01.test
index a9f6d4c83d..c79a32defe 100644
--- a/test/qrf01.test
+++ b/test/qrf01.test
@@ -132,15 +132,24 @@ do_test 1.50 {
db format -style count {SELECT * FROM t1}
} 2
-do_test 1.60 {
- db format -style csv {SELECT * FROM t1}
+do_test 1.60a {
+ db format -style list -columnsep , -rowsep \r\n -text csv -blob tcl {SELECT * FROM t1}
} "1,2.5,three\r\n\"\\102\\114\\117\\102\",,\"á¼Î¼Î®Î½\"\r\n"
-do_test 1.61 {
- db format -style csv -title auto {SELECT * FROM t1}
+do_test 1.60b {
+ db format -style csv -columnsep xyz -rowsep pqr -text sql -blob sql {SELECT * FROM t1}
+} "1,2.5,three\r\nBLOB,,\"á¼Î¼Î®Î½\"\r\n"
+do_test 1.61a {
+ db format -style list -columnsep , -rowsep \r\n -text csv -title auto -blob tcl {SELECT * FROM t1}
} "a,b,c\r\n1,2.5,three\r\n\"\\102\\114\\117\\102\",,\"á¼Î¼Î®Î½\"\r\n"
-do_test 1.62 {
- db format -style csv -title csv {SELECT a AS 'a x y', b, c FROM t1}
+do_test 1.61b {
+ db format -style csv -title auto -blob tcl {SELECT * FROM t1}
+} "a,b,c\r\n1,2.5,three\r\nBLOB,,\"á¼Î¼Î®Î½\"\r\n"
+do_test 1.62a {
+ db format -style list -columnsep , -rowsep \r\n -text csv -title csv -blob tcl {SELECT a AS 'a x y', b, c FROM t1}
} "\"a x y\",b,c\r\n1,2.5,three\r\n\"\\102\\114\\117\\102\",,\"á¼Î¼Î®Î½\"\r\n"
+do_test 1.62b {
+ db format -style csv -title csv -blob tcl {SELECT a AS 'a x y', b, c FROM t1}
+} "\"a x y\",b,c\r\n1,2.5,three\r\nBLOB,,\"á¼Î¼Î®Î½\"\r\n"
do_test 1.70 {
set result "\n[db format -style html {SELECT * FROM t1}]"
diff --git a/test/shell1.test b/test/shell1.test
index 3f0e19e7be..1a502336c3 100644
--- a/test/shell1.test
+++ b/test/shell1.test
@@ -426,7 +426,8 @@ do_test shell1-3.11.2 {
do_test shell1-3.11.3 {
# too many arguments
catchcmd "test.db" ".import FOO BAR BAD"
-} {/1 .ERROR: extra argument: "BAD".*./}
+} {1 {line 1: .import FOO BAR BAD
+line 1: ^--- unknown argument}}
# .indexes ?TABLE? Show names of all indexes
# If TABLE specified, only show indexes for tables
@@ -456,7 +457,7 @@ do_test shell1-3.12.3 {
# tabs Tab-separated values
# tcl TCL list elements
do_test shell1-3.13.1 {
- catchcmd "test.db" ".mode 20250101\n.mode"
+ catchcmd "test.db" ".mode batch\n.mode"
} {0 {current output mode: list}}
do_test shell1-3.13.2 {
catchcmd "test.db" ".mode FOO"
@@ -510,7 +511,7 @@ do_test shell1-3.15.1 {
.print x"
} {0 x}
do_test shell1-3.15.2 {
- catchcmd "test.db" ".mode 20250101\n.output FOO
+ catchcmd "test.db" ".mode batch\n.output FOO
.print x
.output
SELECT readfile('FOO');"
@@ -632,7 +633,7 @@ do_test shell1-3.22.4 {
# .show Show the current values for various settings
do_test shell1-3.23.1 {
- set res [catchcmd "test.db" ".mode 20250101\n.show"]
+ set res [catchcmd "test.db" ".mode batch\n.show"]
list [regexp {echo:} $res] \
[regexp {explain:} $res] \
[regexp {headers:} $res] \
@@ -668,7 +669,7 @@ do_test shell1-3.23b.4 {
# Adverse interaction between .stats and .eqp
#
do_test shell1-3.23b.5 {
- catchcmd "test.db" [string map {"\n " "\n"} {.mode 20250101
+ catchcmd "test.db" [string map {"\n " "\n"} {.mode batch
CREATE TEMP TABLE t1(x);
INSERT INTO t1 VALUES(1),(2);
.stats on
@@ -753,7 +754,7 @@ do_test shell1-3.27.4 {
do_test shell1-3.28.1 {
catchcmd test.db \
- ".mode 20250101\n.log stdout\nSELECT coalesce(sqlite_log(123,'hello'),'456');"
+ ".mode batch\n.log stdout\nSELECT coalesce(sqlite_log(123,'hello'),'456');"
} "0 {(123) hello\n456}"
do_test shell1-3-29.1 {
@@ -1263,7 +1264,7 @@ do_test shell1-7.1.7 {
# information.
#
do_test shell1-8.1 {
- catchcmd ":memory:" {.mode 20250101
+ catchcmd ":memory:" {.mode batch
-- The pow2 table will hold all the necessary powers of two.
CREATE TABLE pow2(x INTEGER PRIMARY KEY, v TEXT);
WITH RECURSIVE c(x,v) AS (
diff --git a/test/shell2.test b/test/shell2.test
index dcf2942e18..274f758e2f 100644
--- a/test/shell2.test
+++ b/test/shell2.test
@@ -75,7 +75,7 @@ do_test shell2-1.3 {
# NB. whitespace is important
do_test shell2-1.4.1 {
forcedelete foo.db
- catchcmd "foo.db" {.mode 20250101
+ catchcmd "foo.db" {.mode batch
CREATE TABLE foo(a);
INSERT INTO foo(a) VALUES(1);
SELECT * FROM foo;}
@@ -98,7 +98,7 @@ SELECT * FROM foo;
do_test shell2-1.4.3 {
forcedelete foo.db
catchcmd "foo.db" {
-.mode 20250101
+.mode batch
.echo ON
CREATE TABLE foo(a);
INSERT INTO foo(a) VALUES(1);
@@ -114,7 +114,7 @@ SELECT * FROM foo;
do_test shell2-1.4.4 {
forcedelete foo.db
catchcmd "foo.db" {
-.mode 20250101
+.mode batch
.echo ON
CREATE TABLE foo(a);
.echo OFF
@@ -130,7 +130,7 @@ SELECT * FROM foo;}
do_test shell2-1.4.5 {
forcedelete foo.db
catchcmdex "foo.db" {
-.mode 20250101
+.mode batch
.echo ON
CREATE TABLE foo1(a);
INSERT INTO foo1(a) VALUES(1);
@@ -161,7 +161,7 @@ SELECT * FROM foo1; SELECT * FROM foo2;
do_test shell2-1.4.6 {
forcedelete foo.db
catchcmdex "foo.db" {
-.mode 20250101
+.mode batch
.echo ON
.headers ON
CREATE TABLE foo1(a);
@@ -217,7 +217,7 @@ do_test shell2-1.4.9 {
do_test shell2-1.4.9 {
forcedelete clone.db
set res [catchcmd :memory: [string trim {
-.mode 20250101
+.mode batch
CREATE TABLE t(id INTEGER PRIMARY KEY AUTOINCREMENT);
INSERT INTO t VALUES (1),(2);
.clone clone.db
@@ -232,7 +232,7 @@ ifcapable vtab {
# See overflow report at https://sqlite.org/forum/forumpost/5d34ce5280
do_test shell2-1.4.10 {
set res [catchcmd :memory: [string trim {
- .mode 20250101
+ .mode batch
SELECT * FROM generate_series(9223372036854775807,9223372036854775807,1);
SELECT * FROM generate_series(9223372036854775807,9223372036854775807,-1);
SELECT avg(value),min(value),max(value) FROM generate_series(
@@ -261,7 +261,7 @@ do_test shell2-1.4.10 {
2}}
do_test shell2-1.4.10b {
set res [catchcmd :memory: [string trim {
- .mode 20251116
+ .mode tty
.print
SELECT * FROM generate_series(9223372036854775807,9223372036854775807,1);
SELECT * FROM generate_series(9223372036854775807,9223372036854775807,-1);
@@ -340,7 +340,7 @@ do_test shell2-1.4.11 {
# Bug from forum post 7cbe081746dd3803
# Keywords as column names were producing an error message.
do_test shell2-1.4.12 {
- set res [catchcmd :memory: [string trim {.mode 20250101
+ set res [catchcmd :memory: [string trim {.mode batch
CREATE TABLE "group"("order" text);
INSERT INTO "group" VALUES ('ABC');
.sha3sum}]]
diff --git a/test/shell5.test b/test/shell5.test
index de64c3198a..2c8181579b 100644
--- a/test/shell5.test
+++ b/test/shell5.test
@@ -40,7 +40,8 @@ do_test shell5-1.1.2 {
do_test shell5-1.1.3 {
# too many arguments
catchcmd "test.db" ".import FOO BAR BAD"
-} {/1 .ERROR: extra argument.*/}
+} {1 {line 1: .import FOO BAR BAD
+line 1: ^--- unknown argument}}
# .separator STRING Change separator used by output mode and .import
do_test shell5-1.2.1 {