From c37fcdd5d1ad84bc88ed1a40f9612349607d5a7d Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 14 Nov 2025 02:22:39 +0000 Subject: [PATCH] Improvements to --titles handling in the .mode command. Fix shell1 test cases having to do with quoting of NULL values in "tcl" mode. FossilOrigin-Name: 0fef770bd654b8ba8011b0bb610128058e75e8444f86c51e7ea59a6f2625a3fc --- manifest | 14 ++-- manifest.uuid | 2 +- src/shell.c.in | 168 +++++++++++++++++++++++++++++++---------------- test/shell1.test | 2 +- 4 files changed, 120 insertions(+), 66 deletions(-) diff --git a/manifest b/manifest index 2bb2fc34c8..64c9564c83 100644 --- 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. diff --git a/manifest.uuid b/manifest.uuid index 8d0f55f15f..29e0f83351 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -48a91f2067005f7f186484354be07384dd76bcfff8427c17579f6e32201e3742 +0fef770bd654b8ba8011b0bb610128058e75e8444f86c51e7ea59a6f2625a3fc diff --git a/src/shell.c.in b/src/shell.c.in index 7ded2051b5..c9311f353c 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -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; diff --git a/test/shell1.test b/test/shell1.test index 07cb68465b..1203bcb028 100644 --- a/test/shell1.test +++ b/test/shell1.test @@ -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"}} -- 2.47.3