From: drh Date: Mon, 17 Aug 2015 15:17:37 +0000 (+0000) Subject: Initial implementation for json_array_length(), json_extract(), and X-Git-Tag: version-3.9.0~204^2~15 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=987eb1fa9b4a26ea00bf105c9725b20d27dbde51;p=thirdparty%2Fsqlite.git Initial implementation for json_array_length(), json_extract(), and json_type(). FossilOrigin-Name: 39983204515837e7bd574cf47918e493acc03d1f --- diff --git a/ext/misc/json.c b/ext/misc/json.c index 2e37835708..1b2b6ff5c2 100644 --- a/ext/misc/json.c +++ b/ext/misc/json.c @@ -24,6 +24,7 @@ SQLITE_EXTENSION_INIT1 #include #include #include +#include /* Unsigned integer types */ typedef sqlite3_uint64 u64; @@ -56,12 +57,21 @@ struct Json { #define JSON_ARRAY 6 #define JSON_OBJECT 7 +/* +** Names of the various JSON types: +*/ +static const char * const jsonType[] = { + "null", "true", "false", "integer", "real", "text", "array", "object" +}; + + /* A single node of parsed JSON */ typedef struct JsonNode JsonNode; struct JsonNode { u8 eType; /* One of the JSON_ type values */ u8 bRaw; /* Content is raw, rather than JSON encoded */ + u8 bBackslash; /* Formatted JSON_STRING contains \ escapes */ u32 n; /* Bytes of content, or number of sub-nodes */ const char *zJContent; /* JSON content */ }; @@ -268,22 +278,89 @@ static void jsonReturn(JsonNode *pNode, sqlite3_context *pCtx){ sqlite3_result_int(pCtx, 0); break; } - - /* FIXME: We really want to do text->numeric conversion on these. - ** Doing so would be easy if these were internal routines, but the - ** necessary interfaces are not exposed for doing it as a loadable - ** extension. */ - case JSON_REAL: + case JSON_REAL: { + double r = strtod(pNode->zJContent, 0); + sqlite3_result_double(pCtx, r); + break; + } case JSON_INT: { - sqlite3_result_text(pCtx, pNode->zJContent, pNode->n, SQLITE_TRANSIENT); + sqlite3_int64 i = 0; + const char *z = pNode->zJContent; + if( z[0]=='-' ){ z++; } + while( z[0]>='0' && z[0]<='9' ){ i = i*10 + *(z++) - '0'; } + if( pNode->zJContent[0]=='-' ){ i = -i; } + sqlite3_result_int64(pCtx, i); break; } - case JSON_STRING: { if( pNode->bRaw ){ sqlite3_result_text(pCtx, pNode->zJContent, pNode->n, SQLITE_TRANSIENT); + }else if( !pNode->bBackslash ){ + /* JSON formatted without any backslash-escapes */ + sqlite3_result_text(pCtx, pNode->zJContent+1, pNode->n-2, + SQLITE_TRANSIENT); }else{ /* Translate JSON formatted string into raw text */ + u32 i; + u32 n = pNode->n; + const char *z = pNode->zJContent; + char *zOut; + u32 j; + zOut = sqlite3_malloc( n+1 ); + if( zOut==0 ){ + sqlite3_result_error_nomem(pCtx); + break; + } + for(i=1, j=0; i='0' && c<='9' ) v = v*16 + c - '0'; + else if( c>='A' && c<='F' ) v = v*16 + c - 'A' + 10; + else if( c>='a' && c<='f' ) v = v*16 + c - 'a' + 10; + else break; + z++; + } + if( v<=0x7f ){ + zOut[j++] = v; + }else if( v<=0x7ff ){ + zOut[j++] = 0xc0 | (v>>6); + zOut[j++] = 0x80 | (v&0x3f); + }else if( v<=0xffff ){ + zOut[j++] = 0xe0 | (v>>12); + zOut[j++] = 0x80 | ((v>>6)&0x3f); + zOut[j++] = 0x80 | (v&0x3f); + }else if( v<=0x10ffff ){ + zOut[j++] = 0xf0 | (v>>18); + zOut[j++] = 0x80 | ((v>>12)&0x3f); + zOut[j++] = 0x80 | ((v>>6)&0x3f); + zOut[j++] = 0x80 | (v&0x3f); + } + }else{ + if( c=='b' ){ + c = '\b'; + }else if( c=='f' ){ + c = '\f'; + }else if( c=='n' ){ + c = '\n'; + }else if( c=='r' ){ + c = '\r'; + }else if( c=='t' ){ + c = '\t'; + } + zOut[j++] = c; + } + } + } + zOut[j] = 0; + sqlite3_result_text(pCtx, zOut, j, sqlite3_free); } break; } @@ -298,116 +375,6 @@ static void jsonReturn(JsonNode *pNode, sqlite3_context *pCtx){ } } -/* -** 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; iaNode[] of the @@ -440,6 +407,7 @@ static int jsonParseAddNode( p = &pParse->aNode[pParse->nNode]; p->eType = (u8)eType; p->bRaw = 0; + p->bBackslash = 0; p->n = n; p->zJContent = zContent; return pParse->nNode++; @@ -509,6 +477,7 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ return j+1; }else if( c=='"' ){ /* Parse string */ + u8 bBackslash = 0; j = i+1; for(;;){ c = pParse->zJson[j]; @@ -516,12 +485,14 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ if( c=='\\' ){ c = pParse->zJson[++j]; if( c==0 ) return -1; + bBackslash = 1; }else if( c=='"' ){ break; } j++; } jsonParseAddNode(pParse, JSON_STRING, j+1-i, &pParse->zJson[i]); + if( bBackslash ) pParse->aNode[pParse->nNode-1].bBackslash = 1; return j+1; }else if( c=='n' && strncmp(pParse->zJson+i,"null",4)==0 @@ -601,6 +572,60 @@ static int jsonParse(JsonParse *pParse, const char *zJson){ } return 0; } +/* +** Search along zPath to find the node specified. Return a pointer +** to that node, or NULL if zPath is malformed or if there is no such +** node. +*/ +static JsonNode *jsonLookup(JsonNode *pRoot, const char *zPath){ + u32 i, j; + if( zPath[0]==0 ) return pRoot; + if( zPath[0]=='.' ){ + if( pRoot->eType!=JSON_OBJECT ) return 0; + zPath++; + for(i=0; isalnum(zPath[i]); i++){} + if( i==0 ) return 0; + j = 1; + while( j<=pRoot->n ){ + if( pRoot[j].n==i+2 + && strncmp(&pRoot[j].zJContent[1],zPath,i)==0 + ){ + return jsonLookup(&pRoot[j+1], &zPath[i]); + } + j++; + if( pRoot[j].eType==JSON_ARRAY || pRoot[j].eType==JSON_OBJECT ){ + j += pRoot[j].n; + } + j++; + } + }else if( zPath[0]=='[' && isdigit(zPath[1]) ){ + if( pRoot->eType!=JSON_ARRAY ) return 0; + i = 0; + zPath++; + while( isdigit(zPath[0]) ){ + i = i + zPath[0] - '0'; + zPath++; + } + if( zPath[0]!=']' ) return 0; + zPath++; + j = 1; + while( i>0 && j<=pRoot->n ){ + if( pRoot[j].eType==JSON_ARRAY || pRoot[j].eType==JSON_OBJECT ){ + j += pRoot[j].n; + } + j++; + i--; + } + if( j<=pRoot->n ){ + return jsonLookup(&pRoot[j], zPath); + } + } + return 0; +} + +/**************************************************************************** +** SQL functions used for testing and debugging +****************************************************************************/ /* ** The json_parse(JSON) function returns a string which describes @@ -616,9 +641,6 @@ static void jsonParseFunc( JsonParse x; /* The parse */ u32 i; char zBuf[50]; - static const char *azType[] = { - "NULL", "TRUE", "FALSE", "INT", "REAL", "STRING", "ARRAY", "OBJECT" - }; assert( argc==1 ); if( jsonParse(&x, (const char*)sqlite3_value_text(argv[0])) ) return; @@ -627,7 +649,7 @@ static void jsonParseFunc( sqlite3_snprintf(sizeof(zBuf), zBuf, "node %u:\n", i); jsonAppend(&s, zBuf); sqlite3_snprintf(sizeof(zBuf), zBuf, " type: %s\n", - azType[x.aNode[i].eType]); + jsonType[x.aNode[i].eType]); jsonAppend(&s, zBuf); if( x.aNode[i].eType>=JSON_INT ){ sqlite3_snprintf(sizeof(zBuf), zBuf, " n: %u\n", x.aNode[i].n); @@ -675,6 +697,222 @@ static void jsonNodeCountFunc( sqlite3_free(x.aNode); } +/**************************************************************************** +** SQL function implementations +****************************************************************************/ + +/* +** 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; ieType==JSON_ARRAY ){ + for(i=1; i<=pNode->n; i++, n++){ + if( pNode[i].eType==JSON_ARRAY || pNode[i].eType==JSON_OBJECT ){ + i += pNode[i].n; + } + } + } + } + sqlite3_free(x.aNode); + } + sqlite3_result_int64(context, n); +} + +/* +** json_extract(JSON, PATH) +** +** Return the element described by PATH. Return NULL if JSON is not +** valid JSON or if there is no PATH element or if PATH is malformed. +*/ +static void jsonExtractFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + JsonParse x; /* The parse */ + JsonNode *pNode; + const char *zPath; + assert( argc==2 ); + zPath = (const char*)sqlite3_value_text(argv[1]); + if( zPath==0 ) return; + if( zPath[0]!='$' ) return; + zPath++; + if( jsonParse(&x, (const char*)sqlite3_value_text(argv[0])) ) return; + pNode = jsonLookup(x.aNode, zPath); + if( pNode ){ + jsonReturn(pNode, context); + } + sqlite3_free(x.aNode); +} + +/* +** Implementation of the json_object(NAME,VALUE,...) function. Return a JSON +** object that contains all name/value given in arguments. Or if any name +** is not a string or if any value is a BLOB, throw an error. +*/ +static void jsonObjectFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + int i; + Json jx; + char cSep = '{'; + const char *z; + u32 n; + + if( argc&1 ){ + sqlite3_result_error(context, "json_object() requires an even number " + "of arguments", -1); + return; + } + jsonInit(&jx, context); + for(i=0; ieType], -1, SQLITE_STATIC); + } + sqlite3_free(x.aNode); +} #ifdef _WIN32 __declspec(dllexport) @@ -691,11 +929,18 @@ int sqlite3_json_init( int nArg; void (*xFunc)(sqlite3_context*,int,sqlite3_value**); } aFunc[] = { - { "json_array", -1, jsonArrayFunc }, - { "json_object", -1, jsonObjectFunc }, - { "json_parse", 1, jsonParseFunc }, /* DEBUG */ - { "json_test1", 1, jsonTest1Func }, /* DEBUG */ - { "json_nodecount", 1, jsonNodeCountFunc }, /* DEBUG */ + { "json_array", -1, jsonArrayFunc }, + { "json_array_length", 1, jsonArrayLengthFunc }, + { "json_array_length", 2, jsonArrayLengthFunc }, + { "json_extract", 2, jsonExtractFunc }, + { "json_object", -1, jsonObjectFunc }, + { "json_type", 1, jsonTypeFunc }, + { "json_type", 2, jsonTypeFunc }, + + /* DEBUG and TESTING functions */ + { "json_parse", 1, jsonParseFunc }, + { "json_test1", 1, jsonTest1Func }, + { "json_nodecount", 1, jsonNodeCountFunc }, }; SQLITE_EXTENSION_INIT2(pApi); (void)pzErrMsg; /* Unused parameter */ diff --git a/manifest b/manifest index a89a28af9e..67b31769fb 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Abandon\sthe\sJSONB\sformat\sfor\snow.\s\s(We\smay\sreturn\sto\sit\sin\sthe\sfuture.)\s\sAdd\na\sfunction\sto\srender\sa\sJSON\sparse. -D 2015-08-17T11:28:03.070 +C Initial\simplementation\sfor\sjson_array_length(),\sjson_extract(),\sand\njson_type(). +D 2015-08-17T15:17:37.780 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 fce2fee3ac62dd53cc502cf2673b2ee5947d702d +F ext/misc/json.c f26cbaa8ba1e396b3bf1e29aa116abed2a27ef95 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 42c15c1e36b5077646fef99028cf12e587a45023 -R 4717cf337d2cdc65a44f3f6bb72a9c97 +P 9703c0aa18ae43375af876474b818e504e1c10a5 +R cfeb418e7f67bb21a131fd01c852e6d6 U drh -Z 1ecceeb77cb551a38df879c773148819 +Z 903f29a5c61013c958dfe3ed9e5ebef0 diff --git a/manifest.uuid b/manifest.uuid index 669996d2c6..be625468c7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9703c0aa18ae43375af876474b818e504e1c10a5 \ No newline at end of file +39983204515837e7bd574cf47918e493acc03d1f \ No newline at end of file