#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
u32 j;
int iThis;
int x;
+ JsonNode *pNode;
while( isspace(pParse->zJson[i]) ){ i++; }
if( (c = pParse->zJson[i])==0 ) return 0;
if( c=='{' ){
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;
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);
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 */
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;
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);
}
}
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;
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;
}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;
}
--- /dev/null
+# 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