From: drh <> Date: Wed, 22 Apr 2026 12:22:32 +0000 (+0000) Subject: QRF and the CLI preserve zero bytes when outputing unformatted BLOB values. X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=429369f4aa5f5565d197ae3c6e6a544cd93be58b;p=thirdparty%2Fsqlite.git QRF and the CLI preserve zero bytes when outputing unformatted BLOB values. FossilOrigin-Name: dbc1d6f0a3452607e4e92155df0fdb1c5ea4ea5ad1654664f86383faf6c105ef --- diff --git a/ext/qrf/qrf.c b/ext/qrf/qrf.c index 7e77dc1860..952f03700a 100644 --- a/ext/qrf/qrf.c +++ b/ext/qrf/qrf.c @@ -717,19 +717,16 @@ static int qrfDisplayWidth(const char *zIn, sqlite3_int64 nByte, int *pnNL){ } /* -** Escape the input string if it is needed and in accordance with -** eEsc, which is either QRF_ESC_Ascii or QRF_ESC_Symbol. +** Escape the text starting at byte iStart of pStr, if needed, using the +** escape encoding of eEsc, which is either QRF_ESC_Ascii or QRF_ESC_Symbol. +** The pStr string is modified appropriately. ** ** Escaping is needed if the string contains any control characters ** other than \t, \n, and \r\n ** -** If no escaping is needed (the common case) then set *ppOut to NULL -** and return 0. If escaping is needed, write the escaped string into -** memory obtained from sqlite3_malloc64() and make *ppOut point to that -** memory and return 0. If an error occurs, return non-zero. -** -** The caller is responsible for freeing *ppFree if it is non-NULL in order -** to reclaim memory. +** If no escaping is needed (the common case) then pStr is unchanged. +** If escaping is required, then pStr is expanded and modified to hold +** an escaped representation of the text. */ static void qrfEscape( int eEsc, /* QRF_ESC_Ascii or QRF_ESC_Symbol */ @@ -737,20 +734,22 @@ static void qrfEscape( int iStart /* Begin escapding on this byte of pStr */ ){ sqlite3_int64 i, j; /* Loop counters */ - sqlite3_int64 sz; /* Size of the string prior to escaping */ sqlite3_int64 nCtrl = 0;/* Number of control characters to escape */ unsigned char *zIn; /* Text to be escaped */ + unsigned char nIn; /* Bytes of text to be escaped */ unsigned char c; /* A single character of the text */ unsigned char *zOut; /* Where to write the results */ /* Find the text to be escaped */ zIn = (unsigned char*)sqlite3_str_value(pStr); + nIn = sqlite3_str_length(pStr); if( zIn==0 ) return; zIn += iStart; + nIn -= iStart; /* Count the control characters */ - for(i=0; (c = zIn[i])!=0; i++){ - if( c<=0x1f + for(i=0; i0x1f + for(i=j=0; i0x1f || c=='\t' || c=='\n' || (c=='\r' && zIn[i+1]=='\n') @@ -785,6 +783,7 @@ static void qrfEscape( j += i; } zIn += i+1; + nIn -= i+1; i = -1; if( eEsc==QRF_ESC_Symbol ){ zOut[j++] = 0xe2; @@ -1187,8 +1186,22 @@ static void qrfRenderValue(Qrf *p, sqlite3_str *pOut, int iCol){ break; } default: { - const char *zTxt = (const char*)sqlite3_column_text(p->pStmt,iCol); - qrfEncodeText(p, pOut, zTxt); + const void *pBlob = sqlite3_column_blob(p->pStmt,iCol); + int nBlob = sqlite3_column_bytes(p->pStmt,iCol); + int rc; + qrfWrite(p); + if( nBlob==0 ){ + /* no-op */ + }else if( p->spec.eEsc==QRF_ESC_Off ){ + rc = p->spec.xWrite(p->spec.pWriteArg,pBlob,nBlob); + if( rc ){ + qrfError(p, rc, "Failed to write %d bytes of BLOB output", nBlob); + } + }else{ + sqlite3_str_append(pOut, pBlob, nBlob); + qrfEscape(p->spec.eEsc, pOut, 0); + qrfWrite(p); + } } } break; diff --git a/manifest b/manifest index 7e0cb481fd..8848319fd6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sharmless\scompiler\swarning\sin\sQRF. -D 2026-04-21T22:19:38.042 +C QRF\sand\sthe\sCLI\spreserve\szero\sbytes\swhen\soutputing\sunformatted\sBLOB\svalues. +D 2026-04-22T12:22:32.829 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -422,7 +422,7 @@ F ext/misc/zipfile.c 5a583b5e72b4d777dc9f845529e6bd185d58024b633aafc93588679c787 F ext/misc/zorder.c bddff2e1b9661a90c95c2a9a9c7ecd8908afab5763256294dd12d609d4664eee F ext/qrf/README.md 9e644615d7d7b77ef7e9db798765679e50c5ed12eda48bce21c9ef9eb4715e9d F ext/qrf/dev-notes.md e68a6d91ce4c7eb296ef2daadc2bb79c95c317ad15b9fafe40850c67b29c2430 -F ext/qrf/qrf.c 0c3396aa6d38ed50d741b81ceef18d1f591813a2691e52e3d33b20b065108867 +F ext/qrf/qrf.c b177155147ae97ce3ddc5791c08782e835405c51ca9b9655f4beffcc03028cae F ext/qrf/qrf.h fbb223ff5789b324b3e9c22e787e4c1f53e217cff7cc5a243164d4b2e8410f4b F ext/rbu/rbu.c 801450b24eaf14440d8fd20385aacc751d5c9d6123398df41b1b5aa804bf4ce8 F ext/rbu/rbu1.test 25870dd7db7eb5597e2b4d6e29e7a7e095abf332660f67d89959552ce8f8f255 @@ -736,7 +736,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 928ff887f2a7c64275182060d94d06fdddbe32226c569781cf7e7edc6f58d7fd F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 4c05cde130f26991b7411d8c6809e0630625e18078742c963a047b4b9cc01d49 -F src/shell.c.in 4e68371fec5a8ab31038d51e7179d9ef3539d693e05a5fdabcd59483e445b27d +F src/shell.c.in 68ce578f06cf2fcf6fa17867f9a35a139be1c39fd019be3ab1a7b9e6732e78ea F src/sqlite.h.in 39d2e09114d2bdb7afd998f4a469c8f8cd065f8093835a7d0422f260fc78fb4f F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479 F src/sqlite3ext.h 9788c301f95370fa30e808861f1d2e6f022a816ddbe2a4f67486784c1b31db2e @@ -2202,8 +2202,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c -P d0d95a39bc211034071746395ee6dbcfbea9d573d27480655391ba9698f7e214 -R 358d94241303ce254bf450c1de0664be +P dcfb505ac7762f7a3102e1c3853a83dca1d1ef436cc8981949b63c7d7992cc5b +R 6f7eb80bc3d8547ebc53c3288e84721f U drh -Z f2e54d7d5452408c8009417fa06b81d6 +Z 8d40acb0befcd8d33f03cbfe6f7ca5f8 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d0cc072f3b..f69900bae9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -dcfb505ac7762f7a3102e1c3853a83dca1d1ef436cc8981949b63c7d7992cc5b +dbc1d6f0a3452607e4e92155df0fdb1c5ea4ea5ad1654664f86383faf6c105ef diff --git a/src/shell.c.in b/src/shell.c.in index fe3e10cc71..16933cd2c0 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -689,6 +689,7 @@ static FILE *iotrace = 0; ** Works like. ** -------------- ** cli_printf(FILE*, const char*, ...); fprintf() +** cli_write(FILE*, const char*, int); write() ** cli_puts(const char*, FILE*); fputs() ** cli_vprintf(FILE*, const char*, va_list); vfprintf() ** @@ -715,6 +716,14 @@ static int cli_printf(FILE *out, const char *zFormat, ...){ va_end(ap); return rc; } +static int cli_write(FILE *out, const char *zData, int nData){ + if( cli_output_capture && (out==stdout || out==stderr) ){ + sqlite3_str_append(cli_output_capture, zData, nData); + }else{ + nData = (int)fwrite(zData, 1, nData, out); + } + return nData; +} static int cli_puts(const char *zText, FILE *out){ if( cli_output_capture && (out==stdout || out==stderr) ){ sqlite3_str_appendall(cli_output_capture, zText); @@ -3414,7 +3423,7 @@ static int expertDotCommand( */ static int shellWriteQR(void *pX, const char *z, sqlite3_int64 n){ ShellState *pArg = (ShellState*)pX; - cli_printf(pArg->out, "%.*s", (int)n, z); + cli_write(pArg->out, z, (int)n); return SQLITE_OK; }