|
63 | 63 | #include "executor/nodeSubplan.h" |
64 | 64 | #include "funcapi.h" |
65 | 65 | #include "miscadmin.h" |
| 66 | +#include "nodes/makefuncs.h" |
66 | 67 | #include "nodes/nodeFuncs.h" |
67 | 68 | #include "parser/parsetree.h" |
| 69 | +#include "parser/parse_expr.h" |
68 | 70 | #include "pgstat.h" |
69 | 71 | #include "utils/builtins.h" |
70 | 72 | #include "utils/date.h" |
@@ -3760,6 +3762,132 @@ EvalJsonPathVar(void *cxt, bool *isnull) |
3760 | 3762 | return ecxt->value; |
3761 | 3763 | } |
3762 | 3764 |
|
| 3765 | +/* |
| 3766 | + * Prepare SQL/JSON item coercion to the output type. Returned a datum of the |
| 3767 | + * corresponding SQL type and a pointer to the coercion state. |
| 3768 | + */ |
| 3769 | +Datum |
| 3770 | +ExecPrepareJsonItemCoercion(JsonbValue *item, JsonReturning *returning, |
| 3771 | + struct JsonScalarCoercions *coercions, |
| 3772 | + MemoryContext mcxt, |
| 3773 | + struct JsonScalarCoercionExprState **pcestate) |
| 3774 | +{ |
| 3775 | + struct JsonScalarCoercionExprState *cestate; |
| 3776 | + Datum res; |
| 3777 | + Oid typid; |
| 3778 | + JsonbValue jbvbuf; |
| 3779 | + |
| 3780 | + if (item->type == jbvBinary && JsonContainerIsScalar(item->val.binary.data)) |
| 3781 | + item = JsonbExtractScalar(item->val.binary.data, &jbvbuf); |
| 3782 | + |
| 3783 | + /* get coercion state reference and datum of the corresponding SQL type */ |
| 3784 | + switch (item->type) |
| 3785 | + { |
| 3786 | + case jbvNull: |
| 3787 | + cestate = &coercions->null; |
| 3788 | + typid = UNKNOWNOID; |
| 3789 | + res = (Datum) 0; |
| 3790 | + break; |
| 3791 | + |
| 3792 | + case jbvString: |
| 3793 | + cestate = &coercions->string; |
| 3794 | + typid = TEXTOID; |
| 3795 | + res = PointerGetDatum( |
| 3796 | + cstring_to_text_with_len(item->val.string.val, |
| 3797 | + item->val.string.len)); |
| 3798 | + break; |
| 3799 | + |
| 3800 | + case jbvNumeric: |
| 3801 | + cestate = &coercions->numeric; |
| 3802 | + typid = NUMERICOID; |
| 3803 | + res = NumericGetDatum(item->val.numeric); |
| 3804 | + break; |
| 3805 | + |
| 3806 | + case jbvBool: |
| 3807 | + cestate = &coercions->boolean; |
| 3808 | + typid = BOOLOID; |
| 3809 | + res = BoolGetDatum(item->val.boolean); |
| 3810 | + break; |
| 3811 | + |
| 3812 | + case jbvDatetime: |
| 3813 | + res = item->val.datetime.value; |
| 3814 | + typid = item->val.datetime.typid; |
| 3815 | + switch (item->val.datetime.typid) |
| 3816 | + { |
| 3817 | + case DATEOID: |
| 3818 | + cestate = &coercions->date; |
| 3819 | + break; |
| 3820 | + case TIMEOID: |
| 3821 | + cestate = &coercions->time; |
| 3822 | + break; |
| 3823 | + case TIMETZOID: |
| 3824 | + cestate = &coercions->timetz; |
| 3825 | + break; |
| 3826 | + case TIMESTAMPOID: |
| 3827 | + cestate = &coercions->timestamp; |
| 3828 | + break; |
| 3829 | + case TIMESTAMPTZOID: |
| 3830 | + cestate = &coercions->timestamptz; |
| 3831 | + break; |
| 3832 | + default: |
| 3833 | + elog(ERROR, "unexpected jsonb datetime type oid %d", |
| 3834 | + item->val.datetime.typid); |
| 3835 | + return (Datum) 0; |
| 3836 | + } |
| 3837 | + break; |
| 3838 | + |
| 3839 | + case jbvArray: |
| 3840 | + case jbvObject: |
| 3841 | + case jbvBinary: |
| 3842 | + cestate = &coercions->composite; |
| 3843 | + res = JsonbPGetDatum(JsonbValueToJsonb(item)); |
| 3844 | + typid = JSONBOID; |
| 3845 | + break; |
| 3846 | + |
| 3847 | + default: |
| 3848 | + elog(ERROR, "unexpected jsonb value type %d", item->type); |
| 3849 | + return (Datum) 0; |
| 3850 | + } |
| 3851 | + |
| 3852 | + /* on-demand initialization of coercion state */ |
| 3853 | + if (!cestate->initialized) |
| 3854 | + { |
| 3855 | + MemoryContext oldCxt = MemoryContextSwitchTo(mcxt); |
| 3856 | + Node *expr; |
| 3857 | + |
| 3858 | + if (item->type == jbvNull) |
| 3859 | + { |
| 3860 | + expr = (Node *) makeNullConst(UNKNOWNOID, -1, InvalidOid); |
| 3861 | + } |
| 3862 | + else |
| 3863 | + { |
| 3864 | + CaseTestExpr *placeholder = makeNode(CaseTestExpr); |
| 3865 | + |
| 3866 | + placeholder->typeId = typid; |
| 3867 | + placeholder->typeMod = -1; |
| 3868 | + placeholder->collation = InvalidOid; |
| 3869 | + |
| 3870 | + expr = (Node *) placeholder; |
| 3871 | + } |
| 3872 | + |
| 3873 | + cestate->result_expr = |
| 3874 | + coerceJsonExpr(NULL, expr, returning, |
| 3875 | + &cestate->coerce_via_io, |
| 3876 | + &cestate->coerce_via_populate); |
| 3877 | + |
| 3878 | + cestate->result_expr_state = |
| 3879 | + ExecInitExpr((Expr *) cestate->result_expr, NULL); |
| 3880 | + |
| 3881 | + MemoryContextSwitchTo(oldCxt); |
| 3882 | + |
| 3883 | + cestate->initialized = true; |
| 3884 | + } |
| 3885 | + |
| 3886 | + *pcestate = cestate; |
| 3887 | + |
| 3888 | + return res; |
| 3889 | +} |
| 3890 | + |
3763 | 3891 | /* ---------------------------------------------------------------- |
3764 | 3892 | * ExecEvalJson |
3765 | 3893 | * ---------------------------------------------------------------- |
@@ -3828,8 +3956,42 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext) |
3828 | 3956 | break; |
3829 | 3957 |
|
3830 | 3958 | case IS_JSON_VALUE: |
3831 | | - res = JsonbPathValue(item, path, &empty, op->d.jsonexpr.args); |
3832 | | - *op->resnull = !DatumGetPointer(res); |
| 3959 | + { |
| 3960 | + JsonbValue *jbv = JsonbPathValue(item, path, &empty, |
| 3961 | + op->d.jsonexpr.args); |
| 3962 | + struct JsonScalarCoercionExprState *cestate; |
| 3963 | + |
| 3964 | + if (!jbv) |
| 3965 | + break; |
| 3966 | + |
| 3967 | + *op->resnull = false; |
| 3968 | + |
| 3969 | + res = ExecPrepareJsonItemCoercion(jbv, |
| 3970 | + &op->d.jsonexpr.jsexpr->returning, |
| 3971 | + &op->d.jsonexpr.scalar, |
| 3972 | + econtext->ecxt_per_query_memory, |
| 3973 | + &cestate); |
| 3974 | + |
| 3975 | + /* coerce item datum to the output type */ |
| 3976 | + if (cestate->coerce_via_io || |
| 3977 | + cestate->coerce_via_populate || /* ignored for scalars jsons */ |
| 3978 | + jexpr->returning.typid == JSONOID || |
| 3979 | + jexpr->returning.typid == JSONBOID) |
| 3980 | + { |
| 3981 | + /* use coercion from json[b] to the output type */ |
| 3982 | + res = JsonbPGetDatum(JsonbValueToJsonb(jbv)); |
| 3983 | + res = ExecEvalJsonExprCoercion(op, econtext, |
| 3984 | + res, op->resnull); |
| 3985 | + } |
| 3986 | + else if (cestate->result_expr_state) |
| 3987 | + { |
| 3988 | + res = ExecEvalExprPassingCaseValue(cestate->result_expr_state, |
| 3989 | + econtext, |
| 3990 | + op->resnull, |
| 3991 | + res, false); |
| 3992 | + } |
| 3993 | + /* else no coercion */ |
| 3994 | + } |
3833 | 3995 | break; |
3834 | 3996 |
|
3835 | 3997 | case IS_JSON_EXISTS: |
@@ -3858,7 +4020,7 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext) |
3858 | 4020 | } |
3859 | 4021 |
|
3860 | 4022 | if (jexpr->op != IS_JSON_EXISTS && |
3861 | | - (!empty || |
| 4023 | + (!empty ? jexpr->op != IS_JSON_VALUE : |
3862 | 4024 | /* result is already coerced in DEFAULT behavior case */ |
3863 | 4025 | jexpr->on_empty.btype != JSON_BEHAVIOR_DEFAULT)) |
3864 | 4026 | res = ExecEvalJsonExprCoercion(op, econtext, res, op->resnull); |
|
0 commit comments