mirror of
https://github.com/postgres/postgres.git
synced 2025-04-20 00:42:27 +03:00
Add soft error handling to some expression nodes
This adjusts the code for CoerceViaIO and CoerceToDomain expression nodes 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 handled softly. Note that there's no functional change as of this commit as no call site of ExecInitExprRec() has been changed. This is intended for implementing new SQL/JSON expression nodes in future commits. Extracted from a much larger patch to add SQL/JSON query functions. Author: Nikita Glukhov <n.gluhov@postgrespro.ru> Author: Teodor Sigaev <teodor@sigaev.ru> Author: Oleg Bartunov <obartunov@gmail.com> Author: Alexander Korotkov <aekorotkov@gmail.com> Author: Andrew Dunstan <andrew@dunslane.net> Author: Amit Langote <amitlangote09@gmail.com> Reviewers have included (in no particular order) Andres Freund, Alexander Korotkov, Pavel Stehule, Andrew Alsup, Erik Rijkers, Zihong Yu, Himanshu Upadhyaya, Daniel Gustafsson, Justin Pryzby, Álvaro Herrera, Jian He, Peter Eisentraut Discussion: https://postgr.es/m/cd0bb935-0158-78a7-08b5-904886deac4b@postgrespro.ru Discussion: https://postgr.es/m/20220616233130.rparivafipt6doj3@alap3.anarazel.de Discussion: https://postgr.es/m/abd9b83b-aa66-f230-3d6d-734817f0995d%40postgresql.org Discussion: https://postgr.es/m/CA+HiwqHROpf9e644D8BRqYvaAPmgBZVup-xKMDPk-nd4EpgzHw@mail.gmail.com Discussion: https://postgr.es/m/CA+HiwqE4XTdfb1nW=Ojoy_tQSRhYt-q_kb6i5d4xcKyrLC1Nbg@mail.gmail.com
This commit is contained in:
parent
bb812ab091
commit
aaaf9449ec
@ -1560,7 +1560,10 @@ ExecInitExprRec(Expr *node, ExprState *state,
|
|||||||
* We don't check permissions here as a type's input/output
|
* We don't check permissions here as a type's input/output
|
||||||
* function are assumed to be executable by everyone.
|
* 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 */
|
/* lookup the source type's output function */
|
||||||
scratch.d.iocoerce.finfo_out = palloc0(sizeof(FmgrInfo));
|
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].value = Int32GetDatum(-1);
|
||||||
fcinfo_in->args[2].isnull = false;
|
fcinfo_in->args[2].isnull = false;
|
||||||
|
|
||||||
|
fcinfo_in->context = (Node *) state->escontext;
|
||||||
|
|
||||||
ExprEvalPushStep(state, &scratch);
|
ExprEvalPushStep(state, &scratch);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -3303,6 +3308,7 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
|
|||||||
/* we'll allocate workspace only if needed */
|
/* we'll allocate workspace only if needed */
|
||||||
scratch->d.domaincheck.checkvalue = NULL;
|
scratch->d.domaincheck.checkvalue = NULL;
|
||||||
scratch->d.domaincheck.checknull = NULL;
|
scratch->d.domaincheck.checknull = NULL;
|
||||||
|
scratch->d.domaincheck.escontext = state->escontext;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Evaluate argument - it's fine to directly store it into resv/resnull,
|
* Evaluate argument - it's fine to directly store it into resv/resnull,
|
||||||
|
@ -63,6 +63,7 @@
|
|||||||
#include "executor/nodeSubplan.h"
|
#include "executor/nodeSubplan.h"
|
||||||
#include "funcapi.h"
|
#include "funcapi.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
|
#include "nodes/miscnodes.h"
|
||||||
#include "nodes/nodeFuncs.h"
|
#include "nodes/nodeFuncs.h"
|
||||||
#include "parser/parsetree.h"
|
#include "parser/parsetree.h"
|
||||||
#include "pgstat.h"
|
#include "pgstat.h"
|
||||||
@ -452,6 +453,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
|
|||||||
&&CASE_EEOP_CASE_TESTVAL,
|
&&CASE_EEOP_CASE_TESTVAL,
|
||||||
&&CASE_EEOP_MAKE_READONLY,
|
&&CASE_EEOP_MAKE_READONLY,
|
||||||
&&CASE_EEOP_IOCOERCE,
|
&&CASE_EEOP_IOCOERCE,
|
||||||
|
&&CASE_EEOP_IOCOERCE_SAFE,
|
||||||
&&CASE_EEOP_DISTINCT,
|
&&CASE_EEOP_DISTINCT,
|
||||||
&&CASE_EEOP_NOT_DISTINCT,
|
&&CASE_EEOP_NOT_DISTINCT,
|
||||||
&&CASE_EEOP_NULLIF,
|
&&CASE_EEOP_NULLIF,
|
||||||
@ -1150,6 +1152,9 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
|
|||||||
* Evaluate a CoerceViaIO node. This can be quite a hot path, so
|
* Evaluate a CoerceViaIO node. This can be quite a hot path, so
|
||||||
* inline as much work as possible. The source value is in our
|
* inline as much work as possible. The source value is in our
|
||||||
* result variable.
|
* result variable.
|
||||||
|
*
|
||||||
|
* Also look at ExecEvalCoerceViaIOSafe() if you change anything
|
||||||
|
* here.
|
||||||
*/
|
*/
|
||||||
char *str;
|
char *str;
|
||||||
|
|
||||||
@ -1205,6 +1210,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
|
|||||||
EEO_NEXT();
|
EEO_NEXT();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EEO_CASE(EEOP_IOCOERCE_SAFE)
|
||||||
|
{
|
||||||
|
ExecEvalCoerceViaIOSafe(state, op);
|
||||||
|
EEO_NEXT();
|
||||||
|
}
|
||||||
|
|
||||||
EEO_CASE(EEOP_DISTINCT)
|
EEO_CASE(EEOP_DISTINCT)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -2510,6 +2521,71 @@ ExecEvalParamExtern(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
|
|||||||
errmsg("no value found for parameter %d", paramId)));
|
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.
|
||||||
|
*
|
||||||
|
* Note: This implements EEOP_IOCOERCE_SAFE. If you change anything here,
|
||||||
|
* also look at the inline code for EEOP_IOCOERCE.
|
||||||
|
*/
|
||||||
|
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 InputFunctionCallSafe) */
|
||||||
|
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.
|
* Evaluate a SQLValueFunction expression.
|
||||||
*/
|
*/
|
||||||
@ -3730,7 +3806,7 @@ void
|
|||||||
ExecEvalConstraintNotNull(ExprState *state, ExprEvalStep *op)
|
ExecEvalConstraintNotNull(ExprState *state, ExprEvalStep *op)
|
||||||
{
|
{
|
||||||
if (*op->resnull)
|
if (*op->resnull)
|
||||||
ereport(ERROR,
|
errsave((Node *) op->d.domaincheck.escontext,
|
||||||
(errcode(ERRCODE_NOT_NULL_VIOLATION),
|
(errcode(ERRCODE_NOT_NULL_VIOLATION),
|
||||||
errmsg("domain %s does not allow null values",
|
errmsg("domain %s does not allow null values",
|
||||||
format_type_be(op->d.domaincheck.resulttype)),
|
format_type_be(op->d.domaincheck.resulttype)),
|
||||||
@ -3745,7 +3821,7 @@ ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op)
|
|||||||
{
|
{
|
||||||
if (!*op->d.domaincheck.checknull &&
|
if (!*op->d.domaincheck.checknull &&
|
||||||
!DatumGetBool(*op->d.domaincheck.checkvalue))
|
!DatumGetBool(*op->d.domaincheck.checkvalue))
|
||||||
ereport(ERROR,
|
errsave((Node *) op->d.domaincheck.escontext,
|
||||||
(errcode(ERRCODE_CHECK_VIOLATION),
|
(errcode(ERRCODE_CHECK_VIOLATION),
|
||||||
errmsg("value for domain %s violates check constraint \"%s\"",
|
errmsg("value for domain %s violates check constraint \"%s\"",
|
||||||
format_type_be(op->d.domaincheck.resulttype),
|
format_type_be(op->d.domaincheck.resulttype),
|
||||||
|
@ -1431,6 +1431,12 @@ llvm_compile_expr(ExprState *state)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case EEOP_IOCOERCE_SAFE:
|
||||||
|
build_EvalXFunc(b, mod, "ExecEvalCoerceViaIOSafe",
|
||||||
|
v_state, op);
|
||||||
|
LLVMBuildBr(b, opblocks[opno + 1]);
|
||||||
|
break;
|
||||||
|
|
||||||
case EEOP_DISTINCT:
|
case EEOP_DISTINCT:
|
||||||
case EEOP_NOT_DISTINCT:
|
case EEOP_NOT_DISTINCT:
|
||||||
{
|
{
|
||||||
|
@ -162,6 +162,7 @@ void *referenced_functions[] =
|
|||||||
ExecEvalRow,
|
ExecEvalRow,
|
||||||
ExecEvalRowNotNull,
|
ExecEvalRowNotNull,
|
||||||
ExecEvalRowNull,
|
ExecEvalRowNull,
|
||||||
|
ExecEvalCoerceViaIOSafe,
|
||||||
ExecEvalSQLValueFunction,
|
ExecEvalSQLValueFunction,
|
||||||
ExecEvalScalarArrayOp,
|
ExecEvalScalarArrayOp,
|
||||||
ExecEvalHashedScalarArrayOp,
|
ExecEvalHashedScalarArrayOp,
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include "executor/nodeAgg.h"
|
#include "executor/nodeAgg.h"
|
||||||
#include "nodes/execnodes.h"
|
#include "nodes/execnodes.h"
|
||||||
|
#include "nodes/miscnodes.h"
|
||||||
|
|
||||||
/* forward references to avoid circularity */
|
/* forward references to avoid circularity */
|
||||||
struct ExprEvalStep;
|
struct ExprEvalStep;
|
||||||
@ -168,6 +169,7 @@ typedef enum ExprEvalOp
|
|||||||
|
|
||||||
/* evaluate assorted special-purpose expression types */
|
/* evaluate assorted special-purpose expression types */
|
||||||
EEOP_IOCOERCE,
|
EEOP_IOCOERCE,
|
||||||
|
EEOP_IOCOERCE_SAFE,
|
||||||
EEOP_DISTINCT,
|
EEOP_DISTINCT,
|
||||||
EEOP_NOT_DISTINCT,
|
EEOP_NOT_DISTINCT,
|
||||||
EEOP_NULLIF,
|
EEOP_NULLIF,
|
||||||
@ -547,6 +549,7 @@ typedef struct ExprEvalStep
|
|||||||
bool *checknull;
|
bool *checknull;
|
||||||
/* OID of domain type */
|
/* OID of domain type */
|
||||||
Oid resulttype;
|
Oid resulttype;
|
||||||
|
ErrorSaveContext *escontext;
|
||||||
} domaincheck;
|
} domaincheck;
|
||||||
|
|
||||||
/* for EEOP_CONVERT_ROWTYPE */
|
/* for EEOP_CONVERT_ROWTYPE */
|
||||||
@ -776,6 +779,7 @@ extern void ExecEvalParamExec(ExprState *state, ExprEvalStep *op,
|
|||||||
ExprContext *econtext);
|
ExprContext *econtext);
|
||||||
extern void ExecEvalParamExtern(ExprState *state, ExprEvalStep *op,
|
extern void ExecEvalParamExtern(ExprState *state, ExprEvalStep *op,
|
||||||
ExprContext *econtext);
|
ExprContext *econtext);
|
||||||
|
extern void ExecEvalCoerceViaIOSafe(ExprState *state, ExprEvalStep *op);
|
||||||
extern void ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op);
|
extern void ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op);
|
||||||
extern void ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op);
|
extern void ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op);
|
||||||
extern void ExecEvalNextValueExpr(ExprState *state, ExprEvalStep *op);
|
extern void ExecEvalNextValueExpr(ExprState *state, ExprEvalStep *op);
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include "fmgr.h"
|
#include "fmgr.h"
|
||||||
#include "lib/ilist.h"
|
#include "lib/ilist.h"
|
||||||
#include "lib/pairingheap.h"
|
#include "lib/pairingheap.h"
|
||||||
|
#include "nodes/miscnodes.h"
|
||||||
#include "nodes/params.h"
|
#include "nodes/params.h"
|
||||||
#include "nodes/plannodes.h"
|
#include "nodes/plannodes.h"
|
||||||
#include "nodes/tidbitmap.h"
|
#include "nodes/tidbitmap.h"
|
||||||
@ -129,6 +130,12 @@ typedef struct ExprState
|
|||||||
|
|
||||||
Datum *innermost_domainval;
|
Datum *innermost_domainval;
|
||||||
bool *innermost_domainnull;
|
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;
|
} ExprState;
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user