]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix the json_tree() scan for the case when a path is supplied. Add new
authordrh <drh@noemail.net>
Thu, 10 Sep 2015 03:29:11 +0000 (03:29 +0000)
committerdrh <drh@noemail.net>
Thu, 10 Sep 2015 03:29:11 +0000 (03:29 +0000)
json1 test cases.

FossilOrigin-Name: 6adc7de76acee6cfb5ff761739e7a8de7b5bf4b2

ext/misc/json1.c
manifest
manifest.uuid
test/json102.test [new file with mode: 0644]

index 5df7551decbba9fa94eda82ad0bca1b0e2b8ed9a..d51d69e1eeac7509dbfb123a9a9b14bf82dc226e 100644 (file)
@@ -83,6 +83,7 @@ static const char * const jsonType[] = {
 #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 */
 
 
 /* A single node of parsed JSON
@@ -583,6 +584,7 @@ static int jsonParseValue(JsonParse *pParse, u32 i){
   u32 j;
   int iThis;
   int x;
+  JsonNode *pNode;
   while( isspace(pParse->zJson[i]) ){ i++; }
   if( (c = pParse->zJson[i])==0 ) return 0;
   if( c=='{' ){
@@ -597,7 +599,9 @@ static int jsonParseValue(JsonParse *pParse, u32 i){
         return -1;
       }
       if( pParse->oom ) return -1;
-      if( pParse->aNode[pParse->nNode-1].eType!=JSON_STRING ) return -1;
+      pNode = &pParse->aNode[pParse->nNode-1];
+      if( pNode->eType!=JSON_STRING ) return -1;
+      pNode->jnFlags |= JNODE_LABEL;
       j = x;
       while( isspace(pParse->zJson[j]) ){ j++; }
       if( pParse->zJson[j]!=':' ) return -1;
@@ -1036,13 +1040,20 @@ static void jsonParseFunc(
   jsonParseFindParents(&x);
   jsonInit(&s, ctx);
   for(i=0; i<x.nNode; i++){
-    jsonPrintf(100, &s,"node %3u: %7s n=%-4d up=%d\n",
-               i, jsonType[x.aNode[i].eType], x.aNode[i].n, x.aUp[i]);
+    const char *zType;
+    if( x.aNode[i].jnFlags & JNODE_LABEL ){
+      assert( x.aNode[i].eType==JSON_STRING );
+      zType = "label";
+    }else{
+      zType = jsonType[x.aNode[i].eType];
+    }
+    jsonPrintf(100, &s,"node %3u: %7s n=%-4d up=%-4d",
+               i, zType, x.aNode[i].n, x.aUp[i]);
     if( x.aNode[i].u.zJContent!=0 ){
-      jsonAppendRaw(&s, "    text: ", 10);
+      jsonAppendRaw(&s, " ", 1);
       jsonAppendRaw(&s, x.aNode[i].u.zJContent, x.aNode[i].n);
-      jsonAppendRaw(&s, "\n", 1);
     }
+    jsonAppendRaw(&s, "\n", 1);
   }
   jsonParseReset(&x);
   jsonResult(&s);
@@ -1427,6 +1438,7 @@ typedef struct JsonEachCursor JsonEachCursor;
 struct JsonEachCursor {
   sqlite3_vtab_cursor base;  /* Base class - must be first */
   u32 iRowid;                /* The rowid */
+  u32 iBegin;                /* The first node of the scan */
   u32 i;                     /* Index in sParse.aNode[] of current row */
   u32 iEnd;                  /* EOF when i equals or exceeds this value */
   u8 eType;                  /* Type of top-level element */
@@ -1534,16 +1546,10 @@ static int jsonEachEof(sqlite3_vtab_cursor *cur){
 static int jsonEachNext(sqlite3_vtab_cursor *cur){
   JsonEachCursor *p = (JsonEachCursor*)cur;
   if( p->bRecursive ){
-    if( p->i==0 ){
-      p->i = 1;
-    }else{
-      u32 iUp = p->sParse.aUp[p->i];
-      JsonNode *pUp = &p->sParse.aNode[iUp];
-      p->i++;
-      if( pUp->eType==JSON_OBJECT && (pUp->n + iUp >= p->i) ) p->i++;
-    }
+    if( p->sParse.aNode[p->i].jnFlags & JNODE_LABEL ) p->i++;
+    p->i++;
     p->iRowid++;
-    if( p->i<p->sParse.nNode ){
+    if( p->i<p->iEnd ){
       u32 iUp = p->sParse.aUp[p->i];
       JsonNode *pUp = &p->sParse.aNode[iUp];
       p->eType = pUp->eType;
@@ -1597,8 +1603,9 @@ static void jsonEachComputePath(
     jsonPrintf(30, pStr, "[%d]", pUp->u.iKey);
   }else{
     assert( pUp->eType==JSON_OBJECT );
-    if( pNode->eType>=JSON_ARRAY ) pNode--;
+    if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--;
     assert( pNode->eType==JSON_STRING );
+    assert( pNode->jnFlags & JNODE_LABEL );
     jsonPrintf(pNode->n+1, pStr, ".%.*s", pNode->n-2, pNode->u.zJContent+1);
   }
 }
@@ -1629,27 +1636,28 @@ static int jsonEachColumn(
       break;
     }
     case JEACH_VALUE: {
-      if( p->eType==JSON_OBJECT && p->i>0 ) pThis++;
+      if( pThis->jnFlags & JNODE_LABEL ) pThis++;
       jsonReturn(pThis, ctx, 0);
       break;
     }
     case JEACH_TYPE: {
-      if( p->eType==JSON_OBJECT && p->i>0 ) pThis++;
+      if( pThis->jnFlags & JNODE_LABEL ) pThis++;
       sqlite3_result_text(ctx, jsonType[pThis->eType], -1, SQLITE_STATIC);
       break;
     }
     case JEACH_ATOM: {
-      if( p->eType==JSON_OBJECT && p->i>0 ) pThis++;
+      if( pThis->jnFlags & JNODE_LABEL ) pThis++;
       if( pThis->eType>=JSON_ARRAY ) break;
       jsonReturn(pThis, ctx, 0);
       break;
     }
     case JEACH_ID: {
-      sqlite3_result_int64(ctx, (sqlite3_int64)p->i + (p->eType==JSON_OBJECT));
+      sqlite3_result_int64(ctx, 
+         (sqlite3_int64)p->i + ((pThis->jnFlags & JNODE_LABEL)!=0));
       break;
     }
     case JEACH_PARENT: {
-      if( p->i>0 && p->bRecursive ){
+      if( p->i>p->iBegin && p->bRecursive ){
         sqlite3_result_int64(ctx, (sqlite3_int64)p->sParse.aUp[p->i]);
       }
       break;
@@ -1794,7 +1802,6 @@ static int jsonEachFilter(
     JsonNode *pNode;
     if( idxNum==3 ){
       const char *zErr = 0;
-      p->bRecursive = 0;
       n = sqlite3_value_bytes(argv[1]);
       p->zPath = sqlite3_malloc64( n+1 );
       if( p->zPath==0 ) return SQLITE_NOMEM;
@@ -1811,12 +1818,18 @@ static int jsonEachFilter(
     }else{
       pNode = p->sParse.aNode;
     }
-    p->i = (int)(pNode - p->sParse.aNode);
+    p->iBegin = p->i = (int)(pNode - p->sParse.aNode);
     p->eType = pNode->eType;
     if( p->eType>=JSON_ARRAY ){
       pNode->u.iKey = 0;
       p->iEnd = p->i + pNode->n + 1;
-      if( !p->bRecursive ) p->i++;
+      if( p->bRecursive ){
+        if( p->i>0 && (p->sParse.aNode[p->i-1].jnFlags & JNODE_LABEL)!=0 ){
+          p->i--;
+        }
+      }else{
+        p->i++;
+      }
     }else{
       p->iEnd = p->i+1;
     }
index 19012fac78412f76427e47a198a627e19d9fcc1d..7b61a44b5a832799474d9951acef527e26d1c3e7 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C No-op\sthe\ssqlite3_memory_alarm()\sinterface\sin\sa\sdifferent\sway,\sthat\sdoes\nnot\sbreak\slegacy\smemory\sbehavior.\s\sThis\sis\sa\sre-do\sof\ncheck-in\s[5d3f5df4da9f40d5].
-D 2015-09-10T01:22:09.178
+C Fix\sthe\sjson_tree()\sscan\sfor\sthe\scase\swhen\sa\spath\sis\ssupplied.\s\sAdd\snew\njson1\stest\scases.
+D 2015-09-10T03:29:11.778
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in f85066ce844a28b671aaeeff320921cd0ce36239
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -194,7 +194,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/json1.c bd51e8c1e8ce580e6f21493bd8e94ed5aca3d777
+F ext/misc/json1.c 46c2aff110eb8241591cd29267ddb57a8d0ab337
 F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342
 F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63
 F ext/misc/regexp.c af92cdaa5058fcec1451e49becc7ba44dba023dc
@@ -814,6 +814,7 @@ F test/jrnlmode.test 7864d59cf7f6e552b9b99ba0f38acd167edc10fa
 F test/jrnlmode2.test 81610545a4e6ed239ea8fa661891893385e23a1d
 F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa
 F test/json101.test 11535d8986184500f4c30cc2f0b154b4ab05cc4e
+F test/json102.test ab2ea59639ecf2ad95101015c3357b1ba53ebd98
 F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff
 F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63
 F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200
@@ -1383,7 +1384,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 86146a731d75eb25279c0e072c0bdda593de905d
-R 7c5553c63d5721a9d16f1642ebb06bea
+P 8250e2a487ee12c9a2dea5603ab60aed51e5dc7b
+R c3aaad669751ca08269190cf668389ec
 U drh
-Z 0fce0dbaade5235ccf22a67355771bfc
+Z cc385c8c27ee83d7a7141ac119ad0322
index 5a207b3806a1e6b195c69b09fe1e6c8a4841955d..fe04945ac4df387900783ed607ee796a2d697b41 100644 (file)
@@ -1 +1 @@
-8250e2a487ee12c9a2dea5603ab60aed51e5dc7b
\ No newline at end of file
+6adc7de76acee6cfb5ff761739e7a8de7b5bf4b2
\ No newline at end of file
diff --git a/test/json102.test b/test/json102.test
new file mode 100644 (file)
index 0000000..4ef1feb
--- /dev/null
@@ -0,0 +1,238 @@
+# 2015-08-12
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements tests for JSON SQL functions extension to the
+# SQLite library.
+#
+# This file contains tests automatically generated from the json1
+# documentation.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+load_static_extension db json
+do_execsql_test json102-100 {
+  SELECT json_array(1,2,'3',4);
+} {{[1,2,"3",4]}}
+do_execsql_test json102-110 {
+  SELECT json_array('[1,2]');
+} {{["[1,2]"]}}
+do_execsql_test json102-120 {
+  SELECT json_array(1,null,'3','[4,5]','{"six":7.7}');
+} {{[1,null,"3","[4,5]","{\"six\":7.7}"]}}
+do_execsql_test json102-130 {
+  SELECT json_array_length('[1,2,3,4]');
+} {{4}}
+do_execsql_test json102-140 {
+  SELECT json_array_length('{"one":[1,2,3]}');
+} {{0}}
+do_execsql_test json102-150 {
+  SELECT json_array_length('{"one":[1,2,3]}', '$.one');
+} {{3}}
+do_execsql_test json102-160 {
+  SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$');
+} {{{"a":2,"c":[4,5,{"f":7}]}}}
+do_execsql_test json102-170 {
+  SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.c');
+} {{[4,5,{"f":7}]}}
+do_execsql_test json102-180 {
+  SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.c[2]');
+} {{{"f":7}}}
+do_execsql_test json102-190 {
+  SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.c[2].f');
+} {{7}}
+do_execsql_test json102-200 {
+  SELECT json_extract('{"a":2,"c":[4,5],"f":7}','$.c','$.a');
+} {{[[4,5],2]}}
+do_execsql_test json102-210 {
+  SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.x');
+} {{}}
+do_execsql_test json102-220 {
+  SELECT json_insert('{"a":2,"c":4}', '$.a', 99);
+} {{{"a":2,"c":4}}}
+do_execsql_test json102-230 {
+  SELECT json_insert('{"a":2,"c":4}', '$.e', 99);
+} {{{"a":2,"c":4,"e":99}}}
+do_execsql_test json102-240 {
+  SELECT json_replace('{"a":2,"c":4}', '$.a', 99);
+} {{{"a":99,"c":4}}}
+do_execsql_test json102-250 {
+  SELECT json_replace('{"a":2,"c":4}', '$.e', 99);
+} {{{"a":2,"c":4}}}
+do_execsql_test json102-260 {
+  SELECT json_set('{"a":2,"c":4}', '$.a', 99);
+} {{{"a":99,"c":4}}}
+do_execsql_test json102-270 {
+  SELECT json_set('{"a":2,"c":4}', '$.e', 99);
+} {{{"a":2,"c":4,"e":99}}}
+do_execsql_test json102-280 {
+  SELECT json_object('a',2,'c',4);
+} {{{"a":2,"c":4}}}
+do_execsql_test json102-290 {
+  SELECT json_object('a',2,'c','{e:5}');
+} {{{"a":2,"c":"{e:5}"}}}
+do_execsql_test json102-300 {
+  SELECT json_remove('[0,1,2,3,4]','$[2]');
+} {{[0,1,3,4]}}
+do_execsql_test json102-310 {
+  SELECT json_remove('[0,1,2,3,4]','$[2]','$[0]');
+} {{[1,3,4]}}
+do_execsql_test json102-320 {
+  SELECT json_remove('[0,1,2,3,4]','$[0]','$[2]');
+} {{[1,2,4]}}
+do_execsql_test json102-330 {
+  SELECT json_remove('{"x":25,"y":42}');
+} {{{"x":25,"y":42}}}
+do_execsql_test json102-340 {
+  SELECT json_remove('{"x":25,"y":42}','$.z');
+} {{{"x":25,"y":42}}}
+do_execsql_test json102-350 {
+  SELECT json_remove('{"x":25,"y":42}','$.y');
+} {{{"x":25}}}
+do_execsql_test json102-360 {
+  SELECT json_remove('{"x":25,"y":42}','$');
+} {{}}
+do_execsql_test json102-370 {
+  SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}');
+} {{object}}
+do_execsql_test json102-380 {
+  SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$');
+} {{object}}
+do_execsql_test json102-390 {
+  SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a');
+} {{array}}
+do_execsql_test json102-400 {
+  SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[0]');
+} {{integer}}
+do_execsql_test json102-410 {
+  SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[1]');
+} {{real}}
+do_execsql_test json102-420 {
+  SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[2]');
+} {{true}}
+do_execsql_test json102-430 {
+  SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[3]');
+} {{false}}
+do_execsql_test json102-440 {
+  SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[4]');
+} {{null}}
+do_execsql_test json102-450 {
+  SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[5]');
+} {{text}}
+do_execsql_test json102-460 {
+  SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[6]');
+} {{}}
+do_execsql_test json102-470 {
+  SELECT json_valid('{"x":35}');
+} {{1}}
+do_execsql_test json102-480 {
+  SELECT json_valid('{"x":35'); -- }
+} {{0}}
+
+do_execsql_test json102-500 {
+  CREATE TABLE user(name,phone);
+  INSERT INTO user(name,phone) VALUES
+     ('Alice','["919-555-2345","804-555-3621"]'),
+     ('Bob','["201-555-8872"]'),
+     ('Cindy','["704-555-9983"]'),
+     ('Dave','["336-555-8421","704-555-4321","803-911-4421"]');
+  SELECT DISTINCT user.name
+    FROM user, json_each(user.phone)
+   WHERE json_each.value LIKE '704-%'
+   ORDER BY 1;
+} {Cindy Dave}
+
+do_execsql_test json102-510 {
+  UPDATE user
+     SET phone=json_extract(phone,'$[0]')
+   WHERE json_array_length(phone)<2;
+  SELECT name, substr(phone,1,5) FROM user ORDER BY name;
+} {Alice {["919} Bob 201-5 Cindy 704-5 Dave {["336}}
+do_execsql_test json102-511 {
+  SELECT name FROM user WHERE phone LIKE '704-%'
+  UNION
+  SELECT user.name
+    FROM user, json_each(user.phone)
+   WHERE json_valid(user.phone)
+     AND json_each.value LIKE '704-%';
+} {Cindy Dave}
+
+do_execsql_test json102-600 {
+  CREATE TABLE big(json JSON);
+  INSERT INTO big(json) VALUES('{
+    "id":123,
+    "stuff":[1,2,3,4],
+    "partlist":[
+       {"uuid":"bb108722-572e-11e5-9320-7f3b63a4ca74"},
+       {"uuid":"c690dc14-572e-11e5-95f9-dfc8861fd535"},
+       {"subassembly":[
+          {"uuid":"6fa5181e-5721-11e5-a04e-57f3d7b32808"}
+       ]}
+    ]
+  }');
+  INSERT INTO big(json) VALUES('{
+    "id":456,
+    "stuff":["hello","world","xyzzy"],
+    "partlist":[
+       {"uuid":false},
+       {"uuid":"c690dc14-572e-11e5-95f9-dfc8861fd535"}
+    ]
+  }');
+} {}
+set correct_answer [list \
+    1 {$.id} 123 \
+    1 {$.stuff[0]} 1 \
+    1 {$.stuff[1]} 2 \
+    1 {$.stuff[2]} 3 \
+    1 {$.stuff[3]} 4 \
+    1 {$.partlist[0].uuid} bb108722-572e-11e5-9320-7f3b63a4ca74 \
+    1 {$.partlist[1].uuid} c690dc14-572e-11e5-95f9-dfc8861fd535 \
+    1 {$.partlist[2].subassembly[0].uuid} 6fa5181e-5721-11e5-a04e-57f3d7b32808 \
+    2 {$.id} 456 \
+    2 {$.stuff[0]} hello \
+    2 {$.stuff[1]} world \
+    2 {$.stuff[2]} xyzzy \
+    2 {$.partlist[0].uuid} 0 \
+    2 {$.partlist[1].uuid} c690dc14-572e-11e5-95f9-dfc8861fd535]
+do_execsql_test json102-610 {
+  SELECT big.rowid, fullkey, value
+    FROM big, json_tree(big.json)
+   WHERE json_tree.type NOT IN ('object','array')
+   ORDER BY +big.rowid, +json_tree.id
+} $correct_answer
+do_execsql_test json102-620 {
+  SELECT big.rowid, fullkey, atom
+    FROM big, json_tree(big.json)
+   WHERE atom IS NOT NULL
+   ORDER BY +big.rowid, +json_tree.id
+} $correct_answer
+
+do_execsql_test json102-630 {
+  SELECT DISTINCT json_extract(big.json,'$.id')
+    FROM big, json_tree(big.json,'$.partlist')
+   WHERE json_tree.key='uuid'
+     AND json_tree.value='6fa5181e-5721-11e5-a04e-57f3d7b32808';
+} {123}
+do_execsql_test json102-631 {
+  SELECT DISTINCT json_extract(big.json,'$.id')
+    FROM big, json_tree(big.json,'$')
+   WHERE json_tree.key='uuid'
+     AND json_tree.value='6fa5181e-5721-11e5-a04e-57f3d7b32808';
+} {123}
+do_execsql_test json102-632 {
+  SELECT DISTINCT json_extract(big.json,'$.id')
+    FROM big, json_tree(big.json)
+   WHERE json_tree.key='uuid'
+     AND json_tree.value='6fa5181e-5721-11e5-a04e-57f3d7b32808';
+} {123}
+
+
+finish_test