]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Experimental code (untested) for a JSONB datatype.
authordrh <drh@noemail.net>
Thu, 13 Aug 2015 13:54:59 +0000 (13:54 +0000)
committerdrh <drh@noemail.net>
Thu, 13 Aug 2015 13:54:59 +0000 (13:54 +0000)
FossilOrigin-Name: e3596ac7b1dd5bde3f9cae5781a6806d8d9f7166

ext/misc/json.c
manifest
manifest.uuid

index 8acbb9bf5fd12d7e6f2392a1e019ac90d1d96144..4f3e1c02bbc89a4a1436881e13d9c2396351b029 100644 (file)
 ** input is understood to be JSONB if it a BLOB and JSON if the input is
 ** of any other type.  Functions that begin with the "json_" prefix return
 ** JSON and functions that begin with "jsonb_" return JSONB.
+**
+** JSONB format:
+**
+** A JSONB blob is a sequence of terms.  Each term begins with a single
+** variable length integer X which determines the type and size of the term.
+**
+**      type = X%8
+**      size = X>>3
+**
+** Term types are 0 through 7 for null, true, false, integer, real, string,
+** array, and object.  The meaning of size depends on the type.
+**
+** For null, true, and false terms, the size is always 0.
+**
+** For integer terms, the size is the number of bytes that contains the
+** integer value.  The value is stored as big-endian twos-complement.
+**
+** For real terms, the size is always 8 and the value is a big-ending
+** double-precision floating-point number.
+**
+** For string terms, the size is the number of bytes in the string.  The
+** string itself immediately follows the X integer.  There are no escapes
+** and the string is not zero-terminated.  The string is always stored as
+** UTF8.
+**
+** For array terms, the size is the number of bytes in content.  The
+** content consists of zero or more additional terms that are the elements
+** of the array.
+**
+** For object terms, the size is the number of bytes of content.  The 
+** content is zero or more pairs of terms.  The first element of each
+** pair is a string term which is the label and the second element is
+** the value.
+**
+** Variable Length Integers:
+**
+** The variable length integer encoding is the 64-bit unsigned integer encoding
+** originally developed for SQLite4.  The encoding for each integer is between
+** 1 and 9 bytes.  Call those bytes A0 through A8.  The encoding is as follows:
+**
+**    If A0 is between 0 and 240 inclusive, then the value is A0.
+** 
+**    If A0 is between 241 and 248 inclusive, then the value is
+**    240+256*(A0-241)+A1.
+** 
+**    If A0 is 249 then the value is 2288+256*A1+A2.
+** 
+**    If A0 is 250 or more then the value is a (A0-247)-byte big-endian
+**    integer taken from starting at A1.
 */
 #include "sqlite3ext.h"
 SQLITE_EXTENSION_INIT1
@@ -34,12 +83,13 @@ typedef sqlite3_uint64 u64;
 typedef unsigned int u32;
 typedef unsigned char u8;
 
-/* An instance of this object represents a JSON string under construction.
+/* An instance of this object represents a JSON string or
+** JSONB blob under construction.
 */
 typedef struct Json Json;
 struct Json {
   sqlite3_context *pCtx;   /* Function context - put error messages here */
-  char *zBuf;              /* Append JSON text here */
+  char *zBuf;              /* Append JSON or JSONB content here */
   u64 nAlloc;              /* Bytes of storage available in zBuf[] */
   u64 nUsed;               /* Bytes of zBuf[] currently used */
   u8 bStatic;              /* True if zBuf is static space */
@@ -47,6 +97,73 @@ struct Json {
   char zSpace[100];        /* Initial static space */
 };
 
+/* JSONB type values
+*/
+#define JSONB_NULL     0
+#define JSONB_TRUE     1
+#define JSONB_FALSE    2
+#define JSONB_INT      3
+#define JSONB_REAL     4
+#define JSONB_STRING   5
+#define JSONB_ARRAY    6
+#define JSONB_OBJECT   7
+
+#if 0
+/*
+** Decode the varint in the first n bytes z[].  Write the integer value
+** into *pResult and return the number of bytes in the varint.
+**
+** If the decode fails because there are not enough bytes in z[] then
+** return 0;
+*/
+static int jsonGetVarint64(
+  const unsigned char *z,
+  int n,
+  u64 *pResult
+){
+  unsigned int x;
+  if( n<1 ) return 0;
+  if( z[0]<=240 ){
+    *pResult = z[0];
+    return 1;
+  }
+  if( z[0]<=248 ){
+    if( n<2 ) return 0;
+    *pResult = (z[0]-241)*256 + z[1] + 240;
+    return 2;
+  }
+  if( n<z[0]-246 ) return 0;
+  if( z[0]==249 ){
+    *pResult = 2288 + 256*z[1] + z[2];
+    return 3;
+  }
+  if( z[0]==250 ){
+    *pResult = (z[1]<<16) + (z[2]<<8) + z[3];
+    return 4;
+  }
+  x = (z[1]<<24) + (z[2]<<16) + (z[3]<<8) + z[4];
+  if( z[0]==251 ){
+    *pResult = x;
+    return 5;
+  }
+  if( z[0]==252 ){
+    *pResult = (((u64)x)<<8) + z[5];
+    return 6;
+  }
+  if( z[0]==253 ){
+    *pResult = (((u64)x)<<16) + (z[5]<<8) + z[6];
+    return 7;
+  }
+  if( z[0]==254 ){
+    *pResult = (((u64)x)<<24) + (z[5]<<16) + (z[6]<<8) + z[7];
+    return 8;
+  }
+  *pResult = (((u64)x)<<32) +
+               (0xffffffff & ((z[5]<<24) + (z[6]<<16) + (z[7]<<8) + z[8]));
+  return 9;
+}
+#endif
+
 /* Set the Json object to an empty string
 */
 static void jsonZero(Json *p){
@@ -138,7 +255,92 @@ static void jsonAppendString(Json *p, const char *zIn, u32 N){
   p->zBuf[p->nUsed++] = '"';
 }
 
-/* Make pJson the result of the SQL function.
+/*
+** Write a 32-bit unsigned integer as 4 big-endian bytes.
+*/
+static void jsonPutInt32(unsigned char *z, unsigned int y){
+  z[0] = (unsigned char)(y>>24);
+  z[1] = (unsigned char)(y>>16);
+  z[2] = (unsigned char)(y>>8);
+  z[3] = (unsigned char)(y);
+}
+
+
+/* Write integer X as a variable-length integer into the buffer z[].
+** z[] is guaranteed to be at least 9 bytes in length.  Return the
+** number of bytes written.
+*/
+int jsonPutVarint64(char *zIn, u64 x){
+  unsigned char *z = (unsigned char*)zIn;
+  unsigned int w, y;
+  if( x<=240 ){
+    z[0] = (unsigned char)x;
+    return 1;
+  }
+  if( x<=2287 ){
+    y = (unsigned int)(x - 240);
+    z[0] = (unsigned char)(y/256 + 241);
+    z[1] = (unsigned char)(y%256);
+    return 2;
+  }
+  if( x<=67823 ){
+    y = (unsigned int)(x - 2288);
+    z[0] = 249;
+    z[1] = (unsigned char)(y/256);
+    z[2] = (unsigned char)(y%256);
+    return 3;
+  }
+  y = (unsigned int)x;
+  w = (unsigned int)(x>>32);
+  if( w==0 ){
+    if( y<=16777215 ){
+      z[0] = 250;
+      z[1] = (unsigned char)(y>>16);
+      z[2] = (unsigned char)(y>>8);
+      z[3] = (unsigned char)(y);
+      return 4;
+    }
+    z[0] = 251;
+    jsonPutInt32(z+1, y);
+    return 5;
+  }
+  if( w<=255 ){
+    z[0] = 252;
+    z[1] = (unsigned char)w;
+    jsonPutInt32(z+2, y);
+    return 6;
+  }
+  if( w<=65535 ){
+    z[0] = 253;
+    z[1] = (unsigned char)(w>>8);
+    z[2] = (unsigned char)w;
+    jsonPutInt32(z+3, y);
+    return 7;
+  }
+  if( w<=16777215 ){
+    z[0] = 254;
+    z[1] = (unsigned char)(w>>16);
+    z[2] = (unsigned char)(w>>8);
+    z[3] = (unsigned char)w;
+    jsonPutInt32(z+4, y);
+    return 8;
+  }
+  z[0] = 255;
+  jsonPutInt32(z+1, w);
+  jsonPutInt32(z+5, y);
+  return 9;
+}
+
+
+/* Append integer X as a variable-length integer on the JSONB currently
+** under construction in p.
+*/
+static void jsonAppendVarint(Json *p, u64 X){
+  if( (p->nUsed+9 > p->nAlloc) && jsonGrow(p,9)!=0 ) return;
+  p->nUsed += jsonPutVarint64(p->zBuf+p->nUsed, X);
+}
+
+/* Make the JSON in p the result of the SQL function.
 */
 static void jsonResult(Json *p){
   if( p->mallocFailed==0 ){
@@ -150,6 +352,17 @@ static void jsonResult(Json *p){
   assert( p->bStatic );
 }
 
+/* Make the JSONB in p the result of the SQL function.
+*/
+static void jsonbResult(Json *p){
+  if( p->mallocFailed==0 ){
+    sqlite3_result_blob(p->pCtx, p->zBuf, p->nUsed, 
+                        p->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
+    jsonZero(p);
+  }
+  assert( p->bStatic );
+}
+
 /*
 ** Implementation of the json_array(VALUE,...) function.  Return a JSON
 ** array that contains all values given in arguments.  Or if any argument
@@ -197,6 +410,55 @@ static void jsonArrayFunc(
   jsonResult(&jx);
 }
 
+/*
+** Implementation of the jsonb_array(VALUE,...) function.  Return a JSON
+** array that contains all values given in arguments.  Or if any argument
+** is a BLOB, throw an error.
+*/
+static void jsonbArrayFunc(
+  sqlite3_context *context,
+  int argc,
+  sqlite3_value **argv
+){
+  int i;
+  Json jx;
+
+  jsonInit(&jx, context);
+  jx.nUsed = 5;
+  for(i=0; i<argc; i++){
+    switch( sqlite3_value_type(argv[i]) ){
+      case SQLITE_NULL: {
+        jsonAppendVarint(&jx, JSONB_NULL);
+        break;
+      }
+      case SQLITE_INTEGER:
+      case SQLITE_FLOAT: {
+        const char *z = (const char*)sqlite3_value_text(argv[i]);
+        u32 n = (u32)sqlite3_value_bytes(argv[i]);
+        jsonAppendRaw(&jx, z, n);
+        break;
+      }
+      case SQLITE_TEXT: {
+        const char *z = (const char*)sqlite3_value_text(argv[i]);
+        u32 n = (u32)sqlite3_value_bytes(argv[i]);
+        jsonAppendVarint(&jx, JSONB_STRING + 4*(u64)n);
+        jsonAppendString(&jx, z, n);
+        break;
+      }
+      default: {
+        jsonZero(&jx);
+        sqlite3_result_error(context, "JSON cannot hold BLOB values", -1);
+        return;
+      }
+    }
+  }
+  if( jx.mallocFailed==0 ){
+    jx.zBuf[0] = 251;
+    jsonPutInt32((unsigned char*)(jx.zBuf+1), jx.nUsed-5);
+    jsonbResult(&jx);
+  }
+}
+
 /*
 ** Implementation of the json_object(NAME,VALUE,...) function.  Return a JSON
 ** object that contains all name/value given in arguments.  Or if any name
@@ -276,6 +538,7 @@ int sqlite3_json_init(
      void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
   } aFunc[] = {
     { "json_array",     -1,    jsonArrayFunc     },
+    { "jsonb_array",    -1,    jsonbArrayFunc    },
     { "json_object",    -1,    jsonObjectFunc    },
   };
   SQLITE_EXTENSION_INIT2(pApi);
index 7bbb95d28d95ad385c238b54bd1d76894847b983..1d41c76aaf5b9aae0b388444fc852e74f7f072db 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sthe\sjson_object()\sfunction.
-D 2015-08-12T17:23:34.617
+C Experimental\scode\s(untested)\sfor\sa\sJSONB\sdatatype.
+D 2015-08-13T13:54:59.587
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 7669f34c487f5b328de6b508f374ee1e56558bb0
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -192,7 +192,7 @@ F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2
 F ext/misc/fileio.c d4171c815d6543a9edef8308aab2951413cd8d0f
 F ext/misc/fuzzer.c 4c84635c71c26cfa7c2e5848cf49fe2d2cfcd767
 F ext/misc/ieee754.c b0362167289170627659e84173f5d2e8fee8566e
-F ext/misc/json.c 9df3054186b0b84dc7b3ce7e8694ebad4fdc5f7c
+F ext/misc/json.c 748c5bffa8840f1b0bdc4b5ff672949c31c7f436
 F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342
 F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63
 F ext/misc/regexp.c af92cdaa5058fcec1451e49becc7ba44dba023dc
@@ -1374,7 +1374,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P dde8afdd8dba1d92560326dca7c1cdfedbe5e070
-R 75cab18873f077d21ed527f3798bc75c
+P 414a95f3b79359f167858b2325fd2be223569c66
+R 45e5df2bce81d8e555551bffb25d0cb6
 U drh
-Z 14906f22419c39d5dff2c5387892aa6a
+Z 62911abad9701038f8848b5004ef4c38
index dd889e0ecfda5a7336a98107c80ba2a4b1d82789..58f3872eaf26a3e49076fad6ad004f02d47f7651 100644 (file)
@@ -1 +1 @@
-414a95f3b79359f167858b2325fd2be223569c66
\ No newline at end of file
+e3596ac7b1dd5bde3f9cae5781a6806d8d9f7166
\ No newline at end of file