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

Commit 61a1eef

Browse files
author
Nikita Glukhov
committed
Add jsonpath array index expressions
1 parent 47646a9 commit 61a1eef

File tree

8 files changed

+280
-81
lines changed

8 files changed

+280
-81
lines changed

src/backend/utils/adt/jsonpath.c

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -130,13 +130,39 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
130130
errmsg("@ is not allowed in root expressions")));
131131
break;
132132
case jpiIndexArray:
133-
appendBinaryStringInfo(buf,
134-
(char*)&item->value.array.nelems,
135-
sizeof(item->value.array.nelems));
136-
appendBinaryStringInfo(buf,
137-
(char*)item->value.array.elems,
138-
item->value.array.nelems *
139-
sizeof(item->value.array.elems[0]));
133+
{
134+
int32 nelems = item->value.array.nelems;
135+
int offset;
136+
int i;
137+
138+
appendBinaryStringInfo(buf, (char *) &nelems, sizeof(nelems));
139+
140+
offset = buf->len;
141+
142+
appendStringInfoSpaces(buf, sizeof(int32) * 2 * nelems);
143+
144+
for (i = 0; i < nelems; i++)
145+
{
146+
int32 *ppos;
147+
int32 topos;
148+
int32 frompos =
149+
flattenJsonPathParseItem(buf,
150+
item->value.array.elems[i].from,
151+
true);
152+
153+
if (item->value.array.elems[i].to)
154+
topos = flattenJsonPathParseItem(buf,
155+
item->value.array.elems[i].to,
156+
true);
157+
else
158+
topos = 0;
159+
160+
ppos = (int32 *) &buf->data[offset + i * 2 * sizeof(int32)];
161+
162+
ppos[0] = frompos;
163+
ppos[1] = topos;
164+
}
165+
}
140166
break;
141167
case jpiAny:
142168
appendBinaryStringInfo(buf,
@@ -380,11 +406,22 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey, bool printBracket
380406
break;
381407
case jpiIndexArray:
382408
appendStringInfoChar(buf, '[');
383-
for(i = 0; i< v->content.array.nelems; i++)
409+
for (i = 0; i < v->content.array.nelems; i++)
384410
{
411+
JsonPathItem from;
412+
JsonPathItem to;
413+
bool range = jspGetArraySubscript(v, &from, &to, i);
414+
385415
if (i)
386416
appendStringInfoChar(buf, ',');
387-
appendStringInfo(buf, "%d", v->content.array.elems[i]);
417+
418+
printJsonPathItem(buf, &from, false, false);
419+
420+
if (range)
421+
{
422+
appendBinaryStringInfo(buf, " to ", 4);
423+
printJsonPathItem(buf, &to, false, false);
424+
}
388425
}
389426
appendStringInfoChar(buf, ']');
390427
break;
@@ -473,7 +510,7 @@ jsonpath_out(PG_FUNCTION_ARGS)
473510
} while(0) \
474511

475512
#define read_int32_n(v, b, p, n) do { \
476-
(v) = (int32*)((b) + (p)); \
513+
(v) = (void *)((b) + (p)); \
477514
(p) += sizeof(int32) * (n); \
478515
} while(0) \
479516

@@ -558,7 +595,8 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
558595
break;
559596
case jpiIndexArray:
560597
read_int32(v->content.array.nelems, base, pos);
561-
read_int32_n(v->content.array.elems, base, pos, v->content.array.nelems);
598+
read_int32_n(v->content.array.elems, base, pos,
599+
v->content.array.nelems * 2);
562600
break;
563601
case jpiAny:
564602
read_int32(v->content.anybounds.first, base, pos);
@@ -695,3 +733,19 @@ jspGetString(JsonPathItem *v, int32 *len)
695733
*len = v->content.value.datalen;
696734
return v->content.value.data;
697735
}
736+
737+
bool
738+
jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from, JsonPathItem *to,
739+
int i)
740+
{
741+
Assert(v->type == jpiIndexArray);
742+
743+
jspInitByBuffer(from, v->base, v->content.array.elems[i].from);
744+
745+
if (!v->content.array.elems[i].to)
746+
return false;
747+
748+
jspInitByBuffer(to, v->base, v->content.array.elems[i].to);
749+
750+
return true;
751+
}

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 97 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,37 @@ recursiveAny(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
716716
return res;
717717
}
718718

719+
static JsonPathExecResult
720+
getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
721+
int32 *index)
722+
{
723+
JsonbValue *jbv;
724+
List *found = NIL;
725+
JsonbValue tmp;
726+
JsonPathExecResult res = recursiveExecute(cxt, jsp, jb, &found);
727+
728+
if (jperIsError(res))
729+
return res;
730+
731+
if (list_length(found) != 1)
732+
return jperMakeError(ERRCODE_INVALID_JSON_SUBSCRIPT);
733+
734+
jbv = linitial(found);
735+
736+
if (JsonbType(jbv) == jbvScalar)
737+
jbv = JsonbExtractScalar(jbv->val.binary.data, &tmp);
738+
739+
if (jbv->type != jbvNumeric)
740+
return jperMakeError(ERRCODE_INVALID_JSON_SUBSCRIPT);
741+
742+
*index = DatumGetInt32(DirectFunctionCall1(numeric_int4,
743+
DirectFunctionCall2(numeric_trunc,
744+
NumericGetDatum(jbv->val.numeric),
745+
Int32GetDatum(0))));
746+
747+
return jperOk;
748+
}
749+
719750
/*
720751
* Main executor function: walks on jsonpath structure and tries to find
721752
* correspoding parts of jsonb. Note, jsonb and jsonpath values should be
@@ -899,39 +930,87 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
899930
case jpiIndexArray:
900931
if (JsonbType(jb) == jbvArray)
901932
{
902-
JsonbValue *v;
903-
int i;
933+
int i;
934+
int size = JsonbArraySize(jb);
904935

905936
hasNext = jspGetNext(jsp, &elem);
906937

907-
for(i=0; i<jsp->content.array.nelems; i++)
938+
for (i = 0; i < jsp->content.array.nelems; i++)
908939
{
909-
/* TODO for future: array index can be expression */
910-
v = getIthJsonbValueFromContainer(jb->val.binary.data,
911-
jsp->content.array.elems[i]);
940+
JsonPathItem from;
941+
JsonPathItem to;
942+
int32 index;
943+
int32 index_from;
944+
int32 index_to;
945+
bool range = jspGetArraySubscript(jsp, &from, &to, i);
946+
947+
res = getArrayIndex(cxt, &from, jb, &index_from);
912948

913-
if (v == NULL)
914-
continue;
949+
if (jperIsError(res))
950+
break;
915951

916-
if (hasNext == true)
952+
if (range)
917953
{
918-
res = recursiveExecute(cxt, &elem, v, found);
954+
res = getArrayIndex(cxt, &to, jb, &index_to);
919955

920-
if (jperIsError(res) || found == NULL)
956+
if (jperIsError(res))
921957
break;
922-
923-
if (res == jperOk && found == NULL)
924-
break;
925958
}
926959
else
960+
index_to = index_from;
961+
962+
if (!cxt->lax &&
963+
(index_from < 0 ||
964+
index_from > index_to ||
965+
index_to >= size))
927966
{
928-
res = jperOk;
967+
res = jperMakeError(ERRCODE_INVALID_JSON_SUBSCRIPT);
968+
break;
969+
}
929970

930-
if (found == NULL)
931-
break;
971+
if (index_from < 0)
972+
index_from = 0;
973+
974+
if (index_to >= size)
975+
index_to = size - 1;
932976

933-
*found = lappend(*found, v);
977+
res = jperNotFound;
978+
979+
for (index = index_from; index <= index_to; index++)
980+
{
981+
JsonbValue *v =
982+
getIthJsonbValueFromContainer(jb->val.binary.data,
983+
(uint32) index);
984+
985+
if (v == NULL)
986+
continue;
987+
988+
if (hasNext)
989+
{
990+
res = recursiveExecute(cxt, &elem, v, found);
991+
992+
if (jperIsError(res))
993+
break;
994+
995+
if (res == jperOk && !found)
996+
break;
997+
}
998+
else
999+
{
1000+
res = jperOk;
1001+
1002+
if (!found)
1003+
break;
1004+
1005+
*found = lappend(*found, v);
1006+
}
9341007
}
1008+
1009+
if (jperIsError(res))
1010+
break;
1011+
1012+
if (res == jperOk && !found)
1013+
break;
9351014
}
9361015
}
9371016
else

src/backend/utils/adt/jsonpath_gram.y

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,14 @@ makeIndexArray(List *list)
175175
v->value.array.elems = palloc(sizeof(v->value.array.elems[0]) * v->value.array.nelems);
176176

177177
foreach(cell, list)
178-
v->value.array.elems[i++] = lfirst_int(cell);
178+
{
179+
JsonPathParseItem *jpi = lfirst(cell);
180+
181+
Assert(jpi->type == jpiSubscript);
182+
183+
v->value.array.elems[i].from = jpi->value.args.left;
184+
v->value.array.elems[i++].to = jpi->value.args.right;
185+
}
179186

180187
return v;
181188
}
@@ -222,10 +229,11 @@ makeAny(int first, int last)
222229

223230
%type <value> scalar_value path_primary expr pexpr array_accessor
224231
any_path accessor_op key predicate delimited_predicate
232+
index_elem
225233

226234
%type <elems> accessor_expr
227235

228-
%type <indexs> index_elem index_list
236+
%type <indexs> index_list
229237

230238
%type <optype> comp_op method
231239

@@ -329,22 +337,13 @@ expr:
329337
;
330338

331339
index_elem:
332-
INT_P { $$ = list_make1_int(pg_atoi($1.val, 4, 0)); }
333-
| INT_P TO_P INT_P {
334-
int start = pg_atoi($1.val, 4, 0),
335-
stop = pg_atoi($3.val, 4, 0),
336-
i;
337-
338-
$$ = NIL;
339-
340-
for(i=start; i<= stop; i++)
341-
$$ = lappend_int($$, i);
342-
}
340+
pexpr { $$ = makeItemBinary(jpiSubscript, $1, NULL); }
341+
| pexpr TO_P pexpr { $$ = makeItemBinary(jpiSubscript, $1, $3); }
343342
;
344343

345344
index_list:
346-
index_elem { $$ = $1; }
347-
| index_list ',' index_elem { $$ = list_concat($1, $3); }
345+
index_elem { $$ = list_make1($1); }
346+
| index_list ',' index_elem { $$ = lappend($1, $3); }
348347
;
349348

350349
array_accessor:

src/include/utils/jsonpath.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ typedef enum JsonPathItemType {
7878
jpiDouble,
7979
jpiDatetime,
8080
jpiKeyValue,
81+
jpiSubscript,
8182
} JsonPathItemType;
8283

8384

@@ -114,7 +115,10 @@ typedef struct JsonPathItem {
114115
/* storage for jpiIndexArray: indexes of array */
115116
struct {
116117
int32 nelems;
117-
int32 *elems;
118+
struct {
119+
int32 from;
120+
int32 to;
121+
} *elems;
118122
} array;
119123

120124
/* jpiAny: levels */
@@ -139,6 +143,8 @@ extern void jspGetRightArg(JsonPathItem *v, JsonPathItem *a);
139143
extern Numeric jspGetNumeric(JsonPathItem *v);
140144
extern bool jspGetBool(JsonPathItem *v);
141145
extern char * jspGetString(JsonPathItem *v, int32 *len);
146+
extern bool jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from,
147+
JsonPathItem *to, int i);
142148

143149
/*
144150
* Parsing
@@ -164,7 +170,11 @@ struct JsonPathParseItem {
164170
/* storage for jpiIndexArray: indexes of array */
165171
struct {
166172
int nelems;
167-
int32 *elems;
173+
struct
174+
{
175+
JsonPathParseItem *from;
176+
JsonPathParseItem *to;
177+
} *elems;
168178
} array;
169179

170180
/* jpiAny: levels */

0 commit comments

Comments
 (0)