diff options
author | Amit Langote <amitlan@postgresql.org> | 2023-10-02 11:52:28 +0900 |
---|---|---|
committer | Amit Langote <amitlan@postgresql.org> | 2023-10-02 11:52:28 +0900 |
commit | 7fbc75b26ed8ec70c729c5e7f8233896c54c900f (patch) | |
tree | c0b389d0970bf286e99e8f558c83dd2394f5f99c /src/backend/executor | |
parent | 2940f1c8373cb3b43b3b42d40786b05d6e901de1 (diff) | |
download | postgresql-7fbc75b26ed8ec70c729c5e7f8233896c54c900f.tar.gz postgresql-7fbc75b26ed8ec70c729c5e7f8233896c54c900f.zip |
Add soft error handling to some expression nodes
This adjusts the expression evaluation code for CoerceViaIO and
CoerceToDomain to handle errors softly if needed.
For CoerceViaIo, this means using InputFunctionCallSafe(), which
provides the option to handle errors softly, instead of calling the
type input function directly.
For CoerceToDomain, this simply entails replacing the ereport() in
ExecEvalConstraintCheck() by errsave().
In both cases, the ErrorSaveContext to be used when evaluating the
expression is stored by ExecInitExprRec() in the expression's struct
in the expression's ExprEvalStep. The ErrorSaveContext is passed by
setting ExprState.escontext to point to it when calling
ExecInitExprRec() on the expression whose errors are to be handled
softly.
Note that no call site of ExecInitExprRec() has been changed in this
commit, so there's no functional change. This is intended for
implementing new SQL/JSON expression nodes in future commits that
will use to it suppress errors that may occur during type coercions.
Reviewed-by: Álvaro Herrera
Discussion: https://postgr.es/m/CA+HiwqE4XTdfb1nW=Ojoy_tQSRhYt-q_kb6i5d4xcKyrLC1Nbg@mail.gmail.com
Diffstat (limited to 'src/backend/executor')
-rw-r--r-- | src/backend/executor/execExpr.c | 28 | ||||
-rw-r--r-- | src/backend/executor/execExprInterp.c | 36 |
2 files changed, 28 insertions, 36 deletions
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index 2c62b0c9c84..f6780bbf52c 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -139,6 +139,7 @@ ExecInitExpr(Expr *node, PlanState *parent) state->expr = node; state->parent = parent; state->ext_params = NULL; + state->escontext = NULL; /* Insert setup steps as needed */ ExecCreateExprSetupSteps(state, (Node *) node); @@ -176,6 +177,7 @@ ExecInitExprWithParams(Expr *node, ParamListInfo ext_params) state->expr = node; state->parent = NULL; state->ext_params = ext_params; + state->escontext = NULL; /* Insert setup steps as needed */ ExecCreateExprSetupSteps(state, (Node *) node); @@ -228,6 +230,7 @@ ExecInitQual(List *qual, PlanState *parent) state->expr = (Expr *) qual; state->parent = parent; state->ext_params = NULL; + state->escontext = NULL; /* mark expression as to be used with ExecQual() */ state->flags = EEO_FLAG_IS_QUAL; @@ -373,6 +376,7 @@ ExecBuildProjectionInfo(List *targetList, state->expr = (Expr *) targetList; state->parent = parent; state->ext_params = NULL; + state->escontext = NULL; state->resultslot = slot; @@ -544,6 +548,7 @@ ExecBuildUpdateProjection(List *targetList, state->expr = NULL; /* not used */ state->parent = parent; state->ext_params = NULL; + state->escontext = NULL; state->resultslot = slot; @@ -1549,8 +1554,6 @@ ExecInitExprRec(Expr *node, ExprState *state, CoerceViaIO *iocoerce = (CoerceViaIO *) node; Oid iofunc; bool typisvarlena; - Oid typioparam; - FunctionCallInfo fcinfo_in; /* evaluate argument into step's result area */ ExecInitExprRec(iocoerce->arg, state, resv, resnull); @@ -1579,25 +1582,13 @@ ExecInitExprRec(Expr *node, ExprState *state, /* lookup the result type's input function */ scratch.d.iocoerce.finfo_in = palloc0(sizeof(FmgrInfo)); - scratch.d.iocoerce.fcinfo_data_in = palloc0(SizeForFunctionCallInfo(3)); - getTypeInputInfo(iocoerce->resulttype, - &iofunc, &typioparam); + &iofunc, &scratch.d.iocoerce.typioparam); fmgr_info(iofunc, scratch.d.iocoerce.finfo_in); fmgr_info_set_expr((Node *) node, scratch.d.iocoerce.finfo_in); - InitFunctionCallInfoData(*scratch.d.iocoerce.fcinfo_data_in, - scratch.d.iocoerce.finfo_in, - 3, InvalidOid, NULL, NULL); - /* - * We can preload the second and third arguments for the input - * function, since they're constants. - */ - fcinfo_in = scratch.d.iocoerce.fcinfo_data_in; - fcinfo_in->args[1].value = ObjectIdGetDatum(typioparam); - fcinfo_in->args[1].isnull = false; - fcinfo_in->args[2].value = Int32GetDatum(-1); - fcinfo_in->args[2].isnull = false; + /* Set ErrorSaveContext if passed by the caller. */ + scratch.d.iocoerce.escontext = state->escontext; ExprEvalPushStep(state, &scratch); break; @@ -1628,6 +1619,7 @@ ExecInitExprRec(Expr *node, ExprState *state, elemstate->expr = acoerce->elemexpr; elemstate->parent = state->parent; elemstate->ext_params = state->ext_params; + state->escontext = NULL; elemstate->innermost_caseval = (Datum *) palloc(sizeof(Datum)); elemstate->innermost_casenull = (bool *) palloc(sizeof(bool)); @@ -3306,6 +3298,8 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest, /* we'll allocate workspace only if needed */ scratch->d.domaincheck.checkvalue = NULL; scratch->d.domaincheck.checknull = NULL; + /* Set ErrorSaveContext if passed by the caller. */ + scratch->d.domaincheck.escontext = state->escontext; /* * Evaluate argument - it's fine to directly store it into resv/resnull, diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index 24c2b60c62a..e55fd10e3b8 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -1177,29 +1177,27 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) /* call input function (similar to InputFunctionCall) */ if (!op->d.iocoerce.finfo_in->fn_strict || str != NULL) { - FunctionCallInfo fcinfo_in; - Datum d; + bool error; - fcinfo_in = op->d.iocoerce.fcinfo_data_in; - fcinfo_in->args[0].value = PointerGetDatum(str); - fcinfo_in->args[0].isnull = *op->resnull; - /* second and third arguments are already set up */ - - fcinfo_in->isnull = false; - d = FunctionCallInvoke(fcinfo_in); - *op->resvalue = d; + /* + * InputFunctionCallSafe() writes directly into *op->resvalue. + * Return NULL if an error is reported. + */ + error = !InputFunctionCallSafe(op->d.iocoerce.finfo_in, str, + op->d.iocoerce.typioparam, -1, + (Node *) op->d.iocoerce.escontext, + op->resvalue); + if (error) + *op->resnull = true; - /* Should get null result if and only if str is NULL */ - if (str == NULL) - { + /* + * Should get null result if and only if str is NULL or if we + * got an error above. + */ + if (str == NULL || error) Assert(*op->resnull); - Assert(fcinfo_in->isnull); - } else - { Assert(!*op->resnull); - Assert(!fcinfo_in->isnull); - } } EEO_NEXT(); @@ -3745,7 +3743,7 @@ ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op) { if (!*op->d.domaincheck.checknull && !DatumGetBool(*op->d.domaincheck.checkvalue)) - ereport(ERROR, + errsave((Node *) op->d.domaincheck.escontext, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("value for domain %s violates check constraint \"%s\"", format_type_be(op->d.domaincheck.resulttype), |