]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Rename QRF_STYLE_JsonLine to QRF_STYLE_JObject. Add new convenience modes
authordrh <>
Wed, 12 Nov 2025 12:44:02 +0000 (12:44 +0000)
committerdrh <>
Wed, 12 Nov 2025 12:44:02 +0000 (12:44 +0000)
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

ext/qrf/qrf.c
ext/qrf/qrf.h
manifest
manifest.uuid
src/shell.c.in
src/tclsqlite.c
test/qrf01.test

index c8382e8ef1593d0161efe4a29a2d9a6e8a49008d..8338061ea73b9041cdfcdf11e2c367a145a55fab 100644 (file)
@@ -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;
index a353ea055f68473dfac19ce7707e90d04fb228f8..169fb4d26fbe3b3d7ba79dbf4af5c3ec11f54ff3 100644 (file)
@@ -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 */
index aff6dfd237f280af6e0887789e3ff871c352589d..f577231cd4ebe67f0458b2b714523ba561dd1cd7 100644 (file)
--- 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.
index b2260f4f2ff4be656a02f894aed9b6383e56d4ae..211a64b0c38fcdf4aa61db561576bcc5697a0d51 100644 (file)
@@ -1 +1 @@
-3450bc7eecb717abedbaeb56dc824e14eb35ed30322fe3dd3b4b1cbd5fd14c60
+4c8109682e3a224fd8e015e73a9c1c7b6d56ad747d7ca7bc387bb92e1c98f731
index c8ae7c2438b10c26afdb431e423540c7a56749eb..2ee7730a287334d74cd0aa90327c859ebb038e56 100644 (file)
@@ -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 && eMode<ArraySize(aModeInfo) );
+  pI = &aModeInfo[eMode];
+  p->eMode = 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; i<ArraySize(aModeInfo); i++){
+    if( cli_strcmp(aModeInfo[i].zName,zName)==0 ) return i;
+  }
+  return -1;
+}
+
+
 /*
 ** A callback for the sqlite3_log() interface.
 */
@@ -2994,8 +3042,8 @@ static int shell_exec(
       break;
     }
   }
-  assert( pArg->mode.eMode>=0 && pArg->mode.eMode<ArraySize(aQrfStyle) );
-  eStyle = aQrfStyle[pArg->mode.eMode];
+  assert( pArg->mode.eMode>=0 && pArg->mode.eMode<ArraySize(aModeInfo) );
+  eStyle = aModeInfo[pArg->mode.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; i<nArg; i++){
     const char *z = azArg[i];
@@ -7071,168 +7160,152 @@ static int dotCmdMode(ShellState *p){
       if( w<(-QRF_MAX_WIDTH) ) w = -QRF_MAX_WIDTH;
       if( w>QRF_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+1<nArg
+       && azArg[i+1][0]!='-'
+       && (iMode>0 || 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; k<ArraySize(qrfEscNames); k++){
-        if( sqlite3_stricmp(zEsc,qrfEscNames[k])==0 ){
-          p->mode.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; k<ArraySize(qrfEscNames); k++){
-          cli_printf(stderr, " %s", qrfEscNames[k]);
-        }
-        cli_printf(stderr, "\n");
+      p->mode.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+1<nArg && azArg[i+1][0]!='-' ){
+        i++;
+        modeSetStr(&p->mode.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);
index ce9d0d127a4469bcf5a2fa6ffff9f8fa915f64ad..4076e0dc09f930a3a2448b1f30270f102b4ff2b2 100644 (file)
@@ -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
index 0e6e3d932b73da2ef7c9c9f802b2dfb08f4f7ace..1fb8629b13e42b99db181ef8bab5a1c7383dc06f 100644 (file)
@@ -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"}