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

Commit c84d3a8

Browse files
author
Nikita Glukhov
committed
Optimize and fix JSON item coercion to target type in JSON_VALUE
1 parent 36499c7 commit c84d3a8

File tree

9 files changed

+248
-17
lines changed

9 files changed

+248
-17
lines changed

src/backend/executor/execExpr.c

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

2099+
memset(&scratch.d.jsonexpr.scalar, 0,
2100+
sizeof(scratch.d.jsonexpr.scalar));
2101+
20992102
ExprEvalPushStep(state, &scratch);
21002103
}
21012104
break;

src/backend/executor/execExprInterp.c

Lines changed: 157 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,10 @@
6565
#include "executor/nodeSubplan.h"
6666
#include "funcapi.h"
6767
#include "miscadmin.h"
68+
#include "nodes/makefuncs.h"
6869
#include "nodes/nodeFuncs.h"
6970
#include "parser/parsetree.h"
71+
#include "parser/parse_expr.h"
7072
#include "pgstat.h"
7173
#include "utils/builtins.h"
7274
#include "utils/date.h"
@@ -3697,6 +3699,126 @@ EvalJsonPathVar(void *cxt, bool *isnull)
36973699
return ecxt->value;
36983700
}
36993701

3702+
Datum
3703+
ExecPrepareJsonItemCoercion(JsonbValue *jbv, JsonReturning *returning,
3704+
struct JsonScalarCoercions *coercions,
3705+
MemoryContext mcxt,
3706+
struct JsonScalarCoercionExprState **pcestate)
3707+
{
3708+
struct JsonScalarCoercionExprState *cestate;
3709+
Datum res;
3710+
Oid typid;
3711+
JsonbValue jbvbuf;
3712+
3713+
if (jbv->type == jbvBinary && JsonContainerIsScalar(jbv->val.binary.data))
3714+
jbv = JsonbExtractScalar(jbv->val.binary.data, &jbvbuf);
3715+
3716+
switch (jbv->type)
3717+
{
3718+
case jbvNull:
3719+
cestate = &coercions->null;
3720+
typid = UNKNOWNOID;
3721+
res = (Datum) 0;
3722+
break;
3723+
3724+
case jbvString:
3725+
cestate = &coercions->string;
3726+
typid = TEXTOID;
3727+
res = PointerGetDatum(
3728+
cstring_to_text_with_len(jbv->val.string.val,
3729+
jbv->val.string.len));
3730+
break;
3731+
3732+
case jbvNumeric:
3733+
cestate = &coercions->numeric;
3734+
typid = NUMERICOID;
3735+
res = NumericGetDatum(jbv->val.numeric);
3736+
break;
3737+
3738+
case jbvBool:
3739+
cestate = &coercions->boolean;
3740+
typid = BOOLOID;
3741+
res = BoolGetDatum(jbv->val.boolean);
3742+
break;
3743+
3744+
case jbvDatetime:
3745+
res = jbv->val.datetime.value;
3746+
typid = jbv->val.datetime.typid;
3747+
switch (jbv->val.datetime.typid)
3748+
{
3749+
case DATEOID:
3750+
cestate = &coercions->date;
3751+
break;
3752+
case TIMEOID:
3753+
cestate = &coercions->time;
3754+
break;
3755+
case TIMETZOID:
3756+
cestate = &coercions->timetz;
3757+
break;
3758+
case TIMESTAMPOID:
3759+
cestate = &coercions->timestamp;
3760+
break;
3761+
case TIMESTAMPTZOID:
3762+
cestate = &coercions->timestamptz;
3763+
break;
3764+
default:
3765+
elog(ERROR, "unexpected jsonb datetime type oid %d",
3766+
jbv->val.datetime.typid);
3767+
return (Datum) 0;
3768+
}
3769+
break;
3770+
3771+
case jbvArray:
3772+
case jbvObject:
3773+
case jbvBinary:
3774+
cestate = &coercions->composite;
3775+
res = JsonbGetDatum(JsonbValueToJsonb(jbv));
3776+
typid = JSONBOID;
3777+
break;
3778+
3779+
default:
3780+
elog(ERROR, "unexpected jsonb value type %d", jbv->type);
3781+
return (Datum) 0;
3782+
}
3783+
3784+
if (!cestate->initialized)
3785+
{
3786+
MemoryContext oldCxt = MemoryContextSwitchTo(mcxt);
3787+
Node *expr;
3788+
3789+
if (jbv->type == jbvNull)
3790+
{
3791+
expr = (Node *) makeNullConst(UNKNOWNOID, -1, InvalidOid);
3792+
}
3793+
else
3794+
{
3795+
CaseTestExpr *placeholder = makeNode(CaseTestExpr);
3796+
3797+
placeholder->typeId = typid;
3798+
placeholder->typeMod = -1;
3799+
placeholder->collation = InvalidOid;
3800+
3801+
expr = (Node *) placeholder;
3802+
}
3803+
3804+
cestate->result_expr =
3805+
coerceJsonExpr(NULL, expr, returning,
3806+
&cestate->coerce_via_io,
3807+
&cestate->coerce_via_populate);
3808+
3809+
cestate->result_expr_state =
3810+
ExecInitExpr((Expr *) cestate->result_expr, NULL);
3811+
3812+
MemoryContextSwitchTo(oldCxt);
3813+
3814+
cestate->initialized = true;
3815+
}
3816+
3817+
*pcestate = cestate;
3818+
3819+
return res;
3820+
}
3821+
37003822
/* ----------------------------------------------------------------
37013823
* ExecEvalJson
37023824
* ----------------------------------------------------------------
@@ -3763,8 +3885,40 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
37633885
break;
37643886

37653887
case IS_JSON_VALUE:
3766-
res = JsonbPathValue(item, path, &empty, op->d.jsonexpr.args);
3767-
*op->resnull = !DatumGetPointer(res);
3888+
{
3889+
JsonbValue *jbv = JsonbPathValue(item, path, &empty,
3890+
op->d.jsonexpr.args);
3891+
struct JsonScalarCoercionExprState *cestate;
3892+
3893+
if (!jbv)
3894+
break;
3895+
3896+
*op->resnull = false;
3897+
3898+
res = ExecPrepareJsonItemCoercion(jbv,
3899+
&op->d.jsonexpr.jsexpr->returning,
3900+
&op->d.jsonexpr.scalar,
3901+
econtext->ecxt_per_query_memory,
3902+
&cestate);
3903+
3904+
if (cestate->coerce_via_io ||
3905+
cestate->coerce_via_populate || /* ignored for scalars jsons */
3906+
(cestate->result_expr &&
3907+
IsA(cestate->result_expr, CoerceViaIO)))
3908+
{
3909+
res = JsonbGetDatum(JsonbValueToJsonb(jbv));
3910+
res = ExecEvalJsonExprCoercion(op, econtext,
3911+
res, op->resnull);
3912+
}
3913+
else if (cestate->result_expr_state)
3914+
{
3915+
res = ExecEvalExprPassingCaseValue(cestate->result_expr_state,
3916+
econtext,
3917+
op->resnull,
3918+
res, false);
3919+
}
3920+
/* else no coercion */
3921+
}
37683922
break;
37693923

37703924
case IS_JSON_EXISTS:
@@ -3792,7 +3946,7 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
37923946
}
37933947

37943948
if (jexpr->op != IS_JSON_EXISTS &&
3795-
(!empty ||
3949+
(!empty ? jexpr->op != IS_JSON_VALUE :
37963950
/* already coerced in DEFAULT case */
37973951
jexpr->on_empty.btype != JSON_BEHAVIOR_DEFAULT))
37983952
res = ExecEvalJsonExprCoercion(op, econtext, res, op->resnull);

src/backend/parser/parse_expr.c

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4300,6 +4300,43 @@ assignDefaultJsonReturningType(Node *context_item, JsonFormat *context_format,
43004300
ret->typmod = -1;
43014301
}
43024302

4303+
Node *
4304+
coerceJsonExpr(ParseState *pstate, Node *expr, JsonReturning *returning,
4305+
bool *coerce_via_io, bool *coerce_via_populate)
4306+
{
4307+
Node *res = coerceJsonFuncExpr(pstate, expr, returning, false);
4308+
char typtype;
4309+
4310+
if (res)
4311+
{
4312+
if (res == expr)
4313+
return NULL;
4314+
4315+
#if 0 /* FIXME */
4316+
if (IsA(res, CoerceViaIO))
4317+
{
4318+
*coerce_via_io = true;
4319+
return NULL;
4320+
}
4321+
#endif
4322+
return res;
4323+
}
4324+
4325+
typtype = get_typtype(returning->typid);
4326+
4327+
if (!coerce_via_populate)
4328+
*coerce_via_io = true;
4329+
else if (returning->typid == RECORDOID ||
4330+
typtype == TYPTYPE_COMPOSITE ||
4331+
typtype == TYPTYPE_DOMAIN ||
4332+
type_is_array(returning->typid))
4333+
*coerce_via_populate = true;
4334+
else
4335+
*coerce_via_io = true;
4336+
4337+
return NULL;
4338+
}
4339+
43034340
static void
43044341
transformJsonFuncExprOutput(ParseState *pstate, JsonFuncExpr *func,
43054342
JsonExpr *jsexpr)
@@ -4333,15 +4370,10 @@ transformJsonFuncExprOutput(ParseState *pstate, JsonFuncExpr *func,
43334370
Assert(((CaseTestExpr *) placeholder)->typeId == ret.typid);
43344371
Assert(((CaseTestExpr *) placeholder)->typeMod == ret.typmod);
43354372

4336-
jsexpr->result_expr = coerceJsonFuncExpr(pstate,
4337-
placeholder,
4338-
&jsexpr->returning,
4339-
false);
4340-
4341-
if (!jsexpr->result_expr)
4342-
jsexpr->coerce_via_io = true;
4343-
else if (jsexpr->result_expr == placeholder)
4344-
jsexpr->result_expr = NULL;
4373+
jsexpr->result_expr = coerceJsonExpr(pstate, placeholder,
4374+
&jsexpr->returning,
4375+
&jsexpr->coerce_via_io,
4376+
NULL);
43454377
}
43464378
}
43474379
else

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2992,7 +2992,7 @@ JsonbPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper,
29922992
return PointerGetDatum(NULL);
29932993
}
29942994

2995-
Datum
2995+
JsonbValue *
29962996
JsonbPathValue(Datum jb, JsonPath *jp, bool *empty, List *vars)
29972997
{
29982998
JsonbValue *res;
@@ -3008,7 +3008,7 @@ JsonbPathValue(Datum jb, JsonPath *jp, bool *empty, List *vars)
30083008
*empty = !count;
30093009

30103010
if (*empty)
3011-
return PointerGetDatum(NULL);
3011+
return NULL;
30123012

30133013
if (count > 1)
30143014
ereport(ERROR,
@@ -3027,7 +3027,7 @@ JsonbPathValue(Datum jb, JsonPath *jp, bool *empty, List *vars)
30273027
errmsg("SQL/JSON scalar required")));
30283028

30293029
if (res->type == jbvNull)
3030-
return PointerGetDatum(NULL);
3030+
return NULL;
30313031

3032-
return JsonbGetDatum(JsonbValueToJsonb(res));
3032+
return res;
30333033
}

src/include/executor/execExpr.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
/* forward reference to avoid circularity */
2020
struct ArrayRefState;
21+
struct JsonbValue;
2122

2223
/* Bits in ExprState->flags (see also execnodes.h for public flag bits): */
2324
/* expression's interpreter has been initialized */
@@ -584,6 +585,27 @@ typedef struct ExprEvalStep
584585
ExprState *default_on_empty;
585586
ExprState *default_on_error;
586587
List *args;
588+
589+
struct JsonScalarCoercions
590+
{
591+
struct JsonScalarCoercionExprState
592+
{
593+
Node *result_expr;
594+
ExprState *result_expr_state;
595+
bool coerce_via_io;
596+
bool coerce_via_populate;
597+
bool initialized;
598+
} string,
599+
numeric,
600+
boolean,
601+
date,
602+
time,
603+
timetz,
604+
timestamp,
605+
timestamptz,
606+
composite,
607+
null;
608+
} scalar;
587609
} jsonexpr;
588610

589611
} d;
@@ -674,5 +696,10 @@ extern void ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op,
674696
ExprContext *econtext);
675697
extern void ExecEvalJson(ExprState *state, ExprEvalStep *op,
676698
ExprContext *econtext);
699+
extern Datum ExecPrepareJsonItemCoercion(struct JsonbValue *jbv,
700+
JsonReturning *returning,
701+
struct JsonScalarCoercions *coercions,
702+
MemoryContext mcxt,
703+
struct JsonScalarCoercionExprState **pcestate);
677704

678705
#endif /* EXEC_EXPR_H */

src/include/parser/parse_expr.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,8 @@ extern Node *transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKin
2323

2424
extern const char *ParseExprKindName(ParseExprKind exprKind);
2525

26+
extern Node *coerceJsonExpr(ParseState *pstate, Node *expr,
27+
JsonReturning *returning,
28+
bool *coerce_via_io, bool *coerce_via_populate);
29+
2630
#endif /* PARSE_EXPR_H */

src/include/utils/jsonpath.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,9 +294,10 @@ JsonPathExecResult executeJsonPath(JsonPath *path,
294294
JsonValueList *foundJson);
295295

296296
extern bool JsonbPathExists(Datum jb, JsonPath *path, List *vars);
297-
extern Datum JsonbPathValue(Datum jb, JsonPath *jp, bool *empty, List *vars);
298297
extern Datum JsonbPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper,
299298
bool *empty, List *vars);
299+
extern JsonbValue *JsonbPathValue(Datum jb, JsonPath *jp, bool *empty,
300+
List *vars);
300301

301302
extern Datum EvalJsonPathVar(void *cxt, bool *isnull);
302303

src/test/regress/expected/jsonb_sqljson.out

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,12 +319,20 @@ SELECT JSON_VALUE(jsonb '1.23', '$');
319319
1.23
320320
(1 row)
321321

322+
SELECT JSON_VALUE(jsonb '1.23', '$' RETURNING int);
323+
?column?
324+
----------
325+
1
326+
(1 row)
327+
322328
SELECT JSON_VALUE(jsonb '"1.23"', '$' RETURNING numeric);
323329
?column?
324330
----------
325331
1.23
326332
(1 row)
327333

334+
SELECT JSON_VALUE(jsonb '"1.23"', '$' RETURNING int ERROR ON ERROR);
335+
ERROR: invalid input syntax for integer: "1.23"
328336
SELECT JSON_VALUE(jsonb '"aaa"', '$');
329337
?column?
330338
----------

src/test/regress/sql/jsonb_sqljson.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,9 @@ SELECT JSON_VALUE(jsonb '123', '$' RETURNING text);
7373
SELECT JSON_VALUE(jsonb '123', '$' RETURNING bytea);
7474

7575
SELECT JSON_VALUE(jsonb '1.23', '$');
76+
SELECT JSON_VALUE(jsonb '1.23', '$' RETURNING int);
7677
SELECT JSON_VALUE(jsonb '"1.23"', '$' RETURNING numeric);
78+
SELECT JSON_VALUE(jsonb '"1.23"', '$' RETURNING int ERROR ON ERROR);
7779

7880
SELECT JSON_VALUE(jsonb '"aaa"', '$');
7981
SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING text);

0 commit comments

Comments
 (0)