#include "postgres.h"#include "access/nbtree.h"#include "executor/execdebug.h"#include "executor/nodeMergejoin.h"#include "utils/lsyscache.h"#include "utils/memutils.h"
Go to the source code of this file.
| #define EXEC_MJ_ENDINNER 11 |
Definition at line 115 of file nodeMergejoin.c.
Referenced by ExecMergeJoin().
| #define EXEC_MJ_ENDOUTER 10 |
Definition at line 114 of file nodeMergejoin.c.
Referenced by ExecMergeJoin().
| #define EXEC_MJ_INITIALIZE_INNER 2 |
Definition at line 106 of file nodeMergejoin.c.
Referenced by ExecMergeJoin().
| #define EXEC_MJ_INITIALIZE_OUTER 1 |
Definition at line 105 of file nodeMergejoin.c.
Referenced by ExecMergeJoin().
| #define EXEC_MJ_JOINTUPLES 3 |
Definition at line 107 of file nodeMergejoin.c.
Referenced by ExecMergeJoin().
| #define EXEC_MJ_NEXTINNER 6 |
Definition at line 110 of file nodeMergejoin.c.
Referenced by ExecMergeJoin().
| #define EXEC_MJ_NEXTOUTER 4 |
Definition at line 108 of file nodeMergejoin.c.
Referenced by ExecMergeJoin().
| #define EXEC_MJ_SKIP_TEST 7 |
Definition at line 111 of file nodeMergejoin.c.
Referenced by ExecMergeJoin().
| #define EXEC_MJ_SKIPINNER_ADVANCE 9 |
Definition at line 113 of file nodeMergejoin.c.
Referenced by ExecMergeJoin().
| #define EXEC_MJ_SKIPOUTER_ADVANCE 8 |
Definition at line 112 of file nodeMergejoin.c.
Referenced by ExecMergeJoin().
| #define EXEC_MJ_TESTOUTER 5 |
Definition at line 109 of file nodeMergejoin.c.
Referenced by ExecMergeJoin().
| #define MarkInnerTuple | ( | innerTupleSlot, | ||
| mergestate | ||||
| ) | ExecCopySlot((mergestate)->mj_MarkedTupleSlot, (innerTupleSlot)) |
Definition at line 151 of file nodeMergejoin.c.
Referenced by ExecMergeJoin().
| typedef struct MergeJoinClauseData MergeJoinClauseData |
| enum MJEvalResult |
Definition at line 143 of file nodeMergejoin.c.
{
MJEVAL_MATCHABLE, /* normal, potentially matchable tuple */
MJEVAL_NONMATCHABLE, /* tuple cannot join because it has a null */
MJEVAL_ENDOFJOIN /* end of input (physical or effective) */
} MJEvalResult;
Definition at line 531 of file nodeMergejoin.c.
References Const::constisnull, Const::constvalue, DatumGetBool, IsA, and lfirst.
Referenced by ExecInitMergeJoin().
{
ListCell *lc;
foreach(lc, qual)
{
Const *con = (Const *) lfirst(lc);
if (!con || !IsA(con, Const))
return false;
if (con->constisnull || !DatumGetBool(con->constvalue))
*is_const_false = true;
}
return true;
}
| void ExecEndMergeJoin | ( | MergeJoinState * | node | ) |
Definition at line 1647 of file nodeMergejoin.c.
References ExecClearTuple(), ExecEndNode(), ExecFreeExprContext(), innerPlanState, MergeJoinState::js, MJ1_printf, MergeJoinState::mj_MarkedTupleSlot, outerPlanState, JoinState::ps, and PlanState::ps_ResultTupleSlot.
Referenced by ExecEndNode().
{
MJ1_printf("ExecEndMergeJoin: %s\n",
"ending node processing");
/*
* Free the exprcontext
*/
ExecFreeExprContext(&node->js.ps);
/*
* clean out the tuple table
*/
ExecClearTuple(node->js.ps.ps_ResultTupleSlot);
ExecClearTuple(node->mj_MarkedTupleSlot);
/*
* shut down the subplans
*/
ExecEndNode(innerPlanState(node));
ExecEndNode(outerPlanState(node));
MJ1_printf("ExecEndMergeJoin: %s\n",
"node processing ended");
}
| MergeJoinState* ExecInitMergeJoin | ( | MergeJoin * | node, | |
| EState * | estate, | |||
| int | eflags | |||
| ) |
Definition at line 1464 of file nodeMergejoin.c.
References Assert, check_constant_qual(), CreateExprContext(), elog, ereport, errcode(), errmsg(), ERROR, EXEC_FLAG_BACKWARD, EXEC_FLAG_MARK, EXEC_FLAG_REWIND, ExecAssignExprContext(), ExecAssignProjectionInfo(), ExecAssignResultTypeFromTL(), ExecGetResultType(), ExecInitExpr(), ExecInitExtraTupleSlot(), ExecInitNode(), ExecInitNullTupleSlot(), ExecInitResultTupleSlot(), ExecSetSlotDescriptor(), innerPlan, innerPlanState, IsA, MergeJoin::join, JOIN_ANTI, JOIN_FULL, JOIN_INNER, JOIN_LEFT, JOIN_RIGHT, JOIN_SEMI, Join::joinqual, JoinState::joinqual, Join::jointype, JoinState::jointype, MergeJoinState::js, list_length(), makeNode, MergeJoin::mergeclauses, MergeJoin::mergeCollations, MergeJoin::mergeFamilies, MergeJoin::mergeNullsFirst, MergeJoin::mergeStrategies, MJ1_printf, MergeJoinState::mj_Clauses, MergeJoinState::mj_ConstFalseJoin, MergeJoinState::mj_ExtraMarks, MergeJoinState::mj_FillInner, MergeJoinState::mj_FillOuter, MergeJoinState::mj_InnerEContext, MergeJoinState::mj_MarkedTupleSlot, MergeJoinState::mj_NullInnerTupleSlot, MergeJoinState::mj_NullOuterTupleSlot, MergeJoinState::mj_NumClauses, MergeJoinState::mj_OuterEContext, MJExamineQuals(), NULL, outerPlan, outerPlanState, Join::plan, PlanState::plan, JoinState::ps, PlanState::ps_TupFromTlist, Plan::qual, PlanState::qual, PlanState::state, Plan::targetlist, and PlanState::targetlist.
Referenced by ExecInitNode().
{
MergeJoinState *mergestate;
/* check for unsupported flags */
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
MJ1_printf("ExecInitMergeJoin: %s\n",
"initializing node");
/*
* create state structure
*/
mergestate = makeNode(MergeJoinState);
mergestate->js.ps.plan = (Plan *) node;
mergestate->js.ps.state = estate;
/*
* Miscellaneous initialization
*
* create expression context for node
*/
ExecAssignExprContext(estate, &mergestate->js.ps);
/*
* we need two additional econtexts in which we can compute the join
* expressions from the left and right input tuples. The node's regular
* econtext won't do because it gets reset too often.
*/
mergestate->mj_OuterEContext = CreateExprContext(estate);
mergestate->mj_InnerEContext = CreateExprContext(estate);
/*
* initialize child expressions
*/
mergestate->js.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->join.plan.targetlist,
(PlanState *) mergestate);
mergestate->js.ps.qual = (List *)
ExecInitExpr((Expr *) node->join.plan.qual,
(PlanState *) mergestate);
mergestate->js.jointype = node->join.jointype;
mergestate->js.joinqual = (List *)
ExecInitExpr((Expr *) node->join.joinqual,
(PlanState *) mergestate);
mergestate->mj_ConstFalseJoin = false;
/* mergeclauses are handled below */
/*
* initialize child nodes
*
* inner child must support MARK/RESTORE.
*/
outerPlanState(mergestate) = ExecInitNode(outerPlan(node), estate, eflags);
innerPlanState(mergestate) = ExecInitNode(innerPlan(node), estate,
eflags | EXEC_FLAG_MARK);
/*
* For certain types of inner child nodes, it is advantageous to issue
* MARK every time we advance past an inner tuple we will never return to.
* For other types, MARK on a tuple we cannot return to is a waste of
* cycles. Detect which case applies and set mj_ExtraMarks if we want to
* issue "unnecessary" MARK calls.
*
* Currently, only Material wants the extra MARKs, and it will be helpful
* only if eflags doesn't specify REWIND.
*/
if (IsA(innerPlan(node), Material) &&
(eflags & EXEC_FLAG_REWIND) == 0)
mergestate->mj_ExtraMarks = true;
else
mergestate->mj_ExtraMarks = false;
/*
* tuple table initialization
*/
ExecInitResultTupleSlot(estate, &mergestate->js.ps);
mergestate->mj_MarkedTupleSlot = ExecInitExtraTupleSlot(estate);
ExecSetSlotDescriptor(mergestate->mj_MarkedTupleSlot,
ExecGetResultType(innerPlanState(mergestate)));
switch (node->join.jointype)
{
case JOIN_INNER:
case JOIN_SEMI:
mergestate->mj_FillOuter = false;
mergestate->mj_FillInner = false;
break;
case JOIN_LEFT:
case JOIN_ANTI:
mergestate->mj_FillOuter = true;
mergestate->mj_FillInner = false;
mergestate->mj_NullInnerTupleSlot =
ExecInitNullTupleSlot(estate,
ExecGetResultType(innerPlanState(mergestate)));
break;
case JOIN_RIGHT:
mergestate->mj_FillOuter = false;
mergestate->mj_FillInner = true;
mergestate->mj_NullOuterTupleSlot =
ExecInitNullTupleSlot(estate,
ExecGetResultType(outerPlanState(mergestate)));
/*
* Can't handle right or full join with non-constant extra
* joinclauses. This should have been caught by planner.
*/
if (!check_constant_qual(node->join.joinqual,
&mergestate->mj_ConstFalseJoin))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("RIGHT JOIN is only supported with merge-joinable join conditions")));
break;
case JOIN_FULL:
mergestate->mj_FillOuter = true;
mergestate->mj_FillInner = true;
mergestate->mj_NullOuterTupleSlot =
ExecInitNullTupleSlot(estate,
ExecGetResultType(outerPlanState(mergestate)));
mergestate->mj_NullInnerTupleSlot =
ExecInitNullTupleSlot(estate,
ExecGetResultType(innerPlanState(mergestate)));
/*
* Can't handle right or full join with non-constant extra
* joinclauses. This should have been caught by planner.
*/
if (!check_constant_qual(node->join.joinqual,
&mergestate->mj_ConstFalseJoin))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("FULL JOIN is only supported with merge-joinable join conditions")));
break;
default:
elog(ERROR, "unrecognized join type: %d",
(int) node->join.jointype);
}
/*
* initialize tuple type and projection info
*/
ExecAssignResultTypeFromTL(&mergestate->js.ps);
ExecAssignProjectionInfo(&mergestate->js.ps, NULL);
/*
* preprocess the merge clauses
*/
mergestate->mj_NumClauses = list_length(node->mergeclauses);
mergestate->mj_Clauses = MJExamineQuals(node->mergeclauses,
node->mergeFamilies,
node->mergeCollations,
node->mergeStrategies,
node->mergeNullsFirst,
(PlanState *) mergestate);
/*
* initialize join state
*/
mergestate->mj_JoinState = EXEC_MJ_INITIALIZE_OUTER;
mergestate->js.ps.ps_TupFromTlist = false;
mergestate->mj_MatchedOuter = false;
mergestate->mj_MatchedInner = false;
mergestate->mj_OuterTupleSlot = NULL;
mergestate->mj_InnerTupleSlot = NULL;
/*
* initialization successful
*/
MJ1_printf("ExecInitMergeJoin: %s\n",
"node initialized");
return mergestate;
}
| TupleTableSlot* ExecMergeJoin | ( | MergeJoinState * | node | ) |
Definition at line 611 of file nodeMergejoin.c.
References Assert, ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, elog, ERROR, EXEC_MJ_ENDINNER, EXEC_MJ_ENDOUTER, EXEC_MJ_INITIALIZE_INNER, EXEC_MJ_INITIALIZE_OUTER, EXEC_MJ_JOINTUPLES, EXEC_MJ_NEXTINNER, EXEC_MJ_NEXTOUTER, EXEC_MJ_SKIP_TEST, EXEC_MJ_SKIPINNER_ADVANCE, EXEC_MJ_SKIPOUTER_ADVANCE, EXEC_MJ_TESTOUTER, ExecMarkPos(), ExecProcNode(), ExecProject(), ExecQual(), ExecRestrPos(), ExprEndResult, ExprMultipleResult, innerPlanState, InstrCountFiltered1, InstrCountFiltered2, JOIN_ANTI, JOIN_SEMI, JoinState::joinqual, JoinState::jointype, MergeJoinState::js, MarkInnerTuple, MJ_DEBUG_COMPARE, MJ_DEBUG_PROC_NODE, MJ_DEBUG_QUAL, MJ_dump, MergeJoinState::mj_ExtraMarks, MergeJoinState::mj_FillInner, MergeJoinState::mj_FillOuter, MergeJoinState::mj_InnerTupleSlot, MergeJoinState::mj_JoinState, MergeJoinState::mj_MarkedTupleSlot, MergeJoinState::mj_MatchedInner, MergeJoinState::mj_MatchedOuter, MergeJoinState::mj_OuterTupleSlot, MJ_printf, MJCompare(), MJEVAL_ENDOFJOIN, MJEVAL_MATCHABLE, MJEVAL_NONMATCHABLE, MJEvalInnerValues(), MJEvalOuterValues(), MJFillInner(), MJFillOuter(), NIL, outerPlanState, JoinState::ps, PlanState::ps_ExprContext, PlanState::ps_ProjInfo, PlanState::ps_TupFromTlist, PlanState::qual, ResetExprContext, and TupIsNull.
Referenced by ExecProcNode().
{
List *joinqual;
List *otherqual;
bool qualResult;
int compareResult;
PlanState *innerPlan;
TupleTableSlot *innerTupleSlot;
PlanState *outerPlan;
TupleTableSlot *outerTupleSlot;
ExprContext *econtext;
bool doFillOuter;
bool doFillInner;
/*
* get information from node
*/
innerPlan = innerPlanState(node);
outerPlan = outerPlanState(node);
econtext = node->js.ps.ps_ExprContext;
joinqual = node->js.joinqual;
otherqual = node->js.ps.qual;
doFillOuter = node->mj_FillOuter;
doFillInner = node->mj_FillInner;
/*
* Check to see if we're still projecting out tuples from a previous join
* tuple (because there is a function-returning-set in the projection
* expressions). If so, try to project another one.
*/
if (node->js.ps.ps_TupFromTlist)
{
TupleTableSlot *result;
ExprDoneCond isDone;
result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
if (isDone == ExprMultipleResult)
return result;
/* Done with that source tuple... */
node->js.ps.ps_TupFromTlist = false;
}
/*
* Reset per-tuple memory context to free any expression evaluation
* storage allocated in the previous tuple cycle. Note this can't happen
* until we're done projecting out tuples from a join tuple.
*/
ResetExprContext(econtext);
/*
* ok, everything is setup.. let's go to work
*/
for (;;)
{
MJ_dump(node);
/*
* get the current state of the join and do things accordingly.
*/
switch (node->mj_JoinState)
{
/*
* EXEC_MJ_INITIALIZE_OUTER means that this is the first time
* ExecMergeJoin() has been called and so we have to fetch the
* first matchable tuple for both outer and inner subplans. We
* do the outer side in INITIALIZE_OUTER state, then advance
* to INITIALIZE_INNER state for the inner subplan.
*/
case EXEC_MJ_INITIALIZE_OUTER:
MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE_OUTER\n");
outerTupleSlot = ExecProcNode(outerPlan);
node->mj_OuterTupleSlot = outerTupleSlot;
/* Compute join values and check for unmatchability */
switch (MJEvalOuterValues(node))
{
case MJEVAL_MATCHABLE:
/* OK to go get the first inner tuple */
node->mj_JoinState = EXEC_MJ_INITIALIZE_INNER;
break;
case MJEVAL_NONMATCHABLE:
/* Stay in same state to fetch next outer tuple */
if (doFillOuter)
{
/*
* Generate a fake join tuple with nulls for the
* inner tuple, and return it if it passes the
* non-join quals.
*/
TupleTableSlot *result;
result = MJFillOuter(node);
if (result)
return result;
}
break;
case MJEVAL_ENDOFJOIN:
/* No more outer tuples */
MJ_printf("ExecMergeJoin: nothing in outer subplan\n");
if (doFillInner)
{
/*
* Need to emit right-join tuples for remaining
* inner tuples. We set MatchedInner = true to
* force the ENDOUTER state to advance inner.
*/
node->mj_JoinState = EXEC_MJ_ENDOUTER;
node->mj_MatchedInner = true;
break;
}
/* Otherwise we're done. */
return NULL;
}
break;
case EXEC_MJ_INITIALIZE_INNER:
MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE_INNER\n");
innerTupleSlot = ExecProcNode(innerPlan);
node->mj_InnerTupleSlot = innerTupleSlot;
/* Compute join values and check for unmatchability */
switch (MJEvalInnerValues(node, innerTupleSlot))
{
case MJEVAL_MATCHABLE:
/*
* OK, we have the initial tuples. Begin by skipping
* non-matching tuples.
*/
node->mj_JoinState = EXEC_MJ_SKIP_TEST;
break;
case MJEVAL_NONMATCHABLE:
/* Mark before advancing, if wanted */
if (node->mj_ExtraMarks)
ExecMarkPos(innerPlan);
/* Stay in same state to fetch next inner tuple */
if (doFillInner)
{
/*
* Generate a fake join tuple with nulls for the
* outer tuple, and return it if it passes the
* non-join quals.
*/
TupleTableSlot *result;
result = MJFillInner(node);
if (result)
return result;
}
break;
case MJEVAL_ENDOFJOIN:
/* No more inner tuples */
MJ_printf("ExecMergeJoin: nothing in inner subplan\n");
if (doFillOuter)
{
/*
* Need to emit left-join tuples for all outer
* tuples, including the one we just fetched. We
* set MatchedOuter = false to force the ENDINNER
* state to emit first tuple before advancing
* outer.
*/
node->mj_JoinState = EXEC_MJ_ENDINNER;
node->mj_MatchedOuter = false;
break;
}
/* Otherwise we're done. */
return NULL;
}
break;
/*
* EXEC_MJ_JOINTUPLES means we have two tuples which satisfied
* the merge clause so we join them and then proceed to get
* the next inner tuple (EXEC_MJ_NEXTINNER).
*/
case EXEC_MJ_JOINTUPLES:
MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");
/*
* Set the next state machine state. The right things will
* happen whether we return this join tuple or just fall
* through to continue the state machine execution.
*/
node->mj_JoinState = EXEC_MJ_NEXTINNER;
/*
* Check the extra qual conditions to see if we actually want
* to return this join tuple. If not, can proceed with merge.
* We must distinguish the additional joinquals (which must
* pass to consider the tuples "matched" for outer-join logic)
* from the otherquals (which must pass before we actually
* return the tuple).
*
* We don't bother with a ResetExprContext here, on the
* assumption that we just did one while checking the merge
* qual. One per tuple should be sufficient. We do have to
* set up the econtext links to the tuples for ExecQual to
* use.
*/
outerTupleSlot = node->mj_OuterTupleSlot;
econtext->ecxt_outertuple = outerTupleSlot;
innerTupleSlot = node->mj_InnerTupleSlot;
econtext->ecxt_innertuple = innerTupleSlot;
qualResult = (joinqual == NIL ||
ExecQual(joinqual, econtext, false));
MJ_DEBUG_QUAL(joinqual, qualResult);
if (qualResult)
{
node->mj_MatchedOuter = true;
node->mj_MatchedInner = true;
/* In an antijoin, we never return a matched tuple */
if (node->js.jointype == JOIN_ANTI)
{
node->mj_JoinState = EXEC_MJ_NEXTOUTER;
break;
}
/*
* In a semijoin, we'll consider returning the first
* match, but after that we're done with this outer tuple.
*/
if (node->js.jointype == JOIN_SEMI)
node->mj_JoinState = EXEC_MJ_NEXTOUTER;
qualResult = (otherqual == NIL ||
ExecQual(otherqual, econtext, false));
MJ_DEBUG_QUAL(otherqual, qualResult);
if (qualResult)
{
/*
* qualification succeeded. now form the desired
* projection tuple and return the slot containing it.
*/
TupleTableSlot *result;
ExprDoneCond isDone;
MJ_printf("ExecMergeJoin: returning tuple\n");
result = ExecProject(node->js.ps.ps_ProjInfo,
&isDone);
if (isDone != ExprEndResult)
{
node->js.ps.ps_TupFromTlist =
(isDone == ExprMultipleResult);
return result;
}
}
else
InstrCountFiltered2(node, 1);
}
else
InstrCountFiltered1(node, 1);
break;
/*
* EXEC_MJ_NEXTINNER means advance the inner scan to the next
* tuple. If the tuple is not nil, we then proceed to test it
* against the join qualification.
*
* Before advancing, we check to see if we must emit an
* outer-join fill tuple for this inner tuple.
*/
case EXEC_MJ_NEXTINNER:
MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\n");
if (doFillInner && !node->mj_MatchedInner)
{
/*
* Generate a fake join tuple with nulls for the outer
* tuple, and return it if it passes the non-join quals.
*/
TupleTableSlot *result;
node->mj_MatchedInner = true; /* do it only once */
result = MJFillInner(node);
if (result)
return result;
}
/*
* now we get the next inner tuple, if any. If there's none,
* advance to next outer tuple (which may be able to join to
* previously marked tuples).
*
* NB: must NOT do "extraMarks" here, since we may need to
* return to previously marked tuples.
*/
innerTupleSlot = ExecProcNode(innerPlan);
node->mj_InnerTupleSlot = innerTupleSlot;
MJ_DEBUG_PROC_NODE(innerTupleSlot);
node->mj_MatchedInner = false;
/* Compute join values and check for unmatchability */
switch (MJEvalInnerValues(node, innerTupleSlot))
{
case MJEVAL_MATCHABLE:
/*
* Test the new inner tuple to see if it matches
* outer.
*
* If they do match, then we join them and move on to
* the next inner tuple (EXEC_MJ_JOINTUPLES).
*
* If they do not match then advance to next outer
* tuple.
*/
compareResult = MJCompare(node);
MJ_DEBUG_COMPARE(compareResult);
if (compareResult == 0)
node->mj_JoinState = EXEC_MJ_JOINTUPLES;
else
{
Assert(compareResult < 0);
node->mj_JoinState = EXEC_MJ_NEXTOUTER;
}
break;
case MJEVAL_NONMATCHABLE:
/*
* It contains a NULL and hence can't match any outer
* tuple, so we can skip the comparison and assume the
* new tuple is greater than current outer.
*/
node->mj_JoinState = EXEC_MJ_NEXTOUTER;
break;
case MJEVAL_ENDOFJOIN:
/*
* No more inner tuples. However, this might be only
* effective and not physical end of inner plan, so
* force mj_InnerTupleSlot to null to make sure we
* don't fetch more inner tuples. (We need this hack
* because we are not transiting to a state where the
* inner plan is assumed to be exhausted.)
*/
node->mj_InnerTupleSlot = NULL;
node->mj_JoinState = EXEC_MJ_NEXTOUTER;
break;
}
break;
/*-------------------------------------------
* EXEC_MJ_NEXTOUTER means
*
* outer inner
* outer tuple - 5 5 - marked tuple
* 5 5
* 6 6 - inner tuple
* 7 7
*
* we know we just bumped into the
* first inner tuple > current outer tuple (or possibly
* the end of the inner stream)
* so get a new outer tuple and then
* proceed to test it against the marked tuple
* (EXEC_MJ_TESTOUTER)
*
* Before advancing, we check to see if we must emit an
* outer-join fill tuple for this outer tuple.
*------------------------------------------------
*/
case EXEC_MJ_NEXTOUTER:
MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n");
if (doFillOuter && !node->mj_MatchedOuter)
{
/*
* Generate a fake join tuple with nulls for the inner
* tuple, and return it if it passes the non-join quals.
*/
TupleTableSlot *result;
node->mj_MatchedOuter = true; /* do it only once */
result = MJFillOuter(node);
if (result)
return result;
}
/*
* now we get the next outer tuple, if any
*/
outerTupleSlot = ExecProcNode(outerPlan);
node->mj_OuterTupleSlot = outerTupleSlot;
MJ_DEBUG_PROC_NODE(outerTupleSlot);
node->mj_MatchedOuter = false;
/* Compute join values and check for unmatchability */
switch (MJEvalOuterValues(node))
{
case MJEVAL_MATCHABLE:
/* Go test the new tuple against the marked tuple */
node->mj_JoinState = EXEC_MJ_TESTOUTER;
break;
case MJEVAL_NONMATCHABLE:
/* Can't match, so fetch next outer tuple */
node->mj_JoinState = EXEC_MJ_NEXTOUTER;
break;
case MJEVAL_ENDOFJOIN:
/* No more outer tuples */
MJ_printf("ExecMergeJoin: end of outer subplan\n");
innerTupleSlot = node->mj_InnerTupleSlot;
if (doFillInner && !TupIsNull(innerTupleSlot))
{
/*
* Need to emit right-join tuples for remaining
* inner tuples.
*/
node->mj_JoinState = EXEC_MJ_ENDOUTER;
break;
}
/* Otherwise we're done. */
return NULL;
}
break;
/*--------------------------------------------------------
* EXEC_MJ_TESTOUTER If the new outer tuple and the marked
* tuple satisfy the merge clause then we know we have
* duplicates in the outer scan so we have to restore the
* inner scan to the marked tuple and proceed to join the
* new outer tuple with the inner tuples.
*
* This is the case when
* outer inner
* 4 5 - marked tuple
* outer tuple - 5 5
* new outer tuple - 5 5
* 6 8 - inner tuple
* 7 12
*
* new outer tuple == marked tuple
*
* If the outer tuple fails the test, then we are done
* with the marked tuples, and we have to look for a
* match to the current inner tuple. So we will
* proceed to skip outer tuples until outer >= inner
* (EXEC_MJ_SKIP_TEST).
*
* This is the case when
*
* outer inner
* 5 5 - marked tuple
* outer tuple - 5 5
* new outer tuple - 6 8 - inner tuple
* 7 12
*
* new outer tuple > marked tuple
*
*---------------------------------------------------------
*/
case EXEC_MJ_TESTOUTER:
MJ_printf("ExecMergeJoin: EXEC_MJ_TESTOUTER\n");
/*
* Here we must compare the outer tuple with the marked inner
* tuple. (We can ignore the result of MJEvalInnerValues,
* since the marked inner tuple is certainly matchable.)
*/
innerTupleSlot = node->mj_MarkedTupleSlot;
(void) MJEvalInnerValues(node, innerTupleSlot);
compareResult = MJCompare(node);
MJ_DEBUG_COMPARE(compareResult);
if (compareResult == 0)
{
/*
* the merge clause matched so now we restore the inner
* scan position to the first mark, and go join that tuple
* (and any following ones) to the new outer.
*
* NOTE: we do not need to worry about the MatchedInner
* state for the rescanned inner tuples. We know all of
* them will match this new outer tuple and therefore
* won't be emitted as fill tuples. This works *only*
* because we require the extra joinquals to be constant
* when doing a right or full join --- otherwise some of
* the rescanned tuples might fail the extra joinquals.
* This obviously won't happen for a constant-true extra
* joinqual, while the constant-false case is handled by
* forcing the merge clause to never match, so we never
* get here.
*/
ExecRestrPos(innerPlan);
/*
* ExecRestrPos probably should give us back a new Slot,
* but since it doesn't, use the marked slot. (The
* previously returned mj_InnerTupleSlot cannot be assumed
* to hold the required tuple.)
*/
node->mj_InnerTupleSlot = innerTupleSlot;
/* we need not do MJEvalInnerValues again */
node->mj_JoinState = EXEC_MJ_JOINTUPLES;
}
else
{
/* ----------------
* if the new outer tuple didn't match the marked inner
* tuple then we have a case like:
*
* outer inner
* 4 4 - marked tuple
* new outer - 5 4
* 6 5 - inner tuple
* 7
*
* which means that all subsequent outer tuples will be
* larger than our marked inner tuples. So we need not
* revisit any of the marked tuples but can proceed to
* look for a match to the current inner. If there's
* no more inners, no more matches are possible.
* ----------------
*/
Assert(compareResult > 0);
innerTupleSlot = node->mj_InnerTupleSlot;
/* reload comparison data for current inner */
switch (MJEvalInnerValues(node, innerTupleSlot))
{
case MJEVAL_MATCHABLE:
/* proceed to compare it to the current outer */
node->mj_JoinState = EXEC_MJ_SKIP_TEST;
break;
case MJEVAL_NONMATCHABLE:
/*
* current inner can't possibly match any outer;
* better to advance the inner scan than the
* outer.
*/
node->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE;
break;
case MJEVAL_ENDOFJOIN:
/* No more inner tuples */
if (doFillOuter)
{
/*
* Need to emit left-join tuples for remaining
* outer tuples.
*/
node->mj_JoinState = EXEC_MJ_ENDINNER;
break;
}
/* Otherwise we're done. */
return NULL;
}
}
break;
/*----------------------------------------------------------
* EXEC_MJ_SKIP means compare tuples and if they do not
* match, skip whichever is lesser.
*
* For example:
*
* outer inner
* 5 5
* 5 5
* outer tuple - 6 8 - inner tuple
* 7 12
* 8 14
*
* we have to advance the outer scan
* until we find the outer 8.
*
* On the other hand:
*
* outer inner
* 5 5
* 5 5
* outer tuple - 12 8 - inner tuple
* 14 10
* 17 12
*
* we have to advance the inner scan
* until we find the inner 12.
*----------------------------------------------------------
*/
case EXEC_MJ_SKIP_TEST:
MJ_printf("ExecMergeJoin: EXEC_MJ_SKIP_TEST\n");
/*
* before we advance, make sure the current tuples do not
* satisfy the mergeclauses. If they do, then we update the
* marked tuple position and go join them.
*/
compareResult = MJCompare(node);
MJ_DEBUG_COMPARE(compareResult);
if (compareResult == 0)
{
ExecMarkPos(innerPlan);
MarkInnerTuple(node->mj_InnerTupleSlot, node);
node->mj_JoinState = EXEC_MJ_JOINTUPLES;
}
else if (compareResult < 0)
node->mj_JoinState = EXEC_MJ_SKIPOUTER_ADVANCE;
else
/* compareResult > 0 */
node->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE;
break;
/*
* SKIPOUTER_ADVANCE: advance over an outer tuple that is
* known not to join to any inner tuple.
*
* Before advancing, we check to see if we must emit an
* outer-join fill tuple for this outer tuple.
*/
case EXEC_MJ_SKIPOUTER_ADVANCE:
MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_ADVANCE\n");
if (doFillOuter && !node->mj_MatchedOuter)
{
/*
* Generate a fake join tuple with nulls for the inner
* tuple, and return it if it passes the non-join quals.
*/
TupleTableSlot *result;
node->mj_MatchedOuter = true; /* do it only once */
result = MJFillOuter(node);
if (result)
return result;
}
/*
* now we get the next outer tuple, if any
*/
outerTupleSlot = ExecProcNode(outerPlan);
node->mj_OuterTupleSlot = outerTupleSlot;
MJ_DEBUG_PROC_NODE(outerTupleSlot);
node->mj_MatchedOuter = false;
/* Compute join values and check for unmatchability */
switch (MJEvalOuterValues(node))
{
case MJEVAL_MATCHABLE:
/* Go test the new tuple against the current inner */
node->mj_JoinState = EXEC_MJ_SKIP_TEST;
break;
case MJEVAL_NONMATCHABLE:
/* Can't match, so fetch next outer tuple */
node->mj_JoinState = EXEC_MJ_SKIPOUTER_ADVANCE;
break;
case MJEVAL_ENDOFJOIN:
/* No more outer tuples */
MJ_printf("ExecMergeJoin: end of outer subplan\n");
innerTupleSlot = node->mj_InnerTupleSlot;
if (doFillInner && !TupIsNull(innerTupleSlot))
{
/*
* Need to emit right-join tuples for remaining
* inner tuples.
*/
node->mj_JoinState = EXEC_MJ_ENDOUTER;
break;
}
/* Otherwise we're done. */
return NULL;
}
break;
/*
* SKIPINNER_ADVANCE: advance over an inner tuple that is
* known not to join to any outer tuple.
*
* Before advancing, we check to see if we must emit an
* outer-join fill tuple for this inner tuple.
*/
case EXEC_MJ_SKIPINNER_ADVANCE:
MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_ADVANCE\n");
if (doFillInner && !node->mj_MatchedInner)
{
/*
* Generate a fake join tuple with nulls for the outer
* tuple, and return it if it passes the non-join quals.
*/
TupleTableSlot *result;
node->mj_MatchedInner = true; /* do it only once */
result = MJFillInner(node);
if (result)
return result;
}
/* Mark before advancing, if wanted */
if (node->mj_ExtraMarks)
ExecMarkPos(innerPlan);
/*
* now we get the next inner tuple, if any
*/
innerTupleSlot = ExecProcNode(innerPlan);
node->mj_InnerTupleSlot = innerTupleSlot;
MJ_DEBUG_PROC_NODE(innerTupleSlot);
node->mj_MatchedInner = false;
/* Compute join values and check for unmatchability */
switch (MJEvalInnerValues(node, innerTupleSlot))
{
case MJEVAL_MATCHABLE:
/* proceed to compare it to the current outer */
node->mj_JoinState = EXEC_MJ_SKIP_TEST;
break;
case MJEVAL_NONMATCHABLE:
/*
* current inner can't possibly match any outer;
* better to advance the inner scan than the outer.
*/
node->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE;
break;
case MJEVAL_ENDOFJOIN:
/* No more inner tuples */
MJ_printf("ExecMergeJoin: end of inner subplan\n");
outerTupleSlot = node->mj_OuterTupleSlot;
if (doFillOuter && !TupIsNull(outerTupleSlot))
{
/*
* Need to emit left-join tuples for remaining
* outer tuples.
*/
node->mj_JoinState = EXEC_MJ_ENDINNER;
break;
}
/* Otherwise we're done. */
return NULL;
}
break;
/*
* EXEC_MJ_ENDOUTER means we have run out of outer tuples, but
* are doing a right/full join and therefore must null-fill
* any remaining unmatched inner tuples.
*/
case EXEC_MJ_ENDOUTER:
MJ_printf("ExecMergeJoin: EXEC_MJ_ENDOUTER\n");
Assert(doFillInner);
if (!node->mj_MatchedInner)
{
/*
* Generate a fake join tuple with nulls for the outer
* tuple, and return it if it passes the non-join quals.
*/
TupleTableSlot *result;
node->mj_MatchedInner = true; /* do it only once */
result = MJFillInner(node);
if (result)
return result;
}
/* Mark before advancing, if wanted */
if (node->mj_ExtraMarks)
ExecMarkPos(innerPlan);
/*
* now we get the next inner tuple, if any
*/
innerTupleSlot = ExecProcNode(innerPlan);
node->mj_InnerTupleSlot = innerTupleSlot;
MJ_DEBUG_PROC_NODE(innerTupleSlot);
node->mj_MatchedInner = false;
if (TupIsNull(innerTupleSlot))
{
MJ_printf("ExecMergeJoin: end of inner subplan\n");
return NULL;
}
/* Else remain in ENDOUTER state and process next tuple. */
break;
/*
* EXEC_MJ_ENDINNER means we have run out of inner tuples, but
* are doing a left/full join and therefore must null- fill
* any remaining unmatched outer tuples.
*/
case EXEC_MJ_ENDINNER:
MJ_printf("ExecMergeJoin: EXEC_MJ_ENDINNER\n");
Assert(doFillOuter);
if (!node->mj_MatchedOuter)
{
/*
* Generate a fake join tuple with nulls for the inner
* tuple, and return it if it passes the non-join quals.
*/
TupleTableSlot *result;
node->mj_MatchedOuter = true; /* do it only once */
result = MJFillOuter(node);
if (result)
return result;
}
/*
* now we get the next outer tuple, if any
*/
outerTupleSlot = ExecProcNode(outerPlan);
node->mj_OuterTupleSlot = outerTupleSlot;
MJ_DEBUG_PROC_NODE(outerTupleSlot);
node->mj_MatchedOuter = false;
if (TupIsNull(outerTupleSlot))
{
MJ_printf("ExecMergeJoin: end of outer subplan\n");
return NULL;
}
/* Else remain in ENDINNER state and process next tuple. */
break;
/*
* broken state value?
*/
default:
elog(ERROR, "unrecognized mergejoin state: %d",
(int) node->mj_JoinState);
}
}
}
| void ExecReScanMergeJoin | ( | MergeJoinState * | node | ) |
Definition at line 1674 of file nodeMergejoin.c.
References PlanState::chgParam, ExecClearTuple(), ExecReScan(), MergeJoinState::js, PlanState::lefttree, MergeJoinState::mj_InnerTupleSlot, MergeJoinState::mj_JoinState, MergeJoinState::mj_MarkedTupleSlot, MergeJoinState::mj_MatchedInner, MergeJoinState::mj_MatchedOuter, MergeJoinState::mj_OuterTupleSlot, NULL, JoinState::ps, PlanState::ps_TupFromTlist, and PlanState::righttree.
Referenced by ExecReScan().
{
ExecClearTuple(node->mj_MarkedTupleSlot);
node->mj_JoinState = EXEC_MJ_INITIALIZE_OUTER;
node->js.ps.ps_TupFromTlist = false;
node->mj_MatchedOuter = false;
node->mj_MatchedInner = false;
node->mj_OuterTupleSlot = NULL;
node->mj_InnerTupleSlot = NULL;
/*
* if chgParam of subnodes is not null then plans will be re-scanned by
* first ExecProcNode.
*/
if (node->js.ps.lefttree->chgParam == NULL)
ExecReScan(node->js.ps.lefttree);
if (node->js.ps.righttree->chgParam == NULL)
ExecReScan(node->js.ps.righttree);
}
| static int MJCompare | ( | MergeJoinState * | mergestate | ) | [static] |
Definition at line 383 of file nodeMergejoin.c.
References ApplySortComparator(), ExprContext::ecxt_per_tuple_memory, i, MergeJoinState::js, MergeJoinClauseData::ldatum, MergeJoinClauseData::lisnull, MemoryContextSwitchTo(), MergeJoinState::mj_Clauses, MergeJoinState::mj_ConstFalseJoin, MergeJoinState::mj_NumClauses, JoinState::ps, PlanState::ps_ExprContext, MergeJoinClauseData::rdatum, ResetExprContext, MergeJoinClauseData::risnull, and MergeJoinClauseData::ssup.
Referenced by ExecMergeJoin().
{
int result = 0;
bool nulleqnull = false;
ExprContext *econtext = mergestate->js.ps.ps_ExprContext;
int i;
MemoryContext oldContext;
/*
* Call the comparison functions in short-lived context, in case they leak
* memory.
*/
ResetExprContext(econtext);
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
for (i = 0; i < mergestate->mj_NumClauses; i++)
{
MergeJoinClause clause = &mergestate->mj_Clauses[i];
/*
* Special case for NULL-vs-NULL, else use standard comparison.
*/
if (clause->lisnull && clause->risnull)
{
nulleqnull = true; /* NULL "=" NULL */
continue;
}
result = ApplySortComparator(clause->ldatum, clause->lisnull,
clause->rdatum, clause->risnull,
&clause->ssup);
if (result != 0)
break;
}
/*
* If we had any NULL-vs-NULL inputs, we do not want to report that the
* tuples are equal. Instead, if result is still 0, change it to +1. This
* will result in advancing the inner side of the join.
*
* Likewise, if there was a constant-false joinqual, do not report
* equality. We have to check this as part of the mergequals, else the
* rescan logic will do the wrong thing.
*/
if (result == 0 &&
(nulleqnull || mergestate->mj_ConstFalseJoin))
result = 1;
MemoryContextSwitchTo(oldContext);
return result;
}
| static MJEvalResult MJEvalInnerValues | ( | MergeJoinState * | mergestate, | |
| TupleTableSlot * | innerslot | |||
| ) | [static] |
Definition at line 333 of file nodeMergejoin.c.
References ExprContext::ecxt_innertuple, ExprContext::ecxt_per_tuple_memory, ExecEvalExpr, i, MemoryContextSwitchTo(), MergeJoinState::mj_Clauses, MergeJoinState::mj_FillInner, MergeJoinState::mj_InnerEContext, MergeJoinState::mj_NumClauses, MJEVAL_MATCHABLE, NULL, MergeJoinClauseData::rdatum, ResetExprContext, MergeJoinClauseData::rexpr, MergeJoinClauseData::risnull, MergeJoinClauseData::ssup, SortSupportData::ssup_nulls_first, and TupIsNull.
Referenced by ExecMergeJoin().
{
ExprContext *econtext = mergestate->mj_InnerEContext;
MJEvalResult result = MJEVAL_MATCHABLE;
int i;
MemoryContext oldContext;
/* Check for end of inner subplan */
if (TupIsNull(innerslot))
return MJEVAL_ENDOFJOIN;
ResetExprContext(econtext);
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
econtext->ecxt_innertuple = innerslot;
for (i = 0; i < mergestate->mj_NumClauses; i++)
{
MergeJoinClause clause = &mergestate->mj_Clauses[i];
clause->rdatum = ExecEvalExpr(clause->rexpr, econtext,
&clause->risnull, NULL);
if (clause->risnull)
{
/* match is impossible; can we end the join early? */
if (i == 0 && !clause->ssup.ssup_nulls_first &&
!mergestate->mj_FillInner)
result = MJEVAL_ENDOFJOIN;
else if (result == MJEVAL_MATCHABLE)
result = MJEVAL_NONMATCHABLE;
}
}
MemoryContextSwitchTo(oldContext);
return result;
}
| static MJEvalResult MJEvalOuterValues | ( | MergeJoinState * | mergestate | ) | [static] |
Definition at line 286 of file nodeMergejoin.c.
References ExprContext::ecxt_outertuple, ExprContext::ecxt_per_tuple_memory, ExecEvalExpr, i, MergeJoinClauseData::ldatum, MergeJoinClauseData::lexpr, MergeJoinClauseData::lisnull, MemoryContextSwitchTo(), MergeJoinState::mj_Clauses, MergeJoinState::mj_FillOuter, MergeJoinState::mj_NumClauses, MergeJoinState::mj_OuterEContext, MergeJoinState::mj_OuterTupleSlot, MJEVAL_MATCHABLE, NULL, ResetExprContext, MergeJoinClauseData::ssup, SortSupportData::ssup_nulls_first, and TupIsNull.
Referenced by ExecMergeJoin().
{
ExprContext *econtext = mergestate->mj_OuterEContext;
MJEvalResult result = MJEVAL_MATCHABLE;
int i;
MemoryContext oldContext;
/* Check for end of outer subplan */
if (TupIsNull(mergestate->mj_OuterTupleSlot))
return MJEVAL_ENDOFJOIN;
ResetExprContext(econtext);
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
econtext->ecxt_outertuple = mergestate->mj_OuterTupleSlot;
for (i = 0; i < mergestate->mj_NumClauses; i++)
{
MergeJoinClause clause = &mergestate->mj_Clauses[i];
clause->ldatum = ExecEvalExpr(clause->lexpr, econtext,
&clause->lisnull, NULL);
if (clause->lisnull)
{
/* match is impossible; can we end the join early? */
if (i == 0 && !clause->ssup.ssup_nulls_first &&
!mergestate->mj_FillOuter)
result = MJEVAL_ENDOFJOIN;
else if (result == MJEVAL_MATCHABLE)
result = MJEVAL_NONMATCHABLE;
}
}
MemoryContextSwitchTo(oldContext);
return result;
}
| static MergeJoinClause MJExamineQuals | ( | List * | mergeclauses, | |
| Oid * | mergefamilies, | |||
| Oid * | mergecollations, | |||
| int * | mergestrategies, | |||
| bool * | mergenullsfirst, | |||
| PlanState * | parent | |||
| ) | [static] |
Definition at line 175 of file nodeMergejoin.c.
References OpExpr::args, Assert, BTEqualStrategyNumber, BTGreaterStrategyNumber, BTLessStrategyNumber, BTORDER_PROC, BTSORTSUPPORT_PROC, SortSupportData::comparator, CurrentMemoryContext, elog, ERROR, ExecInitExpr(), get_op_opfamily_properties(), get_opfamily_proc(), IsA, MergeJoinClauseData::lexpr, lfirst, linitial, list_length(), lsecond, NULL, OidFunctionCall1, OidIsValid, OpExpr::opno, palloc0(), PointerGetDatum, PrepareSortSupportComparisonShim(), MergeJoinClauseData::rexpr, MergeJoinClauseData::ssup, SortSupportData::ssup_collation, SortSupportData::ssup_cxt, SortSupportData::ssup_nulls_first, and SortSupportData::ssup_reverse.
Referenced by ExecInitMergeJoin().
{
MergeJoinClause clauses;
int nClauses = list_length(mergeclauses);
int iClause;
ListCell *cl;
clauses = (MergeJoinClause) palloc0(nClauses * sizeof(MergeJoinClauseData));
iClause = 0;
foreach(cl, mergeclauses)
{
OpExpr *qual = (OpExpr *) lfirst(cl);
MergeJoinClause clause = &clauses[iClause];
Oid opfamily = mergefamilies[iClause];
Oid collation = mergecollations[iClause];
StrategyNumber opstrategy = mergestrategies[iClause];
bool nulls_first = mergenullsfirst[iClause];
int op_strategy;
Oid op_lefttype;
Oid op_righttype;
Oid sortfunc;
if (!IsA(qual, OpExpr))
elog(ERROR, "mergejoin clause is not an OpExpr");
/*
* Prepare the input expressions for execution.
*/
clause->lexpr = ExecInitExpr((Expr *) linitial(qual->args), parent);
clause->rexpr = ExecInitExpr((Expr *) lsecond(qual->args), parent);
/* Set up sort support data */
clause->ssup.ssup_cxt = CurrentMemoryContext;
clause->ssup.ssup_collation = collation;
if (opstrategy == BTLessStrategyNumber)
clause->ssup.ssup_reverse = false;
else if (opstrategy == BTGreaterStrategyNumber)
clause->ssup.ssup_reverse = true;
else /* planner screwed up */
elog(ERROR, "unsupported mergejoin strategy %d", opstrategy);
clause->ssup.ssup_nulls_first = nulls_first;
/* Extract the operator's declared left/right datatypes */
get_op_opfamily_properties(qual->opno, opfamily, false,
&op_strategy,
&op_lefttype,
&op_righttype);
if (op_strategy != BTEqualStrategyNumber) /* should not happen */
elog(ERROR, "cannot merge using non-equality operator %u",
qual->opno);
/* And get the matching support or comparison function */
sortfunc = get_opfamily_proc(opfamily,
op_lefttype,
op_righttype,
BTSORTSUPPORT_PROC);
if (OidIsValid(sortfunc))
{
/* The sort support function should provide a comparator */
OidFunctionCall1(sortfunc, PointerGetDatum(&clause->ssup));
Assert(clause->ssup.comparator != NULL);
}
else
{
/* opfamily doesn't provide sort support, get comparison func */
sortfunc = get_opfamily_proc(opfamily,
op_lefttype,
op_righttype,
BTORDER_PROC);
if (!OidIsValid(sortfunc)) /* should not happen */
elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
BTORDER_PROC, op_lefttype, op_righttype, opfamily);
/* We'll use a shim to call the old-style btree comparator */
PrepareSortSupportComparisonShim(sortfunc, &clause->ssup);
}
iClause++;
}
return clauses;
}
| static TupleTableSlot* MJFillInner | ( | MergeJoinState * | node | ) | [static] |
Definition at line 485 of file nodeMergejoin.c.
References ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, ExecProject(), ExecQual(), ExprEndResult, InstrCountFiltered2, MergeJoinState::js, MergeJoinState::mj_InnerTupleSlot, MergeJoinState::mj_NullOuterTupleSlot, MJ_printf, JoinState::ps, PlanState::ps_ExprContext, PlanState::ps_ProjInfo, PlanState::ps_TupFromTlist, PlanState::qual, and ResetExprContext.
Referenced by ExecMergeJoin().
{
ExprContext *econtext = node->js.ps.ps_ExprContext;
List *otherqual = node->js.ps.qual;
ResetExprContext(econtext);
econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
if (ExecQual(otherqual, econtext, false))
{
/*
* qualification succeeded. now form the desired projection tuple and
* return the slot containing it.
*/
TupleTableSlot *result;
ExprDoneCond isDone;
MJ_printf("ExecMergeJoin: returning inner fill tuple\n");
result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
if (isDone != ExprEndResult)
{
node->js.ps.ps_TupFromTlist =
(isDone == ExprMultipleResult);
return result;
}
}
else
InstrCountFiltered2(node, 1);
return NULL;
}
| static TupleTableSlot* MJFillOuter | ( | MergeJoinState * | node | ) | [static] |
Definition at line 444 of file nodeMergejoin.c.
References ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, ExecProject(), ExecQual(), ExprEndResult, InstrCountFiltered2, MergeJoinState::js, MergeJoinState::mj_NullInnerTupleSlot, MergeJoinState::mj_OuterTupleSlot, MJ_printf, JoinState::ps, PlanState::ps_ExprContext, PlanState::ps_ProjInfo, PlanState::ps_TupFromTlist, PlanState::qual, and ResetExprContext.
Referenced by ExecMergeJoin().
{
ExprContext *econtext = node->js.ps.ps_ExprContext;
List *otherqual = node->js.ps.qual;
ResetExprContext(econtext);
econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
if (ExecQual(otherqual, econtext, false))
{
/*
* qualification succeeded. now form the desired projection tuple and
* return the slot containing it.
*/
TupleTableSlot *result;
ExprDoneCond isDone;
MJ_printf("ExecMergeJoin: returning outer fill tuple\n");
result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
if (isDone != ExprEndResult)
{
node->js.ps.ps_TupFromTlist =
(isDone == ExprMultipleResult);
return result;
}
}
else
InstrCountFiltered2(node, 1);
return NULL;
}
1.7.1