From: drh Date: Wed, 12 Aug 2015 16:49:40 +0000 (+0000) Subject: Begin adding an extension that provides JSON SQL functions. X-Git-Tag: version-3.9.0~204^2~21 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5fa5c10784d6117df1706eb449dcd92b8396939b;p=thirdparty%2Fsqlite.git Begin adding an extension that provides JSON SQL functions. FossilOrigin-Name: dde8afdd8dba1d92560326dca7c1cdfedbe5e070 --- diff --git a/Makefile.in b/Makefile.in index a0f536cb23..4c83d9dfa3 100644 --- a/Makefile.in +++ b/Makefile.in @@ -417,6 +417,7 @@ TESTSRC += \ $(TOP)/ext/fts5/fts5_tcl.c \ $(TOP)/ext/fts5/fts5_test_mi.c \ $(TOP)/ext/misc/ieee754.c \ + $(TOP)/ext/misc/json.c \ $(TOP)/ext/misc/nextchar.c \ $(TOP)/ext/misc/percentile.c \ $(TOP)/ext/misc/regexp.c \ diff --git a/Makefile.msc b/Makefile.msc index 22d3fb523e..52769889aa 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -1083,6 +1083,7 @@ TESTEXT = \ $(TOP)\ext\fts5\fts5_tcl.c \ $(TOP)\ext\fts5\fts5_test_mi.c \ $(TOP)\ext\misc\ieee754.c \ + $(TOP)\ext\misc\json.c \ $(TOP)\ext\misc\nextchar.c \ $(TOP)\ext\misc\percentile.c \ $(TOP)\ext\misc\regexp.c \ diff --git a/ext/misc/json.c b/ext/misc/json.c new file mode 100644 index 0000000000..53442a9c6a --- /dev/null +++ b/ext/misc/json.c @@ -0,0 +1,225 @@ +/* +** 2015-08-12 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This SQLite extension implements JSON functions. The interface is +** modeled after MySQL JSON functions: +** +** https://dev.mysql.com/doc/refman/5.7/en/json.html +** +** JSON is pure text. JSONB is a binary encoding that is smaller and easier +** to parse but which holds the equivalent information. Conversions between +** JSON and JSONB are lossless. +** +** Most of the functions here will accept either JSON or JSONB input. The +** 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. +*/ +#include "sqlite3ext.h" +SQLITE_EXTENSION_INIT1 +#include +#include + +/* Unsigned integer types */ +typedef sqlite3_uint64 u64; +typedef unsigned int u32; +typedef unsigned char u8; + +/* An instance of this object represents a JSON string under construction. +*/ +typedef struct Json Json; +struct Json { + sqlite3_context *pCtx; /* Function context - put error messages here */ + char *zBuf; /* Append JSON text here */ + u64 nAlloc; /* Bytes of storage available in zBuf[] */ + u64 nUsed; /* Bytes of zBuf[] currently used */ + u8 bStatic; /* True if zBuf is static space */ + u8 mallocFailed; /* True if an OOM has been encountered */ + char zSpace[100]; /* Initial static space */ +}; + +/* Set the Json object to an empty string +*/ +static void jsonZero(Json *p){ + p->zBuf = p->zSpace; + p->nAlloc = sizeof(p->zSpace); + p->nUsed = 0; + p->bStatic = 1; +} + +/* Initialize the Json object +*/ +static void jsonInit(Json *p, sqlite3_context *pCtx){ + p->pCtx = pCtx; + p->mallocFailed = 0; + jsonZero(p); +} + + +/* Free all allocated memory and reset the Json object back to its +** initial state. +*/ +static void jsonReset(Json *p){ + if( !p->bStatic ) sqlite3_free(p->zBuf); + jsonZero(p); +} + + +/* Report an out-of-memory (OOM) condition +*/ +static void jsonOom(Json *p){ + p->mallocFailed = 1; + sqlite3_result_error_nomem(p->pCtx); + jsonReset(p); +} + +/* Enlarge pJson->zBuf so that it can hold at least N more bytes. +** Return zero on success. Return non-zero on an OOM error +*/ +static int jsonGrow(Json *p, u32 N){ + u64 nTotal = NnAlloc ? p->nAlloc*2 : p->nAlloc+N+100; + char *zNew; + if( p->bStatic ){ + if( p->mallocFailed ) return SQLITE_NOMEM; + zNew = sqlite3_malloc64(nTotal); + if( zNew==0 ){ + jsonOom(p); + return SQLITE_NOMEM; + } + memcpy(zNew, p->zBuf, p->nUsed); + p->zBuf = zNew; + p->bStatic = 0; + }else{ + zNew = sqlite3_realloc64(p->zBuf, nTotal); + if( zNew==0 ){ + jsonOom(p); + return SQLITE_NOMEM; + } + p->zBuf = zNew; + } + p->nAlloc = nTotal; + return SQLITE_OK; +} + +/* Append N bytes from zIn onto the end of the Json string. +*/ +static void jsonAppendRaw(Json *p, const char *zIn, u32 N){ + if( (N+p->nUsed >= p->nAlloc) && jsonGrow(p,N)!=0 ) return; + memcpy(p->zBuf+p->nUsed, zIn, N); + p->nUsed += N; +} + +/* Append the N-byte string in zIn to the end of the Json string +** under construction. Enclose the string in "..." and escape +** any double-quotes or backslash characters contained within the +** string. +*/ +static void jsonAppendString(Json *p, const char *zIn, u32 N){ + u32 i; + if( (N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0 ) return; + p->zBuf[p->nUsed++] = '"'; + for(i=0; inUsed+N+1-i > p->nAlloc) && jsonGrow(p,N+1-i)!=0 ) return; + p->zBuf[p->nUsed++] = '\\'; + } + p->zBuf[p->nUsed++] = c; + } + p->zBuf[p->nUsed++] = '"'; +} + +/* Make pJson the result of the SQL function. +*/ +static void jsonResult(Json *p){ + if( p->mallocFailed==0 ){ + sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, + p->bStatic ? SQLITE_TRANSIENT : sqlite3_free, + SQLITE_UTF8); + 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 +** is a BLOB, throw an error. +*/ +static void jsonArrayFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + int i; + Json jx; + char cSep = '['; + + jsonInit(&jx, context); + for(i=0; i