|
70 | 70 | #include "pgstat.h"
|
71 | 71 | #include "utils/builtins.h"
|
72 | 72 | #include "utils/date.h"
|
| 73 | +#include "utils/jsonb.h" |
| 74 | +#include "utils/jsonpath.h" |
73 | 75 | #include "utils/lsyscache.h"
|
74 | 76 | #include "utils/timestamp.h"
|
75 | 77 | #include "utils/typcache.h"
|
@@ -363,6 +365,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
|
363 | 365 | &&CASE_EEOP_WINDOW_FUNC,
|
364 | 366 | &&CASE_EEOP_SUBPLAN,
|
365 | 367 | &&CASE_EEOP_ALTERNATIVE_SUBPLAN,
|
| 368 | + &&CASE_EEOP_JSONEXPR, |
366 | 369 | &&CASE_EEOP_LAST
|
367 | 370 | };
|
368 | 371 |
|
@@ -1506,6 +1509,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
|
1506 | 1509 | EEO_NEXT();
|
1507 | 1510 | }
|
1508 | 1511 |
|
| 1512 | + EEO_CASE(EEOP_JSONEXPR) |
| 1513 | + { |
| 1514 | + /* too complex for an inline implementation */ |
| 1515 | + ExecEvalJson(state, op, econtext); |
| 1516 | + EEO_NEXT(); |
| 1517 | + } |
| 1518 | + |
1509 | 1519 | EEO_CASE(EEOP_LAST)
|
1510 | 1520 | {
|
1511 | 1521 | /* unreachable */
|
@@ -3579,3 +3589,251 @@ ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
|
3579 | 3589 | *op->resvalue = PointerGetDatum(dtuple);
|
3580 | 3590 | *op->resnull = false;
|
3581 | 3591 | }
|
| 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