int iErr; /* Error code */
int nCol; /* Number of output columns */
int expMode; /* Original sqlite3_stmt_isexplain() plus 1 */
+ int mxWidth; /* Screen width */
+ int mxHeight; /* mxRowHeight */
union {
struct { /* Content for QRF_STYLE_Line */
int mxColWth; /* Maximum display width of any column */
}
}
+/*
+** Output horizontally justified text into pOut. The text is the
+** first nVal bytes of zVal. Include nWS bytes of whitespace, either
+** split between both sides, or on the left, or on the right, depending
+** on eAlign.
+*/
+static void qrfPrintAligned(
+ sqlite3_str *pOut, /* Append text here */
+ const char *zVal, /* Text to append */
+ int nVal, /* Use only the first nVal bytes of zVal[] */
+ int nWS, /* Whitespace for horizonal alignment */
+ unsigned char eAlign /* Alignment type */
+){
+ eAlign &= QRF_ALIGN_HMASK;
+ if( eAlign==QRF_ALIGN_Center ){
+ /* Center the text */
+ sqlite3_str_appendchar(pOut, nWS/2, ' ');
+ sqlite3_str_append(pOut, zVal, nVal);
+ sqlite3_str_appendchar(pOut, nWS - nWS/2, ' ');
+ }else if( eAlign==QRF_ALIGN_Right){
+ /* Right justify the text */
+ sqlite3_str_appendchar(pOut, nWS, ' ');
+ sqlite3_str_append(pOut, zVal, nVal);
+ }else{
+ /* Left justify the next */
+ sqlite3_str_append(pOut, zVal, nVal);
+ sqlite3_str_appendchar(pOut, nWS, ' ');
+ }
+}
+
/*
** Columnar modes require that the entire query be evaluated first, with
** results written into memory, so that we can compute appropriate column
}
}
}
- }else if( data.bMultiRow==0 ){
+ }else if( data.bMultiRow==0 || w==1 ){
for(j=i; j<data.n; j+=nColumn){
if( data.aiWth[j] > w ){
data.bMultiRow = 1;
+ if( w==1 ){
+ /* If aiWth[j] is 2 or more, then there might be a double-wide
+ ** character somewhere. So make the column width at least 2. */
+ w = 2;
+ }
break;
}
}
bWW = (p->spec.bWordWrap==QRF_Yes && data.bMultiRow);
for(i=0; i<data.n; i+=nColumn){
int bMore;
+ int nRow = 0;
/* Draw a single row of the table. This might be the title line
** (if there is a title line) or a row in the body of the table.
int nWS;
qrfWrapLine(data.azThis[j], data.aiCol[j], bWW, &nThis, &nWide, &iNext);
nWS = data.aiCol[j] - nWide;
- if( (data.aAlign[j] & QRF_ALIGN_HMASK)==QRF_ALIGN_Center ){
- /* Center the text */
- sqlite3_str_appendchar(p->pOut, nWS/2, ' ');
- sqlite3_str_append(p->pOut, data.azThis[j], nThis);
- sqlite3_str_appendchar(p->pOut, nWS - nWS/2, ' ');
- }else if( (data.aAlign[j] & QRF_ALIGN_HMASK)==QRF_ALIGN_Right){
- /* 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, ' ');
- }
+ qrfPrintAligned(p->pOut, data.azThis[j], nThis, nWS, data.aAlign[j]);
data.azThis[j] += iNext;
if( data.azThis[j][0]!=0 ) bMore = 1;
if( j<nColumn-1 ){
sqlite3_str_append(p->pOut, rowSep, szRowSep);
}
}
- }while( bMore );
+ }while( bMore && ++nRow < p->mxHeight );
+ if( bMore ){
+ /* This row was terminated by mxRowHeight. Show ellipsis. */
+ sqlite3_str_append(p->pOut, rowStart, szRowStart);
+ for(j=0; j<nColumn; j++){
+ if( data.azThis[j][0]==0 ){
+ sqlite3_str_appendchar(p->pOut, data.aiCol[j], ' ');
+ }else{
+ int nE = 3;
+ if( nE>data.aiCol[j] ) nE = data.aiCol[j];
+ qrfPrintAligned(p->pOut, "...", nE, data.aiCol[j]-nE, data.aAlign[j]);
+ }
+ if( j<nColumn-1 ){
+ sqlite3_str_append(p->pOut, colSep, szColSep);
+ }else{
+ sqlite3_str_append(p->pOut, rowSep, szRowSep);
+ }
+ }
+ }
/* Draw either (1) the separator between the title line and the body
** of the table, or (2) separators between individual rows of the table
p->pStmt = pOrigStmt;
}
-/*
-** Initialize the internal Qrf object.
-*/
-static void qrfInitialize(
- Qrf *p, /* State object to be initialized */
- sqlite3_stmt *pStmt, /* Query whose output to be formatted */
- const sqlite3_qrf_spec *pSpec, /* Format specification */
- char **pzErr /* Write errors here */
-){
- size_t sz; /* Size of pSpec[], based on pSpec->iVersion */
- memset(p, 0, sizeof(*p));
- p->pzErr = pzErr;
- if( pSpec->iVersion!=1 ){
- qrfError(p, SQLITE_ERROR,
- "unusable sqlite3_qrf_spec.iVersion (%d)",
- pSpec->iVersion);
- return;
- }
- p->pStmt = pStmt;
- p->db = sqlite3_db_handle(pStmt);
- p->pOut = sqlite3_str_new(p->db);
- if( p->pOut==0 ){
- qrfOom(p);
- return;
- }
- p->iErr = 0;
- p->nCol = sqlite3_column_count(p->pStmt);
- p->nRow = 0;
- sz = sizeof(sqlite3_qrf_spec);
- memcpy(&p->spec, pSpec, sz);
- if( p->spec.zNull==0 ) p->spec.zNull = "";
-qrf_reinit:
- switch( p->spec.eStyle ){
- case QRF_Auto: {
- switch( sqlite3_stmt_isexplain(pStmt) ){
- case 0: p->spec.eStyle = QRF_STYLE_Box; break;
- case 1: p->spec.eStyle = QRF_STYLE_Explain; break;
- default: p->spec.eStyle = QRF_STYLE_Eqp; break;
- }
- goto qrf_reinit;
- }
- case QRF_STYLE_List: {
- if( p->spec.zColumnSep==0 ) p->spec.zColumnSep = "|";
- if( p->spec.zRowSep==0 ) p->spec.zRowSep = "\n";
- break;
- }
- case QRF_STYLE_JsonLine:
- case QRF_STYLE_Json: {
- p->spec.eText = QRF_TEXT_Json;
- p->spec.eBlob = QRF_BLOB_Json;
- p->spec.zNull = "null";
- break;
- }
- case QRF_STYLE_Html: {
- p->spec.eText = QRF_TEXT_Html;
- p->spec.zNull = "null";
- break;
- }
- case QRF_STYLE_Insert: {
- p->spec.eText = QRF_TEXT_Sql;
- p->spec.eBlob = QRF_BLOB_Sql;
- p->spec.zNull = "NULL";
- if( p->spec.zTableName==0 || p->spec.zTableName[0]==0 ){
- p->spec.zTableName = "tab";
- }
- break;
- }
- case QRF_STYLE_Csv: {
- p->spec.eStyle = QRF_STYLE_List;
- p->spec.eText = QRF_TEXT_Csv;
- p->spec.eBlob = QRF_BLOB_Tcl;
- p->spec.zColumnSep = ",";
- p->spec.zRowSep = "\r\n";
- break;
- }
- case QRF_STYLE_Quote: {
- p->spec.eText = QRF_TEXT_Sql;
- p->spec.eBlob = QRF_BLOB_Sql;
- p->spec.zNull = "NULL";
- p->spec.zColumnSep = ",";
- p->spec.zRowSep = "\n";
- break;
- }
- case QRF_STYLE_Eqp: {
- int expMode = sqlite3_stmt_isexplain(p->pStmt);
- if( expMode!=2 ){
- sqlite3_stmt_explain(p->pStmt, 2);
- p->expMode = expMode+1;
- }
- break;
- }
- case QRF_STYLE_Explain: {
- int expMode = sqlite3_stmt_isexplain(p->pStmt);
- if( expMode!=1 ){
- sqlite3_stmt_explain(p->pStmt, 1);
- p->expMode = expMode+1;
- }
- break;
- }
- }
- if( p->spec.eEsc==QRF_Auto ){
- p->spec.eEsc = QRF_ESC_Ascii;
- }
- if( p->spec.eText==QRF_Auto ){
- p->spec.eText = QRF_TEXT_Plain;
- }
- if( p->spec.eTitle==QRF_Auto ){
- switch( p->spec.eStyle ){
- case QRF_STYLE_Box:
- case QRF_STYLE_Column:
- case QRF_STYLE_Table:
- p->spec.eTitle = QRF_TEXT_Plain;
- break;
- default:
- p->spec.eTitle = p->spec.eText;
- break;
- }
- }
- if( p->spec.eBlob==QRF_Auto ){
- switch( p->spec.eText ){
- case QRF_TEXT_Sql: p->spec.eBlob = QRF_BLOB_Sql; break;
- case QRF_TEXT_Csv: p->spec.eBlob = QRF_BLOB_Tcl; break;
- case QRF_TEXT_Tcl: p->spec.eBlob = QRF_BLOB_Tcl; break;
- case QRF_TEXT_Json: p->spec.eBlob = QRF_BLOB_Json; break;
- default: p->spec.eBlob = QRF_BLOB_Text; break;
- }
- }
- if( p->spec.bColumnNames==QRF_Auto ){
- switch( p->spec.eStyle ){
- case QRF_STYLE_Box:
- case QRF_STYLE_Csv:
- case QRF_STYLE_Column:
- case QRF_STYLE_Table:
- case QRF_STYLE_Markdown:
- p->spec.bColumnNames = QRF_Yes;
- break;
- default:
- p->spec.bColumnNames = QRF_No;
- break;
- }
- }
- if( p->spec.bWordWrap==QRF_Auto ){
- p->spec.bWordWrap = QRF_Yes;
- }
- if( p->spec.bTextJsonb==QRF_Auto ){
- p->spec.bTextJsonb = QRF_No;
- }
- if( p->spec.zColumnSep==0 ) p->spec.zColumnSep = ",";
- if( p->spec.zRowSep==0 ) p->spec.zRowSep = "\n";
-}
-
/*
** Attempt to determine if identifier zName needs to be quoted, either
** because it contains non-alphanumeric characters, or because it is an
}
/*
-** Render a single row of output.
+** Render a single row of output for non-columnar styles - any
+** style that lets us render row by row as the content is received
+** from the query.
*/
static void qrfOneSimpleRow(Qrf *p){
int i;
}
if( p->nRow ) sqlite3_str_append(p->pOut, "\n", 1);
pVal = sqlite3_str_new(p->db);
- mxW = p->spec.nScreenWidth ? p->spec.nScreenWidth : QRF_MAX_WIDTH;
- mxW -= 3 + p->u.sLine.mxColWth;
+ mxW = p->mxWidth - (3 + p->u.sLine.mxColWth);
bWW = p->spec.bWordWrap==QRF_Yes;
for(i=0; i<p->nCol; i++){
const char *zVal;
qrfWrapLine(zVal, mxW, bWW, &nThis, &nWide, &iNext);
if( cnt ) sqlite3_str_appendchar(p->pOut,p->u.sLine.mxColWth+3,' ');
cnt++;
- if( p->spec.mxRowHeight>0 && cnt>p->spec.mxRowHeight ){
+ if( cnt>p->mxHeight ){
zVal = "...";
nThis = iNext = 3;
}
p->nRow++;
}
+/*
+** Initialize the internal Qrf object.
+*/
+static void qrfInitialize(
+ Qrf *p, /* State object to be initialized */
+ sqlite3_stmt *pStmt, /* Query whose output to be formatted */
+ const sqlite3_qrf_spec *pSpec, /* Format specification */
+ char **pzErr /* Write errors here */
+){
+ size_t sz; /* Size of pSpec[], based on pSpec->iVersion */
+ memset(p, 0, sizeof(*p));
+ p->pzErr = pzErr;
+ if( pSpec->iVersion!=1 ){
+ qrfError(p, SQLITE_ERROR,
+ "unusable sqlite3_qrf_spec.iVersion (%d)",
+ pSpec->iVersion);
+ return;
+ }
+ p->pStmt = pStmt;
+ p->db = sqlite3_db_handle(pStmt);
+ p->pOut = sqlite3_str_new(p->db);
+ if( p->pOut==0 ){
+ qrfOom(p);
+ return;
+ }
+ p->iErr = 0;
+ p->nCol = sqlite3_column_count(p->pStmt);
+ p->nRow = 0;
+ sz = sizeof(sqlite3_qrf_spec);
+ memcpy(&p->spec, pSpec, sz);
+ if( p->spec.zNull==0 ) p->spec.zNull = "";
+ p->mxWidth = p->spec.nScreenWidth;
+ if( p->mxWidth<=0 ) p->mxWidth = QRF_MAX_WIDTH;
+ p->mxHeight = p->spec.mxRowHeight;
+ if( p->mxHeight<=0 ) p->mxHeight = 2147483647;
+qrf_reinit:
+ switch( p->spec.eStyle ){
+ case QRF_Auto: {
+ switch( sqlite3_stmt_isexplain(pStmt) ){
+ case 0: p->spec.eStyle = QRF_STYLE_Box; break;
+ case 1: p->spec.eStyle = QRF_STYLE_Explain; break;
+ default: p->spec.eStyle = QRF_STYLE_Eqp; break;
+ }
+ goto qrf_reinit;
+ }
+ case QRF_STYLE_List: {
+ if( p->spec.zColumnSep==0 ) p->spec.zColumnSep = "|";
+ if( p->spec.zRowSep==0 ) p->spec.zRowSep = "\n";
+ break;
+ }
+ case QRF_STYLE_JsonLine:
+ case QRF_STYLE_Json: {
+ p->spec.eText = QRF_TEXT_Json;
+ p->spec.eBlob = QRF_BLOB_Json;
+ p->spec.zNull = "null";
+ break;
+ }
+ case QRF_STYLE_Html: {
+ p->spec.eText = QRF_TEXT_Html;
+ p->spec.zNull = "null";
+ break;
+ }
+ case QRF_STYLE_Insert: {
+ p->spec.eText = QRF_TEXT_Sql;
+ p->spec.eBlob = QRF_BLOB_Sql;
+ p->spec.zNull = "NULL";
+ if( p->spec.zTableName==0 || p->spec.zTableName[0]==0 ){
+ p->spec.zTableName = "tab";
+ }
+ break;
+ }
+ case QRF_STYLE_Csv: {
+ p->spec.eStyle = QRF_STYLE_List;
+ p->spec.eText = QRF_TEXT_Csv;
+ p->spec.eBlob = QRF_BLOB_Tcl;
+ p->spec.zColumnSep = ",";
+ p->spec.zRowSep = "\r\n";
+ break;
+ }
+ case QRF_STYLE_Quote: {
+ p->spec.eText = QRF_TEXT_Sql;
+ p->spec.eBlob = QRF_BLOB_Sql;
+ p->spec.zNull = "NULL";
+ p->spec.zColumnSep = ",";
+ p->spec.zRowSep = "\n";
+ break;
+ }
+ case QRF_STYLE_Eqp: {
+ int expMode = sqlite3_stmt_isexplain(p->pStmt);
+ if( expMode!=2 ){
+ sqlite3_stmt_explain(p->pStmt, 2);
+ p->expMode = expMode+1;
+ }
+ break;
+ }
+ case QRF_STYLE_Explain: {
+ int expMode = sqlite3_stmt_isexplain(p->pStmt);
+ if( expMode!=1 ){
+ sqlite3_stmt_explain(p->pStmt, 1);
+ p->expMode = expMode+1;
+ }
+ break;
+ }
+ }
+ if( p->spec.eEsc==QRF_Auto ){
+ p->spec.eEsc = QRF_ESC_Ascii;
+ }
+ if( p->spec.eText==QRF_Auto ){
+ p->spec.eText = QRF_TEXT_Plain;
+ }
+ if( p->spec.eTitle==QRF_Auto ){
+ switch( p->spec.eStyle ){
+ case QRF_STYLE_Box:
+ case QRF_STYLE_Column:
+ case QRF_STYLE_Table:
+ p->spec.eTitle = QRF_TEXT_Plain;
+ break;
+ default:
+ p->spec.eTitle = p->spec.eText;
+ break;
+ }
+ }
+ if( p->spec.eBlob==QRF_Auto ){
+ switch( p->spec.eText ){
+ case QRF_TEXT_Sql: p->spec.eBlob = QRF_BLOB_Sql; break;
+ case QRF_TEXT_Csv: p->spec.eBlob = QRF_BLOB_Tcl; break;
+ case QRF_TEXT_Tcl: p->spec.eBlob = QRF_BLOB_Tcl; break;
+ case QRF_TEXT_Json: p->spec.eBlob = QRF_BLOB_Json; break;
+ default: p->spec.eBlob = QRF_BLOB_Text; break;
+ }
+ }
+ if( p->spec.bColumnNames==QRF_Auto ){
+ switch( p->spec.eStyle ){
+ case QRF_STYLE_Box:
+ case QRF_STYLE_Csv:
+ case QRF_STYLE_Column:
+ case QRF_STYLE_Table:
+ case QRF_STYLE_Markdown:
+ p->spec.bColumnNames = QRF_Yes;
+ break;
+ default:
+ p->spec.bColumnNames = QRF_No;
+ break;
+ }
+ }
+ if( p->spec.bWordWrap==QRF_Auto ){
+ p->spec.bWordWrap = QRF_Yes;
+ }
+ if( p->spec.bTextJsonb==QRF_Auto ){
+ p->spec.bTextJsonb = QRF_No;
+ }
+ if( p->spec.zColumnSep==0 ) p->spec.zColumnSep = ",";
+ if( p->spec.zRowSep==0 ) p->spec.zRowSep = "\n";
+}
+
/*
** Finish rendering the results
*/
INSERT INTO t1 VALUES
('entry-one',1708791504,zeroblob(300)),
(unistr('one\u000atwo\u000athree'),1333206973,NULL),
- ('sample-jsonb',1333206973,jsonb('{
+ ('sample-jsonb',1333101221,jsonb('{
"alpha":53.11688723,
"beta":"qrfWidthPrint(p, p->pOut, -p->u.sLine.mxColWth);",
"zeta":[15,null,1333206973,"fd8ffe000104a46494600010101"]}'));
set result "\n[db format -style line -screenwidth 60 -blob sql \
-text sql -wordwrap off -maxrowheight 77 $sql]"
} {
- name = unistr('one\u000atwo\u000athree')
-mtime = 1333206973
- time = '2012-03-31 15:16:13'
-value =
-
name = 'sample-jsonb'
-mtime = 1333206973
- time = '2012-03-31 15:16:13'
+mtime = 1333101221
+ time = '2012-03-30 09:53:41'
value = x'cc7c57616c706861b535332e31313638383732334762657461
c73071726657696474685072696e7428702c20702d3e704f7574
2c202d702d3e752e734c696e652e6d78436f6c577468293b477a
657461cb2c23313500a331333333323036393733c71b66643866
6665303030313034613436343934363030303130313031'
+ name = unistr('one\u000atwo\u000athree')
+mtime = 1333206973
+ time = '2012-03-31 15:16:13'
+value =
+
name = 'entry-one'
mtime = 1708791504
time = '2024-02-24 16:18:24'
-text plain -esc off -textjsonb yes \
-wordwrap yes -maxrowheight 3 $sql]"
} {
+ name = sample-jsonb
+mtime = 1333101221
+ time = 2012-03-30 09:53:41
+value = {"alpha":53.11688723,"beta":"qrfWidthPrint(p,
+ p->pOut, -p->u.sLine.mxColWth);","zeta":[15,null,
+ 1333206973,"fd8ffe000104a46494600010101"]}
+
name = one
two
three
time = 2012-03-31 15:16:13
value =
- name = sample-jsonb
-mtime = 1333206973
- time = 2012-03-31 15:16:13
-value = {"alpha":53.11688723,"beta":"qrfWidthPrint(p,
- p->pOut, -p->u.sLine.mxColWth);","zeta":[15,null,
- 1333206973,"fd8ffe000104a46494600010101"]}
-
name = entry-one
mtime = 1708791504
time = 2024-02-24 16:18:24
0000000000000000000000000000000000000000000000000000
...
}
+do_test 5.3a {
+ set result "\n[db format -style box -widths {0 10 10 14}\
+ -align {left right right center} \
+ -blob sql \
+ -text plain -esc off -textjsonb no \
+ -wordwrap yes -maxrowheight 2 $sql]"
+} {
+┌──────────────┬────────────┬────────────┬────────────────┐
+│ name │ mtime │ time │ value │
+├──────────────┼────────────┼────────────┼────────────────┤
+│ sample-jsonb │ 1333101221 │ 2012-03-30 │ x'cc7c57616c70 │
+│ │ │ 09:53:41 │ 6861b535332e31 │
+│ │ │ │ ... │
+├──────────────┼────────────┼────────────┼────────────────┤
+│ one │ 1333206973 │ 2012-03-31 │ │
+│ two │ │ 15:16:13 │ │
+│ ... │ │ │ │
+├──────────────┼────────────┼────────────┼────────────────┤
+│ entry-one │ 1708791504 │ 2024-02-24 │ x'000000000000 │
+│ │ │ 16:18:24 │ 00000000000000 │
+│ │ │ │ ... │
+└──────────────┴────────────┴────────────┴────────────────┘
+}
+do_test 5.3b {
+ set result "\n[db format -style table -widths {0 10 10 14}\
+ -align {center right right right} \
+ -blob sql \
+ -text plain -esc off -textjsonb no \
+ -wordwrap yes -maxrowheight 2 $sql]"
+} {
++--------------+------------+------------+----------------+
+| name | mtime | time | value |
++--------------+------------+------------+----------------+
+| sample-jsonb | 1333101221 | 2012-03-30 | x'cc7c57616c70 |
+| | | 09:53:41 | 6861b535332e31 |
+| | | | ... |
++--------------+------------+------------+----------------+
+| one | 1333206973 | 2012-03-31 | |
+| two | | 15:16:13 | |
+| ... | | | |
++--------------+------------+------------+----------------+
+| entry-one | 1708791504 | 2024-02-24 | x'000000000000 |
+| | | 16:18:24 | 00000000000000 |
+| | | | ... |
++--------------+------------+------------+----------------+
+}
+do_test 5.3c {
+ set result "\n[db format -style column -widths {0 10 10 14}\
+ -align {center right right right} \
+ -blob sql \
+ -text plain -esc off -textjsonb no \
+ -wordwrap yes -maxrowheight 2 $sql]"
+} {
+ name mtime time value
+------------ ---------- ---------- --------------
+sample-jsonb 1333101221 2012-03-30 x'cc7c57616c70
+ 09:53:41 6861b535332e31
+ ...
+
+ one 1333206973 2012-03-31
+ two 15:16:13
+ ...
+
+ entry-one 1708791504 2024-02-24 x'000000000000
+ 16:18:24 00000000000000
+ ...
+}
+do_test 5.4 {
+ db eval {
+ CREATE TABLE t2(a,b,c,d,e);
+ WITH v(x) AS (SELECT 'abcdefghijklmnopqrstuvwxyz')
+ INSERT INTO t2 SELECT x,x,x,x,x FROM v;
+ }
+ set sql {SELECT char(0x61,0xa,0x62,0xa,0x63,0xa,0x64) a,
+ mtime b, mtime c, mtime d, mtime e FROM t1}
+ set result "\n[db format -style box -widths {1 2 3 4 5}\
+ -maxrowheight 3 -wordwrap off {SELECT *, 'x' AS x FROM t2}]"
+} {
+┌────┬────┬─────┬──────┬───────┬───┐
+│ a │ b │ c │ d │ e │ x │
+├────┼────┼─────┼──────┼───────┼───┤
+│ ab │ ab │ abc │ abcd │ abcde │ x │
+│ cd │ cd │ def │ efgh │ fghij │ │
+│ ef │ ef │ ghi │ ijkl │ klmno │ │
+│ .. │ .. │ ... │ ... │ ... │ │
+└────┴────┴─────┴──────┴───────┴───┘
+}
finish_test