agora inbox for [email protected]
help / color / mirror / Atom feedFrom: Nikita Glukhov <[email protected]>
Subject: [PATCH v1 4/8] Add jsonpath array constructors
Date: Thu, 6 Apr 2017 22:36:05 +0300
---
doc/src/sgml/func.sgml | 7 ++++
src/backend/utils/adt/jsonpath.c | 30 ++++++++++++---
src/backend/utils/adt/jsonpath_exec.c | 19 ++++++++++
src/backend/utils/adt/jsonpath_gram.y | 2 +
src/include/utils/jsonpath.h | 1 +
src/test/regress/expected/jsonb_jsonpath.out | 57 ++++++++++++++++++++++++++++
src/test/regress/expected/jsonpath.out | 12 ++++++
src/test/regress/sql/jsonb_jsonpath.sql | 11 ++++++
src/test/regress/sql/jsonpath.sql | 3 ++
9 files changed, 136 insertions(+), 6 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 9defd1a..7347e71 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -12632,6 +12632,13 @@ table2-mapping
<entry><literal>pg $[*], 4, 5</literal></entry>
<entry><literal>1, 2, 3, 4, 5</literal></entry>
</row>
+ <row>
+ <entry>Array constructor</entry>
+ <entry>Construct a JSON array by enumeration of its elements enclosed in brackets</entry>
+ <entry><literal>[1, 2, 3]</literal></entry>
+ <entry><literal>pg [$[*], 4, 5]</literal></entry>
+ <entry><literal>[1, 2, 3, 4, 5]</literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index 0fb4257..16031ba 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -368,13 +368,19 @@ flattenJsonPathParseItem(JsonPathEncodingContext *cxt, JsonPathParseItem *item,
case jpiMinus:
case jpiExists:
case jpiDatetime:
+ case jpiArray:
{
int32 arg = reserveSpaceForItemPointer(buf);
- chld = !item->value.arg ? pos :
- flattenJsonPathParseItem(cxt, item->value.arg,
- nestingLevel + argNestingLevel,
- insideArraySubscript);
+ if (item->type == jpiArray)
+ checkJsonPathExtensionsEnabled(cxt, item->type);
+
+ if (!item->value.arg)
+ break;
+
+ chld = flattenJsonPathParseItem(cxt, item->value.arg,
+ nestingLevel + argNestingLevel,
+ insideArraySubscript);
*(int32 *) (buf->data + arg) = chld - pos;
}
break;
@@ -780,6 +786,15 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
if (printBracketes || jspHasNext(v))
appendStringInfoChar(buf, ')');
break;
+ case jpiArray:
+ appendStringInfoChar(buf, '[');
+ if (v->content.arg)
+ {
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ']');
+ break;
default:
elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
}
@@ -979,6 +994,7 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiMinus:
case jpiFilter:
case jpiDatetime:
+ case jpiArray:
read_int32(v->content.arg, base, pos);
break;
case jpiIndexArray:
@@ -1009,7 +1025,8 @@ jspGetArg(JsonPathItem *v, JsonPathItem *a)
v->type == jpiExists ||
v->type == jpiPlus ||
v->type == jpiMinus ||
- v->type == jpiDatetime);
+ v->type == jpiDatetime ||
+ v->type == jpiArray);
jspInitByBuffer(a, v->base, v->content.arg);
}
@@ -1060,7 +1077,8 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiDatetime ||
v->type == jpiKeyValue ||
v->type == jpiStartsWith ||
- v->type == jpiSequence);
+ v->type == jpiSequence ||
+ v->type == jpiArray);
if (a)
jspInitByBuffer(a, v->base, v->nextPos);
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 1e31d3c..1dee30a 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1175,6 +1175,25 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
break;
}
+ case jpiArray:
+ {
+ JsonValueList list = {0};
+ JsonbValue *arr;
+
+ if (jsp->content.arg)
+ {
+ jspGetArg(jsp, &elem);
+ res = executeItem(cxt, &elem, jb, &list);
+
+ if (jperIsError(res))
+ break;
+ }
+
+ arr = wrapItemsInArray(&list);
+ res = executeNextItem(cxt, jsp, NULL, arr, found, false);
+ break;
+ }
+
default:
elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
}
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index 1de6f5a..3507c1f 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -217,6 +217,8 @@ path_primary:
| '@' { $$ = makeItemType(jpiCurrent); }
| LAST_P { $$ = makeItemType(jpiLast); }
| '(' expr_seq ')' { $$ = $2; }
+ | '[' ']' { $$ = makeItemUnary(jpiArray, NULL); }
+ | '[' expr_or_seq ']' { $$ = makeItemUnary(jpiArray, $2); }
;
accessor_expr:
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index 4cbdce3..1b2b964 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -88,6 +88,7 @@ typedef enum JsonPathItemType
jpiStartsWith, /* STARTS WITH predicate */
jpiLikeRegex, /* LIKE_REGEX predicate */
jpiSequence, /* sequence constructor: 'expr, ...' */
+ jpiArray, /* array constructor: '[expr, ...]' */
} JsonPathItemType;
/* XQuery regex mode flags for LIKE_REGEX predicate */
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index c5252ad..cd56876 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -2199,6 +2199,12 @@ SELECT jsonb_path_query('[{"a": 1}, {"a": 2}]', '$[*] ? (@.a > 10)');
------------------
(0 rows)
+SELECT jsonb_path_query('[{"a": 1}, {"a": 2}]', 'pg [$[*].a]');
+ jsonb_path_query
+------------------
+ [1, 2]
+(1 row)
+
SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}, {}]', 'strict $[*].a');
ERROR: JSON object does not contain key "a"
SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}]', '$[*].a');
@@ -2231,6 +2237,12 @@ SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 5}]', '$[*].
[]
(1 row)
+SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}]', 'pg [$[*].a]');
+ jsonb_path_query_array
+------------------------
+ [[1, 2]]
+(1 row)
+
SELECT jsonb_path_query_first('[{"a": 1}, {"a": 2}, {}]', 'strict $[*].a');
ERROR: JSON object does not contain key "a"
SELECT jsonb_path_query_first('[{"a": 1}, {"a": 2}, {}]', 'strict $[*].a', silent => true);
@@ -2578,3 +2590,48 @@ select jsonb_path_query('[1,2,3,4,5]', 'pg $[(0, $[*], 5) ? (@ == 3)]');
select jsonb_path_query('[1,2,3,4,5]', 'pg $[(0, $[*], 3) ? (@ == 3)]');
ERROR: jsonpath array subscript is not a single numeric value
+-- extension: array constructors
+select jsonb_path_query('[1, 2, 3]', 'pg []');
+ jsonb_path_query
+------------------
+ []
+(1 row)
+
+select jsonb_path_query('[1, 2, 3]', 'pg [1, 2, $[*], 4, 5]');
+ jsonb_path_query
+-----------------------
+ [1, 2, 1, 2, 3, 4, 5]
+(1 row)
+
+select jsonb_path_query('[1, 2, 3]', 'pg [1, 2, $[*], 4, 5][*]');
+ jsonb_path_query
+------------------
+ 1
+ 2
+ 1
+ 2
+ 3
+ 4
+ 5
+(7 rows)
+
+select jsonb_path_query('[1, 2, 3]', 'pg [(1, (2, $[*])), (4, 5)]');
+ jsonb_path_query
+-----------------------
+ [1, 2, 1, 2, 3, 4, 5]
+(1 row)
+
+select jsonb_path_query('[1, 2, 3]', 'pg [[1, 2], [$[*], 4], 5, [(1,2)?(@ > 5)]]');
+ jsonb_path_query
+-------------------------------
+ [[1, 2], [1, 2, 3, 4], 5, []]
+(1 row)
+
+select jsonb_path_query('[1, 2, 3]', 'pg strict [1, 2, $[*].a, 4, 5]');
+ERROR: jsonpath member accessor can only be applied to an object
+select jsonb_path_query('[[1, 2], [3, 4, 5], [], [6, 7]]', 'pg [$[*][*] ? (@ > 3)]');
+ jsonb_path_query
+------------------
+ [4, 5, 6, 7]
+(1 row)
+
diff --git a/src/test/regress/expected/jsonpath.out b/src/test/regress/expected/jsonpath.out
index f6a132c..e212deb 100644
--- a/src/test/regress/expected/jsonpath.out
+++ b/src/test/regress/expected/jsonpath.out
@@ -604,6 +604,18 @@ select 'pg $[(1, (2, $.a)), 3, (4, 5)]'::jsonpath;
pg $[(1, (2, $."a")),3,(4, 5)]
(1 row)
+select 'pg []'::jsonpath;
+ jsonpath
+----------
+ pg []
+(1 row)
+
+select 'pg [[1, 2], ([(3, 4, 5), 6], []), $.a[*]]'::jsonpath;
+ jsonpath
+---------------------------------------------
+ pg [[1, 2], ([(3, 4, 5), 6], []), $."a"[*]]
+(1 row)
+
select '$ ? (@.a < 1)'::jsonpath;
jsonpath
---------------
diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql
index 0619ade..be5c7c8 100644
--- a/src/test/regress/sql/jsonb_jsonpath.sql
+++ b/src/test/regress/sql/jsonb_jsonpath.sql
@@ -525,6 +525,7 @@ set time zone default;
SELECT jsonb_path_query('[{"a": 1}, {"a": 2}]', '$[*]');
SELECT jsonb_path_query('[{"a": 1}, {"a": 2}]', '$[*] ? (@.a > 10)');
+SELECT jsonb_path_query('[{"a": 1}, {"a": 2}]', 'pg [$[*].a]');
SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}, {}]', 'strict $[*].a');
SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}]', '$[*].a');
@@ -532,6 +533,7 @@ SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}]', '$[*].a ? (@ == 1)');
SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}]', '$[*].a ? (@ > 10)');
SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 5}]', '$[*].a ? (@ > $min && @ < $max)', vars => '{"min": 1, "max": 4}');
SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 5}]', '$[*].a ? (@ > $min && @ < $max)', vars => '{"min": 3, "max": 4}');
+SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}]', 'pg [$[*].a]');
SELECT jsonb_path_query_first('[{"a": 1}, {"a": 2}, {}]', 'strict $[*].a');
SELECT jsonb_path_query_first('[{"a": 1}, {"a": 2}, {}]', 'strict $[*].a', silent => true);
@@ -587,3 +589,12 @@ select jsonb_path_query('[1,2,3,4,5]', 'pg -(10, 20, $[1 to 3], 30)');
select jsonb_path_query('[1,2,3,4,5]', 'pg lax (10, 20.5, $[1 to 3], "30").double()');
select jsonb_path_query('[1,2,3,4,5]', 'pg $[(0, $[*], 5) ? (@ == 3)]');
select jsonb_path_query('[1,2,3,4,5]', 'pg $[(0, $[*], 3) ? (@ == 3)]');
+
+-- extension: array constructors
+select jsonb_path_query('[1, 2, 3]', 'pg []');
+select jsonb_path_query('[1, 2, 3]', 'pg [1, 2, $[*], 4, 5]');
+select jsonb_path_query('[1, 2, 3]', 'pg [1, 2, $[*], 4, 5][*]');
+select jsonb_path_query('[1, 2, 3]', 'pg [(1, (2, $[*])), (4, 5)]');
+select jsonb_path_query('[1, 2, 3]', 'pg [[1, 2], [$[*], 4], 5, [(1,2)?(@ > 5)]]');
+select jsonb_path_query('[1, 2, 3]', 'pg strict [1, 2, $[*].a, 4, 5]');
+select jsonb_path_query('[[1, 2], [3, 4, 5], [], [6, 7]]', 'pg [$[*][*] ? (@ > 3)]');
diff --git a/src/test/regress/sql/jsonpath.sql b/src/test/regress/sql/jsonpath.sql
index 2a53168..23ae376 100644
--- a/src/test/regress/sql/jsonpath.sql
+++ b/src/test/regress/sql/jsonpath.sql
@@ -113,6 +113,9 @@ select 'pg (1, 2, $.a) == 5'::jsonpath;
select 'pg $[(1, 2, $.a) to (3, 4)]'::jsonpath;
select 'pg $[(1, (2, $.a)), 3, (4, 5)]'::jsonpath;
+select 'pg []'::jsonpath;
+select 'pg [[1, 2], ([(3, 4, 5), 6], []), $.a[*]]'::jsonpath;
+
select '$ ? (@.a < 1)'::jsonpath;
select '$ ? (@.a < -1)'::jsonpath;
select '$ ? (@.a < +1)'::jsonpath;
--
2.7.4
--------------574DCE16CAD28E71E2083DCD
Content-Type: text/x-patch;
name="v1-0005-Add-jsonpath-object-constructors.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="v1-0005-Add-jsonpath-object-constructors.patch"
view thread (2+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected]
Subject: Re: [PATCH v1 4/8] Add jsonpath array constructors
In-Reply-To: <no-message-id-189321@localhost>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox