8000 Add soft error handling to some expression nodes · postgrespro/postgres@7fbc75b · GitHub
[go: up one dir, main page]

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
  • include
  • 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)
    0