From: drh <> Date: Sat, 8 Nov 2025 03:20:47 +0000 (+0000) Subject: Rewrite the table layout in QRF. Easier to maintain now. Still a few X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6eb5f5d9a7411c368de19fa15143b8f721dcd3c4;p=thirdparty%2Fsqlite.git Rewrite the table layout in QRF. Easier to maintain now. Still a few shell test failures, but much better after this rewrite. FossilOrigin-Name: 25838d282c5f7a922988245f373edfe8adbe28e929abce83afcda2b08472e685 --- diff --git a/ext/qrf/qrf.c b/ext/qrf/qrf.c index 3650cfa78e..efb5af87e0 100644 --- a/ext/qrf/qrf.c +++ b/ext/qrf/qrf.c @@ -532,6 +532,53 @@ static int qrfDisplayLength(const char *zIn){ return n; } +/* +** Return the display width of the longest line of text +** in the (possibly) multi-line input string zIn[0..nByte]. +** zIn[] is not necessarily zero-terminated. Take +** into account tab characters, zero- and double-width +** characters, CR and NL, and VT100 escape codes. +** +** Write the number of newlines into *pnNL. So, *pnNL will +** return 0 if everything fits on one line, or positive it +** it will need to be split. +*/ +static int qrfDisplayWidth(const char *zIn, sqlite3_int64 nByte, int *pnNL){ + const unsigned char *z = (const unsigned char*)zIn; + const unsigned char *zEnd = &z[nByte]; + int mx = 0; + int n = 0; + int nNL = 0; + while( z0 ){ + z += k; + }else{ + if( z[0]=='\t' ){ + n = (n+8)&~7; + }else if( z[0]=='\n' || z[0]=='\r' ){ + nNL++; + if( n>mx ) mx = n; + n = 0; + } + z++; + } + }else if( (0x80&z[0])==0 ){ + n++; + z++; + }else{ + int u = 0; + int len = sqlite3_qrf_decode_utf8(z, &u); + z += len; + n += sqlite3_qrf_wcwidth(u); + } + } + if( mx>n ) n = mx; + if( pnNL ) *pnNL = nNL; + return n; +} + /* ** Escape the input string if it is needed and in accordance with ** eEsc, which is either QRF_ESC_Ascii or QRF_ESC_Symbol. @@ -883,137 +930,6 @@ static void qrfRenderValue(Qrf *p, sqlite3_str *pOut, int iCol){ } } -/* -** 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 -** as \n, \t, \f, or \r. -** -** Compute characters to display on the first line of z[]. Stop at the -** first \r, \n, or \f. Expand \t into spaces. Return a copy (obtained -** from sqlite3_malloc()) of that first line. The caller is responsible -** for eventually freeing that line of text. -** -** Write anything to display on the next line into *pzTail. If this is -** the last line, write a NULL into *pzTail. (*pzTail is not allocated.) -*/ -static char *qrfTableCell( - Qrf *p, /* To access display settings */ - const unsigned char *z, /* Input text to be transformed */ - const unsigned char **pzTail, /* OUT: Tail of the input for next line */ - int mxWidth, /* Max width. 0 means no limit */ - int bWordWrap /* If true, avoid breaking mid-word */ -){ - int i; /* Input bytes consumed */ - int j; /* Output bytes generated */ - int k; /* Input bytes to be displayed */ - int n; /* Output column number */ - unsigned char *zOut; /* Output text */ - - if( z==0 ){ - *pzTail = 0; - return 0; - } - if( mxWidth<0 ) mxWidth = -mxWidth; - if( mxWidth==0 ) mxWidth = QRF_MX_WIDTH; - i = j = n = 0; - while( n<=mxWidth ){ - unsigned char c = z[i]; - if( c>=0xc0 ){ - int u; - int len = sqlite3_qrf_decode_utf8(&z[i], &u); - i += len; - j += len; - n += sqlite3_qrf_wcwidth(u); - continue; - } - if( c>=' ' ){ - n++; - i++; - j++; - continue; - } - if( c==0 || c=='\n' || (c=='\r' && z[i+1]=='\n') ) break; - if( c=='\t' ){ - do{ - n++; - j++; - }while( (n&7)!=0 && n0 ){ - i += k; - j += k; - }else{ - n++; - j += 3; - i++; - } - } - 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; - } - if( k<=i/2 ){ - for(k=i; k>i/2; k--){ - if( isalnum(z[k-1])!=isalnum(z[k]) && (z[k]&0xc0)!=0x80 ) break; - } - } - if( k<=i/2 ){ - k = i; - }else{ - i = k; - while( z[i]==' ' ) i++; - } - }else{ - k = 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 if( z[i]==0 || z[i+1]==0 ){ - *pzTail = 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); - return 0; - } - i = j = n = 0; - while( i=0xc0 ){ - int u; - int len = sqlite3_qrf_decode_utf8(&z[i], &u); - do{ zOut[j++] = z[i++]; }while( (--len)>0 ); - n += sqlite3_qrf_wcwidth(u); - continue; - } - if( c>=' ' || c=='\033' ){ - n++; - zOut[j++] = z[i++]; - continue; - } - if( c==0 ) break; - if( z[i]=='\t' ){ - do{ - n++; - zOut[j++] = ' '; - }while( (n&7)!=0 && nn; i++) sqlite3_free(p->az[i]); + sqlite3_free(p->az); + sqlite3_free(p->aiWth); + sqlite3_free(p->azThis); + memset(p, 0, sizeof(*p)); +} + +/* +** Allocate space for more cells in the qrfColData object. +** Return non-zero if a memory allocation fails. +*/ +static int qrfColDataEnlarge(qrfColData *p){ + char **azData; + int *aiWth; + p->nAlloc = 2*p->nAlloc + 10*p->nCol; + azData = sqlite3_realloc64(p->az, p->nAlloc*sizeof(char*)); + if( azData==0 ){ + qrfOom(p->p); + qrfColDataFree(p); + return 1; + } + p->az = azData; + aiWth = sqlite3_realloc64(p->aiWth, p->nAlloc*sizeof(int)); + if( aiWth==0 ){ + qrfOom(p->p); + qrfColDataFree(p); + return 1; + } + p->aiWth = aiWth; + return 0; +} + +/* +** (*pz)[] is a line of text that is to be displayed the box or table or +** similar tabular formats. z[] contain newlines or might be too wide +** to fit in the columns so will need to be split into multiple line. +** +** This routine determines: +** +** * How many bytes of z[] should be shown on the current line. +** * How many character positions those bytes will cover. +** * The byte offset to the start of the next line. +*/ +static void qrfWrapLine( + const char *zIn, /* Input text to be displayed */ + int w, /* Column width in characters (not bytes) */ + int bWrap, /* True if we should do word-wrapping */ + int *pnThis, /* OUT: How many bytes of z[] for the current line */ + int *pnWide, /* OUT: How wide is the text of this line */ + int *piNext /* OUT: Offset into z[] to start of the next line */ +){ + int i; /* Input bytes consumed */ + int k; /* Bytes in a VT100 code */ + int n; /* Output column number */ + const unsigned char *z = (const unsigned char*)zIn; + unsigned char c; + + if( zIn[0]==0 ){ + *pnThis = 0; + *pnWide = 0; + *piNext = 0; + return; + } + n = 0; + for(i=0; n=0xc0 ){ + int u; + int len = sqlite3_qrf_decode_utf8(&z[i], &u); + int wcw = sqlite3_qrf_wcwidth(u); + if( wcw+n>w ) break; + i += len-1; + n += wcw; + continue; + } + if( c>=' ' ){ + n++; + continue; + } + if( c==0 || c=='\n' ) break; + if( c=='\r' && zIn[i+1]=='\n' ){ c = zIn[++i]; break; } + if( c=='\t' ){ + int wcw = 8 - (n&7); + if( n+wcw>w ) break; + n += wcw; + continue; + } + if( c==0x1b && (k = qrfIsVt100(&z[i]))>0 ){ + i += k-1; + } + } + if( c==0 ){ + *pnThis = i; + *pnWide = n; + *piNext = i; + return; + } + if( c=='\n' ){ + *pnThis = i; + *pnWide = n; + *piNext = i+1; + return; + } + + /* If we get this far, that means the current line will end at some + ** point that is neither a "\n" or a 0x00. Figure out where that + ** split should occur + */ + if( bWrap && z[i]!=0 && !isspace(z[i]) && isalnum(c)==isalnum(z[i]) ){ + /* Perhaps try to back up to a better place to break the line */ + for(k=i-1; k>=i/2; k--){ + if( isspace(z[k]) ) break; + } + if( ki/2; k--){ + if( isalnum(z[k-1])!=isalnum(z[k]) && (z[k]&0xc0)!=0x80 ) break; + } + } + if( k>=i/2 ){ + i = k; + n = qrfDisplayWidth((const char*)z, k, 0); + } + } + *pnThis = i; + *pnWide = n; + while( zIn[i]==' ' || zIn[i]=='\t' || zIn[i]=='\r' ){ i++; } + *piNext = i; +} + /* ** Print a markdown or table-style row separator using ascii-art */ -static void qrfRowSeparator(Qrf *p, const char *zSep){ +static void qrfRowSeparator(sqlite3_str *pOut, qrfColData *p, char cSep){ int i; if( p->nCol>0 ){ - int nSep = (int)(strlen(zSep)&0x3fffffff); - sqlite3_str_append(p->pOut, zSep, nSep); - sqlite3_str_appendchar(p->pOut, p->actualWidth[0]+2, '-'); + sqlite3_str_append(pOut, &cSep, 1); + sqlite3_str_appendchar(pOut, p->aiCol[0]+2, '-'); for(i=1; inCol; i++){ - sqlite3_str_append(p->pOut, zSep, nSep); - sqlite3_str_appendchar(p->pOut, p->actualWidth[i]+2, '-'); + sqlite3_str_append(pOut, &cSep, 1); + sqlite3_str_appendchar(pOut, p->aiCol[i]+2, '-'); } - sqlite3_str_append(p->pOut, zSep, nSep); + sqlite3_str_append(pOut, &cSep, 1); } - sqlite3_str_append(p->pOut, "\n", 1); + sqlite3_str_append(pOut, "\n", 1); } - /* ** UTF8 box-drawing characters. Imagine box lines like this: ** @@ -1129,301 +1195,261 @@ static void qrfBoxLine(sqlite3_str *pOut, int N){ ** Draw a horizontal separator for a QRF_STYLE_Box table. */ static void qrfBoxSeparator( - Qrf *p, + sqlite3_str *pOut, + qrfColData *p, const char *zSep1, const char *zSep2, const char *zSep3 ){ int i; if( p->nCol>0 ){ - sqlite3_str_appendall(p->pOut, zSep1); - qrfBoxLine(p->pOut, p->actualWidth[0]+2); + sqlite3_str_appendall(pOut, zSep1); + qrfBoxLine(pOut, p->aiCol[0]+2); for(i=1; inCol; i++){ - sqlite3_str_appendall(p->pOut, zSep2); - qrfBoxLine(p->pOut, p->actualWidth[i]+2); + sqlite3_str_appendall(pOut, zSep2); + qrfBoxLine(pOut, p->aiCol[i]+2); } - sqlite3_str_appendall(p->pOut, zSep3); + sqlite3_str_appendall(pOut, zSep3); } - sqlite3_str_append(p->pOut, "\n", 1); + sqlite3_str_append(pOut, "\n", 1); } - /* ** Columnar modes require that the entire query be evaluated first, with ** results written into memory, so that we can compute appropriate column ** widths. */ static void qrfColumnar(Qrf *p){ - sqlite3_int64 nRow = 0; /* Rows displayed. nRow>=p->nRow */ - char **azData = 0; /* Text for all cells */ - int *aiDspyWth = 0; /* Display width of each cell */ - sqlite3_int64 nAlloc = 0; /* Slots allocated for azData[], aiDspyWth[] */ - char *abRowDiv = 0; /* Row is a continuation or is complete */ - const unsigned char *uz; - const char *z; - sqlite3_str **aCol = 0; /* Temporary storage for column data */ - sqlite3_int64 i, j, k, nData; - int rc, nTotal, w, n; + sqlite3_int64 i, j; const char *colSep = 0; const char *rowSep = 0; - const unsigned char **azNextLine = 0; - int bNextLine = 0; - int bMultiLineRowExists = 0; - int bw = p->spec.bWordWrap==QRF_SW_On; + const char *rowStart = 0; + int szColSep, szRowSep, szRowStart; + int rc; int nColumn = p->nCol; + int bWW; + int bCenter; + sqlite3_str *pStr; + qrfColData data; rc = sqlite3_step(p->pStmt); if( rc!=SQLITE_ROW || nColumn==0 ){ return; /* No output */ } - /* Initialize data structures */ - nAlloc = nColumn*4; - azData = sqlite3_malloc64( nAlloc*sizeof(char*) ); - if( azData==0 ){ qrfOom(p); goto qrf_column_end; } - aiDspyWth = sqlite3_malloc64( nAlloc*sizeof(int) ); - if( aiDspyWth==0 ){ qrfOom(p); goto qrf_column_end; } - azNextLine = sqlite3_malloc64( nColumn*sizeof(char*) ); - if( azNextLine==0 ){ qrfOom(p); goto qrf_column_end; } - memset((void*)azNextLine, 0, nColumn*sizeof(char*) ); - aCol = sqlite3_malloc64( nColumn*sizeof(aCol[0]) ); - if( aCol==0 ){ qrfOom(p); goto qrf_column_end; } - for(i=0; idb); - if( aCol[i]==0 ){ qrfOom(p); goto qrf_column_end; } - } - abRowDiv = sqlite3_malloc64( nAlloc/nColumn ); - if( abRowDiv==0 ){ qrfOom(p); goto qrf_column_end; } - p->actualWidth = sqlite3_malloc64( sizeof(int)*nColumn ); - if( p->actualWidth==0 ){ qrfOom(p); goto qrf_column_end; } - for(i=0; ispec.nWidth && ispec.nWidth ? p->spec.aWidth[i] : 0; - if( w<0 ){ - if( w==QRF_MINUS_ZERO ){ w = 0; } - else{ w = -w; } - } - p->actualWidth[i] = w; + /* Initialize the data container */ + memset(&data, 0, sizeof(data)); + data.nCol = p->nCol; + data.azThis = sqlite3_malloc64( nColumn*(sizeof(char*) + sizeof(int)) ); + if( data.azThis==0 ){ + qrfOom(p); + return; } - while( iactualWidth[i++] = 0; + data.aiCol = (int*)&data.azThis[nColumn]; + qrfColDataEnlarge(&data); + if( p->iErr ){ + qrfColDataFree(&data); + return; } - /* Capture the column names as the first row of data */ - if( p->spec.bColumnNames ){ - int saved_eText = p->spec.eText; + /* Load the column header names and all cell content into data */ + if( p->spec.bColumnNames==QRF_SW_On ){ + unsigned char saved_eText = p->spec.eText; p->spec.eText = QRF_TEXT_Off; for(i=0; iactualWidth[i]; - if( wx==0 ){ - wx = p->spec.mxColWidth; - } - 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]); + const char *z = (const char*)sqlite3_column_name(p->pStmt,i); + int nNL = 0; + int n; + pStr = sqlite3_str_new(p->db); + qrfEncodeText(p, pStr, z ? z : ""); + n = sqlite3_str_length(pStr); + z = data.az[data.n] = sqlite3_str_finish(pStr); + data.aiWth[data.n] = qrfDisplayWidth(z, n, &nNL); + data.n++; + if( nNL ) data.bMultiRow = 1; } p->spec.eText = saved_eText; + p->nRow++; + bCenter = 1; + }else{ + bCenter = 0; } - - /* Capture column content for all rows */ - k = nColumn; do{ + if( data.n+nColumn > data.nAlloc ){ + qrfColDataEnlarge(&data); + if( p->iErr ) return; + } + for(i=0; idb); + qrfRenderValue(p, pStr, i); + n = sqlite3_str_length(pStr); + z = data.az[data.n] = sqlite3_str_finish(pStr); + data.aiWth[data.n] = qrfDisplayWidth(z, n, &nNL); + data.n++; + if( nNL ) data.bMultiRow = 1; + } p->nRow++; - do{ - int useNextLine = bNextLine; - bNextLine = 0; - if( (nRow+2)*nColumn >= nAlloc ){ - char **azNewData; - char *abNewDiv; - int *aiNewWidth; - - nAlloc *= 2; - azNewData = sqlite3_realloc64(azData, nAlloc*sizeof(char*)); - if( azNewData==0 ){ qrfOom(p); goto qrf_column_end; } - azData = azNewData; - abNewDiv = sqlite3_realloc64(abRowDiv, nAlloc/nColumn); - if( abNewDiv==0 ){ qrfOom(p); goto qrf_column_end; } - abRowDiv = abNewDiv; - aiNewWidth = sqlite3_realloc64(aiDspyWth, nAlloc*sizeof(int)); - if( aiNewWidth==0 ){ qrfOom(p); goto qrf_column_end; } - aiDspyWth = aiNewWidth; + }while( sqlite3_step(p->pStmt)==SQLITE_ROW && p->iErr==SQLITE_OK ); + if( p->iErr ){ + qrfColDataFree(&data); + return; + } + + /* Compute the width of every column */ + for(i=0; ispec.nWidth ){ + w = p->spec.aWidth[i]; + if( w==QRF_MINUS_ZERO ){ + w = 0; + }else if( w<0 ){ + w = -w; } - abRowDiv[nRow] = 1; - nRow++; - for(i=0; ispec.nWidth ? p->spec.aWidth[i] : 0; - if( wx==0 || wx==QRF_MINUS_ZERO ){ - wx = p->spec.mxColWidth; - } - if( wx<0 ) wx = -wx; - if( useNextLine ){ - uz = azNextLine[i]; - if( uz==0 ) uz = (unsigned char*)""; - }else{ - sqlite3_str_reset(aCol[i]); - qrfRenderValue(p, aCol[i], i); - uz = (unsigned char*)sqlite3_str_value(aCol[i]); - if( uz==0 ) uz = (unsigned char*)""; - } - azData[k] = qrfTableCell(p, uz, &azNextLine[i], wx, bw); - if( p->iErr ) goto qrf_column_end; - aiDspyWth[k] = qrfDisplayLength(azData[k]); - if( aiDspyWth[k]>p->actualWidth[i] ){ - p->actualWidth[i] = aiDspyWth[k]; + } + if( w==0 ){ + for(j=i; j w ){ + w = data.aiWth[j]; + if( p->spec.mxColWidth>0 && w>p->spec.mxColWidth ){ + w = p->spec.mxColWidth; + data.bMultiRow = 1; + break; + } } - k++; - if( azNextLine[i] ){ - bNextLine = 1; - abRowDiv[nRow-1] = 0; - bMultiLineRowExists = 1; + } + }else if( data.bMultiRow==0 ){ + for(j=i; j w ){ + data.bMultiRow = 1; + break; } } - }while( bNextLine ); - }while( sqlite3_step(p->pStmt)==SQLITE_ROW ); - if( sqlite3_is_interrupted(p->db) ) goto qrf_column_end; + } + data.aiCol[i] = w; + } - /* Generate the column titles */ + /* TBD: Narrow columns so that the total is less the p->spec.mxTotalWidth */ + + /* Draw the line across the top of the table */ switch( p->spec.eStyle ){ - case QRF_STYLE_Column: { - colSep = " "; - rowSep = "\n"; - if( p->spec.bColumnNames==QRF_SW_On ){ - for(i=0; iactualWidth[i]; - if( ispec.nWidth && p->spec.aWidth[i]<0 ) w = -w; - qrfWidthPrint(p, p->pOut, w, azData[i]); - sqlite3_str_appendall(p->pOut, i==nColumn-1?"\n":" "); - } - for(i=0; ipOut, p->actualWidth[i], '-'); - sqlite3_str_appendall(p->pOut, i==nColumn-1?"\n":" "); - } - } + case QRF_STYLE_Box: + rowStart = BOX_13 " "; + colSep = " " BOX_13 " "; + rowSep = " " BOX_13 "\n"; + qrfBoxSeparator(p->pOut, &data, BOX_23, BOX_234, BOX_34); break; - } - case QRF_STYLE_Table: { + case QRF_STYLE_Table: + rowStart = "| "; colSep = " | "; rowSep = " |\n"; - 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, "+"); + qrfRowSeparator(p->pOut, &data, '+'); break; - } - case QRF_STYLE_Markdown: { + case QRF_STYLE_Column: + rowStart = ""; + colSep = " "; + rowSep = "\n"; + break; + case QRF_STYLE_Markdown: + rowStart = "| "; colSep = " | "; rowSep = " |\n"; - 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, "|"); - } break; - } - case QRF_STYLE_Box: { - colSep = " " BOX_13 " "; - rowSep = " " BOX_13 "\n"; - qrfBoxSeparator(p, BOX_23, BOX_234, BOX_34); - 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); - } - break; - } } + szRowStart = (int)strlen(rowStart); + szRowSep = (int)strlen(rowSep); + szColSep = (int)strlen(colSep); - /* Render the body of the table */ - nTotal = nColumn*(nRow+1); - for(i=nColumn, j=0; ispec.eStyle!=QRF_STYLE_Column ){ - sqlite3_str_appendall(p->pOut, - p->spec.eStyle==QRF_STYLE_Box ? BOX_13" " : "| "); - } - z = azData[i]; - if( z==0 ) z = ""; - w = p->actualWidth[j]; - if( jspec.nWidth && p->spec.aWidth[j]<0 ) w = -w; - qrfWidthPrint(p, p->pOut, w, z); - if( j==nColumn-1 ){ - sqlite3_str_appendall(p->pOut, rowSep); - if( bMultiLineRowExists && abRowDiv[i/nColumn-1] && i+1spec.eStyle ){ - case QRF_STYLE_Table: - qrfRowSeparator(p, "+"); - break; - case QRF_STYLE_Box: - qrfBoxSeparator(p, BOX_123, BOX_1234, BOX_134); - break; - case QRF_STYLE_Column: + bWW = (p->spec.bWordWrap==QRF_SW_On && data.bMultiRow); + for(i=0; ipOut, rowStart, szRowStart); + bMore = 0; + for(j=0; jpOut, nWS/2, ' '); + sqlite3_str_append(p->pOut, data.azThis[j], nThis); + sqlite3_str_appendchar(p->pOut, nWS - nWS/2, ' '); + }else if( jspec.nWidth && p->spec.aWidth[j]<0 ){ + /* Right justify the text */ + sqlite3_str_appendchar(p->pOut, nWS, ' '); + sqlite3_str_append(p->pOut, data.azThis[j], nThis); + }else{ + /* Left justify the next */ + sqlite3_str_append(p->pOut, data.azThis[j], nThis); + sqlite3_str_appendchar(p->pOut, nWS, ' '); + } + data.azThis[j] += iNext; + if( data.azThis[j][0]!=0 ) bMore = 1; + if( jpOut, colSep, szColSep); + }else{ + sqlite3_str_append(p->pOut, rowSep, szRowSep); + } + } + }while( bMore ); + bCenter = 0; + if( i+nColumnspec.eStyle ){ + case QRF_STYLE_Table: { + if( (i==0 && p->spec.bColumnNames==QRF_SW_On) || data.bMultiRow ){ + qrfRowSeparator(p->pOut, &data, '+'); + } + break; + } + case QRF_STYLE_Box: { + if( (i==0 && p->spec.bColumnNames==QRF_SW_On) || data.bMultiRow ){ + qrfBoxSeparator(p->pOut, &data, BOX_123, BOX_1234, BOX_134); + } + break; + } + case QRF_STYLE_Markdown: { + if( i==0 && p->spec.bColumnNames==QRF_SW_On ){ + qrfRowSeparator(p->pOut, &data, '|'); + } + break; + } + case QRF_STYLE_Column: { + if( i==0 && p->spec.bColumnNames==QRF_SW_On ){ + for(j=0; jpOut, data.aiCol[j], '-'); + if( jpOut, colSep, szColSep); + }else{ + sqlite3_str_append(p->pOut, rowSep, szRowSep); + } + } + }else if( data.bMultiRow ){ sqlite3_str_append(p->pOut, "\n", 1); - break; + } + break; } } - j = -1; - qrfWrite(p); - if( sqlite3_is_interrupted(p->db) ) break; - }else{ - sqlite3_str_appendall(p->pOut, colSep); } } - if( p->spec.eStyle==QRF_STYLE_Table ){ - qrfRowSeparator(p, "+"); - }else if( p->spec.eStyle==QRF_STYLE_Box ){ - qrfBoxSeparator(p, BOX_12, BOX_124, BOX_14); - } - qrfWrite(p); -qrf_column_end: - /* Free allocated memory */ - if( aCol ){ - for(i=0; ispec.eStyle ){ + case QRF_STYLE_Box: + qrfBoxSeparator(p->pOut, &data, BOX_12, BOX_124, BOX_14); + break; + case QRF_STYLE_Table: + qrfRowSeparator(p->pOut, &data, '+'); + break; } - sqlite3_free(azData); - sqlite3_free(abRowDiv); - + + qrfColDataFree(&data); return; } diff --git a/manifest b/manifest index d0078130b0..c4a6e4d043 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\ssome\sproblems.\s\sBut\sthere\sis\sa\sneed\sto\sdo\smajor\ssurgery\son\sthe\ntable\slayout\salgorithm,\sso\sit\sseems\sgood\sto\ssnapshot\sbefore\sproceeding. -D 2025-11-07T19:20:49.895 +C Rewrite\sthe\stable\slayout\sin\sQRF.\s\sEasier\sto\smaintain\snow.\s\sStill\sa\sfew\nshell\stest\sfailures,\sbut\smuch\sbetter\safter\sthis\srewrite. +D 2025-11-08T03:20:47.075 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 550c99ea5f9db2580ec66c12260b28feddc1c8a9b22396bb01bd9c3faaf720b6 -F ext/qrf/qrf.c ac6efea5dcd21c30a6182d772b517245a2b8a11a5a37a2da4cab31e9e1464652 +F ext/qrf/qrf.c e187f25116b1e33a1ccba1d25a03e60e7bf4bddfab72de48cb82da623f9fed7f F ext/qrf/qrf.h 642c717aa5a88074f17712a59763f80c9d83e356c3c165953c3f9ba648511087 F ext/rbu/rbu.c 801450b24eaf14440d8fd20385aacc751d5c9d6123398df41b1b5aa804bf4ce8 F ext/rbu/rbu1.test 25870dd7db7eb5597e2b4d6e29e7a7e095abf332660f67d89959552ce8f8f255 @@ -1506,7 +1506,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 193a3ad8101975d3e574f0855eb7285928a554420fc354916a199600234e3d62 +F test/qrf01.test 68481b70feab9dee97481daf23e3b63c3822542d4f8bc7ea805db93db3b0feaa F test/qrf02.test 39b4afdc000bedccdafc0aecf17638df67a67aaa2d2942865ae6abcc48ba0e92 F test/queryonly.test 5f653159e0f552f0552d43259890c1089391dcca F test/quick.test 1681febc928d686362d50057c642f77a02c62e57 @@ -2173,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 de7fc9afc43d4bd5f5995832cd698633e0e7213c190fa044856c9f82512161f9 -R d7dd914866de963dc2cf233d6a5493f5 +P 9052991a897ad12ac54476d9a370d1ad18937abc3c3fc11efcb678f21cb5ef69 +R bfd19efc8813f884de7c5af24dd2aa88 U drh -Z feff0772021bea128322542ff15b0626 +Z a1cd8bbd8fc7f8460e105f73287d0a96 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 60a0b4ee73..df00c694a6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9052991a897ad12ac54476d9a370d1ad18937abc3c3fc11efcb678f21cb5ef69 +25838d282c5f7a922988245f373edfe8adbe28e929abce83afcda2b08472e685 diff --git a/test/qrf01.test b/test/qrf01.test index cf970f8844..49dee2e521 100644 --- a/test/qrf01.test +++ b/test/qrf01.test @@ -367,17 +367,29 @@ do_test 2.4 { └───────┴───────┴────────────────────┘ } do_test 2.5 { - set result "\n[db format -widths {5 -5 18} -wordwrap off \ + 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 brow │ -│ │ │ n dog. │ +│ │ │ over the lazy brown │ +│ │ │ dog. │ └───────┴───────┴─────────────────────┘ } +do_test 2.6 { + set result "\n[db format -widths {5 -5 18} -wordwrap off \ + {SELECT * FROM t1}]" +} { +┌───────┬───────┬────────────────────┐ +│ a │ b │ c │ +├───────┼───────┼────────────────────┤ +│ 1 │ 2 │ The quick fox jump │ +│ │ │ s over the lazy br │ +│ │ │ own dog. │ +└───────┴───────┴────────────────────┘ +} do_execsql_test 2.10 { UPDATE t1 SET c='Η γρήγορη αλεπού πηδάει πάνω από το τεμπέλικο καφέ σκυλί'; @@ -466,7 +478,7 @@ do_test 4.2 { └─────────────────┴─────────────────┴────┘ } do_test 4.3 { - set result "\n[db format -text off -textjsonb on -maxcolwidth 10 \ + set result "\n[db format -text off -textjsonb on -maxcolwidth 11 \ {SELECT a AS json, b AS jsonb, c AS num FROM t1}]" } { ┌─────────────┬─────────────┬─────┐