Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Initial implementation of the json_mergepatch(A,B) SQL function. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | json_mergepatch |
Files: | files | file ages | folders |
SHA3-256: |
a267444039af519f088dd8f8ee33f686 |
User & Date: | drh 2017-03-22 21:24:31 |
Context
2017-03-22
| ||
21:45 | Change the name of the new function to "json_merge_patch()". check-in: 53bf70f3 user: drh tags: json_mergepatch | |
21:24 | Initial implementation of the json_mergepatch(A,B) SQL function. check-in: a2674440 user: drh tags: json_mergepatch | |
2017-03-21
| ||
20:17 | New simplified memory initialization for MacOS. check-in: 055b36f1 user: drh tags: trunk | |
Changes
Changes to ext/misc/json1.c.
134 134 }; 135 135 136 136 /* Bit values for the JsonNode.jnFlag field 137 137 */ 138 138 #define JNODE_RAW 0x01 /* Content is raw, not JSON encoded */ 139 139 #define JNODE_ESCAPE 0x02 /* Content is text with \ escapes */ 140 140 #define JNODE_REMOVE 0x04 /* Do not output */ 141 -#define JNODE_REPLACE 0x08 /* Replace with JsonNode.iVal */ 142 -#define JNODE_APPEND 0x10 /* More ARRAY/OBJECT entries at u.iAppend */ 143 -#define JNODE_LABEL 0x20 /* Is a label of an object */ 141 +#define JNODE_REPLACE 0x08 /* Replace with JsonNode.u.iReplace */ 142 +#define JNODE_PATCH 0x10 /* Patch with JsonNode.u.pPatch */ 143 +#define JNODE_APPEND 0x20 /* More ARRAY/OBJECT entries at u.iAppend */ 144 +#define JNODE_LABEL 0x40 /* Is a label of an object */ 144 145 145 146 146 147 /* A single node of parsed JSON 147 148 */ 148 149 struct JsonNode { 149 150 u8 eType; /* One of the JSON_ type values */ 150 151 u8 jnFlags; /* JNODE flags */ 151 - u8 iVal; /* Replacement value when JNODE_REPLACE */ 152 152 u32 n; /* Bytes of content, or number of sub-nodes */ 153 153 union { 154 154 const char *zJContent; /* Content for INT, REAL, and STRING */ 155 155 u32 iAppend; /* More terms for ARRAY and OBJECT */ 156 156 u32 iKey; /* Key for ARRAY objects in json_tree() */ 157 + u32 iReplace; /* Replacement content for JNODE_REPLACE */ 158 + JsonNode *pPatch; /* Node chain of patch for JNODE_PATCH */ 157 159 } u; 158 160 }; 159 161 160 162 /* A completely parsed JSON string 161 163 */ 162 164 struct JsonParse { 163 165 u32 nNode; /* Number of slots of aNode[] used */ ................................................................................ 406 408 ** the number of JsonNode objects that are encoded. 407 409 */ 408 410 static void jsonRenderNode( 409 411 JsonNode *pNode, /* The node to render */ 410 412 JsonString *pOut, /* Write JSON here */ 411 413 sqlite3_value **aReplace /* Replacement values */ 412 414 ){ 415 + if( pNode->jnFlags & (JNODE_REPLACE|JNODE_PATCH) ){ 416 + if( pNode->jnFlags & JNODE_REPLACE ){ 417 + jsonAppendValue(pOut, aReplace[pNode->u.iReplace]); 418 + return; 419 + } 420 + pNode = pNode->u.pPatch; 421 + } 413 422 switch( pNode->eType ){ 414 423 default: { 415 424 assert( pNode->eType==JSON_NULL ); 416 425 jsonAppendRaw(pOut, "null", 4); 417 426 break; 418 427 } 419 428 case JSON_TRUE: { ................................................................................ 437 446 break; 438 447 } 439 448 case JSON_ARRAY: { 440 449 u32 j = 1; 441 450 jsonAppendChar(pOut, '['); 442 451 for(;;){ 443 452 while( j<=pNode->n ){ 444 - if( pNode[j].jnFlags & (JNODE_REMOVE|JNODE_REPLACE) ){ 445 - if( pNode[j].jnFlags & JNODE_REPLACE ){ 446 - jsonAppendSeparator(pOut); 447 - jsonAppendValue(pOut, aReplace[pNode[j].iVal]); 448 - } 449 - }else{ 453 + if( (pNode[j].jnFlags & JNODE_REMOVE)==0 ){ 450 454 jsonAppendSeparator(pOut); 451 455 jsonRenderNode(&pNode[j], pOut, aReplace); 452 456 } 453 457 j += jsonNodeSize(&pNode[j]); 454 458 } 455 459 if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; 456 460 pNode = &pNode[pNode->u.iAppend]; ................................................................................ 464 468 jsonAppendChar(pOut, '{'); 465 469 for(;;){ 466 470 while( j<=pNode->n ){ 467 471 if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 ){ 468 472 jsonAppendSeparator(pOut); 469 473 jsonRenderNode(&pNode[j], pOut, aReplace); 470 474 jsonAppendChar(pOut, ':'); 471 - if( pNode[j+1].jnFlags & JNODE_REPLACE ){ 472 - jsonAppendValue(pOut, aReplace[pNode[j+1].iVal]); 473 - }else{ 474 - jsonRenderNode(&pNode[j+1], pOut, aReplace); 475 - } 475 + jsonRenderNode(&pNode[j+1], pOut, aReplace); 476 476 } 477 477 j += 1 + jsonNodeSize(&pNode[j+1]); 478 478 } 479 479 if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; 480 480 pNode = &pNode[pNode->u.iAppend]; 481 481 j = 1; 482 482 } ................................................................................ 695 695 JsonNode *p; 696 696 if( pParse->nNode>=pParse->nAlloc ){ 697 697 return jsonParseAddNodeExpand(pParse, eType, n, zContent); 698 698 } 699 699 p = &pParse->aNode[pParse->nNode]; 700 700 p->eType = (u8)eType; 701 701 p->jnFlags = 0; 702 - p->iVal = 0; 703 702 p->n = n; 704 703 p->u.zJContent = zContent; 705 704 return pParse->nNode++; 706 705 } 707 706 708 707 /* 709 708 ** Return true if z[] begins with 4 (or more) hexadecimal digits ................................................................................ 1352 1351 jsonAppendChar(&jx, ']'); 1353 1352 jsonResult(&jx); 1354 1353 sqlite3_result_subtype(ctx, JSON_SUBTYPE); 1355 1354 } 1356 1355 jsonReset(&jx); 1357 1356 jsonParseReset(&x); 1358 1357 } 1358 + 1359 +/* This is the RFC 7396 MergePatch algorithm. 1360 +*/ 1361 +static JsonNode *jsonMergePatch( 1362 + JsonParse *pParse, /* The JSON parser that contains the TARGET */ 1363 + int iTarget, /* Node of the TARGET in pParse */ 1364 + JsonNode *pPatch /* The PATCH */ 1365 +){ 1366 + int i, j; 1367 + int iApnd; 1368 + JsonNode *pTarget; 1369 + if( pPatch->eType!=JSON_OBJECT ){ 1370 + return pPatch; 1371 + } 1372 + assert( iTarget>=0 && iTarget<pParse->nNode ); 1373 + pTarget = &pParse->aNode[iTarget]; 1374 + assert( (pPatch->jnFlags & JNODE_APPEND)==0 ); 1375 + if( pTarget->eType!=JSON_OBJECT ){ 1376 + for(i=2; i<pPatch->n; i += jsonNodeSize(&pPatch[i])+1){ 1377 + if( pPatch[i].eType==JSON_NULL ){ 1378 + pPatch[i-1].jnFlags |= JNODE_REMOVE; 1379 + } 1380 + } 1381 + return pPatch; 1382 + } 1383 + iApnd = iTarget; 1384 + for(i=1; i<pPatch->n; i += jsonNodeSize(&pPatch[i+1])+1){ 1385 + int nKey; 1386 + const char *zKey; 1387 + assert( pPatch[i].eType==JSON_STRING ); 1388 + assert( pPatch[i].jnFlags & JNODE_LABEL ); 1389 + nKey = pPatch[i].n; 1390 + zKey = pPatch[i].u.zJContent; 1391 + if( (pPatch[i].jnFlags & JNODE_RAW)==0 ){ 1392 + assert( nKey>=2 && zKey[0]=='"' && zKey[nKey-1]=='"' ); 1393 + nKey -= 2; 1394 + zKey ++; 1395 + } 1396 + for(j=1; j<pTarget->n; j += jsonNodeSize(&pTarget[j+1])+1 ){ 1397 + assert( pTarget[j].eType==JSON_STRING ); 1398 + assert( pTarget[j].jnFlags & JNODE_LABEL ); 1399 + if( jsonLabelCompare(&pTarget[j], zKey, nKey) 1400 + && (pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_PATCH))==0 1401 + ){ 1402 + if( pPatch[i+1].eType==JSON_NULL ){ 1403 + pTarget[j+1].jnFlags |= JNODE_REMOVE; 1404 + }else{ 1405 + JsonNode *pNew = jsonMergePatch(pParse, j+1, &pPatch[i+1]); 1406 + if( pNew==0 ) return 0; 1407 + pTarget = &pParse->aNode[iTarget]; 1408 + if( pNew!=&pTarget[j+1] ){ 1409 + pTarget[j+1].u.pPatch = pNew; 1410 + pTarget[j+1].jnFlags |= JNODE_PATCH; 1411 + } 1412 + } 1413 + break; 1414 + } 1415 + } 1416 + if( j>=pTarget->n ){ 1417 + int iStart, iPatch; 1418 + iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0); 1419 + jsonParseAddNode(pParse, JSON_STRING, nKey, zKey); 1420 + iPatch = jsonParseAddNode(pParse, JSON_TRUE, 0, 0); 1421 + if( pParse->oom ) return 0; 1422 + pTarget = &pParse->aNode[iTarget]; 1423 + pParse->aNode[iApnd].jnFlags |= JNODE_APPEND; 1424 + pParse->aNode[iApnd].u.iAppend = iStart; 1425 + iApnd = iStart; 1426 + pParse->aNode[iPatch].jnFlags |= JNODE_PATCH; 1427 + pParse->aNode[iPatch].u.pPatch = &pPatch[i+1]; 1428 + } 1429 + } 1430 + return pTarget; 1431 +} 1432 + 1433 +/* 1434 +** Implementation of the json_mergepatch(JSON1,JSON2) function. Return a JSON 1435 +** object that is the result of running the RFC 7396 MergePatch() algorithm 1436 +** on the two arguments. 1437 +*/ 1438 +static void jsonMergePatchFunc( 1439 + sqlite3_context *ctx, 1440 + int argc, 1441 + sqlite3_value **argv 1442 +){ 1443 + JsonParse x; /* The JSON that is being patched */ 1444 + JsonParse y; /* The patch */ 1445 + 1446 + if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; 1447 + if( jsonParse(&y, ctx, (const char*)sqlite3_value_text(argv[1])) ){ 1448 + jsonParseReset(&x); 1449 + return; 1450 + } 1451 + jsonReturnJson(jsonMergePatch(&x, 0, y.aNode), ctx, 0); 1452 + jsonParseReset(&x); 1453 + jsonParseReset(&y); 1454 +} 1455 + 1359 1456 1360 1457 /* 1361 1458 ** Implementation of the json_object(NAME,VALUE,...) function. Return a JSON 1362 1459 ** object that contains all name/value given in arguments. Or if any name 1363 1460 ** is not a string or if any value is a BLOB, throw an error. 1364 1461 */ 1365 1462 static void jsonObjectFunc( ................................................................................ 1456 1553 assert( x.nNode ); 1457 1554 for(i=1; i<(u32)argc; i+=2){ 1458 1555 zPath = (const char*)sqlite3_value_text(argv[i]); 1459 1556 pNode = jsonLookup(&x, zPath, 0, ctx); 1460 1557 if( x.nErr ) goto replace_err; 1461 1558 if( pNode ){ 1462 1559 pNode->jnFlags |= (u8)JNODE_REPLACE; 1463 - pNode->iVal = (u8)(i+1); 1560 + pNode->u.iReplace = i + 1; 1464 1561 } 1465 1562 } 1466 1563 if( x.aNode[0].jnFlags & JNODE_REPLACE ){ 1467 - sqlite3_result_value(ctx, argv[x.aNode[0].iVal]); 1564 + sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]); 1468 1565 }else{ 1469 1566 jsonReturnJson(x.aNode, ctx, argv); 1470 1567 } 1471 1568 replace_err: 1472 1569 jsonParseReset(&x); 1473 1570 } 1474 1571 ................................................................................ 1510 1607 if( x.oom ){ 1511 1608 sqlite3_result_error_nomem(ctx); 1512 1609 goto jsonSetDone; 1513 1610 }else if( x.nErr ){ 1514 1611 goto jsonSetDone; 1515 1612 }else if( pNode && (bApnd || bIsSet) ){ 1516 1613 pNode->jnFlags |= (u8)JNODE_REPLACE; 1517 - pNode->iVal = (u8)(i+1); 1614 + pNode->u.iReplace = i + 1; 1518 1615 } 1519 1616 } 1520 1617 if( x.aNode[0].jnFlags & JNODE_REPLACE ){ 1521 - sqlite3_result_value(ctx, argv[x.aNode[0].iVal]); 1618 + sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]); 1522 1619 }else{ 1523 1620 jsonReturnJson(x.aNode, ctx, argv); 1524 1621 } 1525 1622 jsonSetDone: 1526 1623 jsonParseReset(&x); 1527 1624 } 1528 1625 ................................................................................ 2156 2253 } aFunc[] = { 2157 2254 { "json", 1, 0, jsonRemoveFunc }, 2158 2255 { "json_array", -1, 0, jsonArrayFunc }, 2159 2256 { "json_array_length", 1, 0, jsonArrayLengthFunc }, 2160 2257 { "json_array_length", 2, 0, jsonArrayLengthFunc }, 2161 2258 { "json_extract", -1, 0, jsonExtractFunc }, 2162 2259 { "json_insert", -1, 0, jsonSetFunc }, 2260 + { "json_mergepatch", 2, 0, jsonMergePatchFunc }, 2163 2261 { "json_object", -1, 0, jsonObjectFunc }, 2164 2262 { "json_quote", 1, 0, jsonQuoteFunc }, 2165 2263 { "json_remove", -1, 0, jsonRemoveFunc }, 2166 2264 { "json_replace", -1, 0, jsonReplaceFunc }, 2167 2265 { "json_set", -1, 1, jsonSetFunc }, 2168 2266 { "json_type", 1, 0, jsonTypeFunc }, 2169 2267 { "json_type", 2, 0, jsonTypeFunc },
Added test/json104.test.
1 +# 2017-03-22 2 +# 3 +# The author disclaims copyright to this source code. In place of 4 +# a legal notice, here is a blessing: 5 +# 6 +# May you do good and not evil. 7 +# May you find forgiveness for yourself and forgive others. 8 +# May you share freely, never taking more than you give. 9 +# 10 +#*********************************************************************** 11 +# This file implements tests for json_mergepatch(A,B) SQL function. 12 +# 13 + 14 +set testdir [file dirname $argv0] 15 +source $testdir/tester.tcl 16 + 17 +ifcapable !json1 { 18 + finish_test 19 + return 20 +} 21 + 22 +# This is the example from pages 2 and 3 of RFC-7396 23 +do_execsql_test json104-100 { 24 + SELECT json_mergepatch( 25 + json('{ 26 + "a": "b", 27 + "c": { 28 + "d": "e", 29 + "f": "g" 30 + } 31 + }'), 32 + json('{ 33 + "a":"z", 34 + "c": { 35 + "f": null 36 + } 37 + }')); 38 +} {{{"a":"z","c":{"d":"e"}}}} 39 + 40 + 41 +# This is the example from pages 4 and 5 of RFC-7396 42 +do_execsql_test json104-110 { 43 + SELECT json_mergepatch( 44 + json('{ 45 + "title": "Goodbye!", 46 + "author" : { 47 + "givenName" : "John", 48 + "familyName" : "Doe" 49 + }, 50 + "tags":[ "example", "sample" ], 51 + "content": "This will be unchanged" 52 + }'), 53 + json('{ 54 + "title": "Hello!", 55 + "phoneNumber": "+01-123-456-7890", 56 + "author": { 57 + "familyName": null 58 + }, 59 + "tags": [ "example" ] 60 + }')); 61 +} {{{"title":"Hello!","author":{"givenName":"John"},"tags":["example"],"content":"This will be unchanged",phoneNumber:"+01-123-456-7890"}}} 62 + 63 +finish_test