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

Commit d020dd6

Browse files
author
Nikita Glukhov
committed
Add JSON_VALUE(), JSON_QUERY(), JSON_EXISTS()
1 parent 7320b4c commit d020dd6

30 files changed

+2899
-165
lines changed

contrib/pg_stat_statements/pg_stat_statements.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2830,6 +2830,17 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
28302830
JumbleExpr(jstate, (Node *) conf->exclRelTlist);
28312831
}
28322832
break;
2833+
case T_JsonExpr:
2834+
{
2835+
JsonExpr *jexpr = (JsonExpr *) node;
2836+
2837+
APP_JUMB(jexpr->op);
2838+
JumbleExpr(jstate, jexpr->raw_expr);
2839+
JumbleExpr(jstate, (Node *) jexpr->passing.values);
2840+
JumbleExpr(jstate, jexpr->on_empty.default_expr);
2841+
JumbleExpr(jstate, jexpr->on_error.default_expr);
2842+
}
2843+
break;
28332844
case T_List:
28342845
foreach(temp, (List *) node)
28352846
{

src/backend/executor/execExpr.c

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

@@ -2026,6 +2027,80 @@ ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
20262027
break;
20272028
}
20282029

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

src/backend/executor/execExprInterp.c

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

0 commit comments

Comments
 (0)