Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 7fbc75b

Browse files
committed
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
1 parent 2940f1c commit 7fbc75b

File tree

9 files changed

+96
-69
lines changed

9 files changed

+96
-69
lines changed

src/backend/executor/execExpr.c

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
139139
state->expr = node;
140140
state->parent = parent;
141141
state->ext_params = NULL;
142+
state->escontext = NULL;
142143

143144
/* Insert setup steps as needed */
144145
ExecCreateExprSetupSteps(state, (Node *) node);
@@ -176,6 +177,7 @@ ExecInitExprWithParams(Expr *node, ParamListInfo ext_params)
176177
state->expr = node;
177178
state->parent = NULL;
178179
state->ext_params = ext_params;
180+
state->escontext = NULL;
179181

180182
/* Insert setup steps as needed */
181183
ExecCreateExprSetupSteps(state, (Node *) node);
@@ -228,6 +230,7 @@ ExecInitQual(List *qual, PlanState *parent)
228230
state->expr = (Expr *) qual;
229231
state->parent = parent;
230232
state->ext_params = NULL;
233+
state->escontext = NULL;
231234

232235
/* mark expression as to be used with ExecQual() */
233236
state->flags = EEO_FLAG_IS_QUAL;
@@ -373,6 +376,7 @@ ExecBuildProjectionInfo(List *targetList,
373376
state->expr = (Expr *) targetList;
374377
state->parent = parent;
375378
state->ext_params = NULL;
379+
state->escontext = NULL;
376380

377381
state->resultslot = slot;
378382

@@ -544,6 +548,7 @@ ExecBuildUpdateProjection(List *targetList,
544548
state->expr = NULL; /* not used */
545549
state->parent = parent;
546550
state->ext_params = NULL;
551+
state->escontext = NULL;
547552

548553
state->resultslot = slot;
549554

@@ -1549,8 +1554,6 @@ ExecInitExprRec(Expr *node, ExprState *state,
15491554
CoerceViaIO *iocoerce = (CoerceViaIO *) node;
15501555
Oid iofunc;
15511556
bool typisvarlena;
1552-
Oid typioparam;
1553-
FunctionCallInfo fcinfo_in;
15541557

15551558
/* evaluate argument into step's result area */
15561559
ExecInitExprRec(iocoerce->arg, state, resv, resnull);
@@ -1579,25 +1582,13 @@ ExecInitExprRec(Expr *node, ExprState *state,
15791582

15801583
/* lookup the result type's input function */
15811584
scratch.d.iocoerce.finfo_in = palloc0(sizeof(FmgrInfo));
1582-
scratch.d.iocoerce.fcinfo_data_in = palloc0(SizeForFunctionCallInfo(3));
1583-
15841585
getTypeInputInfo(iocoerce->resulttype,
1585-
&iofunc, &typioparam);
1586+
&iofunc, &scratch.d.iocoerce.typioparam);
15861587
fmgr_info(iofunc, scratch.d.iocoerce.finfo_in);
15871588
fmgr_info_set_expr((Node *) node, scratch.d.iocoerce.finfo_in);
1588-
InitFunctionCallInfoData(*scratch.d.iocoerce.fcinfo_data_in,
1589-
scratch.d.iocoerce.finfo_in,
1590-
3, InvalidOid, NULL, NULL);
15911589

1592-
/*
1593-
* We can preload the second and third arguments for the input
1594-
* function, since they're constants.
1595-
*/
1596-
fcinfo_in = scratch.d.iocoerce.fcinfo_data_in;
1597-
fcinfo_in->args[1].value = ObjectIdGetDatum(typioparam);
1598-
fcinfo_in->args[1].isnull = false;
1599-
fcinfo_in->args[2].value = Int32GetDatum(-1);
1600-
fcinfo_in->args[2].isnull = false;
1590+
/* Set ErrorSaveContext if passed by the caller. */
1591+
scratch.d.iocoerce.escontext = state->escontext;
16011592

16021593
ExprEvalPushStep(state, &scratch);
16031594
break;
@@ -1628,6 +1619,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
16281619
elemstate->expr = acoerce->elemexpr;
16291620
elemstate->parent = state->parent;
16301621
elemstate->ext_params = state->ext_params;
1622+
state->escontext = NULL;
16311623

16321624
elemstate->innermost_caseval = (Datum *) palloc(sizeof(Datum));
16331625
elemstate->innermost_casenull = (bool *) palloc(sizeof(bool));
@@ -3306,6 +3298,8 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
33063298
/* we'll allocate workspace only if needed */
33073299
scratch->d.domaincheck.checkvalue = NULL;
33083300
scratch->d.domaincheck.checknull = NULL;
3301+
/* Set ErrorSaveContext if passed by the caller. */
3302+
scratch->d.domaincheck.escontext = state->escontext;
33093303

33103304
/*
33113305
* Evaluate argument - it's fine to directly store it into resv/resnull,

src/backend/executor/execExprInterp.c

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,29 +1177,27 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
11771177
/* call input function (similar to InputFunctionCall) */
11781178
if (!op->d.iocoerce.finfo_in->fn_strict || str != NULL)
11791179
{
1180-
FunctionCallInfo fcinfo_in;
1181-
Datum d;
1180+
bool error;
11821181

1183-
fcinfo_in = op->d.iocoerce.fcinfo_data_in;
1184-
fcinfo_in->args[0].value = PointerGetDatum(str);
1185-
fcinfo_in->args[0].isnull = *op->resnull;
1186-
/* second and third arguments are already set up */
1187-
1188-
fcinfo_in->isnull = false;
1189-
d = FunctionCallInvoke(fcinfo_in);
1190-
*op->resvalue = d;
1182+
/*
1183+
* InputFunctionCallSafe() writes directly into *op->resvalue.
1184+
* Return NULL if an error is reported.
1185+
*/
1186+
error = !InputFunctionCallSafe(op->d.iocoerce.finfo_in, str,
1187+
op->d.iocoerce.typioparam, -1,
1188+
(Node *) op->d.iocoerce.escontext,
1189+
op->resvalue);
1190+
if (error)
1191+
*op->resnull = true;
11911192

1192-
/* Should get null result if and only if str is NULL */
1193-
if (str == NULL)
1194-
{
1193+
/*
1194+
* Should get null result if and only if str is NULL or if we
1195+
* got an error above.
1196+
*/
1197+
if (str == NULL || error)
11951198
Assert(*op->resnull);
1196-
Assert(fcinfo_in->isnull);
1197-
}
11981199
else
1199-
{
12001200
Assert(!*op->resnull);
1201-
Assert(!fcinfo_in->isnull);
1202-
}
12031201
}
12041202

12051203
EEO_NEXT();
@@ -3745,7 +3743,7 @@ ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op)
37453743
{
37463744
if (!*op->d.domaincheck.checknull &&
37473745
!DatumGetBool(*op->d.domaincheck.checkvalue))
3748-
ereport(ERROR,
3746+
errsave((Node *) op->d.domaincheck.escontext,
37493747
(errcode(ERRCODE_CHECK_VIOLATION),
37503748
errmsg("value for domain %s violates check constraint \"%s\"",
37513749
format_type_be(op->d.domaincheck.resulttype),

src/backend/jit/llvm/llvmjit.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,14 @@ LLVMTypeRef StructHeapTupleTableSlot;
7070
LLVMTypeRef StructMinimalTupleTableSlot;
7171
LLVMTypeRef StructMemoryContextData;
7272
LLVMTypeRef StructFunctionCallInfoData;
73+
LLVMTypeRef StructFmgrInfo;
7374
LLVMTypeRef StructExprContext;
7475
LLVMTypeRef StructExprEvalStep;
7576
LLVMTypeRef StructExprState;
7677
LLVMTypeRef StructAggState;
7778
LLVMTypeRef StructAggStatePerGroupData;
7879
LLVMTypeRef StructAggStatePerTransData;
80+
LLVMTypeRef StructErrorSaveContext;
7981

8082
LLVMValueRef AttributeTemplate;
8183

@@ -1118,6 +1120,7 @@ llvm_create_types(void)
11181120
StructExprEvalStep = llvm_pg_var_type("StructExprEvalStep");
11191121
StructExprState = llvm_pg_var_type("StructExprState");
11201122
StructFunctionCallInfoData = llvm_pg_var_type("StructFunctionCallInfoData");
1123+
StructFmgrInfo = llvm_pg_var_type("StructFmgrInfo");
11211124
StructMemoryContextData = llvm_pg_var_type("StructMemoryContextData");
11221125
StructTupleTableSlot = llvm_pg_var_type("StructTupleTableSlot");
11231126
StructHeapTupleTableSlot = llvm_pg_var_type("StructHeapTupleTableSlot");
@@ -1127,6 +1130,7 @@ llvm_create_types(void)
11271130
StructAggState = llvm_pg_var_type("StructAggState");
11281131
StructAggStatePerGroupData = llvm_pg_var_type("StructAggStatePerGroupData");
11291132
StructAggStatePerTransData = llvm_pg_var_type("StructAggStatePerTransData");
1133+
StructErrorSaveContext = llvm_pg_var_type("StructErrorSaveContext");
11301134

11311135
AttributeTemplate = LLVMGetNamedFunction(llvm_types_module, "AttributeTemplate");
11321136
}

src/backend/jit/llvm/llvmjit_expr.c

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,14 +1251,9 @@ llvm_compile_expr(ExprState *state)
12511251

12521252
case EEOP_IOCOERCE:
12531253
{
1254-
FunctionCallInfo fcinfo_out,
1255-
fcinfo_in;
1256-
LLVMValueRef v_fn_out,
1257-
v_fn_in;
1258-
LLVMValueRef v_fcinfo_out,
1259-
v_fcinfo_in;
1260-
LLVMValueRef v_fcinfo_in_isnullp;
1261-
LLVMValueRef v_retval;
1254+
FunctionCallInfo fcinfo_out;
1255+
LLVMValueRef v_fn_out;
1256+
LLVMValueRef v_fcinfo_out;
12621257
LLVMValueRef v_resvalue;
12631258
LLVMValueRef v_resnull;
12641259

@@ -1271,7 +1266,6 @@ llvm_compile_expr(ExprState *state)
12711266
LLVMBasicBlockRef b_inputcall;
12721267

12731268
fcinfo_out = op->d.iocoerce.fcinfo_data_out;
1274-
fcinfo_in = op->d.iocoerce.fcinfo_data_in;
12751269

12761270
b_skipoutput = l_bb_before_v(opblocks[opno + 1],
12771271
"op.%d.skipoutputnull", opno);
@@ -1283,14 +1277,7 @@ llvm_compile_expr(ExprState *state)
12831277
"op.%d.inputcall", opno);
12841278

12851279
v_fn_out = llvm_function_reference(context, b, mod, fcinfo_out);
1286-
v_fn_in = llvm_function_reference(context, b, mod, fcinfo_in);
12871280
v_fcinfo_out = l_ptr_const(fcinfo_out, l_ptr(StructFunctionCallInfoData));
1288-
v_fcinfo_in = l_ptr_const(fcinfo_in, l_ptr(StructFunctionCallInfoData));
1289-
1290-
v_fcinfo_in_isnullp =
1291-
LLVMBuildStructGEP(b, v_fcinfo_in,
1292-
FIELDNO_FUNCTIONCALLINFODATA_ISNULL,
1293-
"v_fcinfo_in_isnull");
12941281

12951282
/* output functions are not called on nulls */
12961283
v_resnull = LLVMBuildLoad(b, v_resnullp, "");
@@ -1356,24 +1343,44 @@ llvm_compile_expr(ExprState *state)
13561343
LLVMBuildBr(b, b_inputcall);
13571344
}
13581345

1346+
/*
1347+
* Call the input function.
1348+
*
1349+
* If op->d.iocoerce.escontext references an
1350+
* ErrorSaveContext, InputFunctionCallSafe() would return
1351+
* false upon encountering an error.
1352+
*/
13591353
LLVMPositionBuilderAtEnd(b, b_inputcall);
1360-
/* set arguments */
1361-
/* arg0: output */
1362-
LLVMBuildStore(b, v_output,
1363-
l_funcvaluep(b, v_fcinfo_in, 0));
1364-
LLVMBuildStore(b, v_resnull,
1365-
l_funcnullp(b, v_fcinfo_in, 0));
1366-
1367-
/* arg1: ioparam: preset in execExpr.c */
1368-
/* arg2: typmod: preset in execExpr.c */
1369-
1370-
/* reset fcinfo_in->isnull */
1371-
LLVMBuildStore(b, l_sbool_const(0), v_fcinfo_in_isnullp);
1372-
/* and call function */
1373-
v_retval = LLVMBuildCall(b, v_fn_in, &v_fcinfo_in, 1,
1374-
"funccall_iocoerce_in");
1354+
{
1355+
Oid ioparam = op->d.iocoerce.typioparam;
1356+
LLVMValueRef v_params[6];
1357+
LLVMValueRef v_success;
1358+
1359+
v_params[0] = l_ptr_const(op->d.iocoerce.finfo_in,
1360+
l_ptr(StructFmgrInfo));
1361+
v_params[1] = v_output;
1362+
v_params[2] = l_oid_const(lc, ioparam);
1363+
v_params[3] = l_int32_const(lc, -1);
1364+
v_params[4] = l_ptr_const(op->d.iocoerce.escontext,
1365+
l_ptr(StructErrorSaveContext));
13751366

1376-
LLVMBuildStore(b, v_retval, v_resvaluep);
1367+
/*
1368+
* InputFunctionCallSafe() will write directly into
1369+
* *op->resvalue.
1370+
*/
1371+
v_params[5] = v_resvaluep;
1372+
1373+
v_success = LLVMBuildCall(b, llvm_pg_func(mod, "InputFunctionCallSafe"),
1374+
v_params, lengthof(v_params),
1375+
"funccall_iocoerce_in_safe");
1376+
1377+
/*
1378+
* Return null if InputFunctionCallSafe() encountered
1379+
* an error.
1380+
*/
1381+
v_resnullp = LLVMBuildICmp(b, LLVMIntEQ, v_success,
1382+
l_sbool_const(0), "");
1383+
}
13771384

13781385
LLVMBuildBr(b, opblocks[opno + 1]);
13791386
break;

src/backend/jit/llvm/llvmjit_types.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,15 @@ AggStatePerTransData StructAggStatePerTransData;
5959
ExprContext StructExprContext;
6060
ExprEvalStep StructExprEvalStep;
6161
ExprState StructExprState;
62+
FmgrInfo StructFmgrInfo;
6263
FunctionCallInfoBaseData StructFunctionCallInfoData;
6364
HeapTupleData StructHeapTupleData;
6465
MemoryContextData StructMemoryContextData;
6566
TupleTableSlot StructTupleTableSlot;
6667
HeapTupleTableSlot StructHeapTupleTableSlot;
6768
MinimalTupleTableSlot StructMinimalTupleTableSlot;
6869
TupleDescData StructTupleDescData;
70+
ErrorSaveContext StructErrorSaveContext;
6971

7072

7173
/*
@@ -136,6 +138,7 @@ void *referenced_functions[] =
136138
ExecEvalJsonConstructor,
137139
ExecEvalJsonIsPredicate,
138140
MakeExpandedObjectReadOnlyInternal,
141+
InputFunctionCallSafe,
139142
slot_getmissingattrs,
140143
slot_getsomeattrs_int,
141144
strlen,

src/include/executor/execExpr.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "executor/nodeAgg.h"
1818
#include "nodes/execnodes.h"
19+
#include "nodes/miscnodes.h"
1920

2021
/* forward references to avoid circularity */
2122
struct ExprEvalStep;
@@ -416,7 +417,8 @@ typedef struct ExprEvalStep
416417
FunctionCallInfo fcinfo_data_out;
417418
/* lookup and call info for result type's input function */
418419
FmgrInfo *finfo_in;
419-
FunctionCallInfo fcinfo_data_in;
420+
Oid typioparam;
421+
ErrorSaveContext *escontext;
420422
} iocoerce;
421423

422424
/* for EEOP_SQLVALUEFUNCTION */
@@ -547,6 +549,7 @@ typedef struct ExprEvalStep
547549
bool *checknull;
548550
/* OID of domain type */
549551
Oid resulttype;
552+
ErrorSaveContext *escontext;
550553
} domaincheck;
551554

552555
/* for EEOP_CONVERT_ROWTYPE */

src/include/jit/llvmjit.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,15 @@ extern PGDLLIMPORT LLVMTypeRef StructTupleTableSlot;
7575
extern PGDLLIMPORT LLVMTypeRef StructHeapTupleTableSlot;
7676
extern PGDLLIMPORT LLVMTypeRef StructMinimalTupleTableSlot;
7777
extern PGDLLIMPORT LLVMTypeRef StructMemoryContextData;
78+
extern PGDLLIMPORT LLVMTypeRef StructFmgrInfo;
7879
extern PGDLLIMPORT LLVMTypeRef StructFunctionCallInfoData;
7980
extern PGDLLIMPORT LLVMTypeRef StructExprContext;
8081
extern PGDLLIMPORT LLVMTypeRef StructExprEvalStep;
8182
extern PGDLLIMPORT LLVMTypeRef StructExprState;
8283
extern PGDLLIMPORT LLVMTypeRef StructAggState;
8384
extern PGDLLIMPORT LLVMTypeRef StructAggStatePerTransData;
8485
extern PGDLLIMPORT LLVMTypeRef StructAggStatePerGroupData;
86+
extern PGDLLIMPORT LLVMTypeRef StructErrorSaveContext;
8587

8688
extern PGDLLIMPORT LLVMValueRef AttributeTemplate;
8789

src/include/jit/llvmjit_emit.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,15 @@ l_sizet_const(size_t i)
8585
return LLVMConstInt(TypeSizeT, i, false);
8686
}
8787

88+
/*
89+
* Emit constant oid.
90+
*/
91+
static inline LLVMValueRef
92+
l_oid_const(LLVMContextRef lc, Oid i)
93+
{
94+
return LLVMConstInt(LLVMInt32TypeInContext(lc), i, false);
95+
}
96+
8897
/*
8998
* Emit constant boolean, as used for storage (e.g. global vars, structs).
9099
*/

src/include/nodes/execnodes.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "fmgr.h"
3535
#include "lib/ilist.h"
3636
#include "lib/pairingheap.h"
37+
#include "nodes/miscnodes.h"
3738
#include "nodes/params.h"
3839
#include "nodes/plannodes.h"
3940
#include "nodes/tidbitmap.h"
@@ -129,6 +130,12 @@ typedef struct ExprState
129130

130131
Datum *innermost_domainval;
131132
bool *innermost_domainnull;
133+
134+
/*
135+
* For expression nodes that support soft errors. Should be set to NULL
136+
* before calling ExecInitExprRec() if the caller wants errors thrown.
137+
*/
138+
ErrorSaveContext *escontext;
132139
} ExprState;
133140

134141

0 commit comments

Comments
 (0)