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

Commit 5070aad

Browse files
author
Nikita Glukhov
committed
Add JSON_QUERY support for row, array and domain types
1 parent 3287f38 commit 5070aad

File tree

7 files changed

+125
-3
lines changed

7 files changed

+125
-3
lines changed

src/backend/executor/execExpr.c

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

2275+
scratch.d.jsonexpr.cache = NULL;
2276+
22752277
if (jexpr->coercions)
22762278
{
22772279
JsonCoercion **coercion;

src/backend/executor/execExprInterp.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4563,6 +4563,13 @@ ExecEvalJsonExprCoercion(ExprEvalStep *op, ExprContext *econtext,
45634563
else if (op->d.jsonexpr.result_expr)
45644564
res = ExecEvalExprPassingCaseValue(op->d.jsonexpr.result_expr, econtext,
45654565
isNull, res, *isNull);
4566+
else if (coercion && coercion->via_populate)
4567+
res = json_populate_type(res, JSONBOID,
4568+
jexpr->returning->typid,
4569+
jexpr->returning->typmod,
4570+
&op->d.jsonexpr.cache,
4571+
econtext->ecxt_per_query_memory,
4572+
isNull);
45664573
/* else no coercion, simply return item */
45674574

45684575
return res;

src/backend/utils/adt/jsonfuncs.c

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2628,11 +2628,11 @@ populate_array_dim_jsonb(PopulateArrayContext *ctx, /* context */
26282628

26292629
check_stack_depth();
26302630

2631-
if (jbv->type != jbvBinary || !JsonContainerIsArray(jbc))
2631+
if (jbv->type != jbvBinary ||
2632+
!JsonContainerIsArray(jbc) ||
2633+
JsonContainerIsScalar(jbc))
26322634
populate_array_report_expected_array(ctx, ndim - 1);
26332635

2634-
Assert(!JsonContainerIsScalar(jbc));
2635-
26362636
it = JsonbIteratorInit(jbc);
26372637

26382638
tok = JsonbIteratorNext(&it, &val, true);
@@ -3104,6 +3104,50 @@ populate_record_field(ColumnIOData *col,
31043104
}
31053105
}
31063106

3107+
/* recursively populate specified type from a json/jsonb value */
3108+
Datum
3109+
json_populate_type(Datum json_val, Oid json_type, Oid typid, int32 typmod,
3110+
void **cache, MemoryContext mcxt, bool *isnull)
3111+
{
3112+
JsValue jsv = { 0 };
3113+
JsonbValue jbv;
3114+
3115+
jsv.is_json = json_type == JSONOID;
3116+
3117+
if (*isnull)
3118+
{
3119+
if (jsv.is_json)
3120+
jsv.val.json.str = NULL;
3121+
else
3122+
jsv.val.jsonb = NULL;
3123+
}
3124+
else if (jsv.is_json)
3125+
{
3126+
text *json = DatumGetTextPP(json_val);
3127+
3128+
jsv.val.json.str = VARDATA_ANY(json);
3129+
jsv.val.json.len = VARSIZE_ANY_EXHDR(json);
3130+
jsv.val.json.type = JSON_TOKEN_INVALID; /* not used in populate_composite() */
3131+
}
3132+
else
3133+
{
3134+
Jsonb *jsonb = DatumGetJsonbP(json_val);
3135+
3136+
jsv.val.jsonb = &jbv;
3137+
3138+
/* fill binary jsonb value pointing to jb */
3139+
jbv.type = jbvBinary;
3140+
jbv.val.binary.data = &jsonb->root;
3141+
jbv.val.binary.len = VARSIZE(jsonb) - VARHDRSZ;
3142+
}
3143+
3144+
if (!*cache)
3145+
*cache = MemoryContextAllocZero(mcxt, sizeof(ColumnIOData));
3146+
3147+
return populate_record_field(*cache , typid, typmod, NULL, mcxt,
3148+
PointerGetDatum(NULL), &jsv, isnull);
3149+
}
3150+
31073151
static RecordIOData *
31083152
allocate_record_info(MemoryContext mcxt, int ncolumns)
31093153
{

src/include/executor/execExpr.h

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

687+
void *cache; /* cache for json_populate_type() */
688+
687689
struct JsonCoercionsState
688690
{
689691
struct JsonCoercionState

src/include/utils/jsonfuncs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,8 @@ extern Jsonb *transform_jsonb_string_values(Jsonb *jsonb, void *action_state,
5858
extern text *transform_json_string_values(text *json, void *action_state,
5959
JsonTransformStringValuesAction transform_action);
6060

61+
extern Datum json_populate_type(Datum json_val, Oid json_type,
62+
Oid typid, int32 typmod,
63+
void **cache, MemoryContext mcxt, bool *isnull);
64+
6165
#endif

src/test/regress/expected/jsonb_sqljson.out

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,53 @@ FROM
758758
4 | 4 | [4]
759759
(25 rows)
760760

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

src/test/regress/sql/jsonb_sqljson.sql

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

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

219235
CREATE TABLE test_jsonb_constraints (

0 commit comments

Comments
 (0)