diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index 2c62b0c9c8..629dd23cf5 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -125,7 +125,17 @@ static void ExecBuildAggTransCall(ExprState *state, AggState *aggstate, * although ExecQual and ExecCheck will accept one (and treat it as "true"). */ ExprState * -ExecInitExpr(Expr *node, PlanState *parent) +ExecInitExpr(Expr* node, PlanState* parent) +{ + return ExecInitExprWithErrorContext(node, parent, NULL); +} + +/* + * As ExecInitExpr but attempt to save errors into 'err_context' when + * supported by the functions being called. XXX write something better here. + */ +ExprState * +ExecInitExprWithErrorContext(Expr *node, PlanState *parent, fmNodePtr *err_context) { ExprState *state; ExprEvalStep scratch = {0}; @@ -139,6 +149,7 @@ ExecInitExpr(Expr *node, PlanState *parent) state->expr = node; state->parent = parent; state->ext_params = NULL; + state->err_context = err_context; /* Insert setup steps as needed */ ExecCreateExprSetupSteps(state, (Node *) node); @@ -2618,7 +2629,7 @@ ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, /* Initialize function call parameter structure too */ InitFunctionCallInfoData(*fcinfo, flinfo, - nargs, inputcollid, NULL, NULL); + nargs, inputcollid, (fmNodePtr) state->err_context, NULL); /* Keep extra copies of this info to save an indirection at runtime */ scratch->d.func.fn_addr = flinfo->fn_addr; diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index da258968b8..33e96aa6f5 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -31,6 +31,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "nodes/miscnodes.h" #include "nodes/multibitmapset.h" #include "nodes/nodeFuncs.h" #include "nodes/subscripting.h" @@ -2348,7 +2349,7 @@ estimate_expression_value(PlannerInfo *root, Node *node) ((Node *) evaluate_expr((Expr *) (node), \ exprType((Node *) (node)), \ exprTypmod((Node *) (node)), \ - exprCollation((Node *) (node)))) + exprCollation((Node *) (node)), false)) /* * Recursive guts of eval_const_expressions/estimate_expression_value @@ -3251,7 +3252,8 @@ eval_const_expressions_mutator(Node *node, return (Node *) evaluate_expr((Expr *) svf, svf->type, svf->typmod, - InvalidOid); + InvalidOid, + false); else return copyObject((Node *) svf); } @@ -4407,7 +4409,7 @@ evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, newexpr->location = -1; return evaluate_expr((Expr *) newexpr, result_type, result_typmod, - result_collid); + result_collid, true); } /* @@ -4861,11 +4863,16 @@ sql_inline_error_callback(void *arg) * * We use the executor's routine ExecEvalExpr() to avoid duplication of * code and ensure we get the same result as the executor would get. + * + * 'null_on_error' may be passed as true to have the expression evalulation + * code attempt to supress ERRORs and save them. This of course requires + * functions properly errsave/ereturn rather than elog/ereport. */ Expr * evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod, - Oid result_collation) + Oid result_collation, bool null_on_error) { + ErrorSaveContext escontext = {T_ErrorSaveContext}; EState *estate; ExprState *exprstate; MemoryContext oldcontext; @@ -4889,7 +4896,12 @@ evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod, * Prepare expr for execution. (Note: we can't use ExecPrepareExpr * because it'd result in recursively invoking eval_const_expressions.) */ - exprstate = ExecInitExpr(expr, NULL); + if (null_on_error) + exprstate = ExecInitExprWithErrorContext(expr, + NULL, + (fmNodePtr *) &escontext); + else + exprstate = ExecInitExpr(expr, NULL); /* * And evaluate it. @@ -4917,7 +4929,7 @@ evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod, * data. (makeConst would handle detoasting anyway, but it's worth a few * extra lines here so that we can do the copy and detoast in one step.) */ - if (!const_is_null) + if (!escontext.error_occurred && !const_is_null) { if (resultTypLen == -1) const_val = PointerGetDatum(PG_DETOAST_DATUM_COPY(const_val)); @@ -4928,6 +4940,9 @@ evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod, /* Release all the junk we just created */ FreeExecutorState(estate); + if (escontext.error_occurred) + return NULL; + /* * Make the constant result node. */ diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index cf0d432ab1..efe7c210a2 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -4462,7 +4462,7 @@ transformPartitionBoundValue(ParseState *pstate, Node *val, assign_expr_collations(pstate, value); value = (Node *) expression_planner((Expr *) value); value = (Node *) evaluate_expr((Expr *) value, colType, colTypmod, - partCollation); + partCollation, false); if (!IsA(value, Const)) elog(ERROR, "could not evaluate partition bound expression"); } diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index aeebe0e0ff..e926d9e62b 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -278,6 +278,8 @@ ExecProcNode(PlanState *node) * prototypes from functions in execExpr.c */ extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); +extern ExprState *ExecInitExprWithErrorContext(Expr *node, PlanState *parent, + fmNodePtr *err_context); extern ExprState *ExecInitExprWithParams(Expr *node, ParamListInfo ext_params); extern ExprState *ExecInitQual(List *qual, PlanState *parent); extern ExprState *ExecInitCheck(List *qual, PlanState *parent); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index cb714f4a19..c008515f17 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -124,6 +124,12 @@ typedef struct ExprState struct PlanState *parent; /* parent PlanState node, if any */ ParamListInfo ext_params; /* for compiling PARAM_EXTERN nodes */ + /* + * ErrorSaveContext pointer to record any saved ERRORs into or NULL if + * errors are to be raised right away. + */ + fmNodePtr *err_context; + Datum *innermost_caseval; bool *innermost_casenull; diff --git a/src/include/optimizer/optimizer.h b/src/include/optimizer/optimizer.h index 514746c585..da994760ed 100644 --- a/src/include/optimizer/optimizer.h +++ b/src/include/optimizer/optimizer.h @@ -148,7 +148,7 @@ extern void convert_saop_to_hashed_saop(Node *node); extern Node *estimate_expression_value(PlannerInfo *root, Node *node); extern Expr *evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod, - Oid result_collation); + Oid result_collation, bool null_on_error); extern List *expand_function_arguments(List *args, bool include_out_arguments, Oid result_type,