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

Commit c17a4be

Browse files
author
Nikita Glukhov
committed
Add JSON_VALUE, JSON_EXISTS, JSON_QUERY
1 parent 38267c6 commit c17a4be

26 files changed

+2502
-13
lines changed

contrib/pg_stat_statements/pg_stat_statements.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2865,6 +2865,17 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
28652865
APP_JUMB(opts->value_type);
28662866
}
28672867
break;
2868+
case T_JsonExpr:
2869+
{
2870+
JsonExpr *jexpr = (JsonExpr *) node;
2871+
2872+
APP_JUMB(jexpr->op);
2873+
JumbleExpr(jstate, jexpr->raw_expr);
2874+
JumbleExpr(jstate, (Node *) jexpr->passing.values);
2875+
JumbleExpr(jstate, jexpr->on_empty.default_expr);
2876+
JumbleExpr(jstate, jexpr->on_error.default_expr);
2877+
}
2878+
break;
28682879
case T_List:
28692880
foreach(temp, (List *) node)
28702881
{

src/backend/executor/execExpr.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include "optimizer/planner.h"
4444
#include "pgstat.h"
4545
#include "utils/builtins.h"
46+
#include "utils/jsonpath.h"
4647
#include "utils/lsyscache.h"
4748
#include "utils/typcache.h"
4849

@@ -2031,6 +2032,72 @@ ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
20312032
parent, state, resv, resnull);
20322033
break;
20332034

2035+
case T_JsonExpr:
2036+
{
2037+
JsonExpr *jexpr = castNode(JsonExpr, node);
2038+
ListCell *argexprlc;
2039+
ListCell *argnamelc;
2040+
2041+
scratch.opcode = EEOP_JSONEXPR;
2042+
scratch.d.jsonexpr.jsexpr = jexpr;
2043+
2044+
scratch.d.jsonexpr.raw_expr =
2045+
palloc(sizeof(*scratch.d.jsonexpr.raw_expr));
2046+
2047+
ExecInitExprRec((Expr *) jexpr->raw_expr, parent, state,
2048+
&scratch.d.jsonexpr.raw_expr->value,
2049+
&scratch.d.jsonexpr.raw_expr->isnull);
2050+
2051+
scratch.d.jsonexpr.formatted_expr =
2052+
ExecInitExpr((Expr *) jexpr->formatted_expr, parent);
2053+
2054+
scratch.d.jsonexpr.result_expr =
2055+
ExecInitExpr((Expr *) jexpr->result_expr, parent);
2056+
2057+
scratch.d.jsonexpr.default_on_empty =
2058+
ExecInitExpr((Expr *) jexpr->on_empty.default_expr, parent);
2059+
2060+
scratch.d.jsonexpr.default_on_error =
2061+
ExecInitExpr((Expr *) jexpr->on_error.default_expr, parent);
2062+
2063+
if (jexpr->coerce_via_io || jexpr->omit_quotes)
2064+
{
2065+
Oid typinput;
2066+
2067+
/* lookup the result type's input function */
2068+
getTypeInputInfo(jexpr->returning.typid, &typinput,
2069+
&scratch.d.jsonexpr.input.typioparam);
2070+
fmgr_info(typinput, &scratch.d.jsonexpr.input.func);
2071+
}
2072+
2073+
scratch.d.jsonexpr.args = NIL;
2074+
2075+
forboth(argexprlc, jexpr->passing.values,
2076+
argnamelc, jexpr->passing.names)
2077+
{
2078+
Expr *argexpr = (Expr *) lfirst(argexprlc);
2079+
Value *argname = (Value *) lfirst(argnamelc);
2080+
JsonPathVariableEvalContext *var = palloc(sizeof(*var));
2081+
2082+
var->var.varName = cstring_to_text(argname->val.str);
2083+
var->var.typid = exprType((Node *) argexpr);
2084+
var->var.typmod = exprTypmod((Node *) argexpr);
2085+
var->var.cb = EvalJsonPathVar;
2086+
var->var.cb_arg = var;
2087+
var->estate = ExecInitExpr(argexpr, parent);
2088+
var->econtext = NULL;
2089+
var->evaluated = false;
2090+
var->value = (Datum) 0;
2091+
var->isnull = true;
2092+
2093+
scratch.d.jsonexpr.args =
2094+
lappend(scratch.d.jsonexpr.args, var);
2095+
}
2096+
2097+
ExprEvalPushStep(state, &scratch);
2098+
}
2099+
break;
2100+
20342101
default:
20352102
elog(ERROR, "unrecognized node type: %d",
20362103
(int) nodeTag(node));

src/backend/executor/execExprInterp.c

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@
7070
#include "pgstat.h"
7171
#include "utils/builtins.h"
7272
#include "utils/date.h"
73+
#include "utils/jsonb.h"
74+
#include "utils/jsonpath.h"
7375
#include "utils/lsyscache.h"
7476
#include "utils/timestamp.h"
7577
#include "utils/typcache.h"
@@ -363,6 +365,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
363365
&&CASE_EEOP_WINDOW_FUNC,
364366
&&CASE_EEOP_SUBPLAN,
365367
&&CASE_EEOP_ALTERNATIVE_SUBPLAN,
368+
&&CASE_EEOP_JSONEXPR,
366369
&&CASE_EEOP_LAST
367370
};
368371

@@ -1506,6 +1509,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
15061509
EEO_NEXT();
15071510
}
15081511

1512+
EEO_CASE(EEOP_JSONEXPR)
1513+
{
1514+
/* too complex for an inline implementation */
1515+
ExecEvalJson(state, op, econtext);
1516+
EEO_NEXT();
1517+
}
1518+
15091519
EEO_CASE(EEOP_LAST)
15101520
{
15111521
/* unreachable */
@@ -3579,3 +3589,251 @@ ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
35793589
*op->resvalue = PointerGetDatum(dtuple);
35803590
*op->resnull = false;
35813591
}
3592+
3593+
/*
3594+
* Evaluate a expression substituting specified value in its CaseTestExpr nodes.
3595+
*/
3596+
static Datum
3597+
ExecEvalExprPassingCaseValue(ExprState *estate, ExprContext *econtext,
3598+
bool *isnull,
3599+
Datum caseval_datum, bool caseval_isnull)
3600+
{
3601+
Datum res;
3602+
Datum save_datum = econtext->caseValue_datum;
3603+
bool save_isNull = econtext->caseValue_isNull;
3604+
3605+
econtext->caseValue_datum = caseval_datum;
3606+
econtext->caseValue_isNull = caseval_isnull;
3607+
3608+
PG_TRY();
3609+
{
3610+
res = ExecEvalExpr(estate, econtext, isnull);
3611+
}
3612+
PG_CATCH();
3613+
{
3614+
econtext->caseValue_datum = save_datum;
3615+
econtext->caseValue_isNull = save_isNull;
3616+
3617+
PG_RE_THROW();
3618+
}
3619+
PG_END_TRY();
3620+
3621+
econtext->caseValue_datum = save_datum;
3622+
econtext->caseValue_isNull = save_isNull;
3623+
3624+
return res;
3625+
}
3626+
3627+
/*
3628+
* Evaluate a JSON error/empty behavior result.
3629+
*/
3630+
static Datum
3631+
ExecEvalJsonBehavior(ExprContext *econtext, JsonBehavior *behavior,
3632+
ExprState *default_estate, bool is_jsonb, bool *is_null)
3633+
{
3634+
*is_null = false;
3635+
3636+
switch (behavior->btype)
3637+
{
3638+
case JSON_BEHAVIOR_EMPTY_ARRAY:
3639+
return is_jsonb ?
3640+
JsonbPGetDatum(JsonbMakeEmptyArray()) :
3641+
PointerGetDatum(cstring_to_text("[]"));
3642+
3643+
case JSON_BEHAVIOR_EMPTY_OBJECT:
3644+
return is_jsonb ?
3645+
JsonbPGetDatum(JsonbMakeEmptyObject()) :
3646+
PointerGetDatum(cstring_to_text("{}"));
3647+
3648+
case JSON_BEHAVIOR_TRUE:
3649+
return BoolGetDatum(true);
3650+
3651+
case JSON_BEHAVIOR_FALSE:
3652+
return BoolGetDatum(false);
3653+
3654+
case JSON_BEHAVIOR_NULL:
3655+
case JSON_BEHAVIOR_UNKNOWN:
3656+
*is_null = true;
3657+
return (Datum) 0;
3658+
3659+
case JSON_BEHAVIOR_DEFAULT:
3660+
return ExecEvalExpr(default_estate, econtext, is_null);
3661+
3662+
default:
3663+
elog(ERROR, "unrecognized SQL/JSON behavior %d", behavior->btype);
3664+
return (Datum) 0;
3665+
}
3666+
}
3667+
3668+
/*
3669+
* Evaluate a coercion of a JSON item to the target type.
3670+
*/
3671+
static Datum
3672+
ExecEvalJsonExprCoercion(ExprEvalStep *op, ExprContext *econtext,
3673+
Datum res, bool *isNull)
3674+
{
3675+
JsonExpr *jexpr = op->d.jsonexpr.jsexpr;
3676+
Jsonb *jb = *isNull ? NULL : DatumGetJsonbP(res);
3677+
3678+
if (jexpr->coerce_via_io ||
3679+
(jexpr->omit_quotes && !*isNull && JB_ROOT_IS_SCALAR(jb)))
3680+
{
3681+
/* strip quotes and call typinput function */
3682+
char *str = *isNull ? NULL : JsonbUnquote(jb);
3683+
3684+
res = InputFunctionCall(&op->d.jsonexpr.input.func, str,
3685+
op->d.jsonexpr.input.typioparam,
3686+
jexpr->returning.typmod);
3687+
}
3688+
else if (op->d.jsonexpr.result_expr)
3689+
res = ExecEvalExprPassingCaseValue(op->d.jsonexpr.result_expr, econtext,
3690+
isNull, res, *isNull);
3691+
/* else no coercion, simply return item */
3692+
3693+
return res;
3694+
}
3695+
3696+
/*
3697+
* Evaluate a JSON path variable caching computed value.
3698+
*/
3699+
Datum
3700+
EvalJsonPathVar(void *cxt, bool *isnull)
3701+
{
3702+
JsonPathVariableEvalContext *ecxt = cxt;
3703+
3704+
if (!ecxt->evaluated)
3705+
{
3706+
ecxt->value = ExecEvalExpr(ecxt->estate, ecxt->econtext, &ecxt->isnull);
3707+
ecxt->evaluated = true;
3708+
}
3709+
3710+
*isnull = ecxt->isnull;
3711+
return ecxt->value;
3712+
}
3713+
3714+
/* ----------------------------------------------------------------
3715+
* ExecEvalJson
3716+
* ----------------------------------------------------------------
3717+
*/
3718+
void
3719+
ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
3720+
{
3721+
JsonExpr *jexpr = op->d.jsonexpr.jsexpr;
3722+
Datum item;
3723+
Datum res = (Datum) 0;
3724+
JsonPath *path;
3725+
ListCell *lc;
3726+
Oid formattedType = exprType(jexpr->formatted_expr ?
3727+
jexpr->formatted_expr :
3728+
jexpr->raw_expr);
3729+
bool isjsonb = formattedType == JSONBOID;
3730+
MemoryContext mcxt = CurrentMemoryContext;
3731+
3732+
*op->resnull = true; /* until we get a result */
3733+
*op->resvalue = (Datum) 0;
3734+
3735+
if (op->d.jsonexpr.raw_expr->isnull)
3736+
{
3737+
/* execute domain checks for NULLs */
3738+
(void) ExecEvalJsonExprCoercion(op, econtext, res, op->resnull);
3739+
return;
3740+
}
3741+
3742+
item = op->d.jsonexpr.raw_expr->value;
3743+
3744+
path = DatumGetJsonPathP(jexpr->path_spec->constvalue);
3745+
3746+
/* reset JSON path variable contexts */
3747+
foreach(lc, op->d.jsonexpr.args)
3748+
{
3749+
JsonPathVariableEvalContext *var = lfirst(lc);
3750+
3751+
var->econtext = econtext;
3752+
var->evaluated = false;
3753+
}
3754+
3755+
PG_TRY();
3756+
{
3757+
bool empty = false;
3758+
3759+
if (op->d.jsonexpr.formatted_expr)
3760+
{
3761+
bool isnull;
3762+
3763+
item = ExecEvalExprPassingCaseValue(op->d.jsonexpr.formatted_expr,
3764+
econtext, &isnull, item, false);
3765+
if (isnull)
3766+
{
3767+
/* execute domain checks for NULLs */
3768+
(void) ExecEvalJsonExprCoercion(op, econtext, res, op->resnull);
3769+
return;
3770+
}
3771+
}
3772+
3773+
switch (jexpr->op)
3774+
{
3775+
case IS_JSON_QUERY:
3776+
res = JsonbPathQuery(item, path, jexpr->wrapper, &empty,
3777+
op->d.jsonexpr.args);
3778+
*op->resnull = !DatumGetPointer(res);
3779+
break;
3780+
3781+
case IS_JSON_VALUE:
3782+
res = JsonbPathValue(item, path, &empty, op->d.jsonexpr.args);
3783+
*op->resnull = !DatumGetPointer(res);
3784+
break;
3785+
3786+
case IS_JSON_EXISTS:
3787+
res = BoolGetDatum(JsonbPathExists(item, path,
3788+
op->d.jsonexpr.args));
3789+
*op->resnull = false;
3790+
break;
3791+
3792+
default:
3793+
elog(ERROR, "unrecognized SQL/JSON expression op %d",
3794+
jexpr->op);
3795+
return;
3796+
}
3797+
3798+
if (empty)
3799+
{
3800+
if (jexpr->on_empty.btype == JSON_BEHAVIOR_ERROR)
3801+
ereport(ERROR,
3802+
(errcode(ERRCODE_NO_JSON_ITEM),
3803+
errmsg("no SQL/JSON item")));
3804+
3805+
/* execute ON EMPTY behavior */
3806+
res = ExecEvalJsonBehavior(econtext, &jexpr->on_empty,
3807+
op->d.jsonexpr.default_on_empty,
3808+
isjsonb, op->resnull);
3809+
}
3810+
3811+
if (jexpr->op != IS_JSON_EXISTS &&
3812+
(!empty ||
3813+
/* result is already coerced in DEFAULT behavior case */
3814+
jexpr->on_empty.btype != JSON_BEHAVIOR_DEFAULT))
3815+
res = ExecEvalJsonExprCoercion(op, econtext, res, op->resnull);
3816+
}
3817+
PG_CATCH();
3818+
{
3819+
if (jexpr->on_error.btype == JSON_BEHAVIOR_ERROR ||
3820+
ERRCODE_TO_CATEGORY(geterrcode()) != ERRCODE_DATA_EXCEPTION)
3821+
PG_RE_THROW();
3822+
3823+
FlushErrorState();
3824+
MemoryContextSwitchTo(mcxt);
3825+
3826+
/* execute ON ERROR behavior */
3827+
res = ExecEvalJsonBehavior(econtext, &jexpr->on_error,
3828+
op->d.jsonexpr.default_on_error,
3829+
isjsonb, op->resnull);
3830+
3831+
if (jexpr->op != IS_JSON_EXISTS &&
3832+
/* result is already coerced in DEFAULT behavior case */
3833+
jexpr->on_error.btype != JSON_BEHAVIOR_DEFAULT)
3834+
res = ExecEvalJsonExprCoercion(op, econtext, res, op->resnull);
3835+
}
3836+
PG_END_TRY();
3837+
3838+
*op->resvalue = res;
3839+
}

0 commit comments

Comments
 (0)