From: drh <> Date: Wed, 12 Nov 2025 12:44:02 +0000 (+0000) Subject: Rename QRF_STYLE_JsonLine to QRF_STYLE_JObject. Add new convenience modes X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3b847a404057109b281bc5be89f268584561e9af;p=thirdparty%2Fsqlite.git Rename QRF_STYLE_JsonLine to QRF_STYLE_JObject. Add new convenience modes to the CLI and generally refactor and improve the ".mode" command. Some tests are failing, mostly due to changes in error message output and similar. FossilOrigin-Name: 4c8109682e3a224fd8e015e73a9c1c7b6d56ad747d7ca7bc387bb92e1c98f731 --- diff --git a/ext/qrf/qrf.c b/ext/qrf/qrf.c index c8382e8ef1..8338061ea7 100644 --- a/ext/qrf/qrf.c +++ b/ext/qrf/qrf.c @@ -1779,7 +1779,7 @@ static int qrf_need_quote(const char *zName){ } /* -** Helper function for QRF_STYLE_Json and QRF_STYLE_JsonLine. +** Helper function for QRF_STYLE_Json and QRF_STYLE_JObject. ** The initial "{" for a JSON object that will contain row content ** has been output. Now output all the content. */ @@ -1819,7 +1819,7 @@ static void qrfOneSimpleRow(Qrf *p){ qrfOneJsonRow(p); break; } - case QRF_STYLE_JsonLine: { + case QRF_STYLE_JObject: { if( p->nRow==0 ){ sqlite3_str_append(p->pOut, "{", 1); }else{ @@ -2009,7 +2009,7 @@ qrf_reinit: if( p->spec.zRowSep==0 ) p->spec.zRowSep = "\n"; break; } - case QRF_STYLE_JsonLine: + case QRF_STYLE_JObject: case QRF_STYLE_Json: { p->spec.eText = QRF_TEXT_Json; p->spec.eBlob = QRF_BLOB_Json; @@ -2129,7 +2129,7 @@ static void qrfFinalize(Qrf *p){ qrfWrite(p); break; } - case QRF_STYLE_JsonLine: { + case QRF_STYLE_JObject: { sqlite3_str_append(p->pOut, "}\n", 2); qrfWrite(p); break; diff --git a/ext/qrf/qrf.h b/ext/qrf/qrf.h index a353ea055f..169fb4d26f 100644 --- a/ext/qrf/qrf.h +++ b/ext/qrf/qrf.h @@ -83,7 +83,7 @@ int sqlite3_format_query_result( #define QRF_STYLE_Html 7 /* Generate an XHTML table */ #define QRF_STYLE_Insert 8 /* Generate SQL "insert" statements */ #define QRF_STYLE_Json 9 /* Output is a list of JSON objects */ -#define QRF_STYLE_JsonLine 10 /* Independent JSON objects for each row */ +#define QRF_STYLE_JObject 10 /* Independent JSON objects for each row */ #define QRF_STYLE_Line 11 /* One column per line. */ #define QRF_STYLE_List 12 /* One record per line with a separator */ #define QRF_STYLE_Markdown 13 /* Markdown formatting */ diff --git a/manifest b/manifest index aff6dfd237..f577231cd4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Minor\srefactoring\sof\ssome\sfield\snames\sin\sthe\ssqlite3_qrf_spec\sobject,\sto\stry\nto\smake\sthe\snames\smore\sintuitive,\smemorable,\sand\ssuccinct. -D 2025-11-11T18:07:42.284 +C Rename\sQRF_STYLE_JsonLine\sto\sQRF_STYLE_JObject.\s\sAdd\snew\sconvenience\smodes\nto\sthe\sCLI\sand\sgenerally\srefactor\sand\simprove\sthe\s".mode"\scommand.\s\sSome\ntests\sare\sfailing,\smostly\sdue\sto\schanges\sin\serror\smessage\soutput\sand\ssimilar. +D 2025-11-12T12:44:02.867 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -417,8 +417,8 @@ F ext/misc/windirent.h 02211ce51f3034c675f2dbf4d228194d51b3ee05734678bad5106fff6 F ext/misc/zipfile.c 09e6e3a3ff40a99677de3c0bc6569bd5f4709b1844ac3d1c1452a456c5a62f1c F ext/misc/zorder.c bddff2e1b9661a90c95c2a9a9c7ecd8908afab5763256294dd12d609d4664eee F ext/qrf/README.md fee287e4ee6be54bc6d43af7df2328bf18395178c40385c7d9914c0f2781333b -F ext/qrf/qrf.c 2ab94743a9caf24cacc9d8614dd074195f7675aeac7b30acbb6dd046bbc9c75e -F ext/qrf/qrf.h 02d9f6293c5974492b531a8e8afb2b332a3b1ac1644a3591328bfe135c2011e1 +F ext/qrf/qrf.c 259d4391d68c865f11eb36f65c8c89da7d9aa0ff2de9a507d9470cb13b7c6e7f +F ext/qrf/qrf.h b4b3489b3b3683523fd248d15cf5945830643b036943efacdb772a3e00367aa2 F ext/rbu/rbu.c 801450b24eaf14440d8fd20385aacc751d5c9d6123398df41b1b5aa804bf4ce8 F ext/rbu/rbu1.test 25870dd7db7eb5597e2b4d6e29e7a7e095abf332660f67d89959552ce8f8f255 F ext/rbu/rbu10.test 7c22caa32c2ff26983ca8320779a31495a6555737684af7aba3daaf762ef3363 @@ -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 0bf3c5b49f639e5f68e19ee8504ac466fa49009aabe4a17cc22bd21125d641ab +F src/shell.c.in ea1a9f9eb07db0d762b2ae12d23988fdc09360f4f6beac324a2a226d9fd5f740 F src/sqlite.h.in 7403a952a8f1239de7525b73c4e3a0f9540ec0607ed24fec887f5832642d44b8 F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479 F src/sqlite3ext.h 7f236ca1b175ffe03316d974ef57df79b3938466c28d2f95caef5e08c57f3a52 @@ -743,7 +743,7 @@ F src/sqliteInt.h 88f7fc9ce1630d9a5f7e0a8e1f3287cdc63882fba985c18e7eee1b9f457f59 F src/sqliteLimit.h fe70bd8983e5d317a264f2ea97473b359faf3ebb0827877a76813f5cf0cdc364 F src/status.c 7565d63a79aa2f326339a24a0461a60096d0bd2bce711fefb50b5c89335f3592 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 -F src/tclsqlite.c 7f65beb073a25c2db9a56c0bb4be5c66718763fe4d97b807ec265dc294e591a4 +F src/tclsqlite.c 381384fbe3cf342115f9ad01208fa81092e9a2156a4ea4d44de87852b8df3a8a F src/tclsqlite.h 614b3780a62522bc9f8f2b9fb22689e8009958e7aa77e572d0f3149050af348a F src/test1.c d27c91455865fb191eb1b2c892e7586c5e3d9d3977f54913c8e70e2e8e5148b3 F src/test2.c 62f0830958f9075692c29c6de51b495ae8969e1bef85f239ffcd9ba5fb44a5ff @@ -1506,7 +1506,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 ad05382a6693bcd99147857babce976f52e51cffb8508a3aca1d16c08c5d3797 +F test/qrf01.test 8e10a9d5f69db76666191899d0f3e48bf1e1ea55d81905ebb1a3e59aaf9388fc F test/qrf02.test 39b4afdc000bedccdafc0aecf17638df67a67aaa2d2942865ae6abcc48ba0e92 F test/queryonly.test 5f653159e0f552f0552d43259890c1089391dcca F test/quick.test 1681febc928d686362d50057c642f77a02c62e57 @@ -2173,8 +2173,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 ddd167044753f5215624fc9ee0e3657836c528f23a6a3e262401fd1b64cdbb21 -R 55e92c2a04b40f143479b945540fb2e9 +P 3450bc7eecb717abedbaeb56dc824e14eb35ed30322fe3dd3b4b1cbd5fd14c60 +R bae867483b903590efb761aa22052bac U drh -Z 069f6c588dd2080df4a98b06586c707c +Z c473f1769dc85c25ae73a4d9b1c09693 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b2260f4f2f..211a64b0c3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3450bc7eecb717abedbaeb56dc824e14eb35ed30322fe3dd3b4b1cbd5fd14c60 +4c8109682e3a224fd8e015e73a9c1c7b6d56ad747d7ca7bc387bb92e1c98f731 diff --git a/src/shell.c.in b/src/shell.c.in index c8ae7c2438..2ee7730a28 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -1366,9 +1366,10 @@ static ShellState shellState; ** top-level SQL statement */ #define SHELL_PROGRESS_ONCE 0x04 /* Cancel the --limit after firing once */ -/* Names of values for Mode.spec.eEsc. +/* Names of values for Mode.spec.eEsc and Mode.spec.eText */ static const char *qrfEscNames[] = { "auto", "off", "ascii", "symbol" }; +static const char *qrfQuoteNames[] = { "off","off","sql","hex", "tcl", "json"}; /* ** These are the allowed shellFlgs values @@ -1394,61 +1395,77 @@ static const char *qrfEscNames[] = { "auto", "off", "ascii", "symbol" }; ** between these values and the Mode.spec.eStyle values, but they are not ** one-to-one, and thus need to be tracked separately. */ -#define MODE_Line 0 /* One column per line. Blank line between records */ -#define MODE_Column 1 /* One record per line in neat columns */ -#define MODE_List 2 /* One record per line with a separator */ -#define MODE_Html 3 /* Generate an XHTML table */ -#define MODE_Insert 4 /* Generate SQL "insert" statements */ -#define MODE_Quote 5 /* Quote values as for SQL */ -#define MODE_Tcl 6 /* Generate ANSI-C or TCL quoted elements */ -#define MODE_Csv 7 /* Quote strings, numbers are plain */ -#define MODE_Ascii 8 /* Use ASCII unit and record separators (0x1F/0x1E) */ -#define MODE_Json 9 /* Output JSON */ -#define MODE_Markdown 10 /* Markdown formatting */ -#define MODE_Table 11 /* MySQL-style table formatting */ -#define MODE_Box 12 /* Unicode box-drawing characters */ -#define MODE_Count 13 /* Output only a count of the rows of output */ +#define MODE_Ascii 0 /* Use ASCII unit and record separators (0x1F/0x1E) */ +#define MODE_Box 1 /* Unicode box-drawing characters */ +#define MODE_C 2 /* Comma-separated list of C-strings */ +#define MODE_Column 3 /* One record per line in neat columns */ +#define MODE_Count 4 /* Output only a count of the rows of output */ +#define MODE_Csv 5 /* Quote strings, numbers are plain */ +#define MODE_Html 6 /* Generate an XHTML table */ +#define MODE_Insert 7 /* Generate SQL "insert" statements */ +#define MODE_JAtom 8 /* Comma-separated list of JSON atoms */ +#define MODE_JObject 9 /* One JSON object per row */ +#define MODE_Json 10 /* Output JSON */ +#define MODE_Line 11 /* One column per line. Blank line between records */ +#define MODE_List 12 /* One record per line with a separator */ +#define MODE_Markdown 13 /* Markdown formatting */ #define MODE_Off 14 /* No query output shown */ -#define MODE_Www 15 /* Full web-page output */ - -static const char *modeDescr[] = { - "line", - "column", - "list", - "html", - "insert", - "quote", - "tcl", - "csv", - "ascii", - "json", - "markdown", - "table", - "box", - "count", - "off", - "www", -}; - -/* This is the translation from Mode.eMode to Mode.spec.eStyle: */ -static const unsigned char aQrfStyle[] = { - /* line */ QRF_STYLE_Line, - /* column */ QRF_STYLE_Column, - /* list */ QRF_STYLE_List, - /* html */ QRF_STYLE_Html, - /* insert */ QRF_STYLE_Insert, - /* quote */ QRF_STYLE_Quote, - /* tcl */ QRF_STYLE_List, - /* csv */ QRF_STYLE_Csv, - /* ascii */ QRF_STYLE_List, - /* json */ QRF_STYLE_Json, - /* markdown */ QRF_STYLE_Markdown, - /* table */ QRF_STYLE_Table, - /* box */ QRF_STYLE_Box, - /* count */ QRF_STYLE_Count, - /* off */ QRF_STYLE_Off, - /* www */ QRF_STYLE_Html, +#define MODE_QBox 15 /* BOX with SQL-quoted content */ +#define MODE_Quote 16 /* Quote values as for SQL */ +#define MODE_Table 17 /* MySQL-style table formatting */ +#define MODE_Tcl 18 /* Space-separated list of TCL strings */ +#define MODE_Tsv 19 /* Tab-separated values */ +#define MODE_Www 20 /* Full web-page output */ + +/* +*/ +typedef struct ModeInfo ModeInfo; +struct ModeInfo { + char zName[9]; /* Symbolic name of the mode */ + unsigned char eCSep; /* Column separator */ + unsigned char eRSep; /* Row separator */ + unsigned char eNull; /* Null representation */ + unsigned char eText; /* Default text encoding */ + unsigned char eBlob; /* Default blob encoding */ + unsigned char bHdr; /* Show headers? */ + unsigned char eTitle; /* Header incoding. */ + unsigned char eStyle; /* Underlying QRF style */ + unsigned char eCx; /* 0: other, 1: line, 2: columnar */ }; +static const char *aModeStr[] = + /* 0 1 2 3 4 5 6 7 8 9 10 11 */ + { 0, "\n", "|", " ", ",", "\r\n", "\036", "\037", "\t", "", "NULL", "null" }; +static const ModeInfo aModeInfo[] = { +/* zName eCSep eRSep eNull eText eBlob bHdr eTitle eStyle eCx */ + { "ascii", 7, 8, 9, 3, 1, 1, 3, 12, 0 }, + { "box", 0, 0, 9, 1, 1, 2, 1, 1, 2 }, + { "c", 4, 1, 2, 5, 4, 1, 5, 12, 0 }, + { "column", 0, 0, 9, 1, 1, 2, 1, 2, 2 }, + { "count", 0, 0, 0, 0, 0, 0, 0, 3, 0 }, + { "csv", 4, 5, 9, 3, 1, 1, 3, 12, 0 }, + { "html", 0, 0, 9, 4, 1, 2, 4, 7, 0 }, + { "insert", 0, 0, 10, 2, 2, 1, 2, 8, 0 }, + { "jatom", 4, 1, 11, 6, 5, 1, 6, 12, 0 }, + { "jobject", 0, 1, 11, 6, 5, 0, 0, 10, 0 }, + { "json", 0, 0, 11, 6, 6, 0, 0, 9, 0 }, + { "line", 0, 1, 9, 1, 1, 0, 0, 11, 1 }, + { "list", 2, 1, 9, 1, 1, 1, 1, 12, 0 }, + { "markdown", 0, 0, 9, 1, 1, 2, 1, 13, 2 }, + { "off", 0, 0, 0, 0, 0, 0, 0, 14, 0 }, + { "qbox", 0, 0, 9, 2, 2, 2, 1, 1, 2 }, + { "quote", 4, 1, 10, 2, 2, 1, 2, 12, 0 }, + { "table", 0, 0, 9, 1, 1, 2, 1, 19, 2 }, + { "tcl", 3, 1, 9, 5, 4, 1, 5, 12, 0 }, + { "tsv", 8, 5, 9, 3, 1, 1, 3, 12, 0 }, + { "www", 0, 0, 9, 4, 1, 2, 4, 7, 0 }, +}; /* | / / \ \ | / / + ** | / / \ \ | / / + ** First three columns These five column use integers that + ** are indexed into correspond to QRF codes. If QRF codes + ** aModeStr[]. change, these columns must change to match. + ** eText and eTitle are QRF_TEXT_.... + ** eBlob is QRF_BLOB_.... bHdr is QRF_SW_.... + ** eStyle is QRF_STYLE_.... */ /* ** These are the column/row/line separators used by the various @@ -1469,22 +1486,6 @@ static const unsigned char aQrfStyle[] = { */ #define MAX_INPUT_NESTING 25 -/* -** Initialize a newly allocate Mode object to reasonable -** defaults. -*/ -static void modeInit(Mode *p){ - memset(p, 0, sizeof(*p)); - p->spec.iVersion = 1; - p->autoExplain = 1; - p->eMode = MODE_List; -#ifdef _WIN32 - p->crlfMode = 1; -#endif - p->spec.zColumnSep = strdup("|"); - p->spec.zRowSep = strdup("\n"); - p->spec.zNull = strdup(""); -} /* ** Clear a display mode, freeing any allocated memory that it @@ -1555,6 +1556,53 @@ static void modeSetStr(char **az, const char *zNew){ } } +/* +** Change the mode to eMode +*/ +static void modeChange(Mode *p, unsigned char eMode){ + const ModeInfo *pI; + assert( eMode>=0 && eModeeMode = eMode; + if( pI->eCSep ) modeSetStr(&p->spec.zColumnSep, aModeStr[pI->eCSep]); + if( pI->eRSep ) modeSetStr(&p->spec.zRowSep, aModeStr[pI->eRSep]); + if( pI->eNull ) modeSetStr(&p->spec.zNull, aModeStr[pI->eNull]); + p->spec.eText = pI->eText; + p->spec.eBlob = pI->eBlob; + p->spec.bTitles = pI->bHdr; + p->spec.eTitle = pI->eTitle; +} + +/* +** Initialize a newly allocate Mode object to reasonable +** defaults. +*/ +static void modeInit(Mode *p){ + memset(p, 0, sizeof(*p)); + p->spec.iVersion = 1; + p->autoExplain = 1; + p->eMode = MODE_List; +#ifdef _WIN32 + p->crlfMode = 1; +#endif + p->spec.zColumnSep = strdup("|"); + p->spec.zRowSep = strdup("\n"); + p->spec.zNull = strdup(""); +} + +/* +** Find the number of a display mode given its name. Return -1 if +** the name does not match any mode. +*/ +static int modeFind(const char *zName){ + int i; + for(i=0; imode.eMode>=0 && pArg->mode.eModemode.eMode]; + assert( pArg->mode.eMode>=0 && pArg->mode.eModemode.eMode].eStyle; #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION) if( pArg->expert.pExpert ){ @@ -7047,22 +7095,63 @@ static int faultsim_callback(int iArg){ return faultsim_state.iErr; } +/* +** pickStr(zArg, &zErr, zS1, zS2, ..., ""); +** +** Try to match zArg against zS1, zS2, and so forth until the first +** emptry string. Return the index of the match or -1 if none is found. +** If no match is found, and &zErr is not NULL, then write into +** zErr a message describing the valid choices. +*/ +static int pickStr(const char *zArg, char **pzErr, ...){ + int i, n; + const char *z; + sqlite3_str *pMsg; + va_list ap; + va_start(ap, pzErr); + i = 0; + while( (z = va_arg(ap,const char*))!=0 && z[0]!=0 ){ + if( cli_strcmp(zArg, z)==0 ) return i; + i++; + } + va_end(ap); + if( pzErr==0 ) return -1; + n = i; + pMsg = sqlite3_str_new(0); + va_start(ap, pzErr); + sqlite3_str_appendall(pMsg, "should be"); + i = 0; + while( (z = va_arg(ap, const char*))!=0 && z[0]!=0 ){ + if( i==n-1 ){ + sqlite3_str_append(pMsg,", or",4); + }else if( i>0 ){ + sqlite3_str_append(pMsg, ",", 1); + } + sqlite3_str_appendf(pMsg, " %s", z); + i++; + } + va_end(ap); + *pzErr = sqlite3_str_finish(pMsg); + return -1; +} + /* ** DOT-COMMAND: .mode ** -** USAGE: .mode [OPTIONS] [MODE] +** USAGE: .mode [MODE] [OPTIONS] ** ** Change the output mode to MODE. And/or apply OPTIONS to the ** output mode. If no arguments, show the current output mode and ** relevant options. */ static int dotCmdMode(ShellState *p){ - int nArg = p->dot.nArg; - char **azArg = p->dot.azArg; - const char *zMode = 0; - const char *zTabname = 0; - int i, n2; - int chng = 0; /* 0x01: change to cmopts. 0x02: Any other change */ + int nArg = p->dot.nArg; /* Number of arguments */ + char **azArg = p->dot.azArg;/* Argument list */ + int eMode = -1; /* New mode value, or -1 for none */ + int iMode = -1; /* Index of the argument that is the mode name */ + int i; /* Loop counter */ + int k; /* Misc index variable */ + int chng = 0; /* True if anything has changed */ for(i=1; iQRF_MAX_WIDTH ) w = QRF_MAX_WIDTH; p->mode.spec.nWrap = w; - chng |= 1; + chng = 1; }else if( optionMatch(z,"ww") ){ p->mode.spec.bWordWrap = QRF_Yes; - chng |= 1; + chng = 1; }else if( optionMatch(z,"wordwrap") ){ if( i+1>=nArg ){ dotCmdError(p, i, "missing argument", 0); return 1; } p->mode.spec.bWordWrap = (u8)booleanValue(azArg[++i]) ? QRF_Yes : QRF_No; - chng |= 1; + chng = 1; }else if( optionMatch(z,"quote") ){ - p->mode.spec.eText = QRF_TEXT_Sql; - p->mode.spec.eBlob = QRF_BLOB_Sql; - chng |= 1; + if( i+10 || modeFind(azArg[i+1])<0) + ){ + /* --quote is followed by an argument other that is not an option + ** or a mode name. See it must be a boolean or a keyword to describe + ** how to set quoting. */ + i++; + if( (k = pickStr(azArg[i],0,"no","yes","0","1",""))>=0 ){ + k &= 1; /* 0 for "off". 1 for "on". */ + }else{ + char *zErr = 0; /* 0 1 2 3 4 5 6 */ + k = pickStr(azArg[i],&zErr,"off","on","sql","csv","html","tcl","json", + ""); + if( k<0 ){ + dotCmdError(p, i, "unknown", "%z", zErr); + return 1; + } + } + }else{ + /* (Legacy) no following boolean argument. Turn quoting on */ + k = 1; + } + switch( k ){ + case 1: /* on */ + case 2: /* sql */ + p->mode.spec.eText = QRF_TEXT_Sql; + p->mode.spec.eBlob = QRF_BLOB_Sql; + break; + case 3: /* csv */ + p->mode.spec.eText = QRF_TEXT_Csv; + p->mode.spec.eBlob = QRF_BLOB_Text; + break; + case 4: /* html */ + p->mode.spec.eText = QRF_TEXT_Html; + p->mode.spec.eBlob = QRF_BLOB_Text; + break; + case 5: /* tcl */ + p->mode.spec.eText = QRF_TEXT_Tcl; + p->mode.spec.eBlob = QRF_BLOB_Text; + break; + case 6: /* json */ + p->mode.spec.eText = QRF_TEXT_Json; + p->mode.spec.eBlob = QRF_BLOB_Json; + break; + default: /* off */ + p->mode.spec.eText = QRF_TEXT_Plain; + p->mode.spec.eBlob = QRF_BLOB_Text; + break; + } + chng = 1; + }else if( optionMatch(z,"noquote") ){ + /* (undocumented legacy) --noquote always turns quoting off */ + p->mode.spec.eText = QRF_TEXT_Plain; + p->mode.spec.eBlob = QRF_BLOB_Text; + chng = 1; }else if( optionMatch(z,"textjsonb") ){ if( i+1>=nArg ){ dotCmdError(p, i, "missing argument", 0); return 1; } p->mode.spec.bTextJsonb = (u8)booleanValue(azArg[++i]) ? QRF_Yes : QRF_No; - chng |= 1; - }else if( optionMatch(z,"noquote") ){ - p->mode.spec.eText = QRF_Auto; - p->mode.spec.eBlob = QRF_Auto; - chng |= 1; + chng = 1; }else if( optionMatch(z,"escape") ){ /* See similar code at tag-20250224-1 */ + char *zErr = 0; if( i+1>=nArg ){ dotCmdError(p, i, "missing argument", 0); return 1; } - const char *zEsc = azArg[++i]; - int k; - for(k=0; kmode.spec.eEsc = k; - chng |= 2; - break; - } + i++; /* 0 1 2 <-- One less than QRF_ESC_ */ + k = pickStr(azArg[i],&zErr,"off","ascii","symbol",""); + if( k<0 ){ + dotCmdError(p, i, "unknown escape type", "%s", zErr); + sqlite3_free(zErr); + return 1; } - if( k>=ArraySize(qrfEscNames) ){ - cli_printf(stderr, "unknown control character escape mode \"%s\"" - " - choices:", zEsc); - for(k=0; kmode.spec.eEsc = k+1; + chng = 1; + }else if( 0<=(k=pickStr(z,0,"-tablename","-rowsep","-colsep","-null","")) ){ + /* 0 1 2 3 */ + if( i+1>=nArg ){ + dotCmdError(p, i, "missing argument", 0); return 1; } - }else if( zMode==0 ){ - zMode = z; - /* Apply defaults for qbox pseudo-mode. If that - * overwrites already-set values, user was informed of this. - */ - chng |= 1; - if( cli_strcmp(z, "qbox")==0 ){ - zMode = "box"; - p->mode.spec.eText = QRF_TEXT_Sql; - p->mode.spec.eBlob = QRF_BLOB_Sql; - } - }else if( zTabname==0 ){ - zTabname = z; + i++; + switch( k ){ + case 0: modeSetStr(&p->mode.spec.zTableName, azArg[i]); break; + case 1: modeSetStr(&p->mode.spec.zRowSep, azArg[i]); break; + case 2: modeSetStr(&p->mode.spec.zColumnSep, azArg[i]); break; + case 3: modeSetStr(&p->mode.spec.zNull, azArg[i]); break; + } + chng = 1; + }else if( iMode<0 && (eMode = modeFind(azArg[i]))>=0 && eMode!=MODE_Www ){ + iMode = i; + modeChange(&p->mode, eMode); + /* (Legacy) If the mode is MODE_Insert and the next argument + ** is not an option, then the next argument must be the table + ** name. + */ + if( i+1mode.spec.zTableName, azArg[i]); + } + chng = 1; }else if( z[0]=='-' ){ - cli_printf(stderr,"unknown option: %s\n", z); - eputz("options:\n" - " --escape MODE\n" - " --noquote\n" - " --quote\n" - " --wordwrap on/off\n" - " --wrap N\n" - " --ww\n"); + dotCmdError(p, i, "bad option", "Use \".help .mode\" for more info"); return 1; }else{ - cli_printf(stderr,"extra argument: \"%s\"\n", z); + dotCmdError(p, i, iMode>0?"bad argument":"unknown mode", + "Use \".help .mode\" for more info"); return 1; } } if( !chng ){ - if( p->mode.eMode==MODE_Column - || p->mode.eMode==MODE_Box - || p->mode.eMode==MODE_Table - || p->mode.eMode==MODE_Markdown - ){ - cli_printf(p->out, "current output mode: %s", - modeDescr[p->mode.eMode]); + const ModeInfo *pI = aModeInfo + p->mode.eMode; + sqlite3_str *pDesc = sqlite3_str_new(p->db); + char *zDesc; + sqlite3_str_appendall(pDesc,pI->zName); + if( pI->eCx>0 && p->mode.spec.bWordWrap ){ if( p->mode.spec.nWrap ){ - cli_printf(p->out, " --wrap %d", - p->mode.spec.nWrap); - }else{ - cli_printf(p->out, " --wrap off"); + sqlite3_str_appendf(pDesc, " --wrap %d", p->mode.spec.nWrap); } - cli_printf(p->out, " --wordwrap %s --%squote --escape %s\n", - p->mode.spec.bWordWrap==QRF_Yes ? "on" : "off", - p->mode.spec.eText==QRF_TEXT_Sql ? "" : "no", - qrfEscNames[p->mode.spec.eEsc==QRF_ESC_Auto? - QRF_ESC_Ascii:p->mode.spec.eEsc] - ); - }else{ - cli_printf(p->out, - "current output mode: %s --escape %s\n", - modeDescr[p->mode.eMode], - qrfEscNames[p->mode.spec.eEsc==QRF_ESC_Auto? - QRF_ESC_Ascii:p->mode.spec.eEsc] - ); + sqlite3_str_append(pDesc, " --ww", 5); } - } - if( zMode==0 ){ - zMode = modeDescr[p->mode.eMode]; - } - n2 = strlen30(zMode); - if( cli_strncmp(zMode,"lines",n2)==0 ){ - p->mode.eMode = MODE_Line; - modeSetStr(&p->mode.spec.zRowSep, SEP_Row); - }else if( cli_strncmp(zMode,"columns",n2)==0 ){ - p->mode.eMode = MODE_Column; - modeSetStr(&p->mode.spec.zRowSep, SEP_Row); - }else if( cli_strncmp(zMode,"list",n2)==0 ){ - p->mode.eMode = MODE_List; - modeSetStr(&p->mode.spec.zColumnSep, SEP_Column); - modeSetStr(&p->mode.spec.zRowSep, SEP_Row); - }else if( cli_strncmp(zMode,"html",n2)==0 ){ - p->mode.eMode = MODE_Html; - }else if( cli_strncmp(zMode,"tcl",n2)==0 ){ - p->mode.eMode = MODE_Tcl; - modeSetStr(&p->mode.spec.zColumnSep, SEP_Space); - modeSetStr(&p->mode.spec.zRowSep, SEP_Row); - }else if( cli_strncmp(zMode,"csv",n2)==0 ){ - p->mode.eMode = MODE_Csv; - modeSetStr(&p->mode.spec.zColumnSep, SEP_Comma); - modeSetStr(&p->mode.spec.zRowSep, SEP_CrLf); - }else if( cli_strncmp(zMode,"tabs",n2)==0 ){ - p->mode.eMode = MODE_List; - modeSetStr(&p->mode.spec.zColumnSep, SEP_Tab); - }else if( cli_strncmp(zMode,"insert",n2)==0 ){ - p->mode.eMode = MODE_Insert; - modeSetStr(&p->mode.spec.zTableName, zTabname); - }else if( cli_strncmp(zMode,"quote",n2)==0 ){ - p->mode.eMode = MODE_Quote; - modeSetStr(&p->mode.spec.zColumnSep, SEP_Comma); - modeSetStr(&p->mode.spec.zRowSep, SEP_Row); - }else if( cli_strncmp(zMode,"ascii",n2)==0 ){ - p->mode.eMode = MODE_Ascii; - modeSetStr(&p->mode.spec.zColumnSep, SEP_Unit); - modeSetStr(&p->mode.spec.zRowSep, SEP_Record); - }else if( cli_strncmp(zMode,"markdown",n2)==0 ){ - p->mode.eMode = MODE_Markdown; - }else if( cli_strncmp(zMode,"table",n2)==0 ){ - p->mode.eMode = MODE_Table; - }else if( cli_strncmp(zMode,"box",n2)==0 ){ - p->mode.eMode = MODE_Box; - }else if( cli_strncmp(zMode,"count",n2)==0 ){ - p->mode.eMode = MODE_Count; - }else if( cli_strncmp(zMode,"off",n2)==0 ){ - p->mode.eMode = MODE_Off; - }else if( cli_strncmp(zMode,"json",n2)==0 ){ - p->mode.eMode = MODE_Json; - }else{ - eputz("Error: mode should be one of: " - "ascii box column csv html insert json line list markdown " - "qbox quote table tabs tcl\n"); - return 1; + if( pI->eText!=p->mode.spec.eText ){ + sqlite3_str_appendf(pDesc," --quote %s",qrfQuoteNames[p->mode.spec.eText]); + } + if( p->mode.spec.eEsc!=QRF_Auto ){ + sqlite3_str_appendf(pDesc, " --escape %s", qrfEscNames[p->mode.spec.eEsc]); + } + zDesc = sqlite3_str_finish(pDesc); + cli_printf(p->out, "current output mode: %s\n", zDesc); + sqlite3_free(zDesc); } return 0; } @@ -10115,11 +10188,12 @@ static int do_meta_command(const char *zLine, ShellState *p){ ){ cli_printf(p->out, "%12.12s: %s --wrap %d --wordwrap %s --%squote\n", "mode", - modeDescr[p->mode.eMode], p->mode.spec.nWrap, + aModeInfo[p->mode.eMode].zName, p->mode.spec.nWrap, p->mode.spec.bWordWrap==QRF_Yes ? "on" : "off", p->mode.spec.eText==QRF_TEXT_Sql ? "" : "no"); }else{ - cli_printf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode.eMode]); + cli_printf(p->out, "%12.12s: %s\n","mode", + aModeInfo[p->mode.eMode].zName); } cli_printf(p->out, "%12.12s: ", "nullvalue"); output_c_string(p->out, p->mode.spec.zNull); diff --git a/src/tclsqlite.c b/src/tclsqlite.c index ce9d0d127a..4076e0dc09 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -2152,7 +2152,7 @@ static int dbQrf(SqliteDb *pDb, int objc, Tcl_Obj *const*objv){ "auto", "box", "column", "count", "csv", "eqp", "explain", "html", "insert", - "json", "json-line", "line", + "jobject", "json", "line", "list", "markdown", "quote", "stats", "stats-est", "stats-vm", "table", 0 @@ -2161,7 +2161,7 @@ static int dbQrf(SqliteDb *pDb, int objc, Tcl_Obj *const*objv){ QRF_STYLE_Auto, QRF_STYLE_Box, QRF_STYLE_Column, QRF_STYLE_Count, QRF_STYLE_Csv, QRF_STYLE_Eqp, QRF_STYLE_Explain, QRF_STYLE_Html, QRF_STYLE_Insert, - QRF_STYLE_Json, QRF_STYLE_JsonLine, QRF_STYLE_Line, + QRF_STYLE_JObject, QRF_STYLE_Json, QRF_STYLE_Line, QRF_STYLE_List, QRF_STYLE_Markdown, QRF_STYLE_Quote, QRF_STYLE_Stats, QRF_STYLE_StatsEst, QRF_STYLE_StatsVm, QRF_STYLE_Table diff --git a/test/qrf01.test b/test/qrf01.test index 0e6e3d932b..1fb8629b13 100644 --- a/test/qrf01.test +++ b/test/qrf01.test @@ -80,7 +80,7 @@ do_test 1.15 { do_test 1.16 { catch {db format -text sql -style unk {SELECT * FROM t1}} res set res -} {bad format style (-style) "unk": must be auto, box, column, count, csv, eqp, explain, html, insert, json, json-line, line, list, markdown, quote, stats, stats-est, stats-vm, or table} +} {bad format style (-style) "unk": must be auto, box, column, count, csv, eqp, explain, html, insert, jobject, json, line, list, markdown, quote, stats, stats-est, stats-vm, or table} do_test 1.20 { @@ -210,13 +210,13 @@ do_test 1.90 { {"a":"\u0042\u004c\u004f\u0042","b":null,"c":"Ἀμήν"}] } do_test 1.91 { - set result "\n[db format -style json-line {SELECT * FROM t1}]" + set result "\n[db format -style jobject {SELECT * FROM t1}]" } { {"a":1,"b":2.5,"c":"three"} {"a":"\u0042\u004c\u004f\u0042","b":null,"c":"Ἀμήν"} } do_test 1.92 { - set result "\n[db format -style json-line {SELECT *, unistr('abc\u000a123\u000d\u000axyz') AS xyz FROM t1}]" + set result "\n[db format -style jobject {SELECT *, unistr('abc\u000a123\u000d\u000axyz') AS xyz FROM t1}]" } { {"a":1,"b":2.5,"c":"three","xyz":"abc\n123\r\nxyz"} {"a":"\u0042\u004c\u004f\u0042","b":null,"c":"Ἀμήν","xyz":"abc\n123\r\nxyz"}