Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit a1f1013

Browse files
author
Nikita Glukhov
committed
Add "id" field to the output of jsonpath .keyvalue() method
1 parent c9fe600 commit a1f1013

File tree

2 files changed

+109
-45
lines changed

2 files changed

+109
-45
lines changed

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 94 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,18 @@
2727
/* Special pseudo-ErrorData with zero sqlerrcode for existence queries. */
2828
ErrorData jperNotFound[1];
2929

30+
typedef struct JsonBaseObjectInfo
31+
{
32+
JsonbContainer *jbc;
33+
int id;
34+
} JsonBaseObjectInfo;
3035

3136
typedef struct JsonPathExecContext
3237
{
3338
List *vars;
3439
JsonbValue *root; /* for $ evaluation */
40+
JsonBaseObjectInfo baseObject; /* for .keyvalue().id evaluation */
41+
int generatedObjectId;
3542
int innermostArraySize; /* for LAST array index evaluation */
3643
bool laxMode;
3744
bool ignoreStructuralErrors;
@@ -155,7 +162,7 @@ JsonbWrapInBinary(JsonbValue *jbv, JsonbValue *out)
155162
/*
156163
* Find value of jsonpath variable in a list of passing params
157164
*/
158-
static void
165+
static int
159166
computeJsonPathVariable(JsonPathItem *variable, List *vars, JsonbValue *value)
160167
{
161168
ListCell *cell;
@@ -164,6 +171,7 @@ computeJsonPathVariable(JsonPathItem *variable, List *vars, JsonbValue *value)
164171
Datum computedValue;
165172
char *varName;
166173
int varNameLength;
174+
int varId = 1;
167175

168176
Assert(variable->type == jpiVariable);
169177
varName = jspGetString(variable, &varNameLength);
@@ -177,6 +185,7 @@ computeJsonPathVariable(JsonPathItem *variable, List *vars, JsonbValue *value)
177185
break;
178186

179187
var = NULL;
188+
varId++;
180189
}
181190

182191
if (var == NULL)
@@ -190,7 +199,7 @@ computeJsonPathVariable(JsonPathItem *variable, List *vars, JsonbValue *value)
190199
if (isNull)
191200
{
192201
value->type = jbvNull;
193-
return;
202+
return varId;
194203
}
195204

196205
switch (var->typid)
@@ -264,12 +273,14 @@ computeJsonPathVariable(JsonPathItem *variable, List *vars, JsonbValue *value)
264273
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
265274
errmsg("only bool, numeric and text types could be casted to supported jsonpath types")));
266275
}
276+
277+
return varId;
267278
}
268279

269280
/*
270281
* Convert jsonpath's scalar or variable node to actual jsonb value
271282
*/
272-
static void
283+
static int
273284
computeJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item, JsonbValue *value)
274285
{
275286
switch(item->type)
@@ -290,11 +301,12 @@ computeJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item, JsonbValue *va
290301
value->val.string.val = jspGetString(item, &value->val.string.len);
291302
break;
292303
case jpiVariable:
293-
computeJsonPathVariable(item, cxt->vars, value);
294-
break;
304+
return computeJsonPathVariable(item, cxt->vars, value);
295305
default:
296306
elog(ERROR, "Wrong type");
297307
}
308+
309+
return 0;
298310
}
299311

300312

@@ -1397,6 +1409,44 @@ recursiveExecuteBool(JsonPathExecContext *cxt, JsonPathItem *jsp,
13971409
}
13981410
}
13991411

1412+
static inline JsonPathExecResult
1413+
recursiveExecuteBase(JsonPathExecContext *cxt, JsonPathItem *jsp,
1414+
JsonbValue *jbv, JsonValueList *found)
1415+
{
1416+
JsonbValue *v;
1417+
JsonbValue vbuf;
1418+
bool copy = true;
1419+
1420+
if (JsonbType(jbv) == jbvScalar)
1421+
{
1422+
if (jspHasNext(jsp))
1423+
v = &vbuf;
1424+
else
1425+
{
1426+
v = palloc(sizeof(*v));
1427+
copy = false;
1428+
}
1429+
1430+
JsonbExtractScalar(jbv->val.binary.data, v);
1431+
}
1432+
else
1433+
v = jbv;
1434+
1435+
return recursiveExecuteNext(cxt, jsp, NULL, v, found, copy);
1436+
}
1437+
1438+
static inline JsonBaseObjectInfo
1439+
setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
1440+
{
1441+
JsonBaseObjectInfo baseObject = cxt->baseObject;
1442+
1443+
cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
1444+
(JsonbContainer *) jbv->val.binary.data;
1445+
cxt->baseObject.id = id;
1446+
1447+
return baseObject;
1448+
}
1449+
14001450
/*
14011451
* Main executor function: walks on jsonpath structure and tries to find
14021452
* correspoding parts of jsonb. Note, jsonb and jsonpath values should be
@@ -1414,6 +1464,7 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
14141464
JsonPathItem elem;
14151465
JsonPathExecResult res = jperNotFound;
14161466
bool hasNext;
1467+
JsonBaseObjectInfo baseObject;
14171468

14181469
check_stack_depth();
14191470
CHECK_FOR_INTERRUPTS();
@@ -1474,33 +1525,18 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
14741525
res = jperMakeError(ERRCODE_JSON_MEMBER_NOT_FOUND);
14751526
}
14761527
break;
1528+
14771529
case jpiRoot:
14781530
jb = cxt->root;
1479-
/* fall through */
1480-
case jpiCurrent:
1481-
{
1482-
JsonbValue *v;
1483-
JsonbValue vbuf;
1484-
bool copy = true;
1485-
1486-
if (JsonbType(jb) == jbvScalar)
1487-
{
1488-
if (jspHasNext(jsp))
1489-
v = &vbuf;
1490-
else
1491-
{
1492-
v = palloc(sizeof(*v));
1493-
copy = false;
1494-
}
1531+
baseObject = setBaseObject(cxt, jb, 0);
1532+
res = recursiveExecuteBase(cxt, jsp, jb, found);
1533+
cxt->baseObject = baseObject;
1534+
break;
14951535

1496-
JsonbExtractScalar(jb->val.binary.data, v);
1497-
}
1498-
else
1499-
v = jb;
1536+
case jpiCurrent:
1537+
res = recursiveExecuteBase(cxt, jsp, jb, found);
1538+
break;
15001539

1501-
res = recursiveExecuteNext(cxt, jsp, NULL, v, found, copy);
1502-
break;
1503-
}
15041540
case jpiAnyArray:
15051541
if (JsonbType(jb) == jbvArray)
15061542
{
@@ -1762,6 +1798,7 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
17621798
JsonbValue vbuf;
17631799
JsonbValue *v;
17641800
bool hasNext = jspGetNext(jsp, &elem);
1801+
int id;
17651802

17661803
if (!hasNext && !found)
17671804
{
@@ -1771,9 +1808,11 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
17711808

17721809
v = hasNext ? &vbuf : palloc(sizeof(*v));
17731810

1774-
computeJsonPathItem(cxt, jsp, v);
1811+
id = computeJsonPathItem(cxt, jsp, v);
17751812

1813+
baseObject = setBaseObject(cxt, v, id);
17761814
res = recursiveExecuteNext(cxt, jsp, &elem, v, found, hasNext);
1815+
cxt->baseObject = baseObject;
17771816
}
17781817
break;
17791818
case jpiType:
@@ -2026,11 +2065,14 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
20262065
JsonbValue bin;
20272066
JsonbValue key;
20282067
JsonbValue val;
2068+
JsonbValue idval;
20292069
JsonbValue obj;
20302070
JsonbValue keystr;
20312071
JsonbValue valstr;
2072+
JsonbValue idstr;
20322073
JsonbIterator *it;
20332074
JsonbParseState *ps = NULL;
2075+
int64 id;
20342076

20352077
hasNext = jspGetNext(jsp, &elem);
20362078

@@ -2053,9 +2095,21 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
20532095
valstr.val.string.val = "value";
20542096
valstr.val.string.len = 5;
20552097

2098+
idstr.type = jbvString;
2099+
idstr.val.string.val = "id";
2100+
idstr.val.string.len = 2;
2101+
20562102
if (jb->type == jbvObject)
20572103
jb = JsonbWrapInBinary(jb, &bin);
20582104

2105+
id = jb->type != jbvBinary ? 0 :
2106+
(int64)((char *) jb->val.binary.data -
2107+
(char *) cxt->baseObject.jbc);
2108+
id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
2109+
2110+
idval.type = jbvNumeric;
2111+
idval.val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric, Int64GetDatum(id)));
2112+
20592113
it = JsonbIteratorInit(jb->val.binary.data);
20602114

20612115
while ((r = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
@@ -2078,18 +2132,25 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
20782132
pushJsonbValue(&ps, WJB_KEY, &keystr);
20792133
pushJsonbValue(&ps, WJB_VALUE, &key);
20802134

2081-
20822135
pushJsonbValue(&ps, WJB_KEY, &valstr);
20832136
pushJsonbValue(&ps, WJB_VALUE, &val);
20842137

2138+
pushJsonbValue(&ps, WJB_KEY, &idstr);
2139+
pushJsonbValue(&ps, WJB_VALUE, &idval);
2140+
20852141
keyval = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
20862142

20872143
jsonb = JsonbValueToJsonb(keyval);
20882144

20892145
JsonbInitBinary(&obj, jsonb);
20902146

2147+
baseObject = setBaseObject(cxt, &obj,
2148+
cxt->generatedObjectId++);
2149+
20912150
res = recursiveExecuteNext(cxt, jsp, &elem, &obj, found, true);
20922151

2152+
cxt->baseObject = baseObject;
2153+
20932154
if (jperIsError(res))
20942155
break;
20952156

@@ -2254,6 +2315,9 @@ executeJsonPath(JsonPath *path, List *vars, Jsonb *json, JsonValueList *foundJso
22542315
cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
22552316
cxt.ignoreStructuralErrors = cxt.laxMode;
22562317
cxt.root = JsonbInitBinary(&jbv, json);
2318+
cxt.baseObject.jbc = NULL;
2319+
cxt.baseObject.id = 0;
2320+
cxt.generatedObjectId = list_length(vars) + 1;
22572321
cxt.innermostArraySize = -1;
22582322

22592323
if (jspStrictAbsenseOfErrors(&cxt) && !foundJson)

src/test/regress/expected/jsonb_jsonpath.out

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,29 +1060,29 @@ select jsonb '{}' @* '$.keyvalue()';
10601060
(0 rows)
10611061

10621062
select jsonb '{"a": 1, "b": [1, 2], "c": {"a": "bbb"}}' @* '$.keyvalue()';
1063-
?column?
1064-
-------------------------------------
1065-
{"key": "a", "value": 1}
1066-
{"key": "b", "value": [1, 2]}
1067-
{"key": "c", "value": {"a": "bbb"}}
1063+
?column?
1064+
----------------------------------------------
1065+
{"id": 0, "key": "a", "value": 1}
1066+
{"id": 0, "key": "b", "value": [1, 2]}
1067+
{"id": 0, "key": "c", "value": {"a": "bbb"}}
10681068
(3 rows)
10691069

10701070
select jsonb '[{"a": 1, "b": [1, 2]}, {"c": {"a": "bbb"}}]' @* '$[*].keyvalue()';
1071-
?column?
1072-
-------------------------------------
1073-
{"key": "a", "value": 1}
1074-
{"key": "b", "value": [1, 2]}
1075-
{"key": "c", "value": {"a": "bbb"}}
1071+
?column?
1072+
-----------------------------------------------
1073+
{"id": 12, "key": "a", "value": 1}
1074+
{"id": 12, "key": "b", "value": [1, 2]}
1075+
{"id": 72, "key": "c", "value": {"a": "bbb"}}
10761076
(3 rows)
10771077

10781078
select jsonb '[{"a": 1, "b": [1, 2]}, {"c": {"a": "bbb"}}]' @* 'strict $.keyvalue()';
10791079
ERROR: SQL/JSON object not found
10801080
select jsonb '[{"a": 1, "b": [1, 2]}, {"c": {"a": "bbb"}}]' @* 'lax $.keyvalue()';
1081-
?column?
1082-
-------------------------------------
1083-
{"key": "a", "value": 1}
1084-
{"key": "b", "value": [1, 2]}
1085-
{"key": "c", "value": {"a": "bbb"}}
1081+
?column?
1082+
-----------------------------------------------
1083+
{"id": 12, "key": "a", "value": 1}
1084+
{"id": 12, "key": "b", "value": [1, 2]}
1085+
{"id": 72, "key": "c", "value": {"a": "bbb"}}
10861086
(3 rows)
10871087

10881088
select jsonb 'null' @* '$.double()';

0 commit comments

Comments
 (0)