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

Commit b321bdf

Browse files
author
Nikita Glukhov
committed
Add JSON_QUERY support for row, array and domain types
1 parent eb62e38 commit b321bdf

File tree

7 files changed

+123
-0
lines changed

7 files changed

+123
-0
lines changed

src/backend/executor/execExpr.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2177,6 +2177,8 @@ ExecInitExprRec(Expr *node, ExprState *state,
21772177
lappend(scratch.d.jsonexpr.args, var);
21782178
}
21792179

2180+
scratch.d.jsonexpr.cache = NULL;
2181+
21802182
if (jexpr->coercions)
21812183
{
21822184
JsonCoercion **coercion;

src/backend/executor/execExprInterp.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
#include "utils/date.h"
7474
#include "utils/datum.h"
7575
#include "utils/expandedrecord.h"
76+
#include "utils/jsonapi.h"
7677
#include "utils/jsonb.h"
7778
#include "utils/jsonpath.h"
7879
#include "utils/lsyscache.h"
@@ -4249,6 +4250,13 @@ ExecEvalJsonExprCoercion(ExprEvalStep *op, ExprContext *econtext,
42494250
else if (op->d.jsonexpr.result_expr)
42504251
res = ExecEvalExprPassingCaseValue(op->d.jsonexpr.result_expr, econtext,
42514252
isNull, res, *isNull);
4253+
else if (coercion && coercion->via_populate)
4254+
res = json_populate_type(res, JSONBOID,
4255+
jexpr->returning.typid,
4256+
jexpr->returning.typmod,
4257+
&op->d.jsonexpr.cache,
4258+
econtext->ecxt_per_query_memory,
4259+
isNull);
42524260
/* else no coercion, simply return item */
42534261

42544262
return res;

src/backend/utils/adt/jsonfuncs.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3050,6 +3050,50 @@ populate_record_field(ColumnIOData *col,
30503050
}
30513051
}
30523052

3053+
/* recursively populate specified type from a json/jsonb value */
3054+
Datum
3055+
json_populate_type(Datum json_val, Oid json_type, Oid typid, int32 typmod,
3056+
void **cache, MemoryContext mcxt, bool *isnull)
3057+
{
3058+
JsValue jsv = { 0 };
3059+
JsonbValue jbv;
3060+
3061+
jsv.is_json = json_type == JSONOID;
3062+
3063+
if (*isnull)
3064+
{
3065+
if (jsv.is_json)
3066+
jsv.val.json.str = NULL;
3067+
else
3068+
jsv.val.jsonb = NULL;
3069+
}
3070+
else if (jsv.is_json)
3071+
{
3072+
text *json = DatumGetTextPP(json_val);
3073+
3074+
jsv.val.json.str = VARDATA_ANY(json);
3075+
jsv.val.json.len = VARSIZE_ANY_EXHDR(json);
3076+
jsv.val.json.type = JSON_TOKEN_INVALID; /* not used in populate_composite() */
3077+
}
3078+
else
3079+
{
3080+
Jsonb *jsonb = DatumGetJsonbP(json_val);
3081+
3082+
jsv.val.jsonb = &jbv;
3083+
3084+
/* fill binary jsonb value pointing to jb */
3085+
jbv.type = jbvBinary;
3086+
jbv.val.binary.data = &jsonb->root;
3087+
jbv.val.binary.len = VARSIZE(jsonb) - VARHDRSZ;
3088+
}
3089+
3090+
if (!*cache)
3091+
*cache = MemoryContextAllocZero(mcxt, sizeof(ColumnIOData));
3092+
3093+
return populate_record_field(*cache , typid, typmod, NULL, mcxt,
3094+
PointerGetDatum(NULL), &jsv, isnull);
3095+
}
3096+
30533097
static RecordIOData *
30543098
allocate_record_info(MemoryContext mcxt, int ncolumns)
30553099
{

src/include/executor/execExpr.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,8 @@ typedef struct ExprEvalStep
680680
ExprState *default_on_error; /* ON ERROR DEFAULT expression */
681681
List *args; /* passing arguments */
682682

683+
void *cache; /* cache for json_populate_type() */
684+
683685
struct JsonCoercionsState
684686
{
685687
struct JsonCoercionState

src/include/utils/jsonapi.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,10 @@ extern text *transform_json_string_values(text *json, void *action_state,
217217
extern char *JsonEncodeDateTime(char *buf, Datum value, Oid typid,
218218
const int *tzp);
219219

220+
extern Datum json_populate_type(Datum json_val, Oid json_type,
221+
Oid typid, int32 typmod,
222+
void **cache, MemoryContext mcxt, bool *isnull);
223+
220224
extern Json *JsonCreate(text *json);
221225
extern JsonbIteratorToken JsonIteratorNext(JsonIterator **pit, JsonbValue *val,
222226
bool skipNested);

src/test/regress/expected/jsonb_sqljson.out

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,53 @@ FROM
752752
4 | 4 | [4]
753753
(25 rows)
754754

755+
-- Extension: record types returning
756+
CREATE TYPE sqljsonb_rec AS (a int, t text, js json, jb jsonb, jsa json[]);
757+
CREATE TYPE sqljsonb_reca AS (reca sqljsonb_rec[]);
758+
SELECT JSON_QUERY(jsonb '[{"a": 1, "b": "foo", "t": "aaa", "js": [1, "2", {}], "jb": {"x": [1, "2", {}]}}, {"a": 2}]', '$[0]' RETURNING sqljsonb_rec);
759+
json_query
760+
-----------------------------------------------------
761+
(1,aaa,"[1, ""2"", {}]","{""x"": [1, ""2"", {}]}",)
762+
(1 row)
763+
764+
SELECT * FROM unnest((JSON_QUERY(jsonb '{"jsa": [{"a": 1, "b": ["foo"]}, {"a": 2, "c": {}}, 123]}', '$' RETURNING sqljsonb_rec)).jsa);
765+
unnest
766+
------------------------
767+
{"a": 1, "b": ["foo"]}
768+
{"a": 2, "c": {}}
769+
123
770+
(3 rows)
771+
772+
SELECT * FROM unnest((JSON_QUERY(jsonb '{"reca": [{"a": 1, "t": ["foo", []]}, {"a": 2, "jb": [{}, true]}]}', '$' RETURNING sqljsonb_reca)).reca);
773+
a | t | js | jb | jsa
774+
---+-------------+----+------------+-----
775+
1 | ["foo", []] | | |
776+
2 | | | [{}, true] |
777+
(2 rows)
778+
779+
-- Extension: array types returning
780+
SELECT JSON_QUERY(jsonb '[1,2,null,"3"]', '$[*]' RETURNING int[] WITH WRAPPER);
781+
json_query
782+
--------------
783+
{1,2,NULL,3}
784+
(1 row)
785+
786+
SELECT * FROM unnest(JSON_QUERY(jsonb '[{"a": 1, "t": ["foo", []]}, {"a": 2, "jb": [{}, true]}]', '$' RETURNING sqljsonb_rec[]));
787+
a | t | js | jb | jsa
788+
---+-------------+----+------------+-----
789+
1 | ["foo", []] | | |
790+
2 | | | [{}, true] |
791+
(2 rows)
792+
793+
-- Extension: domain types returning
794+
SELECT JSON_QUERY(jsonb '{"a": 1}', '$.a' RETURNING sqljsonb_int_not_null);
795+
json_query
796+
------------
797+
1
798+
(1 row)
799+
800+
SELECT JSON_QUERY(jsonb '{"a": 1}', '$.b' RETURNING sqljsonb_int_not_null);
801+
ERROR: domain sqljsonb_int_not_null does not allow null values
755802
-- Test constraints
756803
CREATE TABLE test_jsonb_constraints (
757804
js text,

src/test/regress/sql/jsonb_sqljson.sql

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,22 @@ FROM
213213
generate_series(0, 4) x,
214214
generate_series(0, 4) y;
215215

216+
-- Extension: record types returning
217+
CREATE TYPE sqljsonb_rec AS (a int, t text, js json, jb jsonb, jsa json[]);
218+
CREATE TYPE sqljsonb_reca AS (reca sqljsonb_rec[]);
219+
220+
SELECT JSON_QUERY(jsonb '[{"a": 1, "b": "foo", "t": "aaa", "js": [1, "2", {}], "jb": {"x": [1, "2", {}]}}, {"a": 2}]', '$[0]' RETURNING sqljsonb_rec);
221+
SELECT * FROM unnest((JSON_QUERY(jsonb '{"jsa": [{"a": 1, "b": ["foo"]}, {"a": 2, "c": {}}, 123]}', '$' RETURNING sqljsonb_rec)).jsa);
222+
SELECT * FROM unnest((JSON_QUERY(jsonb '{"reca": [{"a": 1, "t": ["foo", []]}, {"a": 2, "jb": [{}, true]}]}', '$' RETURNING sqljsonb_reca)).reca);
223+
224+
-- Extension: array types returning
225+
SELECT JSON_QUERY(jsonb '[1,2,null,"3"]', '$[*]' RETURNING int[] WITH WRAPPER);
226+
SELECT * FROM unnest(JSON_QUERY(jsonb '[{"a": 1, "t": ["foo", []]}, {"a": 2, "jb": [{}, true]}]', '$' RETURNING sqljsonb_rec[]));
227+
228+
-- Extension: domain types returning
229+
SELECT JSON_QUERY(jsonb '{"a": 1}', '$.a' RETURNING sqljsonb_int_not_null);
230+
SELECT JSON_QUERY(jsonb '{"a": 1}', '$.b' RETURNING sqljsonb_int_not_null);
231+
216232
-- Test constraints
217233

218234
CREATE TABLE test_jsonb_constraints (

0 commit comments

Comments
 (0)