Begin adding an extension that provides JSON SQL functions.
FossilOrigin-Name: dde8afdd8dba1d92560326dca7c1cdfedbe5e070
This commit is contained 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 \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
+225
@@ -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 <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
/* 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 = N<p->nAlloc ? 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; i<N; i++){
|
||||
char c = zIn[i];
|
||||
if( c=='"' || c=='\\' ){
|
||||
if( (p->nUsed+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<argc; i++){
|
||||
jsonAppendRaw(&jx, &cSep, 1);
|
||||
cSep = ',';
|
||||
switch( sqlite3_value_type(argv[i]) ){
|
||||
case SQLITE_NULL: {
|
||||
jsonAppendRaw(&jx, "null", 4);
|
||||
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]);
|
||||
jsonAppendString(&jx, z, n);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
jsonZero(&jx);
|
||||
sqlite3_result_error(context, "JSON cannot hold BLOB values", -1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
jsonAppendRaw(&jx, "]", 1);
|
||||
jsonResult(&jx);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_json_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
int i;
|
||||
static const struct {
|
||||
const char *zName;
|
||||
int nArg;
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
||||
} aFunc[] = {
|
||||
{ "json_array", -1, jsonArrayFunc },
|
||||
};
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
|
||||
rc = sqlite3_create_function(db, aFunc[i].zName, aFunc[i].nArg,
|
||||
SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
|
||||
aFunc[i].xFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -297,6 +297,7 @@ TESTSRC += \
|
||||
$(TOP)/ext/misc/fileio.c \
|
||||
$(TOP)/ext/misc/fuzzer.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 \
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
C Add\sthe\sxPhraseFirst()\sand\sxPhraseNext()\sfts5\sAPIs,\sfor\sfaster\siteration\sthrough\sa\ssingle\sphrases\sposition\slist.\sAlso\soptimize\sxInst()\sand\sxInstCount()\sa\sbit.
|
||||
D 2015-08-12T12:11:28.744
|
||||
C Begin\sadding\san\sextension\sthat\sprovides\sJSON\sSQL\sfunctions.
|
||||
D 2015-08-12T16:49:40.032
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 2fc9ca6bf5949d415801c007ed3004a4bdb7c380
|
||||
F Makefile.in 7669f34c487f5b328de6b508f374ee1e56558bb0
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
F Makefile.msc 5f7861c62c41fe8e3205ef14b90ebed28fa21f1b
|
||||
F Makefile.msc 0e9dbc907fbd3e08444b5dd4c03e1919442575b8
|
||||
F Makefile.vxworks e1b65dea203f054e71653415bd8f96dcaed47858
|
||||
F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7
|
||||
F VERSION ccfc4d1576dbfdeece0a4372a2e6a2e37d3e7975
|
||||
@@ -192,6 +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 172375d024d799aa6506b8a329128ee0b67e1a94
|
||||
F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342
|
||||
F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63
|
||||
F ext/misc/regexp.c af92cdaa5058fcec1451e49becc7ba44dba023dc
|
||||
@@ -255,7 +256,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
|
||||
F main.mk 73167b34b0e67c0be32c1da2d988a376851c9ab1
|
||||
F main.mk d79682036d2dbb4197b09bc5c6a448fd35e7fc9a
|
||||
F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
|
||||
F mkopcodeh.awk 0e7f04a8eb90f92259e47d80110e4e98d7ce337a
|
||||
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
|
||||
@@ -344,7 +345,7 @@ F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46
|
||||
F src/status.c f266ad8a2892d659b74f0f50cb6a88b6e7c12179
|
||||
F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e
|
||||
F src/tclsqlite.c d9439b6a910985b7fff43ba6756bcef00de22649
|
||||
F src/test1.c d339ae9b9baf9221c657c9628c9061d88bd831f6
|
||||
F src/test1.c 0fcf7ae97001ced91bfbfaf738e9689779d9bf61
|
||||
F src/test2.c 577961fe48961b2f2e5c8b56ee50c3f459d3359d
|
||||
F src/test3.c 64d2afdd68feac1bb5e2ffb8226c8c639f798622
|
||||
F src/test4.c d168f83cc78d02e8d35567bb5630e40dcd85ac1e
|
||||
@@ -807,6 +808,7 @@ F test/journal3.test ff8af941f9e06161d3db1b46bb9f965ff0e7f307
|
||||
F test/jrnlmode.test 7864d59cf7f6e552b9b99ba0f38acd167edc10fa
|
||||
F test/jrnlmode2.test 81610545a4e6ed239ea8fa661891893385e23a1d
|
||||
F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa
|
||||
F test/json1.test f09587c250426291faff60cacc5aae759216dbd7
|
||||
F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff
|
||||
F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63
|
||||
F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200
|
||||
@@ -1372,7 +1374,10 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P 61cb2fc6c12810863c965c74e90bc502e20cf810
|
||||
R d3b6dadb6d0d16c8d1ad296c14b96300
|
||||
U dan
|
||||
Z 2a4c5177ad3c19913de9e61965a0ba07
|
||||
P f7682435278419829a46bb4cc9b5625d46549e22
|
||||
R 400e916c0bc56219e72f7a73e2b83997
|
||||
T *branch * json
|
||||
T *sym-json *
|
||||
T -sym-trunk *
|
||||
U drh
|
||||
Z 44e10dc14fd52cdaf44120cd6aeb4786
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
f7682435278419829a46bb4cc9b5625d46549e22
|
||||
dde8afdd8dba1d92560326dca7c1cdfedbe5e070
|
||||
@@ -6377,6 +6377,7 @@ static int tclLoadStaticExtensionCmd(
|
||||
extern int sqlite3_fileio_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_fuzzer_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_ieee_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_json_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_nextchar_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_percentile_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_regexp_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
@@ -6397,6 +6398,7 @@ static int tclLoadStaticExtensionCmd(
|
||||
{ "fileio", sqlite3_fileio_init },
|
||||
{ "fuzzer", sqlite3_fuzzer_init },
|
||||
{ "ieee754", sqlite3_ieee_init },
|
||||
{ "json", sqlite3_json_init },
|
||||
{ "nextchar", sqlite3_nextchar_init },
|
||||
{ "percentile", sqlite3_percentile_init },
|
||||
{ "regexp", sqlite3_regexp_init },
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# 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 file implements tests for JSON SQL functions extension to the
|
||||
# SQLite library.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
load_static_extension db json
|
||||
do_execsql_test json1-1.1 {
|
||||
SELECT json_array(1,2.5,null,'hello');
|
||||
} {[1,2.5,null,"hello"]}
|
||||
do_execsql_test json1-1.2 {
|
||||
SELECT hex(json_array('String "\ Test'));
|
||||
} {5B22537472696E67205C225C5C2054657374225D}
|
||||
do_catchsql_test json1-1.3 {
|
||||
SELECT json_array(1,2,x'abcd',3);
|
||||
} {1 {JSON cannot hold BLOB values}}
|
||||
do_execsql_test json1-1.4 {
|
||||
SELECT json_array(-9223372036854775808,9223372036854775807,0,1,-1,
|
||||
0.0, 1.0, -1.0, -1e99, +2e100,
|
||||
'one','two','three',
|
||||
4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
|
||||
19, NULL, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
'abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||
'abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||
'abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||
99);
|
||||
} {[-9223372036854775808,9223372036854775807,0,1,-1,0.0,1.0,-1.0,-1.0e+99,2.0e+100,"one","two","three",4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,null,21,22,23,24,25,26,27,28,29,30,31,"abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ",99]}
|
||||
|
||||
finish_test
|
||||
Reference in New Issue
Block a user