|
68 | 68 | #include "pgstat.h"
|
69 | 69 | #include "utils/builtins.h"
|
70 | 70 | #include "utils/date.h"
|
| 71 | +#include "utils/jsonb.h" |
| 72 | +#include "utils/jsonpath.h" |
71 | 73 | #include "utils/lsyscache.h"
|
72 | 74 | #include "utils/timestamp.h"
|
73 | 75 | #include "utils/typcache.h"
|
@@ -367,6 +369,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
|
367 | 369 | &&CASE_EEOP_WINDOW_FUNC,
|
368 | 370 | &&CASE_EEOP_SUBPLAN,
|
369 | 371 | &&CASE_EEOP_ALTERNATIVE_SUBPLAN,
|
| 372 | + &&CASE_EEOP_JSONEXPR, |
370 | 373 | &&CASE_EEOP_LAST
|
371 | 374 | };
|
372 | 375 |
|
@@ -1535,6 +1538,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
|
1535 | 1538 | EEO_NEXT();
|
1536 | 1539 | }
|
1537 | 1540 |
|
| 1541 | + EEO_CASE(EEOP_JSONEXPR) |
| 1542 | + { |
| 1543 | + /* too complex for an inline implementation */ |
| 1544 | + ExecEvalJson(state, op, econtext); |
| 1545 | + EEO_NEXT(); |
| 1546 | + } |
| 1547 | + |
1538 | 1548 | EEO_CASE(EEOP_LAST)
|
1539 | 1549 | {
|
1540 | 1550 | /* unreachable */
|
@@ -3628,3 +3638,251 @@ ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
|
3628 | 3638 | *op->resvalue = PointerGetDatum(dtuple);
|
3629 | 3639 | *op->resnull = false;
|
3630 | 3640 | }
|
| 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