#define JSON_ARRAY 6
#define JSON_OBJECT 7
+/* The "subtype" set for JSON values */
+#define JSON_SUBTYPE 74 /* Ascii for "J" */
+
/*
** Names of the various JSON types:
*/
#define JNODE_REMOVE 0x04 /* Do not output */
#define JNODE_REPLACE 0x08 /* Replace with JsonNode.iVal */
#define JNODE_APPEND 0x10 /* More ARRAY/OBJECT entries at u.iAppend */
-#define JNODE_JSON 0x20 /* Treat REPLACE as JSON text */
-#define JNODE_LABEL 0x40 /* Is a label of an object */
+#define JNODE_LABEL 0x20 /* Is a label of an object */
/* A single node of parsed JSON
*/
static void jsonAppendValue(
JsonString *p, /* Append to this JSON string */
- sqlite3_value *pValue, /* Value to append */
- u8 textIsJson /* Try to treat text values as JSON */
+ sqlite3_value *pValue /* Value to append */
){
switch( sqlite3_value_type(pValue) ){
case SQLITE_NULL: {
case SQLITE_TEXT: {
const char *z = (const char*)sqlite3_value_text(pValue);
u32 n = (u32)sqlite3_value_bytes(pValue);
- if( textIsJson ){
+ if( sqlite3_value_subtype(pValue)==JSON_SUBTYPE ){
jsonAppendRaw(p, z, n);
}else{
jsonAppendString(p, z, n);
if( pNode[j].jnFlags & (JNODE_REMOVE|JNODE_REPLACE) ){
if( pNode[j].jnFlags & JNODE_REPLACE ){
jsonAppendSeparator(pOut);
- jsonAppendValue(pOut, aReplace[pNode[j].iVal],
- (pNode[j].jnFlags & JNODE_JSON)!=0);
+ jsonAppendValue(pOut, aReplace[pNode[j].iVal]);
}
}else{
jsonAppendSeparator(pOut);
jsonRenderNode(&pNode[j], pOut, aReplace);
jsonAppendChar(pOut, ':');
if( pNode[j+1].jnFlags & JNODE_REPLACE ){
- jsonAppendValue(pOut, aReplace[pNode[j+1].iVal],
- (pNode[j+1].jnFlags & JNODE_JSON)!=0);
+ jsonAppendValue(pOut, aReplace[pNode[j+1].iVal]);
}else{
jsonRenderNode(&pNode[j+1], pOut, aReplace);
}
jsonInit(&s, pCtx);
jsonRenderNode(pNode, &s, aReplace);
jsonResult(&s);
+ sqlite3_result_subtype(pCtx, JSON_SUBTYPE);
}
/*
**
** If pApnd!=NULL then try to append missing nodes and set *pApnd = 1 if
** nodes are appended.
-**
-** If the path starts with $$ then set *pFlags to JNODE_REPLACE|JNODE_JSON
-** as a single to the caller that the input text to be inserted should be
-** interpreted as JSON rather than as ordinary text.
*/
static JsonNode *jsonLookup(
JsonParse *pParse, /* The JSON to search */
const char *zPath, /* The path to search */
int *pApnd, /* Append nodes to complete path if not NULL */
- sqlite3_context *pCtx, /* Report errors here, if not NULL */
- u8 *pFlags /* Write JNODE_REPLACE or _REPLACE|_JSON here */
+ sqlite3_context *pCtx /* Report errors here, if not NULL */
){
const char *zErr = 0;
JsonNode *pNode = 0;
- u8 fg = JNODE_REPLACE;
if( zPath==0 ) return 0;
if( zPath[0]!='$' ){
goto lookup_err;
}
zPath++;
- if( zPath[0]=='$' ){
- if( pFlags==0 ){
- zErr = zPath;
- goto lookup_err;
- }
- zPath++;
- fg = JNODE_REPLACE|JNODE_JSON;
- }
- if( pFlags ) *pFlags = fg;
pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr);
return pNode;
sqlite3_result_error_nomem(pCtx);
}
}
- if( pFlags ) *pFlags = fg;
return 0;
}
}
/*
-** The json_test1(JSON) function parses and rebuilds the JSON string.
+** The json_test1(JSON) function return true (1) if the input is JSON
+** text generated by another json function. It returns (0) if the input
+** is not known to be JSON.
*/
static void jsonTest1Func(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
- JsonParse x; /* The parse */
- if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
- jsonReturnJson(x.aNode, ctx, 0);
- jsonParseReset(&x);
-}
-
-/*
-** The json_nodecount(JSON) function returns the number of nodes in the
-** input JSON string.
-*/
-static void jsonNodeCountFunc(
- sqlite3_context *ctx,
- int argc,
- sqlite3_value **argv
-){
- JsonParse x; /* The parse */
- if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
- sqlite3_result_int64(ctx, (sqlite3_int64)x.nNode);
- jsonParseReset(&x);
+ sqlite3_result_int(ctx, sqlite3_value_subtype(argv[0])==JSON_SUBTYPE);
}
#endif /* SQLITE_DEBUG */
jsonAppendChar(&jx, '[');
for(i=0; i<argc; i++){
jsonAppendSeparator(&jx);
- jsonAppendValue(&jx, argv[i], 0);
+ jsonAppendValue(&jx, argv[i]);
}
jsonAppendChar(&jx, ']');
jsonResult(&jx);
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
JsonNode *pNode;
if( argc==2 ){
const char *zPath = (const char*)sqlite3_value_text(argv[1]);
- pNode = jsonLookup(&x, zPath, 0, ctx, 0);
+ pNode = jsonLookup(&x, zPath, 0, ctx);
}else{
pNode = x.aNode;
}
jsonAppendChar(&jx, '[');
for(i=1; i<argc; i++){
zPath = (const char*)sqlite3_value_text(argv[i]);
- pNode = jsonLookup(&x, zPath, 0, ctx, 0);
+ pNode = jsonLookup(&x, zPath, 0, ctx);
if( x.nErr ) break;
if( argc>2 ){
jsonAppendSeparator(&jx);
if( argc>2 && i==argc ){
jsonAppendChar(&jx, ']');
jsonResult(&jx);
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
jsonReset(&jx);
jsonParseReset(&x);
n = (u32)sqlite3_value_bytes(argv[i]);
jsonAppendString(&jx, z, n);
jsonAppendChar(&jx, ':');
- jsonAppendValue(&jx, argv[i+1], 0);
+ jsonAppendValue(&jx, argv[i+1]);
}
jsonAppendChar(&jx, '}');
jsonResult(&jx);
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
for(i=1; i<(u32)argc; i++){
zPath = (const char*)sqlite3_value_text(argv[i]);
if( zPath==0 ) goto remove_done;
- pNode = jsonLookup(&x, zPath, 0, ctx, 0);
+ pNode = jsonLookup(&x, zPath, 0, ctx);
if( x.nErr ) goto remove_done;
if( pNode ) pNode->jnFlags |= JNODE_REMOVE;
}
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
if( x.nNode ){
for(i=1; i<(u32)argc; i+=2){
- u8 jnFlags = JNODE_REPLACE;
zPath = (const char*)sqlite3_value_text(argv[i]);
- pNode = jsonLookup(&x, zPath, 0, ctx, &jnFlags);
+ pNode = jsonLookup(&x, zPath, 0, ctx);
if( x.nErr ) goto replace_err;
if( pNode ){
- pNode->jnFlags &= ~JNODE_JSON;
- pNode->jnFlags |= jnFlags;
+ pNode->jnFlags |= JNODE_REPLACE;
pNode->iVal = i+1;
}
}
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
if( x.nNode ){
for(i=1; i<(u32)argc; i+=2){
- u8 jnFlags = JNODE_REPLACE;
zPath = (const char*)sqlite3_value_text(argv[i]);
bApnd = 0;
- pNode = jsonLookup(&x, zPath, &bApnd, ctx, &jnFlags);
+ pNode = jsonLookup(&x, zPath, &bApnd, ctx);
if( x.oom ){
sqlite3_result_error_nomem(ctx);
goto jsonSetDone;
}else if( x.nErr ){
goto jsonSetDone;
}else if( pNode && (bApnd || bIsSet) ){
- pNode->jnFlags &= ~JNODE_JSON;
- pNode->jnFlags |= jnFlags;
+ pNode->jnFlags |= JNODE_REPLACE;
pNode->iVal = i+1;
}
}
JsonNode *pNode;
if( argc==2 ){
zPath = (const char*)sqlite3_value_text(argv[1]);
- pNode = jsonLookup(&x, zPath, 0, ctx, 0);
+ pNode = jsonLookup(&x, zPath, 0, ctx);
}else{
pNode = x.aNode;
}
int flag;
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
} aFunc[] = {
+ { "json", 1, 0, jsonRemoveFunc },
{ "json_array", -1, 0, jsonArrayFunc },
{ "json_array_length", 1, 0, jsonArrayLengthFunc },
{ "json_array_length", 2, 0, jsonArrayLengthFunc },
/* DEBUG and TESTING functions */
{ "json_parse", 1, 0, jsonParseFunc },
{ "json_test1", 1, 0, jsonTest1Func },
- { "json_nodecount", 1, 0, jsonNodeCountFunc },
#endif
};
#ifndef SQLITE_OMIT_VIRTUALTABLE
source $testdir/tester.tcl
load_static_extension db json
-do_execsql_test json1-1.1 {
+do_execsql_test json1-1.1.00 {
SELECT json_array(1,2.5,null,'hello');
} {[1,2.5,null,"hello"]}
+do_execsql_test json1-1.1.01 {
+ SELECT json_array(1,'{"abc":2.5,"def":null,"ghi":hello}',99);
+ -- the second term goes in as a string:
+} {[1,"{\\"abc\\":2.5,\\"def\\":null,\\"ghi\\":hello}",99]}
+do_execsql_test json1-1.1.02 {
+ SELECT json_array(1,json('{"abc":2.5,"def":null,"ghi":"hello"}'),99);
+ -- the second term goes in as JSON
+} {[1,{"abc":2.5,"def":null,"ghi":"hello"},99]}
+do_execsql_test json1-1.1.03 {
+ SELECT json_array(1,json_object('abc',2.5,'def',null,'ghi','hello'),99);
+ -- the second term goes in as JSON
+} {[1,{"abc":2.5,"def":null,"ghi":"hello"},99]}
do_execsql_test json1-1.2 {
SELECT hex(json_array('String "\ Test'));
} {5B22537472696E67205C225C5C2054657374225D}
SELECT json_replace('{"a":1,"b":2}','$.a','[3,4,5]');
} {{{"a":"[3,4,5]","b":2}}}
do_execsql_test json1-3.2 {
- SELECT json_replace('{"a":1,"b":2}','$$.a','[3,4,5]');
+ SELECT json_replace('{"a":1,"b":2}','$.a',json('[3,4,5]'));
} {{{"a":[3,4,5],"b":2}}}
do_execsql_test json1-3.3 {
SELECT json_type(json_set('{"a":1,"b":2}','$.b','{"x":3,"y":4}'),'$.b');
} {text}
do_execsql_test json1-3.4 {
- SELECT json_type(json_set('{"a":1,"b":2}','$$.b','{"x":3,"y":4}'),'$.b');
+ SELECT json_type(json_set('{"a":1,"b":2}','$.b',json('{"x":3,"y":4}')),'$.b');
} {object}
# Per rfc7159, any JSON value is allowed at the top level, and whitespace