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

Commit 722d08b

Browse files
author
Nikita Glukhov
committed
Add JSON_VALUE, JSON_EXISTS, JSON_QUERY
1 parent d45341c commit 722d08b

25 files changed

+2534
-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
@@ -2805,6 +2805,17 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
28052805
JumbleExpr(jstate, (Node *) conf->exclRelTlist);
28062806
}
28072807
break;
2808+
case T_JsonExpr:
2809+
{
2810+
JsonExpr *jexpr = (JsonExpr *) node;
2811+
2812+
APP_JUMB(jexpr->op);
2813+
JumbleExpr(jstate, jexpr->raw_expr);
2814+
JumbleExpr(jstate, (Node *) jexpr->passing.values);
2815+
JumbleExpr(jstate, jexpr->on_empty.default_expr);
2816+
JumbleExpr(jstate, jexpr->on_error.default_expr);
2817+
}
2818+
break;
28082819
case T_List:
28092820
foreach(temp, (List *) node)
28102821
{

src/backend/executor/execExpr.c

Lines changed: 68 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

@@ -2033,6 +2034,73 @@ ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
20332034
break;
20342035
}
20352036

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

src/backend/executor/execExprInterp.c

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@
6868
#include "pgstat.h"
6969
#include "utils/builtins.h"
7070
#include "utils/date.h"
71+
#include "utils/jsonb.h"
72+
#include "utils/jsonpath.h"
7173
#include "utils/lsyscache.h"
7274
#include "utils/timestamp.h"
7375
#include "utils/typcache.h"
@@ -367,6 +369,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
367369
&&CASE_EEOP_WINDOW_FUNC,
368370
&&CASE_EEOP_SUBPLAN,
369371
&&CASE_EEOP_ALTERNATIVE_SUBPLAN,
372+
&&CASE_EEOP_JSONEXPR,
370373
&&CASE_EEOP_LAST
371374
};
372375

@@ -1535,6 +1538,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
15351538
EEO_NEXT();
15361539
}
15371540

1541+
EEO_CASE(EEOP_JSONEXPR)
1542+
{
1543+
/* too complex for an inline implementation */
1544+
ExecEvalJson(state, op, econtext);
1545+
EEO_NEXT();
1546+
}
1547+
15381548
EEO_CASE(EEOP_LAST)
15391549
{
15401550
/* unreachable */
@@ -3628,3 +3638,251 @@ ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
36283638
*op->resvalue = PointerGetDatum(dtuple);
36293639
*op->resnull = false;
36303640
}
3641+
3642+
/*
3643+
* Evaluate a expression substituting specified value in its CaseTestExpr nodes.
3644+
*/
3645+
static Datum
3646+
ExecEvalExprPassingCaseValue(ExprState *estate, ExprContext *econtext,
3647+
bool *isnull,
3648+
Datum caseval_datum, bool caseval_isnull)
3649+
{
3650+
Datum res;
3651+
Datum save_datum = econtext->caseValue_datum;
3652+
bool save_isNull = econtext->caseValue_isNull;
3653+
3654+
econtext->caseValue_datum = caseval_datum;
3655+
econtext->caseValue_isNull = caseval_isnull;
3656+
3657+
PG_TRY();
3658+
{
3659+
res = ExecEvalExpr(estate, econtext, isnull);
3660+
}
3661+
PG_CATCH();
3662+
{
3663+
econtext->caseValue_datum = save_datum;
3664+
econtext->caseValue_isNull = save_isNull;
3665+
3666+
PG_RE_THROW();
3667+
}
3668+
PG_END_TRY();
3669+
3670+
econtext->caseValue_datum = save_datum;
3671+
econtext->caseValue_isNull = save_isNull;
3672+
3673+
return res;
3674+
}
3675+
3676+
/*
3677+
* Evaluate a JSON error/empty behavior result.
3678+
*/
3679+
static Datum
3680+
ExecEvalJsonBehavior(ExprContext *econtext, JsonBehavior *behavior,
3681+
ExprState *default_estate, bool is_jsonb, bool *is_null)
3682+
{
3683+
*is_null = false;
3684+
3685+
switch (behavior->btype)
3686+
{
3687+
case JSON_BEHAVIOR_EMPTY_ARRAY:
3688+
return is_jsonb ?
3689+
JsonbPGetDatum(JsonbMakeEmptyArray()) :
3690+
PointerGetDatum(cstring_to_text("[]"));
3691+
3692+
case JSON_BEHAVIOR_EMPTY_OBJECT:
3693+
return is_jsonb ?
3694+
JsonbPGetDatum(JsonbMakeEmptyObject()) :
3695+
PointerGetDatum(cstring_to_text("{}"));
3696+
3697+
case JSON_BEHAVIOR_TRUE:
3698+
return BoolGetDatum(true);
3699+
3700+
case JSON_BEHAVIOR_FALSE:
3701+
return BoolGetDatum(false);
3702+
3703+
case JSON_BEHAVIOR_NULL:
3704+
case JSON_BEHAVIOR_UNKNOWN:
3705+
*is_null = true;
3706+
return (Datum) 0;
3707+
3708+
case JSON_BEHAVIOR_DEFAULT:
3709+
return ExecEvalExpr(default_estate, econtext, is_null);
3710+
3711+
default:
3712+
elog(ERROR, "unrecognized SQL/JSON behavior %d", behavior->btype);
3713+
return (Datum) 0;
3714+
}
3715+
}
3716+
3717+
/*
3718+
* Evaluate a coercion of a JSON item to the target type.
3719+
*/
3720+
static Datum
3721+
ExecEvalJsonExprCoercion(ExprEvalStep *op, ExprContext *econtext,
3722+
Datum res, bool *isNull)
3723+
{
3724+
JsonExpr *jexpr = op->d.jsonexpr.jsexpr;
3725+
Jsonb *jb = *isNull ? NULL : DatumGetJsonbP(res);
3726+
3727+
if (jexpr->coerce_via_io ||
3728+
(jexpr->omit_quotes && !*isNull && JB_ROOT_IS_SCALAR(jb)))
3729+
{
3730+
/* strip quotes and call typinput function */
3731+
char *str = *isNull ? NULL : JsonbUnquote(jb);
3732+
3733+
res = InputFunctionCall(&op->d.jsonexpr.input.func, str,
3734+
op->d.jsonexpr.input.typioparam,
3735+
jexpr->returning.typmod);
3736+
}
3737+
else if (op->d.jsonexpr.result_expr)
3738+
res = ExecEvalExprPassingCaseValue(op->d.jsonexpr.result_expr, econtext,
3739+
isNull, res, *isNull);
3740+
/* else no coercion, simply return item */
3741+
3742+
return res;
3743+
}
3744+
3745+
/*
3746+
* Evaluate a JSON path variable caching computed value.
3747+
*/
3748+
Datum
3749+
EvalJsonPathVar(void *cxt, bool *isnull)
3750+
{
3751+
JsonPathVariableEvalContext *ecxt = cxt;
3752+
3753+
if (!ecxt->evaluated)
3754+
{
3755+
ecxt->value = ExecEvalExpr(ecxt->estate, ecxt->econtext, &ecxt->isnull);
3756+
ecxt->evaluated = true;
3757+
}
3758+
3759+
*isnull = ecxt->isnull;
3760+
return ecxt->value;
3761+
}
3762+
3763+
/* ----------------------------------------------------------------
3764+
* ExecEvalJson
3765+
* ----------------------------------------------------------------
3766+
*/
3767+
void
3768+
ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
3769+
{
3770+
JsonExpr *jexpr = op->d.jsonexpr.jsexpr;
3771+
Datum item;
3772+
Datum res = (Datum) 0;
3773+
JsonPath *path;
3774+
ListCell *lc;
3775+
Oid formattedType = exprType(jexpr->formatted_expr ?
3776+
jexpr->formatted_expr :
3777+
jexpr->raw_expr);
3778+
bool isjsonb = formattedType == JSONBOID;
3779+
MemoryContext mcxt = CurrentMemoryContext;
3780+
3781+
*op->resnull = true; /* until we get a result */
3782+
*op->resvalue = (Datum) 0;
3783+
3784+
if (op->d.jsonexpr.raw_expr->isnull)
3785+
{
3786+
/* execute domain checks for NULLs */
3787+
(void) ExecEvalJsonExprCoercion(op, econtext, res, op->resnull);
3788+
return;
3789+
}
3790+
3791+
item = op->d.jsonexpr.raw_expr->value;
3792+
3793+
path = DatumGetJsonPathP(jexpr->path_spec->constvalue);
3794+
3795+
/* reset JSON path variable contexts */
3796+
foreach(lc, op->d.jsonexpr.args)
3797+
{
3798+
JsonPathVariableEvalContext *var = lfirst(lc);
3799+
3800+
var->econtext = econtext;
3801+
var->evaluated = false;
3802+
}
3803+
3804+
PG_TRY();
3805+
{
3806+
bool empty = false;
3807+
3808+
if (op->d.jsonexpr.formatted_expr)
3809+
{
3810+
bool isnull;
3811+
3812+
item = ExecEvalExprPassingCaseValue(op->d.jsonexpr.formatted_expr,
3813+
econtext, &isnull, item, false);
3814+
if (isnull)
3815+
{
3816+
/* execute domain checks for NULLs */
3817+
(void) ExecEvalJsonExprCoercion(op, econtext, res, op->resnull);
3818+
return;
3819+
}
3820+
}
3821+
3822+
switch (jexpr->op)
3823+
{
3824+
case IS_JSON_QUERY:
3825+
res = JsonbPathQuery(item, path, jexpr->wrapper, &empty,
3826+
op->d.jsonexpr.args);
3827+
*op->resnull = !DatumGetPointer(res);
3828+
break;
3829+
3830+
case IS_JSON_VALUE:
3831+
res = JsonbPathValue(item, path, &empty, op->d.jsonexpr.args);
3832+
*op->resnull = !DatumGetPointer(res);
3833+
break;
3834+
3835+
case IS_JSON_EXISTS:
3836+
res = BoolGetDatum(JsonbPathExists(item, path,
3837+
op->d.jsonexpr.args));
3838+
*op->resnull = false;
3839+
break;
3840+
3841+
default:
3842+
elog(ERROR, "unrecognized SQL/JSON expression op %d",
3843+
jexpr->op);
3844+
return;
3845+
}
3846+
3847+
if (empty)
3848+
{
3849+
if (jexpr->on_empty.btype == JSON_BEHAVIOR_ERROR)
3850+
ereport(ERROR,
3851+
(errcode(ERRCODE_NO_JSON_ITEM),
3852+
errmsg("no SQL/JSON item")));
3853+
3854+
/* execute ON EMPTY behavior */
3855+
res = ExecEvalJsonBehavior(econtext, &jexpr->on_empty,
3856+
op->d.jsonexpr.default_on_empty,
3857+
isjsonb, op->resnull);
3858+
}
3859+
3860+
if (jexpr->op != IS_JSON_EXISTS &&
3861+
(!empty ||
3862+
/* result is already coerced in DEFAULT behavior case */
3863+
jexpr->on_empty.btype != JSON_BEHAVIOR_DEFAULT))
3864+
res = ExecEvalJsonExprCoercion(op, econtext, res, op->resnull);
3865+
}
3866+
PG_CATCH();
3867+
{
3868+
if (jexpr->on_error.btype == JSON_BEHAVIOR_ERROR ||
3869+
ERRCODE_TO_CATEGORY(geterrcode()) != ERRCODE_DATA_EXCEPTION)
3870+
PG_RE_THROW();
3871+
3872+
FlushErrorState();
3873+
MemoryContextSwitchTo(mcxt);
3874+
3875+
/* execute ON ERROR behavior */
3876+
res = ExecEvalJsonBehavior(econtext, &jexpr->on_error,
3877+
op->d.jsonexpr.default_on_error,
3878+
isjsonb, op->resnull);
3879+
3880+
if (jexpr->op != IS_JSON_EXISTS &&
3881+
/* result is already coerced in DEFAULT behavior case */
3882+
jexpr->on_error.btype != JSON_BEHAVIOR_DEFAULT)
3883+
res = ExecEvalJsonExprCoercion(op, econtext, res, op->resnull);
3884+
}
3885+
PG_END_TRY();
3886+
3887+
*op->resvalue = res;
3888+
}

0 commit comments

Comments
 (0)