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

Commit 29fa653

Browse files
author
Nikita Glukhov
committed
Add jsonpath array index expressions
1 parent db40029 commit 29fa653

File tree

8 files changed

+291
-81
lines changed

8 files changed

+291
-81
lines changed

src/backend/utils/adt/jsonpath.c

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,39 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
132132
errmsg("@ is not allowed in root expressions")));
133133
break;
134134
case jpiIndexArray:
135-
appendBinaryStringInfo(buf,
136-
(char*)&item->value.array.nelems,
137-
sizeof(item->value.array.nelems));
138-
appendBinaryStringInfo(buf,
139-
(char*)item->value.array.elems,
140-
item->value.array.nelems *
141-
sizeof(item->value.array.elems[0]));
135+
{
136+
int32 nelems = item->value.array.nelems;
137+
int offset;
138+
int i;
139+
140+
appendBinaryStringInfo(buf, (char *) &nelems, sizeof(nelems));
141+
142+
offset = buf->len;
143+
144+
appendStringInfoSpaces(buf, sizeof(int32) * 2 * nelems);
145+
146+
for (i = 0; i < nelems; i++)
147+
{
148+
int32 *ppos;
149+
int32 topos;
150+
int32 frompos =
151+
flattenJsonPathParseItem(buf,
152+
item->value.array.elems[i].from,
153+
true);
154+
155+
if (item->value.array.elems[i].to)
156+
topos = flattenJsonPathParseItem(buf,
157+
item->value.array.elems[i].to,
158+
true);
159+
else
160+
topos = 0;
161+
162+
ppos = (int32 *) &buf->data[offset + i * 2 * sizeof(int32)];
163+
164+
ppos[0] = frompos;
165+
ppos[1] = topos;
166+
}
167+
}
142168
break;
143169
case jpiAny:
144170
appendBinaryStringInfo(buf,
@@ -384,11 +410,22 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey, bool printBracket
384410
break;
385411
case jpiIndexArray:
386412
appendStringInfoChar(buf, '[');
387-
for(i = 0; i< v->content.array.nelems; i++)
413+
for (i = 0; i < v->content.array.nelems; i++)
388414
{
415+
JsonPathItem from;
416+
JsonPathItem to;
417+
bool range = jspGetArraySubscript(v, &from, &to, i);
418+
389419
if (i)
390420
appendStringInfoChar(buf, ',');
391-
appendStringInfo(buf, "%d", v->content.array.elems[i]);
421+
422+
printJsonPathItem(buf, &from, false, false);
423+
424+
if (range)
425+
{
426+
appendBinaryStringInfo(buf, " to ", 4);
427+
printJsonPathItem(buf, &to, false, false);
428+
}
392429
}
393430
appendStringInfoChar(buf, ']');
394431
break;
@@ -477,7 +514,7 @@ jsonpath_out(PG_FUNCTION_ARGS)
477514
} while(0) \
478515

479516
#define read_int32_n(v, b, p, n) do { \
480-
(v) = (int32*)((b) + (p)); \
517+
(v) = (void *)((b) + (p)); \
481518
(p) += sizeof(int32) * (n); \
482519
} while(0) \
483520

@@ -562,7 +599,8 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
562599
break;
563600
case jpiIndexArray:
564601
read_int32(v->content.array.nelems, base, pos);
565-
read_int32_n(v->content.array.elems, base, pos, v->content.array.nelems);
602+
read_int32_n(v->content.array.elems, base, pos,
603+
v->content.array.nelems * 2);
566604
break;
567605
case jpiAny:
568606
read_int32(v->content.anybounds.first, base, pos);
@@ -699,3 +737,19 @@ jspGetString(JsonPathItem *v, int32 *len)
699737
*len = v->content.value.datalen;
700738
return v->content.value.data;
701739
}
740+
741+
bool
742+
jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from, JsonPathItem *to,
743+
int i)
744+
{
745+
Assert(v->type == jpiIndexArray);
746+
747+
jspInitByBuffer(from, v->base, v->content.array.elems[i].from);
748+
749+
if (!v->content.array.elems[i].to)
750+
return false;
751+
752+
jspInitByBuffer(to, v->base, v->content.array.elems[i].to);
753+
754+
return true;
755+
}

src/backend/utils/adt/jsonpath_exec.c

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

740+
static JsonPathExecResult
741+
getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
742+
int32 *index)
743+
{
744+
JsonbValue *jbv;
745+
List *found = NIL;
746+
JsonbValue tmp;
747+
JsonPathExecResult res = recursiveExecute(cxt, jsp, jb, &found);
748+
749+
if (jperIsError(res))
750+
return res;
751+
752+
if (list_length(found) != 1)
753+
return jperMakeError(ERRCODE_INVALID_JSON_SUBSCRIPT);
754+
755+
jbv = linitial(found);
756+
757+
if (JsonbType(jbv) == jbvScalar)
758+
jbv = JsonbExtractScalar(jbv->val.binary.data, &tmp);
759+
760+
if (jbv->type != jbvNumeric)
761+
return jperMakeError(ERRCODE_INVALID_JSON_SUBSCRIPT);
762+
763+
*index = DatumGetInt32(DirectFunctionCall1(numeric_int4,
764+
DirectFunctionCall2(numeric_trunc,
765+
NumericGetDatum(jbv->val.numeric),
766+
Int32GetDatum(0))));
767+
768+
return jperOk;
769+
}
770+
740771
/*
741772
* Main executor function: walks on jsonpath structure and tries to find
742773
* correspoding parts of jsonb. Note, jsonb and jsonpath values should be
@@ -921,39 +952,87 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
921952
case jpiIndexArray:
922953
if (JsonbType(jb) == jbvArray)
923954
{
924-
JsonbValue *v;
925-
int i;
955+
int i;
956+
int size = JsonbArraySize(jb);
926957

927958
hasNext = jspGetNext(jsp, &elem);
928959

929-
for(i=0; i<jsp->content.array.nelems; i++)
960+
for (i = 0; i < jsp->content.array.nelems; i++)
930961
{
931-
/* TODO for future: array index can be expression */
932-
v = getIthJsonbValueFromContainer(jb->val.binary.data,
933-
jsp->content.array.elems[i]);
962+
JsonPathItem from;
963+
JsonPathItem to;
964+
int32 index;
965+
int32 index_from;
966+
int32 index_to;
967+
bool range = jspGetArraySubscript(jsp, &from, &to, i);
968+
969+
res = getArrayIndex(cxt, &from, jb, &index_from);
934970

935-
if (v == NULL)
936-
continue;
971+
if (jperIsError(res))
972+
break;
937973

938-
if (hasNext == true)
974+
if (range)
939975
{
940-
res = recursiveExecute(cxt, &elem, v, found);
976+
res = getArrayIndex(cxt, &to, jb, &index_to);
941977

942-
if (jperIsError(res) || found == NULL)
978+
if (jperIsError(res))
943979
break;
944-
945-
if (res == jperOk && found == NULL)
946-
break;
947980
}
948981
else
982+
index_to = index_from;
983+
984+
if (!cxt->lax &&
985+
(index_from < 0 ||
986+
index_from > index_to ||
987+
index_to >= size))
949988
{
950-
res = jperOk;
989+
res = jperMakeError(ERRCODE_INVALID_JSON_SUBSCRIPT);
990+
break;
991+
}
951992

952-
if (found == NULL)
953-
break;
993+
if (index_from < 0)
994+
index_from = 0;
995+
996+
if (index_to >= size)
997+
index_to = size - 1;
954998

955-
*found = lappend(*found, v);
999+
res = jperNotFound;
1000+
1001+
for (index = index_from; index <= index_to; index++)
1002+
{
1003+
JsonbValue *v =
1004+
getIthJsonbValueFromContainer(jb->val.binary.data,
1005+
(uint32) index);
1006+
1007+
if (v == NULL)
1008+
continue;
1009+
1010+
if (hasNext)
1011+
{
1012+
res = recursiveExecute(cxt, &elem, v, found);
1013+
1014+
if (jperIsError(res))
1015+
break;
1016+
1017+
if (res == jperOk && !found)
1018+
break;
1019+
}
1020+
else
1021+
{
1022+
res = jperOk;
1023+
1024+
if (!found)
1025+
break;
1026+
1027+
*found = lappend(*found, v);
1028+
}
9561029
}
1030+
1031+
if (jperIsError(res))
1032+
break;
1033+
1034+
if (res == jperOk && !found)
1035+
break;
9571036
}
9581037
}
9591038
else

src/backend/utils/adt/jsonpath_gram.y

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

180180
foreach(cell, list)
181-
v->value.array.elems[i++] = lfirst_int(cell);
181+
{
182+
JsonPathParseItem *jpi = lfirst(cell);
183+
184+
Assert(jpi->type == jpiSubscript);
185+
186+
v->value.array.elems[i].from = jpi->value.args.left;
187+
v->value.array.elems[i++].to = jpi->value.args.right;
188+
}
182189

183190
return v;
184191
}
@@ -225,10 +232,11 @@ makeAny(int first, int last)
225232

226233
%type <value> scalar_value path_primary expr pexpr array_accessor
227234
any_path accessor_op key predicate delimited_predicate
235+
index_elem
228236

229237
%type <elems> accessor_expr
230238

231-
%type <indexs> index_elem index_list
239+
%type <indexs> index_list
232240

233241
%type <optype> comp_op method
234242

@@ -332,22 +340,13 @@ expr:
332340
;
333341

334342
index_elem:
335-
INT_P { $$ = list_make1_int(pg_atoi($1.val, 4, 0)); }
336-
| INT_P TO_P INT_P {
337-
int start = pg_atoi($1.val, 4, 0),
338-
stop = pg_atoi($3.val, 4, 0),
339-
i;
340-
341-
$$ = NIL;
342-
343-
for(i=start; i<= stop; i++)
344-
$$ = lappend_int($$, i);
345-
}
343+
pexpr { $$ = makeItemBinary(jpiSubscript, $1, NULL); }
344+
| pexpr TO_P pexpr { $$ = makeItemBinary(jpiSubscript, $1, $3); }
346345
;
347346

348347
index_list:
349-
index_elem { $$ = $1; }
350-
| index_list ',' index_elem { $$ = list_concat($1, $3); }
348+
index_elem { $$ = list_make1($1); }
349+
| index_list ',' index_elem { $$ = lappend($1, $3); }
351350
;
352351

353352
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)