Skip to content

Commit d0166fb

Browse files
committed
implement #N path key - N-th element of array
1 parent 058fb69 commit d0166fb

File tree

10 files changed

+130
-9
lines changed

10 files changed

+130
-9
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ the simplest case path is just an key name. In general path is key names and
8989
placeholders combined by dot signs. Path can use following placeholders:
9090

9191
* `#` – any index of array;
92+
* `#N` – N-th index of array;
9293
* `%` – any key of object;
9394
* `*` – any sequence of array indexes and object keys;
9495
* `@#` – length of array or object, could be only used as last component of

expected/jsquery.out

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,18 @@ select 'is.not < 1'::jsquery;
517517
"is"."not" < 1
518518
(1 row)
519519

520+
select 'a.b.#4 > 4'::jsquery;
521+
jsquery
522+
----------------
523+
"a"."b".#4 > 4
524+
(1 row)
525+
526+
select 'a.b.#10203.* > 4'::jsquery;
527+
jsquery
528+
----------------------
529+
"a"."b".#10203.* > 4
530+
(1 row)
531+
520532
select '{"a": {"b": null}}'::jsonb @@ 'a.b = 1';
521533
?column?
522534
----------
@@ -997,6 +1009,42 @@ select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%=[5,6]';
9971009
t
9981010
(1 row)
9991011

1012+
select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#1 = 2';
1013+
?column?
1014+
----------
1015+
t
1016+
(1 row)
1017+
1018+
select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#2 = 2';
1019+
?column?
1020+
----------
1021+
f
1022+
(1 row)
1023+
1024+
select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#3 = 2';
1025+
?column?
1026+
----------
1027+
f
1028+
(1 row)
1029+
1030+
select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#1.x = 2';
1031+
?column?
1032+
----------
1033+
t
1034+
(1 row)
1035+
1036+
select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#2.x = 2';
1037+
?column?
1038+
----------
1039+
f
1040+
(1 row)
1041+
1042+
select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#3.x = 2';
1043+
?column?
1044+
----------
1045+
f
1046+
(1 row)
1047+
10001048
select '"XXX"'::jsonb @@ '$="XXX"';
10011049
?column?
10021050
----------

jsquery.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ typedef enum JsQueryItemType {
5656
jqiCurrent,
5757
jqiLength,
5858
jqiIn,
59-
jqiIs
59+
jqiIs,
60+
jqiIndexArray
6061
} JsQueryItemType;
6162

6263
/*
@@ -104,6 +105,8 @@ typedef struct JsQueryItem {
104105
int current;
105106
int32 *arrayPtr;
106107
} array;
108+
109+
uint32 arrayIndex;
107110
};
108111
} JsQueryItem;
109112

@@ -153,6 +156,8 @@ struct JsQueryParseItem {
153156
int nelems;
154157
JsQueryParseItem **elems;
155158
} array;
159+
160+
uint32 arrayIndex;
156161
};
157162
};
158163

jsquery_extract.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ recursiveExtract(JsQueryItem *jsq, bool not, bool indirect, PathItem *path)
105105
pathItem->parent = path;
106106
jsqGetNext(jsq, &elem);
107107
return recursiveExtract(&elem, not, true, pathItem);
108+
case jqiIndexArray:
108109
case jqiAnyArray:
109110
case jqiAllArray:
110111
if ((not && jsq->type == jqiAnyArray) || (!not && jsq->type == jqiAllArray))

jsquery_gram.y

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,16 @@ makeItemType(int type)
6565
return v;
6666
}
6767

68+
static JsQueryParseItem*
69+
makeIndexArray(string *s)
70+
{
71+
JsQueryParseItem* v = makeItemType(jqiIndexArray);
72+
73+
v->arrayIndex = pg_atoi(s->val, 4, 0);
74+
75+
return v;
76+
}
77+
6878
static JsQueryParseItem*
6979
makeItemString(string *s)
7080
{
@@ -213,13 +223,13 @@ makeItemList(List *list) {
213223
ARRAY_T FALSE_P NUMERIC_T OBJECT_T
214224
STRING_T BOOLEAN_T
215225

216-
%token <str> STRING_P NUMERIC_P
226+
%token <str> STRING_P NUMERIC_P INT_P
217227

218228
%type <value> result scalar_value
219229

220230
%type <elems> path value_list
221231

222-
%type <value> key key_any right_expr expr array
232+
%type <value> key key_any right_expr expr array numeric
223233

224234
%token <hint> HINT_P
225235

@@ -257,22 +267,28 @@ scalar_value:
257267
| STRING_T { $$ = makeItemString(&$1); }
258268
| BOOLEAN_T { $$ = makeItemString(&$1); }
259269
| NUMERIC_P { $$ = makeItemNumeric(&$1); }
270+
| INT_P { $$ = makeItemNumeric(&$1); }
260271
;
261272

262273
value_list:
263274
scalar_value { $$ = lappend(NIL, $1); }
264275
| value_list ',' scalar_value { $$ = lappend($1, $3); }
265276
;
266277

278+
numeric:
279+
NUMERIC_P { $$ = makeItemNumeric(&$1); }
280+
| INT_P { $$ = makeItemNumeric(&$1); }
281+
;
282+
267283
right_expr:
268284
'=' scalar_value { $$ = makeItemUnary(jqiEqual, $2); }
269285
| IN_P '(' value_list ')' { $$ = makeItemUnary(jqiIn, makeItemArray($3)); }
270286
| '=' array { $$ = makeItemUnary(jqiEqual, $2); }
271287
| '=' '*' { $$ = makeItemUnary(jqiEqual, makeItemType(jqiAny)); }
272-
| '<' NUMERIC_P { $$ = makeItemUnary(jqiLess, makeItemNumeric(&$2)); }
273-
| '>' NUMERIC_P { $$ = makeItemUnary(jqiGreater, makeItemNumeric(&$2)); }
274-
| '<' '=' NUMERIC_P { $$ = makeItemUnary(jqiLessOrEqual, makeItemNumeric(&$3)); }
275-
| '>' '=' NUMERIC_P { $$ = makeItemUnary(jqiGreaterOrEqual, makeItemNumeric(&$3)); }
288+
| '<' numeric { $$ = makeItemUnary(jqiLess, $2); }
289+
| '>' numeric { $$ = makeItemUnary(jqiGreater, $2); }
290+
| '<' '=' numeric { $$ = makeItemUnary(jqiLessOrEqual, $3); }
291+
| '>' '=' numeric { $$ = makeItemUnary(jqiGreaterOrEqual, $3); }
276292
| '@' '>' array { $$ = makeItemUnary(jqiContains, $3); }
277293
| '<' '@' array { $$ = makeItemUnary(jqiContained, $3); }
278294
| '&' '&' array { $$ = makeItemUnary(jqiOverlap, $3); }
@@ -311,6 +327,7 @@ key:
311327
| '%' ':' { $$ = makeItemType(jqiAllKey); }
312328
| '$' { $$ = makeItemType(jqiCurrent); }
313329
| '@' '#' { $$ = makeItemType(jqiLength); }
330+
| '#' INT_P { $$ = makeIndexArray(&$2); }
314331
| STRING_P { $$ = makeItemKey(&$1); }
315332
| IN_P { $$ = makeItemKey(&$1); }
316333
| IS_P { $$ = makeItemKey(&$1); }
@@ -325,6 +342,7 @@ key:
325342
| STRING_T { $$ = makeItemKey(&$1); }
326343
| BOOLEAN_T { $$ = makeItemKey(&$1); }
327344
| NUMERIC_P { $$ = makeItemKey(&$1); }
345+
| INT_P { $$ = makeItemKey(&$1); }
328346
;
329347

330348
/*

jsquery_io.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ flattenJsQueryParseItem(StringInfo buf, JsQueryParseItem *item, bool onlyCurrent
113113
*(int32*)(buf->data + arg) = chld;
114114
}
115115
break;
116+
case jqiIndexArray:
117+
appendBinaryStringInfo(buf, (char*)&item->arrayIndex,
118+
sizeof(item->arrayIndex));
116119
case jqiAny:
117120
case jqiAnyArray:
118121
case jqiAnyKey:
@@ -272,7 +275,7 @@ printJsQueryItem(StringInfo buf, JsQueryItem *v, bool inKey, bool printBracketes
272275
break;
273276
case jqiArray:
274277
if (printBracketes)
275-
appendStringInfoChar(buf, '[');
278+
appendStringInfoChar(buf, '[');
276279

277280
while(jsqIterateArray(v, &elem))
278281
{
@@ -364,6 +367,11 @@ printJsQueryItem(StringInfo buf, JsQueryItem *v, bool inKey, bool printBracketes
364367
appendStringInfoChar(buf, '%');
365368
appendStringInfoChar(buf, ':');
366369
break;
370+
case jqiIndexArray:
371+
if (inKey)
372+
appendStringInfoChar(buf, '.');
373+
appendStringInfo(buf, "#%u", v->arrayIndex);
374+
break;
367375
default:
368376
elog(ERROR, "Unknown JsQueryItem type: %d", v->type);
369377
}

jsquery_op.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,19 @@ recursiveExecute(JsQueryItem *jsq, JsonbValue *jb, JsQueryItem *jsqLeftArg)
518518
}
519519
}
520520
break;
521+
case jqiIndexArray:
522+
if (JsonbType(jb) == jbvArray)
523+
{
524+
JsonbValue *v;
525+
526+
jsqGetNext(jsq, &elem);
527+
528+
v = getIthJsonbValueFromContainer(jb->val.binary.data,
529+
jsq->arrayIndex);
530+
531+
res = v && recursiveExecute(&elem, v, NULL);
532+
}
533+
break;
521534
case jqiAnyKey:
522535
case jqiAllKey:
523536
if (JsonbType(jb) == jbvObject)
@@ -670,6 +683,10 @@ compareJsQuery(JsQueryItem *v1, JsQueryItem *v2)
670683
case jqiAllArray:
671684
case jqiAllKey:
672685
break;
686+
case jqiIndexArray:
687+
if (v1->arrayIndex != v2->arrayIndex)
688+
res = (v1->arrayIndex > v2->arrayIndex) ? 1 : -1;
689+
break;
673690
case jqiKey:
674691
case jqiString:
675692
{
@@ -964,6 +981,9 @@ hashJsQuery(JsQueryItem *v, pg_crc32 *crc)
964981
case jqiAllArray:
965982
case jqiAllKey:
966983
break;
984+
case jqiIndexArray:
985+
COMP_CRC32(*crc, &v->arrayIndex, sizeof(v->arrayIndex));
986+
break;
967987
default:
968988
elog(ERROR, "Unknown JsQueryItem type: %d", v->type);
969989
}

jsquery_scan.l

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,19 @@ unicode \\u[0-9A-Fa-f]{4}
8585
return NUMERIC_P;
8686
}
8787
88-
<INITIAL>[+-]?[0-9]+ {
88+
<INITIAL>[+-][0-9]+ {
8989
addstring(true, yytext, yyleng);
9090
addchar(false, '\0');
9191
yylval->str = scanstring;
9292
return NUMERIC_P;
9393
}
9494
95+
<INITIAL>[0-9]+ {
96+
addstring(true, yytext, yyleng);
97+
addchar(false, '\0');
98+
yylval->str = scanstring;
99+
return INT_P;
100+
}
95101
96102
<INITIAL>{any}+ {
97103
addstring(true, yytext, yyleng);

jsquery_support.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ jsqInitByBuffer(JsQueryItem *v, char *base, int32 pos)
8080
case jqiAllArray:
8181
case jqiAllKey:
8282
break;
83+
case jqiIndexArray:
84+
read_int32(v->arrayIndex, base, pos);
85+
break;
8386
case jqiKey:
8487
case jqiString:
8588
read_int32(v->value.datalen, base, pos);
@@ -144,6 +147,7 @@ jsqGetNext(JsQueryItem *v, JsQueryItem *a)
144147
Assert(
145148
v->type == jqiKey ||
146149
v->type == jqiAny ||
150+
v->type == jqiIndexArray ||
147151
v->type == jqiAnyArray ||
148152
v->type == jqiAnyKey ||
149153
v->type == jqiAll ||

sql/jsquery.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ select 'is.in < 1'::jsquery;
101101
select 'is.is < 1'::jsquery;
102102
select 'is.not < 1'::jsquery;
103103

104+
select 'a.b.#4 > 4'::jsquery;
105+
select 'a.b.#10203.* > 4'::jsquery;
106+
104107
select '{"a": {"b": null}}'::jsonb @@ 'a.b = 1';
105108
select '{"a": {"b": null}}'::jsonb @@ 'a.b = null';
106109
select '{"a": {"b": null}}'::jsonb @@ 'a.b = false';
@@ -202,6 +205,13 @@ select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%.%="hey"';
202205
select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%="hey"';
203206
select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%=[5,6]';
204207

208+
select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#1 = 2';
209+
select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#2 = 2';
210+
select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#3 = 2';
211+
select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#1.x = 2';
212+
select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#2.x = 2';
213+
select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#3.x = 2';
214+
205215
select '"XXX"'::jsonb @@ '$="XXX"';
206216
select '"XXX"'::jsonb @@ '#.$="XXX"';
207217

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy