Skip to content

Commit da16b2b

Browse files
danielgustafssonCommitfest Bot
authored and
Commitfest Bot
committed
Add special case fast-paths for strict functions
Many STRICT function calls will have one or two arguments, in which case we can speed up checking for NULL input by avoiding setting up a loop over the arguments. This adds EEOP_FUNCEXPR_STRICT_1 and the corresponding EEOP_FUNCEXPR_STRICT_2 for functions with one and two arguments respectively. Author: Andres Freund, Daniel Gustafsson Reviewed-by: Andreas Karlsson <[email protected]> Discussion: https://postgr.es/m/[email protected] Discussion: https://postgr.es/m/[email protected]
1 parent 554c44c commit da16b2b

File tree

4 files changed

+97
-6
lines changed

4 files changed

+97
-6
lines changed

src/backend/executor/execExpr.c

+13-2
Original file line numberDiff line numberDiff line change
@@ -2788,7 +2788,15 @@ ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
27882788
if (pgstat_track_functions <= flinfo->fn_stats)
27892789
{
27902790
if (flinfo->fn_strict && nargs > 0)
2791-
scratch->opcode = EEOP_FUNCEXPR_STRICT;
2791+
{
2792+
/* Choose nargs optimized implementation if available. */
2793+
if (nargs == 1)
2794+
scratch->opcode = EEOP_FUNCEXPR_STRICT_1;
2795+
else if (nargs == 2)
2796+
scratch->opcode = EEOP_FUNCEXPR_STRICT_2;
2797+
else
2798+
scratch->opcode = EEOP_FUNCEXPR_STRICT;
2799+
}
27922800
else
27932801
scratch->opcode = EEOP_FUNCEXPR;
27942802
}
@@ -3892,6 +3900,8 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
38923900
{
38933901
if (strictnulls)
38943902
scratch.opcode = EEOP_AGG_STRICT_INPUT_CHECK_NULLS;
3903+
else if (strictargs && pertrans->numTransInputs == 1)
3904+
scratch.opcode = EEOP_AGG_STRICT_INPUT_CHECK_ARGS_1;
38953905
else
38963906
scratch.opcode = EEOP_AGG_STRICT_INPUT_CHECK_ARGS;
38973907
scratch.d.agg_strict_input_check.nulls = strictnulls;
@@ -3968,6 +3978,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
39683978
as->d.jump.jumpdone = state->steps_len;
39693979
}
39703980
else if (as->opcode == EEOP_AGG_STRICT_INPUT_CHECK_ARGS ||
3981+
as->opcode == EEOP_AGG_STRICT_INPUT_CHECK_ARGS_1 ||
39713982
as->opcode == EEOP_AGG_STRICT_INPUT_CHECK_NULLS)
39723983
{
39733984
Assert(as->d.agg_strict_input_check.jumpnull == -1);
@@ -4722,7 +4733,7 @@ ExecBuildParamSetEqual(TupleDesc desc,
47224733

47234734
scratch.resvalue = NULL;
47244735
scratch.resnull = NULL;
4725-
scratch.opcode = EEOP_DONE;
4736+
scratch.opcode = EEOP_DONE_RETURN;
47264737
ExprEvalPushStep(state, &scratch);
47274738

47284739
ExecReadyExpr(state);

src/backend/executor/execExprInterp.c

+73-1
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,9 @@ ExecReadyInterpretedExpr(ExprState *state)
366366
return;
367367
}
368368
else if (step0 == EEOP_CASE_TESTVAL &&
369-
step1 == EEOP_FUNCEXPR_STRICT)
369+
(step1 == EEOP_FUNCEXPR_STRICT ||
370+
step1 == EEOP_FUNCEXPR_STRICT_1 ||
371+
step1 == EEOP_FUNCEXPR_STRICT_2))
370372
{
371373
state->evalfunc_private = ExecJustApplyFuncToCase;
372374
return;
@@ -498,6 +500,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
498500
&&CASE_EEOP_CONST,
499501
&&CASE_EEOP_FUNCEXPR,
500502
&&CASE_EEOP_FUNCEXPR_STRICT,
503+
&&CASE_EEOP_FUNCEXPR_STRICT_1,
504+
&&CASE_EEOP_FUNCEXPR_STRICT_2,
501505
&&CASE_EEOP_FUNCEXPR_FUSAGE,
502506
&&CASE_EEOP_FUNCEXPR_STRICT_FUSAGE,
503507
&&CASE_EEOP_BOOL_AND_STEP_FIRST,
@@ -575,6 +579,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
575579
&&CASE_EEOP_AGG_STRICT_DESERIALIZE,
576580
&&CASE_EEOP_AGG_DESERIALIZE,
577581
&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
582+
&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS_1,
578583
&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_NULLS,
579584
&&CASE_EEOP_AGG_PLAIN_PERGROUP_NULLCHECK,
580585
&&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL,
@@ -925,13 +930,16 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
925930
EEO_NEXT();
926931
}
927932

933+
/* strict function call with more than two arguments */
928934
EEO_CASE(EEOP_FUNCEXPR_STRICT)
929935
{
930936
FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
931937
NullableDatum *args = fcinfo->args;
932938
int nargs = op->d.func.nargs;
933939
Datum d;
934940

941+
Assert(nargs > 2);
942+
935943
/* strict function, so check for NULL args */
936944
for (int argno = 0; argno < nargs; argno++)
937945
{
@@ -950,6 +958,54 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
950958
EEO_NEXT();
951959
}
952960

961+
/* strict function call with one argument */
962+
EEO_CASE(EEOP_FUNCEXPR_STRICT_1)
963+
{
964+
FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
965+
NullableDatum *args = fcinfo->args;
966+
967+
Assert(op->d.func.nargs == 1);
968+
969+
/* strict function, so check for NULL args */
970+
if (args[0].isnull)
971+
*op->resnull = true;
972+
else
973+
{
974+
Datum d;
975+
976+
fcinfo->isnull = false;
977+
d = op->d.func.fn_addr(fcinfo);
978+
*op->resvalue = d;
979+
*op->resnull = fcinfo->isnull;
980+
}
981+
982+
EEO_NEXT();
983+
}
984+
985+
/* strict function call with two arguments */
986+
EEO_CASE(EEOP_FUNCEXPR_STRICT_2)
987+
{
988+
FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
989+
NullableDatum *args = fcinfo->args;
990+
991+
Assert(op->d.func.nargs == 2);
992+
993+
/* strict function, so check for NULL args */
994+
if (args[0].isnull || args[1].isnull)
995+
*op->resnull = true;
996+
else
997+
{
998+
Datum d;
999+
1000+
fcinfo->isnull = false;
1001+
d = op->d.func.fn_addr(fcinfo);
1002+
*op->resvalue = d;
1003+
*op->resnull = fcinfo->isnull;
1004+
}
1005+
1006+
EEO_NEXT();
1007+
}
1008+
9531009
EEO_CASE(EEOP_FUNCEXPR_FUSAGE)
9541010
{
9551011
/* not common enough to inline */
@@ -1982,11 +2038,14 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
19822038
* input is not NULL.
19832039
*/
19842040

2041+
/* when checking more than one argument */
19852042
EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK_ARGS)
19862043
{
19872044
NullableDatum *args = op->d.agg_strict_input_check.args;
19882045
int nargs = op->d.agg_strict_input_check.nargs;
19892046

2047+
Assert(nargs > 1);
2048+
19902049
for (int argno = 0; argno < nargs; argno++)
19912050
{
19922051
if (args[argno].isnull)
@@ -1995,6 +2054,19 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
19952054
EEO_NEXT();
19962055
}
19972056

2057+
/* special case for just one argument */
2058+
EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK_ARGS_1)
2059+
{
2060+
NullableDatum *args = op->d.agg_strict_input_check.args;
2061+
PG_USED_FOR_ASSERTS_ONLY int nargs = op->d.agg_strict_input_check.nargs;
2062+
2063+
Assert(nargs == 1);
2064+
2065+
if (args[0].isnull)
2066+
EEO_JUMP(op->d.agg_strict_input_check.jumpnull);
2067+
EEO_NEXT();
2068+
}
2069+
19982070
EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK_NULLS)
19992071
{
20002072
bool *nulls = op->d.agg_strict_input_check.nulls;

src/backend/jit/llvm/llvmjit_expr.c

+6-1
Original file line numberDiff line numberDiff line change
@@ -662,12 +662,16 @@ llvm_compile_expr(ExprState *state)
662662

663663
case EEOP_FUNCEXPR:
664664
case EEOP_FUNCEXPR_STRICT:
665+
case EEOP_FUNCEXPR_STRICT_1:
666+
case EEOP_FUNCEXPR_STRICT_2:
665667
{
666668
FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
667669
LLVMValueRef v_fcinfo_isnull;
668670
LLVMValueRef v_retval;
669671

670-
if (opcode == EEOP_FUNCEXPR_STRICT)
672+
if (opcode == EEOP_FUNCEXPR_STRICT ||
673+
opcode == EEOP_FUNCEXPR_STRICT_1 ||
674+
opcode == EEOP_FUNCEXPR_STRICT_2)
671675
{
672676
LLVMBasicBlockRef b_nonull;
673677
LLVMBasicBlockRef *b_checkargnulls;
@@ -2482,6 +2486,7 @@ llvm_compile_expr(ExprState *state)
24822486
}
24832487

24842488
case EEOP_AGG_STRICT_INPUT_CHECK_ARGS:
2489+
case EEOP_AGG_STRICT_INPUT_CHECK_ARGS_1:
24852490
case EEOP_AGG_STRICT_INPUT_CHECK_NULLS:
24862491
{
24872492
int nargs = op->d.agg_strict_input_check.nargs;

src/include/executor/execExpr.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,13 @@ typedef enum ExprEvalOp
116116

117117
/*
118118
* Evaluate function call (including OpExprs etc). For speed, we
119-
* distinguish in the opcode whether the function is strict and/or
120-
* requires usage stats tracking.
119+
* distinguish in the opcode whether the function is strict with 1, 2, or
120+
* more arguments and/or requires usage stats tracking.
121121
*/
122122
EEOP_FUNCEXPR,
123123
EEOP_FUNCEXPR_STRICT,
124+
EEOP_FUNCEXPR_STRICT_1,
125+
EEOP_FUNCEXPR_STRICT_2,
124126
EEOP_FUNCEXPR_FUSAGE,
125127
EEOP_FUNCEXPR_STRICT_FUSAGE,
126128

@@ -276,6 +278,7 @@ typedef enum ExprEvalOp
276278
EEOP_AGG_STRICT_DESERIALIZE,
277279
EEOP_AGG_DESERIALIZE,
278280
EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
281+
EEOP_AGG_STRICT_INPUT_CHECK_ARGS_1,
279282
EEOP_AGG_STRICT_INPUT_CHECK_NULLS,
280283
EEOP_AGG_PLAIN_PERGROUP_NULLCHECK,
281284
EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL,

0 commit comments

Comments
 (0)