public inbox for [email protected]  
help / color / mirror / Atom feed
From: Daniel Gustafsson <[email protected]>
To: PostgreSQL Hackers <[email protected]>
To: Andres Freund <[email protected]>
Subject: Special-case executor expression steps for common combinations
Date: Thu, 12 Oct 2023 11:48:35 +0200
Message-ID: <[email protected]> (raw)

The attached patch adds special-case expression steps for common sets of steps
in the executor to shave a few cycles off during execution, and make the JIT
generated code simpler.

* Adds EEOP_FUNCEXPR_STRICT_1 and EEOP_FUNCEXPR_STRICT_2 for function calls of
  strict functions with 1 or 2 arguments (EEOP_FUNCEXPR_STRICT remains used for
  > 2 arguments).
* Adds EEOP_AGG_STRICT_INPUT_CHECK_ARGS_1 which is a special case for the
  common case of one arg aggs.
* Replace EEOP_DONE with EEOP_DONE_RETURN and EEOP_DONE_NO_RETURN to be able to
  skip extra setup for steps which are only interested in the side effects.

Stressing the EEOP_FUNCEXPR_STRICT_* steps specifically shows a 1.5%
improvement and pgbench over the branch shows a ~1% improvement in TPS (both
measured over 6 runs with outliers removed).

EEOP_FUNCEXPR_STRICT_* (10M iterations):
    master  : (7503.317, 7553.691, 7634.524) 
    patched : (7422.756, 7455.120, 7492.393)

pgbench:
    master  : (3653.83, 3792.97, 3863.70)
    patched : (3743.04, 3830.02, 3869.80)

This patch was extracted from a larger body of work from Andres [0] aiming at
providing the necessary executor infrastructure for making JIT expression
caching possible.  This patch, and more which are to be submitted, is however
separate in the sense that it is not part of the infrastructure, it's an
improvements on its own.

Thoughts?

--
Daniel Gustafsson

[0]: https://postgr.es/m/[email protected]



Attachments:

  [application/octet-stream] v1-0001-Add-fast-path-expression-steps-for-common-combina.patch (18.4K, ../[email protected]/2-v1-0001-Add-fast-path-expression-steps-for-common-combina.patch)
  download | inline diff:
From 0935adcda7f9bc62174204f3decb9283b93cfd5a Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <[email protected]>
Date: Thu, 12 Oct 2023 11:33:27 +0200
Subject: [PATCH v1] Add fast-path expression steps for common combinations

This adds special-case expression steps for common sets of steps
in the executor in order to optimize evaluation.

* EEOP_FUNCEXPR_STRICT_1 and EEOP_FUNCEXPR_STRICT_2 for function
  calls of strict functions with one or two arguments (the older
  EEOP_FUNCEXPR_STRICT op remains used for > two arguments).
* Adds EEOP_AGG_STRICT_INPUT_CHECK_ARGS_1 which is a special case
  for the common case of one arg aggs.
* Replace EEOP_DONE with EEOP_DONE_RETURN and EEOP_DONE_NO_RETURN
  to be able to skip extra setup for steps which are only using
  the side effects.

This patch was extracted from a larger body of work providing the
necessary executor infrastructure for JIT expression caching.

Author: Andres Freund <[email protected]>
Reviewed-by: Daniel Gustafsson <[email protected]>
Discussion: https://postgr.es/m/[email protected]
Discussion: https://postgr.es/m/TBD
---
 src/backend/executor/README           | 11 ++-
 src/backend/executor/execExpr.c       | 33 ++++++---
 src/backend/executor/execExprInterp.c | 99 ++++++++++++++++++++++++---
 src/backend/executor/nodeAgg.c        |  7 +-
 src/backend/jit/llvm/llvmjit_expr.c   | 13 +++-
 src/include/executor/execExpr.h       | 14 ++--
 src/include/executor/executor.h       | 52 +++++++++++++-
 7 files changed, 192 insertions(+), 37 deletions(-)

diff --git a/src/backend/executor/README b/src/backend/executor/README
index 642d63be61..54f4782f31 100644
--- a/src/backend/executor/README
+++ b/src/backend/executor/README
@@ -133,9 +133,14 @@ is used by the function evaluation step, thus avoiding extra work to copy
 the result values around.
 
 The last entry in a completed ExprState->steps array is always an
-EEOP_DONE step; this removes the need to test for end-of-array while
-iterating.  Also, if the expression contains any variable references (to
-user columns of the ExprContext's INNER, OUTER, or SCAN tuples), the steps
+EEOP_DONE_RETURN or EEOP_DONE_NO_RETURN step; this removes the need to
+test for end-of-array while iterating. The former is used when the
+expression returns a value directly, the latter when side-effects of
+expression initialization are the goal (e.g. for projection or
+aggregate transition value computation).
+
+Also, if the expression contains any variable references (to user
+columns of the ExprContext's INNER, OUTER, or SCAN tuples), the steps
 array begins with EEOP_*_FETCHSOME steps that ensure that the relevant
 tuples have been deconstructed to make the required columns directly
 available (cf. slot_getsomeattrs()).  This allows individual Var-fetching
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 2c62b0c9c8..84a9ecec02 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -8,7 +8,7 @@
  *	using ExecInitExpr() et al.  This converts the tree into a flat array
  *	of ExprEvalSteps, which may be thought of as instructions in a program.
  *	At runtime, we'll execute steps, starting with the first, until we reach
- *	an EEOP_DONE opcode.
+ *	an EEOP_DONE_{RETURN|NO_RETURN} opcode.
  *
  *	This file contains the "compilation" logic.  It is independent of the
  *	specific execution technology we use (switch statement, computed goto,
@@ -147,7 +147,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 	ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
 
 	/* Finally, append a DONE step */
-	scratch.opcode = EEOP_DONE;
+	scratch.opcode = EEOP_DONE_RETURN;
 	ExprEvalPushStep(state, &scratch);
 
 	ExecReadyExpr(state);
@@ -184,7 +184,7 @@ ExecInitExprWithParams(Expr *node, ParamListInfo ext_params)
 	ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
 
 	/* Finally, append a DONE step */
-	scratch.opcode = EEOP_DONE;
+	scratch.opcode = EEOP_DONE_RETURN;
 	ExprEvalPushStep(state, &scratch);
 
 	ExecReadyExpr(state);
@@ -279,7 +279,7 @@ ExecInitQual(List *qual, PlanState *parent)
 	 * have yielded TRUE, and since its result is stored in the desired output
 	 * location, we're done.
 	 */
-	scratch.opcode = EEOP_DONE;
+	scratch.opcode = EEOP_DONE_RETURN;
 	ExprEvalPushStep(state, &scratch);
 
 	ExecReadyExpr(state);
@@ -474,7 +474,7 @@ ExecBuildProjectionInfo(List *targetList,
 		}
 	}
 
-	scratch.opcode = EEOP_DONE;
+	scratch.opcode = EEOP_DONE_NO_RETURN;
 	ExprEvalPushStep(state, &scratch);
 
 	ExecReadyExpr(state);
@@ -713,7 +713,7 @@ ExecBuildUpdateProjection(List *targetList,
 		}
 	}
 
-	scratch.opcode = EEOP_DONE;
+	scratch.opcode = EEOP_DONE_NO_RETURN;
 	ExprEvalPushStep(state, &scratch);
 
 	ExecReadyExpr(state);
@@ -1644,7 +1644,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				else
 				{
 					/* Not trivial, so append a DONE step */
-					scratch.opcode = EEOP_DONE;
+					scratch.opcode = EEOP_DONE_RETURN;
 					ExprEvalPushStep(elemstate, &scratch);
 					/* and ready the subexpression */
 					ExecReadyExpr(elemstate);
@@ -2663,7 +2663,15 @@ ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
 	if (pgstat_track_functions <= flinfo->fn_stats)
 	{
 		if (flinfo->fn_strict && nargs > 0)
-			scratch->opcode = EEOP_FUNCEXPR_STRICT;
+		{
+			/* Choose nargs optimized implementation if available. */
+			if (nargs == 1)
+				scratch->opcode = EEOP_FUNCEXPR_STRICT_1;
+			else if (nargs == 2)
+				scratch->opcode = EEOP_FUNCEXPR_STRICT_2;
+			else
+				scratch->opcode = EEOP_FUNCEXPR_STRICT;
+		}
 		else
 			scratch->opcode = EEOP_FUNCEXPR;
 	}
@@ -3674,6 +3682,8 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 		{
 			if (strictnulls)
 				scratch.opcode = EEOP_AGG_STRICT_INPUT_CHECK_NULLS;
+			else if (strictargs && pertrans->numTransInputs == 1)
+				scratch.opcode = EEOP_AGG_STRICT_INPUT_CHECK_ARGS_1;
 			else
 				scratch.opcode = EEOP_AGG_STRICT_INPUT_CHECK_ARGS;
 			scratch.d.agg_strict_input_check.nulls = strictnulls;
@@ -3750,6 +3760,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 				as->d.jump.jumpdone = state->steps_len;
 			}
 			else if (as->opcode == EEOP_AGG_STRICT_INPUT_CHECK_ARGS ||
+					 as->opcode == EEOP_AGG_STRICT_INPUT_CHECK_ARGS_1 ||
 					 as->opcode == EEOP_AGG_STRICT_INPUT_CHECK_NULLS)
 			{
 				Assert(as->d.agg_strict_input_check.jumpnull == -1);
@@ -3773,7 +3784,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 
 	scratch.resvalue = NULL;
 	scratch.resnull = NULL;
-	scratch.opcode = EEOP_DONE;
+	scratch.opcode = EEOP_DONE_NO_RETURN;
 	ExprEvalPushStep(state, &scratch);
 
 	ExecReadyExpr(state);
@@ -4037,7 +4048,7 @@ ExecBuildGroupingEqual(TupleDesc ldesc, TupleDesc rdesc,
 
 	scratch.resvalue = NULL;
 	scratch.resnull = NULL;
-	scratch.opcode = EEOP_DONE;
+	scratch.opcode = EEOP_DONE_RETURN;
 	ExprEvalPushStep(state, &scratch);
 
 	ExecReadyExpr(state);
@@ -4171,7 +4182,7 @@ ExecBuildParamSetEqual(TupleDesc desc,
 
 	scratch.resvalue = NULL;
 	scratch.resnull = NULL;
-	scratch.opcode = EEOP_DONE;
+	scratch.opcode = EEOP_DONE_RETURN;
 	ExprEvalPushStep(state, &scratch);
 
 	ExecReadyExpr(state);
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 24c2b60c62..265c201897 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -239,7 +239,8 @@ ExecReadyInterpretedExpr(ExprState *state)
 
 	/* Simple validity checks on expression */
 	Assert(state->steps_len >= 1);
-	Assert(state->steps[state->steps_len - 1].opcode == EEOP_DONE);
+	Assert(state->steps[state->steps_len - 1].opcode == EEOP_DONE_RETURN ||
+		   state->steps[state->steps_len - 1].opcode == EEOP_DONE_NO_RETURN);
 
 	/*
 	 * Don't perform redundant initialization. This is unreachable in current
@@ -314,7 +315,9 @@ ExecReadyInterpretedExpr(ExprState *state)
 			return;
 		}
 		else if (step0 == EEOP_CASE_TESTVAL &&
-				 step1 == EEOP_FUNCEXPR_STRICT &&
+				 (step1 == EEOP_FUNCEXPR_STRICT ||
+				  step1 == EEOP_FUNCEXPR_STRICT_1 ||
+				  step1 == EEOP_FUNCEXPR_STRICT_2) &&
 				 state->steps[0].d.casetest.value)
 		{
 			state->evalfunc_private = (void *) ExecJustApplyFuncToCase;
@@ -405,7 +408,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 	 */
 #if defined(EEO_USE_COMPUTED_GOTO)
 	static const void *const dispatch_table[] = {
-		&&CASE_EEOP_DONE,
+		&&CASE_EEOP_DONE_RETURN,
+		&&CASE_EEOP_DONE_NO_RETURN,
 		&&CASE_EEOP_INNER_FETCHSOME,
 		&&CASE_EEOP_OUTER_FETCHSOME,
 		&&CASE_EEOP_SCAN_FETCHSOME,
@@ -424,6 +428,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		&&CASE_EEOP_CONST,
 		&&CASE_EEOP_FUNCEXPR,
 		&&CASE_EEOP_FUNCEXPR_STRICT,
+		&&CASE_EEOP_FUNCEXPR_STRICT_1,
+		&&CASE_EEOP_FUNCEXPR_STRICT_2,
 		&&CASE_EEOP_FUNCEXPR_FUSAGE,
 		&&CASE_EEOP_FUNCEXPR_STRICT_FUSAGE,
 		&&CASE_EEOP_BOOL_AND_STEP_FIRST,
@@ -487,6 +493,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		&&CASE_EEOP_AGG_STRICT_DESERIALIZE,
 		&&CASE_EEOP_AGG_DESERIALIZE,
 		&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
+		&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS_1,
 		&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_NULLS,
 		&&CASE_EEOP_AGG_PLAIN_PERGROUP_NULLCHECK,
 		&&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL,
@@ -524,9 +531,16 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 
 	EEO_SWITCH()
 	{
-		EEO_CASE(EEOP_DONE)
+		EEO_CASE(EEOP_DONE_RETURN)
 		{
-			goto out;
+			*isnull = state->resnull;
+			return state->resvalue;
+		}
+
+		EEO_CASE(EEOP_DONE_NO_RETURN)
+		{
+			Assert(isnull == NULL);
+			return 0;
 		}
 
 		EEO_CASE(EEOP_INNER_FETCHSOME)
@@ -738,6 +752,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			EEO_NEXT();
 		}
 
+		/* strict function call with more than two arguments */
 		EEO_CASE(EEOP_FUNCEXPR_STRICT)
 		{
 			FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
@@ -745,6 +760,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			int			nargs = op->d.func.nargs;
 			Datum		d;
 
+			Assert(nargs > 2);
+
 			/* strict function, so check for NULL args */
 			for (int argno = 0; argno < nargs; argno++)
 			{
@@ -763,6 +780,54 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			EEO_NEXT();
 		}
 
+		/* strict function call with one argument */
+		EEO_CASE(EEOP_FUNCEXPR_STRICT_1)
+		{
+			FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+			NullableDatum *args = fcinfo->args;
+
+			Assert(op->d.func.nargs == 1);
+
+			/* strict function, so check for NULL args */
+			if (args[0].isnull)
+				*op->resnull = true;
+			else
+			{
+				Datum		d;
+
+				fcinfo->isnull = false;
+				d = op->d.func.fn_addr(fcinfo);
+				*op->resvalue = d;
+				*op->resnull = fcinfo->isnull;
+			}
+
+			EEO_NEXT();
+		}
+
+		/* strict function call with two arguments */
+		EEO_CASE(EEOP_FUNCEXPR_STRICT_2)
+		{
+			FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+			NullableDatum *args = fcinfo->args;
+
+			Assert(op->d.func.nargs == 2);
+
+			/* strict function, so check for NULL args */
+			if (args[0].isnull || args[1].isnull)
+				*op->resnull = true;
+			else
+			{
+				Datum		d;
+
+				fcinfo->isnull = false;
+				d = op->d.func.fn_addr(fcinfo);
+				*op->resvalue = d;
+				*op->resnull = fcinfo->isnull;
+			}
+
+			EEO_NEXT();
+		}
+
 		EEO_CASE(EEOP_FUNCEXPR_FUSAGE)
 		{
 			/* not common enough to inline */
@@ -1625,11 +1690,14 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		 * input is not NULL.
 		 */
 
+		/* when checking more than one argument */
 		EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK_ARGS)
 		{
 			NullableDatum *args = op->d.agg_strict_input_check.args;
 			int			nargs = op->d.agg_strict_input_check.nargs;
 
+			Assert(nargs > 1);
+
 			for (int argno = 0; argno < nargs; argno++)
 			{
 				if (args[argno].isnull)
@@ -1638,6 +1706,19 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			EEO_NEXT();
 		}
 
+		/* special case for just one argument */
+		EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK_ARGS_1)
+		{
+			NullableDatum *args = op->d.agg_strict_input_check.args;
+			PG_USED_FOR_ASSERTS_ONLY int nargs = op->d.agg_strict_input_check.nargs;
+
+			Assert(nargs == 1);
+
+			if (args[0].isnull)
+				EEO_JUMP(op->d.agg_strict_input_check.jumpnull);
+			EEO_NEXT();
+		}
+
 		EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK_NULLS)
 		{
 			bool	   *nulls = op->d.agg_strict_input_check.nulls;
@@ -1840,13 +1921,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		{
 			/* unreachable */
 			Assert(false);
-			goto out;
+			goto out_error;
 		}
 	}
 
-out:
-	*isnull = state->resnull;
-	return state->resvalue;
+out_error:
+	pg_unreachable();
+	return (Datum) 0;
 }
 
 /*
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index f154f28902..85f2bb970c 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -816,11 +816,8 @@ advance_transition_function(AggState *aggstate,
 static void
 advance_aggregates(AggState *aggstate)
 {
-	bool		dummynull;
-
-	ExecEvalExprSwitchContext(aggstate->phase->evaltrans,
-							  aggstate->tmpcontext,
-							  &dummynull);
+	ExecEvalExprNoReturnSwitchContext(aggstate->phase->evaltrans,
+									  aggstate->tmpcontext);
 }
 
 /*
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index 4b51aa1ce0..fbf257ddf5 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -251,7 +251,7 @@ llvm_compile_expr(ExprState *state)
 
 		switch (opcode)
 		{
-			case EEOP_DONE:
+			case EEOP_DONE_RETURN:
 				{
 					LLVMValueRef v_tmpisnull;
 					LLVMValueRef v_tmpvalue;
@@ -265,6 +265,10 @@ llvm_compile_expr(ExprState *state)
 					break;
 				}
 
+			case EEOP_DONE_NO_RETURN:
+				LLVMBuildRet(b, l_sizet_const(0));
+				break;
+
 			case EEOP_INNER_FETCHSOME:
 			case EEOP_OUTER_FETCHSOME:
 			case EEOP_SCAN_FETCHSOME:
@@ -542,12 +546,16 @@ llvm_compile_expr(ExprState *state)
 
 			case EEOP_FUNCEXPR:
 			case EEOP_FUNCEXPR_STRICT:
+			case EEOP_FUNCEXPR_STRICT_1:
+			case EEOP_FUNCEXPR_STRICT_2:
 				{
 					FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
 					LLVMValueRef v_fcinfo_isnull;
 					LLVMValueRef v_retval;
 
-					if (opcode == EEOP_FUNCEXPR_STRICT)
+					if (opcode == EEOP_FUNCEXPR_STRICT ||
+						opcode == EEOP_FUNCEXPR_STRICT_1 ||
+						opcode == EEOP_FUNCEXPR_STRICT_2)
 					{
 						LLVMBasicBlockRef b_nonull;
 						LLVMBasicBlockRef *b_checkargnulls;
@@ -1984,6 +1992,7 @@ llvm_compile_expr(ExprState *state)
 				}
 
 			case EEOP_AGG_STRICT_INPUT_CHECK_ARGS:
+			case EEOP_AGG_STRICT_INPUT_CHECK_ARGS_1:
 			case EEOP_AGG_STRICT_INPUT_CHECK_NULLS:
 				{
 					int			nargs = op->d.agg_strict_input_check.nargs;
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 048573c2bc..4e6609dd26 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -64,8 +64,11 @@ typedef struct ExprEvalRowtypeCache
  */
 typedef enum ExprEvalOp
 {
-	/* entire expression has been evaluated completely, return */
-	EEOP_DONE,
+	/* entire expression has been evaluated, return value */
+	EEOP_DONE_RETURN,
+
+	/* entire expression has been evaluated, no return value */
+	EEOP_DONE_NO_RETURN,
 
 	/* apply slot_getsomeattrs on corresponding tuple slot */
 	EEOP_INNER_FETCHSOME,
@@ -104,11 +107,13 @@ typedef enum ExprEvalOp
 
 	/*
 	 * Evaluate function call (including OpExprs etc).  For speed, we
-	 * distinguish in the opcode whether the function is strict and/or
-	 * requires usage stats tracking.
+	 * distinguish in the opcode whether the function is strict with 1, 2, or
+	 * more arguments and/or requires usage stats tracking.
 	 */
 	EEOP_FUNCEXPR,
 	EEOP_FUNCEXPR_STRICT,
+	EEOP_FUNCEXPR_STRICT_1,
+	EEOP_FUNCEXPR_STRICT_2,
 	EEOP_FUNCEXPR_FUSAGE,
 	EEOP_FUNCEXPR_STRICT_FUSAGE,
 
@@ -247,6 +252,7 @@ typedef enum ExprEvalOp
 	EEOP_AGG_STRICT_DESERIALIZE,
 	EEOP_AGG_DESERIALIZE,
 	EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
+	EEOP_AGG_STRICT_INPUT_CHECK_ARGS_1,
 	EEOP_AGG_STRICT_INPUT_CHECK_NULLS,
 	EEOP_AGG_PLAIN_PERGROUP_NULLCHECK,
 	EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL,
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index e1eefb400b..cc321bbf9e 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -337,6 +337,34 @@ ExecEvalExpr(ExprState *state,
 }
 #endif
 
+/*
+ * ExecEvalExprNoReturn
+ *
+ * Like ExecEvalExpr(), but for cases where no return value is expected,
+ * because the side-effects of expression evaluation are what's desired. This
+ * is e.g. used for projection and aggregate transition computation.
+
+ * Evaluate expression identified by "state" in the execution context
+ * given by "econtext".
+ *
+ * The caller should already have switched into the temporary memory context
+ * econtext->ecxt_per_tuple_memory.  The convenience entry point
+ * ExecEvalExprNoReturnSwitchContext() is provided for callers who don't
+ * prefer to do the switch in an outer loop.
+ */
+#ifndef FRONTEND
+static inline void
+ExecEvalExprNoReturn(ExprState *state,
+					 ExprContext *econtext)
+{
+	PG_USED_FOR_ASSERTS_ONLY Datum retDatum;
+
+	retDatum = state->evalfunc(state, econtext, NULL);
+
+	Assert(retDatum == (Datum) 0);
+}
+#endif
+
 /*
  * ExecEvalExprSwitchContext
  *
@@ -358,6 +386,25 @@ ExecEvalExprSwitchContext(ExprState *state,
 }
 #endif
 
+/*
+ * ExecEvalExprNoReturnSwitchContext
+ *
+ * Same as ExecEvalExprNoReturn, but get into the right allocation context
+ * explicitly.
+ */
+#ifndef FRONTEND
+static inline void
+ExecEvalExprNoReturnSwitchContext(ExprState *state,
+								  ExprContext *econtext)
+{
+	MemoryContext oldContext;
+
+	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+	ExecEvalExprNoReturn(state, econtext);
+	MemoryContextSwitchTo(oldContext);
+}
+#endif
+
 /*
  * ExecProject
  *
@@ -377,7 +424,6 @@ ExecProject(ProjectionInfo *projInfo)
 	ExprContext *econtext = projInfo->pi_exprContext;
 	ExprState  *state = &projInfo->pi_state;
 	TupleTableSlot *slot = state->resultslot;
-	bool		isnull;
 
 	/*
 	 * Clear any former contents of the result slot.  This makes it safe for
@@ -385,8 +431,8 @@ ExecProject(ProjectionInfo *projInfo)
 	 */
 	ExecClearTuple(slot);
 
-	/* Run the expression, discarding scalar result from the last column. */
-	(void) ExecEvalExprSwitchContext(state, econtext, &isnull);
+	/* Run the expression */
+	ExecEvalExprNoReturnSwitchContext(state, econtext);
 
 	/*
 	 * Successfully formed a result row.  Mark the result slot as containing a
-- 
2.32.1 (Apple Git-133)



view thread (2+ messages)

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: [email protected]
  Cc: [email protected], [email protected], [email protected]
  Subject: Re: Special-case executor expression steps for common combinations
  In-Reply-To: <[email protected]>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox