From 839d78150177c2df20e8d4a24810525d12ff931d Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 8 Jul 2026 15:34:53 +0000 Subject: [PATCH] Make the (legacy, deprecated) ".header" command in the CLI responsive to the "always" title mode. Clean up title mode and define its operation with improved comments in the TCL interface and in the CLI. Add the "-title auto" option to the CLI. FossilOrigin-Name: f94259fc07cd7856e9c6f81a46689f4f96bbf464fda7f1d11b51a308c296aca0 --- manifest | 21 +++++------- manifest.uuid | 2 +- src/shell.c.in | 75 ++++++++++++++++++++++++++++++++---------- src/tclsqlite.c | 22 ++++++++++++- test/modeA.sql | 85 ++++++++++++++++++++++++++++++++++++++++++++++++ test/shell1.test | 8 ++--- 6 files changed, 177 insertions(+), 36 deletions(-) diff --git a/manifest b/manifest index c3e2299e5e..b26efd4ffb 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Change\sthe\sCLI\sso\sthat\sthe\sdefault\s"--title"\sis\s"always"\sinstead\sof\s"on"\nfor\ssome\sdisplay\smodes.\s\sThis\scheck-in\salso\sinclude\sa\sfix\sto\s"--title\salways"\nfor\sthe\s"markdown"\sdisplay\smode. -D 2026-07-07T14:53:53.364 +C Make\sthe\s(legacy,\sdeprecated)\s".header"\scommand\sin\sthe\sCLI\sresponsive\sto\nthe\s"always"\stitle\smode.\s\sClean\sup\stitle\smode\sand\sdefine\sits\soperation\swith\nimproved\scomments\sin\sthe\sTCL\sinterface\sand\sin\sthe\sCLI.\s\sAdd\sthe\n"-title\sauto"\soption\sto\sthe\sCLI. +D 2026-07-08T15:34:53.977 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -743,7 +743,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 54395ee97eb710e695202d4112cf2b1c1c7767a57afcea745df71abb1c917768 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c f553420eaf5c72a49cef786621eea79dd8c4411671839fb05250bb49ca74a0a0 -F src/shell.c.in 8dd3e9145c5fb5e5ec1b343fa26cf007bdc9f995bd3574dab2518748cfe10724 +F src/shell.c.in 5b94be0ff5a385cd42beed62ca229a062e5a696c2afd6567ce699b0d6d8bd21b F src/sqlite.h.in d9ec41feb4cd804e68b174328b43beb3f1f71bba13e1c7a439efb826d301cccc F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479 F src/sqlite3ext.h 0efd4723bad9124ea1f581d9f1ea0254ac1c6f3e5fb29e4f3dcf36c72485a456 @@ -751,7 +751,7 @@ F src/sqliteInt.h 92563b8edc673513b82d87bf6a8d7aeb554b2c7b7d5e30992fbca04010db79 F src/sqliteLimit.h c70656b67ab5b96741a8f1c812bdd80c81f2b1c1e443d0cc3ea8c33bb1f1a092 F src/status.c 7565d63a79aa2f326339a24a0461a60096d0bd2bce711fefb50b5c89335f3592 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 -F src/tclsqlite.c 9ac18aac16dd91fec546d1329c17a41b6b2fd095b2294ba063fa02aca0161f8e +F src/tclsqlite.c fac4e166b90c1e8c4cac76c1339dfb9d94646debda2cdf8026120a7e19a822ff F src/tclsqlite.h 614b3780a62522bc9f8f2b9fb22689e8009958e7aa77e572d0f3149050af348a F src/test1.c df2df0a74c9aa81cbbad9d7d8277e49ac255334cba97da9ee22501beac274201 F src/test2.c 2b9ab96bba63a1c369d5769390475259ad210f144a877805f2e32e563f9e93c1 @@ -1461,7 +1461,7 @@ F test/mmap4.test 2e2b4e32555b58da15176e6fe750f17c9dcf7f93 F test/mmapcorrupt.test 470fb44fe92e99c1d23701d156f8c17865f5b027063c9119dcfdb842791f4465 F test/mmapfault.test d4c9eff9cd8c2dc14bc43e71e042f175b0a26fe3 F test/mmapwarm.test 2272005969cd17a910077bd5082f70bc1fefad9a875afec7fc9af483898ecaf3 -F test/modeA.sql 70bd5172122f992c2a2caa8e247d3dae68dbac2d854915ea20d4e01b98940d78 +F test/modeA.sql 4c1e10ec06f0a061ae75f06c0dfc102674c519ee1574323faa1e11cb47db44c8 F test/multiplex.test d74c034e52805f6de8cc5432cef8c9eb774bb64ec29b83a22effc8ca4dac1f08 F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a F test/multiplex3.test fac575e0b1b852025575a6a8357701d80933e98b5d2fe6d35ddaa68f92f6a1f7 @@ -1634,7 +1634,7 @@ F test/sharedB.test 1a84863d7a2204e0d42f2e1606577c5e92e4473fa37ea0f5bdf829e4bf8e F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939 F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304 F test/shell-prompt.sql 8a320a78812d75349bd5e1a95178f7ff1f2194bb4cceee572865d127c51684b3 -F test/shell1.test df7a33a7056220ae12966228731658b36c98c22ba27e114bbf2785ff5b84fea2 +F test/shell1.test 72cd5c5e7d172d28aefba8fe72d174945d06cba2818440648b57d27382203fb6 F test/shell2.test 1d7b9133b612d53f3c355dcba55845f84fcf2fe0d09459f82adbfaccae3c60b1 F test/shell3.test 91efdd545097a61a1f72cf79c9ad5b49da080f3f10282eaf4c3c272cd1012db2 F test/shell4.test e25580a792b7b54560c3a76b6968bd8189261f38979fe28e6bc6312c5db280db @@ -2213,11 +2213,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c -P 4edba31af4ef18350cb415079fb92cae0c53767fc0e74d63d404296974e75e5c -R 91f96872ad01984a543b449ea7837a3b -T *branch * title-always-default-on -T *sym-title-always-default-on * -T -sym-trunk * +P c3b0fff1b76b8c354b34fef17ff639968698e97234d6ab7af15e0e1a835486e9 +R 688db872c29fb684e76bc6c6acf8cc9b U drh -Z 8732b504c20e338d03135e902dabf4df +Z abf35e5e3a6771887b73b278adcf21be # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 679fac24d6..e2979933ad 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c3b0fff1b76b8c354b34fef17ff639968698e97234d6ab7af15e0e1a835486e9 +f94259fc07cd7856e9c6f81a46689f4f96bbf464fda7f1d11b51a308c296aca0 diff --git a/src/shell.c.in b/src/shell.c.in index d000d88ed3..a30c82b810 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -8558,12 +8558,12 @@ static int dotCmdImport(ShellState *p){ ** 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 "--titles always" +** 1: Show "--title off" +** 2: Show "--title on" +** 3: Show "--title always" ** 4: Show "--title VALUE" where VALUE is an encoding method ** to use, one of: plain sql csv html tcl json -** 5: Show "--title VALUE" as in 3, followed by "--titles always" +** 5: Show "--title VALUE" as in 3, followed by "--title always" ** ** Inputs are: ** @@ -8603,8 +8603,8 @@ static int modeTitleDsply(ShellState *p, int bAll){ v &= 3; /* At this point v means: ** 0: The titles do not matter. Never show anything. - ** 1: Show "--titles off" - ** 2: Show "--titles on" + ** 1: Show "--title off" + ** 2: Show "--title on" ** 3: Show "--title VALUE" where VALUE is an encoding method ** to use, one of: plain sql csv html tcl json ** If bTitles is QRF_Always, convert 2->3, 3->5. If bTitles is not @@ -8676,7 +8676,7 @@ static int modeTitleDsply(ShellState *p, int bAll){ ** --textjsonb BOOLEAN If enabled, JSONB text is displayed as text JSON. ** --title ARG Whether or not to show column headers, and if so ** how to encode them. ARG can be "off", "on", -** "sql", "csv", "html", "tcl", "json", or "always". +** "always", "sql", "csv", "html", "tcl", or "json". ** --titlelimit N Limit the length of column titles to N characters. ** -v|--verbose Verbose output ** --widths LIST Set the columns widths for columnar modes. The @@ -8964,24 +8964,58 @@ static int dotCmdMode(ShellState *p){ p->mode.spec.bTextJsonb = booleanValue(azArg[++i]) ? QRF_Yes : QRF_No; chng = 1; }else if( optionMatch(z,"titles") || optionMatch(z,"title") ){ + /* The --titles option controls two fields of the + ** sqlite3_qrf_spec object: eTitle and bTitles. + ** + ** --titles eTitle bTitles + ** -------- --------------- --------------------- + ** "off" (unchanged) QRF_Off + ** "on" (unchanged) QRF_On + ** "always" (unchanged) QRF_Always + ** "auto", (default) (default) + ** "plain" QRF_TEXT_Plain QRF_Always or QRF_On + ** "sql" QRF_TEXT_Sql QRF_Always or QRF_On + ** "csv" QRF_TEXT_Csv QRF_Always or QRF_On + ** "html" QRF_TEXT_Html QRF_Always or QRF_On + ** "tcl" QRF_TEXT_Tcl QRF_Always or QRF_On + ** "json" QRF_TEXT_Json QRF_Always or QRF_On + ** + ** This mapping is different from the -title option to the "format" + ** method of the TCL interface. In this mapping: + ** + ** * "off", "on", and "always" affect only bTitles and leave + ** eTitle unchanged + ** + ** * "auto" sets both bTitles and eTitle to the default value + ** in aModeInfo[] according to the current --style. + ** + ** * "plain" through "json" set eTitle but leave bTitles + ** unchanged if it is not QRF_Off. If bTitles is QRF_Off, + ** then it is changed QRF_Always. + */ char *zErr = 0; if( i+1>=nArg ){ dotCmdError(p, i, "missing argument", 0); return 1; } k = pickStr(azArg[++i],&zErr, - "off","on","plain","sql","csv","html","tcl","json","always",""); - /* 0 1 2 3 4 5 6 7 8 */ + "off","on","always","auto","plain","sql","csv","html","tcl","json",""); + /* 0 1 2 3 4 5 6 7 8 9 */ if( k<0 ){ dotCmdError(p, i, "bad --titles value","%z", zErr); return 1; } - if( k==8 ){ - p->mode.spec.bTitles = QRF_Always; + p->mode.mFlags &= ~MFLG_HDR; + if( k<=2 ){ + p->mode.spec.bTitles = QRF_No+k; + }else if( k==3 ){ + p->mode.spec.eTitle = aModeInfo[p->mode.eMode].eHdr; + p->mode.spec.bTitles = aModeInfo[p->mode.eMode].bHdr; }else{ - p->mode.spec.bTitles = k>=1 ? QRF_Yes : QRF_No; - p->mode.mFlags &= ~MFLG_HDR; - p->mode.spec.eTitle = k>1 ? k-1 : aModeInfo[p->mode.eMode].eHdr; + p->mode.spec.eTitle = (k-4)+QRF_TEXT_Plain; + if( p->mode.spec.bTitles==QRF_No ){ + p->mode.spec.bTitles = QRF_Always; + } } chng = 1; }else if( optionMatch(z,"widths") || optionMatch(z,"width") ){ @@ -10502,11 +10536,15 @@ 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; + if( cli_strcmp(azArg[1],"always")==0 ){ + p->mode.spec.bTitles = QRF_Always; + }else{ + p->mode.spec.bTitles = booleanValue(azArg[1]) ? QRF_Yes : QRF_No; + } p->mode.mFlags |= MFLG_HDR; p->mode.spec.eTitle = aModeInfo[p->mode.eMode].eHdr; }else{ - eputz("Usage: .headers on|off\n"); + eputz("Usage: .headers off|on|always\n"); rc = 1; } }else @@ -12102,7 +12140,8 @@ static int do_meta_command(const char *zLine, ShellState *p){ cli_printf(p->out, "%12.12s: %s\n","explain", p->mode.autoExplain ? "auto" : "off"); cli_printf(p->out, "%12.12s: %s\n","headers", - azBool[p->mode.spec.bTitles==QRF_Yes]); + p->mode.spec.bTitles==QRF_Always ? "always" : + azBool[p->mode.spec.bTitles==QRF_Yes]); if( p->mode.spec.eStyle==QRF_STYLE_Column || p->mode.spec.eStyle==QRF_STYLE_Box || p->mode.spec.eStyle==QRF_STYLE_Table @@ -14176,7 +14215,7 @@ int SQLITE_CDECL main(int argc, char **argv){ modeSetStr(&data.mode.spec.zNull, cmdline_option_value(argc,argv,++i)); }else if( cli_strcmp(z,"-header")==0 ){ - data.mode.spec.bTitles = QRF_Yes; + data.mode.spec.bTitles = QRF_Always; data.mode.mFlags |= MFLG_HDR; }else if( cli_strcmp(z,"-noheader")==0 ){ data.mode.spec.bTitles = QRF_No; diff --git a/src/tclsqlite.c b/src/tclsqlite.c index bee03fe3d9..cd13358012 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -2088,7 +2088,7 @@ static void DbHookCmd( ** -style eStyle ** -esc eEsc ** -text eText -** -title eTitle, bTitle +** -title eTitle, bTitles ** -blob eBlob ** -wordwrap bWordWrap ** -textjsonb bTextJsonb @@ -2108,6 +2108,26 @@ static void DbHookCmd( ** -rowsep zRowSep ** -tablename zTableName ** -null zNull +** +** The TCL -title option maps to eTitle and bTitles as follows: +** +** -title eTitle bTitles +** ------- ----------------- ------------ +** off QRF_TEXT_Auto QRF_Off +** on QRF_TEXT_Auto QRF_On +** always QRF_TEXT_Auto QRF_Always +** auto QRF_TEXT_Auto QRF_On +** csv QRF_TEXT_Csv QRF_On +** html QRF_TEXT_Html QRF_On +** json QRF_TEXT_Json QRF_On +** plain QRF_TEXT_Plain QRF_On +** relaxed QRF_TEXT_Relaxed QRF_On +** sql QRF_TEXT_Sql QRF_On +** tcl QRF_TEXT_Tcl QRF_On +** +** Note in particular that for the last seven entries ("csv" through "tcl"), +** the CLI .mode --title option sets bTitles to QRF_Always, whereas this TCL +** interface set it to QRF_On. */ static int dbQrf(SqliteDb *pDb, int objc, Tcl_Obj *const*objv){ #ifndef SQLITE_QRF_H diff --git a/test/modeA.sql b/test/modeA.sql index 62de2ecdb6..675b6c39aa 100644 --- a/test/modeA.sql +++ b/test/modeA.sql @@ -406,3 +406,88 @@ SELECT * FROM t1; 2 | | 2 (2 rows) END + +# Variations on ".mode -title" +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1("a b", "c'""<""'d", "xyz", "123"); +.testcase 1000 +.mode box -reset -title off -title plain +SELECT * FROM t1; +.check <