return 1;
}
+/*
+** Check to see if z[] is a valid VT100 escape. If it is, then
+** return the number of bytes in the escape sequence. Return 0 if
+** z[] is not a VT100 escape.
+**
+** This routine assumes that z[0] is \033 (ESC).
+*/
+static int qrfIsVt100(const unsigned char *z){
+ int i;
+ if( z[1]!='[' ) return 0;
+ i = 2;
+ while( z[i]>=0x30 && z[i]<=0x3f ){ i++; }
+ while( z[i]>=0x20 && z[i]<=0x2f ){ i++; }
+ if( z[i]<0x40 || z[i]>0x7e ) return 0;
+ return i+1;
+}
+
/*
** Return the length of a string in display characters.
** Multibyte UTF8 characters count as a single character
** for single-width characters, or as two characters for
** double-width characters.
*/
-static int qrfDisplayLength(const char *z){
+static int qrfDisplayLength(const char *zIn){
+ const unsigned char *z = (const unsigned char*)zIn;
int n = 0;
while( *z ){
- if( (0x80&z[0])==0 ){
+ if( z[0]<' ' ){
+ int k;
+ if( z[0]=='\033' && (k = qrfIsVt100(z))>0 ){
+ z += k;
+ }else{
+ z++;
+ }
+ }else if( (0x80&z[0])==0 ){
n++;
z++;
}else{
int u = 0;
- int len = sqlite3_qrf_decode_utf8((const unsigned char*)z, &u);
+ int len = sqlite3_qrf_decode_utf8(z, &u);
z += len;
n += sqlite3_qrf_wcwidth(u);
}
}
}
-/*
-** Check to see if z[] is a valid VT100 escape. If it is, then
-** return the number of bytes in the escape sequence. Return 0 if
-** z[] is not a VT100 escape.
-**
-** This routine assumes that z[0] is \033 (ESC).
-*/
-static int qrfIsVt100(const unsigned char *z){
- int i;
- if( z[1]!='[' ) return 0;
- i = 2;
- while( z[i]>=0x30 && z[i]<=0x3f ){ i++; }
- while( z[i]>=0x20 && z[i]<=0x2f ){ i++; }
- if( z[i]<0x40 || z[i]>0x7e ) return 0;
- return i+1;
-}
-
/*
** z[] is a line of text that is to be displayed the box or table or
** similar tabular formats. z[] might contain control characters such
if( mxWidth<0 ) mxWidth = -mxWidth;
if( mxWidth==0 ) mxWidth = 1000000;
i = j = n = 0;
- while( n<mxWidth ){
+ while( n<=mxWidth ){
unsigned char c = z[i];
if( c>=0xc0 ){
int u;
i++;
}
}
- if( n>=mxWidth && bWordWrap ){
+ if( n>mxWidth && bWordWrap ){
/* Perhaps try to back up to a better place to break the line */
for(k=i; k>i/2; k--){
if( isspace(z[k-1]) ) break;
}else{
k = i;
}
- if( n>=mxWidth && z[i]>=' ' ){
+ if( n>mxWidth && z[i]>=' ' ){
*pzTail = &z[i];
}else if( z[i]=='\r' && z[i+1]=='\n' ){
*pzTail = z[i+2] ? &z[i+2] : 0;
}else{
*pzTail = &z[i+1];
}
+ if( n>=mxWidth && k>0 && z[k-1]==' ' ) k--;
zOut = sqlite3_malloc64( j+1 );
if( zOut==0 ){
qrfOom(p);
n += sqlite3_qrf_wcwidth(u);
continue;
}
- if( c>=' ' ){
+ if( c>=' ' || c=='\033' ){
n++;
zOut[j++] = z[i++];
continue;
}
/* Capture the column names as the first row of data */
- for(i=0; i<nColumn; i++){
- const unsigned char *zNotUsed;
- int wx = p->actualWidth[i];
- if( wx==0 ){
- wx = p->spec.mxWidth;
- }
- if( wx<0 ) wx = -wx;
- uz = (const unsigned char*)sqlite3_column_name(p->pStmt,i);
- if( uz==0 ) uz = (unsigned char*)"";
- qrfEncodeText(p, aCol[i], (const char*)uz);
- uz = (unsigned char*)sqlite3_str_value(aCol[i]);
- azData[i] = qrfTableCell(p, uz, &zNotUsed, wx, bw);
- if( p->iErr ) goto qrf_column_end;
- aiDspyWth[i] = qrfDisplayLength(azData[i]);
- if( aiDspyWth[i]>p->actualWidth[i] ){
- p->actualWidth[i] = aiDspyWth[i];
- }
- sqlite3_str_reset(aCol[i]);
+ if( p->spec.bColumnNames ){
+ int saved_eText = p->spec.eText;
+ p->spec.eText = QRF_TEXT_Off;
+ for(i=0; i<nColumn; i++){
+ const unsigned char *zNotUsed;
+ int wx = p->actualWidth[i];
+ if( wx==0 ){
+ wx = p->spec.mxWidth;
+ }
+ if( wx<0 ) wx = -wx;
+ uz = (const unsigned char*)sqlite3_column_name(p->pStmt,i);
+ if( uz==0 ) uz = (unsigned char*)"";
+ qrfEncodeText(p, aCol[i], (const char*)uz);
+ uz = (unsigned char*)sqlite3_str_value(aCol[i]);
+ azData[i] = qrfTableCell(p, uz, &zNotUsed, wx, bw);
+ if( p->iErr ) goto qrf_column_end;
+ aiDspyWth[i] = qrfDisplayLength(azData[i]);
+ if( aiDspyWth[i]>p->actualWidth[i] ){
+ p->actualWidth[i] = aiDspyWth[i];
+ }
+ sqlite3_str_reset(aCol[i]);
+ }
+ p->spec.eText = saved_eText;
}
/* Capture column content for all rows */
case QRF_STYLE_Table: {
colSep = " | ";
rowSep = " |\n";
- qrfRowSeparator(p, "+");
- sqlite3_str_append(p->pOut, "| ", 2);
- for(i=0; i<nColumn; i++){
- w = p->actualWidth[i];
- n = aiDspyWth[i];
- sqlite3_str_appendchar(p->pOut, (w-n)/2, ' ');
- sqlite3_str_appendall(p->pOut, azData[i]);
- sqlite3_str_appendchar(p->pOut, (w-n+1)/2, ' ');
- sqlite3_str_append(p->pOut, i==nColumn-1?" |\n":" | ", 3);
+ if( p->spec.bColumnNames==QRF_SW_On ){
+ qrfRowSeparator(p, "+");
+ sqlite3_str_append(p->pOut, "| ", 2);
+ for(i=0; i<nColumn; i++){
+ w = p->actualWidth[i];
+ n = aiDspyWth[i];
+ sqlite3_str_appendchar(p->pOut, (w-n)/2, ' ');
+ sqlite3_str_appendall(p->pOut, azData[i]);
+ sqlite3_str_appendchar(p->pOut, (w-n+1)/2, ' ');
+ sqlite3_str_append(p->pOut, i==nColumn-1?" |\n":" | ", 3);
+ }
}
qrfRowSeparator(p, "+");
break;
case QRF_STYLE_Markdown: {
colSep = " | ";
rowSep = " |\n";
- sqlite3_str_append(p->pOut, "| ", 2);
- for(i=0; i<nColumn; i++){
- w = p->actualWidth[i];
- n = aiDspyWth[i];
- sqlite3_str_appendchar(p->pOut, (w-n)/2, ' ');
- sqlite3_str_appendall(p->pOut, azData[i]);
- sqlite3_str_appendchar(p->pOut, (w-n+1)/2, ' ');
- sqlite3_str_append(p->pOut, i==nColumn-1 ? " |\n" : " | ", 3);
+ if( p->spec.bColumnNames==QRF_SW_On ){
+ sqlite3_str_append(p->pOut, "| ", 2);
+ for(i=0; i<nColumn; i++){
+ w = p->actualWidth[i];
+ n = aiDspyWth[i];
+ sqlite3_str_appendchar(p->pOut, (w-n)/2, ' ');
+ sqlite3_str_appendall(p->pOut, azData[i]);
+ sqlite3_str_appendchar(p->pOut, (w-n+1)/2, ' ');
+ sqlite3_str_append(p->pOut, i==nColumn-1 ? " |\n" : " | ", 3);
+ }
+ qrfRowSeparator(p, "|");
}
- qrfRowSeparator(p, "|");
break;
}
case QRF_STYLE_Box: {
colSep = " " BOX_13 " ";
rowSep = " " BOX_13 "\n";
qrfBoxSeparator(p, BOX_23, BOX_234, BOX_34);
- sqlite3_str_appendall(p->pOut, BOX_13 " ");
- for(i=0; i<nColumn; i++){
- w = p->actualWidth[i];
- n = aiDspyWth[i];
- sqlite3_str_appendchar(p->pOut, (w-n)/2, ' ');
- sqlite3_str_appendall(p->pOut, azData[i]);
- sqlite3_str_appendchar(p->pOut, (w-n+1)/2, ' ');
- sqlite3_str_appendall(p->pOut, i==nColumn-1?" "BOX_13"\n":" "BOX_13" ");
+ if( p->spec.bColumnNames==QRF_SW_On ){
+ sqlite3_str_appendall(p->pOut, BOX_13 " ");
+ for(i=0; i<nColumn; i++){
+ w = p->actualWidth[i];
+ n = aiDspyWth[i];
+ sqlite3_str_appendchar(p->pOut, (w-n)/2, ' ');
+ sqlite3_str_appendall(p->pOut, azData[i]);
+ sqlite3_str_appendchar(p->pOut, (w-n+1)/2, ' ');
+ sqlite3_str_appendall(p->pOut, i==nColumn-1?" "BOX_13"\n":" "BOX_13" ");
+ }
+ qrfBoxSeparator(p, BOX_123, BOX_1234, BOX_134);
}
- qrfBoxSeparator(p, BOX_123, BOX_1234, BOX_134);
break;
}
}
if( p->spec.bTextJsonb==QRF_SW_Auto ){
p->spec.bTextJsonb = QRF_SW_Off;
}
+ if( p->spec.zColumnSep==0 ) p->spec.zColumnSep = ",";
+ if( p->spec.zRowSep==0 ) p->spec.zRowSep = "\n";
}
/*
}
default: { /* QRF_STYLE_List */
if( p->nRow==0 && p->spec.bColumnNames==QRF_SW_On ){
+ int saved_eText = p->spec.eText;
+ if( p->spec.eText!=QRF_TEXT_Csv ) p->spec.eText = QRF_TEXT_Off;
for(i=0; i<p->nCol; i++){
const char *zCName = sqlite3_column_name(p->pStmt, i);
if( i>0 ) sqlite3_str_appendall(p->pOut, p->spec.zColumnSep);
}
sqlite3_str_appendall(p->pOut, p->spec.zRowSep);
qrfWrite(p);
+ p->spec.eText = saved_eText;
}
for(i=0; i<p->nCol; i++){
if( i>0 ) sqlite3_str_appendall(p->pOut, p->spec.zColumnSep);
-C The\sqrf-tester.c\sprogram\swas\sremoved\sin\sthe\sprevious\scheck-in,\sbut\sthe\nmakefile\srules\sto\sbuild\sit\swere\snot.\s\sThis\scheck-in\sfixes\sthat\soversight.
-D 2025-11-05T18:35:54.490
+C Further\stesting\sand\sbug\sfixing.
+D 2025-11-05T22:33:47.393
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F ext/misc/zipfile.c 09e6e3a3ff40a99677de3c0bc6569bd5f4709b1844ac3d1c1452a456c5a62f1c
F ext/misc/zorder.c bddff2e1b9661a90c95c2a9a9c7ecd8908afab5763256294dd12d609d4664eee
F ext/qrf/README.md 5f12f91104d5df8ba8783e2240635698569ce54935e721b3a4e93abf4e00ddcb
-F ext/qrf/qrf.c 8dd8f3245f86686ddacdf92354655dd290e5ed7668f6cf7d6bcf1f9453160062
+F ext/qrf/qrf.c 4286a30696a106a39f7fe43f65384a239157c2e1bb7e55b92600086799b5c00c
F ext/qrf/qrf.h 98e02eded10848c42053d37e56bd15ab69cb6fe9fc1f1ca05823d9047e9c8509
F ext/rbu/rbu.c 801450b24eaf14440d8fd20385aacc751d5c9d6123398df41b1b5aa804bf4ce8
F ext/rbu/rbu1.test 25870dd7db7eb5597e2b4d6e29e7a7e095abf332660f67d89959552ce8f8f255
F src/sqliteLimit.h fe70bd8983e5d317a264f2ea97473b359faf3ebb0827877a76813f5cf0cdc364
F src/status.c 7565d63a79aa2f326339a24a0461a60096d0bd2bce711fefb50b5c89335f3592
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
-F src/tclsqlite.c 45476ca0b3088504c7e296ce52b4ab0b6b9af57132abeca2867fbcf1a50f84af
+F src/tclsqlite.c be2a4b5593298d1937e7c39b94e552eb74d8ff2297db8cda9c4bac41acc67d74
F src/tclsqlite.h 614b3780a62522bc9f8f2b9fb22689e8009958e7aa77e572d0f3149050af348a
F src/test1.c f880ab766eeedf2c063662bd9538b923fd42c4341b7bfc2150a6d93ab8b9341c
F src/test2.c 62f0830958f9075692c29c6de51b495ae8969e1bef85f239ffcd9ba5fb44a5ff
F test/progress.test ebab27f670bd0d4eb9d20d49cef96e68141d92fb
F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
F test/pushdown.test 46a626ef1c0ca79b85296ff2e078b9da20a50e9b804b38f441590c3987580ddd
-F test/qrf01.test 84699d3a41d371dda947a9ca994e8bc19cbc3e43b83d6716c6b43b1b6a7c1b6f
+F test/qrf01.test 12b211db9dcd5926d657b1ee64b40db059270133d8b0338d0666dbcf301ec35b
+F test/qrf02.test 39b4afdc000bedccdafc0aecf17638df67a67aaa2d2942865ae6abcc48ba0e92
F test/queryonly.test 5f653159e0f552f0552d43259890c1089391dcca
F test/quick.test 1681febc928d686362d50057c642f77a02c62e57
F test/quickcheck.test a4b7e878cd97e46108291c409b0bf8214f29e18fddd68a42bc5c1375ad1fb80a
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 633fe4fe584ae14ed6ced1ae137bf96a434a012423d70f0f93876737b0ca075c
-R 70942b4daded82313531eeb35df731ae
+P d1542696409b67635d0f172042642709452118d62102ebfab2ad3ff2b5732687
+R f4a27ec41a5221a665784b11fc4b6185
U drh
-Z 687a4d4793d1bb836e5c2deeb266cdf6
+Z c4401bada00b73794af6d52fe089dac2
# Remove this line to create a well-formed Fossil manifest.
-d1542696409b67635d0f172042642709452118d62102ebfab2ad3ff2b5732687
+d6b1bac15a692a69f3707fca721b57b3b283edb3850efa34ba42b02c3669fc71
** Based on provided options, format the results of the SQL statement(s)
** provided into human-readable form using the Query Result Formatter (QRF)
** and return the resuling text.
+**
+** Syntax: db format OPTIONS SQL
+**
+** OPTIONS may be:
+**
+** -style ("auto"|"box"|"column"|...) Output style
+** -esc ("auto"|"off"|"ascii"|"symbol") How to deal with ctrl chars
+** -text ("auto"|"off"|"sql"|"csv"|...) How to escape TEXT values
+** -blob ("auto"|"text"|"sql"|...) How to escape BLOB values
+** -columnnames ("auto"|"off"|"on") Show column names?
+** -wordwrap ("auto"|"off"|"on") Try to wrap at word boundry?
+** -textjsonb ("auto"|"off"|"on") Auto-convert JSONB to text?
+** -maxwidth NUMBER Default column width
+** -widths LIST-OF-NUMBERS Widths for individual columns
+** -columnsep TEXT Column separator text
+** -rowsep TEXT Row separator text
+** -tablename TEXT Table name for style "insert"
+** -null TEXT Text for NULL values
+**
+** A mapping from TCL "format" command options to sqlite3_qrf_spec fields
+** is below. Use this to reference the QRF documentation:
+**
+** TCL Option spec field
+** ---------- ----------
+** -style eStyle
+** -esc eEsc
+** -text eText
+** -blob eBlob
+** -columnnames bColumnNames
+** -wordwrap bWordWrap
+** -textjsonb bTextJsonb
+** -maxwidth mxWidth
+** -widths nWidth, aWidth
+** -columnsep zColumnSep
+** -rowsep zRowSep
+** -tablename zTableName
+** -null zNull
*/
static int dbQrf(SqliteDb *pDb, int objc, Tcl_Obj *const*objv){
#ifndef SQLITE_QRF_H
};
int style;
rc = Tcl_GetIndexFromObj(pDb->interp, objv[i+1], azStyles,
- "format style", 0, &style);
+ "format style (-style)", 0, &style);
if( rc ) goto format_failed;
qrf.eStyle = aStyleMap[style];
i++;
- }else if( strcmp(zArg,"-null")==0 ){
- qrf.zNull = Tcl_GetString(objv[i+1]);
- i++;
- }else if( strcmp(zArg,"-rowsep")==0 ){
- qrf.zRowSep = Tcl_GetString(objv[i+1]);
+ }else if( strcmp(zArg,"-esc")==0 ){
+ static const char *azEsc[] = {
+ "ascii", "auto", "off", "symbol", 0
+ };
+ static unsigned char aEscMap[] = {
+ QRF_ESC_Ascii, QRF_ESC_Auto, QRF_ESC_Off, QRF_ESC_Symbol
+ };
+ int esc;
+ rc = Tcl_GetIndexFromObj(pDb->interp, objv[i+1], azEsc,
+ "control character escape (-esc)", 0, &esc);
+ if( rc ) goto format_failed;
+ qrf.eEsc = aEscMap[esc];
i++;
- }else if( strcmp(zArg,"-columnsep")==0 ){
- qrf.zColumnSep = Tcl_GetString(objv[i+1]);
+ }else if( strcmp(zArg,"-text")==0 ){
+ static const char *azText[] = {
+ "auto", "csv", "html",
+ "json", "off", "sql",
+ "tcl", 0
+ };
+ static unsigned char aTextMap[] = {
+ QRF_TEXT_Auto, QRF_TEXT_Csv, QRF_TEXT_Html,
+ QRF_TEXT_Json, QRF_TEXT_Off, QRF_TEXT_Sql,
+ QRF_TEXT_Tcl
+ };
+ int txt;
+ rc = Tcl_GetIndexFromObj(pDb->interp, objv[i+1], azText,
+ "text encoding (-text)", 0, &txt);
+ if( rc ) goto format_failed;
+ qrf.eText = aTextMap[txt];
i++;
- }else if( strcmp(zArg,"-tablename")==0 ){
- qrf.zTableName = Tcl_GetString(objv[i+1]);
+ }else if( strcmp(zArg,"-blob")==0 ){
+ static const char *azBlob[] = {
+ "auto", "hex", "json",
+ "tcl", "text", "sql", 0
+ };
+ static unsigned char aBlobMap[] = {
+ QRF_BLOB_Auto, QRF_BLOB_Hex, QRF_BLOB_Json,
+ QRF_BLOB_Tcl, QRF_BLOB_Text, QRF_BLOB_Sql
+ };
+ int blob;
+ rc = Tcl_GetIndexFromObj(pDb->interp, objv[i+1], azBlob,
+ "BLOB encoding (-blob)", 0, &blob);
+ if( rc ) goto format_failed;
+ qrf.eBlob = aBlobMap[blob];
i++;
}else if( strcmp(zArg,"-columnnames")==0 ){
int v = 0;
}
}
i++;
+ }else if( strcmp(zArg,"-columnsep")==0 ){
+ qrf.zColumnSep = Tcl_GetString(objv[i+1]);
+ i++;
+ }else if( strcmp(zArg,"-rowsep")==0 ){
+ qrf.zRowSep = Tcl_GetString(objv[i+1]);
+ i++;
+ }else if( strcmp(zArg,"-tablename")==0 ){
+ qrf.zTableName = Tcl_GetString(objv[i+1]);
+ i++;
+ }else if( strcmp(zArg,"-null")==0 ){
+ qrf.zNull = Tcl_GetString(objv[i+1]);
+ i++;
}else{
Tcl_AppendResult(pDb->interp, "unknown option: ", zArg, (char*)0);
rc = TCL_ERROR;
│ BLOB │ │ Ἀμήν │
└──────┴─────┴───────┘
}
+do_test 1.11a {
+ set result "\n[db format -columnnames off {SELECT * FROM t1}]"
+} {
+┌──────┬─────┬───────┐
+│ 1 │ 2.5 │ three │
+│ BLOB │ │ Ἀμήν │
+└──────┴─────┴───────┘
+}
+do_test 1.11b {
+ set result "\n[db format -text sql {SELECT * FROM t1}]"
+} {
+┌─────────────┬─────┬─────────┐
+│ a │ b │ c │
+├─────────────┼─────┼─────────┤
+│ 1 │ 2.5 │ 'three' │
+│ x'424c4f42' │ │ 'Ἀμήν' │
+└─────────────┴─────┴─────────┘
+}
+do_test 1.12 {
+ set result "\n[db format -text csv {SELECT * FROM t1}]"
+} {
+┌────────────────────┬─────┬────────┐
+│ a │ b │ c │
+├────────────────────┼─────┼────────┤
+│ 1 │ 2.5 │ three │
+│ "\102\114\117\102" │ │ "Ἀμήν" │
+└────────────────────┴─────┴────────┘
+}
+do_test 1.13 {
+ set result "\n[db format -text csv -blob hex {SELECT * FROM t1}]"
+} {
+┌──────────┬─────┬────────┐
+│ a │ b │ c │
+├──────────┼─────┼────────┤
+│ 1 │ 2.5 │ three │
+│ 424c4f42 │ │ "Ἀμήν" │
+└──────────┴─────┴────────┘
+}
+do_test 1.14 {
+ catch {db format -text unk -blob hex {SELECT * FROM t1}} res
+ set res
+} {bad text encoding (-text) "unk": must be auto, csv, html, json, off, sql, or tcl}
+do_test 1.15 {
+ catch {db format -text sql -blob unk {SELECT * FROM t1}} res
+ set res
+} {bad BLOB encoding (-blob) "unk": must be auto, hex, json, tcl, text, or sql}
+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, line, list, markdown, quote, scanexp, or table}
+
do_test 1.20 {
set result "\n[db format -style box {SELECT * FROM t1}]"
} {
| BLOB | | Ἀμήν |
+------+-----+-------+
}
+do_test 1.31 {
+ set result "\n[db format -style table -columnnames off {SELECT * FROM t1}]"
+} {
++------+-----+-------+
+| 1 | 2.5 | three |
+| BLOB | | Ἀμήν |
++------+-----+-------+
+}
do_test 1.40 {
set result "\n[db format -style column {SELECT * FROM t1}]"
do_test 1.61 {
db format -style csv -columnnames on {SELECT * FROM t1}
} "a,b,c\r\n1,2.5,three\r\n\"\\102\\114\\117\\102\",,\"Ἀμήν\"\r\n"
+do_test 1.62 {
+ db format -style csv -columnnames on {SELECT a AS 'a x y', b, c FROM t1}
+} "\"a x y\",b,c\r\n1,2.5,three\r\n\"\\102\\114\\117\\102\",,\"Ἀμήν\"\r\n"
do_test 1.70 {
set result "\n[db format -style html {SELECT * FROM t1}]"
b = (NULL)
c = Ἀμήν
}
+do_test 1.102 {
+ set result "\n[db format -style line -null (NULL) \
+ -text sql {SELECT * FROM t1}]"
+} {
+a = 1
+b = 2.5
+c = 'three'
+
+a = x'424c4f42'
+b = (NULL)
+c = 'Ἀμήν'
+}
do_test 1.110 {
set result "\n[db format -style list {SELECT * FROM t1}]"
BLOB||Ἀμήν
}
do_test 1.112 {
+ set result "\n[db format -style list -columnnames on -text sql -null NULL \
+ {SELECT * FROM t1}]"
+} {
+a|b|c
+1|2.5|'three'
+x'424c4f42'|NULL|'Ἀμήν'
+}
+do_test 1.118 {
set rc [catch {db format -style list -columnnames unk {SELECT * FROM t1}} res]
lappend rc $res
} {1 {bad -columnnames "unk": must be auto, off, or on}}
| 1 | 2.5 | three |
| BLOB | | Ἀμήν |
}
+do_test 1.121 {
+ set result "\n[db format -style markdown -columnnames off {SELECT * FROM t1}]"
+} {
+| 1 | 2.5 | three |
+| BLOB | | Ἀμήν |
+}
do_test 1.130 {
set result "\n[db format -style quote {SELECT * FROM t1}]"
do_test 1.131 {
set result "\n[db format -style quote -columnnames on {SELECT * FROM t1}]"
} {
-'a','b','c'
+a,b,c
1,2.5,'three'
x'424c4f42',NULL,'Ἀμήν'
}
INSERT INTO t1 VALUES(1,2,'The quick fox jumps over the lazy brown dog.');
}
do_test 2.1 {
- set result "\n[db format -widths {10 -10 22} -wordwrap off \
+ set result "\n[db format -widths {5 -5 19} -wordwrap on \
+ {SELECT * FROM t1}]"
+} {
+┌───────┬───────┬─────────────────────┐
+│ a │ b │ c │
+├───────┼───────┼─────────────────────┤
+│ 1 │ 2 │ The quick fox jumps │
+│ │ │ over the lazy brown │
+│ │ │ dog. │
+└───────┴───────┴─────────────────────┘
+}
+do_test 2.2 {
+ set result "\n[db format -widths {5 -5 19} -wordwrap off \
+ {SELECT * FROM t1}]"
+} {
+┌───────┬───────┬─────────────────────┐
+│ a │ b │ c │
+├───────┼───────┼─────────────────────┤
+│ 1 │ 2 │ The quick fox jumps │
+│ │ │ over the lazy brown │
+│ │ │ dog. │
+└───────┴───────┴─────────────────────┘
+}
+do_test 2.3 {
+ set result "\n[db format -widths {5 -5 18} -wordwrap on \
+ {SELECT * FROM t1}]"
+} {
+┌───────┬───────┬────────────────────┐
+│ a │ b │ c │
+├───────┼───────┼────────────────────┤
+│ 1 │ 2 │ The quick fox │
+│ │ │ jumps over the │
+│ │ │ lazy brown dog. │
+└───────┴───────┴────────────────────┘
+}
+do_test 2.4 {
+ set result "\n[db format -widths {5 -5 -18} -wordwrap on \
+ {SELECT * FROM t1}]"
+} {
+┌───────┬───────┬────────────────────┐
+│ a │ b │ c │
+├───────┼───────┼────────────────────┤
+│ 1 │ 2 │ The quick fox │
+│ │ │ jumps over the │
+│ │ │ lazy brown dog. │
+└───────┴───────┴────────────────────┘
+}
+do_test 2.5 {
+ set result "\n[db format -widths {5 -5 18} -wordwrap off \
{SELECT * FROM t1}]"
} {
-┌────────────┬────────────┬────────────────────────┐
-│ a │ b │ c │
-├────────────┼────────────┼────────────────────────┤
-│ 1 │ 2 │ The quick fox jumps ov │
-│ │ │ er the lazy brown dog. │
-└────────────┴────────────┴────────────────────────┘
+┌───────┬───────┬─────────────────────┐
+│ a │ b │ c │
+├───────┼───────┼─────────────────────┤
+│ 1 │ 2 │ The quick fox jumps │
+│ │ │ over the lazy brow │
+│ │ │ n dog. │
+└───────┴───────┴─────────────────────┘
+}
+
+
+do_execsql_test 3.0 {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2,unistr('abc\u001b[1;31m123\u001b[0mxyz'));
+}
+do_test 3.1 {
+ set result "\n[db format {SELECT * FROM t1}]"
+} {
+┌───┬───┬────────────────────────┐
+│ a │ b │ c │
+├───┼───┼────────────────────────┤
+│ 1 │ 2 │ abc^[[1;31m123^[[0mxyz │
+└───┴───┴────────────────────────┘
+}
+do_test 3.2 {
+ set result "\n[db format -esc off {SELECT * FROM t1}]"
+ string map [list \033 X] $result
+} {
+┌───┬───┬───────────┐
+│ a │ b │ c │
+├───┼───┼───────────┤
+│ 1 │ 2 │ abcX[1;31m123X[0mxyz │
+└───┴───┴───────────┘
+}
+do_test 3.3 {
+ set result "\n[db format -esc symbol {SELECT * FROM t1}]"
+} {
+┌───┬───┬──────────────────────┐
+│ a │ b │ c │
+├───┼───┼──────────────────────┤
+│ 1 │ 2 │ abc␛[1;31m123␛[0mxyz │
+└───┴───┴──────────────────────┘
+}
+do_test 3.4 {
+ set result "\n[db format -esc ascii {SELECT * FROM t1}]"
+} {
+┌───┬───┬────────────────────────┐
+│ a │ b │ c │
+├───┼───┼────────────────────────┤
+│ 1 │ 2 │ abc^[[1;31m123^[[0mxyz │
+└───┴───┴────────────────────────┘
}
+do_test 3.5 {
+ catch {db format -esc unk {SELECT * FROM t1}} res
+ set res
+} {bad control character escape (-esc) "unk": must be ascii, auto, off, or symbol}
finish_test
--- /dev/null
+# 2025-11-05
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+# Test cases for the Query Result Formatter (QRF)
+#
+# These tests are for EXPLAIN and EXPLAIN QUERY PLAN formatting, the
+# output of which can change when enhancments are made to the query
+# planner. So expect to have to modify the expected results of these
+# test cases in the future.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix qrf02
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a);
+ INSERT INTO t1 VALUES(1);
+}
+
+set result [db format {EXPLAIN SELECT * FROM t1}]
+do_test 1.10 {
+ set result
+} {/*addr opcode p1 p2 p3 p4 p5 comment
+---- ------------- ---- ---- ---- ------------- -- -------------
+0 Init */}
+regsub -all {\d+} $result {N} result2
+do_test 1.11 {
+ set result2
+} "/.*\nN Rewind .*\nN Column .*/"
+
+do_test 1.20 {
+ set result "\n[db format {EXPLAIN QUERY PLAN SELECT * FROM t1}]"
+} {
+QUERY PLAN
+`--SCAN t1
+}
+
+finish_test