*/
#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:
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);
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);
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;
}
}
#endif /* EEO_USE_COMPUTED_GOTO */
- state->evalfunc = ExecInterpExpr;
+ state->evalfunc_private = (void *) ExecInterpExpr;
}
&&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,
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;
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;
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;
return state->resvalue;
}
+/*
+ * 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
* 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
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)
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);
}
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);
}
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.
#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
}
*
* 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)
#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;