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

Commit ec155fc

Browse files
author
Nikita Glukhov
committed
Add JSON_VALUE, JSON_EXISTS, JSON_QUERY
1 parent b6288f6 commit ec155fc

25 files changed

+2484
-14
lines changed

contrib/pg_stat_statements/pg_stat_statements.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2840,6 +2840,17 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
28402840
APP_JUMB(opts->value_type);
28412841
}
28422842
break;
2843+
case T_JsonExpr:
2844+
{
2845+
JsonExpr *jexpr = (JsonExpr *) node;
2846+
2847+
APP_JUMB(jexpr->op);
2848+
JumbleExpr(jstate, jexpr->raw_expr);
2849+
JumbleExpr(jstate, (Node *) jexpr->passing.values);
2850+
JumbleExpr(jstate, jexpr->on_empty.default_expr);
2851+
JumbleExpr(jstate, jexpr->on_error.default_expr);
2852+
}
2853+
break;
28432854
case T_List:
28442855
foreach(temp, (List *) node)
28452856
{

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

@@ -2038,6 +2039,72 @@ ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
20382039
parent, state, resv, resnull);
20392040
break;
20402041

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