From: drh <> Date: Wed, 5 Nov 2025 22:33:47 +0000 (+0000) Subject: Further testing and bug fixing. X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=2e660d30f5f4073a5a4aa891f3f04161af0c6edf;p=thirdparty%2Fsqlite.git Further testing and bug fixing. FossilOrigin-Name: d6b1bac15a692a69f3707fca721b57b3b283edb3850efa34ba42b02c3669fc71 --- diff --git a/ext/qrf/qrf.c b/ext/qrf/qrf.c index 5f0dd80122..7347fcc7c8 100644 --- a/ext/qrf/qrf.c +++ b/ext/qrf/qrf.c @@ -358,21 +358,46 @@ int sqlite3_qrf_decode_utf8(const unsigned char *z, int *pU){ 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); } @@ -731,23 +756,6 @@ static void qrfRenderValue(Qrf *p, sqlite3_str *pOut, int iCol){ } } -/* -** 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 @@ -781,7 +789,7 @@ static char *qrfTableCell( if( mxWidth<0 ) mxWidth = -mxWidth; if( mxWidth==0 ) mxWidth = 1000000; i = j = n = 0; - while( n=0xc0 ){ int u; @@ -815,7 +823,7 @@ static char *qrfTableCell( 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; @@ -834,7 +842,7 @@ static char *qrfTableCell( }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; @@ -843,6 +851,7 @@ static char *qrfTableCell( }else{ *pzTail = &z[i+1]; } + if( n>=mxWidth && k>0 && z[k-1]==' ' ) k--; zOut = sqlite3_malloc64( j+1 ); if( zOut==0 ){ qrfOom(p); @@ -858,7 +867,7 @@ static char *qrfTableCell( n += sqlite3_qrf_wcwidth(u); continue; } - if( c>=' ' ){ + if( c>=' ' || c=='\033' ){ n++; zOut[j++] = z[i++]; continue; @@ -1073,24 +1082,29 @@ static void qrfColumnar(Qrf *p){ } /* Capture the column names as the first row of data */ - for(i=0; iactualWidth[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; iactualWidth[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 */ @@ -1172,15 +1186,17 @@ static void qrfColumnar(Qrf *p){ case QRF_STYLE_Table: { colSep = " | "; rowSep = " |\n"; - qrfRowSeparator(p, "+"); - sqlite3_str_append(p->pOut, "| ", 2); - for(i=0; iactualWidth[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; iactualWidth[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; @@ -1188,32 +1204,36 @@ static void qrfColumnar(Qrf *p){ case QRF_STYLE_Markdown: { colSep = " | "; rowSep = " |\n"; - sqlite3_str_append(p->pOut, "| ", 2); - for(i=0; iactualWidth[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; iactualWidth[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; iactualWidth[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; iactualWidth[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; } } @@ -1607,6 +1627,8 @@ qrf_reinit: 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"; } /* @@ -1744,6 +1766,8 @@ static void qrfOneSimpleRow(Qrf *p){ } 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; inCol; i++){ const char *zCName = sqlite3_column_name(p->pStmt, i); if( i>0 ) sqlite3_str_appendall(p->pOut, p->spec.zColumnSep); @@ -1751,6 +1775,7 @@ static void qrfOneSimpleRow(Qrf *p){ } sqlite3_str_appendall(p->pOut, p->spec.zRowSep); qrfWrite(p); + p->spec.eText = saved_eText; } for(i=0; inCol; i++){ if( i>0 ) sqlite3_str_appendall(p->pOut, p->spec.zColumnSep); diff --git a/manifest b/manifest index 532caceb36..e8abbca481 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -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 @@ -417,7 +417,7 @@ F ext/misc/windirent.h 02211ce51f3034c675f2dbf4d228194d51b3ee05734678bad5106fff6 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 @@ -743,7 +743,7 @@ F src/sqliteInt.h 88f7fc9ce1630d9a5f7e0a8e1f3287cdc63882fba985c18e7eee1b9f457f59 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 @@ -1506,7 +1506,8 @@ F test/printf2.test 3f55c1871a5a65507416076f6eb97e738d5210aeda7595a74ee895f2224c 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 @@ -2172,8 +2173,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 633fe4fe584ae14ed6ced1ae137bf96a434a012423d70f0f93876737b0ca075c -R 70942b4daded82313531eeb35df731ae +P d1542696409b67635d0f172042642709452118d62102ebfab2ad3ff2b5732687 +R f4a27ec41a5221a665784b11fc4b6185 U drh -Z 687a4d4793d1bb836e5c2deeb266cdf6 +Z c4401bada00b73794af6d52fe089dac2 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d1fcb0a154..944ac86bc2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d1542696409b67635d0f172042642709452118d62102ebfab2ad3ff2b5732687 +d6b1bac15a692a69f3707fca721b57b3b283edb3850efa34ba42b02c3669fc71 diff --git a/src/tclsqlite.c b/src/tclsqlite.c index bda4b8bd40..7471605d86 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -2050,6 +2050,43 @@ static void DbHookCmd( ** 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 @@ -2098,21 +2135,54 @@ static int dbQrf(SqliteDb *pDb, int objc, Tcl_Obj *const*objv){ }; 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; @@ -2179,6 +2249,18 @@ static int dbQrf(SqliteDb *pDb, int objc, Tcl_Obj *const*objv){ } } 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; diff --git a/test/qrf01.test b/test/qrf01.test index 64975331ca..181476fa2f 100644 --- a/test/qrf01.test +++ b/test/qrf01.test @@ -31,7 +31,58 @@ do_test 1.10 { │ 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}]" } { @@ -53,6 +104,14 @@ do_test 1.30 { | 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}]" @@ -79,6 +138,9 @@ do_test 1.60 { 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}]" @@ -170,6 +232,18 @@ a = BLOB 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}]" @@ -185,6 +259,14 @@ a|b|c 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}} @@ -198,6 +280,12 @@ do_test 1.120 { | 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}]" @@ -208,7 +296,7 @@ x'424c4f42',NULL,'Ἀμήν' 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,'Ἀμήν' } @@ -219,15 +307,111 @@ do_execsql_test 2.0 { 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 diff --git a/test/qrf02.test b/test/qrf02.test new file mode 100644 index 0000000000..07e1568f71 --- /dev/null +++ b/test/qrf02.test @@ -0,0 +1,47 @@ +# 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