From: drh <> Date: Fri, 7 Nov 2025 12:12:02 +0000 (+0000) Subject: Add the JsonLine output style. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=10747754d432f73f42d83c799bfd108155580165;p=thirdparty%2Fsqlite.git Add the JsonLine output style. FossilOrigin-Name: 7dfba841cfc515ac28401c4eb90ec24e04fd52cddaeae41be3cfa6967dac0eac --- diff --git a/ext/qrf/README.md b/ext/qrf/README.md index 7d62b1939b..36ef6cf346 100644 --- a/ext/qrf/README.md +++ b/ext/qrf/README.md @@ -386,15 +386,16 @@ The following output modes are currently defined: #define QRF_STYLE_Html 7 /* Generate an XHTML table */ #define QRF_STYLE_Insert 8 /* Generate SQL "insert" statements */ #define QRF_STYLE_Json 9 /* Output is a list of JSON objects */ -#define QRF_STYLE_Line 10 /* One column per line. */ -#define QRF_STYLE_List 11 /* One record per line with a separator */ -#define QRF_STYLE_Markdown 12 /* Markdown formatting */ -#define QRF_STYLE_Off 13 /* No query output shown */ -#define QRF_STYLE_Quote 14 /* SQL-quoted, comma-separated */ -#define QRF_STYLE_Stats 15 /* EQP-like output but with performance stats */ -#define QRF_STYLE_StatsEst 16 /* EQP-like output with planner estimates */ -#define QRF_STYLE_StatsVm 17 /* EXPLAIN-like output with performance stats */ -#define QRF_STYLE_Table 18 /* MySQL-style table formatting */ +#define QRF_STYLE_JsonLine 10 /* Independent JSON objects for each row */ +#define QRF_STYLE_Line 11 /* One column per line. */ +#define QRF_STYLE_List 12 /* One record per line with a separator */ +#define QRF_STYLE_Markdown 13 /* Markdown formatting */ +#define QRF_STYLE_Off 14 /* No query output shown */ +#define QRF_STYLE_Quote 15 /* SQL-quoted, comma-separated */ +#define QRF_STYLE_Stats 16 /* EQP-like output but with performance stats */ +#define QRF_STYLE_StatsEst 17 /* EQP-like output with planner estimates */ +#define QRF_STYLE_StatsVm 18 /* EXPLAIN-like output with performance stats */ +#define QRF_STYLE_Table 19 /* MySQL-style table formatting */ ~~~ In the following subsections, these styles will often be referred @@ -430,7 +431,7 @@ from the data below. This is very similar to default output styling in psql. The **Markdown** renders its result in the Markdown table format. -### 4.3 Line-oriented Styles (Csv, Html, Insert, Json, Line, List, Quote) +### 4.3 Line-oriented Styles The line-oriented styles output each row of result as it is received from the prepared statement. @@ -460,8 +461,11 @@ that will inserts the data that is output into a table whose name is defined by the zTableName field of `sqlite3_qrf_spec`. If zTableName is NULL, then a substitute name is used. -The **Json** style generates JSON text for the query result. -The JSON is an array of structures with on structure per row. +The **Json** and **JsonLine** styles generates JSON text for the query result. +The **Json** style produces a JSON array of structures with on +structure per row. **JsonLine** outputs independent JSON objects, one per +row, with each structure on a separate line all by itself, and not +part of a larger array. Finally, the **Line** style paints each column of a row on a separate line with the column name on the left and a "`=`" separating the diff --git a/ext/qrf/qrf.c b/ext/qrf/qrf.c index 9923aa9769..e6790f0c4d 100644 --- a/ext/qrf/qrf.c +++ b/ext/qrf/qrf.c @@ -1679,8 +1679,8 @@ qrf_reinit: if( p->spec.zRowSep==0 ) p->spec.zRowSep = "\n"; break; } + case QRF_STYLE_JsonLine: case QRF_STYLE_Json: { - p->spec.zColumnSep = ","; p->spec.eText = QRF_TEXT_Json; p->spec.eBlob = QRF_BLOB_Json; p->spec.zNull = "null"; @@ -1792,6 +1792,26 @@ static int qrf_need_quote(const char *zName){ return sqlite3_keyword_check(zName, i)!=0; } +/* +** Helper function for QRF_STYLE_Json and QRF_STYLE_JsonLine. +** The initial "{" for a JSON object that will contain row content +** has been output. Now output all the content. +*/ +static void qrfOneJsonRow(Qrf *p){ + int i, nItem; + for(nItem=i=0; inCol; i++){ + const char *zCName; + if( sqlite3_column_type(p->pStmt,i)==SQLITE_NULL ) continue; + zCName = sqlite3_column_name(p->pStmt, i); + if( nItem>0 ) sqlite3_str_append(p->pOut, ",", 1); + nItem++; + qrfEncodeText(p, p->pOut, zCName); + sqlite3_str_append(p->pOut, ":", 1); + qrfRenderValue(p, p->pOut, i); + } + qrfWrite(p); +} + /* ** Render a single row of output. */ @@ -1809,14 +1829,16 @@ static void qrfOneSimpleRow(Qrf *p){ }else{ sqlite3_str_append(p->pOut, "},\n{", 4); } - for(i=0; inCol; i++){ - const char *zCName = sqlite3_column_name(p->pStmt, i); - if( i>0 ) sqlite3_str_append(p->pOut, ",", 1); - qrfEncodeText(p, p->pOut, zCName); - sqlite3_str_append(p->pOut, ":", 1); - qrfRenderValue(p, p->pOut, i); + qrfOneJsonRow(p); + break; + } + case QRF_STYLE_JsonLine: { + if( p->nRow==0 ){ + sqlite3_str_append(p->pOut, "{", 1); + }else{ + sqlite3_str_append(p->pOut, "}\n{", 3); } - qrfWrite(p); + qrfOneJsonRow(p); break; } case QRF_STYLE_Html: { @@ -1945,6 +1967,11 @@ static void qrfFinalize(Qrf *p){ qrfWrite(p); break; } + case QRF_STYLE_JsonLine: { + sqlite3_str_append(p->pOut, "}\n", 2); + qrfWrite(p); + break; + } case QRF_STYLE_Line: { if( p->u.sLine.azCol ) sqlite3_free(p->u.sLine.azCol); break; diff --git a/ext/qrf/qrf.h b/ext/qrf/qrf.h index 4638d1d5e7..fb567a77ff 100644 --- a/ext/qrf/qrf.h +++ b/ext/qrf/qrf.h @@ -74,15 +74,16 @@ int sqlite3_format_query_result( #define QRF_STYLE_Html 7 /* Generate an XHTML table */ #define QRF_STYLE_Insert 8 /* Generate SQL "insert" statements */ #define QRF_STYLE_Json 9 /* Output is a list of JSON objects */ -#define QRF_STYLE_Line 10 /* One column per line. */ -#define QRF_STYLE_List 11 /* One record per line with a separator */ -#define QRF_STYLE_Markdown 12 /* Markdown formatting */ -#define QRF_STYLE_Off 13 /* No query output shown */ -#define QRF_STYLE_Quote 14 /* SQL-quoted, comma-separated */ -#define QRF_STYLE_Stats 15 /* EQP-like output but with performance stats */ -#define QRF_STYLE_StatsEst 16 /* EQP-like output with planner estimates */ -#define QRF_STYLE_StatsVm 17 /* EXPLAIN-like output with performance stats */ -#define QRF_STYLE_Table 18 /* MySQL-style table formatting */ +#define QRF_STYLE_JsonLine 10 /* Independent JSON objects for each row */ +#define QRF_STYLE_Line 11 /* One column per line. */ +#define QRF_STYLE_List 12 /* One record per line with a separator */ +#define QRF_STYLE_Markdown 13 /* Markdown formatting */ +#define QRF_STYLE_Off 14 /* No query output shown */ +#define QRF_STYLE_Quote 15 /* SQL-quoted, comma-separated */ +#define QRF_STYLE_Stats 16 /* EQP-like output but with performance stats */ +#define QRF_STYLE_StatsEst 17 /* EQP-like output with planner estimates */ +#define QRF_STYLE_StatsVm 18 /* EXPLAIN-like output with performance stats */ +#define QRF_STYLE_Table 19 /* MySQL-style table formatting */ /* ** Quoting styles for text. diff --git a/manifest b/manifest index 06ebad8659..96b492861f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sthe\ssize\sof\san\sinteger\sin\sthe\sTCL\sinterface. -D 2025-11-07T00:05:44.203 +C Add\sthe\sJsonLine\soutput\sstyle. +D 2025-11-07T12:12:02.995 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -416,9 +416,9 @@ F ext/misc/wholenumber.c 0fa0c082676b7868bf2fa918e911133f2b349bcdceabd1198bba5f6 F ext/misc/windirent.h 02211ce51f3034c675f2dbf4d228194d51b3ee05734678bad5106fff6292e60c F ext/misc/zipfile.c 09e6e3a3ff40a99677de3c0bc6569bd5f4709b1844ac3d1c1452a456c5a62f1c F ext/misc/zorder.c bddff2e1b9661a90c95c2a9a9c7ecd8908afab5763256294dd12d609d4664eee -F ext/qrf/README.md 6f5a674919ef7fafdd48253ce20f3a4ca0ebd60d343ef95d731399a745ed66b2 -F ext/qrf/qrf.c 82392dc3fb199d019dea4aa0bb381b7f4fa3b9cefb8e24f4806306f6fae17c02 -F ext/qrf/qrf.h 82723bce4cc458a9afc8e935c93a61bd210f0b67016d5b5c4b778da68e73edf3 +F ext/qrf/README.md 251568600920db5f82524ec63fc07e2dfebf98697cbaa708271b4ac6055ffb58 +F ext/qrf/qrf.c 772c87f50d591d7fa6e82730856ce04ac4321c31169d5025eae8608df3adc9b9 +F ext/qrf/qrf.h 39ba5d895f123bcce155b40446ec6d2728055f1f8bf29fdf8221f1eb8835d56d F ext/rbu/rbu.c 801450b24eaf14440d8fd20385aacc751d5c9d6123398df41b1b5aa804bf4ce8 F ext/rbu/rbu1.test 25870dd7db7eb5597e2b4d6e29e7a7e095abf332660f67d89959552ce8f8f255 F ext/rbu/rbu10.test 7c22caa32c2ff26983ca8320779a31495a6555737684af7aba3daaf762ef3363 @@ -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 7232269f64f386ad3718f6a4e13f8fec4a81ce987a2cda60f7b6f2ebff7a7a03 +F src/tclsqlite.c c180acb528411fcb468a2d5f925befdc53afe4c4f821bd7ca35d86826a6d4787 F src/tclsqlite.h 614b3780a62522bc9f8f2b9fb22689e8009958e7aa77e572d0f3149050af348a F src/test1.c f880ab766eeedf2c063662bd9538b923fd42c4341b7bfc2150a6d93ab8b9341c F src/test2.c 62f0830958f9075692c29c6de51b495ae8969e1bef85f239ffcd9ba5fb44a5ff @@ -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 1560df867282d37c4d43a4b2e66606ab129ade70fa1a1a3b9a720769ec8e36d0 +F test/qrf01.test 3f4d7169b7965545eb511258ed2ca4ef204a58f52cfe802d555b24239ef62ec8 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 d5e891426f8b2a0c3326bf868f2d811c44a1f7e619f42b273c7445ab443d618c -R d99d23dcb374c7d11bbf931dfdaab5ca +P 04156c34e4efeebe9b8c2ee2944f9576b685c6aa7e6180c8a5caff4958cde2e8 +R f3ddaa46784828527c17e04e8e958c82 U drh -Z 92bea9d231151b2c0d2bdb00c6a35f42 +Z 61b1f0db4819b7d638d03ceafac00a34 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7422aa83d5..d4055d2c78 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -04156c34e4efeebe9b8c2ee2944f9576b685c6aa7e6180c8a5caff4958cde2e8 +7dfba841cfc515ac28401c4eb90ec24e04fd52cddaeae41be3cfa6967dac0eac diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 5e897e13bb..31c03acdb1 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -2121,17 +2121,19 @@ static int dbQrf(SqliteDb *pDb, int objc, Tcl_Obj *const*objv){ "auto", "box", "column", "count", "csv", "eqp", "explain", "html", "insert", - "json", "line", "list", - "markdown", "quote", "stats", - "stats-est", "stats-vm", "table", 0 + "json", "json-line", "line", + "list", "markdown", "quote", + "stats", "stats-est", "stats-vm", + "table", 0 }; static unsigned char aStyleMap[] = { QRF_STYLE_Auto, QRF_STYLE_Box, QRF_STYLE_Column, QRF_STYLE_Count, QRF_STYLE_Csv, QRF_STYLE_Eqp, QRF_STYLE_Explain, QRF_STYLE_Html, QRF_STYLE_Insert, - QRF_STYLE_Json, QRF_STYLE_Line, QRF_STYLE_List, - QRF_STYLE_Markdown, QRF_STYLE_Quote, QRF_STYLE_Stats, - QRF_STYLE_StatsEst, QRF_STYLE_StatsVm, QRF_STYLE_Table + QRF_STYLE_Json, QRF_STYLE_JsonLine, QRF_STYLE_Line, + QRF_STYLE_List, QRF_STYLE_Markdown, QRF_STYLE_Quote, + QRF_STYLE_Stats, QRF_STYLE_StatsEst, QRF_STYLE_StatsVm, + QRF_STYLE_Table }; int style; rc = Tcl_GetIndexFromObj(pDb->interp, objv[i+1], azStyles, diff --git a/test/qrf01.test b/test/qrf01.test index 77d8b9329d..e4b87948ad 100644 --- a/test/qrf01.test +++ b/test/qrf01.test @@ -207,7 +207,19 @@ do_test 1.90 { set result "\n[db format -style json {SELECT * FROM t1}]" } { [{"a":1,"b":2.5,"c":"three"}, -{"a":"\u0042\u004c\u004f\u0042","b":null,"c":"Ἀμήν"}] +{"a":"\u0042\u004c\u004f\u0042","c":"Ἀμήν"}] +} +do_test 1.91 { + set result "\n[db format -style json-line {SELECT * FROM t1}]" +} { +{"a":1,"b":2.5,"c":"three"} +{"a":"\u0042\u004c\u004f\u0042","c":"Ἀμήν"} +} +do_test 1.92 { + set result "\n[db format -style json-line {SELECT *, unistr('abc\u000a123\u000d\u000axyz') AS xyz FROM t1}]" +} { +{"a":1,"b":2.5,"c":"three","xyz":"abc\n123\r\nxyz"} +{"a":"\u0042\u004c\u004f\u0042","c":"Ἀμήν","xyz":"abc\n123\r\nxyz"} } do_test 1.100 {