]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Improvements to --titles handling in the .mode command. Fix shell1 test
authordrh <>
Fri, 14 Nov 2025 02:22:39 +0000 (02:22 +0000)
committerdrh <>
Fri, 14 Nov 2025 02:22:39 +0000 (02:22 +0000)
cases having to do with quoting of NULL values in "tcl" mode.

FossilOrigin-Name: 0fef770bd654b8ba8011b0bb610128058e75e8444f86c51e7ea59a6f2625a3fc

manifest
manifest.uuid
src/shell.c.in
test/shell1.test

index 2bb2fc34c8865643e5f8a38609eaa64816e5a4a1..64c9564c83ef9f6e899ede2b0e542076f5980d50 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Get\s--screenwidth\sauto\sworking\son\slinux\sand\swindows.
-D 2025-11-13T11:52:34.054
+C Improvements\sto\s--titles\shandling\sin\sthe\s.mode\scommand.\s\sFix\sshell1\stest\ncases\shaving\sto\sdo\swith\squoting\sof\sNULL\svalues\sin\s"tcl"\smode.
+D 2025-11-14T02:22:39.968
 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -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 a583ee672e169092f74dd5b5a0162c048b2399fa668831b6fc14cd56b4a1d28d
+F src/shell.c.in 18e4dad1702c640eb272aa88b529bd7e65bae98a28d4740226a7a89095f7b4ea
 F src/sqlite.h.in 7403a952a8f1239de7525b73c4e3a0f9540ec0607ed24fec887f5832642d44b8
 F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
 F src/sqlite3ext.h 7f236ca1b175ffe03316d974ef57df79b3938466c28d2f95caef5e08c57f3a52
@@ -1602,7 +1602,7 @@ 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 cdc63d6badd34aef37cd31d92a30b3d3c38fe1386a9c98ac047a7ecec5bfc6a3
+F test/shell1.test f16539b28a1dd53eb88733b6010350fd861d073adada16a4158d8a3e235e9d0d
 F test/shell2.test d8da6a06dcce1d8f04f776f918d4d57c28ddc28c54f3a44f95429794892e3a91
 F test/shell3.test 603b448e917537cf77be0f265c05c6f63bc677c63a533c8e96aae923b56f4a0e
 F test/shell4.test 03593fa7908a55f255916ffeda707cdf55680c777736e3da62b1d78cde0d684d
@@ -2175,8 +2175,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 cc25643ebc516db0799406797e9961a2af574875ae9639178b722dbc280c96ad
-R 31066a85466510dad4651e3d0c66a67b
+P 48a91f2067005f7f186484354be07384dd76bcfff8427c17579f6e32201e3742
+R c041cd4adb38af91b40e6a3e783e78aa
 U drh
-Z 45c94c43aea0848aa5e0a4f6660d06e7
+Z 64f25318fa7858c3f29a71187bafc4d0
 # Remove this line to create a well-formed Fossil manifest.
index 8d0f55f15f9578223c4137165556690284486244..29e0f8335106f0089a424ecd36a2a8ce9a1fbf67 100644 (file)
@@ -1 +1 @@
-48a91f2067005f7f186484354be07384dd76bcfff8427c17579f6e32201e3742
+0fef770bd654b8ba8011b0bb610128058e75e8444f86c51e7ea59a6f2625a3fc
index 7ded2051b52ba2ce2476521992433836b498b9f8..c9311f353c718c00f285287f6883bbe8ba002f60 100644 (file)
@@ -1421,6 +1421,7 @@ static const char *qrfQuoteNames[] = { "off","off","sql","hex", "tcl", "json"};
 #define MODE_Www      20  /* Full web-page output */
 
 /*
+** Information about how each display-mode behaves.
 */
 typedef struct ModeInfo ModeInfo;
 struct ModeInfo {
@@ -1429,48 +1430,57 @@ struct ModeInfo {
   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 eHdr;    /* Default header encoding. */
+  unsigned char eBlob;   /* Default blob encoding. */
+  unsigned char bHdr;    /* Show headers by default.  0: n/a, 1: no 2: yes */
   unsigned char eStyle;  /* Underlying QRF style */
   unsigned char eCx;     /* 0: other, 1: line, 2: columnar */
-  unsigned char bTx;     /* Column headers enabled by default */
 };
 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 bTx */
-  { "ascii",    7,     6,    9,    1,    1,    1,   1,     12,    0,  0  },
-  { "box",      0,     0,    9,    1,    1,    2,   1,     1,     2,  1  },
-  { "c",        4,     1,    2,    5,    4,    1,   5,     12,    0,  0  },
-  { "column",   0,     0,    9,    1,    1,    2,   1,     2,     2,  1  },
-  { "count",    0,     0,    0,    0,    0,    0,   0,     3,     0,  0  },
-  { "csv",      4,     5,    9,    3,    1,    1,   3,     12,    0,  0  },
-  { "html",     0,     0,    9,    4,    1,    2,   4,     7,     0,  0  },
-  { "insert",   0,     0,    10,   2,    2,    1,   2,     8,     0,  0  },
-  { "jatom",    4,     1,    11,   6,    5,    1,   6,     12,    0,  0  },
-  { "jobject",  0,     1,    11,   6,    5,    0,   0,     10,    0,  0  },
-  { "json",     0,     0,    11,   6,    6,    0,   0,     9,     0,  0  },
-  { "line",     0,     1,    9,    1,    1,    0,   0,     11,    1,  0  },
-  { "list",     2,     1,    9,    1,    1,    1,   1,     12,    0,  0  },
-  { "markdown", 0,     0,    9,    1,    1,    2,   1,     13,    2,  1  },
-  { "off",      0,     0,    0,    0,    0,    0,   0,     14,    0,  0  },
-  { "qbox",     0,     0,    9,    2,    2,    2,   1,     1,     2,  1  },
-  { "quote",    4,     1,    10,   2,    2,    1,   2,     12,    0,  0  },
-  { "table",    0,     0,    9,    1,    1,    2,   1,     19,    2,  1  },
-  { "tabs",     8,     1,    9,    3,    1,    1,   3,     12,    0,  0  },
-  { "tcl",      3,     1,    9,    5,    4,    1,   5,     12,    0,  0  },
-  { "www",      0,     0,    9,    4,    1,    2,   4,     7,     0,  1  },
-};     /*       |     /     /       \      \   |   /      /
-       **       |    /     /         \      \  |  /      /
-       ** 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_....                   */
+  /* 0    1       2       3       4     5        6        7        8    */
+   { 0,   "\n",   "|",    " ",    ",",  "\r\n",  "\036",  "\037",  "\t",
+     "",  "NULL", "null", "\"\""                                         };
+  /* 9    10      11      12                                            */
 
+static const ModeInfo aModeInfo[] = {
+/*   zName      eCSep  eRSep eNull eText eHdr eBlob bHdr eStyle eCx */
+  { "ascii",    7,     6,    9,    1,    1,    1,   1,   12,    0 },
+  { "box",      0,     0,    9,    1,    1,    1,   2,   1,     2 },
+  { "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 },
+  { "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 },
+  { "jobject",  0,     1,    11,   6,    6,    5,   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,    1,   2,   13,    2 },
+  { "off",      0,     0,    0,    0,    0,    0,   0,   14,    0 },
+  { "qbox",     0,     0,    9,    2,    1,    2,   2,   1,     2 },
+  { "quote",    4,     1,    10,   2,    2,    2,   1,   12,    0 },
+  { "table",    0,     0,    9,    1,    1,    1,   2,   19,    2 },
+  { "tabs",     8,     1,    9,    3,    3,    1,   1,   12,    0 },
+  { "tcl",      3,     1,    12,   5,    5,    4,   1,   12,    0 },
+  { "www",      0,     0,    9,    4,    4,    1,   2,   7,     0 }
+};     /*       |     /     /      |     /    /     |    |       \
+       **       |    /     /       |    /    /      |    |        \_ 2: columnar
+       ** Index into aModeStr[]    |   /    /       |    |           1: line
+       **                          |  /    /        |    |           0: other
+       **                          | /    /         |     \
+       **           text encoding  |/     |    show |      \
+       **      v-------------------'      |   hdrs? |       The QRF style
+       **      0: n/a                blob |   v-----'
+       **      1: plain        v_---------'   0: n/a
+       **      2: sql          0: n/a         1: no         
+       **      3: csv          1: as-text     2: yes
+       **      4: html         2: sql
+       **      5: c            3: hex
+       **      6: json         4: c
+       **                      5: json
+       ******************************************************************/
 /*
 ** These are the column/row/line separators used by the various
 ** import/export modes.
@@ -1575,7 +1585,7 @@ static void modeChange(Mode *p, unsigned char eMode){
   p->spec.eText = pI->eText;
   p->spec.eBlob = pI->eBlob;
   p->spec.bTitles = pI->bHdr;
-  p->spec.eTitle = pI->eTitle;
+  p->spec.eTitle = pI->eHdr;
 }
 
 /*
@@ -7204,6 +7214,54 @@ static int pickStr(const char *zArg, char **pzErr, ...){
   return -1;
 }
 
+/*
+** This function computes what to show the user about the configured
+** titles (or column-names).  Output is an integer between 0 and 3:
+**
+**    0:     The titles do not matter.  Never show anything.
+**    1:     Show  "--titles off"
+**    2:     Show  "--titles on"
+**    3:     Show  "--title VALUE"  where VALUE is an encoding method
+**             to use, one of: plain sql csv html tcl json
+**
+** Inputs are:
+**
+**    spec.bTitles   (bT)     Whether or not to show the titles
+**    spec.eTitle    (eT)     The actual encoding to be used for titles
+**    ModeInfo.bHdr  (bH)     Default value for spec.bTitles
+**    ModeInfo.eHdr  (eH)     Default value for spec.eTitle
+**    bAll                    Whether the -v option is used
+*/
+static int modeTitleDsply(ShellState *p, int bAll){
+  int eMode = p->mode.eMode;
+  const ModeInfo *pI = &aModeInfo[eMode];
+  int bT = p->mode.spec.bTitles;
+  int eT = p->mode.spec.eTitle;
+  int bH = pI->bHdr;
+  int eH = pI->eHdr;
+
+  /* Variable "v" is the truth table that will determine the answer
+  **
+  **                   Actual encoding is different from default 
+  **                   vvvvvvvv                                 */
+  sqlite3_uint64 v = 0x0133013311220102;
+  /*                   ^^^^    ^^^^
+  **                   Upper 2-byte groups for when ON/OFF disagrees with
+  **                   the default.                                         */
+
+  if( bH==0 ) return 0;  /* Header not appliable.  Ex: off, count */
+
+  if( eT==0 ) eT = eH;   /* Fill in missing spec.eTitle */
+  if( bT==0 ) bT = bH;   /* Fill in missing spec.bTitles */
+
+  if( eT!=eH ) v >>= 32; /* Encoding disagree in upper 4-bytes */
+  if( bT!=bH ) v >>= 16; /* ON/OFF disagree in upper 2-byte pairs */
+  if( bT<2 ) v >>= 8;    /* ON in even bytes, OFF in odd bytes (1st byte 0) */
+  if( !bAll ) v >>= 4;   /* bAll values are in the lower half-byte */
+
+  return v & 3;          /* Return the selected truth-table entry */
+}
+
 /*
 ** DOT-COMMAND:  .mode
 **
@@ -7474,14 +7532,14 @@ static int dotCmdMode(ShellState *p){
         return 1;
       }
       k = pickStr(azArg[++i],&zErr,
-              "off","on","plain","sql","html","tcl","json","");
-          /*   0     1    2       3     4      5     6 */
+              "off","on","plain","sql","csv","html","tcl","json","");
+          /*   0     1    2       3     4     5     6      7 */
       if( k<0 ){
         dotCmdError(p, i, "bad --titles value","%z", zErr);
         return 1;
       }
       p->mode.spec.bTitles = k>=1 ? QRF_Yes : QRF_No;
-      p->mode.spec.eTitle = k>1 ? k-1 : aModeInfo[p->mode.eMode].eTitle;
+      p->mode.spec.eTitle = k>1 ? k-1 : aModeInfo[p->mode.eMode].eHdr;
       chng = 1;
     }else if( optionMatch(z,"widths") ){
       int nWidth = 0;
@@ -7557,8 +7615,7 @@ static int dotCmdMode(ShellState *p){
     sqlite3_str *pDesc = sqlite3_str_new(p->db);
     char *zDesc;
     const char *zSetting;
-    int bSameTitle;
-    int bTitleOn;
+
     sqlite3_str_appendall(pDesc,pI->zName);
     if( bAll || (p->mode.spec.nAlign && pI->eCx==2) ){
       int i;
@@ -7616,20 +7673,17 @@ static int dotCmdMode(ShellState *p){
       sqlite3_str_appendf(pDesc," --textjsonb %s",
              p->mode.spec.bTextJsonb==QRF_Yes ? "on" : "off");
     }
-    bSameTitle = p->mode.spec.eTitle==pI->eTitle;
-    bTitleOn = (p->mode.spec.bTitles==QRF_Yes);
-    if( bAll || bTitleOn!=pI->bTx || (bTitleOn && !bSameTitle) ){
+    k = modeTitleDsply(p, bAll);
+    if( k==1 ){
+      sqlite3_str_appendall(pDesc, " --titles off");
+    }else if( k==2 ){
+      sqlite3_str_appendall(pDesc, " --titles on");
+    }else if( k==3 ){
       static const char *azTitle[] =
-          { "off", "on", "plain", "sql", "csv", "html", "tcl", "json"};
-      if( !bTitleOn ){
-        k = 0;
-      }else if( bSameTitle ){
-        k = 1;
-      }else{
-        k = p->mode.spec.eTitle+1;
-      }
-      sqlite3_str_appendf(pDesc," --titles %s", azTitle[k]);
-    }
+          { "plain", "sql", "csv", "html", "tcl", "json"};
+      sqlite3_str_appendf(pDesc, " --titles %s",
+                   azTitle[p->mode.spec.eTitle-1]);
+    }    
     if( p->mode.spec.nWidth>0 && (bAll || pI->eCx==2) ){
       int i;
       const char *zSep = " --widths ";
@@ -8797,7 +8851,7 @@ static int do_meta_command(const char *zLine, ShellState *p){
   if( c=='h' && cli_strncmp(azArg[0], "headers", n)==0 ){
     if( nArg==2 ){
       p->mode.spec.bTitles = booleanValue(azArg[1]) ? QRF_Yes : QRF_No;
-      p->mode.spec.eTitle = aModeInfo[p->mode.eMode].eTitle;
+      p->mode.spec.eTitle = aModeInfo[p->mode.eMode].eHdr;
     }else{
       eputz("Usage: .headers on|off\n");
       rc = 1;
index 07cb68465b8cf127a7af4ba319474b28db150732..1203bcb02804289cb87947c4cb816f175e8755e8 100644 (file)
@@ -1079,7 +1079,7 @@ do_test shell1-4.4 {
 #
 do_test shell1-4.5 {
   catchcmd test.db ".mode tcl\n.nullvalue NULL\nselect * from t2;"
-} {0 {"NULL" ""
+} {0 {NULL ""
 1 2.25
 "hello" "\200\177"}}