v35-0001-Add-soft-error-handling-to-some-expression-nodes.patch
application/octet-stream
Filename: v35-0001-Add-soft-error-handling-to-some-expression-nodes.patch
Type: application/octet-stream
Part: 1
Message:
Re: remaining sql/json patches
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 v35-0001
Subject: Add soft error handling to some expression nodes
| File | + | − |
|---|---|---|
| src/backend/executor/execExpr.c | 7 | 1 |
| src/backend/executor/execExprInterp.c | 72 | 2 |
| src/backend/jit/llvm/llvmjit_expr.c | 6 | 0 |
| src/backend/jit/llvm/llvmjit_types.c | 1 | 0 |
| src/include/executor/execExpr.h | 4 | 0 |
| src/include/nodes/execnodes.h | 7 | 0 |
From 6ac0da3a9f216d377fe22a3ca659a7563141bef7 Mon Sep 17 00:00:00 2001
From: Amit Langote <amitlan@postgresql.org>
Date: Thu, 18 Jan 2024 16:16:21 +0900
Subject: [PATCH v35 1/8] Add soft error handling to some expression nodes
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This adjusts the CoerceViaIO and CoerceToDomain expression evaluation
code to handle errors softly.
For CoerceViaIo, this adds a new ExprEvalStep opcode
EEOP_IOCOERCE_SAFE, which is implemented in the new accompanying
function ExecEvalCoerceViaIOSafe(). The only difference from
EEOP_IOCOERCE's inline implementation is that the input function
receives an ErrorSaveContext via the function's
FunctionCallInfo.context, which it can use to handle errors softly.
For CoerceToDomain, this simply entails replacing the ereport() in
ExecEvalConstraintNotNull() and ExecEvalConstraintCheck() by
errsave() passing it the ErrorSaveContext passed in the expression's
ExprEvalStep.
In both cases, the ErrorSaveContext to be used is passed by setting
ExprState.escontext to point to it before calling ExecInitExprRec()
on the expression tree whose errors are to be suppressed.
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.
Reviewed-by: Álvaro Herrera
Reviewed-by: Andres Freund
Discussion: https://postgr.es/m/CA+HiwqE4XTdfb1nW=Ojoy_tQSRhYt-q_kb6i5d4xcKyrLC1Nbg@mail.gmail.com
---
src/backend/executor/execExpr.c | 8 ++-
src/backend/executor/execExprInterp.c | 74 ++++++++++++++++++++++++++-
src/backend/jit/llvm/llvmjit_expr.c | 6 +++
src/backend/jit/llvm/llvmjit_types.c | 1 +
src/include/executor/execExpr.h | 4 ++
src/include/nodes/execnodes.h | 7 +++
6 files changed, 97 insertions(+), 3 deletions(-)
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 91df2009be..3181b1136a 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -1560,7 +1560,10 @@ ExecInitExprRec(Expr *node, ExprState *state,
* We don't check permissions here as a type's input/output
* function are assumed to be executable by everyone.
*/
- scratch.opcode = EEOP_IOCOERCE;
+ if (state->escontext == NULL)
+ scratch.opcode = EEOP_IOCOERCE;
+ else
+ scratch.opcode = EEOP_IOCOERCE_SAFE;
/* lookup the source type's output function */
scratch.d.iocoerce.finfo_out = palloc0(sizeof(FmgrInfo));
@@ -1596,6 +1599,8 @@ ExecInitExprRec(Expr *node, ExprState *state,
fcinfo_in->args[2].value = Int32GetDatum(-1);
fcinfo_in->args[2].isnull = false;
+ fcinfo_in->context = (Node *) state->escontext;
+
ExprEvalPushStep(state, &scratch);
break;
}
@@ -3303,6 +3308,7 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
/* we'll allocate workspace only if needed */
scratch->d.domaincheck.checkvalue = NULL;
scratch->d.domaincheck.checknull = NULL;
+ 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 3c17cc6b1e..b17cab06b6 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -63,6 +63,7 @@
#include "executor/nodeSubplan.h"
#include "funcapi.h"
#include "miscadmin.h"
+#include "nodes/miscnodes.h"
#include "nodes/nodeFuncs.h"
#include "parser/parsetree.h"
#include "pgstat.h"
@@ -452,6 +453,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_CASE_TESTVAL,
&&CASE_EEOP_MAKE_READONLY,
&&CASE_EEOP_IOCOERCE,
+ &&CASE_EEOP_IOCOERCE_SAFE,
&&CASE_EEOP_DISTINCT,
&&CASE_EEOP_NOT_DISTINCT,
&&CASE_EEOP_NULLIF,
@@ -1205,6 +1207,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_IOCOERCE_SAFE)
+ {
+ ExecEvalCoerceViaIOSafe(state, op);
+ EEO_NEXT();
+ }
+
EEO_CASE(EEOP_DISTINCT)
{
/*
@@ -2510,6 +2518,68 @@ ExecEvalParamExtern(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
errmsg("no value found for parameter %d", paramId)));
}
+/*
+ * Evaluate a CoerceViaIO node in soft-error mode.
+ *
+ * The source value is in op's result variable.
+ */
+void
+ExecEvalCoerceViaIOSafe(ExprState *state, ExprEvalStep *op)
+{
+ char *str;
+
+ /* call output function (similar to OutputFunctionCall) */
+ if (*op->resnull)
+ {
+ /* output functions are not called on nulls */
+ str = NULL;
+ }
+ else
+ {
+ FunctionCallInfo fcinfo_out;
+
+ fcinfo_out = op->d.iocoerce.fcinfo_data_out;
+ fcinfo_out->args[0].value = *op->resvalue;
+ fcinfo_out->args[0].isnull = false;
+
+ fcinfo_out->isnull = false;
+ str = DatumGetCString(FunctionCallInvoke(fcinfo_out));
+
+ /* OutputFunctionCall assumes result isn't null */
+ Assert(!fcinfo_out->isnull);
+ }
+
+ /* call input function (similar to InputFunctionCall) */
+ if (!op->d.iocoerce.finfo_in->fn_strict || str != NULL)
+ {
+ FunctionCallInfo fcinfo_in;
+
+ 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 */
+
+ /* ErrorSaveContext must be present. */
+ Assert(IsA(fcinfo_in->context, ErrorSaveContext));
+
+ fcinfo_in->isnull = false;
+ *op->resvalue = FunctionCallInvoke(fcinfo_in);
+
+ if (SOFT_ERROR_OCCURRED(fcinfo_in->context))
+ {
+ *op->resnull = true;
+ *op->resvalue = (Datum) 0;
+ return;
+ }
+
+ /* Should get null result if and only if str is NULL */
+ if (str == NULL)
+ Assert(*op->resnull);
+ else
+ Assert(!*op->resnull);
+ }
+}
+
/*
* Evaluate a SQLValueFunction expression.
*/
@@ -3730,7 +3800,7 @@ void
ExecEvalConstraintNotNull(ExprState *state, ExprEvalStep *op)
{
if (*op->resnull)
- ereport(ERROR,
+ errsave((Node *) op->d.domaincheck.escontext,
(errcode(ERRCODE_NOT_NULL_VIOLATION),
errmsg("domain %s does not allow null values",
format_type_be(op->d.domaincheck.resulttype)),
@@ -3745,7 +3815,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),
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index 33161d812f..09994503b1 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -1431,6 +1431,12 @@ llvm_compile_expr(ExprState *state)
break;
}
+ case EEOP_IOCOERCE_SAFE:
+ build_EvalXFunc(b, mod, "ExecEvalCoerceViaIOSafe",
+ v_state, op);
+ LLVMBuildBr(b, opblocks[opno + 1]);
+ break;
+
case EEOP_DISTINCT:
case EEOP_NOT_DISTINCT:
{
diff --git a/src/backend/jit/llvm/llvmjit_types.c b/src/backend/jit/llvm/llvmjit_types.c
index 5212f529c8..47c9daf402 100644
--- a/src/backend/jit/llvm/llvmjit_types.c
+++ b/src/backend/jit/llvm/llvmjit_types.c
@@ -162,6 +162,7 @@ void *referenced_functions[] =
ExecEvalRow,
ExecEvalRowNotNull,
ExecEvalRowNull,
+ ExecEvalCoerceViaIOSafe,
ExecEvalSQLValueFunction,
ExecEvalScalarArrayOp,
ExecEvalHashedScalarArrayOp,
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index a20c539a25..a28ddcdd77 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -16,6 +16,7 @@
#include "executor/nodeAgg.h"
#include "nodes/execnodes.h"
+#include "nodes/miscnodes.h"
/* forward references to avoid circularity */
struct ExprEvalStep;
@@ -168,6 +169,7 @@ typedef enum ExprEvalOp
/* evaluate assorted special-purpose expression types */
EEOP_IOCOERCE,
+ EEOP_IOCOERCE_SAFE,
EEOP_DISTINCT,
EEOP_NOT_DISTINCT,
EEOP_NULLIF,
@@ -547,6 +549,7 @@ typedef struct ExprEvalStep
bool *checknull;
/* OID of domain type */
Oid resulttype;
+ ErrorSaveContext *escontext;
} domaincheck;
/* for EEOP_CONVERT_ROWTYPE */
@@ -776,6 +779,7 @@ extern void ExecEvalParamExec(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
extern void ExecEvalParamExtern(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
+extern void ExecEvalCoerceViaIOSafe(ExprState *state, ExprEvalStep *op);
extern void ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op);
extern void ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op);
extern void ExecEvalNextValueExpr(ExprState *state, ExprEvalStep *op);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 561fdd98f1..444a5f0fd5 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -34,6 +34,7 @@
#include "fmgr.h"
#include "lib/ilist.h"
#include "lib/pairingheap.h"
+#include "nodes/miscnodes.h"
#include "nodes/params.h"
#include "nodes/plannodes.h"
#include "nodes/tidbitmap.h"
@@ -129,6 +130,12 @@ typedef struct ExprState
Datum *innermost_domainval;
bool *innermost_domainnull;
+
+ /*
+ * For expression nodes that support soft errors. Should be set to NULL
+ * before calling ExecInitExprRec() if the caller wants errors thrown.
+ */
+ ErrorSaveContext *escontext;
} ExprState;
--
2.35.3