From f4197be567871b3dc7b8c63f59444bd108cf3d1f Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 22 Nov 2025 00:38:17 +0000 Subject: [PATCH] Implementation of bWrapSnglCol in QRF. FossilOrigin-Name: 4bbd3f7ead50c0babd7843f58972e2e1762a84866cc4ed88a2a74375b13da11c --- ext/qrf/qrf.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++- manifest | 16 +++---- manifest.uuid | 2 +- src/tclsqlite.c | 11 ++++- test/qrf01.test | 94 +++++++++++++++++++++++++++++++++++++ 5 files changed, 231 insertions(+), 13 deletions(-) diff --git a/ext/qrf/qrf.c b/ext/qrf/qrf.c index eb32424c3d..07a771242a 100644 --- a/ext/qrf/qrf.c +++ b/ext/qrf/qrf.c @@ -1463,12 +1463,126 @@ static void qrfLoadAlignment(qrfColData *pData, Qrf *p){ } } +/* +** If the single column in pData->a[] with pData->n entries can be +** laid out as nCol columns with a 2-space gap between each such +** that all columns fit within nSW, then return a pointer to an array +** of integers which is the width of each column from left to right. +** +** If the layout is not possible, return a NULL pointer. +** +** Space to hold the returned array is from sqlite_malloc64(). +*/ +static int *qrfValidLayout( + qrfColData *pData, /* Collected query results */ + Qrf *p, /* On which to report an OOM */ + int nCol, /* Attempt this many columns */ + int nSW /* Screen width */ +){ + int i; /* Loop counter */ + int nr; /* Number of rows */ + int w = 0; /* Width of the current column */ + int t; /* Total width of all columns */ + int *aw; /* Array of individual column widths */ + + aw = sqlite3_malloc64( sizeof(int)*nCol ); + if( aw==0 ){ + qrfOom(p); + return 0; + } + nr = (pData->n + nCol - 1)/nCol; + for(i=0; in; i++){ + if( (i%nr)==0 ){ + if( i>0 ) aw[i/nr-1] = w; + w = pData->aiWth[i]; + }else if( pData->aiWth[i]>w ){ + w = pData->aiWth[i]; + } + } + aw[nCol-1] = w; + for(t=i=0; inSW ){ + sqlite3_free(aw); + return 0; + } + return aw; +} + /* ** The output is single-column and the bWrapSnglCol flag is set. ** Check to see if the single-column output can be split into multiple ** columns that appear side-by-side. Adjust pData appropriately. */ static void qrfWrapSingleColumn(qrfColData *pData, Qrf *p){ + int nCol = 1; + int *aw = 0; + char **az = 0; + int *aiWth = 0; + int nColNext = 2; + struct qrfPerCol *a = 0; + sqlite3_int64 nRow = 1; + sqlite3_int64 i; + while( 1/*exit-by-break*/ ){ + int *awNew = qrfValidLayout(pData, p, nColNext, p->spec.nScreenWidth); + if( awNew==0 ) break; + sqlite3_free(aw); + aw = awNew; + nCol = nColNext; + nRow = (pData->n + nCol - 1)/nCol; + if( nRow==1 ) break; + nColNext++; + while( (pData->n + nColNext - 1)/nColNext == nRow ) nColNext++; + } + if( nCol==1 ){ + sqlite3_free(aw); + return; /* Cannot do better than 1 column */ + } + az = sqlite3_malloc64( nRow*nCol*sizeof(char*) ); + if( az==0 ){ + qrfOom(p); + return; + } + aiWth = sqlite3_malloc64( nRow*nCol*sizeof(int) ); + if( aiWth==0 ){ + sqlite3_free(az); + qrfOom(p); + return; + } + a = sqlite3_malloc64( nCol*sizeof(struct qrfPerCol) ); + if( a==0 ){ + sqlite3_free(az); + sqlite3_free(aiWth); + qrfOom(p); + return; + } + for(i=0; in; i++){ + sqlite3_int64 j = (i%nRow)*nCol + (i/nRow); + az[j] = pData->az[i]; + pData->az[i] = 0; + aiWth[j] = pData->aiWth[i]; + } + while( ia[0].e; + } + sqlite3_free(pData->az); + sqlite3_free(pData->aiWth); + sqlite3_free(pData->a); + sqlite3_free(aw); + pData->az = az; + pData->aiWth = aiWth; + pData->a = a; + pData->nCol = nCol; + pData->n = pData->nAlloc = nRow*nCol; + pData->nMargin = 2; } /* @@ -1671,7 +1785,7 @@ static void qrfColumnar(Qrf *p){ } if( nColumn==1 - && p->spec.bWrapSnglCol + && p->spec.bWrapSnglCol==QRF_Yes && p->spec.eStyle==QRF_STYLE_Column && p->spec.bTitles==QRF_No && p->spec.nScreenWidth>data.a[0].w+3 @@ -1680,6 +1794,7 @@ static void qrfColumnar(Qrf *p){ ** verticle wrapping, if the screen is wide enough and if the ** bWrapSnglCol flag is set. */ qrfWrapSingleColumn(&data, p); + nColumn = data.nCol; }else{ /* Adjust the column widths due to screen width restrictions */ qrfRestrictScreenWidth(&data, p); @@ -1756,7 +1871,9 @@ static void qrfColumnar(Qrf *p){ nWS = data.a[j].w - nWide; qrfPrintAligned(p->pOut, data.a[j].z, nThis, nWS, data.a[j].e); data.a[j].z += iNext; - if( data.a[j].z[0]!=0 ) bMore = 1; + if( data.a[j].z[0]!=0 ){ + bMore = 1; + } if( jpOut, colSep, szColSep); }else{ diff --git a/manifest b/manifest index 0f09f6e0ed..a0e22d8444 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\sbWrapSnglCol\sflag\sto\sthe\sQRF\sspec,\sthough\sit\sis\snot\syet\sdocumented\nand\sdoes\snot\syet\swork.\s\sFix\scolumn\soutput\sso\sthat\sit\somits\strailing\sspace. -D 2025-11-21T20:10:12.657 +C Implementation\sof\sbWrapSnglCol\sin\sQRF. +D 2025-11-22T00:38:17.646 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 dd565fd1ca0c46ea37dbf4d496e368b9ecade768c92669640bc106e039629016 -F ext/qrf/qrf.c 33379ad5044ba63729b204889fd6e59948ed5e80eb3037710d98f146907c4262 +F ext/qrf/qrf.c f388bd56d8f9804511fa0b3d704cca648084d05f716525f48fdaea8be761d7c7 F ext/qrf/qrf.h 116f9d7847c04f6377d40cd22dd2b1c6a1336a26201dfe6d69b1d58ec41d02e7 F ext/rbu/rbu.c 801450b24eaf14440d8fd20385aacc751d5c9d6123398df41b1b5aa804bf4ce8 F ext/rbu/rbu1.test 25870dd7db7eb5597e2b4d6e29e7a7e095abf332660f67d89959552ce8f8f255 @@ -743,7 +743,7 @@ F src/sqliteInt.h a89c3a9296928dffcb4c287df176a739f9cf620c7c9d33aec59e8efb9b39cb F src/sqliteLimit.h 0a5516b4ec192a205c541e05f67009028a9451dc6678aae4cf8e68596903c246 F src/status.c 7565d63a79aa2f326339a24a0461a60096d0bd2bce711fefb50b5c89335f3592 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 -F src/tclsqlite.c 381384fbe3cf342115f9ad01208fa81092e9a2156a4ea4d44de87852b8df3a8a +F src/tclsqlite.c caceb8b872d414479a1df3715e565bc9a486cb5763dcf639ad4ff84550d19dc4 F src/tclsqlite.h 614b3780a62522bc9f8f2b9fb22689e8009958e7aa77e572d0f3149050af348a F src/test1.c 0e71fbcb484a271564e98e0158192c28c24f5521594218c3ba48bcb4cf634f91 F src/test2.c 62f0830958f9075692c29c6de51b495ae8969e1bef85f239ffcd9ba5fb44a5ff @@ -1509,7 +1509,7 @@ F test/printf2.test 3f55c1871a5a65507416076f6eb97e738d5210aeda7595a74ee895f2224c F test/progress.test ebab27f670bd0d4eb9d20d49cef96e68141d92fb F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc F test/pushdown.test 46a626ef1c0ca79b85296ff2e078b9da20a50e9b804b38f441590c3987580ddd -F test/qrf01.test e76be7da90e9c40010fd08336461c4fdc9825875167ee17170442c1e23631342 +F test/qrf01.test 92334aaf93e0b96dbb117c9592f2ec7385e9077e65db16a175ec352f4d754ef3 F test/qrf02.test 39b4afdc000bedccdafc0aecf17638df67a67aaa2d2942865ae6abcc48ba0e92 F test/qrf03.test 9de53aea459f5a127283db03cbb6011500757685646d21aa3c29c44c6ef23e86 F test/queryonly.test 5f653159e0f552f0552d43259890c1089391dcca @@ -2178,8 +2178,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 04394387e626cd99ff98df978c4b7f2d32f65760e0c26e53f1ef4f59e4e91a4f -R c2811c2ce4ec10183543473b397451a5 +P a858027fc92727c680b7e984303df7f9e0a2d90c860c547176f290b113a69390 +R a51fa5dfb24a3bcee8421313d1d3bb87 U drh -Z 7925dda900ed8e83a9a5d4713fd7d290 +Z a16fb45a4bab603e7bb9dd853cb499d3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 706bb90ab3..e18dea149d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a858027fc92727c680b7e984303df7f9e0a2d90c860c547176f290b113a69390 +4bbd3f7ead50c0babd7843f58972e2e1762a84866cc4ed88a2a74375b13da11c diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 4076e0dc09..036de64f6e 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -2063,6 +2063,7 @@ static void DbHookCmd( ** -wordwrap ("auto"|"off"|"on") Try to wrap at word boundry? ** -textjsonb ("auto"|"off"|"on") Auto-convert JSONB to text? ** -textnull ("auto"|"off"|"on") Use text encoding for -null. +** -wrapsnglcol ("auto"|"off"|"on") Enable wrap-single-column ** -defaultalign ("auto"|"left"|...) Default alignment ** -titalalign ("auto"|"left"|"right"|...) Default column name alignment ** -wrap NUMBER Max width of any single column @@ -2089,6 +2090,7 @@ static void DbHookCmd( ** -wordwrap bWordWrap ** -textjsonb bTextJsonb ** -textnull bTestNull +** -wrapsnglcol bWrapSnglCol ** -defaultalign eDfltAlign ** -titlealign eTitleAlign ** -wrap nWrap @@ -2236,15 +2238,20 @@ static int dbQrf(SqliteDb *pDb, int objc, Tcl_Obj *const*objv){ if( rc ) goto format_failed; qrf.bWordWrap = aBoolMap[v]; i++; - }else if( strcmp(zArg,"-textjsonb")==0 || strcmp(zArg,"-textnull")==0 ){ + }else if( strcmp(zArg,"-textjsonb")==0 + || strcmp(zArg,"-textnull")==0 + || strcmp(zArg,"-wrapsnglcol")==0 + ){ int v = 0; rc = Tcl_GetIndexFromObj(pDb->interp, objv[i+1], azBool, zArg, 0, &v); if( rc ) goto format_failed; if( zArg[5]=='j' ){ qrf.bTextJsonb = aBoolMap[v]; - }else{ + }else if( zArg[5]=='n' ){ qrf.bTextNull = aBoolMap[v]; + }else{ + qrf.bWrapSnglCol = aBoolMap[v]; } i++; }else if( strcmp(zArg,"-defaultalign")==0 || strcmp(zArg,"-titlealign")==0){ diff --git a/test/qrf01.test b/test/qrf01.test index 4bba0fe9d4..81f1fc3a05 100644 --- a/test/qrf01.test +++ b/test/qrf01.test @@ -895,6 +895,100 @@ do_test 9.4 { db format -style jobject {SELECT * FROM t9 WHERE rowid<0} } {} +do_test 10.1 { + db eval { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(x); + INSERT INTO t1(x) VALUES + ('alice'), + ('bob'), + ('cinderella-cinderella'), + ('daniel'), + ('emma'), + ('fred'), + ('gertrude'), + ('harold'), + ('ingrid'), + ('jake'), + ('lisa'), + ('mike'), + ('nina'), + ('octavian'), + ('paula'), + ('quintus'), + ('rita'), + ('sam'), + ('tammy'), + ('ulysses'), + ('violet'), + ('william'), + ('xanthippe'), + ('yates'), + ('zoe'); + } + set result "\n[db format -style column -title off -screenwidth 41 -wrapsnglcol on \ + {SELECT x FROM t1}]" +} { +alice octavian +bob paula +cinderella-cinderella quintus +daniel rita +emma sam +fred tammy +gertrude ulysses +harold violet +ingrid william +jake xanthippe +lisa yates +mike zoe +nina +} +do_test 10.2 { + set result "\n[db format -style column -title off -screenwidth 42 -wrapsnglcol on \ + {SELECT x FROM t1}]" +} { +alice jake tammy +bob lisa ulysses +cinderella-cinderella mike violet +daniel nina william +emma octavian xanthippe +fred paula yates +gertrude quintus zoe +harold rita +ingrid sam +} +do_test 10.3 { + set result "\n[db format -style column -title off -screenwidth 51 -wrapsnglcol on \ + {SELECT x FROM t1}]" +} { +alice harold paula william +bob ingrid quintus xanthippe +cinderella-cinderella jake rita yates +daniel lisa sam zoe +emma mike tammy +fred nina ulysses +gertrude octavian violet +} +do_test 10.4 { + set result "\n[db format -style column -title off -screenwidth 61 -wrapsnglcol on \ + {SELECT x FROM t1}]" +} { +alice fred lisa quintus violet +bob gertrude mike rita william +cinderella-cinderella harold nina sam xanthippe +daniel ingrid octavian tammy yates +emma jake paula ulysses zoe +} +do_test 10.5 { + set result "\n[db format -style column -title off -screenwidth 74 -wrapsnglcol on \ + {SELECT x FROM t1}]" +} { +alice emma ingrid nina rita violet zoe +bob fred jake octavian sam william +cinderella-cinderella gertrude lisa paula tammy xanthippe +daniel harold mike quintus ulysses yates +} + db close finish_test -- 2.47.3