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

Commit 4406615

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

File tree

9 files changed

+129
-6
lines changed

9 files changed

+129
-6
lines changed

src/backend/executor/execExpr.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2094,6 +2094,8 @@ ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
20942094
lappend(scratch.d.jsonexpr.args, var);
20952095
}
20962096

2097+
scratch.d.jsonexpr.cache = NULL;
2098+
20972099
memset(&scratch.d.jsonexpr.scalar, 0,
20982100
sizeof(scratch.d.jsonexpr.scalar));
20992101

src/backend/executor/execExprInterp.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
#include "pgstat.h"
7373
#include "utils/builtins.h"
7474
#include "utils/date.h"
75+
#include "utils/jsonapi.h"
7576
#include "utils/jsonb.h"
7677
#include "utils/jsonpath.h"
7778
#include "utils/lsyscache.h"
@@ -3690,6 +3691,13 @@ ExecEvalJsonExprCoercion(ExprEvalStep *op, ExprContext *econtext,
36903691
else if (op->d.jsonexpr.result_expr)
36913692
res = ExecEvalExprPassingCaseValue(op->d.jsonexpr.result_expr, econtext,
36923693
isNull, res, *isNull);
3694+
else if (jexpr->coerce_via_populate)
3695+
res = json_populate_type(res, JSONBOID,
3696+
jexpr->returning.typid,
3697+
jexpr->returning.typmod,
3698+
&op->d.jsonexpr.cache,
3699+
econtext->ecxt_per_query_memory,
3700+
isNull);
36933701
/* else no coercion, simply return item */
36943702

36953703
return res;

src/backend/parser/parse_expr.c

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4483,11 +4483,10 @@ coerceJsonExpr(ParseState *pstate, Node *expr, JsonReturning *returning,
44834483

44844484
typtype = get_typtype(returning->typid);
44854485

4486-
if (coerce_via_populate &&
4487-
(returning->typid == RECORDOID ||
4488-
typtype == TYPTYPE_COMPOSITE ||
4489-
typtype == TYPTYPE_DOMAIN ||
4490-
type_is_array(returning->typid)))
4486+
if (returning->typid == RECORDOID ||
4487+
typtype == TYPTYPE_COMPOSITE ||
4488+
typtype == TYPTYPE_DOMAIN ||
4489+
type_is_array(returning->typid))
44914490
*coerce_via_populate = true;
44924491
else
44934492
*coerce_via_io = true;
@@ -4542,7 +4541,7 @@ transformJsonFuncExprOutput(ParseState *pstate, JsonFuncExpr *func,
45424541
jsexpr->result_expr = coerceJsonExpr(pstate, placeholder,
45434542
&jsexpr->returning,
45444543
&jsexpr->coerce_via_io,
4545-
NULL);
4544+
&jsexpr->coerce_via_populate);
45464545
}
45474546
}
45484547
else

src/backend/utils/adt/jsonfuncs.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2978,6 +2978,50 @@ populate_record_field(ColumnIOData *col,
29782978
}
29792979
}
29802980

2981+
/* recursively populate specified type from a json/jsonb value */
2982+
Datum
2983+
json_populate_type(Datum json_val, Oid json_type, Oid typid, int32 typmod,
2984+
void **cache, MemoryContext mcxt, bool *isnull)
2985+
{
2986+
JsValue jsv = { 0 };
2987+
JsonbValue jbv;
2988+
2989+
jsv.is_json = json_type == JSONOID;
2990+
2991+
if (*isnull)
2992+
{
2993+
if (jsv.is_json)
2994+
jsv.val.json.str = NULL;
2995+
else
2996+
jsv.val.jsonb = NULL;
2997+
}
2998+
else if (jsv.is_json)
2999+
{
3000+
text *json = DatumGetTextPP(json_val);
3001+
3002+
jsv.val.json.str = VARDATA_ANY(json);
3003+
jsv.val.json.len = VARSIZE_ANY_EXHDR(json);
3004+
jsv.val.json.type = JSON_TOKEN_INVALID; /* not used in populate_composite() */
3005+
}
3006+
else
3007+
{
3008+
Jsonb *jsonb = DatumGetJsonbP(json_val);
3009+
3010+
jsv.val.jsonb = &jbv;
3011+
3012+
/* fill binary jsonb value pointing to jb */
3013+
jbv.type = jbvBinary;
3014+
jbv.val.binary.data = &jsonb->root;
3015+
jbv.val.binary.len = VARSIZE(jsonb) - VARHDRSZ;
3016+
}
3017+
3018+
if (!*cache)
3019+
*cache = MemoryContextAllocZero(mcxt, sizeof(ColumnIOData));
3020+
3021+
return populate_record_field(*cache , typid, typmod, NULL, mcxt,
3022+
PointerGetDatum(NULL), &jsv, isnull);
3023+
}
3024+
29813025
static RecordIOData *
29823026
allocate_record_info(MemoryContext mcxt, int ncolumns)
29833027
{

src/include/executor/execExpr.h

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

589+
void *cache; /* cache for json_populate_type() */
590+
589591
struct JsonScalarCoercions
590592
{
591593
struct JsonScalarCoercionExprState

src/include/nodes/primnodes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,6 +1284,7 @@ typedef struct JsonExpr
12841284
Node *raw_expr; /* raw context item expression */
12851285
Node *formatted_expr; /* formatted context item expression */
12861286
Node *result_expr; /* resulting expression (coerced to RETURNING type) */
1287+
bool coerce_via_populate; /* coerce result using json_populate_type() */
12871288
bool coerce_via_io; /* coerce result using type input function */
12881289
Oid coerce_via_io_collation; /* collation for conversion through I/O */
12891290
JsonFormat format; /* context item format (JSON/JSONB) */

src/include/utils/jsonapi.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,10 @@ extern Jsonb *transform_jsonb_string_values(Jsonb *jsonb, void *action_state,
190190
extern text *transform_json_string_values(text *json, void *action_state,
191191
JsonTransformStringValuesAction transform_action);
192192

193+
extern Datum json_populate_type(Datum json_val, Oid json_type,
194+
Oid typid, int32 typmod,
195+
void **cache, MemoryContext mcxt, bool *isnull);
196+
193197
extern Json *JsonCreate(text *json);
194198
extern JsonbIteratorToken JsonIteratorNext(JsonIterator **pit, JsonbValue *val,
195199
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+
-- Conversion to record types
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+
?column?
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+
-- Conversion to array types
779+
SELECT JSON_QUERY(jsonb '[1,2,null,"3"]', '$[*]' RETURNING int[] WITH WRAPPER);
780+
?column?
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+
-- Conversion to domain types
793+
SELECT JSON_QUERY(jsonb '{"a": 1}', '$.a' RETURNING sqljsonb_int_not_null);
794+
?column?
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+
-- Conversion to record types
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+
-- Conversion to array types
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+
-- Conversion to domain types
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)