]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add separate quoting style for BLOB values.
authordrh <>
Tue, 21 Oct 2025 15:30:38 +0000 (15:30 +0000)
committerdrh <>
Tue, 21 Oct 2025 15:30:38 +0000 (15:30 +0000)
FossilOrigin-Name: dfe537175e7d15507e8532817a06f76dc375c7eb00f5cdc4b4f8d71ec516137e

ext/misc/resfmt-tester.c
ext/misc/resfmt.c
ext/misc/resfmt.h
manifest
manifest.uuid

index 81bcc91b75713756e971af0e7704a758548a2384..96a120508b8107a429feb41c34206da4c9752f49 100644 (file)
@@ -218,13 +218,12 @@ int main(int argc, char **argv){
     }else
     if( strncmp(zLine, "--eQuote=", 9)==0 ){
       const struct { const char *z; int e; } aQuote[] = {
-         { "c",        RESFMT_Q_C       },
          { "csv",      RESFMT_Q_Csv     },
          { "html",     RESFMT_Q_Html    },
          { "json",     RESFMT_Q_Json    },
          { "off",      RESFMT_Q_Off     },
          { "sql",      RESFMT_Q_Sql     },
-         { "tcl",      RESFMT_Q_C       },
+         { "tcl",      RESFMT_Q_Tcl     },
       };
       int i;
       for(i=0; i<COUNT(aQuote); i++){
@@ -243,6 +242,32 @@ int main(int argc, char **argv){
         sqlite3_free(sqlite3_str_finish(pMsg));
       }
     }else
+    if( strncmp(zLine, "--eBlob=", 8)==0 ){
+      const struct { const char *z; int e; } aBlob[] = {
+         { "auto",    RESFMT_B_Auto    },
+         { "hex",     RESFMT_B_Hex     },
+         { "json",    RESFMT_B_Json    },
+         { "tcl",     RESFMT_B_Tcl     },
+         { "text",    RESFMT_B_Text    },
+         { "sql",     RESFMT_B_Sql     },
+      };
+      int i;
+      for(i=0; i<COUNT(aBlob); i++){
+        if( strcmp(aBlob[i].z,&zLine[8])==0 ){
+          spec.eBlob = aBlob[i].e;
+          break;
+        }
+      }
+      if( i>=COUNT(aBlob) ){
+        sqlite3_str *pMsg = sqlite3_str_new(0);
+        for(i=0; i<COUNT(aBlob); i++){
+          sqlite3_str_appendf(pMsg, " %s", aBlob[i].z);
+        }
+        fprintf(stderr, "%s:%d: no such blob style: \"%s\"\nChoices: %s\n",
+                zSrc, lineNum, &zLine[8], sqlite3_str_value(pMsg));
+        sqlite3_free(sqlite3_str_finish(pMsg));
+      }
+    }else
     if( strncmp(zLine, "--eEscape=", 10)==0 ){
       const struct { const char *z; int e; } aEscape[] = {
          { "ascii",     RESFMT_E_Ascii   },
index 274a4807d8d050fef46212dba9820a76f422f4dc..7fd952747fe78a613370c6ed329e9ea4ba6fa023 100644 (file)
@@ -187,26 +187,65 @@ static void resfmtRenderValue(sqlite3_resfmt *p, int iCol){
       break;
     }
     case SQLITE_BLOB: {
-      switch( p->spec.eQuote ){
-        case RESFMT_Q_Sql: {
-          int iStart = sqlite3_str_length(p->pOut);
+      switch( p->spec.eBlob ){
+        case RESFMT_B_Hex:
+        case RESFMT_B_Sql: {
+          int iStart;
           int nBlob = sqlite3_column_bytes(p->pStmt,iCol);
           int i, j;
           char *zVal;
           const unsigned char *a = sqlite3_column_blob(p->pStmt,iCol);
-          sqlite3_str_append(p->pOut, "x'", 2);
+          if( p->spec.eBlob==RESFMT_B_Sql ){
+            sqlite3_str_append(p->pOut, "x'", 2);
+          }
+          iStart = sqlite3_str_length(p->pOut);
           sqlite3_str_appendchar(p->pOut, nBlob, ' ');
           sqlite3_str_appendchar(p->pOut, nBlob, ' ');
-          sqlite3_str_appendchar(p->pOut, 1, '\'');
+          if( p->spec.eBlob==RESFMT_B_Sql ){
+            sqlite3_str_appendchar(p->pOut, 1, '\'');
+          }
           if( sqlite3_str_errcode(p->pOut) ) return;
           zVal = sqlite3_str_value(p->pOut);
-          for(i=0, j=iStart+2; i<nBlob; i++, j+=2){
+          for(i=0, j=iStart; i<nBlob; i++, j+=2){
             unsigned char c = a[i];
             zVal[j] = "0123456789abcdef"[(c>>4)&0xf];
             zVal[j+1] = "0123456789abcdef"[(c)&0xf];
           }
           break;
         }
+        case RESFMT_B_Tcl:
+        case RESFMT_B_Json: {
+          int iStart;
+          int nBlob = sqlite3_column_bytes(p->pStmt,iCol);
+          int i, j;
+          char *zVal;
+          const unsigned char *a = sqlite3_column_blob(p->pStmt,iCol);
+          int szC = p->spec.eBlob==RESFMT_B_Json ? 6 : 4;
+          sqlite3_str_append(p->pOut, "\"", 1);
+          iStart = sqlite3_str_length(p->pOut);
+          for(i=szC; i>0; i--){
+            sqlite3_str_appendchar(p->pOut, nBlob, ' ');
+          }
+          sqlite3_str_appendchar(p->pOut, 1, '"');
+          if( sqlite3_str_errcode(p->pOut) ) return;
+          zVal = sqlite3_str_value(p->pOut);
+          for(i=0, j=iStart; i<nBlob; i++, j+=szC){
+            unsigned char c = a[i];
+            zVal[j] = '\\';
+            if( szC==4 ){
+              zVal[j+1] = '0' + ((c>>6)&3);
+              zVal[j+2] = '0' + ((c>>3)&7);
+              zVal[j+3] = '0' + (c&7);
+            }else{
+              zVal[j+1] = 'u';
+              zVal[j+2] = '0';
+              zVal[j+3] = '0';
+              zVal[j+4] = "0123456789abcdef"[(c>>4)&0xf];
+              zVal[j+5] = "0123456789abcdef"[(c)&0xf];
+            }
+          }
+          break;
+        }
         default: {
           const char *zTxt = (const char*)sqlite3_column_text(p->pStmt,iCol);
           resfmtEncodeText(p, zTxt);
@@ -255,6 +294,15 @@ sqlite3_resfmt *sqlite3_resfmt_begin(
   sz = sizeof(sqlite3_resfmt_spec);
   memcpy(&p->spec, pSpec, sz);
   if( p->spec.zNull==0 ) p->spec.zNull = "";
+  if( p->spec.eBlob==RESFMT_B_Auto ){
+    switch( p->spec.eQuote ){
+      case RESFMT_Q_Sql:  p->spec.eBlob = RESFMT_B_Sql;  break;
+      case RESFMT_Q_Csv:  p->spec.eBlob = RESFMT_B_Tcl;  break;
+      case RESFMT_Q_Tcl:  p->spec.eBlob = RESFMT_B_Tcl;  break;
+      case RESFMT_Q_Json: p->spec.eBlob = RESFMT_B_Json; break;
+      default:            p->spec.eBlob = RESFMT_B_Text; break;
+    }
+  }
   switch( p->spec.eFormat ){
     case RESFMT_List: {
       if( p->spec.zColumnSep==0 ) p->spec.zColumnSep = "|";
index 1ac93d5e2af14bdfc67069dca3834a0f51bab0f3..0de5b4a7428b387181fff30677aef94abcb51862 100644 (file)
 */
 typedef struct sqlite3_resfmt_spec sqlite3_resfmt_spec;
 struct sqlite3_resfmt_spec {
-  int iVersion;               /* Version number of this structure */
-  int eFormat;                /* Output format */
+  short int iVersion;         /* Version number of this structure */
+  unsigned char eFormat;      /* Output format */
   unsigned char bShowCNames;  /* True to show column names */
   unsigned char eEscape;      /* How to deal with control characters */
-  unsigned char eQuote;       /* Quoting style */
+  unsigned char eQuote;       /* Quoting style for text */
+  unsigned char eBlob;        /* Quoting style for BLOBs */
   unsigned char bWordWrap;    /* Try to wrap on word boundaries */
-  int mxWidth;                /* Maximum column width in columnar modes */
+  short int mxWidth;          /* Maximum column width in columnar modes */
+  int nWidth;                 /* Number of column width parameters */
+  short int *aWidth;          /* Column widths */
   const char *zColumnSep;     /* Alternative column separator */
   const char *zRowSep;        /* Alternative row separator */
   const char *zTableName;     /* Output table name */
   const char *zNull;          /* Rendering of NULL */
   const char *zFloatFmt;      /* printf-style string for rendering floats */
-  int nWidth;                 /* Number of column width parameters */
-  short int *aWidth;          /* Column widths */
   char *(*xRender)(void*,sqlite3_value*);                /* Render a value */
   ssize_t (*xWrite)(void*,const unsigned char*,size_t);  /* Write callback */
   void *pRenderArg;           /* First argument to the xRender callback */
@@ -77,16 +78,27 @@ int sqlite3_resfmt_finish(sqlite3_resfmt*,int*,char**);
 #define RESFMT_Www      17 /* Full web-page output */
 
 /*
-** Quoting styles.
+** Quoting styles for text.
 ** Allowed values for sqlite3_resfmt_spec.eQuote
 */
 #define RESFMT_Q_Off     0 /* Literal text */
 #define RESFMT_Q_Sql     1 /* Quote as an SQL literal */
 #define RESFMT_Q_Csv     2 /* CSV-style quoting */
 #define RESFMT_Q_Html    3 /* HTML-style quoting */
-#define RESFMT_Q_C       4 /* C/Tcl quoting */
+#define RESFMT_Q_Tcl     4 /* C/Tcl quoting */
 #define RESFMT_Q_Json    5 /* JSON quoting */
 
+/*
+** Quoting styles for BLOBs
+** Allowed values for sqlite3_resfmt_spec.eBlob
+*/
+#define RESFMT_B_Auto    0 /* Determine BLOB quoting using eQuote */
+#define RESFMT_B_Text    1 /* Display content exactly as it is */
+#define RESFMT_B_Sql     2 /* Quote as an SQL literal */
+#define RESFMT_B_Hex     3 /* Hexadecimal representation */
+#define RESFMT_B_Tcl     4 /* "\000" notation */
+#define RESFMT_B_Json    5 /* A JSON string */
+
 /*
 ** Control-character escape modes.
 ** Allowed values for sqlite3_resfmt_spec.eEscape
index 4f9f28a0133d447a7834a24cc60729d5acc0eaf3..ae39ac0fbd25bf57ac61bae5b3bccfcae5009936 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Control-character\sescaping
-D 2025-10-21T13:07:51.412
+C Add\sseparate\squoting\sstyle\sfor\sBLOB\svalues.
+D 2025-10-21T15:30:38.080
 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -389,9 +389,9 @@ F ext/misc/qpvtab.c fc189e127f68f791af90a487f4460ec91539a716daf45a0c357e963fd47c
 F ext/misc/randomjson.c ef835fc64289e76ac4873b85fe12f9463a036168d7683cf2b773e36e6262c4ed
 F ext/misc/regexp.c 548151f3e57506fda678e6a65e85a763f4eece653287e1ad44e167f9485e0c6b
 F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c
-F ext/misc/resfmt-tester.c 1cb1dae3f31caa13e4b7ef635eec05a5b1981aaa2202a72ca74cd995ceb5c677
-F ext/misc/resfmt.c cae8cff21efa71d0779999f715023488e96739d690c1f5bff17f947b7f43a10a
-F ext/misc/resfmt.h 54937535c8d7bfda506ec3ecd9c9e6fe247f3621c0645d067809f2eb9d4bb70e
+F ext/misc/resfmt-tester.c 4c493f3b74f1bc49c606a504ca8417e4a0c7bc114611c2b963df1e35f34f19bc
+F ext/misc/resfmt.c 40cc38e8ca5ba02f3ee03bfdd02f305e6dc0db151ce6929629365ac356702692
+F ext/misc/resfmt.h 0abf02956c5f03ff7823861d0134e541e435426ed5ba7209bf6dfb8b61e0cd0e
 F ext/misc/resfmt.md 6f6cefd95fa11ce30e4f34ea84052e7a8291dd48b7e666352bd7cf2e22c22ec4
 F ext/misc/rot13.c 51ac5f51e9d5fd811db58a9c23c628ad5f333c173f1fc53c8491a3603d38556c
 F ext/misc/scrub.c 2a44b0d44c69584c0580ad2553f6290a307a49df4668941d2812135bfb96a946
@@ -2175,8 +2175,8 @@ F tool/version-info.c 33d0390ef484b3b1cb685d59362be891ea162123cea181cb8e6d2cf6dd
 F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
 F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 3ee2a8775fcc557a53c36240ddc039024ff52e21d9ad9021d7cf5bf79f8b9ed4
-R 78c35db5d2e47632e3afeaff2a301639
+P fbb69764559f4333890860f4f3c010efa9cc994a82743766b948e7635fd079e8
+R 0b9bda5bbcc16ecc914edff675de2c46
 U drh
-Z dd3d4f12786ad3019ddd480d2d6b5f14
+Z 666fe7e2fd9d7f3f1b51180f13bf63cd
 # Remove this line to create a well-formed Fossil manifest.
index b62a9bd40291cdde3b4dc9ec23fdf1cd60c23507..8a56790cebdc5dd8ae1bb74b81b096a28654a808 100644 (file)
@@ -1 +1 @@
-fbb69764559f4333890860f4f3c010efa9cc994a82743766b948e7635fd079e8
+dfe537175e7d15507e8532817a06f76dc375c7eb00f5cdc4b4f8d71ec516137e