aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/execExpr.c301
-rw-r--r--src/backend/executor/execExprInterp.c338
2 files changed, 638 insertions, 1 deletions
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 728c8d5fda9..bc5feb0115a 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -48,6 +48,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/jsonfuncs.h"
+#include "utils/jsonpath.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
@@ -87,6 +88,12 @@ static void ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
FunctionCallInfo fcinfo, AggStatePerTrans pertrans,
int transno, int setno, int setoff, bool ishash,
bool nullcheck);
+static void ExecInitJsonExpr(JsonExpr *jsexpr, ExprState *state,
+ Datum *resv, bool *resnull,
+ ExprEvalStep *scratch);
+static void ExecInitJsonCoercion(ExprState *state, JsonReturning *returning,
+ ErrorSaveContext *escontext,
+ Datum *resv, bool *resnull);
/*
@@ -2425,6 +2432,14 @@ ExecInitExprRec(Expr *node, ExprState *state,
break;
}
+ case T_JsonExpr:
+ {
+ JsonExpr *jsexpr = castNode(JsonExpr, node);
+
+ ExecInitJsonExpr(jsexpr, state, resv, resnull, &scratch);
+ break;
+ }
+
case T_NullTest:
{
NullTest *ntest = (NullTest *) node;
@@ -4193,3 +4208,289 @@ ExecBuildParamSetEqual(TupleDesc desc,
return state;
}
+
+/*
+ * Push steps to evaluate a JsonExpr and its various subsidiary expressions.
+ */
+static void
+ExecInitJsonExpr(JsonExpr *jsexpr, ExprState *state,
+ Datum *resv, bool *resnull,
+ ExprEvalStep *scratch)
+{
+ JsonExprState *jsestate = palloc0(sizeof(JsonExprState));
+ ListCell *argexprlc;
+ ListCell *argnamelc;
+ List *jumps_return_null = NIL;
+ List *jumps_to_end = NIL;
+ ListCell *lc;
+ ErrorSaveContext *escontext =
+ jsexpr->on_error->btype != JSON_BEHAVIOR_ERROR ?
+ &jsestate->escontext : NULL;
+
+ jsestate->jsexpr = jsexpr;
+
+ /*
+ * Evaluate formatted_expr storing the result into
+ * jsestate->formatted_expr.
+ */
+ ExecInitExprRec((Expr *) jsexpr->formatted_expr, state,
+ &jsestate->formatted_expr.value,
+ &jsestate->formatted_expr.isnull);
+
+ /* JUMP to return NULL if formatted_expr evaluates to NULL */
+ jumps_return_null = lappend_int(jumps_return_null, state->steps_len);
+ scratch->opcode = EEOP_JUMP_IF_NULL;
+ scratch->resnull = &jsestate->formatted_expr.isnull;
+ scratch->d.jump.jumpdone = -1; /* set below */
+ ExprEvalPushStep(state, scratch);
+
+ /*
+ * Evaluate pathspec expression storing the result into
+ * jsestate->pathspec.
+ */
+ ExecInitExprRec((Expr *) jsexpr->path_spec, state,
+ &jsestate->pathspec.value,
+ &jsestate->pathspec.isnull);
+
+ /* JUMP to return NULL if path_spec evaluates to NULL */
+ jumps_return_null = lappend_int(jumps_return_null, state->steps_len);
+ scratch->opcode = EEOP_JUMP_IF_NULL;
+ scratch->resnull = &jsestate->pathspec.isnull;
+ scratch->d.jump.jumpdone = -1; /* set below */
+ ExprEvalPushStep(state, scratch);
+
+ /* Steps to compute PASSING args. */
+ jsestate->args = NIL;
+ forboth(argexprlc, jsexpr->passing_values,
+ argnamelc, jsexpr->passing_names)
+ {
+ Expr *argexpr = (Expr *) lfirst(argexprlc);
+ String *argname = lfirst_node(String, argnamelc);
+ JsonPathVariable *var = palloc(sizeof(*var));
+
+ var->name = argname->sval;
+ var->typid = exprType((Node *) argexpr);
+ var->typmod = exprTypmod((Node *) argexpr);
+
+ ExecInitExprRec((Expr *) argexpr, state, &var->value, &var->isnull);
+
+ jsestate->args = lappend(jsestate->args, var);
+ }
+
+ /* Step for jsonpath evaluation; see ExecEvalJsonExprPath(). */
+ scratch->opcode = EEOP_JSONEXPR_PATH;
+ scratch->resvalue = resv;
+ scratch->resnull = resnull;
+ scratch->d.jsonexpr.jsestate = jsestate;
+ ExprEvalPushStep(state, scratch);
+
+ /*
+ * Step to return NULL after jumping to skip the EEOP_JSONEXPR_PATH step
+ * when either formatted_expr or pathspec is NULL. Adjust jump target
+ * addresses of JUMPs that we added above.
+ */
+ foreach(lc, jumps_return_null)
+ {
+ ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+ as->d.jump.jumpdone = state->steps_len;
+ }
+ scratch->opcode = EEOP_CONST;
+ scratch->resvalue = resv;
+ scratch->resnull = resnull;
+ scratch->d.constval.value = (Datum) 0;
+ scratch->d.constval.isnull = true;
+ ExprEvalPushStep(state, scratch);
+
+ /*
+ * Jump to coerce the NULL using coercion_expr if present. Coercing NULL
+ * is only interesting when the RETURNING type is a domain whose
+ * constraints must be checked. jsexpr->coercion_expr containing a
+ * CoerceToDomain node must have been set in that case.
+ */
+ if (jsexpr->coercion_expr)
+ {
+ scratch->opcode = EEOP_JUMP;
+ scratch->d.jump.jumpdone = state->steps_len + 1;
+ ExprEvalPushStep(state, scratch);
+ }
+
+ /*
+ * To handle coercion errors softly, use the following ErrorSaveContext to
+ * pass to ExecInitExprRec() when initializing the coercion expressions
+ * and in the EEOP_JSONEXPR_COERCION step.
+ */
+ jsestate->escontext.type = T_ErrorSaveContext;
+
+ /*
+ * Steps to coerce the result value computed by EEOP_JSONEXPR_PATH or the
+ * NULL returned on NULL input as described above.
+ */
+ jsestate->jump_eval_coercion = -1;
+ if (jsexpr->coercion_expr)
+ {
+ Datum *save_innermost_caseval;
+ bool *save_innermost_casenull;
+ ErrorSaveContext *save_escontext;
+
+ jsestate->jump_eval_coercion = state->steps_len;
+
+ save_innermost_caseval = state->innermost_caseval;
+ save_innermost_casenull = state->innermost_casenull;
+ save_escontext = state->escontext;
+
+ state->innermost_caseval = resv;
+ state->innermost_casenull = resnull;
+ state->escontext = escontext;
+
+ ExecInitExprRec((Expr *) jsexpr->coercion_expr, state, resv, resnull);
+
+ state->innermost_caseval = save_innermost_caseval;
+ state->innermost_casenull = save_innermost_casenull;
+ state->escontext = save_escontext;
+ }
+ else if (jsexpr->use_json_coercion)
+ {
+ jsestate->jump_eval_coercion = state->steps_len;
+
+ ExecInitJsonCoercion(state, jsexpr->returning, escontext, resv, resnull);
+ }
+ else if (jsexpr->use_io_coercion)
+ {
+ /*
+ * Here we only need to initialize the FunctionCallInfo for the target
+ * type's input function, which is called by ExecEvalJsonExprPath()
+ * itself, so no additional step is necessary.
+ */
+ Oid typinput;
+ Oid typioparam;
+ FmgrInfo *finfo;
+ FunctionCallInfo fcinfo;
+
+ getTypeInputInfo(jsexpr->returning->typid, &typinput, &typioparam);
+ finfo = palloc0(sizeof(FmgrInfo));
+ fcinfo = palloc0(SizeForFunctionCallInfo(3));
+ fmgr_info(typinput, finfo);
+ fmgr_info_set_expr((Node *) jsexpr->returning, finfo);
+ InitFunctionCallInfoData(*fcinfo, finfo, 3, InvalidOid, NULL, NULL);
+
+ /*
+ * We can preload the second and third arguments for the input
+ * function, since they're constants.
+ */
+ fcinfo->args[1].value = ObjectIdGetDatum(typioparam);
+ fcinfo->args[1].isnull = false;
+ fcinfo->args[2].value = Int32GetDatum(jsexpr->returning->typmod);
+ fcinfo->args[2].isnull = false;
+ fcinfo->context = (Node *) escontext;
+
+ jsestate->input_finfo = finfo;
+ jsestate->input_fcinfo = fcinfo;
+ }
+
+ /*
+ * Add a special step, if needed, to check if the coercion evaluation ran
+ * into an error but was not thrown because the ON ERROR behavior is not
+ * ERROR. It will set jsesestate->error if an error did occur.
+ */
+ if (jsestate->jump_eval_coercion >= 0 && escontext != NULL)
+ {
+ scratch->opcode = EEOP_JSONEXPR_COERCION_FINISH;
+ scratch->d.jsonexpr.jsestate = jsestate;
+ ExprEvalPushStep(state, scratch);
+ }
+
+ jsestate->jump_empty = jsestate->jump_error = -1;
+
+ /*
+ * Step to check jsestate->error and return the ON ERROR expression if
+ * there is one. This handles both the errors that occur during jsonpath
+ * evaluation in EEOP_JSONEXPR_PATH and subsequent coercion evaluation.
+ */
+ if (jsexpr->on_error &&
+ jsexpr->on_error->btype != JSON_BEHAVIOR_ERROR)
+ {
+ jsestate->jump_error = state->steps_len;
+
+ /* JUMP to end if false, that is, skip the ON ERROR expression. */
+ jumps_to_end = lappend_int(jumps_to_end, state->steps_len);
+ scratch->opcode = EEOP_JUMP_IF_NOT_TRUE;
+ scratch->resvalue = &jsestate->error.value;
+ scratch->resnull = &jsestate->error.isnull;
+ scratch->d.jump.jumpdone = -1; /* set below */
+ ExprEvalPushStep(state, scratch);
+
+ /* Steps to evaluate the ON ERROR expression */
+ ExecInitExprRec((Expr *) jsexpr->on_error->expr,
+ state, resv, resnull);
+
+ /* Step to coerce the ON ERROR expression if needed */
+ if (jsexpr->on_error->coerce)
+ ExecInitJsonCoercion(state, jsexpr->returning, escontext, resv,
+ resnull);
+
+ /* JUMP to end to skip the ON EMPTY steps added below. */
+ jumps_to_end = lappend_int(jumps_to_end, state->steps_len);
+ scratch->opcode = EEOP_JUMP;
+ scratch->d.jump.jumpdone = -1;
+ ExprEvalPushStep(state, scratch);
+ }
+
+ /*
+ * Step to check jsestate->empty and return the ON EMPTY expression if
+ * there is one.
+ */
+ if (jsexpr->on_empty != NULL &&
+ jsexpr->on_empty->btype != JSON_BEHAVIOR_ERROR)
+ {
+ jsestate->jump_empty = state->steps_len;
+
+ /* JUMP to end if false, that is, skip the ON EMPTY expression. */
+ jumps_to_end = lappend_int(jumps_to_end, state->steps_len);
+ scratch->opcode = EEOP_JUMP_IF_NOT_TRUE;
+ scratch->resvalue = &jsestate->empty.value;
+ scratch->resnull = &jsestate->empty.isnull;
+ scratch->d.jump.jumpdone = -1; /* set below */
+ ExprEvalPushStep(state, scratch);
+
+ /* Steps to evaluate the ON EMPTY expression */
+ ExecInitExprRec((Expr *) jsexpr->on_empty->expr,
+ state, resv, resnull);
+
+ /* Step to coerce the ON EMPTY expression if needed */
+ if (jsexpr->on_empty->coerce)
+ ExecInitJsonCoercion(state, jsexpr->returning, escontext, resv,
+ resnull);
+ }
+
+ foreach(lc, jumps_to_end)
+ {
+ ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+ as->d.jump.jumpdone = state->steps_len;
+ }
+
+ jsestate->jump_end = state->steps_len;
+}
+
+/*
+ * Initialize a EEOP_JSONEXPR_COERCION step to coerce the value given in resv
+ * to the given RETURNING type.
+ */
+static void
+ExecInitJsonCoercion(ExprState *state, JsonReturning *returning,
+ ErrorSaveContext *escontext,
+ Datum *resv, bool *resnull)
+{
+ ExprEvalStep scratch = {0};
+
+ /* For json_populate_type() */
+ scratch.opcode = EEOP_JSONEXPR_COERCION;
+ scratch.resvalue = resv;
+ scratch.resnull = resnull;
+ scratch.d.jsonexpr_coercion.targettype = returning->typid;
+ scratch.d.jsonexpr_coercion.targettypmod = returning->typmod;
+ scratch.d.jsonexpr_coercion.json_populate_type_cache = NULL;
+ scratch.d.jsonexpr_coercion.escontext = escontext;
+ ExprEvalPushStep(state, &scratch);
+}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index a25ab7570fe..24a3990a30a 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -72,8 +72,8 @@
#include "utils/datum.h"
#include "utils/expandedrecord.h"
#include "utils/json.h"
-#include "utils/jsonb.h"
#include "utils/jsonfuncs.h"
+#include "utils/jsonpath.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/timestamp.h"
@@ -180,6 +180,7 @@ static pg_attribute_always_inline void ExecAggPlainTransByRef(AggState *aggstate
AggStatePerGroup pergroup,
ExprContext *aggcontext,
int setno);
+static char *ExecGetJsonValueItemString(JsonbValue *item, bool *resnull);
/*
* ScalarArrayOpExprHashEntry
@@ -481,6 +482,9 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_XMLEXPR,
&&CASE_EEOP_JSON_CONSTRUCTOR,
&&CASE_EEOP_IS_JSON,
+ &&CASE_EEOP_JSONEXPR_PATH,
+ &&CASE_EEOP_JSONEXPR_COERCION,
+ &&CASE_EEOP_JSONEXPR_COERCION_FINISH,
&&CASE_EEOP_AGGREF,
&&CASE_EEOP_GROUPING_FUNC,
&&CASE_EEOP_WINDOW_FUNC,
@@ -1554,6 +1558,28 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_JSONEXPR_PATH)
+ {
+ /* too complex for an inline implementation */
+ EEO_JUMP(ExecEvalJsonExprPath(state, op, econtext));
+ }
+
+ EEO_CASE(EEOP_JSONEXPR_COERCION)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalJsonCoercion(state, op, econtext);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_JSONEXPR_COERCION_FINISH)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalJsonCoercionFinish(state, op);
+
+ EEO_NEXT();
+ }
+
EEO_CASE(EEOP_AGGREF)
{
/*
@@ -4222,6 +4248,316 @@ ExecEvalJsonIsPredicate(ExprState *state, ExprEvalStep *op)
*op->resvalue = BoolGetDatum(res);
}
+/*
+ * Evaluate a jsonpath against a document, both of which must have been
+ * evaluated and their values saved in op->d.jsonexpr.jsestate.
+ *
+ * If an error occurs during JsonPath* evaluation or when coercing its result
+ * to the RETURNING type, JsonExprState.error is set to true, provided the
+ * ON ERROR behavior is not ERROR. Similarly, if JsonPath{Query|Value}() found
+ * no matching items, JsonExprState.empty is set to true, provided the ON EMPTY
+ * behavior is not ERROR. That is to signal to the subsequent steps that check
+ * those flags to return the ON ERROR / ON EMPTY expression.
+ *
+ * Return value is the step address to be performed next. It will be one of
+ * jump_error, jump_empty, jump_eval_coercion, or jump_end, all given in
+ * op->d.jsonexpr.jsestate.
+ */
+int
+ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op,
+ ExprContext *econtext)
+{
+ JsonExprState *jsestate = op->d.jsonexpr.jsestate;
+ JsonExpr *jsexpr = jsestate->jsexpr;
+ Datum item;
+ JsonPath *path;
+ bool throw_error = jsexpr->on_error->btype == JSON_BEHAVIOR_ERROR;
+ bool error = false,
+ empty = false;
+ int jump_eval_coercion = jsestate->jump_eval_coercion;
+ char *val_string = NULL;
+
+ item = jsestate->formatted_expr.value;
+ path = DatumGetJsonPathP(jsestate->pathspec.value);
+
+ /* Set error/empty to false. */
+ memset(&jsestate->error, 0, sizeof(NullableDatum));
+ memset(&jsestate->empty, 0, sizeof(NullableDatum));
+
+ /*
+ * Also reset ErrorSaveContext contents for the next row. Since we don't
+ * set details_wanted, we don't need to also reset error_data, which would
+ * be NULL anyway.
+ */
+ Assert(!jsestate->escontext.details_wanted &&
+ jsestate->escontext.error_data == NULL);
+ jsestate->escontext.error_occurred = false;
+
+ switch (jsexpr->op)
+ {
+ case JSON_EXISTS_OP:
+ {
+ bool exists = JsonPathExists(item, path,
+ !throw_error ? &error : NULL,
+ jsestate->args);
+
+ if (!error)
+ {
+ *op->resvalue = BoolGetDatum(exists);
+ *op->resnull = false;
+ }
+ }
+ break;
+
+ case JSON_QUERY_OP:
+ *op->resvalue = JsonPathQuery(item, path, jsexpr->wrapper, &empty,
+ !throw_error ? &error : NULL,
+ jsestate->args);
+
+ *op->resnull = (DatumGetPointer(*op->resvalue) == NULL);
+
+ /* Handle OMIT QUOTES. */
+ if (!*op->resnull && jsexpr->omit_quotes)
+ {
+ val_string = JsonbUnquote(DatumGetJsonbP(*op->resvalue));
+
+ /*
+ * Pass the string as a text value to the cast expression if
+ * one present. If not, use the input function call below to
+ * do the coercion.
+ */
+ if (jump_eval_coercion >= 0)
+ *op->resvalue =
+ DirectFunctionCall1(textin,
+ PointerGetDatum(val_string));
+ }
+ break;
+
+ case JSON_VALUE_OP:
+ {
+ JsonbValue *jbv = JsonPathValue(item, path, &empty,
+ !throw_error ? &error : NULL,
+ jsestate->args);
+
+ if (jbv == NULL)
+ {
+ /* Will be coerced with coercion_expr, if any. */
+ *op->resvalue = (Datum) 0;
+ *op->resnull = true;
+ }
+ else if (!error && !empty)
+ {
+ if (jsexpr->returning->typid == JSONOID ||
+ jsexpr->returning->typid == JSONBOID)
+ {
+ val_string = DatumGetCString(DirectFunctionCall1(jsonb_out,
+ JsonbPGetDatum(JsonbValueToJsonb(jbv))));
+ }
+ else
+ {
+ val_string = ExecGetJsonValueItemString(jbv, op->resnull);
+
+ /*
+ * Pass the string as a text value to the cast
+ * expression if one present. If not, use the input
+ * function call below to do the coercion.
+ */
+ *op->resvalue = PointerGetDatum(val_string);
+ if (jump_eval_coercion >= 0)
+ *op->resvalue = DirectFunctionCall1(textin, *op->resvalue);
+ }
+ }
+ break;
+ }
+
+ default:
+ elog(ERROR, "unrecognized SQL/JSON expression op %d",
+ (int) jsexpr->op);
+ return false;
+ }
+
+ /*
+ * Coerce the result value to the RETURNING type by calling its input
+ * function.
+ */
+ if (!*op->resnull && jsexpr->use_io_coercion)
+ {
+ FunctionCallInfo fcinfo;
+
+ Assert(jump_eval_coercion == -1);
+ fcinfo = jsestate->input_fcinfo;
+ Assert(fcinfo != NULL);
+ Assert(val_string != NULL);
+ fcinfo->args[0].value = PointerGetDatum(val_string);
+ fcinfo->args[0].isnull = *op->resnull;
+
+ /*
+ * Second and third arguments are already set up in
+ * ExecInitJsonExpr().
+ */
+
+ fcinfo->isnull = false;
+ *op->resvalue = FunctionCallInvoke(fcinfo);
+ if (SOFT_ERROR_OCCURRED(&jsestate->escontext))
+ error = true;
+ }
+
+ /* Handle ON EMPTY. */
+ if (empty)
+ {
+ if (jsexpr->on_empty)
+ {
+ if (jsexpr->on_empty->btype == JSON_BEHAVIOR_ERROR)
+ ereport(ERROR,
+ errcode(ERRCODE_NO_SQL_JSON_ITEM),
+ errmsg("no SQL/JSON item"));
+ else
+ jsestate->empty.value = BoolGetDatum(true);
+
+ Assert(jsestate->jump_empty >= 0);
+ return jsestate->jump_empty;
+ }
+ else if (jsexpr->on_error->btype == JSON_BEHAVIOR_ERROR)
+ ereport(ERROR,
+ errcode(ERRCODE_NO_SQL_JSON_ITEM),
+ errmsg("no SQL/JSON item"));
+ else
+ jsestate->error.value = BoolGetDatum(true);
+
+ *op->resvalue = (Datum) 0;
+ *op->resnull = true;
+
+ Assert(!throw_error && jsestate->jump_error >= 0);
+ return jsestate->jump_error;
+ }
+
+ /*
+ * ON ERROR. Wouldn't get here if the behavior is ERROR, because they
+ * would have already been thrown.
+ */
+ if (error)
+ {
+ Assert(!throw_error && jsestate->jump_error >= 0);
+ *op->resvalue = (Datum) 0;
+ *op->resnull = true;
+ jsestate->error.value = BoolGetDatum(true);
+ return jsestate->jump_error;
+ }
+
+ return jump_eval_coercion >= 0 ? jump_eval_coercion : jsestate->jump_end;
+}
+
+/*
+ * Convert the given JsonbValue to its C string representation
+ *
+ * *resnull is set if the JsonbValue is a jbvNull.
+ */
+static char *
+ExecGetJsonValueItemString(JsonbValue *item, bool *resnull)
+{
+ *resnull = false;
+
+ /* get coercion state reference and datum of the corresponding SQL type */
+ switch (item->type)
+ {
+ case jbvNull:
+ *resnull = true;
+ return NULL;
+
+ case jbvString:
+ {
+ char *str = palloc(item->val.string.len + 1);
+
+ memcpy(str, item->val.string.val, item->val.string.len);
+ str[item->val.string.len] = '\0';
+ return str;
+ }
+
+ case jbvNumeric:
+ return DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(item->val.numeric)));
+
+ case jbvBool:
+ return DatumGetCString(DirectFunctionCall1(boolout,
+ BoolGetDatum(item->val.boolean)));
+
+ case jbvDatetime:
+ switch (item->val.datetime.typid)
+ {
+ case DATEOID:
+ return DatumGetCString(DirectFunctionCall1(date_out,
+ item->val.datetime.value));
+ case TIMEOID:
+ return DatumGetCString(DirectFunctionCall1(time_out,
+ item->val.datetime.value));
+ case TIMETZOID:
+ return DatumGetCString(DirectFunctionCall1(timetz_out,
+ item->val.datetime.value));
+ case TIMESTAMPOID:
+ return DatumGetCString(DirectFunctionCall1(timestamp_out,
+ item->val.datetime.value));
+ case TIMESTAMPTZOID:
+ return DatumGetCString(DirectFunctionCall1(timestamptz_out,
+ item->val.datetime.value));
+ default:
+ elog(ERROR, "unexpected jsonb datetime type oid %u",
+ item->val.datetime.typid);
+ }
+ break;
+
+ case jbvArray:
+ case jbvObject:
+ case jbvBinary:
+ return DatumGetCString(DirectFunctionCall1(jsonb_out,
+ JsonbPGetDatum(JsonbValueToJsonb(item))));
+
+ default:
+ elog(ERROR, "unexpected jsonb value type %d", item->type);
+ }
+
+ Assert(false);
+ *resnull = true;
+ return NULL;
+}
+
+/*
+ * Coerce a jsonb value produced by ExecEvalJsonExprPath() or an ON ERROR /
+ * ON EMPTY behavior expression to the target type.
+ *
+ * Any soft errors that occur here will be checked by
+ * EEOP_JSONEXPR_COERCION_FINISH that will run after this.
+ */
+void
+ExecEvalJsonCoercion(ExprState *state, ExprEvalStep *op,
+ ExprContext *econtext)
+{
+ ErrorSaveContext *escontext = op->d.jsonexpr_coercion.escontext;
+
+ *op->resvalue = json_populate_type(*op->resvalue, JSONBOID,
+ op->d.jsonexpr_coercion.targettype,
+ op->d.jsonexpr_coercion.targettypmod,
+ &op->d.jsonexpr_coercion.json_populate_type_cache,
+ econtext->ecxt_per_query_memory,
+ op->resnull, (Node *) escontext);
+}
+
+/*
+ * Checks if an error occurred either when evaluating JsonExpr.coercion_expr or
+ * in ExecEvalJsonCoercion(). If so, this sets JsonExprState.error to trigger
+ * the ON ERROR handling steps.
+ */
+void
+ExecEvalJsonCoercionFinish(ExprState *state, ExprEvalStep *op)
+{
+ JsonExprState *jsestate = op->d.jsonexpr.jsestate;
+
+ if (SOFT_ERROR_OCCURRED(&jsestate->escontext))
+ {
+ *op->resvalue = (Datum) 0;
+ *op->resnull = true;
+ jsestate->error.value = BoolGetDatum(true);
+ }
+}
/*
* ExecEvalGroupingFunc