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

Commit f51fb8f

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

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
@@ -2187,6 +2187,8 @@ ExecInitExprRec(Expr *node, ExprState *state,
21872187
lappend(scratch.d.jsonexpr.args, var);
21882188
}
21892189

2190+
scratch.d.jsonexpr.cache = NULL;
2191+
21902192
if (jexpr->coercions)
21912193
{
21922194
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"
@@ -4232,6 +4233,13 @@ ExecEvalJsonExprCoercion(ExprEvalStep *op, ExprContext *econtext,
42324233
else if (op->d.jsonexpr.result_expr)
42334234
res = ExecEvalExprPassingCaseValue(op->d.jsonexpr.result_expr, econtext,
42344235
isNull, res, *isNull);
4236+
else if (coercion && coercion->via_populate)
4237+
res = json_populate_type(res, JSONBOID,
4238+
jexpr->returning.typid,
4239+
jexpr->returning.typmod,
4240+
&op->d.jsonexpr.cache,
4241+
econtext->ecxt_per_query_memory,
4242+
isNull);
42354243
/* else no coercion, simply return item */
42364244

42374245
return res;

src/backend/utils/adt/jsonfuncs.c

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

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

src/include/executor/execExpr.h

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

665+
void *cache; /* cache for json_populate_type() */
666+
665667
struct JsonCoercionsState
666668
{
667669
struct JsonCoercionState

src/include/utils/jsonapi.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@ extern text *transform_json_string_values(text *json, void *action_state,
206206

207207
extern char *JsonEncodeDateTime(char *buf, Datum value, Oid typid, int *tz);
208208

209+
extern Datum json_populate_type(Datum json_val, Oid json_type,
210+
Oid typid, int32 typmod,
211+
void **cache, MemoryContext mcxt, bool *isnull);
212+
209213
extern Json *JsonCreate(text *json);
210214
extern JsonbIteratorToken JsonIteratorNext(JsonIterator **pit, JsonbValue *val,
211215
bool skipNested);

src/test/regress/expected/jsonb_sqljson.out

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

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