} /* End switch(z[i]) */
}
+/* Mark node i of pParse as being a child of iParent. Call recursively
+** to fill in all the descendants of node i.
+*/
+static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){
+ JsonNode *pNode = &pParse->aNode[i];
+ u32 j;
+ pParse->aUp[i] = iParent;
+ switch( pNode->eType ){
+ case JSON_ARRAY: {
+ for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j)){
+ jsonParseFillInParentage(pParse, i+j, i);
+ }
+ break;
+ }
+ case JSON_OBJECT: {
+ for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j+1)+1){
+ pParse->aUp[i+j] = i;
+ jsonParseFillInParentage(pParse, i+j+1, i);
+ }
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
/*
** Parse a complete JSON string. Return 0 on success or non-zero if there
** are any errors. If an error occurs, free all memory held by pParse,
}
/*
-** Expand pParse->aBlob so that it holds at least N bytes.
-**
-** Return the number of errors.
+** Compute the parentage of all nodes in a completed parse.
*/
-static int jsonBlobExpand(JsonParse *pParse, u32 N){
- u8 *aNew;
- u32 t;
- if( N<=pParse->nBlobAlloc ) return 0;
- if( pParse->nBlobAlloc==0 ){
- t = 100;
- }else{
- t = pParse->nBlobAlloc*2;
+static int jsonParseFindParents(JsonParse *pParse){
+ u32 *aUp;
+ assert( pParse->aUp==0 );
+ aUp = pParse->aUp = sqlite3_malloc64( sizeof(u32)*pParse->nNode );
+ if( aUp==0 ){
+ pParse->oom = 1;
+ return SQLITE_NOMEM;
}
- if( t<N ) t = N+100;
- aNew = sqlite3_realloc64( pParse->aBlob, t );
- if( aNew==0 ){ pParse->oom = 1; return 1; }
- pParse->aBlob = aNew;
- pParse->nBlobAlloc = t;
- return 0;
+ jsonParseFillInParentage(pParse, 0, 0);
+ return SQLITE_OK;
}
-/* Expand pParse->aBlob and append N bytes.
+/*
+** Magic number used for the JSON parse cache in sqlite3_get_auxdata()
+*/
+#define JSON_CACHE_ID (-429938) /* First cache entry */
+#define JSON_CACHE_SZ 4 /* Max number of cache entries */
+
+/*
+** Obtain a complete parse of the JSON found in the pJson argument
**
-** Return the number of errors.
+** Use the sqlite3_get_auxdata() cache to find a preexisting parse
+** if it is available. If the cache is not available or if it
+** is no longer valid, parse the JSON again and return the new parse.
+** Also register the new parse so that it will be available for
+** future sqlite3_get_auxdata() calls.
+**
+** If an error occurs and pErrCtx!=0 then report the error on pErrCtx
+** and return NULL.
+**
+** The returned pointer (if it is not NULL) is owned by the cache in
+** most cases, not the caller. The caller does NOT need to invoke
+** jsonParseFree(), in most cases.
+**
+** Except, if an error occurs and pErrCtx==0 then return the JsonParse
+** object with JsonParse.nErr non-zero and the caller will own the JsonParse
+** object. In that case, it will be the responsibility of the caller to
+** invoke jsonParseFree(). To summarize:
+**
+** pErrCtx!=0 || p->nErr==0 ==> Return value p is owned by the
+** cache. Call does not need to
+** free it.
+**
+** pErrCtx==0 && p->nErr!=0 ==> Return value is owned by the caller
+** and so the caller must free it.
*/
-static SQLITE_NOINLINE int jsonBlobExpandAndAppend(
- JsonParse *pParse,
- const u8 *aData,
- u32 N
+static JsonParse *jsonParseCached(
+ sqlite3_context *pCtx, /* Context to use for cache search */
+ sqlite3_value *pJson, /* Function param containing JSON text */
+ sqlite3_context *pErrCtx, /* Write parse errors here if not NULL */
+ int bUnedited /* No prior edits allowed */
){
- if( jsonBlobExpand(pParse, pParse->nBlob+N) ) return 1;
- memcpy(&pParse->aBlob[pParse->nBlob], aData, N);
- pParse->nBlob += N;
- return 0;
-}
+ char *zJson = (char*)sqlite3_value_text(pJson);
+ int nJson = sqlite3_value_bytes(pJson);
+ JsonParse *p;
+ JsonParse *pMatch = 0;
+ int iKey;
+ int iMinKey = 0;
+ u32 iMinHold = 0xffffffff;
+ u32 iMaxHold = 0;
+ int bJsonRCStr;
-/* Append a single character. Return 1 if an error occurs.
-*/
-static int jsonBlobAppendOneByte(JsonParse *pParse, u8 c){
- if( pParse->nBlob >= pParse->nBlobAlloc ){
- return jsonBlobExpandAndAppend(pParse, &c, 1);
+ if( zJson==0 ) return 0;
+ for(iKey=0; iKey<JSON_CACHE_SZ; iKey++){
+ p = (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iKey);
+ if( p==0 ){
+ iMinKey = iKey;
+ break;
+ }
+ if( pMatch==0
+ && p->nJson==nJson
+ && (p->hasMod==0 || bUnedited==0)
+ && (p->zJson==zJson || memcmp(p->zJson,zJson,nJson)==0)
+ ){
+ p->nErr = 0;
+ p->useMod = 0;
+ pMatch = p;
+ }else
+ if( pMatch==0
+ && p->zAlt!=0
+ && bUnedited==0
+ && p->nAlt==nJson
+ && memcmp(p->zAlt, zJson, nJson)==0
+ ){
+ p->nErr = 0;
+ p->useMod = 1;
+ pMatch = p;
+ }else if( p->iHold<iMinHold ){
+ iMinHold = p->iHold;
+ iMinKey = iKey;
+ }
+ if( p->iHold>iMaxHold ){
+ iMaxHold = p->iHold;
+ }
+ }
+ if( pMatch ){
+ /* The input JSON text was found in the cache. Use the preexisting
+ ** parse of this JSON */
+ pMatch->nErr = 0;
+ pMatch->iHold = iMaxHold+1;
+ assert( pMatch->nJPRef>0 ); /* pMatch is owned by the cache */
+ return pMatch;
}
- pParse->aBlob[pParse->nBlob++] = c;
- return 0;
-}
-/* Append bytes. Return 1 if an error occurs.
-*/
-static int jsonBlobAppendNBytes(JsonParse *pParse, const u8 *aData, u32 N){
- if( pParse->nBlob+N > pParse->nBlobAlloc ){
- return jsonBlobExpandAndAppend(pParse, aData, N);
+ /* The input JSON was not found anywhere in the cache. We will need
+ ** to parse it ourselves and generate a new JsonParse object.
+ */
+ bJsonRCStr = sqlite3ValueIsOfClass(pJson,(void(*)(void*))sqlite3RCStrUnref);
+ p = sqlite3_malloc64( sizeof(*p) + (bJsonRCStr ? 0 : nJson+1) );
+ if( p==0 ){
+ sqlite3_result_error_nomem(pCtx);
+ return 0;
}
- memcpy(&pParse->aBlob[pParse->nBlob], aData, N);
- pParse->nBlob += N;
- return 0;
+ memset(p, 0, sizeof(*p));
+ if( bJsonRCStr ){
+ p->zJson = sqlite3RCStrRef(zJson);
+ p->bJsonIsRCStr = 1;
+ }else{
+ p->zJson = (char*)&p[1];
+ memcpy(p->zJson, zJson, nJson+1);
+ }
+ p->nJPRef = 1;
+ if( jsonParse(p, pErrCtx) ){
+ if( pErrCtx==0 ){
+ p->nErr = 1;
+ assert( p->nJPRef==1 ); /* Caller will own the new JsonParse object p */
+ return p;
+ }
+ jsonParseFree(p);
+ return 0;
+ }
+ p->nJson = nJson;
+ p->iHold = iMaxHold+1;
+ /* Transfer ownership of the new JsonParse to the cache */
+ sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p,
+ (void(*)(void*))jsonParseFree);
+ return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey);
}
-/* Append a u32. Return 1 if an error occurs.
+/*
+** Compare the OBJECT label at pNode against zKey,nKey. Return true on
+** a match.
*/
-static int jsonBlobAppendU32(JsonParse *pParse, u32 x){
- u8 a[4];
- a[3] = x & 0xff;
- x >>= 8;
- a[2] = x & 0xff;
- x >>= 8;
- a[1] = x & 0xff;
- x >>= 8;
- a[0] = x & 0xff;
- return jsonBlobAppendNBytes(pParse, a, 4);
+static int jsonLabelCompare(const JsonNode *pNode, const char *zKey, u32 nKey){
+ assert( pNode->eU==1 );
+ if( pNode->jnFlags & JNODE_RAW ){
+ if( pNode->n!=nKey ) return 0;
+ return strncmp(pNode->u.zJContent, zKey, nKey)==0;
+ }else{
+ if( pNode->n!=nKey+2 ) return 0;
+ return strncmp(pNode->u.zJContent+1, zKey, nKey)==0;
+ }
}
-
-/* Write a u32 and offset i.
-*/
-static int jsonBlobWriteU32(JsonParse *pParse, u32 x, u32 i){
- u8 *a = &pParse->aBlob[i];
- a[3] = x & 0xff;
- x >>= 8;
- a[2] = x & 0xff;
- x >>= 8;
- a[1] = x & 0xff;
- x >>= 8;
- a[0] = x & 0xff;
- return 0;
+static int jsonSameLabel(const JsonNode *p1, const JsonNode *p2){
+ if( p1->jnFlags & JNODE_RAW ){
+ return jsonLabelCompare(p2, p1->u.zJContent, p1->n);
+ }else if( p2->jnFlags & JNODE_RAW ){
+ return jsonLabelCompare(p1, p2->u.zJContent, p2->n);
+ }else{
+ return p1->n==p2->n && strncmp(p1->u.zJContent,p2->u.zJContent,p1->n)==0;
+ }
}
-/* Append a VarInt. Return 1 if an error occurs.
+/* forward declaration */
+static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**);
+
+/*
+** 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.
+**
+** If pApnd!=0, then try to append new nodes to complete zPath if it is
+** possible to do so and if no existing node corresponds to zPath. If
+** new nodes are appended *pApnd is set to 1.
*/
-static int jsonBlobAppendVarint(JsonParse *pParse, u32 x){
- u8 a[5];
- if( x<=0x7f ) return jsonBlobAppendOneByte(pParse, x & 0x7f);
- a[0] = 0x80 | (x & 0x7f);
- x >>= 7;
- a[1] = x & 0x7f;
- x >>= 7;
- if( x==0 ) return jsonBlobAppendNBytes(pParse, a, 2);
- a[1] |= 0x80;
- a[2] = x & 0x7f;
- x >>= 7;
- if( x==0 ) return jsonBlobAppendNBytes(pParse, a, 3);
- a[2] |= 0x80;
- a[3] = x & 0x7f;
- x >>= 7;
- if( x==0 ) return jsonBlobAppendNBytes(pParse, a, 4);
- a[3] |= 0x80;
- a[4] = x & 0x7f;
- return jsonBlobAppendNBytes(pParse, a, 5);
-}
-
-/*
-** Parse a single JSON text value which begins at pParse->zJson[i] into
-** its equivalent BLOB representation in pParse->aBlob[]. The parse is
-** appended to pParse->aBlob[] beginning at pParse->nBlob. The size of
-** pParse->aBlob[] is increased as necessary.
-**
-** Return the index of the first character past the end of the value parsed,
-** or one of the following special result codes:
-**
-** 0 End of input
-** -1 Syntax error
-** -2 '}' seen
-** -3 ']' seen
-** -4 ',' seen
-** -5 ':' seen
-*/
-static int jsonParseValueB(JsonParse *pParse, u32 i){
- char c;
- u32 j;
- int iThis;
- int x;
- u8 t;
- const char *z = pParse->zJson;
-json_parse_restart:
- switch( (u8)z[i] ){
- case '{': {
- /* Parse object */
- jsonBlobAppendOneByte(pParse, JSONB_OBJECT);
- iThis = pParse->nBlob;
- jsonBlobAppendU32(pParse, 0);
- if( ++pParse->iDepth > JSON_MAX_DEPTH ){
- pParse->iErr = i;
- return -1;
- }
- for(j=i+1;;j++){
- u32 iBlob = pParse->nBlob;
- x = jsonParseValueB(pParse, j);
- if( x<=0 ){
- if( x==(-2) ){
- j = pParse->iErr;
- if( pParse->nBlob!=(u32)iThis+1 ) pParse->hasNonstd = 1;
+static JsonNode *jsonLookupStep(
+ JsonParse *pParse, /* The JSON to search */
+ u32 iRoot, /* Begin the search at this node */
+ const char *zPath, /* The path to search */
+ int *pApnd, /* Append nodes to complete path if not NULL */
+ const char **pzErr /* Make *pzErr point to any syntax error in zPath */
+){
+ u32 i, j, nKey;
+ const char *zKey;
+ JsonNode *pRoot;
+ if( pParse->oom ) return 0;
+ pRoot = &pParse->aNode[iRoot];
+ if( pRoot->jnFlags & (JNODE_REPLACE|JNODE_REMOVE) && pParse->useMod ){
+ while( (pRoot->jnFlags & JNODE_REPLACE)!=0 ){
+ u32 idx = (u32)(pRoot - pParse->aNode);
+ i = pParse->iSubst;
+ while( 1 /*exit-by-break*/ ){
+ assert( i<pParse->nNode );
+ assert( pParse->aNode[i].eType==JSON_SUBST );
+ assert( pParse->aNode[i].eU==4 );
+ assert( pParse->aNode[i].u.iPrev<i );
+ if( pParse->aNode[i].n==idx ){
+ pRoot = &pParse->aNode[i+1];
+ iRoot = i+1;
break;
}
- j += json5Whitespace(&z[j]);
- if( sqlite3JsonId1(z[j])
- || (z[j]=='\\' && z[j+1]=='u' && jsonIs4Hex(&z[j+2]))
- ){
- int k = j+1;
- while( (sqlite3JsonId2(z[k]) && json5Whitespace(&z[k])==0)
- || (z[k]=='\\' && z[k+1]=='u' && jsonIs4Hex(&z[k+2]))
- ){
- k++;
- }
- assert( iBlob==pParse->nBlob );
- jsonBlobAppendOneByte(pParse, JSONB_TEXT5);
- jsonBlobAppendVarint(pParse, k-j);
- jsonBlobAppendNBytes(pParse, (const u8*)&z[j], k-j);
- pParse->hasNonstd = 1;
- x = k;
- }else{
- if( x!=-1 ) pParse->iErr = j;
- return -1;
- }
- }
- if( pParse->oom ) return -1;
- t = pParse->aBlob[iBlob];
- if( t<JSONB_TEXT || t>JSONB_TEXT5 ){
- pParse->iErr = j;
- return -1;
+ i = pParse->aNode[i].u.iPrev;
}
- j = x;
- if( z[j]==':' ){
- j++;
+ }
+ if( pRoot->jnFlags & JNODE_REMOVE ){
+ return 0;
+ }
+ }
+ if( zPath[0]==0 ) return pRoot;
+ if( zPath[0]=='.' ){
+ if( pRoot->eType!=JSON_OBJECT ) return 0;
+ zPath++;
+ if( zPath[0]=='"' ){
+ zKey = zPath + 1;
+ for(i=1; zPath[i] && zPath[i]!='"'; i++){}
+ nKey = i-1;
+ if( zPath[i] ){
+ i++;
}else{
- if( fast_isspace(z[j]) ){
- do{ j++; }while( fast_isspace(z[j]) );
- if( z[j]==':' ){
- j++;
- goto parse_object_value;
- }
- }
- x = jsonParseValueB(pParse, j);
- if( x!=(-5) ){
- if( x!=(-1) ) pParse->iErr = j;
- return -1;
+ *pzErr = zPath;
+ return 0;
+ }
+ testcase( nKey==0 );
+ }else{
+ zKey = zPath;
+ for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){}
+ nKey = i;
+ if( nKey==0 ){
+ *pzErr = zPath;
+ return 0;
+ }
+ }
+ j = 1;
+ for(;;){
+ while( j<=pRoot->n ){
+ if( jsonLabelCompare(pRoot+j, zKey, nKey) ){
+ return jsonLookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr);
}
- j = pParse->iErr+1;
+ j++;
+ j += jsonNodeSize(&pRoot[j]);
}
- parse_object_value:
- x = jsonParseValueB(pParse, j);
- if( x<=0 ){
- if( x!=(-1) ) pParse->iErr = j;
- return -1;
+ if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
+ if( pParse->useMod==0 ) break;
+ assert( pRoot->eU==2 );
+ iRoot = pRoot->u.iAppend;
+ pRoot = &pParse->aNode[iRoot];
+ j = 1;
+ }
+ if( pApnd ){
+ u32 iStart, iLabel;
+ JsonNode *pNode;
+ assert( pParse->useMod );
+ iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0);
+ iLabel = jsonParseAddNode(pParse, JSON_STRING, nKey, zKey);
+ zPath += i;
+ pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
+ if( pParse->oom ) return 0;
+ if( pNode ){
+ pRoot = &pParse->aNode[iRoot];
+ assert( pRoot->eU==0 );
+ pRoot->u.iAppend = iStart;
+ pRoot->jnFlags |= JNODE_APPEND;
+ VVA( pRoot->eU = 2 );
+ pParse->aNode[iLabel].jnFlags |= JNODE_RAW;
}
- j = x;
- if( z[j]==',' ){
- continue;
- }else if( z[j]=='}' ){
- break;
- }else{
- if( fast_isspace(z[j]) ){
- do{ j++; }while( fast_isspace(z[j]) );
- if( z[j]==',' ){
- continue;
- }else if( z[j]=='}' ){
- break;
+ return pNode;
+ }
+ }else if( zPath[0]=='[' ){
+ i = 0;
+ j = 1;
+ while( sqlite3Isdigit(zPath[j]) ){
+ i = i*10 + zPath[j] - '0';
+ j++;
+ }
+ if( j<2 || zPath[j]!=']' ){
+ if( zPath[1]=='#' ){
+ JsonNode *pBase = pRoot;
+ int iBase = iRoot;
+ if( pRoot->eType!=JSON_ARRAY ) return 0;
+ for(;;){
+ while( j<=pBase->n ){
+ if( (pBase[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i++;
+ j += jsonNodeSize(&pBase[j]);
}
+ if( (pBase->jnFlags & JNODE_APPEND)==0 ) break;
+ if( pParse->useMod==0 ) break;
+ assert( pBase->eU==2 );
+ iBase = pBase->u.iAppend;
+ pBase = &pParse->aNode[iBase];
+ j = 1;
}
- x = jsonParseValueB(pParse, j);
- if( x==(-4) ){
- j = pParse->iErr;
- continue;
+ j = 2;
+ if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){
+ unsigned int x = 0;
+ j = 3;
+ do{
+ x = x*10 + zPath[j] - '0';
+ j++;
+ }while( sqlite3Isdigit(zPath[j]) );
+ if( x>i ) return 0;
+ i -= x;
}
- if( x==(-2) ){
- j = pParse->iErr;
- break;
+ if( zPath[j]!=']' ){
+ *pzErr = zPath;
+ return 0;
}
+ }else{
+ *pzErr = zPath;
+ return 0;
}
- pParse->iErr = j;
- return -1;
}
- jsonBlobWriteU32(pParse, pParse->nBlob - iThis, iThis);
- pParse->iDepth--;
- return j+1;
- }
- case '[': {
- /* Parse array */
- jsonBlobAppendOneByte(pParse, JSONB_ARRAY);
- iThis = pParse->nBlob;
- jsonBlobAppendU32(pParse, 0);
- if( pParse->oom ) return -1;
- if( ++pParse->iDepth > JSON_MAX_DEPTH ){
- pParse->iErr = i;
- return -1;
- }
- for(j=i+1;;j++){
- x = jsonParseValueB(pParse, j);
- if( x<=0 ){
- if( x==(-3) ){
- j = pParse->iErr;
- if( pParse->nBlob!=iThis+4 ) pParse->hasNonstd = 1;
- break;
- }
- if( x!=(-1) ) pParse->iErr = j;
- return -1;
- }
- j = x;
- if( z[j]==',' ){
- continue;
- }else if( z[j]==']' ){
- break;
- }else{
- if( fast_isspace(z[j]) ){
- do{ j++; }while( fast_isspace(z[j]) );
- if( z[j]==',' ){
- continue;
- }else if( z[j]==']' ){
- break;
- }
- }
- x = jsonParseValueB(pParse, j);
- if( x==(-4) ){
- j = pParse->iErr;
- continue;
- }
- if( x==(-3) ){
- j = pParse->iErr;
- break;
- }
+ if( pRoot->eType!=JSON_ARRAY ) return 0;
+ zPath += j + 1;
+ j = 1;
+ for(;;){
+ while( j<=pRoot->n
+ && (i>0 || ((pRoot[j].jnFlags & JNODE_REMOVE)!=0 && pParse->useMod))
+ ){
+ if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i--;
+ j += jsonNodeSize(&pRoot[j]);
}
- pParse->iErr = j;
- return -1;
+ if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
+ if( pParse->useMod==0 ) break;
+ assert( pRoot->eU==2 );
+ iRoot = pRoot->u.iAppend;
+ pRoot = &pParse->aNode[iRoot];
+ j = 1;
}
- jsonBlobWriteU32(pParse, pParse->nBlob - iThis, iThis);
- pParse->iDepth--;
- return j+1;
- }
- case '\'': {
- u8 opcode;
- char cDelim;
- pParse->hasNonstd = 1;
- opcode = JNODE_JSON5;
- goto parse_string;
- case '"':
- /* Parse string */
- opcode = JSONB_TEXT;
- parse_string:
- cDelim = z[i];
- for(j=i+1; 1; j++){
- if( jsonIsOk[(unsigned char)z[j]] ) continue;
- c = z[j];
- if( c==cDelim ){
- break;
- }else if( c=='\\' ){
- c = z[++j];
- if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
- || c=='n' || c=='r' || c=='t'
- || (c=='u' && jsonIs4Hex(&z[j+1])) ){
- if( opcode==JSONB_TEXT ) opcode = JSONB_TEXTJ;
- }else if( c=='\'' || c=='0' || c=='v' || c=='\n'
- || (0xe2==(u8)c && 0x80==(u8)z[j+1]
- && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2]))
- || (c=='x' && jsonIs2Hex(&z[j+1])) ){
- opcode = JSONB_TEXT5;
- pParse->hasNonstd = 1;
- }else if( c=='\r' ){
- if( z[j+1]=='\n' ) j++;
- opcode = JSONB_TEXT5;
- pParse->hasNonstd = 1;
- }else{
- pParse->iErr = j;
- return -1;
- }
- }else if( c<=0x1f ){
- /* Control characters are not allowed in strings */
- pParse->iErr = j;
- return -1;
+ if( j<=pRoot->n ){
+ return jsonLookupStep(pParse, iRoot+j, zPath, pApnd, pzErr);
+ }
+ if( i==0 && pApnd ){
+ u32 iStart;
+ JsonNode *pNode;
+ assert( pParse->useMod );
+ iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0);
+ pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
+ if( pParse->oom ) return 0;
+ if( pNode ){
+ pRoot = &pParse->aNode[iRoot];
+ assert( pRoot->eU==0 );
+ pRoot->u.iAppend = iStart;
+ pRoot->jnFlags |= JNODE_APPEND;
+ VVA( pRoot->eU = 2 );
}
+ return pNode;
}
- jsonBlobAppendOneByte(pParse, opcode);
- jsonBlobAppendVarint(pParse, j+1-i);
- jsonBlobAppendNBytes(pParse, (const u8*)&z[i], j+1-i);
- return j+1;
+ }else{
+ *pzErr = zPath;
}
- case 't': {
- if( strncmp(z+i,"true",4)==0 && !sqlite3Isalnum(z[i+4]) ){
- jsonBlobAppendOneByte(pParse, JSONB_TRUE);
- return i+4;
- }
- pParse->iErr = i;
- return -1;
+ return 0;
+}
+
+/*
+** Append content to pParse that will complete zPath. Return a pointer
+** to the inserted node, or return NULL if the append fails.
+*/
+static JsonNode *jsonLookupAppend(
+ JsonParse *pParse, /* Append content to the JSON parse */
+ const char *zPath, /* Description of content to append */
+ int *pApnd, /* Set this flag to 1 */
+ const char **pzErr /* Make this point to any syntax error */
+){
+ *pApnd = 1;
+ if( zPath[0]==0 ){
+ jsonParseAddNode(pParse, JSON_NULL, 0, 0);
+ return pParse->oom ? 0 : &pParse->aNode[pParse->nNode-1];
}
- case 'f': {
- if( strncmp(z+i,"false",5)==0 && !sqlite3Isalnum(z[i+5]) ){
- jsonBlobAppendOneByte(pParse, JSONB_FALSE);
- return i+5;
- }
- pParse->iErr = i;
- return -1;
+ if( zPath[0]=='.' ){
+ jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
+ }else if( strncmp(zPath,"[0]",3)==0 ){
+ jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
+ }else{
+ return 0;
}
- case '+': {
- u8 seenDP, seenE, jnFlags;
- pParse->hasNonstd = 1;
- jnFlags = 0;
- goto parse_number;
- case '.':
- if( sqlite3Isdigit(z[i+1]) ){
- pParse->hasNonstd = 1;
- jnFlags = JNODE_JSON5;
- seenE = 0;
- seenDP = JSON_REAL;
- goto parse_number_2;
- }
- pParse->iErr = i;
- return -1;
- case '-':
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- /* Parse number */
- jnFlags = 0;
- parse_number:
- seenDP = JSON_INT;
- seenE = 0;
- assert( '-' < '0' );
- assert( '+' < '0' );
- assert( '.' < '0' );
- c = z[i];
+ if( pParse->oom ) return 0;
+ return jsonLookupStep(pParse, pParse->nNode-1, zPath, pApnd, pzErr);
+}
- if( c<='0' ){
- if( c=='0' ){
- if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){
- assert( seenDP==JSON_INT );
- pParse->hasNonstd = 1;
- jnFlags |= JNODE_JSON5;
- for(j=i+3; sqlite3Isxdigit(z[j]); j++){}
- goto parse_number_finish;
- }else if( sqlite3Isdigit(z[i+1]) ){
- pParse->iErr = i+1;
- return -1;
- }
- }else{
- if( !sqlite3Isdigit(z[i+1]) ){
- /* JSON5 allows for "+Infinity" and "-Infinity" using exactly
- ** that case. SQLite also allows these in any case and it allows
- ** "+inf" and "-inf". */
- if( (z[i+1]=='I' || z[i+1]=='i')
- && sqlite3StrNICmp(&z[i+1], "inf",3)==0
- ){
- pParse->hasNonstd = 1;
- jsonBlobAppendOneByte(pParse, JSONB_NUMBER5);
- if( z[i]=='-' ){
- jsonBlobAppendVarint(pParse, 8);
- jsonBlobAppendNBytes(pParse, (const u8*)"-9.0e999", 8);
- }else{
- jsonBlobAppendVarint(pParse, 7);
- jsonBlobAppendNBytes(pParse, (const u8*)"9.0e999", 7);
- }
- return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4);
- }
- if( z[i+1]=='.' ){
- pParse->hasNonstd = 1;
- jnFlags |= JNODE_JSON5;
- goto parse_number_2;
- }
- pParse->iErr = i;
- return -1;
- }
- if( z[i+1]=='0' ){
- if( sqlite3Isdigit(z[i+2]) ){
- pParse->iErr = i+1;
- return -1;
- }else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){
- pParse->hasNonstd = 1;
- jnFlags |= JNODE_JSON5;
- for(j=i+4; sqlite3Isxdigit(z[j]); j++){}
- goto parse_number_finish;
- }
- }
- }
- }
- parse_number_2:
- for(j=i+1;; j++){
- c = z[j];
- if( sqlite3Isdigit(c) ) continue;
- if( c=='.' ){
- if( seenDP==JSON_REAL ){
- pParse->iErr = j;
- return -1;
- }
- seenDP = JSON_REAL;
- continue;
- }
- if( c=='e' || c=='E' ){
- if( z[j-1]<'0' ){
- if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){
- pParse->hasNonstd = 1;
- jnFlags |= JNODE_JSON5;
- }else{
- pParse->iErr = j;
- return -1;
- }
- }
- if( seenE ){
- pParse->iErr = j;
- return -1;
- }
- seenDP = JSON_REAL;
- seenE = 1;
- c = z[j+1];
- if( c=='+' || c=='-' ){
- j++;
- c = z[j+1];
- }
- if( c<'0' || c>'9' ){
- pParse->iErr = j;
- return -1;
- }
- continue;
- }
- break;
- }
- if( z[j-1]<'0' ){
- if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){
- pParse->hasNonstd = 1;
- jnFlags |= JNODE_JSON5;
- }else{
- pParse->iErr = j;
- return -1;
- }
- }
- parse_number_finish:
- if( jnFlags & JNODE_JSON5 ){
- jsonBlobAppendOneByte(pParse, JSONB_NUMBER5);
- }else{
- jsonBlobAppendOneByte(pParse, JSONB_NUMBER);
- }
- jsonBlobAppendVarint(pParse, j-i);
- jsonBlobAppendNBytes(pParse, (const u8*)&z[i], j-i);
- return j;
- }
- case '}': {
- pParse->iErr = i;
- return -2; /* End of {...} */
- }
- case ']': {
- pParse->iErr = i;
- return -3; /* End of [...] */
- }
- case ',': {
- pParse->iErr = i;
- return -4; /* List separator */
- }
- case ':': {
- pParse->iErr = i;
- return -5; /* Object label/value separator */
- }
- case 0: {
- return 0; /* End of file */
- }
- case 0x09:
- case 0x0a:
- case 0x0d:
- case 0x20: {
- do{
- i++;
- }while( fast_isspace(z[i]) );
- goto json_parse_restart;
- }
- case 0x0b:
- case 0x0c:
- case '/':
- case 0xc2:
- case 0xe1:
- case 0xe2:
- case 0xe3:
- case 0xef: {
- j = json5Whitespace(&z[i]);
- if( j>0 ){
- i += j;
- pParse->hasNonstd = 1;
- goto json_parse_restart;
- }
- pParse->iErr = i;
- return -1;
- }
- case 'n': {
- if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){
- jsonBlobAppendOneByte(pParse, JSONB_NULL);
- return i+4;
- }
- /* fall-through into the default case that checks for NaN */
- }
- default: {
- u32 k;
- int nn;
- c = z[i];
- for(k=0; k<sizeof(aNanInfName)/sizeof(aNanInfName[0]); k++){
- if( c!=aNanInfName[k].c1 && c!=aNanInfName[k].c2 ) continue;
- nn = aNanInfName[k].n;
- if( sqlite3StrNICmp(&z[i], aNanInfName[k].zMatch, nn)!=0 ){
- continue;
- }
- if( sqlite3Isalnum(z[i+nn]) ) continue;
- if( aNanInfName[k].eType==JSON_REAL ){
- jsonBlobAppendOneByte(pParse, JSONB_NUMBER);
- jsonBlobAppendOneByte(pParse, 7);
- jsonBlobAppendNBytes(pParse, (const u8*)"9.0e999", 7);
- }else{
- jsonBlobAppendOneByte(pParse, JSONB_NULL);
- }
- pParse->hasNonstd = 1;
- return i + nn;
- }
- pParse->iErr = i;
- return -1; /* Syntax error */
- }
- } /* End switch(z[i]) */
-}
-
-
-/* Mark node i of pParse as being a child of iParent. Call recursively
-** to fill in all the descendants of node i.
+/*
+** Return the text of a syntax error message on a JSON path. Space is
+** obtained from sqlite3_malloc().
*/
-static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){
- JsonNode *pNode = &pParse->aNode[i];
- u32 j;
- pParse->aUp[i] = iParent;
- switch( pNode->eType ){
- case JSON_ARRAY: {
- for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j)){
- jsonParseFillInParentage(pParse, i+j, i);
- }
- break;
- }
- case JSON_OBJECT: {
- for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j+1)+1){
- pParse->aUp[i+j] = i;
- jsonParseFillInParentage(pParse, i+j+1, i);
- }
- break;
- }
- default: {
- break;
- }
- }
+static char *jsonPathSyntaxError(const char *zErr){
+ return sqlite3_mprintf("JSON path error near '%q'", zErr);
}
/*
-** Parse a complete JSON string. Return 0 on success or non-zero if there
-** are any errors. If an error occurs, free all memory held by pParse,
-** but not pParse itself.
+** Do a node lookup using zPath. Return a pointer to the node on success.
+** Return NULL if not found or if there is an error.
**
-** pParse must be initialized to an empty parse object prior to calling
-** this routine.
+** On an error, write an error message into pCtx and increment the
+** pParse->nErr counter.
+**
+** If pApnd!=NULL then try to append missing nodes and set *pApnd = 1 if
+** nodes are appended.
*/
-static int jsonParseB(
- JsonParse *pParse, /* Initialize and fill this JsonParse object */
- sqlite3_context *pCtx /* Report errors here */
+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 */
){
- int i;
- const char *zJson = pParse->zJson;
- jsonBlobAppendOneByte(pParse, 0x4a);
- jsonBlobAppendOneByte(pParse, 0x01);
- i = jsonParseValueB(pParse, 0);
- if( pParse->oom ) i = -1;
- if( i>0 ){
- assert( pParse->iDepth==0 );
- while( fast_isspace(zJson[i]) ) i++;
- if( zJson[i] ){
- i += json5Whitespace(&zJson[i]);
- if( zJson[i] ){
- jsonParseReset(pParse);
- return 1;
- }
- pParse->hasNonstd = 1;
- }
+ const char *zErr = 0;
+ JsonNode *pNode = 0;
+ char *zMsg;
+
+ if( zPath==0 ) return 0;
+ if( zPath[0]!='$' ){
+ zErr = zPath;
+ goto lookup_err;
}
- if( i<=0 ){
- if( pCtx!=0 ){
- if( pParse->oom ){
- sqlite3_result_error_nomem(pCtx);
- }else{
- sqlite3_result_error(pCtx, "malformed JSON", -1);
- }
- }
- jsonParseReset(pParse);
- return 1;
+ zPath++;
+ pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr);
+ if( zErr==0 ) return pNode;
+
+lookup_err:
+ pParse->nErr++;
+ assert( zErr!=0 && pCtx!=0 );
+ zMsg = jsonPathSyntaxError(zErr);
+ if( zMsg ){
+ sqlite3_result_error(pCtx, zMsg, -1);
+ sqlite3_free(zMsg);
+ }else{
+ sqlite3_result_error_nomem(pCtx);
}
return 0;
}
-/*
-** Compute the parentage of all nodes in a completed parse.
-*/
-static int jsonParseFindParents(JsonParse *pParse){
- u32 *aUp;
- assert( pParse->aUp==0 );
- aUp = pParse->aUp = sqlite3_malloc64( sizeof(u32)*pParse->nNode );
- if( aUp==0 ){
- pParse->oom = 1;
- return SQLITE_NOMEM;
- }
- jsonParseFillInParentage(pParse, 0, 0);
- return SQLITE_OK;
-}
/*
-** Magic number used for the JSON parse cache in sqlite3_get_auxdata()
+** Report the wrong number of arguments for json_insert(), json_replace()
+** or json_set().
*/
-#define JSON_CACHE_ID (-429938) /* First cache entry */
-#define JSON_CACHE_SZ 4 /* Max number of cache entries */
-
-/*
-** Obtain a complete parse of the JSON found in the pJson argument
-**
-** Use the sqlite3_get_auxdata() cache to find a preexisting parse
-** if it is available. If the cache is not available or if it
-** is no longer valid, parse the JSON again and return the new parse.
-** Also register the new parse so that it will be available for
-** future sqlite3_get_auxdata() calls.
-**
-** If an error occurs and pErrCtx!=0 then report the error on pErrCtx
-** and return NULL.
-**
-** The returned pointer (if it is not NULL) is owned by the cache in
-** most cases, not the caller. The caller does NOT need to invoke
-** jsonParseFree(), in most cases.
-**
-** Except, if an error occurs and pErrCtx==0 then return the JsonParse
-** object with JsonParse.nErr non-zero and the caller will own the JsonParse
-** object. In that case, it will be the responsibility of the caller to
-** invoke jsonParseFree(). To summarize:
-**
-** pErrCtx!=0 || p->nErr==0 ==> Return value p is owned by the
-** cache. Call does not need to
-** free it.
-**
-** pErrCtx==0 && p->nErr!=0 ==> Return value is owned by the caller
-** and so the caller must free it.
-*/
-static JsonParse *jsonParseCached(
- sqlite3_context *pCtx, /* Context to use for cache search */
- sqlite3_value *pJson, /* Function param containing JSON text */
- sqlite3_context *pErrCtx, /* Write parse errors here if not NULL */
- int bUnedited /* No prior edits allowed */
+static void jsonWrongNumArgs(
+ sqlite3_context *pCtx,
+ const char *zFuncName
){
- char *zJson = (char*)sqlite3_value_text(pJson);
- int nJson = sqlite3_value_bytes(pJson);
- JsonParse *p;
- JsonParse *pMatch = 0;
- int iKey;
- int iMinKey = 0;
- u32 iMinHold = 0xffffffff;
- u32 iMaxHold = 0;
- int bJsonRCStr;
+ char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments",
+ zFuncName);
+ sqlite3_result_error(pCtx, zMsg, -1);
+ sqlite3_free(zMsg);
+}
- if( zJson==0 ) return 0;
- for(iKey=0; iKey<JSON_CACHE_SZ; iKey++){
- p = (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iKey);
- if( p==0 ){
- iMinKey = iKey;
- break;
- }
- if( pMatch==0
- && p->nJson==nJson
- && (p->hasMod==0 || bUnedited==0)
- && (p->zJson==zJson || memcmp(p->zJson,zJson,nJson)==0)
- ){
- p->nErr = 0;
- p->useMod = 0;
- pMatch = p;
- }else
- if( pMatch==0
- && p->zAlt!=0
- && bUnedited==0
- && p->nAlt==nJson
- && memcmp(p->zAlt, zJson, nJson)==0
- ){
- p->nErr = 0;
- p->useMod = 1;
- pMatch = p;
- }else if( p->iHold<iMinHold ){
- iMinHold = p->iHold;
- iMinKey = iKey;
- }
- if( p->iHold>iMaxHold ){
- iMaxHold = p->iHold;
+/*
+** Mark all NULL entries in the Object passed in as JNODE_REMOVE.
+*/
+static void jsonRemoveAllNulls(JsonNode *pNode){
+ int i, n;
+ assert( pNode->eType==JSON_OBJECT );
+ n = pNode->n;
+ for(i=2; i<=n; i += jsonNodeSize(&pNode[i])+1){
+ switch( pNode[i].eType ){
+ case JSON_NULL:
+ pNode[i].jnFlags |= JNODE_REMOVE;
+ break;
+ case JSON_OBJECT:
+ jsonRemoveAllNulls(&pNode[i]);
+ break;
}
}
- if( pMatch ){
- /* The input JSON text was found in the cache. Use the preexisting
- ** parse of this JSON */
- pMatch->nErr = 0;
- pMatch->iHold = iMaxHold+1;
- assert( pMatch->nJPRef>0 ); /* pMatch is owned by the cache */
- return pMatch;
- }
+}
- /* The input JSON was not found anywhere in the cache. We will need
- ** to parse it ourselves and generate a new JsonParse object.
- */
- bJsonRCStr = sqlite3ValueIsOfClass(pJson,(void(*)(void*))sqlite3RCStrUnref);
- p = sqlite3_malloc64( sizeof(*p) + (bJsonRCStr ? 0 : nJson+1) );
- if( p==0 ){
- sqlite3_result_error_nomem(pCtx);
- return 0;
- }
- memset(p, 0, sizeof(*p));
- if( bJsonRCStr ){
- p->zJson = sqlite3RCStrRef(zJson);
- p->bJsonIsRCStr = 1;
+/****************************************************************************
+** Utility routines for dealing with the binary BLOB representation of JSON
+****************************************************************************/
+
+
+/*
+** Expand pParse->aBlob so that it holds at least N bytes.
+**
+** Return the number of errors.
+*/
+static int jsonBlobExpand(JsonParse *pParse, u32 N){
+ u8 *aNew;
+ u32 t;
+ if( N<=pParse->nBlobAlloc ) return 0;
+ if( pParse->nBlobAlloc==0 ){
+ t = 100;
}else{
- p->zJson = (char*)&p[1];
- memcpy(p->zJson, zJson, nJson+1);
- }
- p->nJPRef = 1;
- if( jsonParse(p, pErrCtx) ){
- if( pErrCtx==0 ){
- p->nErr = 1;
- assert( p->nJPRef==1 ); /* Caller will own the new JsonParse object p */
- return p;
- }
- jsonParseFree(p);
- return 0;
+ t = pParse->nBlobAlloc*2;
}
- p->nJson = nJson;
- p->iHold = iMaxHold+1;
- /* Transfer ownership of the new JsonParse to the cache */
- sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p,
- (void(*)(void*))jsonParseFree);
- return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey);
+ if( t<N ) t = N+100;
+ aNew = sqlite3_realloc64( pParse->aBlob, t );
+ if( aNew==0 ){ pParse->oom = 1; return 1; }
+ pParse->aBlob = aNew;
+ pParse->nBlobAlloc = t;
+ return 0;
}
-/*
-** Compare the OBJECT label at pNode against zKey,nKey. Return true on
-** a match.
+/* Expand pParse->aBlob and append N bytes.
+**
+** Return the number of errors.
*/
-static int jsonLabelCompare(const JsonNode *pNode, const char *zKey, u32 nKey){
- assert( pNode->eU==1 );
- if( pNode->jnFlags & JNODE_RAW ){
- if( pNode->n!=nKey ) return 0;
- return strncmp(pNode->u.zJContent, zKey, nKey)==0;
- }else{
- if( pNode->n!=nKey+2 ) return 0;
- return strncmp(pNode->u.zJContent+1, zKey, nKey)==0;
+static SQLITE_NOINLINE int jsonBlobExpandAndAppend(
+ JsonParse *pParse,
+ const u8 *aData,
+ u32 N
+){
+ if( jsonBlobExpand(pParse, pParse->nBlob+N) ) return 1;
+ memcpy(&pParse->aBlob[pParse->nBlob], aData, N);
+ pParse->nBlob += N;
+ return 0;
+}
+
+/* Append a single character. Return 1 if an error occurs.
+*/
+static int jsonBlobAppendOneByte(JsonParse *pParse, u8 c){
+ if( pParse->nBlob >= pParse->nBlobAlloc ){
+ return jsonBlobExpandAndAppend(pParse, &c, 1);
}
+ pParse->aBlob[pParse->nBlob++] = c;
+ return 0;
}
-static int jsonSameLabel(const JsonNode *p1, const JsonNode *p2){
- if( p1->jnFlags & JNODE_RAW ){
- return jsonLabelCompare(p2, p1->u.zJContent, p1->n);
- }else if( p2->jnFlags & JNODE_RAW ){
- return jsonLabelCompare(p1, p2->u.zJContent, p2->n);
- }else{
- return p1->n==p2->n && strncmp(p1->u.zJContent,p2->u.zJContent,p1->n)==0;
+
+/* Append bytes. Return 1 if an error occurs.
+*/
+static int jsonBlobAppendNBytes(JsonParse *pParse, const u8 *aData, u32 N){
+ if( pParse->nBlob+N > pParse->nBlobAlloc ){
+ return jsonBlobExpandAndAppend(pParse, aData, N);
}
+ memcpy(&pParse->aBlob[pParse->nBlob], aData, N);
+ pParse->nBlob += N;
+ return 0;
}
-/* forward declaration */
-static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**);
+/* Append a u32. Return 1 if an error occurs.
+*/
+static int jsonBlobAppendU32(JsonParse *pParse, u32 x){
+ u8 a[4];
+ a[3] = x & 0xff;
+ x >>= 8;
+ a[2] = x & 0xff;
+ x >>= 8;
+ a[1] = x & 0xff;
+ x >>= 8;
+ a[0] = x & 0xff;
+ return jsonBlobAppendNBytes(pParse, a, 4);
+}
+
+/* Write a u32 and offset i.
+*/
+static int jsonBlobWriteU32(JsonParse *pParse, u32 x, u32 i){
+ u8 *a = &pParse->aBlob[i];
+ a[3] = x & 0xff;
+ x >>= 8;
+ a[2] = x & 0xff;
+ x >>= 8;
+ a[1] = x & 0xff;
+ x >>= 8;
+ a[0] = x & 0xff;
+ return 0;
+}
+
+/* Append a VarInt. Return 1 if an error occurs.
+*/
+static int jsonBlobAppendVarint(JsonParse *pParse, u32 x){
+ u8 a[5];
+ if( x<=0x7f ) return jsonBlobAppendOneByte(pParse, x & 0x7f);
+ a[0] = 0x80 | (x & 0x7f);
+ x >>= 7;
+ a[1] = x & 0x7f;
+ x >>= 7;
+ if( x==0 ) return jsonBlobAppendNBytes(pParse, a, 2);
+ a[1] |= 0x80;
+ a[2] = x & 0x7f;
+ x >>= 7;
+ if( x==0 ) return jsonBlobAppendNBytes(pParse, a, 3);
+ a[2] |= 0x80;
+ a[3] = x & 0x7f;
+ x >>= 7;
+ if( x==0 ) return jsonBlobAppendNBytes(pParse, a, 4);
+ a[3] |= 0x80;
+ a[4] = x & 0x7f;
+ return jsonBlobAppendNBytes(pParse, a, 5);
+}
/*
-** 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.
+** Parse a single JSON text value which begins at pParse->zJson[i] into
+** its equivalent BLOB representation in pParse->aBlob[]. The parse is
+** appended to pParse->aBlob[] beginning at pParse->nBlob. The size of
+** pParse->aBlob[] is increased as necessary.
**
-** If pApnd!=0, then try to append new nodes to complete zPath if it is
-** possible to do so and if no existing node corresponds to zPath. If
-** new nodes are appended *pApnd is set to 1.
+** Return the index of the first character past the end of the value parsed,
+** or one of the following special result codes:
+**
+** 0 End of input
+** -1 Syntax error
+** -2 '}' seen
+** -3 ']' seen
+** -4 ',' seen
+** -5 ':' seen
*/
-static JsonNode *jsonLookupStep(
- JsonParse *pParse, /* The JSON to search */
- u32 iRoot, /* Begin the search at this node */
- const char *zPath, /* The path to search */
- int *pApnd, /* Append nodes to complete path if not NULL */
- const char **pzErr /* Make *pzErr point to any syntax error in zPath */
-){
- u32 i, j, nKey;
- const char *zKey;
- JsonNode *pRoot;
- if( pParse->oom ) return 0;
- pRoot = &pParse->aNode[iRoot];
- if( pRoot->jnFlags & (JNODE_REPLACE|JNODE_REMOVE) && pParse->useMod ){
- while( (pRoot->jnFlags & JNODE_REPLACE)!=0 ){
- u32 idx = (u32)(pRoot - pParse->aNode);
- i = pParse->iSubst;
- while( 1 /*exit-by-break*/ ){
- assert( i<pParse->nNode );
- assert( pParse->aNode[i].eType==JSON_SUBST );
- assert( pParse->aNode[i].eU==4 );
- assert( pParse->aNode[i].u.iPrev<i );
- if( pParse->aNode[i].n==idx ){
- pRoot = &pParse->aNode[i+1];
- iRoot = i+1;
+static int jsonParseValueB(JsonParse *pParse, u32 i){
+ char c;
+ u32 j;
+ int iThis;
+ int x;
+ u8 t;
+ const char *z = pParse->zJson;
+json_parse_restart:
+ switch( (u8)z[i] ){
+ case '{': {
+ /* Parse object */
+ jsonBlobAppendOneByte(pParse, JSONB_OBJECT);
+ iThis = pParse->nBlob;
+ jsonBlobAppendU32(pParse, 0);
+ if( ++pParse->iDepth > JSON_MAX_DEPTH ){
+ pParse->iErr = i;
+ return -1;
+ }
+ for(j=i+1;;j++){
+ u32 iBlob = pParse->nBlob;
+ x = jsonParseValueB(pParse, j);
+ if( x<=0 ){
+ if( x==(-2) ){
+ j = pParse->iErr;
+ if( pParse->nBlob!=(u32)iThis+1 ) pParse->hasNonstd = 1;
+ break;
+ }
+ j += json5Whitespace(&z[j]);
+ if( sqlite3JsonId1(z[j])
+ || (z[j]=='\\' && z[j+1]=='u' && jsonIs4Hex(&z[j+2]))
+ ){
+ int k = j+1;
+ while( (sqlite3JsonId2(z[k]) && json5Whitespace(&z[k])==0)
+ || (z[k]=='\\' && z[k+1]=='u' && jsonIs4Hex(&z[k+2]))
+ ){
+ k++;
+ }
+ assert( iBlob==pParse->nBlob );
+ jsonBlobAppendOneByte(pParse, JSONB_TEXT5);
+ jsonBlobAppendVarint(pParse, k-j);
+ jsonBlobAppendNBytes(pParse, (const u8*)&z[j], k-j);
+ pParse->hasNonstd = 1;
+ x = k;
+ }else{
+ if( x!=-1 ) pParse->iErr = j;
+ return -1;
+ }
+ }
+ if( pParse->oom ) return -1;
+ t = pParse->aBlob[iBlob];
+ if( t<JSONB_TEXT || t>JSONB_TEXT5 ){
+ pParse->iErr = j;
+ return -1;
+ }
+ j = x;
+ if( z[j]==':' ){
+ j++;
+ }else{
+ if( fast_isspace(z[j]) ){
+ do{ j++; }while( fast_isspace(z[j]) );
+ if( z[j]==':' ){
+ j++;
+ goto parse_object_value;
+ }
+ }
+ x = jsonParseValueB(pParse, j);
+ if( x!=(-5) ){
+ if( x!=(-1) ) pParse->iErr = j;
+ return -1;
+ }
+ j = pParse->iErr+1;
+ }
+ parse_object_value:
+ x = jsonParseValueB(pParse, j);
+ if( x<=0 ){
+ if( x!=(-1) ) pParse->iErr = j;
+ return -1;
+ }
+ j = x;
+ if( z[j]==',' ){
+ continue;
+ }else if( z[j]=='}' ){
+ break;
+ }else{
+ if( fast_isspace(z[j]) ){
+ do{ j++; }while( fast_isspace(z[j]) );
+ if( z[j]==',' ){
+ continue;
+ }else if( z[j]=='}' ){
+ break;
+ }
+ }
+ x = jsonParseValueB(pParse, j);
+ if( x==(-4) ){
+ j = pParse->iErr;
+ continue;
+ }
+ if( x==(-2) ){
+ j = pParse->iErr;
+ break;
+ }
+ }
+ pParse->iErr = j;
+ return -1;
+ }
+ jsonBlobWriteU32(pParse, pParse->nBlob - iThis, iThis);
+ pParse->iDepth--;
+ return j+1;
+ }
+ case '[': {
+ /* Parse array */
+ jsonBlobAppendOneByte(pParse, JSONB_ARRAY);
+ iThis = pParse->nBlob;
+ jsonBlobAppendU32(pParse, 0);
+ if( pParse->oom ) return -1;
+ if( ++pParse->iDepth > JSON_MAX_DEPTH ){
+ pParse->iErr = i;
+ return -1;
+ }
+ for(j=i+1;;j++){
+ x = jsonParseValueB(pParse, j);
+ if( x<=0 ){
+ if( x==(-3) ){
+ j = pParse->iErr;
+ if( pParse->nBlob!=iThis+4 ) pParse->hasNonstd = 1;
+ break;
+ }
+ if( x!=(-1) ) pParse->iErr = j;
+ return -1;
+ }
+ j = x;
+ if( z[j]==',' ){
+ continue;
+ }else if( z[j]==']' ){
+ break;
+ }else{
+ if( fast_isspace(z[j]) ){
+ do{ j++; }while( fast_isspace(z[j]) );
+ if( z[j]==',' ){
+ continue;
+ }else if( z[j]==']' ){
+ break;
+ }
+ }
+ x = jsonParseValueB(pParse, j);
+ if( x==(-4) ){
+ j = pParse->iErr;
+ continue;
+ }
+ if( x==(-3) ){
+ j = pParse->iErr;
break;
}
- i = pParse->aNode[i].u.iPrev;
+ }
+ pParse->iErr = j;
+ return -1;
+ }
+ jsonBlobWriteU32(pParse, pParse->nBlob - iThis, iThis);
+ pParse->iDepth--;
+ return j+1;
+ }
+ case '\'': {
+ u8 opcode;
+ char cDelim;
+ pParse->hasNonstd = 1;
+ opcode = JNODE_JSON5;
+ goto parse_string;
+ case '"':
+ /* Parse string */
+ opcode = JSONB_TEXT;
+ parse_string:
+ cDelim = z[i];
+ for(j=i+1; 1; j++){
+ if( jsonIsOk[(unsigned char)z[j]] ) continue;
+ c = z[j];
+ if( c==cDelim ){
+ break;
+ }else if( c=='\\' ){
+ c = z[++j];
+ if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
+ || c=='n' || c=='r' || c=='t'
+ || (c=='u' && jsonIs4Hex(&z[j+1])) ){
+ if( opcode==JSONB_TEXT ) opcode = JSONB_TEXTJ;
+ }else if( c=='\'' || c=='0' || c=='v' || c=='\n'
+ || (0xe2==(u8)c && 0x80==(u8)z[j+1]
+ && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2]))
+ || (c=='x' && jsonIs2Hex(&z[j+1])) ){
+ opcode = JSONB_TEXT5;
+ pParse->hasNonstd = 1;
+ }else if( c=='\r' ){
+ if( z[j+1]=='\n' ) j++;
+ opcode = JSONB_TEXT5;
+ pParse->hasNonstd = 1;
+ }else{
+ pParse->iErr = j;
+ return -1;
+ }
+ }else if( c<=0x1f ){
+ /* Control characters are not allowed in strings */
+ pParse->iErr = j;
+ return -1;
}
}
- if( pRoot->jnFlags & JNODE_REMOVE ){
- return 0;
+ jsonBlobAppendOneByte(pParse, opcode);
+ jsonBlobAppendVarint(pParse, j+1-i);
+ jsonBlobAppendNBytes(pParse, (const u8*)&z[i], j+1-i);
+ return j+1;
+ }
+ case 't': {
+ if( strncmp(z+i,"true",4)==0 && !sqlite3Isalnum(z[i+4]) ){
+ jsonBlobAppendOneByte(pParse, JSONB_TRUE);
+ return i+4;
}
+ pParse->iErr = i;
+ return -1;
}
- if( zPath[0]==0 ) return pRoot;
- if( zPath[0]=='.' ){
- if( pRoot->eType!=JSON_OBJECT ) return 0;
- zPath++;
- if( zPath[0]=='"' ){
- zKey = zPath + 1;
- for(i=1; zPath[i] && zPath[i]!='"'; i++){}
- nKey = i-1;
- if( zPath[i] ){
- i++;
- }else{
- *pzErr = zPath;
- return 0;
- }
- testcase( nKey==0 );
- }else{
- zKey = zPath;
- for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){}
- nKey = i;
- if( nKey==0 ){
- *pzErr = zPath;
- return 0;
- }
+ case 'f': {
+ if( strncmp(z+i,"false",5)==0 && !sqlite3Isalnum(z[i+5]) ){
+ jsonBlobAppendOneByte(pParse, JSONB_FALSE);
+ return i+5;
}
- j = 1;
- for(;;){
- while( j<=pRoot->n ){
- if( jsonLabelCompare(pRoot+j, zKey, nKey) ){
- return jsonLookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr);
+ pParse->iErr = i;
+ return -1;
+ }
+ case '+': {
+ u8 seenDP, seenE, jnFlags;
+ pParse->hasNonstd = 1;
+ jnFlags = 0;
+ goto parse_number;
+ case '.':
+ if( sqlite3Isdigit(z[i+1]) ){
+ pParse->hasNonstd = 1;
+ jnFlags = JNODE_JSON5;
+ seenE = 0;
+ seenDP = JSON_REAL;
+ goto parse_number_2;
+ }
+ pParse->iErr = i;
+ return -1;
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ /* Parse number */
+ jnFlags = 0;
+ parse_number:
+ seenDP = JSON_INT;
+ seenE = 0;
+ assert( '-' < '0' );
+ assert( '+' < '0' );
+ assert( '.' < '0' );
+ c = z[i];
+
+ if( c<='0' ){
+ if( c=='0' ){
+ if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){
+ assert( seenDP==JSON_INT );
+ pParse->hasNonstd = 1;
+ jnFlags |= JNODE_JSON5;
+ for(j=i+3; sqlite3Isxdigit(z[j]); j++){}
+ goto parse_number_finish;
+ }else if( sqlite3Isdigit(z[i+1]) ){
+ pParse->iErr = i+1;
+ return -1;
+ }
+ }else{
+ if( !sqlite3Isdigit(z[i+1]) ){
+ /* JSON5 allows for "+Infinity" and "-Infinity" using exactly
+ ** that case. SQLite also allows these in any case and it allows
+ ** "+inf" and "-inf". */
+ if( (z[i+1]=='I' || z[i+1]=='i')
+ && sqlite3StrNICmp(&z[i+1], "inf",3)==0
+ ){
+ pParse->hasNonstd = 1;
+ jsonBlobAppendOneByte(pParse, JSONB_NUMBER5);
+ if( z[i]=='-' ){
+ jsonBlobAppendVarint(pParse, 8);
+ jsonBlobAppendNBytes(pParse, (const u8*)"-9.0e999", 8);
+ }else{
+ jsonBlobAppendVarint(pParse, 7);
+ jsonBlobAppendNBytes(pParse, (const u8*)"9.0e999", 7);
+ }
+ return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4);
+ }
+ if( z[i+1]=='.' ){
+ pParse->hasNonstd = 1;
+ jnFlags |= JNODE_JSON5;
+ goto parse_number_2;
+ }
+ pParse->iErr = i;
+ return -1;
+ }
+ if( z[i+1]=='0' ){
+ if( sqlite3Isdigit(z[i+2]) ){
+ pParse->iErr = i+1;
+ return -1;
+ }else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){
+ pParse->hasNonstd = 1;
+ jnFlags |= JNODE_JSON5;
+ for(j=i+4; sqlite3Isxdigit(z[j]); j++){}
+ goto parse_number_finish;
+ }
}
- j++;
- j += jsonNodeSize(&pRoot[j]);
}
- if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
- if( pParse->useMod==0 ) break;
- assert( pRoot->eU==2 );
- iRoot = pRoot->u.iAppend;
- pRoot = &pParse->aNode[iRoot];
- j = 1;
}
- if( pApnd ){
- u32 iStart, iLabel;
- JsonNode *pNode;
- assert( pParse->useMod );
- iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0);
- iLabel = jsonParseAddNode(pParse, JSON_STRING, nKey, zKey);
- zPath += i;
- pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
- if( pParse->oom ) return 0;
- if( pNode ){
- pRoot = &pParse->aNode[iRoot];
- assert( pRoot->eU==0 );
- pRoot->u.iAppend = iStart;
- pRoot->jnFlags |= JNODE_APPEND;
- VVA( pRoot->eU = 2 );
- pParse->aNode[iLabel].jnFlags |= JNODE_RAW;
+ parse_number_2:
+ for(j=i+1;; j++){
+ c = z[j];
+ if( sqlite3Isdigit(c) ) continue;
+ if( c=='.' ){
+ if( seenDP==JSON_REAL ){
+ pParse->iErr = j;
+ return -1;
+ }
+ seenDP = JSON_REAL;
+ continue;
}
- return pNode;
- }
- }else if( zPath[0]=='[' ){
- i = 0;
- j = 1;
- while( sqlite3Isdigit(zPath[j]) ){
- i = i*10 + zPath[j] - '0';
- j++;
- }
- if( j<2 || zPath[j]!=']' ){
- if( zPath[1]=='#' ){
- JsonNode *pBase = pRoot;
- int iBase = iRoot;
- if( pRoot->eType!=JSON_ARRAY ) return 0;
- for(;;){
- while( j<=pBase->n ){
- if( (pBase[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i++;
- j += jsonNodeSize(&pBase[j]);
+ if( c=='e' || c=='E' ){
+ if( z[j-1]<'0' ){
+ if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){
+ pParse->hasNonstd = 1;
+ jnFlags |= JNODE_JSON5;
+ }else{
+ pParse->iErr = j;
+ return -1;
}
- if( (pBase->jnFlags & JNODE_APPEND)==0 ) break;
- if( pParse->useMod==0 ) break;
- assert( pBase->eU==2 );
- iBase = pBase->u.iAppend;
- pBase = &pParse->aNode[iBase];
- j = 1;
}
- j = 2;
- if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){
- unsigned int x = 0;
- j = 3;
- do{
- x = x*10 + zPath[j] - '0';
- j++;
- }while( sqlite3Isdigit(zPath[j]) );
- if( x>i ) return 0;
- i -= x;
+ if( seenE ){
+ pParse->iErr = j;
+ return -1;
}
- if( zPath[j]!=']' ){
- *pzErr = zPath;
- return 0;
+ seenDP = JSON_REAL;
+ seenE = 1;
+ c = z[j+1];
+ if( c=='+' || c=='-' ){
+ j++;
+ c = z[j+1];
}
- }else{
- *pzErr = zPath;
- return 0;
- }
- }
- if( pRoot->eType!=JSON_ARRAY ) return 0;
- zPath += j + 1;
- j = 1;
- for(;;){
- while( j<=pRoot->n
- && (i>0 || ((pRoot[j].jnFlags & JNODE_REMOVE)!=0 && pParse->useMod))
- ){
- if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i--;
- j += jsonNodeSize(&pRoot[j]);
+ if( c<'0' || c>'9' ){
+ pParse->iErr = j;
+ return -1;
+ }
+ continue;
}
- if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
- if( pParse->useMod==0 ) break;
- assert( pRoot->eU==2 );
- iRoot = pRoot->u.iAppend;
- pRoot = &pParse->aNode[iRoot];
- j = 1;
- }
- if( j<=pRoot->n ){
- return jsonLookupStep(pParse, iRoot+j, zPath, pApnd, pzErr);
+ break;
}
- if( i==0 && pApnd ){
- u32 iStart;
- JsonNode *pNode;
- assert( pParse->useMod );
- iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0);
- pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
- if( pParse->oom ) return 0;
- if( pNode ){
- pRoot = &pParse->aNode[iRoot];
- assert( pRoot->eU==0 );
- pRoot->u.iAppend = iStart;
- pRoot->jnFlags |= JNODE_APPEND;
- VVA( pRoot->eU = 2 );
+ if( z[j-1]<'0' ){
+ if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){
+ pParse->hasNonstd = 1;
+ jnFlags |= JNODE_JSON5;
+ }else{
+ pParse->iErr = j;
+ return -1;
}
- return pNode;
}
- }else{
- *pzErr = zPath;
+ parse_number_finish:
+ if( jnFlags & JNODE_JSON5 ){
+ jsonBlobAppendOneByte(pParse, JSONB_NUMBER5);
+ }else{
+ jsonBlobAppendOneByte(pParse, JSONB_NUMBER);
+ }
+ jsonBlobAppendVarint(pParse, j-i);
+ jsonBlobAppendNBytes(pParse, (const u8*)&z[i], j-i);
+ return j;
}
- return 0;
-}
-
-/*
-** Append content to pParse that will complete zPath. Return a pointer
-** to the inserted node, or return NULL if the append fails.
-*/
-static JsonNode *jsonLookupAppend(
- JsonParse *pParse, /* Append content to the JSON parse */
- const char *zPath, /* Description of content to append */
- int *pApnd, /* Set this flag to 1 */
- const char **pzErr /* Make this point to any syntax error */
-){
- *pApnd = 1;
- if( zPath[0]==0 ){
- jsonParseAddNode(pParse, JSON_NULL, 0, 0);
- return pParse->oom ? 0 : &pParse->aNode[pParse->nNode-1];
+ case '}': {
+ pParse->iErr = i;
+ return -2; /* End of {...} */
}
- if( zPath[0]=='.' ){
- jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
- }else if( strncmp(zPath,"[0]",3)==0 ){
- jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
- }else{
- return 0;
+ case ']': {
+ pParse->iErr = i;
+ return -3; /* End of [...] */
}
- if( pParse->oom ) return 0;
- return jsonLookupStep(pParse, pParse->nNode-1, zPath, pApnd, pzErr);
-}
-
-/*
-** Return the text of a syntax error message on a JSON path. Space is
-** obtained from sqlite3_malloc().
-*/
-static char *jsonPathSyntaxError(const char *zErr){
- return sqlite3_mprintf("JSON path error near '%q'", zErr);
-}
-
-/*
-** Do a node lookup using zPath. Return a pointer to the node on success.
-** Return NULL if not found or if there is an error.
-**
-** On an error, write an error message into pCtx and increment the
-** pParse->nErr counter.
-**
-** If pApnd!=NULL then try to append missing nodes and set *pApnd = 1 if
-** nodes are appended.
-*/
-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 */
-){
- const char *zErr = 0;
- JsonNode *pNode = 0;
- char *zMsg;
-
- if( zPath==0 ) return 0;
- if( zPath[0]!='$' ){
- zErr = zPath;
- goto lookup_err;
+ case ',': {
+ pParse->iErr = i;
+ return -4; /* List separator */
}
- zPath++;
- pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr);
- if( zErr==0 ) return pNode;
-
-lookup_err:
- pParse->nErr++;
- assert( zErr!=0 && pCtx!=0 );
- zMsg = jsonPathSyntaxError(zErr);
- if( zMsg ){
- sqlite3_result_error(pCtx, zMsg, -1);
- sqlite3_free(zMsg);
- }else{
- sqlite3_result_error_nomem(pCtx);
+ case ':': {
+ pParse->iErr = i;
+ return -5; /* Object label/value separator */
}
- return 0;
+ case 0: {
+ return 0; /* End of file */
+ }
+ case 0x09:
+ case 0x0a:
+ case 0x0d:
+ case 0x20: {
+ do{
+ i++;
+ }while( fast_isspace(z[i]) );
+ goto json_parse_restart;
+ }
+ case 0x0b:
+ case 0x0c:
+ case '/':
+ case 0xc2:
+ case 0xe1:
+ case 0xe2:
+ case 0xe3:
+ case 0xef: {
+ j = json5Whitespace(&z[i]);
+ if( j>0 ){
+ i += j;
+ pParse->hasNonstd = 1;
+ goto json_parse_restart;
+ }
+ pParse->iErr = i;
+ return -1;
+ }
+ case 'n': {
+ if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){
+ jsonBlobAppendOneByte(pParse, JSONB_NULL);
+ return i+4;
+ }
+ /* fall-through into the default case that checks for NaN */
+ }
+ default: {
+ u32 k;
+ int nn;
+ c = z[i];
+ for(k=0; k<sizeof(aNanInfName)/sizeof(aNanInfName[0]); k++){
+ if( c!=aNanInfName[k].c1 && c!=aNanInfName[k].c2 ) continue;
+ nn = aNanInfName[k].n;
+ if( sqlite3StrNICmp(&z[i], aNanInfName[k].zMatch, nn)!=0 ){
+ continue;
+ }
+ if( sqlite3Isalnum(z[i+nn]) ) continue;
+ if( aNanInfName[k].eType==JSON_REAL ){
+ jsonBlobAppendOneByte(pParse, JSONB_NUMBER);
+ jsonBlobAppendOneByte(pParse, 7);
+ jsonBlobAppendNBytes(pParse, (const u8*)"9.0e999", 7);
+ }else{
+ jsonBlobAppendOneByte(pParse, JSONB_NULL);
+ }
+ pParse->hasNonstd = 1;
+ return i + nn;
+ }
+ pParse->iErr = i;
+ return -1; /* Syntax error */
+ }
+ } /* End switch(z[i]) */
}
/*
-** Report the wrong number of arguments for json_insert(), json_replace()
-** or json_set().
+** Parse a complete JSON string. Return 0 on success or non-zero if there
+** are any errors. If an error occurs, free all memory held by pParse,
+** but not pParse itself.
+**
+** pParse must be initialized to an empty parse object prior to calling
+** this routine.
*/
-static void jsonWrongNumArgs(
- sqlite3_context *pCtx,
- const char *zFuncName
+static int jsonParseB(
+ JsonParse *pParse, /* Initialize and fill this JsonParse object */
+ sqlite3_context *pCtx /* Report errors here */
){
- char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments",
- zFuncName);
- sqlite3_result_error(pCtx, zMsg, -1);
- sqlite3_free(zMsg);
-}
-
-/*
-** Mark all NULL entries in the Object passed in as JNODE_REMOVE.
-*/
-static void jsonRemoveAllNulls(JsonNode *pNode){
- int i, n;
- assert( pNode->eType==JSON_OBJECT );
- n = pNode->n;
- for(i=2; i<=n; i += jsonNodeSize(&pNode[i])+1){
- switch( pNode[i].eType ){
- case JSON_NULL:
- pNode[i].jnFlags |= JNODE_REMOVE;
- break;
- case JSON_OBJECT:
- jsonRemoveAllNulls(&pNode[i]);
- break;
+ int i;
+ const char *zJson = pParse->zJson;
+ jsonBlobAppendOneByte(pParse, 0x4a);
+ jsonBlobAppendOneByte(pParse, 0x01);
+ i = jsonParseValueB(pParse, 0);
+ if( pParse->oom ) i = -1;
+ if( i>0 ){
+ assert( pParse->iDepth==0 );
+ while( fast_isspace(zJson[i]) ) i++;
+ if( zJson[i] ){
+ i += json5Whitespace(&zJson[i]);
+ if( zJson[i] ){
+ jsonParseReset(pParse);
+ return 1;
+ }
+ pParse->hasNonstd = 1;
+ }
+ }
+ if( i<=0 ){
+ if( pCtx!=0 ){
+ if( pParse->oom ){
+ sqlite3_result_error_nomem(pCtx);
+ }else{
+ sqlite3_result_error(pCtx, "malformed JSON", -1);
+ }
}
+ jsonParseReset(pParse);
+ return 1;
}
+ return 0;
}
-
/****************************************************************************
** SQL functions used for testing and debugging
****************************************************************************/