Take out the goofy '$$' path syntax. Instead, use subtypes to communicate when
a string is JSON. Add the json() function that validates and minifies the JSON and sets the appropriate subtype. FossilOrigin-Name: 8a80d6459e246ec1b38325e1cbd1e862157138b3
This commit is contained in:
+28
-61
@@ -68,6 +68,9 @@ struct JsonString {
|
||||
#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:
|
||||
*/
|
||||
@@ -82,8 +85,7 @@ static const char * const jsonType[] = {
|
||||
#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
|
||||
@@ -243,8 +245,7 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
|
||||
*/
|
||||
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: {
|
||||
@@ -261,7 +262,7 @@ static void jsonAppendValue(
|
||||
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);
|
||||
@@ -365,8 +366,7 @@ static void jsonRenderNode(
|
||||
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);
|
||||
@@ -391,8 +391,7 @@ static void jsonRenderNode(
|
||||
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);
|
||||
}
|
||||
@@ -421,6 +420,7 @@ static void jsonReturnJson(
|
||||
jsonInit(&s, pCtx);
|
||||
jsonRenderNode(pNode, &s, aReplace);
|
||||
jsonResult(&s);
|
||||
sqlite3_result_subtype(pCtx, JSON_SUBTYPE);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -951,21 +951,15 @@ static char *jsonPathSyntaxError(const char *zErr){
|
||||
**
|
||||
** 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]!='$' ){
|
||||
@@ -973,15 +967,6 @@ static JsonNode *jsonLookup(
|
||||
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;
|
||||
|
||||
@@ -996,7 +981,6 @@ lookup_err:
|
||||
sqlite3_result_error_nomem(pCtx);
|
||||
}
|
||||
}
|
||||
if( pFlags ) *pFlags = fg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1060,32 +1044,16 @@ static void jsonParseFunc(
|
||||
}
|
||||
|
||||
/*
|
||||
** 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 */
|
||||
|
||||
@@ -1110,10 +1078,11 @@ static void jsonArrayFunc(
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1138,7 +1107,7 @@ static void jsonArrayLengthFunc(
|
||||
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;
|
||||
}
|
||||
@@ -1180,7 +1149,7 @@ static void jsonExtractFunc(
|
||||
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);
|
||||
@@ -1196,6 +1165,7 @@ static void jsonExtractFunc(
|
||||
if( argc>2 && i==argc ){
|
||||
jsonAppendChar(&jx, ']');
|
||||
jsonResult(&jx);
|
||||
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
|
||||
}
|
||||
jsonReset(&jx);
|
||||
jsonParseReset(&x);
|
||||
@@ -1234,10 +1204,11 @@ static void jsonObjectFunc(
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1263,7 +1234,7 @@ static void jsonRemoveFunc(
|
||||
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;
|
||||
}
|
||||
@@ -1299,13 +1270,11 @@ static void jsonReplaceFunc(
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1351,18 +1320,16 @@ static void jsonSetFunc(
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1396,7 +1363,7 @@ static void jsonTypeFunc(
|
||||
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;
|
||||
}
|
||||
@@ -1917,6 +1884,7 @@ int sqlite3_json_init(
|
||||
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 },
|
||||
@@ -1934,7 +1902,6 @@ int sqlite3_json_init(
|
||||
/* DEBUG and TESTING functions */
|
||||
{ "json_parse", 1, 0, jsonParseFunc },
|
||||
{ "json_test1", 1, 0, jsonTest1Func },
|
||||
{ "json_nodecount", 1, 0, jsonNodeCountFunc },
|
||||
#endif
|
||||
};
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
Reference in New Issue
Block a user