diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/executor/execExpr.c | 7 | ||||
-rw-r--r-- | src/backend/executor/execExprInterp.c | 277 | ||||
-rw-r--r-- | src/include/executor/execExpr.h | 14 | ||||
-rw-r--r-- | src/include/nodes/execnodes.h | 3 |
4 files changed, 165 insertions, 136 deletions
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index 55bb9251917..2642b404ff6 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -680,20 +680,19 @@ ExecInitExprRec(Expr *node, ExprState *state, /* regular user column */ scratch.d.var.attnum = variable->varattno - 1; scratch.d.var.vartype = variable->vartype; - /* select EEOP_*_FIRST opcode to force one-time checks */ switch (variable->varno) { case INNER_VAR: - scratch.opcode = EEOP_INNER_VAR_FIRST; + scratch.opcode = EEOP_INNER_VAR; break; case OUTER_VAR: - scratch.opcode = EEOP_OUTER_VAR_FIRST; + scratch.opcode = EEOP_OUTER_VAR; break; /* INDEX_VAR is handled by default case */ default: - scratch.opcode = EEOP_SCAN_VAR_FIRST; + scratch.opcode = EEOP_SCAN_VAR; break; } } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index 0c3f66803f1..fa4ab30e997 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -95,8 +95,17 @@ */ #if defined(EEO_USE_COMPUTED_GOTO) +/* struct for jump target -> opcode lookup table */ +typedef struct ExprEvalOpLookup +{ + const void *opcode; + ExprEvalOp op; +} ExprEvalOpLookup; + /* to make dispatch_table accessible outside ExecInterpExpr() */ static const void **dispatch_table = NULL; +/* jump target -> opcode lookup table */ +static ExprEvalOpLookup reverse_dispatch_table[EEOP_LAST]; #define EEO_SWITCH() #define EEO_CASE(name) CASE_##name: @@ -137,11 +146,8 @@ static void ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op, ExprContext *econtext, bool checkisnull); /* fast-path evaluation functions */ -static Datum ExecJustInnerVarFirst(ExprState *state, ExprContext *econtext, bool *isnull); static Datum ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull); -static Datum ExecJustOuterVarFirst(ExprState *state, ExprContext *econtext, bool *isnull); static Datum ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull); -static Datum ExecJustScanVarFirst(ExprState *state, ExprContext *econtext, bool *isnull); static Datum ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull); static Datum ExecJustConst(ExprState *state, ExprContext *econtext, bool *isnull); static Datum ExecJustAssignInnerVar(ExprState *state, ExprContext *econtext, bool *isnull); @@ -171,6 +177,14 @@ ExecReadyInterpretedExpr(ExprState *state) if (state->flags & EEO_FLAG_INTERPRETER_INITIALIZED) return; + /* + * First time through, check whether attribute matches Var. Might not be + * ok anymore, due to schema changes. We do that by setting up a callback + * that does checking on the first call, which then sets the evalfunc + * callback to the actual method of execution. + */ + state->evalfunc = ExecInterpExprStillValid; + /* DIRECT_THREADED should not already be set */ Assert((state->flags & EEO_FLAG_DIRECT_THREADED) == 0); @@ -192,53 +206,53 @@ ExecReadyInterpretedExpr(ExprState *state) ExprEvalOp step1 = state->steps[1].opcode; if (step0 == EEOP_INNER_FETCHSOME && - step1 == EEOP_INNER_VAR_FIRST) + step1 == EEOP_INNER_VAR) { - state->evalfunc = ExecJustInnerVarFirst; + state->evalfunc_private = (void *) ExecJustInnerVar; return; } else if (step0 == EEOP_OUTER_FETCHSOME && - step1 == EEOP_OUTER_VAR_FIRST) + step1 == EEOP_OUTER_VAR) { - state->evalfunc = ExecJustOuterVarFirst; + state->evalfunc_private = (void *) ExecJustOuterVar; return; } else if (step0 == EEOP_SCAN_FETCHSOME && - step1 == EEOP_SCAN_VAR_FIRST) + step1 == EEOP_SCAN_VAR) { - state->evalfunc = ExecJustScanVarFirst; + state->evalfunc_private = (void *) ExecJustScanVar; return; } else if (step0 == EEOP_INNER_FETCHSOME && step1 == EEOP_ASSIGN_INNER_VAR) { - state->evalfunc = ExecJustAssignInnerVar; + state->evalfunc_private = (void *) ExecJustAssignInnerVar; return; } else if (step0 == EEOP_OUTER_FETCHSOME && step1 == EEOP_ASSIGN_OUTER_VAR) { - state->evalfunc = ExecJustAssignOuterVar; + state->evalfunc_private = (void *) ExecJustAssignOuterVar; return; } else if (step0 == EEOP_SCAN_FETCHSOME && step1 == EEOP_ASSIGN_SCAN_VAR) { - state->evalfunc = ExecJustAssignScanVar; + state->evalfunc_private = (void *) ExecJustAssignScanVar; return; } else if (step0 == EEOP_CASE_TESTVAL && step1 == EEOP_FUNCEXPR_STRICT && state->steps[0].d.casetest.value) { - state->evalfunc = ExecJustApplyFuncToCase; + state->evalfunc_private = (void *) ExecJustApplyFuncToCase; return; } } else if (state->steps_len == 2 && state->steps[0].opcode == EEOP_CONST) { - state->evalfunc = ExecJustConst; + state->evalfunc_private = (void *) ExecJustConst; return; } @@ -262,7 +276,7 @@ ExecReadyInterpretedExpr(ExprState *state) } #endif /* EEO_USE_COMPUTED_GOTO */ - state->evalfunc = ExecInterpExpr; + state->evalfunc_private = (void *) ExecInterpExpr; } @@ -293,11 +307,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) &&CASE_EEOP_INNER_FETCHSOME, &&CASE_EEOP_OUTER_FETCHSOME, &&CASE_EEOP_SCAN_FETCHSOME, - &&CASE_EEOP_INNER_VAR_FIRST, &&CASE_EEOP_INNER_VAR, - &&CASE_EEOP_OUTER_VAR_FIRST, &&CASE_EEOP_OUTER_VAR, - &&CASE_EEOP_SCAN_VAR_FIRST, &&CASE_EEOP_SCAN_VAR, &&CASE_EEOP_INNER_SYSVAR, &&CASE_EEOP_OUTER_SYSVAR, @@ -420,22 +431,6 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) EEO_NEXT(); } - EEO_CASE(EEOP_INNER_VAR_FIRST) - { - int attnum = op->d.var.attnum; - - /* - * First time through, check whether attribute matches Var. Might - * not be ok anymore, due to schema changes. - */ - CheckVarSlotCompatibility(innerslot, attnum + 1, op->d.var.vartype); - - /* Skip that check on subsequent evaluations */ - op->opcode = EEO_OPCODE(EEOP_INNER_VAR); - - /* FALL THROUGH to EEOP_INNER_VAR */ - } - EEO_CASE(EEOP_INNER_VAR) { int attnum = op->d.var.attnum; @@ -453,18 +448,6 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) EEO_NEXT(); } - EEO_CASE(EEOP_OUTER_VAR_FIRST) - { - int attnum = op->d.var.attnum; - - /* See EEOP_INNER_VAR_FIRST comments */ - - CheckVarSlotCompatibility(outerslot, attnum + 1, op->d.var.vartype); - op->opcode = EEO_OPCODE(EEOP_OUTER_VAR); - - /* FALL THROUGH to EEOP_OUTER_VAR */ - } - EEO_CASE(EEOP_OUTER_VAR) { int attnum = op->d.var.attnum; @@ -478,18 +461,6 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) EEO_NEXT(); } - EEO_CASE(EEOP_SCAN_VAR_FIRST) - { - int attnum = op->d.var.attnum; - - /* See EEOP_INNER_VAR_FIRST comments */ - - CheckVarSlotCompatibility(scanslot, attnum + 1, op->d.var.vartype); - op->opcode = EEO_OPCODE(EEOP_SCAN_VAR); - - /* FALL THROUGH to EEOP_SCAN_VAR */ - } - EEO_CASE(EEOP_SCAN_VAR) { int attnum = op->d.var.attnum; @@ -1557,6 +1528,78 @@ out: } /* + * Expression evaluation callback that performs extra checks before executing + * the expression. Declared extern so other methods of execution can use it + * too. + */ +Datum +ExecInterpExprStillValid(ExprState *state, ExprContext *econtext, bool *isNull) +{ + /* + * First time through, check whether attribute matches Var. Might + * not be ok anymore, due to schema changes. + */ + CheckExprStillValid(state, econtext); + + /* skip the check during further executions */ + state->evalfunc = (ExprStateEvalFunc) state->evalfunc_private; + + /* and actually execute */ + return state->evalfunc(state, econtext, isNull); +} + +/* + * Check that an expression is still valid in the face of potential schema + * changes since the plan has been created. + */ +void +CheckExprStillValid(ExprState *state, ExprContext *econtext) +{ + int i = 0; + TupleTableSlot *innerslot; + TupleTableSlot *outerslot; + TupleTableSlot *scanslot; + + innerslot = econtext->ecxt_innertuple; + outerslot = econtext->ecxt_outertuple; + scanslot = econtext->ecxt_scantuple; + + for (i = 0; i < state->steps_len;i++) + { + ExprEvalStep *op = &state->steps[i]; + + switch (ExecEvalStepOp(state, op)) + { + case EEOP_INNER_VAR: + { + int attnum = op->d.var.attnum; + + CheckVarSlotCompatibility(innerslot, attnum + 1, op->d.var.vartype); + break; + } + + case EEOP_OUTER_VAR: + { + int attnum = op->d.var.attnum; + + CheckVarSlotCompatibility(outerslot, attnum + 1, op->d.var.vartype); + break; + } + + case EEOP_SCAN_VAR: + { + int attnum = op->d.var.attnum; + + CheckVarSlotCompatibility(scanslot, attnum + 1, op->d.var.vartype); + break; + } + default: + break; + } + } +} + +/* * Check whether a user attribute in a slot can be referenced by a Var * expression. This should succeed unless there have been schema changes * since the expression tree has been created. @@ -1668,20 +1711,14 @@ ShutdownTupleDescRef(Datum arg) * Fast-path functions, for very simple expressions */ -/* Simple reference to inner Var, first time through */ +/* Simple reference to inner Var */ static Datum -ExecJustInnerVarFirst(ExprState *state, ExprContext *econtext, bool *isnull) +ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull) { ExprEvalStep *op = &state->steps[1]; int attnum = op->d.var.attnum + 1; TupleTableSlot *slot = econtext->ecxt_innertuple; - /* See ExecInterpExpr()'s comments for EEOP_INNER_VAR_FIRST */ - - CheckVarSlotCompatibility(slot, attnum, op->d.var.vartype); - op->opcode = EEOP_INNER_VAR; /* just for cleanliness */ - state->evalfunc = ExecJustInnerVar; - /* * Since we use slot_getattr(), we don't need to implement the FETCHSOME * step explicitly, and we also needn't Assert that the attnum is in range @@ -1690,34 +1727,6 @@ ExecJustInnerVarFirst(ExprState *state, ExprContext *econtext, bool *isnull) return slot_getattr(slot, attnum, isnull); } -/* Simple reference to inner Var */ -static Datum -ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull) -{ - ExprEvalStep *op = &state->steps[1]; - int attnum = op->d.var.attnum + 1; - TupleTableSlot *slot = econtext->ecxt_innertuple; - - /* See comments in ExecJustInnerVarFirst */ - return slot_getattr(slot, attnum, isnull); -} - -/* Simple reference to outer Var, first time through */ -static Datum -ExecJustOuterVarFirst(ExprState *state, ExprContext *econtext, bool *isnull) -{ - ExprEvalStep *op = &state->steps[1]; - int attnum = op->d.var.attnum + 1; - TupleTableSlot *slot = econtext->ecxt_outertuple; - - CheckVarSlotCompatibility(slot, attnum, op->d.var.vartype); - op->opcode = EEOP_OUTER_VAR; /* just for cleanliness */ - state->evalfunc = ExecJustOuterVar; - - /* See comments in ExecJustInnerVarFirst */ - return slot_getattr(slot, attnum, isnull); -} - /* Simple reference to outer Var */ static Datum ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull) @@ -1726,23 +1735,7 @@ ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull) int attnum = op->d.var.attnum + 1; TupleTableSlot *slot = econtext->ecxt_outertuple; - /* See comments in ExecJustInnerVarFirst */ - return slot_getattr(slot, attnum, isnull); -} - -/* Simple reference to scan Var, first time through */ -static Datum -ExecJustScanVarFirst(ExprState *state, ExprContext *econtext, bool *isnull) -{ - ExprEvalStep *op = &state->steps[1]; - int attnum = op->d.var.attnum + 1; - TupleTableSlot *slot = econtext->ecxt_scantuple; - - CheckVarSlotCompatibility(slot, attnum, op->d.var.vartype); - op->opcode = EEOP_SCAN_VAR; /* just for cleanliness */ - state->evalfunc = ExecJustScanVar; - - /* See comments in ExecJustInnerVarFirst */ + /* See comments in ExecJustInnerVar */ return slot_getattr(slot, attnum, isnull); } @@ -1754,7 +1747,7 @@ ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull) int attnum = op->d.var.attnum + 1; TupleTableSlot *slot = econtext->ecxt_scantuple; - /* See comments in ExecJustInnerVarFirst */ + /* See comments in ExecJustInnerVar */ return slot_getattr(slot, attnum, isnull); } @@ -1860,6 +1853,24 @@ ExecJustApplyFuncToCase(ExprState *state, ExprContext *econtext, bool *isnull) return d; } +#if defined(EEO_USE_COMPUTED_GOTO) +/* + * Comparator used when building address->opcode lookup table for + * ExecEvalStepOp() in the threaded dispatch case. + */ +static int +dispatch_compare_ptr(const void* a, const void *b) +{ + const ExprEvalOpLookup *la = (const ExprEvalOpLookup *) a; + const ExprEvalOpLookup *lb = (const ExprEvalOpLookup *) b; + + if (la->opcode < lb->opcode) + return -1; + else if (la->opcode > lb->opcode) + return 1; + return 0; +} +#endif /* * Do one-time initialization of interpretation machinery. @@ -1870,8 +1881,25 @@ ExecInitInterpreter(void) #if defined(EEO_USE_COMPUTED_GOTO) /* Set up externally-visible pointer to dispatch table */ if (dispatch_table == NULL) + { + int i; + dispatch_table = (const void **) DatumGetPointer(ExecInterpExpr(NULL, NULL, NULL)); + + /* build reverse lookup table */ + for (i = 0; i < EEOP_LAST; i++) + { + reverse_dispatch_table[i].opcode = dispatch_table[i]; + reverse_dispatch_table[i].op = (ExprEvalOp) i; + } + + /* make it bsearch()able */ + qsort(reverse_dispatch_table, + EEOP_LAST /* nmembers */, + sizeof(ExprEvalOpLookup), + dispatch_compare_ptr); + } #endif } @@ -1880,10 +1908,6 @@ ExecInitInterpreter(void) * * When direct-threading is in use, ExprState->opcode isn't easily * decipherable. This function returns the appropriate enum member. - * - * This currently is only supposed to be used in paths that aren't critical - * performance-wise. If that changes, we could add an inverse dispatch_table - * that's sorted on the address, so a binary search can be performed. */ ExprEvalOp ExecEvalStepOp(ExprState *state, ExprEvalStep *op) @@ -1891,16 +1915,17 @@ ExecEvalStepOp(ExprState *state, ExprEvalStep *op) #if defined(EEO_USE_COMPUTED_GOTO) if (state->flags & EEO_FLAG_DIRECT_THREADED) { - int i; - - for (i = 0; i < EEOP_LAST; i++) - { - if ((void *) op->opcode == dispatch_table[i]) - { - return (ExprEvalOp) i; - } - } - elog(ERROR, "unknown opcode"); + ExprEvalOpLookup key; + ExprEvalOpLookup *res; + + key.opcode = (void *) op->opcode; + res = bsearch(&key, + reverse_dispatch_table, + EEOP_LAST /* nmembers */, + sizeof(ExprEvalOpLookup), + dispatch_compare_ptr); + Assert(res); /* unknown ops shouldn't get looked up */ + return res->op; } #endif return (ExprEvalOp) op->opcode; diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h index 080252fad60..511205b5acc 100644 --- a/src/include/executor/execExpr.h +++ b/src/include/executor/execExpr.h @@ -51,12 +51,8 @@ typedef enum ExprEvalOp EEOP_SCAN_FETCHSOME, /* compute non-system Var value */ - /* "FIRST" variants are used only the first time through */ - EEOP_INNER_VAR_FIRST, EEOP_INNER_VAR, - EEOP_OUTER_VAR_FIRST, EEOP_OUTER_VAR, - EEOP_SCAN_VAR_FIRST, EEOP_SCAN_VAR, /* compute system Var value */ @@ -67,8 +63,11 @@ typedef enum ExprEvalOp /* compute wholerow Var */ EEOP_WHOLEROW, - /* compute non-system Var value, assign it into ExprState's resultslot */ - /* (these are not used if _FIRST checks would be needed) */ + /* + * Compute non-system Var value, assign it into ExprState's + * resultslot. These are not used if a CheckVarSlotCompatibility() check + * would be needed. + */ EEOP_ASSIGN_INNER_VAR, EEOP_ASSIGN_OUTER_VAR, EEOP_ASSIGN_SCAN_VAR, @@ -621,6 +620,9 @@ extern void ExprEvalPushStep(ExprState *es, const ExprEvalStep *s); extern void ExecReadyInterpretedExpr(ExprState *state); extern ExprEvalOp ExecEvalStepOp(ExprState *state, ExprEvalStep *op); +extern Datum ExecInterpExprStillValid(ExprState *state, ExprContext *econtext, bool *isNull); +extern void CheckExprStillValid(ExprState *state, ExprContext *econtext); + /* * Non fast-path execution functions. These are externs instead of statics in * execExprInterp.c, because that allows them to be used by other methods of diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index c9a5279dc58..94351eafadd 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -87,6 +87,9 @@ typedef struct ExprState /* original expression tree, for debugging only */ Expr *expr; + /* private state for an evalfunc */ + void *evalfunc_private; + /* * XXX: following fields only needed during "compilation" (ExecInitExpr); * could be thrown away afterwards. |