From 1217392db3918ca941e6734b5855f7cf130451cc Mon Sep 17 00:00:00 2001 From: jian he Date: Mon, 24 Nov 2025 17:03:45 +0800 Subject: [PATCH v11 19/20] invent some error safe functions stringTypeDatumSafe: error safe version of stringTypeDatum evaluate_expr_extended: If you wish to evaluate a constant expression in a manner that is safe from potential errors, set the error_safe parameter to true ExecInitExprSafe: soft error variant of ExecInitExpr OidInputFunctionCallSafe: soft error variant of OidInputFunctionCall CoerceUnknownConstSafe: attempts to coerce an UNKNOWN Const to the target type. Returns NULL if the coercion is not possible. discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com --- src/backend/executor/execExpr.c | 41 +++++++++++++ src/backend/optimizer/util/clauses.c | 21 ++++++- src/backend/parser/parse_coerce.c | 92 ++++++++++++++++++++++++++++ src/backend/parser/parse_type.c | 14 +++++ src/backend/utils/fmgr/fmgr.c | 13 ++++ src/include/executor/executor.h | 1 + src/include/fmgr.h | 3 + src/include/optimizer/optimizer.h | 3 + src/include/parser/parse_coerce.h | 4 ++ src/include/parser/parse_type.h | 2 + 10 files changed, 193 insertions(+), 1 deletion(-) diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index f1569879b52..b302be36f73 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -170,6 +170,47 @@ ExecInitExpr(Expr *node, PlanState *parent) return state; } +/* + * ExecInitExprSafe: soft error variant of ExecInitExpr. + * + * use it only for expression nodes support soft errors, not all expression + * nodes support it. +*/ +ExprState * +ExecInitExprSafe(Expr *node, PlanState *parent) +{ + ExprState *state; + ExprEvalStep scratch = {0}; + + /* Special case: NULL expression produces a NULL ExprState pointer */ + if (node == NULL) + return NULL; + + /* Initialize ExprState with empty step list */ + state = makeNode(ExprState); + state->expr = node; + state->parent = parent; + state->ext_params = NULL; + state->escontext = makeNode(ErrorSaveContext); + state->escontext->type = T_ErrorSaveContext; + state->escontext->error_occurred = false; + state->escontext->details_wanted = false; + + /* Insert setup steps as needed */ + ExecCreateExprSetupSteps(state, (Node *) node); + + /* Compile the expression proper */ + ExecInitExprRec(node, state, &state->resvalue, &state->resnull); + + /* Finally, append a DONE step */ + scratch.opcode = EEOP_DONE_RETURN; + ExprEvalPushStep(state, &scratch); + + ExecReadyExpr(state); + + return state; +} + /* * ExecInitExprWithParams: prepare a standalone expression tree for execution * diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 202ba8ed4bb..32af0df6c70 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -5081,6 +5081,16 @@ sql_inline_error_callback(void *arg) Expr * evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod, Oid result_collation) +{ + return evaluate_expr_extended(expr, + result_type, + result_typmod, + result_collation, + false); +} +Expr * +evaluate_expr_extended(Expr *expr, Oid result_type, int32 result_typmod, + Oid result_collation, bool error_safe) { EState *estate; ExprState *exprstate; @@ -5105,7 +5115,10 @@ 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 (error_safe) + exprstate = ExecInitExprSafe(expr, NULL); + else + exprstate = ExecInitExpr(expr, NULL); /* * And evaluate it. @@ -5125,6 +5138,12 @@ evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod, /* Get back to outer memory context */ MemoryContextSwitchTo(oldcontext); + if (error_safe && SOFT_ERROR_OCCURRED(exprstate->escontext)) + { + FreeExecutorState(estate); + return NULL; + } + /* * Must copy result out of sub-context used by expression eval. * diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 78b1e366ad7..cdcdd44a799 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -657,6 +657,98 @@ can_coerce_type(int nargs, const Oid *input_typeids, const Oid *target_typeids, return true; } +/* + * Create an expression tree to represent coercion a UNKNOWN Const node. + * + * 'node': the input expression + * 'baseTypeId': base type of domain + * 'baseTypeMod': base type typmod of domain + * 'targetType': target type to coerce to + * 'targetTypeMod': target type typmod + * 'ccontext': context indicator to control coercions + * 'cformat': coercion display format + * 'location': coercion request location + * 'hideInputCoercion': if true, hide the input coercion under this one. + */ +Node * +CoerceUnknownConstSafe(ParseState *pstate, Node *node, Oid targetType, int32 targetTypeMod, + CoercionContext ccontext, CoercionForm cformat, int location, + bool hideInputCoercion) +{ + Oid baseTypeId; + int32 baseTypeMod; + int32 inputTypeMod; + Type baseType; + char *string; + Datum datum; + Const *newcon; + Node *result = NULL; + Const *con = (Const *) node; + + Assert(IsA(node, Const)); + Assert(exprType(node) == UNKNOWNOID); + + baseTypeMod = targetTypeMod; + baseTypeId = getBaseTypeAndTypmod(targetType, &baseTypeMod); + + if (baseTypeId == INTERVALOID) + inputTypeMod = baseTypeMod; + else + inputTypeMod = -1; + + baseType = typeidType(baseTypeId); + + /* + * We assume here that UNKNOWN's internal representation is the same as + * CSTRING. + */ + if (!con->constisnull) + string = DatumGetCString(con->constvalue); + else + string = NULL; + + if (!stringTypeDatumSafe(baseType, + string, + inputTypeMod, + &datum)) + { + ReleaseSysCache(baseType); + return NULL; + } + + newcon = makeNode(Const); + newcon->consttype = baseTypeId; + newcon->consttypmod = inputTypeMod; + newcon->constcollid = typeTypeCollation(baseType); + newcon->constlen = typeLen(baseType); + newcon->constbyval = typeByVal(baseType); + newcon->constisnull = con->constisnull; + newcon->constvalue = datum; + + /* + * If it's a varlena value, force it to be in non-expanded + * (non-toasted) format; this avoids any possible dependency on + * external values and improves consistency of representation. + */ + if (!con->constisnull && newcon->constlen == -1) + newcon->constvalue = + PointerGetDatum(PG_DETOAST_DATUM(newcon->constvalue)); + + result = (Node *) newcon; + + /* If target is a domain, apply constraints. */ + if (baseTypeId != targetType) + result = coerce_to_domain(result, + baseTypeId, baseTypeMod, + targetType, + ccontext, cformat, location, + false); + + ReleaseSysCache(baseType); + + return result; +} + /* * Create an expression tree to represent coercion to a domain type. diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index 7713bdc6af0..d260aeec5dc 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -19,6 +19,7 @@ #include "catalog/pg_type.h" #include "lib/stringinfo.h" #include "nodes/makefuncs.h" +#include "nodes/miscnodes.h" #include "parser/parse_type.h" #include "parser/parser.h" #include "utils/array.h" @@ -660,6 +661,19 @@ stringTypeDatum(Type tp, char *string, int32 atttypmod) return OidInputFunctionCall(typinput, string, typioparam, atttypmod); } +/* error safe version of stringTypeDatum */ +bool +stringTypeDatumSafe(Type tp, char *string, int32 atttypmod, Datum *result) +{ + Form_pg_type typform = (Form_pg_type) GETSTRUCT(tp); + Oid typinput = typform->typinput; + Oid typioparam = getTypeIOParam(tp); + ErrorSaveContext escontext = {T_ErrorSaveContext}; + + return OidInputFunctionCallSafe(typinput, string, typioparam, atttypmod, + (Node *) &escontext, result); +} + /* * Given a typeid, return the type's typrelid (associated relation), if any. * Returns InvalidOid if type is not a composite type. diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index 0fe63c6bb83..aaa4a42b1ea 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -1759,6 +1759,19 @@ OidInputFunctionCall(Oid functionId, char *str, Oid typioparam, int32 typmod) return InputFunctionCall(&flinfo, str, typioparam, typmod); } +bool +OidInputFunctionCallSafe(Oid functionId, char *str, Oid typioparam, + int32 typmod, Node *escontext, + Datum *result) +{ + FmgrInfo flinfo; + + fmgr_info(functionId, &flinfo); + + return InputFunctionCallSafe(&flinfo, str, typioparam, typmod, + escontext, result); +} + char * OidOutputFunctionCall(Oid functionId, Datum val) { diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index fa2b657fb2f..f99fc26eb1f 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -324,6 +324,7 @@ ExecProcNode(PlanState *node) * prototypes from functions in execExpr.c */ extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); +extern ExprState *ExecInitExprSafe(Expr *node, PlanState *parent); 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/fmgr.h b/src/include/fmgr.h index 74fe3ea0575..991e14034d3 100644 --- a/src/include/fmgr.h +++ b/src/include/fmgr.h @@ -750,6 +750,9 @@ extern bool DirectInputFunctionCallSafe(PGFunction func, char *str, Datum *result); extern Datum OidInputFunctionCall(Oid functionId, char *str, Oid typioparam, int32 typmod); +extern bool OidInputFunctionCallSafe(Oid functionId, char *str, Oid typioparam, + int32 typmod, Node *escontext, + Datum *result); extern char *OutputFunctionCall(FmgrInfo *flinfo, Datum val); extern char *OidOutputFunctionCall(Oid functionId, Datum val); extern Datum ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf, diff --git a/src/include/optimizer/optimizer.h b/src/include/optimizer/optimizer.h index d0aa8ab0c1c..97a0337452c 100644 --- a/src/include/optimizer/optimizer.h +++ b/src/include/optimizer/optimizer.h @@ -144,6 +144,9 @@ extern Node *estimate_expression_value(PlannerInfo *root, Node *node); extern Expr *evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod, Oid result_collation); +extern Expr *evaluate_expr_extended(Expr *expr, Oid result_type, + int32 result_typmod, Oid result_collation, + bool error_safe); extern bool var_is_nonnullable(PlannerInfo *root, Var *var, bool use_rel_info); diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h index 8d775c72c59..ad16cdd7022 100644 --- a/src/include/parser/parse_coerce.h +++ b/src/include/parser/parse_coerce.h @@ -48,6 +48,10 @@ extern bool can_coerce_type(int nargs, const Oid *input_typeids, const Oid *targ extern Node *coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod, CoercionContext ccontext, CoercionForm cformat, int location); +extern Node *CoerceUnknownConstSafe(ParseState *pstate, Node *node, + Oid targetType, int32 targetTypeMod, + CoercionContext ccontext, CoercionForm cformat, + int location, bool hideInputCoercion); extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId, CoercionContext ccontext, CoercionForm cformat, int location, diff --git a/src/include/parser/parse_type.h b/src/include/parser/parse_type.h index 0d919d8bfa2..12381aed64c 100644 --- a/src/include/parser/parse_type.h +++ b/src/include/parser/parse_type.h @@ -47,6 +47,8 @@ extern char *typeTypeName(Type t); extern Oid typeTypeRelid(Type typ); extern Oid typeTypeCollation(Type typ); extern Datum stringTypeDatum(Type tp, char *string, int32 atttypmod); +extern bool stringTypeDatumSafe(Type tp, char *string, int32 atttypmod, + Datum *result); extern Oid typeidTypeRelid(Oid type_id); extern Oid typeOrDomainTypeRelid(Oid type_id); -- 2.34.1