v12-0019-invent-some-error-safe-functions.patch
text/x-patch
Filename: v12-0019-invent-some-error-safe-functions.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v12-0019
Subject: invent some error safe functions
| File | + | − |
|---|---|---|
| src/backend/executor/execExpr.c | 41 | 0 |
| src/backend/optimizer/util/clauses.c | 20 | 1 |
| src/backend/parser/parse_coerce.c | 92 | 0 |
| src/backend/parser/parse_type.c | 14 | 0 |
| src/backend/utils/fmgr/fmgr.c | 13 | 0 |
| src/include/executor/executor.h | 1 | 0 |
| src/include/fmgr.h | 3 | 0 |
| src/include/optimizer/optimizer.h | 3 | 0 |
| src/include/parser/parse_coerce.h | 4 | 0 |
| src/include/parser/parse_type.h | 2 | 0 |
From 6803dd3fcf06de55544d6e62baa48ad2c7c18d05 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Mon, 24 Nov 2025 17:03:45 +0800
Subject: [PATCH v12 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