summaryrefslogtreecommitdiff
path: root/src/backend/executor/nodeMergejoin.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/nodeMergejoin.c')
-rw-r--r--src/backend/executor/nodeMergejoin.c1595
1 files changed, 0 insertions, 1595 deletions
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
deleted file mode 100644
index 4467fef9e12..00000000000
--- a/src/backend/executor/nodeMergejoin.c
+++ /dev/null
@@ -1,1595 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * nodeMergejoin.c
- * routines supporting merge joins
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.50 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-/*
- * INTERFACE ROUTINES
- * ExecMergeJoin mergejoin outer and inner relations.
- * ExecInitMergeJoin creates and initializes run time states
- * ExecEndMergeJoin cleans up the node.
- *
- * NOTES
- * Essential operation of the merge join algorithm is as follows:
- *
- * Join { -
- * get initial outer and inner tuples INITIALIZE
- * Skip Inner SKIPINNER
- * mark inner position JOINMARK
- * do forever { -
- * while (outer == inner) { JOINTEST
- * join tuples JOINTUPLES
- * advance inner position NEXTINNER
- * } -
- * advance outer position NEXTOUTER
- * if (outer == mark) { TESTOUTER
- * restore inner position to mark TESTOUTER
- * continue -
- * } else { -
- * Skip Outer SKIPOUTER
- * mark inner position JOINMARK
- * } -
- * } -
- * } -
- *
- * Skip Outer { SKIPOUTER_BEGIN
- * if (inner == outer) Join Tuples JOINTUPLES
- * while (outer < inner) SKIPOUTER_TEST
- * advance outer SKIPOUTER_ADVANCE
- * if (outer > inner) SKIPOUTER_TEST
- * Skip Inner SKIPINNER
- * } -
- *
- * Skip Inner { SKIPINNER_BEGIN
- * if (inner == outer) Join Tuples JOINTUPLES
- * while (outer > inner) SKIPINNER_TEST
- * advance inner SKIPINNER_ADVANCE
- * if (outer < inner) SKIPINNER_TEST
- * Skip Outer SKIPOUTER
- * } -
- *
- * The merge join operation is coded in the fashion
- * of a state machine. At each state, we do something and then
- * proceed to another state. This state is stored in the node's
- * execution state information and is preserved across calls to
- * ExecMergeJoin. -cim 10/31/89
- *
- */
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "access/printtup.h"
-#include "catalog/pg_operator.h"
-#include "executor/execdebug.h"
-#include "executor/execdefs.h"
-#include "executor/nodeMergejoin.h"
-#include "utils/lsyscache.h"
-#include "utils/syscache.h"
-
-
-static bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext);
-
-#define MarkInnerTuple(innerTupleSlot, mergestate) \
-( \
- ExecStoreTuple(heap_copytuple((innerTupleSlot)->val), \
- (mergestate)->mj_MarkedTupleSlot, \
- InvalidBuffer, \
- true) \
-)
-
-
-/* ----------------------------------------------------------------
- * MJFormSkipQuals
- *
- * This takes the mergeclause which is a qualification of the
- * form ((= expr expr) (= expr expr) ...) and forms new lists
- * of the forms ((< expr expr) (< expr expr) ...) and
- * ((> expr expr) (> expr expr) ...). These lists will be used
- * by ExecMergeJoin() to determine if we should skip tuples.
- * (We expect there to be suitable operators because the "=" operators
- * were marked mergejoinable; however, there might be a different
- * one needed in each qual clause.)
- * ----------------------------------------------------------------
- */
-static void
-MJFormSkipQuals(List *qualList, List **ltQuals, List **gtQuals)
-{
- List *ltcdr,
- *gtcdr;
-
- /*
- * Make modifiable copies of the qualList.
- */
- *ltQuals = (List *) copyObject((Node *) qualList);
- *gtQuals = (List *) copyObject((Node *) qualList);
-
- /*
- * Scan both lists in parallel, so that we can update the operators
- * with the minimum number of syscache searches.
- */
- ltcdr = *ltQuals;
- foreach(gtcdr, *gtQuals)
- {
- Expr *ltqual = (Expr *) lfirst(ltcdr);
- Expr *gtqual = (Expr *) lfirst(gtcdr);
- Oper *ltop = (Oper *) ltqual->oper;
- Oper *gtop = (Oper *) gtqual->oper;
-
- /*
- * The two ops should be identical, so use either one for lookup.
- */
- if (!IsA(ltop, Oper))
- elog(ERROR, "MJFormSkipQuals: op not an Oper!");
-
- /*
- * Lookup the operators, and replace the data in the copied
- * operator nodes.
- */
- op_mergejoin_crossops(ltop->opno,
- &ltop->opno,
- &gtop->opno,
- &ltop->opid,
- &gtop->opid);
- ltop->op_fcache = NULL;
- gtop->op_fcache = NULL;
-
- ltcdr = lnext(ltcdr);
- }
-}
-
-/* ----------------------------------------------------------------
- * MergeCompare
- *
- * Compare the keys according to 'compareQual' which is of the
- * form: { (key1a > key2a) (key1b > key2b) ... }.
- *
- * (actually, it could also be of the form (key1a < key2a)...)
- *
- * This is different from calling ExecQual because ExecQual returns
- * true only if ALL the comparison clauses are satisfied.
- * However, there is an order of significance among the keys with
- * the first keys being most significant. Therefore, the clauses
- * are evaluated in order and the 'compareQual' is satisfied
- * if (key1i > key2i) is true and (key1j = key2j) for 0 < j < i.
- * We use the original mergeclause items to detect equality.
- * ----------------------------------------------------------------
- */
-static bool
-MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
-{
- bool result;
- MemoryContext oldContext;
- List *clause;
- List *eqclause;
-
- /*
- * Do expression eval in short-lived context.
- */
- oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
- /*
- * for each pair of clauses, test them until our compare conditions
- * are satisfied. if we reach the end of the list, none of our key
- * greater-than conditions were satisfied so we return false.
- */
- result = false; /* assume 'false' result */
-
- eqclause = eqQual;
- foreach(clause, compareQual)
- {
- Datum const_value;
- bool isNull;
-
- /*
- * first test if our compare clause is satisfied. if so then
- * return true.
- *
- * A NULL result is considered false.
- */
- const_value = ExecEvalExpr((Node *) lfirst(clause), econtext,
- &isNull, NULL);
-
- if (DatumGetBool(const_value) && !isNull)
- {
- result = true;
- break;
- }
-
- /*-----------
- * ok, the compare clause failed so we test if the keys are
- * equal... if key1 != key2, we return false. otherwise
- * key1 = key2 so we move on to the next pair of keys.
- *-----------
- */
- const_value = ExecEvalExpr((Node *) lfirst(eqclause),
- econtext,
- &isNull,
- NULL);
-
- if (!DatumGetBool(const_value) || isNull)
- break; /* return false */
-
- eqclause = lnext(eqclause);
- }
-
- MemoryContextSwitchTo(oldContext);
-
- return result;
-}
-
-/* ----------------------------------------------------------------
- * ExecMergeTupleDump
- *
- * This function is called through the MJ_dump() macro
- * when EXEC_MERGEJOINDEBUG is defined
- * ----------------------------------------------------------------
- */
-#ifdef EXEC_MERGEJOINDEBUG
-
-static void
-ExecMergeTupleDumpOuter(MergeJoinState *mergestate)
-{
- TupleTableSlot *outerSlot = mergestate->mj_OuterTupleSlot;
-
- printf("==== outer tuple ====\n");
- if (TupIsNull(outerSlot))
- printf("(nil)\n");
- else
- MJ_debugtup(outerSlot->val,
- outerSlot->ttc_tupleDescriptor);
-}
-
-static void
-ExecMergeTupleDumpInner(MergeJoinState *mergestate)
-{
- TupleTableSlot *innerSlot = mergestate->mj_InnerTupleSlot;
-
- printf("==== inner tuple ====\n");
- if (TupIsNull(innerSlot))
- printf("(nil)\n");
- else
- MJ_debugtup(innerSlot->val,
- innerSlot->ttc_tupleDescriptor);
-}
-
-static void
-ExecMergeTupleDumpMarked(MergeJoinState *mergestate)
-{
- TupleTableSlot *markedSlot = mergestate->mj_MarkedTupleSlot;
-
- printf("==== marked tuple ====\n");
- if (TupIsNull(markedSlot))
- printf("(nil)\n");
- else
- MJ_debugtup(markedSlot->val,
- markedSlot->ttc_tupleDescriptor);
-}
-
-static void
-ExecMergeTupleDump(MergeJoinState *mergestate)
-{
- printf("******** ExecMergeTupleDump ********\n");
-
- ExecMergeTupleDumpOuter(mergestate);
- ExecMergeTupleDumpInner(mergestate);
- ExecMergeTupleDumpMarked(mergestate);
-
- printf("******** \n");
-}
-#endif
-
-/* ----------------------------------------------------------------
- * ExecMergeJoin
- *
- * old comments
- * Details of the merge-join routines:
- *
- * (1) ">" and "<" operators
- *
- * Merge-join is done by joining the inner and outer tuples satisfying
- * the join clauses of the form ((= outerKey innerKey) ...).
- * The join clauses is provided by the query planner and may contain
- * more than one (= outerKey innerKey) clauses (for composite key).
- *
- * However, the query executor needs to know whether an outer
- * tuple is "greater/smaller" than an inner tuple so that it can
- * "synchronize" the two relations. For e.g., consider the following
- * relations:
- *
- * outer: (0 ^1 1 2 5 5 5 6 6 7) current tuple: 1
- * inner: (1 ^3 5 5 5 5 6) current tuple: 3
- *
- * To continue the merge-join, the executor needs to scan both inner
- * and outer relations till the matching tuples 5. It needs to know
- * that currently inner tuple 3 is "greater" than outer tuple 1 and
- * therefore it should scan the outer relation first to find a
- * matching tuple and so on.
- *
- * Therefore, when initializing the merge-join node, the executor
- * creates the "greater/smaller" clause by substituting the "="
- * operator in the join clauses with the corresponding ">" operator.
- * The opposite "smaller/greater" clause is formed by substituting "<".
- *
- * Note: prior to v6.5, the relational clauses were formed using the
- * sort op used to sort the inner relation, which of course would fail
- * if the outer and inner keys were of different data types.
- * In the current code, we instead assume that operators named "<" and ">"
- * will do the right thing. This should be true since the mergejoin "="
- * operator's pg_operator entry will have told the planner to sort by
- * "<" for each of the left and right sides.
- *
- * (2) repositioning inner "cursor"
- *
- * Consider the above relations and suppose that the executor has
- * just joined the first outer "5" with the last inner "5". The
- * next step is of course to join the second outer "5" with all
- * the inner "5's". This requires repositioning the inner "cursor"
- * to point at the first inner "5". This is done by "marking" the
- * first inner 5 and restore the "cursor" to it before joining
- * with the second outer 5. The access method interface provides
- * routines to mark and restore to a tuple.
- * ----------------------------------------------------------------
- */
-TupleTableSlot *
-ExecMergeJoin(MergeJoin *node)
-{
- EState *estate;
- MergeJoinState *mergestate;
- ScanDirection direction;
- List *innerSkipQual;
- List *outerSkipQual;
- List *mergeclauses;
- List *joinqual;
- List *otherqual;
- bool qualResult;
- bool compareResult;
- Plan *innerPlan;
- TupleTableSlot *innerTupleSlot;
- Plan *outerPlan;
- TupleTableSlot *outerTupleSlot;
- ExprContext *econtext;
- bool doFillOuter;
- bool doFillInner;
-
- /*
- * get information from node
- */
- mergestate = node->mergestate;
- estate = node->join.plan.state;
- direction = estate->es_direction;
- innerPlan = innerPlan((Plan *) node);
- outerPlan = outerPlan((Plan *) node);
- econtext = mergestate->jstate.cs_ExprContext;
- mergeclauses = node->mergeclauses;
- joinqual = node->join.joinqual;
- otherqual = node->join.plan.qual;
-
- switch (node->join.jointype)
- {
- case JOIN_INNER:
- doFillOuter = false;
- doFillInner = false;
- break;
- case JOIN_LEFT:
- doFillOuter = true;
- doFillInner = false;
- break;
- case JOIN_FULL:
- doFillOuter = true;
- doFillInner = true;
- break;
- case JOIN_RIGHT:
- doFillOuter = false;
- doFillInner = true;
- break;
- default:
- elog(ERROR, "ExecMergeJoin: unsupported join type %d",
- (int) node->join.jointype);
- doFillOuter = false; /* keep compiler quiet */
- doFillInner = false;
- break;
- }
-
- if (ScanDirectionIsForward(direction))
- {
- outerSkipQual = mergestate->mj_OuterSkipQual;
- innerSkipQual = mergestate->mj_InnerSkipQual;
- }
- else
- {
- outerSkipQual = mergestate->mj_InnerSkipQual;
- innerSkipQual = mergestate->mj_OuterSkipQual;
- }
-
- /*
- * 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 (mergestate->jstate.cs_TupFromTlist)
- {
- TupleTableSlot *result;
- ExprDoneCond isDone;
-
- result = ExecProject(mergestate->jstate.cs_ProjInfo, &isDone);
- if (isDone == ExprMultipleResult)
- return result;
- /* Done with that source tuple... */
- mergestate->jstate.cs_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 (;;)
- {
- /*
- * get the current state of the join and do things accordingly.
- * Note: The join states are highlighted with 32-* comments for
- * improved readability.
- */
- MJ_dump(mergestate);
-
- switch (mergestate->mj_JoinState)
- {
- /*
- * EXEC_MJ_INITIALIZE means that this is the first time
- * ExecMergeJoin() has been called and so we have to fetch
- * the first tuple for both outer and inner subplans. If
- * we fail to get a tuple here, then that subplan is
- * empty, and we either end the join or go to one of the
- * fill-remaining-tuples states.
- */
- case EXEC_MJ_INITIALIZE:
- MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE\n");
-
- outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
- mergestate->mj_OuterTupleSlot = outerTupleSlot;
- if (TupIsNull(outerTupleSlot))
- {
- MJ_printf("ExecMergeJoin: outer subplan is empty\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.
- */
- mergestate->mj_JoinState = EXEC_MJ_ENDOUTER;
- mergestate->mj_MatchedInner = true;
- break;
- }
- /* Otherwise we're done. */
- return NULL;
- }
-
- innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
- mergestate->mj_InnerTupleSlot = innerTupleSlot;
- if (TupIsNull(innerTupleSlot))
- {
- MJ_printf("ExecMergeJoin: inner subplan is empty\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 this tuple before advancing
- * outer.
- */
- mergestate->mj_JoinState = EXEC_MJ_ENDINNER;
- mergestate->mj_MatchedOuter = false;
- break;
- }
- /* Otherwise we're done. */
- return NULL;
- }
-
- /*
- * OK, we have the initial tuples. Begin by skipping
- * unmatched inner tuples.
- */
- mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_BEGIN;
- break;
-
- /*
- * EXEC_MJ_JOINMARK means we have just found a new outer
- * tuple and a possible matching inner tuple. This is the
- * case after the INITIALIZE, SKIPOUTER or SKIPINNER
- * states.
- */
- case EXEC_MJ_JOINMARK:
- MJ_printf("ExecMergeJoin: EXEC_MJ_JOINMARK\n");
-
- ExecMarkPos(innerPlan);
-
- MarkInnerTuple(mergestate->mj_InnerTupleSlot, mergestate);
-
- mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
- break;
-
- /*
- * EXEC_MJ_JOINTEST means we have two tuples which might
- * satisfy the merge clause, so we test them.
- *
- * If they do satisfy, then we join them and move on to the
- * next inner tuple (EXEC_MJ_JOINTUPLES).
- *
- * If they do not satisfy then advance to next outer tuple.
- */
- case EXEC_MJ_JOINTEST:
- MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n");
-
- ResetExprContext(econtext);
-
- outerTupleSlot = mergestate->mj_OuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_InnerTupleSlot;
- econtext->ecxt_innertuple = innerTupleSlot;
-
- qualResult = ExecQual(mergeclauses, econtext, false);
- MJ_DEBUG_QUAL(mergeclauses, qualResult);
-
- if (qualResult)
- mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
- else
- mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER;
- 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_NEXT_INNER).
- */
- case EXEC_MJ_JOINTUPLES:
- MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");
-
- mergestate->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 before checking the
- * merge qual. One per tuple should be sufficient. Also,
- * the econtext's tuple pointers were set up before
- * checking the merge qual, so we needn't do it again.
- */
- qualResult = (joinqual == NIL ||
- ExecQual(joinqual, econtext, false));
- MJ_DEBUG_QUAL(joinqual, qualResult);
-
- if (qualResult)
- {
- mergestate->mj_MatchedOuter = true;
- mergestate->mj_MatchedInner = true;
-
- 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(mergestate->jstate.cs_ProjInfo,
- &isDone);
-
- if (isDone != ExprEndResult)
- {
- mergestate->jstate.cs_TupFromTlist =
- (isDone == ExprMultipleResult);
- return result;
- }
- }
- }
- 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 && !mergestate->mj_MatchedInner)
- {
- /*
- * Generate a fake join tuple with nulls for the outer
- * tuple, and return it if it passes the non-join
- * quals.
- */
- mergestate->mj_MatchedInner = true; /* do it only once */
-
- ResetExprContext(econtext);
-
- outerTupleSlot = mergestate->mj_NullOuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_InnerTupleSlot;
- econtext->ecxt_innertuple = 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 fill tuple\n");
-
- result = ExecProject(mergestate->jstate.cs_ProjInfo,
- &isDone);
-
- if (isDone != ExprEndResult)
- {
- mergestate->jstate.cs_TupFromTlist =
- (isDone == ExprMultipleResult);
- return result;
- }
- }
- }
-
- /*
- * now we get the next inner tuple, if any
- */
- innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
- mergestate->mj_InnerTupleSlot = innerTupleSlot;
- MJ_DEBUG_PROC_NODE(innerTupleSlot);
- mergestate->mj_MatchedInner = false;
-
- if (TupIsNull(innerTupleSlot))
- mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER;
- else
- mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
- 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
- * 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 && !mergestate->mj_MatchedOuter)
- {
- /*
- * Generate a fake join tuple with nulls for the inner
- * tuple, and return it if it passes the non-join
- * quals.
- */
- mergestate->mj_MatchedOuter = true; /* do it only once */
-
- ResetExprContext(econtext);
-
- outerTupleSlot = mergestate->mj_OuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_NullInnerTupleSlot;
- econtext->ecxt_innertuple = 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 fill tuple\n");
-
- result = ExecProject(mergestate->jstate.cs_ProjInfo,
- &isDone);
-
- if (isDone != ExprEndResult)
- {
- mergestate->jstate.cs_TupFromTlist =
- (isDone == ExprMultipleResult);
- return result;
- }
- }
- }
-
- /*
- * now we get the next outer tuple, if any
- */
- outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
- mergestate->mj_OuterTupleSlot = outerTupleSlot;
- MJ_DEBUG_PROC_NODE(outerTupleSlot);
- mergestate->mj_MatchedOuter = false;
-
- /*
- * if the outer tuple is null then we are done with the
- * join, unless we have inner tuples we need to null-fill.
- */
- if (TupIsNull(outerTupleSlot))
- {
- MJ_printf("ExecMergeJoin: end of outer subplan\n");
- innerTupleSlot = mergestate->mj_InnerTupleSlot;
- if (doFillInner && !TupIsNull(innerTupleSlot))
- {
- /*
- * Need to emit right-join tuples for remaining
- * inner tuples.
- */
- mergestate->mj_JoinState = EXEC_MJ_ENDOUTER;
- break;
- }
- /* Otherwise we're done. */
- return NULL;
- }
-
- mergestate->mj_JoinState = EXEC_MJ_TESTOUTER;
- 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 tuples with the inner tuples (EXEC_MJ_JOINTEST)
- *
- * 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 know we have
- * to proceed to skip outer tuples until outer >= inner
- * (EXEC_MJ_SKIPOUTER).
- *
- * 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 compare the outer tuple with the marked inner
- * tuple
- */
- ResetExprContext(econtext);
-
- outerTupleSlot = mergestate->mj_OuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_MarkedTupleSlot;
- econtext->ecxt_innertuple = innerTupleSlot;
-
- qualResult = ExecQual(mergeclauses, econtext, false);
- MJ_DEBUG_QUAL(mergeclauses, qualResult);
-
- if (qualResult)
- {
- /*
- * the merge clause matched so now we restore the
- * inner scan position to the first mark, and loop
- * back to JOINTEST. Actually, since we know the
- * mergeclause matches, we can skip JOINTEST and go
- * straight to JOINTUPLES.
- *
- * 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 nil when doing a right or full join ---
- * otherwise some of the rescanned tuples might fail
- * the extra joinquals.
- */
- ExecRestrPos(innerPlan);
- mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
- }
- else
- {
- /* ----------------
- * if the inner tuple was nil and the new outer
- * tuple didn't match the marked outer tuple then
- * we have the case:
- *
- * outer inner
- * 4 4 - marked tuple
- * new outer - 5 4
- * 6 nil - inner tuple
- * 7
- *
- * which means that all subsequent outer tuples will be
- * larger than our marked inner tuples. So we're done.
- * ----------------
- */
- innerTupleSlot = mergestate->mj_InnerTupleSlot;
- if (TupIsNull(innerTupleSlot))
- {
- if (doFillOuter)
- {
- /*
- * Need to emit left-join tuples for remaining
- * outer tuples.
- */
- mergestate->mj_JoinState = EXEC_MJ_ENDINNER;
- break;
- }
- /* Otherwise we're done. */
- return NULL;
- }
-
- /* continue on to skip outer tuples */
- mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_BEGIN;
- }
- break;
-
- /*----------------------------------------------------------
- * EXEC_MJ_SKIPOUTER means skip over tuples in the outer plan
- * until we find an outer tuple >= current inner tuple.
- *
- * 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.
- *
- * To avoid redundant tests, we divide this into three
- * sub-states: BEGIN, TEST, ADVANCE.
- *----------------------------------------------------------
- */
- case EXEC_MJ_SKIPOUTER_BEGIN:
- MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_BEGIN\n");
-
- /*
- * before we advance, make sure the current tuples do not
- * satisfy the mergeclauses. If they do, then we update
- * the marked tuple and go join them.
- */
- ResetExprContext(econtext);
-
- outerTupleSlot = mergestate->mj_OuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_InnerTupleSlot;
- econtext->ecxt_innertuple = innerTupleSlot;
-
- qualResult = ExecQual(mergeclauses, econtext, false);
- MJ_DEBUG_QUAL(mergeclauses, qualResult);
-
- if (qualResult)
- {
- ExecMarkPos(innerPlan);
-
- MarkInnerTuple(innerTupleSlot, mergestate);
-
- mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
- break;
- }
-
- mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_TEST;
- break;
-
- case EXEC_MJ_SKIPOUTER_TEST:
- MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_TEST\n");
-
- /*
- * ok, now test the skip qualification
- */
- outerTupleSlot = mergestate->mj_OuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_InnerTupleSlot;
- econtext->ecxt_innertuple = innerTupleSlot;
-
- compareResult = MergeCompare(mergeclauses,
- outerSkipQual,
- econtext);
-
- MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult);
-
- /*
- * compareResult is true as long as we should continue
- * skipping outer tuples.
- */
- if (compareResult)
- {
- mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_ADVANCE;
- break;
- }
-
- /*
- * now check the inner skip qual to see if we should now
- * skip inner tuples... if we fail the inner skip qual,
- * then we know we have a new pair of matching tuples.
- */
- compareResult = MergeCompare(mergeclauses,
- innerSkipQual,
- econtext);
-
- MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult);
-
- if (compareResult)
- mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_BEGIN;
- else
- mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
- break;
-
- /*
- * 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 && !mergestate->mj_MatchedOuter)
- {
- /*
- * Generate a fake join tuple with nulls for the inner
- * tuple, and return it if it passes the non-join
- * quals.
- */
- mergestate->mj_MatchedOuter = true; /* do it only once */
-
- ResetExprContext(econtext);
-
- outerTupleSlot = mergestate->mj_OuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_NullInnerTupleSlot;
- econtext->ecxt_innertuple = 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 fill tuple\n");
-
- result = ExecProject(mergestate->jstate.cs_ProjInfo,
- &isDone);
-
- if (isDone != ExprEndResult)
- {
- mergestate->jstate.cs_TupFromTlist =
- (isDone == ExprMultipleResult);
- return result;
- }
- }
- }
-
- /*
- * now we get the next outer tuple, if any
- */
- outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
- mergestate->mj_OuterTupleSlot = outerTupleSlot;
- MJ_DEBUG_PROC_NODE(outerTupleSlot);
- mergestate->mj_MatchedOuter = false;
-
- /*
- * if the outer tuple is null then we are done with the
- * join, unless we have inner tuples we need to null-fill.
- */
- if (TupIsNull(outerTupleSlot))
- {
- MJ_printf("ExecMergeJoin: end of outer subplan\n");
- innerTupleSlot = mergestate->mj_InnerTupleSlot;
- if (doFillInner && !TupIsNull(innerTupleSlot))
- {
- /*
- * Need to emit right-join tuples for remaining
- * inner tuples.
- */
- mergestate->mj_JoinState = EXEC_MJ_ENDOUTER;
- break;
- }
- /* Otherwise we're done. */
- return NULL;
- }
-
- /*
- * otherwise test the new tuple against the skip qual.
- */
- mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_TEST;
- break;
-
- /*-----------------------------------------------------------
- * EXEC_MJ_SKIPINNER means skip over tuples in the inner plan
- * until we find an inner tuple >= current outer tuple.
- *
- * For example:
- *
- * 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.
- *
- * To avoid redundant tests, we divide this into three
- * sub-states: BEGIN, TEST, ADVANCE.
- *-------------------------------------------------------
- */
- case EXEC_MJ_SKIPINNER_BEGIN:
- MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_BEGIN\n");
-
- /*
- * before we advance, make sure the current tuples do not
- * satisfy the mergeclauses. If they do, then we update
- * the marked tuple and go join them.
- */
- ResetExprContext(econtext);
-
- outerTupleSlot = mergestate->mj_OuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_InnerTupleSlot;
- econtext->ecxt_innertuple = innerTupleSlot;
-
- qualResult = ExecQual(mergeclauses, econtext, false);
- MJ_DEBUG_QUAL(mergeclauses, qualResult);
-
- if (qualResult)
- {
- ExecMarkPos(innerPlan);
-
- MarkInnerTuple(innerTupleSlot, mergestate);
-
- mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
- break;
- }
-
- mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_TEST;
- break;
-
- case EXEC_MJ_SKIPINNER_TEST:
- MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_TEST\n");
-
- /*
- * ok, now test the skip qualification
- */
- outerTupleSlot = mergestate->mj_OuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_InnerTupleSlot;
- econtext->ecxt_innertuple = innerTupleSlot;
-
- compareResult = MergeCompare(mergeclauses,
- innerSkipQual,
- econtext);
-
- MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult);
-
- /*
- * compareResult is true as long as we should continue
- * skipping inner tuples.
- */
- if (compareResult)
- {
- mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE;
- break;
- }
-
- /*
- * now check the outer skip qual to see if we should now
- * skip outer tuples... if we fail the outer skip qual,
- * then we know we have a new pair of matching tuples.
- */
- compareResult = MergeCompare(mergeclauses,
- outerSkipQual,
- econtext);
-
- MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult);
-
- if (compareResult)
- mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_BEGIN;
- else
- mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
- break;
-
- /*
- * 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 && !mergestate->mj_MatchedInner)
- {
- /*
- * Generate a fake join tuple with nulls for the outer
- * tuple, and return it if it passes the non-join
- * quals.
- */
- mergestate->mj_MatchedInner = true; /* do it only once */
-
- ResetExprContext(econtext);
-
- outerTupleSlot = mergestate->mj_NullOuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_InnerTupleSlot;
- econtext->ecxt_innertuple = 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 fill tuple\n");
-
- result = ExecProject(mergestate->jstate.cs_ProjInfo,
- &isDone);
-
- if (isDone != ExprEndResult)
- {
- mergestate->jstate.cs_TupFromTlist =
- (isDone == ExprMultipleResult);
- return result;
- }
- }
- }
-
- /*
- * now we get the next inner tuple, if any
- */
- innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
- mergestate->mj_InnerTupleSlot = innerTupleSlot;
- MJ_DEBUG_PROC_NODE(innerTupleSlot);
- mergestate->mj_MatchedInner = false;
-
- /*
- * if the inner tuple is null then we are done with the
- * join, unless we have outer tuples we need to null-fill.
- */
- if (TupIsNull(innerTupleSlot))
- {
- MJ_printf("ExecMergeJoin: end of inner subplan\n");
- outerTupleSlot = mergestate->mj_OuterTupleSlot;
- if (doFillOuter && !TupIsNull(outerTupleSlot))
- {
- /*
- * Need to emit left-join tuples for remaining
- * outer tuples.
- */
- mergestate->mj_JoinState = EXEC_MJ_ENDINNER;
- break;
- }
- /* Otherwise we're done. */
- return NULL;
- }
-
- /*
- * otherwise test the new tuple against the skip qual.
- */
- mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_TEST;
- 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 remaing unmatched inner tuples.
- */
- case EXEC_MJ_ENDOUTER:
- MJ_printf("ExecMergeJoin: EXEC_MJ_ENDOUTER\n");
-
- Assert(doFillInner);
-
- if (!mergestate->mj_MatchedInner)
- {
- /*
- * Generate a fake join tuple with nulls for the outer
- * tuple, and return it if it passes the non-join
- * quals.
- */
- mergestate->mj_MatchedInner = true; /* do it only once */
-
- ResetExprContext(econtext);
-
- outerTupleSlot = mergestate->mj_NullOuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_InnerTupleSlot;
- econtext->ecxt_innertuple = 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 fill tuple\n");
-
- result = ExecProject(mergestate->jstate.cs_ProjInfo,
- &isDone);
-
- if (isDone != ExprEndResult)
- {
- mergestate->jstate.cs_TupFromTlist =
- (isDone == ExprMultipleResult);
- return result;
- }
- }
- }
-
- /*
- * now we get the next inner tuple, if any
- */
- innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
- mergestate->mj_InnerTupleSlot = innerTupleSlot;
- MJ_DEBUG_PROC_NODE(innerTupleSlot);
- mergestate->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 remaing unmatched outer tuples.
- */
- case EXEC_MJ_ENDINNER:
- MJ_printf("ExecMergeJoin: EXEC_MJ_ENDINNER\n");
-
- Assert(doFillOuter);
-
- if (!mergestate->mj_MatchedOuter)
- {
- /*
- * Generate a fake join tuple with nulls for the inner
- * tuple, and return it if it passes the non-join
- * quals.
- */
- mergestate->mj_MatchedOuter = true; /* do it only once */
-
- ResetExprContext(econtext);
-
- outerTupleSlot = mergestate->mj_OuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_NullInnerTupleSlot;
- econtext->ecxt_innertuple = 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 fill tuple\n");
-
- result = ExecProject(mergestate->jstate.cs_ProjInfo,
- &isDone);
-
- if (isDone != ExprEndResult)
- {
- mergestate->jstate.cs_TupFromTlist =
- (isDone == ExprMultipleResult);
- return result;
- }
- }
- }
-
- /*
- * now we get the next outer tuple, if any
- */
- outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
- mergestate->mj_OuterTupleSlot = outerTupleSlot;
- MJ_DEBUG_PROC_NODE(outerTupleSlot);
- mergestate->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;
-
- /*
- * if we get here it means our code is fouled up and so we
- * just end the join prematurely.
- */
- default:
- elog(WARNING, "ExecMergeJoin: invalid join state %d, aborting",
- mergestate->mj_JoinState);
- return NULL;
- }
- }
-}
-
-/* ----------------------------------------------------------------
- * ExecInitMergeJoin
- *
- * old comments
- * Creates the run-time state information for the node and
- * sets the relation id to contain relevant decriptors.
- * ----------------------------------------------------------------
- */
-bool
-ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
-{
- MergeJoinState *mergestate;
-
- MJ1_printf("ExecInitMergeJoin: %s\n",
- "initializing node");
-
- /*
- * assign the node's execution state and get the range table and
- * direction from it
- */
- node->join.plan.state = estate;
-
- /*
- * create new merge state for node
- */
- mergestate = makeNode(MergeJoinState);
- node->mergestate = mergestate;
-
- /*
- * Miscellaneous initialization
- *
- * create expression context for node
- */
- ExecAssignExprContext(estate, &mergestate->jstate);
-
- /*
- * initialize subplans
- */
- ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node);
- ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node);
-
-#define MERGEJOIN_NSLOTS 4
-
- /*
- * tuple table initialization
- */
- ExecInitResultTupleSlot(estate, &mergestate->jstate);
-
- mergestate->mj_MarkedTupleSlot = ExecInitExtraTupleSlot(estate);
- ExecSetSlotDescriptor(mergestate->mj_MarkedTupleSlot,
- ExecGetTupType(innerPlan((Plan *) node)),
- false);
-
- switch (node->join.jointype)
- {
- case JOIN_INNER:
- break;
- case JOIN_LEFT:
- mergestate->mj_NullInnerTupleSlot =
- ExecInitNullTupleSlot(estate,
- ExecGetTupType(innerPlan((Plan *) node)));
- break;
- case JOIN_RIGHT:
- mergestate->mj_NullOuterTupleSlot =
- ExecInitNullTupleSlot(estate,
- ExecGetTupType(outerPlan((Plan *) node)));
-
- /*
- * Can't handle right or full join with non-nil extra
- * joinclauses.
- */
- if (node->join.joinqual != NIL)
- elog(ERROR, "RIGHT JOIN is only supported with mergejoinable join conditions");
- break;
- case JOIN_FULL:
- mergestate->mj_NullOuterTupleSlot =
- ExecInitNullTupleSlot(estate,
- ExecGetTupType(outerPlan((Plan *) node)));
- mergestate->mj_NullInnerTupleSlot =
- ExecInitNullTupleSlot(estate,
- ExecGetTupType(innerPlan((Plan *) node)));
-
- /*
- * Can't handle right or full join with non-nil extra
- * joinclauses.
- */
- if (node->join.joinqual != NIL)
- elog(ERROR, "FULL JOIN is only supported with mergejoinable join conditions");
- break;
- default:
- elog(ERROR, "ExecInitMergeJoin: unsupported join type %d",
- (int) node->join.jointype);
- }
-
- /*
- * initialize tuple type and projection info
- */
- ExecAssignResultTypeFromTL((Plan *) node, &mergestate->jstate);
- ExecAssignProjectionInfo((Plan *) node, &mergestate->jstate);
-
- /*
- * form merge skip qualifications
- */
- MJFormSkipQuals(node->mergeclauses,
- &mergestate->mj_OuterSkipQual,
- &mergestate->mj_InnerSkipQual);
-
- MJ_printf("\nExecInitMergeJoin: OuterSkipQual is ");
- MJ_nodeDisplay(mergestate->mj_OuterSkipQual);
- MJ_printf("\nExecInitMergeJoin: InnerSkipQual is ");
- MJ_nodeDisplay(mergestate->mj_InnerSkipQual);
- MJ_printf("\n");
-
- /*
- * initialize join state
- */
- mergestate->mj_JoinState = EXEC_MJ_INITIALIZE;
- mergestate->jstate.cs_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 TRUE;
-}
-
-int
-ExecCountSlotsMergeJoin(MergeJoin *node)
-{
- return ExecCountSlotsNode(outerPlan((Plan *) node)) +
- ExecCountSlotsNode(innerPlan((Plan *) node)) +
- MERGEJOIN_NSLOTS;
-}
-
-/* ----------------------------------------------------------------
- * ExecEndMergeJoin
- *
- * old comments
- * frees storage allocated through C routines.
- * ----------------------------------------------------------------
- */
-void
-ExecEndMergeJoin(MergeJoin *node)
-{
- MergeJoinState *mergestate;
-
- MJ1_printf("ExecEndMergeJoin: %s\n",
- "ending node processing");
-
- /*
- * get state information from the node
- */
- mergestate = node->mergestate;
-
- /*
- * Free the projection info and the scan attribute info
- *
- * Note: we don't ExecFreeResultType(mergestate) because the rule manager
- * depends on the tupType returned by ExecMain(). So for now, this is
- * freed at end-transaction time. -cim 6/2/91
- */
- ExecFreeProjectionInfo(&mergestate->jstate);
- ExecFreeExprContext(&mergestate->jstate);
-
- /*
- * shut down the subplans
- */
- ExecEndNode((Plan *) innerPlan((Plan *) node), (Plan *) node);
- ExecEndNode((Plan *) outerPlan((Plan *) node), (Plan *) node);
-
- /*
- * clean out the tuple table
- */
- ExecClearTuple(mergestate->jstate.cs_ResultTupleSlot);
- ExecClearTuple(mergestate->mj_MarkedTupleSlot);
-
- MJ1_printf("ExecEndMergeJoin: %s\n",
- "node processing ended");
-}
-
-void
-ExecReScanMergeJoin(MergeJoin *node, ExprContext *exprCtxt, Plan *parent)
-{
- MergeJoinState *mergestate = node->mergestate;
-
- ExecClearTuple(mergestate->mj_MarkedTupleSlot);
-
- mergestate->mj_JoinState = EXEC_MJ_INITIALIZE;
- mergestate->jstate.cs_TupFromTlist = false;
- mergestate->mj_MatchedOuter = false;
- mergestate->mj_MatchedInner = false;
- mergestate->mj_OuterTupleSlot = NULL;
- mergestate->mj_InnerTupleSlot = NULL;
-
- /*
- * if chgParam of subnodes is not null then plans will be re-scanned
- * by first ExecProcNode.
- */
- if (((Plan *) node)->lefttree->chgParam == NULL)
- ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
- if (((Plan *) node)->righttree->chgParam == NULL)
- ExecReScan(((Plan *) node)->righttree, exprCtxt, (Plan *) node);
-
-}