mirror of
https://github.com/postgres/postgres.git
synced 2025-07-14 08:21:07 +03:00
Replace EEOP_DONE with special steps for return/no return
Knowing when the side-effects of an expression is the intended result of the execution, rather than the returnvalue, is important for being able generate more efficient JITed code. This replaces EEOP_DONE with two new steps: EEOP_DONE_RETURN and EEOP_DONE_NO_RETURN. Expressions which return a value should use the former step; expressions used for their side-effects which don't return value should use the latter. Author: Andres Freund <andres@anarazel.de> Co-authored-by: Daniel Gustafsson <daniel@yesql.se> Reviewed-by: Andreas Karlsson <andreas@proxel.se> Discussion: https://postgr.es/m/415721CE-7D2E-4B74-B5D9-1950083BA03E@yesql.se Discussion: https://postgr.es/m/20191023163849.sosqbfs5yenocez3@alap3.anarazel.de
This commit is contained in:
@ -133,9 +133,14 @@ is used by the function evaluation step, thus avoiding extra work to copy
|
|||||||
the result values around.
|
the result values around.
|
||||||
|
|
||||||
The last entry in a completed ExprState->steps array is always an
|
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
|
EEOP_DONE_RETURN or EEOP_DONE_NO_RETURN step; this removes the need to
|
||||||
iterating. Also, if the expression contains any variable references (to
|
test for end-of-array while iterating. The former is used when the
|
||||||
user columns of the ExprContext's INNER, OUTER, or SCAN tuples), the steps
|
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
|
array begins with EEOP_*_FETCHSOME steps that ensure that the relevant
|
||||||
tuples have been deconstructed to make the required columns directly
|
tuples have been deconstructed to make the required columns directly
|
||||||
available (cf. slot_getsomeattrs()). This allows individual Var-fetching
|
available (cf. slot_getsomeattrs()). This allows individual Var-fetching
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* using ExecInitExpr() et al. This converts the tree into a flat array
|
* using ExecInitExpr() et al. This converts the tree into a flat array
|
||||||
* of ExprEvalSteps, which may be thought of as instructions in a program.
|
* 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
|
* 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
|
* This file contains the "compilation" logic. It is independent of the
|
||||||
* specific execution technology we use (switch statement, computed goto,
|
* specific execution technology we use (switch statement, computed goto,
|
||||||
@ -162,7 +162,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
|
|||||||
ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
|
ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
|
||||||
|
|
||||||
/* Finally, append a DONE step */
|
/* Finally, append a DONE step */
|
||||||
scratch.opcode = EEOP_DONE;
|
scratch.opcode = EEOP_DONE_RETURN;
|
||||||
ExprEvalPushStep(state, &scratch);
|
ExprEvalPushStep(state, &scratch);
|
||||||
|
|
||||||
ExecReadyExpr(state);
|
ExecReadyExpr(state);
|
||||||
@ -199,7 +199,7 @@ ExecInitExprWithParams(Expr *node, ParamListInfo ext_params)
|
|||||||
ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
|
ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
|
||||||
|
|
||||||
/* Finally, append a DONE step */
|
/* Finally, append a DONE step */
|
||||||
scratch.opcode = EEOP_DONE;
|
scratch.opcode = EEOP_DONE_RETURN;
|
||||||
ExprEvalPushStep(state, &scratch);
|
ExprEvalPushStep(state, &scratch);
|
||||||
|
|
||||||
ExecReadyExpr(state);
|
ExecReadyExpr(state);
|
||||||
@ -291,7 +291,7 @@ ExecInitQual(List *qual, PlanState *parent)
|
|||||||
* have yielded TRUE, and since its result is stored in the desired output
|
* have yielded TRUE, and since its result is stored in the desired output
|
||||||
* location, we're done.
|
* location, we're done.
|
||||||
*/
|
*/
|
||||||
scratch.opcode = EEOP_DONE;
|
scratch.opcode = EEOP_DONE_RETURN;
|
||||||
ExprEvalPushStep(state, &scratch);
|
ExprEvalPushStep(state, &scratch);
|
||||||
|
|
||||||
ExecReadyExpr(state);
|
ExecReadyExpr(state);
|
||||||
@ -503,7 +503,7 @@ ExecBuildProjectionInfo(List *targetList,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scratch.opcode = EEOP_DONE;
|
scratch.opcode = EEOP_DONE_NO_RETURN;
|
||||||
ExprEvalPushStep(state, &scratch);
|
ExprEvalPushStep(state, &scratch);
|
||||||
|
|
||||||
ExecReadyExpr(state);
|
ExecReadyExpr(state);
|
||||||
@ -742,7 +742,7 @@ ExecBuildUpdateProjection(List *targetList,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scratch.opcode = EEOP_DONE;
|
scratch.opcode = EEOP_DONE_NO_RETURN;
|
||||||
ExprEvalPushStep(state, &scratch);
|
ExprEvalPushStep(state, &scratch);
|
||||||
|
|
||||||
ExecReadyExpr(state);
|
ExecReadyExpr(state);
|
||||||
@ -1714,7 +1714,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Not trivial, so append a DONE step */
|
/* Not trivial, so append a DONE step */
|
||||||
scratch.opcode = EEOP_DONE;
|
scratch.opcode = EEOP_DONE_RETURN;
|
||||||
ExprEvalPushStep(elemstate, &scratch);
|
ExprEvalPushStep(elemstate, &scratch);
|
||||||
/* and ready the subexpression */
|
/* and ready the subexpression */
|
||||||
ExecReadyExpr(elemstate);
|
ExecReadyExpr(elemstate);
|
||||||
@ -3991,7 +3991,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
|
|||||||
|
|
||||||
scratch.resvalue = NULL;
|
scratch.resvalue = NULL;
|
||||||
scratch.resnull = NULL;
|
scratch.resnull = NULL;
|
||||||
scratch.opcode = EEOP_DONE;
|
scratch.opcode = EEOP_DONE_NO_RETURN;
|
||||||
ExprEvalPushStep(state, &scratch);
|
ExprEvalPushStep(state, &scratch);
|
||||||
|
|
||||||
ExecReadyExpr(state);
|
ExecReadyExpr(state);
|
||||||
@ -4258,7 +4258,7 @@ ExecBuildHash32FromAttrs(TupleDesc desc, const TupleTableSlotOps *ops,
|
|||||||
|
|
||||||
scratch.resvalue = NULL;
|
scratch.resvalue = NULL;
|
||||||
scratch.resnull = NULL;
|
scratch.resnull = NULL;
|
||||||
scratch.opcode = EEOP_DONE;
|
scratch.opcode = EEOP_DONE_RETURN;
|
||||||
ExprEvalPushStep(state, &scratch);
|
ExprEvalPushStep(state, &scratch);
|
||||||
|
|
||||||
ExecReadyExpr(state);
|
ExecReadyExpr(state);
|
||||||
@ -4431,7 +4431,7 @@ ExecBuildHash32Expr(TupleDesc desc, const TupleTableSlotOps *ops,
|
|||||||
|
|
||||||
scratch.resvalue = NULL;
|
scratch.resvalue = NULL;
|
||||||
scratch.resnull = NULL;
|
scratch.resnull = NULL;
|
||||||
scratch.opcode = EEOP_DONE;
|
scratch.opcode = EEOP_DONE_RETURN;
|
||||||
ExprEvalPushStep(state, &scratch);
|
ExprEvalPushStep(state, &scratch);
|
||||||
|
|
||||||
ExecReadyExpr(state);
|
ExecReadyExpr(state);
|
||||||
@ -4586,7 +4586,7 @@ ExecBuildGroupingEqual(TupleDesc ldesc, TupleDesc rdesc,
|
|||||||
|
|
||||||
scratch.resvalue = NULL;
|
scratch.resvalue = NULL;
|
||||||
scratch.resnull = NULL;
|
scratch.resnull = NULL;
|
||||||
scratch.opcode = EEOP_DONE;
|
scratch.opcode = EEOP_DONE_RETURN;
|
||||||
ExprEvalPushStep(state, &scratch);
|
ExprEvalPushStep(state, &scratch);
|
||||||
|
|
||||||
ExecReadyExpr(state);
|
ExecReadyExpr(state);
|
||||||
@ -4722,7 +4722,7 @@ ExecBuildParamSetEqual(TupleDesc desc,
|
|||||||
|
|
||||||
scratch.resvalue = NULL;
|
scratch.resvalue = NULL;
|
||||||
scratch.resnull = NULL;
|
scratch.resnull = NULL;
|
||||||
scratch.opcode = EEOP_DONE;
|
scratch.opcode = EEOP_DONE_RETURN;
|
||||||
ExprEvalPushStep(state, &scratch);
|
ExprEvalPushStep(state, &scratch);
|
||||||
|
|
||||||
ExecReadyExpr(state);
|
ExecReadyExpr(state);
|
||||||
|
@ -246,7 +246,8 @@ ExecReadyInterpretedExpr(ExprState *state)
|
|||||||
|
|
||||||
/* Simple validity checks on expression */
|
/* Simple validity checks on expression */
|
||||||
Assert(state->steps_len >= 1);
|
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
|
* Don't perform redundant initialization. This is unreachable in current
|
||||||
@ -469,7 +470,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
|
|||||||
*/
|
*/
|
||||||
#if defined(EEO_USE_COMPUTED_GOTO)
|
#if defined(EEO_USE_COMPUTED_GOTO)
|
||||||
static const void *const dispatch_table[] = {
|
static const void *const dispatch_table[] = {
|
||||||
&&CASE_EEOP_DONE,
|
&&CASE_EEOP_DONE_RETURN,
|
||||||
|
&&CASE_EEOP_DONE_NO_RETURN,
|
||||||
&&CASE_EEOP_INNER_FETCHSOME,
|
&&CASE_EEOP_INNER_FETCHSOME,
|
||||||
&&CASE_EEOP_OUTER_FETCHSOME,
|
&&CASE_EEOP_OUTER_FETCHSOME,
|
||||||
&&CASE_EEOP_SCAN_FETCHSOME,
|
&&CASE_EEOP_SCAN_FETCHSOME,
|
||||||
@ -612,9 +614,16 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
|
|||||||
|
|
||||||
EEO_SWITCH()
|
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 (Datum) 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
EEO_CASE(EEOP_INNER_FETCHSOME)
|
EEO_CASE(EEOP_INNER_FETCHSOME)
|
||||||
@ -2188,13 +2197,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
|
|||||||
{
|
{
|
||||||
/* unreachable */
|
/* unreachable */
|
||||||
Assert(false);
|
Assert(false);
|
||||||
goto out;
|
goto out_error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out_error:
|
||||||
*isnull = state->resnull;
|
pg_unreachable();
|
||||||
return state->resvalue;
|
return (Datum) 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -816,11 +816,8 @@ advance_transition_function(AggState *aggstate,
|
|||||||
static void
|
static void
|
||||||
advance_aggregates(AggState *aggstate)
|
advance_aggregates(AggState *aggstate)
|
||||||
{
|
{
|
||||||
bool dummynull;
|
ExecEvalExprNoReturnSwitchContext(aggstate->phase->evaltrans,
|
||||||
|
aggstate->tmpcontext);
|
||||||
ExecEvalExprSwitchContext(aggstate->phase->evaltrans,
|
|
||||||
aggstate->tmpcontext,
|
|
||||||
&dummynull);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -321,7 +321,7 @@ llvm_compile_expr(ExprState *state)
|
|||||||
|
|
||||||
switch (opcode)
|
switch (opcode)
|
||||||
{
|
{
|
||||||
case EEOP_DONE:
|
case EEOP_DONE_RETURN:
|
||||||
{
|
{
|
||||||
LLVMValueRef v_tmpisnull;
|
LLVMValueRef v_tmpisnull;
|
||||||
LLVMValueRef v_tmpvalue;
|
LLVMValueRef v_tmpvalue;
|
||||||
@ -335,6 +335,10 @@ llvm_compile_expr(ExprState *state)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case EEOP_DONE_NO_RETURN:
|
||||||
|
LLVMBuildRet(b, l_sizet_const(0));
|
||||||
|
break;
|
||||||
|
|
||||||
case EEOP_INNER_FETCHSOME:
|
case EEOP_INNER_FETCHSOME:
|
||||||
case EEOP_OUTER_FETCHSOME:
|
case EEOP_OUTER_FETCHSOME:
|
||||||
case EEOP_SCAN_FETCHSOME:
|
case EEOP_SCAN_FETCHSOME:
|
||||||
|
@ -65,8 +65,11 @@ typedef struct ExprEvalRowtypeCache
|
|||||||
*/
|
*/
|
||||||
typedef enum ExprEvalOp
|
typedef enum ExprEvalOp
|
||||||
{
|
{
|
||||||
/* entire expression has been evaluated completely, return */
|
/* entire expression has been evaluated, return value */
|
||||||
EEOP_DONE,
|
EEOP_DONE_RETURN,
|
||||||
|
|
||||||
|
/* entire expression has been evaluated, no return value */
|
||||||
|
EEOP_DONE_NO_RETURN,
|
||||||
|
|
||||||
/* apply slot_getsomeattrs on corresponding tuple slot */
|
/* apply slot_getsomeattrs on corresponding tuple slot */
|
||||||
EEOP_INNER_FETCHSOME,
|
EEOP_INNER_FETCHSOME,
|
||||||
|
@ -379,6 +379,34 @@ ExecEvalExpr(ExprState *state,
|
|||||||
}
|
}
|
||||||
#endif
|
#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
|
* ExecEvalExprSwitchContext
|
||||||
*
|
*
|
||||||
@ -400,6 +428,25 @@ ExecEvalExprSwitchContext(ExprState *state,
|
|||||||
}
|
}
|
||||||
#endif
|
#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
|
* ExecProject
|
||||||
*
|
*
|
||||||
@ -419,7 +466,6 @@ ExecProject(ProjectionInfo *projInfo)
|
|||||||
ExprContext *econtext = projInfo->pi_exprContext;
|
ExprContext *econtext = projInfo->pi_exprContext;
|
||||||
ExprState *state = &projInfo->pi_state;
|
ExprState *state = &projInfo->pi_state;
|
||||||
TupleTableSlot *slot = state->resultslot;
|
TupleTableSlot *slot = state->resultslot;
|
||||||
bool isnull;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Clear any former contents of the result slot. This makes it safe for
|
* Clear any former contents of the result slot. This makes it safe for
|
||||||
@ -427,8 +473,8 @@ ExecProject(ProjectionInfo *projInfo)
|
|||||||
*/
|
*/
|
||||||
ExecClearTuple(slot);
|
ExecClearTuple(slot);
|
||||||
|
|
||||||
/* Run the expression, discarding scalar result from the last column. */
|
/* Run the expression */
|
||||||
(void) ExecEvalExprSwitchContext(state, econtext, &isnull);
|
ExecEvalExprNoReturnSwitchContext(state, econtext);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Successfully formed a result row. Mark the result slot as containing a
|
* Successfully formed a result row. Mark the result slot as containing a
|
||||||
|
Reference in New Issue
Block a user