int bRTrim; /* Trim trailing space */
rc = sqlite3_step(p->pStmt);
- if( rc!=SQLITE_ROW || nColumn==0 ){
+ if( nColumn==0 ){
+ return; /* Not a query. No results every shown. */
+ }
+ if( rc!=SQLITE_ROW && (rc!=SQLITE_DONE || p->spec.bTitles!=QRF_Always) ){
return; /* No output */
}
if( qrfColDataEnlarge(&data) ) return;
assert( data.az!=0 );
- /* Load the column header names and all cell content into data */
- if( p->spec.bTitles==QRF_Yes ){
+ /* Load the column header names into data */
+ if( p->spec.bTitles>=QRF_Yes ){
unsigned char saved_eText = p->spec.eText;
p->spec.eText = p->spec.eTitle;
memset(data.abNum, 0, nColumn);
if( nNL ) data.bMultiRow = 1;
}
p->spec.eText = saved_eText;
- p->nRow++;
}
- do{
+
+ /* Load query results into data */
+ while( rc==SQLITE_ROW && p->iErr==SQLITE_OK ){
if( data.n+nColumn > data.nAlloc ){
if( qrfColDataEnlarge(&data) ) return;
}
if( nNL ) data.bMultiRow = 1;
}
p->nRow++;
- }while( sqlite3_step(p->pStmt)==SQLITE_ROW && p->iErr==SQLITE_OK );
+ rc = sqlite3_step(p->pStmt);
+ }
if( p->iErr ){
qrfColDataFree(&data);
return;
** body. isTitleDataSeparator will be true if we are doing (1).
*/
if( (i==0 || data.bMultiRow) && i+nColumn<data.n ){
- int isTitleDataSeparator = (i==0 && p->spec.bTitles==QRF_Yes);
+ int isTitleDataSeparator = (i==0 && p->spec.bTitles>=QRF_Yes);
if( isTitleDataSeparator ){
qrfLoadAlignment(&data, p);
}
}
/* Draw the line across the bottom of the table */
- if( p->spec.bBorder!=QRF_No ){
+ if( p->spec.bBorder!=QRF_No || p->nRow==0 ){
switch( p->spec.eStyle ){
case QRF_STYLE_Box:
qrfBoxSeparator(p->pOut, &data, BOX_R12, BOX_124, BOX_R14, 0);
break;
}
}
+ if( p->spec.bRowCount==QRF_Yes ){
+ sqlite3_int64 n = p->nRow;
+ sqlite3_str_appendf(p->pOut, "(%d row%s)\n", n, n==1 ? "" : "s");
+ }
qrfWrite(p);
qrfColDataFree(&data);
break;
}
case QRF_STYLE_Html: {
- if( p->nRow==0 && p->spec.bTitles==QRF_Yes ){
+ if( p->nRow==0 && p->spec.bTitles>=QRF_Yes ){
sqlite3_str_append(p->pOut, "<TR>", 4);
for(i=0; i<p->nCol; i++){
const char *zCName = sqlite3_column_name(p->pStmt, i);
}else{
sqlite3_str_appendf(p->pOut,"INSERT INTO %s",p->spec.zTableName);
}
- if( p->spec.bTitles==QRF_Yes ){
+ if( p->spec.bTitles>=QRF_Yes ){
for(i=0; i<p->nCol; i++){
const char *zCName = sqlite3_column_name(p->pStmt, i);
if( qrf_need_quote(zCName) ){
break;
}
default: { /* QRF_STYLE_List */
- if( p->nRow==0 && p->spec.bTitles==QRF_Yes ){
+ if( p->nRow==0 && p->spec.bTitles>=QRF_Yes ){
int saved_eText = p->spec.eText;
p->spec.eText = p->spec.eTitle;
for(i=0; i<p->nCol; i++){
size_t sz; /* Size of pSpec[], based on pSpec->iVersion */
memset(p, 0, sizeof(*p));
p->pzErr = pzErr;
- if( pSpec->iVersion>1 ){
+ if( pSpec->iVersion>2 ){
qrfError(p, SQLITE_ERROR,
"unusable sqlite3_qrf_spec.iVersion (%d)",
pSpec->iVersion);
if( p->spec.eText>QRF_TEXT_Relaxed ) p->spec.eText = QRF_Auto;
if( p->spec.eTitle>QRF_TEXT_Relaxed ) p->spec.eTitle = QRF_Auto;
if( p->spec.eBlob>QRF_BLOB_Size ) p->spec.eBlob = QRF_Auto;
+ if( pSpec->iVersion<=1 ){
+ p->spec.bRowCount = 0;
+ }
qrf_reinit:
switch( p->spec.eStyle ){
case QRF_Auto: {
void *pRenderArg; /* First argument to the xRender callback */
void *pWriteArg; /* First argument to the xWrite callback */
char **pzOutput; /* Storage location for output string */
+ /* The following are available in iVersion 2 and later */
+ unsigned char bRowCount; /* Show the number of rows at end of each query */
/* Additional fields may be added in the future */
};
#define QRF_No 1 /* Alternate spelling for QRF_SW_Off */
#define QRF_Yes 2 /* Alternate spelling for QRF_SW_On */
+/*
+** Additional value allowed for bTitles
+*/
+#define QRF_Always 3 /* Always show titles, even if no rows */
+
/*
** Possible alignment values alignment settings
**
-C Reduce\sdivergence\sbetween\sext/misc/fossildelta.c,\nthe\sdelta\scode\sin\sext/rbu/sqlite3rbu.c,\sand\sFossil\sitself.
-D 2026-07-06T16:26:30.821
+C Add\snew\soption\s"bRowCount"\sto\sQRF.\s\sAdd\sQRF_Always\sas\sa\spossibility\sfor\nbTitles.\s\sUse\sthese\sfeature\sto\senhance\s".mode\spsql"\sto\sbe\sa\sbetter\napproximation\sof\swhat\spsql\sactually\sdoes.
+D 2026-07-06T19:24:15.260
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F ext/misc/zorder.c bddff2e1b9661a90c95c2a9a9c7ecd8908afab5763256294dd12d609d4664eee
F ext/qrf/README.md 9e644615d7d7b77ef7e9db798765679e50c5ed12eda48bce21c9ef9eb4715e9d
F ext/qrf/dev-notes.md e68a6d91ce4c7eb296ef2daadc2bb79c95c317ad15b9fafe40850c67b29c2430
-F ext/qrf/qrf.c c8e1badc5f48dcf5e31fb6a4c9da786d64f981784be116a55b5503127d27eb17
-F ext/qrf/qrf.h 7a832022bc3f40dc09fff1cb6f18025395a19313de090265a4a1d22bb9b400be
+F ext/qrf/qrf.c 99c3ab46eedbc68211296e58797d601a80667f75a8db746a75cb92ec20ca923a
+F ext/qrf/qrf.h fcbc33578176b2fd9dda8310d8f6ba4eaf4f75052f466dfe682802e6664df8bd
F ext/rbu/rbu.c 801450b24eaf14440d8fd20385aacc751d5c9d6123398df41b1b5aa804bf4ce8
F ext/rbu/rbu1.test 25870dd7db7eb5597e2b4d6e29e7a7e095abf332660f67d89959552ce8f8f255
F ext/rbu/rbu10.test 7c22caa32c2ff26983ca8320779a31495a6555737684af7aba3daaf762ef3363
F src/resolve.c 54395ee97eb710e695202d4112cf2b1c1c7767a57afcea745df71abb1c917768
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
F src/select.c f553420eaf5c72a49cef786621eea79dd8c4411671839fb05250bb49ca74a0a0
-F src/shell.c.in cabdf85361fa9ac9c9f6ee900fb7cce012984917583a2302c03ef40829e6a90c
+F src/shell.c.in c26f8aca2312ed8a8be1f63f887c23f45e37911d4ede17f9c6b9d7897d574b80
F src/sqlite.h.in d9ec41feb4cd804e68b174328b43beb3f1f71bba13e1c7a439efb826d301cccc
F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
F src/sqlite3ext.h 0efd4723bad9124ea1f581d9f1ea0254ac1c6f3e5fb29e4f3dcf36c72485a456
F test/mmapcorrupt.test 470fb44fe92e99c1d23701d156f8c17865f5b027063c9119dcfdb842791f4465
F test/mmapfault.test d4c9eff9cd8c2dc14bc43e71e042f175b0a26fe3
F test/mmapwarm.test 2272005969cd17a910077bd5082f70bc1fefad9a875afec7fc9af483898ecaf3
-F test/modeA.sql 859e608297646e2d573e431507f38e465bc1513fa5862916ec8fb20a1d6314a4
+F test/modeA.sql b3110eb88e3d7eb86bbf0e52e8d45bde036608baa3c98fbce07dfe3eab1928e1
F test/multiplex.test d74c034e52805f6de8cc5432cef8c9eb774bb64ec29b83a22effc8ca4dac1f08
F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a
F test/multiplex3.test fac575e0b1b852025575a6a8357701d80933e98b5d2fe6d35ddaa68f92f6a1f7
F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939
F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
F test/shell-prompt.sql 8a320a78812d75349bd5e1a95178f7ff1f2194bb4cceee572865d127c51684b3
-F test/shell1.test 72893bc7957ff6402552ce4e31aa98a94478fa91bf5c4009a458711ad2e6efac
+F test/shell1.test df7a33a7056220ae12966228731658b36c98c22ba27e114bbf2785ff5b84fea2
F test/shell2.test e0842ea6a28f86f1ef235e117b3a381d16b999e7bdd9655b1de663026dd8ea27
F test/shell3.test 91efdd545097a61a1f72cf79c9ad5b49da080f3f10282eaf4c3c272cd1012db2
F test/shell4.test e25580a792b7b54560c3a76b6968bd8189261f38979fe28e6bc6312c5db280db
F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
-P aeb85fdfca0a546cfd3ef26efd58b9ffc8d5fda15562558d8479dee55321596f
-R 1ee393656bab6e0046f97e98b14c8cbd
+P 7f49a7a90eda01753c0dff65197bd7bc48a751e24a46919d30af6e2baf0788fc
+R d458787ae041129a9286190077b172aa
+T *branch * psql-mode
+T *sym-psql-mode *
+T -sym-trunk *
U drh
-Z 617b711efa64511b929c4bdfdf8bb7a8
+Z e2f6af93169d3a630e55ed3303140615
# Remove this line to create a well-formed Fossil manifest.
-branch trunk
-tag trunk
+branch psql-mode
+tag psql-mode
-7f49a7a90eda01753c0dff65197bd7bc48a751e24a46919d30af6e2baf0788fc
+600c6b464050078b38cc688668148f684243e7c5f08d0aae4d5f72aa6f985482
{ "list", 2, 1, 9, 1, 1, 0, 1, 12, 0, 0 },
{ "markdown", 0, 0, 9, 1, 1, 0, 2, 13, 2, 0 },
{ "off", 0, 0, 0, 0, 0, 0, 0, 14, 0, 0 },
- { "psql", 0, 0, 9, 1, 1, 0, 2, 19, 2, 1 },
+ { "psql", 0, 0, 9, 1, 1, 0, 3, 19, 2, 1 },
{ "qbox", 0, 0, 10, 2, 1, 0, 2, 1, 2, 0 },
{ "quote", 4, 1, 10, 2, 2, 0, 1, 12, 0, 0 },
{ "split", 0, 0, 9, 1, 1, 0, 1, 2, 2, 2 },
** 1: plain v----------' 0: n/a
** 2: sql 0: auto 1: no
** 3: csv 1: as-text 2: yes
- ** 4: html 2: sql
+ ** 4: html 2: sql 3: always
** 5: c 3: hex
** 6: json 4: c
** 5: json
free(p->spec.zTableName);
free(p->spec.zNull);
memset(p, 0, sizeof(*p));
- p->spec.iVersion = 1;
+ p->spec.iVersion = 2;
p->autoExplain = autoExplain;
}
pM->spec.eTitle = pI->eHdr;
if( pI->mFlg & 0x01 ){
pM->spec.bBorder = QRF_No;
+ pM->spec.bRowCount = QRF_Yes;
}else{
pM->spec.bBorder = QRF_Auto;
}
** already been freed and zeroed prior to calling this routine.
*/
static void modeDefault(ShellState *p){
- p->mode.spec.iVersion = 1;
+ p->mode.spec.iVersion = 2;
p->mode.autoExplain = 1;
if( stdin_is_interactive || stdout_is_console ){
modeChange(p, MODE_TTY);
** 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
+** 3: Show "--titles 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"
**
** Inputs are:
**
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 */
+ v &= 3;
+ /* At this point v means:
+ ** 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
+ ** If bTitles is QRF_Always, convert 2->3, 3->5. If bTitles is not
+ ** QRF_Always, convert 3->4.
+ */
+ if( p->mode.spec.bTitles==QRF_Always ){
+ if( v==3 ) v = 5;
+ if( v==2 ) v = 3;
+ }else{
+ if( v==3 ) v = 4;
+ }
+ return v;
}
/*
** "tcl", or "json". "off" means show the text as-is.
** "on" is an alias for "sql".
** --reset Changes all mode settings back to their default.
+** --rowcount BOOLEAN Show "(N rows)" at the end of output.
** --rowsep STRING Use STRING as the row separator
** --sw|--screenwidth N Declare the screen width of the output device
** to be N characters. An attempt may be made to
int saved_eMode = p->mode.eMode;
modeFree(&p->mode);
modeChange(p, saved_eMode);
+ }else if( optionMatch(z,"rowcount") ){
+ if( i+1>=nArg ){
+ dotCmdError(p, i, "missing argument", 0);
+ return 1;
+ }
+ p->mode.spec.bRowCount = booleanValue(azArg[++i]) ? QRF_Yes : QRF_No;
+ chng = 1;
}else if( optionMatch(z,"screenwidth") || optionMatch(z,"sw") ){
if( (++i)>=nArg ){
dotCmdError(p, i-1, "missing argument", 0);
return 1;
}
k = pickStr(azArg[++i],&zErr,
- "off","on","plain","sql","csv","html","tcl","json","");
- /* 0 1 2 3 4 5 6 7 */
+ "off","on","plain","sql","csv","html","tcl","json","always","");
+ /* 0 1 2 3 4 5 6 7 8 */
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.mFlags &= ~MFLG_HDR;
- p->mode.spec.eTitle = k>1 ? k-1 : aModeInfo[p->mode.eMode].eHdr;
+ if( k==8 ){
+ p->mode.spec.bTitles = QRF_Always;
+ }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;
+ }
chng = 1;
}else if( optionMatch(z,"widths") || optionMatch(z,"width") ){
int nWidth = 0;
){
sqlite3_str_appendf(pDesc," --quote %s",qrfQuoteNames[p->mode.spec.eText]);
}
+ if( bAll
+ || ((p->mode.spec.bRowCount==QRF_Yes) != ((pI->mFlg&1)!=0))
+ ){
+ sqlite3_str_appendf(pDesc," --rowcount %s",
+ p->mode.spec.bRowCount==QRF_Yes ? "on" : "off");
+ }
zSetting = aModeStr[pI->eRSep];
if( bAll || (zSetting && cli_strcmp(zSetting,p->mode.spec.zRowSep)!=0) ){
sqlite3_str_appendf(pDesc, " --rowsep ");
}else if( k==2 ){
sqlite3_str_appendall(pDesc, " --titles on");
}else if( k==3 ){
+ sqlite3_str_appendall(pDesc, " --titles always");
+ }else if( k>=4 ){
static const char *azTitle[] =
{ "plain", "sql", "csv", "html", "tcl", "json"};
sqlite3_str_appendf(pDesc, " --titles %s",
azTitle[p->mode.spec.eTitle-1]);
+ if( k==5 ){
+ sqlite3_str_appendf(pDesc, " --titles always");
+ }
}
if( p->mode.spec.nWidth>0 && (bAll || pI->eCx==2) ){
int ii;
.testcase 140
.mode -v
.check <<END
-.mode qbox --align "" --border on --blob-quote auto --colsep "" --escape auto --limits on --multiinsert 3000 --null "NULL" --quote relaxed --rowsep "" --sw auto --tablename "" --textjsonb on --titles on --widths "" --wordwrap off --wrap 10
+.mode qbox --align "" --border on --blob-quote auto --colsep "" --escape auto --limits on --multiinsert 3000 --null "NULL" --quote relaxed --rowcount off --rowsep "" --sw auto --tablename "" --textjsonb on --titles on --widths "" --wordwrap off --wrap 10
END
.testcase 150 --error-prefix "Error:"
.mode foo
.mode --limits 0,0,0
.mode -v
.check <<END
-.mode box --align "" --border on --blob-quote auto --colsep "" --escape auto --limits off --multiinsert 0 --null "" --quote off --rowsep "" --sw 0 --tablename "" --textjsonb off --titles on --widths "" --wordwrap off
+.mode box --align "" --border on --blob-quote auto --colsep "" --escape auto --limits off --multiinsert 0 --null "" --quote off --rowcount off --rowsep "" --sw 0 --tablename "" --textjsonb off --titles on --widths "" --wordwrap off
END
.testcase 400
SELECT hex(randomblob(100)) c;
.check -glob "*: string or blob too big"
.limit length 10000000
+
+# "psql" mode.
+#
+.testcase 900
+.mode --reset psql -v
+.check <<END
+.mode psql --align "" --border off --blob-quote auto --colsep "" --escape auto --limits off --multiinsert 0 --null "" --quote off --rowcount on --rowsep "" --sw 0 --tablename "" --textjsonb off --titles always --widths "" --wordwrap off
+END
+.testcase 901
+.mode
+.check <<END
+.mode psql
+END
+.testcase 902
+.mode --rowcount off
+.mode
+.check <<END
+.mode psql --rowcount off
+END
{SELECT ieee754_mantissa(47.49) AS M, ieee754_exponent(47.49) AS E;}
} {0 { M | E
------------------+-----
- 6683623321994527 | -47}}
+ 6683623321994527 | -47
+(1 row)}}
do_test_with_ansi_output shell1-8.5 {
catchcmd ":memory: --box" {
create table t(a text, b int);