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

Commit cf3d961

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

File tree

9 files changed

+125
-4
lines changed

9 files changed

+125
-4
lines changed

src/backend/executor/execExpr.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2096,6 +2096,8 @@ ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
20962096
}
20972097
}
20982098

2099+
scratch.d.jsonexpr.cache = NULL;
2100+
20992101
memset(&scratch.d.jsonexpr.scalar, 0,
21002102
sizeof(scratch.d.jsonexpr.scalar));
21012103

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"
@@ -3680,6 +3681,13 @@ ExecEvalJsonExprCoercion(ExprEvalStep *op, ExprContext *econtext,
36803681
else if (op->d.jsonexpr.result_expr)
36813682
res = ExecEvalExprPassingCaseValue(op->d.jsonexpr.result_expr, econtext,
36823683
isNull, res, *isNull);
3684+
else if (jexpr->coerce_via_populate)
3685+
res = json_populate_type(res, JSONBOID,
3686+
jexpr->returning.typid,
3687+
jexpr->returning.typmod,
3688+
&op->d.jsonexpr.cache,
3689+
econtext->ecxt_per_query_memory,
3690+
isNull);
36833691

36843692
return res;
36853693
}

src/backend/parser/parse_expr.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4324,9 +4324,7 @@ coerceJsonExpr(ParseState *pstate, Node *expr, JsonReturning *returning,
43244324

43254325
typtype = get_typtype(returning->typid);
43264326

4327-
if (!coerce_via_populate)
4328-
*coerce_via_io = true;
4329-
else if (returning->typid == RECORDOID ||
4327+
if (returning->typid == RECORDOID ||
43304328
typtype == TYPTYPE_COMPOSITE ||
43314329
typtype == TYPTYPE_DOMAIN ||
43324330
type_is_array(returning->typid))
@@ -4373,7 +4371,7 @@ transformJsonFuncExprOutput(ParseState *pstate, JsonFuncExpr *func,
43734371
jsexpr->result_expr = coerceJsonExpr(pstate, placeholder,
43744372
&jsexpr->returning,
43754373
&jsexpr->coerce_via_io,
4376-
NULL);
4374+
&jsexpr->coerce_via_populate);
43774375
}
43784376
}
43794377
else

src/backend/utils/adt/jsonfuncs.c

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

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

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;
587587
List *args;
588588

589+
void *cache;
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
@@ -1230,6 +1230,7 @@ typedef struct JsonExpr
12301230
Node *raw_expr; /* raw context item expression */
12311231
Node *formatted_expr; /* formatted context item expression */
12321232
Node *result_expr; /* resulting expression (coerced to RETURNING type) */
1233+
bool coerce_via_populate;
12331234
bool coerce_via_io; /* coerce result using type input function */
12341235
Oid coerce_via_io_collation; /* collation for conversion through I/O */
12351236
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
@@ -189,6 +189,10 @@ extern Jsonb *transform_jsonb_string_values(Jsonb *jsonb, void *action_state,
189189
extern text *transform_json_string_values(text *json, void *action_state,
190190
JsonTransformStringValuesAction transform_action);
191191

192+
extern Datum json_populate_type(Datum json_val, Oid json_type,
193+
Oid typid, int32 typmod,
194+
void **cache, MemoryContext mcxt, bool *isnull);
195+
192196
extern Json *JsonCreate(text *json);
193197
extern JsonbIteratorToken JsonIteratorNext(JsonIterator **pit, JsonbValue *val,
194198
bool skipNested);

src/test/regress/expected/jsonb_sqljson.out

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,53 @@ FROM
866866
4 | 4 | [4]
867867
(25 rows)
868868

869+
-- Conversion to record types
870+
CREATE TYPE sqljsonb_rec AS (a int, t text, js json, jb jsonb, jsa json[]);
871+
CREATE TYPE sqljsonb_reca AS (reca sqljsonb_rec[]);
872+
SELECT JSON_QUERY(jsonb '[{"a": 1, "b": "foo", "t": "aaa", "js": [1, "2", {}], "jb": {"x": [1, "2", {}]}}, {"a": 2}]', '$[0]' RETURNING sqljsonb_rec);
873+
?column?
874+
-----------------------------------------------------
875+
(1,aaa,"[1, ""2"", {}]","{""x"": [1, ""2"", {}]}",)
876+
(1 row)
877+
878+
SELECT * FROM unnest((JSON_QUERY(jsonb '{"jsa": [{"a": 1, "b": ["foo"]}, {"a": 2, "c": {}}, 123]}', '$' RETURNING sqljsonb_rec)).jsa);
879+
unnest
880+
------------------------
881+
{"a": 1, "b": ["foo"]}
882+
{"a": 2, "c": {}}
883+
123
884+
(3 rows)
885+
886+
SELECT * FROM unnest((JSON_QUERY(jsonb '{"reca": [{"a": 1, "t": ["foo", []]}, {"a": 2, "jb": [{}, true]}]}', '$' RETURNING sqljsonb_reca)).reca);
887+
a | t | js | jb | jsa
888+
---+-------------+----+------------+-----
889+
1 | ["foo", []] | | |
890+
2 | | | [{}, true] |
891+
(2 rows)
892+
893+
-- Conversion to array types
894+
SELECT JSON_QUERY(jsonb '[1,2,null,"3"]', '$[*]' RETURNING int[] WITH WRAPPER);
895+
?column?
896+
--------------
897+
{1,2,NULL,3}
898+
(1 row)
899+
900+
SELECT * FROM unnest(JSON_QUERY(jsonb '[{"a": 1, "t": ["foo", []]}, {"a": 2, "jb": [{}, true]}]', '$' RETURNING sqljsonb_rec[]));
901+
a | t | js | jb | jsa
902+
---+-------------+----+------------+-----
903+
1 | ["foo", []] | | |
904+
2 | | | [{}, true] |
905+
(2 rows)
906+
907+
-- Conversion to domain types
908+
SELECT JSON_QUERY(jsonb '{"a": 1}', '$.a' RETURNING sqljsonb_int_not_null);
909+
?column?
910+
----------
911+
1
912+
(1 row)
913+
914+
SELECT JSON_QUERY(jsonb '{"a": 1}', '$.b' RETURNING sqljsonb_int_not_null);
915+
ERROR: domain sqljsonb_int_not_null does not allow null values
869916
-- Test constraints
870917
CREATE TABLE test_jsonb_constraints (
871918
js text,

src/test/regress/sql/jsonb_sqljson.sql

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,22 @@ FROM
237237
generate_series(0, 4) x,
238238
generate_series(0, 4) y;
239239

240+
-- Conversion to record types
241+
CREATE TYPE sqljsonb_rec AS (a int, t text, js json, jb jsonb, jsa json[]);
242+
CREATE TYPE sqljsonb_reca AS (reca sqljsonb_rec[]);
243+
244+
SELECT JSON_QUERY(jsonb '[{"a": 1, "b": "foo", "t": "aaa", "js": [1, "2", {}], "jb": {"x": [1, "2", {}]}}, {"a": 2}]', '$[0]' RETURNING sqljsonb_rec);
245+
SELECT * FROM unnest((JSON_QUERY(jsonb '{"jsa": [{"a": 1, "b": ["foo"]}, {"a": 2, "c": {}}, 123]}', '$' RETURNING sqljsonb_rec)).jsa);
246+
SELECT * FROM unnest((JSON_QUERY(jsonb '{"reca": [{"a": 1, "t": ["foo", []]}, {"a": 2, "jb": [{}, true]}]}', '$' RETURNING sqljsonb_reca)).reca);
247+
248+
-- Conversion to array types
249+
SELECT JSON_QUERY(jsonb '[1,2,null,"3"]', '$[*]' RETURNING int[] WITH WRAPPER);
250+
SELECT * FROM unnest(JSON_QUERY(jsonb '[{"a": 1, "t": ["foo", []]}, {"a": 2, "jb": [{}, true]}]', '$' RETURNING sqljsonb_rec[]));
251+
252+
-- Conversion to domain types
253+
SELECT JSON_QUERY(jsonb '{"a": 1}', '$.a' RETURNING sqljsonb_int_not_null);
254+
SELECT JSON_QUERY(jsonb '{"a": 1}', '$.b' RETURNING sqljsonb_int_not_null);
255+
240256
-- Test constraints
241257

242258
CREATE TABLE test_jsonb_constraints (

0 commit comments

Comments
 (0)