home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / docs / apps / database / postgres / postgre4.z / postgre4 / src / executor / n_mergejoin.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-08-27  |  36.8 KB  |  1,262 lines

  1. /* ----------------------------------------------------------------
  2.  *   FILE
  3.  *    mergejoin.c
  4.  *    
  5.  *   DESCRIPTION
  6.  *    routines supporting merge joins
  7.  *   
  8.  *   INTERFACE ROUTINES
  9.  *       ExecMergeJoin         mergejoin outer and inner relations.
  10.  *       ExecInitMergeJoin    creates and initializes run time states
  11.  *       ExecEndMergeJoin      cleand up the node.
  12.  *
  13.  *   NOTES
  14.  *    Essential operation of the merge join algorithm is as follows:
  15.  *    (** indicates the tuples satisify the merge clause).
  16.  *
  17.  *    Join {                               -
  18.  *        get initial outer and inner tuples            INITIALIZE
  19.  *        Skip Inner                        SKIPINNER
  20.  *        mark inner position                    JOINMARK
  21.  *        do forever {                       -
  22.  *            while (outer ** inner) {            JOINTEST
  23.  *            join tuples                    JOINTUPLES
  24.  *                advance inner position            NEXTINNER
  25.  *            }                           -
  26.  *            advance outer position                NEXTOUTER
  27.  *            if (outer ** mark) {                TESTOUTER
  28.  *                  restore inner position to mark          TESTOUTER
  29.  *                continue                                   -
  30.  *            } else {                       -
  31.  *                Skip Outer                        SKIPOUTER
  32.  *            mark inner position                JOINMARK
  33.  *            }                           -
  34.  *        }                               -
  35.  *      }                               -
  36.  *
  37.  *      Skip Outer {                        SKIPOUTER
  38.  *        if (inner ** outer)    Join Tuples            JOINTUPLES
  39.  *        while (outer < inner)                SKIPOUTER
  40.  *        advance outer                    SKIPOUTER
  41.  *        if (outer > inner)                    SKIPOUTER
  42.  *            Skip Inner                    SKIPINNER
  43.  *    }                               -
  44.  *
  45.  *    Skip Inner {                        SKIPINNER
  46.  *        if (inner ** outer)    Join Tuples            JOINTUPLES
  47.  *        while (outer > inner)                SKIPINNER
  48.  *            advance inner                    SKIPINNER
  49.  *        if (outer < inner)                    SKIPINNER
  50.  *            Skip Outer                    SKIPOUTER
  51.  *    }                               -
  52.  *
  53.  *    Currently, the merge join operation is coded in the fashion
  54.  *    of a state machine.  At each state, we do something and then
  55.  *    proceed to another state.  This state is stored in the node's
  56.  *    execution state information and is preserved across calls to
  57.  *    ExecMergeJoin. -cim 10/31/89
  58.  *
  59.  *    Warning:  This code is known to fail for inequality operations
  60.  *          and is being redesigned.  Specifically, = and > work
  61.  *          but the logic is not correct for <.  Since mergejoins
  62.  *          are no better then nestloops for inequalitys, the planner
  63.  *          should not plan them anyways.  Alternatively, the
  64.  *          planner could just exchange the inner/outer relations
  65.  *          if it ever sees a <... -cim 7/1/90
  66.  *
  67.  *    Update:      The executor tuple table has long since alleviated the
  68.  *          problem described above -cim 4/23/91
  69.  *
  70.  *   IDENTIFICATION
  71.  *    $Header: /private/postgres/src/executor/RCS/n_mergejoin.c,v 1.7 1992/08/04 17:37:56 mer Exp $
  72.  * ----------------------------------------------------------------
  73.  */
  74.  
  75. #include "executor/executor.h"
  76.  
  77.  RcsId("$Header: /private/postgres/src/executor/RCS/n_mergejoin.c,v 1.7 1992/08/04 17:37:56 mer Exp $");
  78.  
  79. /* ----------------------------------------------------------------
  80.  *    MarkInnerTuple and RestoreInnerTuple macros
  81.  *
  82.  *    when we "mark" a tuple, we place a pointer to it
  83.  *      in the marked tuple slot.  now there are two pointers
  84.  *      to this tuple and we don't want it to be freed until
  85.  *      next time we mark a tuple, so we move the policy to
  86.  *      the marked tuple slot and set the inner tuple slot policy
  87.  *      to false.
  88.  *
  89.  *    But, when we restore the inner tuple, the marked tuple
  90.  *    retains the policy.  Basically once a tuple is marked, it
  91.  *    should only be freed when we mark another tuple.  -cim 9/27/90
  92.  *
  93.  *    Note:  now that we store buffers in the tuple table,
  94.  *           we have to also increment buffer reference counts
  95.  *           correctly whenever we propagate an additional pointer
  96.  *           to a buffer item.  Later, when ExecStoreTuple() is
  97.  *           called again on this slot, the refcnt is decremented
  98.  *           when the old tuple is replaced.
  99.  * ----------------------------------------------------------------
  100.  */
  101. #define MarkInnerTuple(innerTupleSlot, mergestate) \
  102. { \
  103.     bool       shouldFree; \
  104.     shouldFree = ExecSetSlotPolicy((Pointer) innerTupleSlot, false); \
  105.     ExecStoreTuple(ExecFetchTuple((Pointer)innerTupleSlot), \
  106.            (Pointer) get_mj_MarkedTupleSlot(mergestate), \
  107.            ExecSlotBuffer((Pointer) innerTupleSlot), \
  108.            shouldFree); \
  109.     ExecIncrSlotBufferRefcnt((Pointer) innerTupleSlot); \
  110. }
  111.  
  112. #define RestoreInnerTuple(innerTupleSlot, markedTupleSlot) \
  113.     ExecStoreTuple(ExecFetchTuple(markedTupleSlot), \
  114.            innerTupleSlot, \
  115.            ExecSlotBuffer(markedTupleSlot), \
  116.            false); \
  117.     ExecIncrSlotBufferRefcnt(innerTupleSlot)
  118.  
  119. /* ----------------------------------------------------------------
  120.  *       MJFormOSortopI
  121.  *
  122.  *    This takes the mergeclause which is a qualification of the
  123.  *    form ((= expr expr) (= expr expr) ...) and forms a new
  124.  *    qualification like ((> expr expr) (> expr expr) ...) which
  125.  *    is used by ExecMergeJoin() in order to determine if we should
  126.  *    skip tuples.
  127.  *
  128.  * old comments
  129.  *       The 'qual' must be of the form:
  130.  *          {(= outerkey1 innerkey1)(= outerkey2 innerkey2) ...}
  131.  *       The "sortOp outerkey innerkey" is formed by substituting the "="
  132.  *       by "sortOp".
  133.  * ----------------------------------------------------------------
  134.  */
  135.  
  136. /**** xxref:
  137.  *           MJFormISortopO
  138.  *           ExecInitMergeJoin
  139.  ****/
  140. List
  141. MJFormOSortopI(qualList, sortOp)
  142.     List     qualList;
  143.     ObjectId     sortOp;
  144. {
  145.     List qualCopy;
  146.     List qualcdr;
  147.     List qual;
  148.     Oper op;
  149.     Oper newop;
  150.     
  151.     /* ----------------
  152.      *  qualList is a list: ((op .. ..) ...)
  153.      *    first we make a copy of it.  CopyObject() makes a deep copy 
  154.      *  so let's use it instead of the old fashoned lispCopy()...
  155.      * ----------------
  156.      */
  157.     qualCopy = (List) CopyObject(qualList);
  158.     
  159.     foreach (qualcdr, qualCopy) {
  160.     /* ----------------
  161.      *   first get the current (op .. ..) list
  162.      * ----------------
  163.      */
  164.     qual = CAR(qualcdr);
  165.     
  166.     /* ----------------
  167.      *   now get at the op
  168.      * ----------------
  169.      */
  170.     op = (Oper) CAR(qual);
  171.     if (!ExactNodeType(op,Oper)) {
  172.         elog(DEBUG, "MJFormOSortopI: op not an Oper!");
  173.         return LispNil;
  174.     }
  175.     
  176.     /* ----------------
  177.      *   change it's opid and since Op nodes now carry around a
  178.      *   cached pointer to the associated op function, we have
  179.      *   to make sure we invalidate this.  Otherwise you get bizarre
  180.      *   behavior when someone runs a mergejoin with _exec_repeat_ > 1
  181.      *   -cim 4/23/91
  182.      * ----------------
  183.      */
  184.     set_opid(op, sortOp);
  185.     set_op_fcache(op, NULL);
  186.     }
  187.     
  188.     return qualCopy;
  189. }
  190.  
  191. /* ----------------------------------------------------------------
  192.  *       MJFormISortopO
  193.  *
  194.  *    This does the same thing as MJFormOSortopI() except that
  195.  *    it also reverses the expressions in the qualifications.
  196.  *    For example: ((= expr1 expr2)) produces ((> expr2 expr1))
  197.  *
  198.  * old comments
  199.  *       The 'qual' must be of the form:
  200.  *          {(= outerkey1 innerkey1) (= outerkey2 innerkey2) ...}
  201.  *       The 'sortOp innerkey1 outerkey" is formed by substituting the "="
  202.  *       by "sortOp" and reversing the positions of the keys.
  203.  *  ----------------------------------------------------------------
  204.  */
  205.  
  206. /**** xxref:
  207.  *           ExecInitMergeJoin
  208.  ****/
  209. List
  210. MJFormISortopO(qualList,sortOp)
  211.     List     qualList;
  212.     ObjectId     sortOp;
  213. {
  214.     List     ISortopO;
  215.     List     qualcdr;
  216.     
  217.     /* ----------------
  218.      *    first generate OSortopI, a list of the form
  219.      *  ((op outer inner) (op outer inner) ... )
  220.      * ----------------
  221.      */
  222.     ISortopO = MJFormOSortopI(qualList, sortOp);
  223.     
  224.     /* ----------------
  225.      *    now swap the cadr and caddr of each qual to form ISortopO,
  226.      *  ((op inner outer) (op inner outer) ... )
  227.      * ----------------
  228.      */
  229.     foreach (qualcdr, ISortopO) {
  230.     List qual;
  231.     List inner;
  232.     List outer;
  233.     qual =  CAR(qualcdr);
  234.     
  235.     inner = CAR(CDR(qual));
  236.     outer = CAR(CDR(CDR(qual)));
  237.     CAR(CDR(qual)) = outer;
  238.     CAR(CDR(CDR(qual))) = inner;
  239.     }
  240.     
  241.     return ISortopO;
  242. }
  243.  
  244. /* ----------------------------------------------------------------
  245.  *       MergeCompare
  246.  *   
  247.  *       Compare the keys according to 'compareQual' which is of the 
  248.  *       form: {(key1a > key2a)(key1b > key2b) ...}.
  249.  *
  250.  *       (actually, it could also be the form (key1a < key2a)..)
  251.  *   
  252.  *       This is different from calling ExecQual because ExecQual returns
  253.  *       true only if ALL the comparisions clauses are satisfied.
  254.  *       However, there is an order of significance among the keys with
  255.  *       the first keys being most significant. Therefore, the clauses
  256.  *       are evaluated in order and the 'compareQual' is satisfied
  257.  *       if (key1i > key2i) is true and (key1j = key2j) for 0 < j < i.
  258.  * ----------------------------------------------------------------
  259.  */
  260.  
  261. /**** xxref:
  262.  *           ExecMergeJoin
  263.  ****/
  264. bool
  265. MergeCompare(eqQual, compareQual, econtext)
  266.     List     eqQual;
  267.     List     compareQual;
  268.     ExprContext econtext;
  269. {
  270.     List   clause;
  271.     List   eqclause;
  272.     Const  expr_value;
  273.     Datum  const_value;
  274.     Boolean isNull;
  275.     Boolean isDone;
  276.     
  277.     /* ----------------
  278.      *    if we have no compare qualification, return nil
  279.      * ----------------
  280.      */
  281.     if (lispNullp(compareQual))
  282.     return false;
  283.     
  284.     /* ----------------
  285.      *    for each pair of clauses, test them until
  286.      *  our compare conditions are satisified
  287.      * ----------------
  288.      */
  289.     eqclause = eqQual;
  290.     foreach (clause, compareQual) {
  291.     /* ----------------
  292.      *   first test if our compare clause is satisified.
  293.      *   if so then return true. ignore isDone, don't iterate in
  294.      *   quals.
  295.      * ----------------
  296.      */
  297.     const_value = (Datum)
  298.         ExecEvalExpr((Node) CAR(clause), econtext, &isNull, &isDone);
  299.     
  300.     if (ExecCTrue(DatumGetInt32(const_value)))
  301.         return true;
  302.     
  303.     /* ----------------
  304.      *   ok, the compare clause failed so we test if the keys
  305.      *   are equal... if key1 != key2, we return false.
  306.      *   otherwise key1 = key2 so we move on to the next pair of keys.
  307.      *
  308.      *   ignore isDone, don't iterate in quals.
  309.      * ----------------
  310.      */
  311.     const_value = ExecEvalExpr((Node) CAR(eqclause),
  312.                    econtext,
  313.                    &isNull,
  314.                    &isDone);
  315.     
  316.     if (! ExecCTrue(DatumGetInt32(const_value)))
  317.         return false;
  318.     eqclause = CDR(eqclause);
  319.     }
  320.     
  321.     /* ----------------
  322.      *    if we get here then it means none of our key greater-than
  323.      *  conditions were satisified so we return false.
  324.      * ----------------
  325.      */
  326.     return false;
  327. }
  328.  
  329. /* ----------------------------------------------------------------
  330.  *    ExecMergeTupleDump
  331.  *
  332.  *    This function is called through the MJ_dump() macro
  333.  *    when EXEC_MERGEJOINDEBUG is defined
  334.  * ----------------------------------------------------------------
  335.  */
  336.  
  337. void
  338. ExecMergeTupleDumpInner(econtext)
  339.     ExprContext    econtext;
  340. {
  341.     TupleTableSlot innerSlot;
  342.  
  343.     printf("==== inner tuple ====\n");
  344.     innerSlot = get_ecxt_innertuple(econtext);
  345.     if (TupIsNull((Pointer) innerSlot))
  346.     printf("(nil)\n");
  347.     else
  348.     debugtup((HeapTuple) ExecFetchTuple((Pointer) innerSlot),
  349.          (struct attribute **)
  350.          ExecSlotDescriptor((Pointer)innerSlot)); /* bug -- glass */
  351. }
  352.  
  353. void
  354. ExecMergeTupleDumpOuter(econtext)
  355.     ExprContext    econtext;
  356. {
  357.     TupleTableSlot outerSlot;
  358.  
  359.     printf("==== outer tuple ====\n");
  360.     outerSlot = get_ecxt_outertuple(econtext);
  361.     if (TupIsNull((Pointer) outerSlot))
  362.     printf("(nil)\n");
  363.     else
  364.     debugtup((HeapTuple) ExecFetchTuple((Pointer)
  365.                 outerSlot),
  366.          (struct attribute **) ExecSlotDescriptor((Pointer) outerSlot));
  367. }
  368.  
  369. void
  370. ExecMergeTupleDumpMarked(econtext, mergestate)
  371.     ExprContext    econtext;
  372.     MergeJoinState mergestate;
  373. {
  374.     TupleTableSlot markedSlot;
  375.  
  376.     printf("==== marked tuple ====\n");
  377.     markedSlot = get_mj_MarkedTupleSlot(mergestate);
  378.  
  379.     if (TupIsNull((Pointer)markedSlot))
  380.     printf("(nil)\n");
  381.     else
  382.     debugtup((HeapTuple)ExecFetchTuple((Pointer)markedSlot),
  383.          (struct attribute **)ExecSlotDescriptor((Pointer)markedSlot));
  384. }
  385.  
  386. void
  387. ExecMergeTupleDump(econtext, mergestate)
  388.     ExprContext    econtext;
  389.     MergeJoinState mergestate;
  390. {
  391.     printf("******** ExecMergeTupleDump ********\n");
  392.     
  393.     ExecMergeTupleDumpInner(econtext);
  394.     ExecMergeTupleDumpOuter(econtext);
  395.     ExecMergeTupleDumpMarked(econtext, mergestate);
  396.     
  397.     printf("******** \n");
  398. }
  399.  
  400. /* ----------------------------------------------------------------
  401.  *       ExecMergeJoin
  402.  *
  403.  * old comments
  404.  *       Details of the merge-join routines:
  405.  *       
  406.  *       (1) ">" and "<" operators
  407.  *   
  408.  *       Merge-join is done by joining the inner and outer tuples satisfying 
  409.  *       the join clauses of the form ((= outerKey innerKey) ...).
  410.  *       The join clauses is provided by the query planner and may contain
  411.  *       more than one (= outerKey innerKey) clauses (for composite key).
  412.  *   
  413.  *       However, the query executor needs to know whether an outer
  414.  *       tuple is "greater/smaller" than an inner tuple so that it can
  415.  *       "synchronize" the two relations. For e.g., consider the following
  416.  *       relations:
  417.  *   
  418.  *           outer: (0 ^1 1 2 5 5 5 6 6 7)    current tuple: 1
  419.  *            inner: (1 ^3 5 5 5 5 6)            current tuple: 3
  420.  *   
  421.  *       To continue the merge-join, the executor needs to scan both inner
  422.  *       and outer relations till the matching tuples 5. It needs to know
  423.  *       that currently inner tuple 3 is "greater" than outer tuple 1 and
  424.  *       therefore it should scan the outer relation first to find a 
  425.  *       matching tuple and so on.
  426.  *       
  427.  *       Therefore, when initializing the merge-join node, the executor
  428.  *       creates the "greater/smaller" clause by substituting the "=" 
  429.  *       operator in the join clauses with the sort operator used to
  430.  *       sort the outer and inner relation forming (outerKey sortOp innerKey).
  431.  *       The sort operator is "<" if the relations are in ascending order  
  432.  *       otherwise, it is ">" if the relations are in descending order.
  433.  *       The opposite "smaller/greater" clause is formed by reversing the
  434.  *       outer and inner keys forming (innerKey sortOp outerKey).
  435.  *   
  436.  *       (2) repositioning inner "cursor"
  437.  *   
  438.  *       Consider the above relations and suppose that the executor has
  439.  *       just joined the first outer "5" with the last inner "5". The
  440.  *       next step is of course to join the second outer "5" with all
  441.  *       the inner "5's". This requires repositioning the inner "cursor"
  442.  *       to point at the first inner "5". This is done by "marking" the
  443.  *       first inner 5 and restore the "cursor" to it before joining
  444.  *       with the second outer 5. The access method interface provides
  445.  *       routines to mark and restore to a tuple.
  446.  * ----------------------------------------------------------------
  447.  */
  448.  
  449. /**** xxref:
  450.  *           ExecMergeJoin
  451.  *           ExecProcNode
  452.  ****/
  453. TupleTableSlot
  454. ExecMergeJoin(node)
  455.     MergeJoin     node;
  456. {
  457.     EState       estate;
  458.     MergeJoinState mergestate;
  459.     ScanDirection  direction;
  460.     List       innerSkipQual;
  461.     List       outerSkipQual;
  462.     List       mergeclauses;
  463.     List       qual;
  464.     bool       qualResult;    
  465.     bool       compareResult;
  466.     
  467.     Plan       innerPlan;
  468.     TupleTableSlot innerTupleSlot;
  469.     
  470.     Plan       outerPlan;
  471.     TupleTableSlot outerTupleSlot;
  472.     
  473.     TupleTableSlot markedTupleSlot;
  474.     
  475.     ExprContext       econtext;
  476.     List       targetList;
  477.     int           len;
  478.     TupleDescriptor tupType;
  479.     Pointer       tupValue;
  480.     
  481.     HeapTuple       resultTuple;
  482.     TupleTableSlot resultSlot;
  483.     
  484.     /* ----------------
  485.      *    get information from node
  486.      * ----------------
  487.      */
  488.     mergestate =   get_mergestate(node);
  489.     estate = (EState) get_state((Plan) node);
  490.     direction =    get_es_direction(estate);
  491.     innerPlan =    get_innerPlan((Plan) node);
  492.     outerPlan =    get_outerPlan((Plan) node);
  493.     econtext =     get_cs_ExprContext((CommonState) mergestate);
  494.     mergeclauses = get_mergeclauses(node);
  495.     qual =        get_qpqual((Plan) node);
  496.     
  497.     if (direction == EXEC_FRWD) {
  498.     outerSkipQual = get_mj_OSortopI(mergestate);
  499.     innerSkipQual = get_mj_ISortopO(mergestate);
  500.     } else {
  501.     outerSkipQual = get_mj_ISortopO(mergestate);
  502.     innerSkipQual = get_mj_OSortopI(mergestate);
  503.     }
  504.  
  505.     /* ----------------
  506.      *    ok, everything is setup.. let's go to work
  507.      * ----------------
  508.      */
  509.     if (get_cs_TupFromTlist((CommonState)mergestate)) {
  510.     TupleTableSlot result;
  511.     ProjectionInfo projInfo;
  512.     bool           isDone;
  513.  
  514.     projInfo = get_cs_ProjInfo((CommonState)mergestate);
  515.     result   = ExecProject(projInfo, &isDone);
  516.     if (!isDone)
  517.         return result;
  518.     }
  519.     for (;;) {
  520.     /* ----------------
  521.      *  get the current state of the join and do things accordingly.
  522.      *  Note: The join states are highlighted with 32-* comments for
  523.      *        improved readability.
  524.      * ----------------
  525.      */
  526.     MJ_dump(econtext, mergestate);
  527.     
  528.     switch (get_mj_JoinState(mergestate)) {
  529.     /* ********************************
  530.      *    EXEC_MJ_INITIALIZE means that this is the first time
  531.      *    ExecMergeJoin() has been called and so we have to
  532.      *    initialize the inner, outer and marked tuples as well
  533.      *    as various stuff in the expression context.
  534.      * ********************************
  535.      */
  536.     case EXEC_MJ_INITIALIZE:
  537.         MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE\n");
  538.         /* ----------------
  539.          *   Note: at this point, if either of our inner or outer
  540.          *   tuples are nil, then the join ends immediately because
  541.          *   we know one of the subplans is empty.
  542.          * ----------------
  543.          */
  544.         innerTupleSlot = ExecProcNode(innerPlan);
  545.         if (TupIsNull((Pointer)innerTupleSlot)) {
  546.         MJ_printf("ExecMergeJoin: **** inner tuple is nil ****\n");
  547.         return NULL;
  548.         }
  549.         
  550.         outerTupleSlot = ExecProcNode(outerPlan);
  551.         if (TupIsNull((Pointer)outerTupleSlot)) {
  552.         MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n");
  553.         return NULL;
  554.         }
  555.         
  556.         /* ----------------
  557.          *   store the inner and outer tuple in the merge state
  558.          * ----------------
  559.          */
  560.         set_ecxt_innertuple(econtext, innerTupleSlot);
  561.         set_ecxt_outertuple(econtext, outerTupleSlot);
  562.         
  563.         /* ----------------
  564.          *     set the marked tuple to nil
  565.          *   and initialize its tuple descriptor atttributes. 
  566.          *      -jeff 10 july 1991
  567.          * ----------------
  568.          */
  569.         ExecClearTuple((Pointer)get_mj_MarkedTupleSlot(mergestate));
  570.         SetSlotTupleDescriptor(get_mj_MarkedTupleSlot(mergestate), 
  571.             SlotTupleDescriptor(innerTupleSlot));
  572.         SetSlotExecTupDescriptor(get_mj_MarkedTupleSlot(mergestate), 
  573.             SlotExecTupDescriptor(innerTupleSlot));
  574.         
  575.         /* ----------------
  576.          *  initialize merge join state to skip inner tuples.
  577.          * ----------------
  578.          */
  579.         set_mj_JoinState(mergestate, EXEC_MJ_SKIPINNER);
  580.         break;
  581.         
  582.     /* ********************************
  583.      *    EXEC_MJ_JOINMARK means we have just found a new
  584.      *      outer tuple and a possible matching inner tuple.
  585.      *      This is the case after the INITIALIZE, SKIPOUTER
  586.      *      or SKIPINNER states.
  587.      * ********************************
  588.      */
  589.     case EXEC_MJ_JOINMARK:
  590.         MJ_printf("ExecMergeJoin: EXEC_MJ_JOINMARK\n");
  591.         ExecMarkPos(innerPlan);
  592.         
  593.         innerTupleSlot = get_ecxt_innertuple(econtext);
  594.         MarkInnerTuple(innerTupleSlot, mergestate);
  595.         
  596.         set_mj_JoinState(mergestate, EXEC_MJ_JOINTEST);
  597.         break;
  598.         
  599.     /* ********************************
  600.      *    EXEC_MJ_JOINTEST means we have two tuples which
  601.      *      might satisify the merge clause, so we test them.
  602.      *
  603.      *      If they do satisify, then we join them and move
  604.      *      on to the next inner tuple (EXEC_MJ_JOINTUPLES).
  605.      *
  606.      *    If they do not satisify then advance to next outer tuple.
  607.      * ********************************
  608.      */
  609.     case EXEC_MJ_JOINTEST:
  610.         MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n");
  611.         
  612.         qualResult = ExecQual(mergeclauses, econtext);
  613.         MJ_DEBUG_QUAL(mergeclauses, qualResult);
  614.         
  615.         if (qualResult)
  616.         {
  617.         set_mj_JoinState(mergestate, EXEC_MJ_JOINTUPLES);
  618.         }
  619.         else 
  620.         {
  621.         set_mj_JoinState(mergestate, EXEC_MJ_NEXTOUTER);
  622.         }
  623.         break;
  624.         
  625.     /* ********************************
  626.      *    EXEC_MJ_JOINTUPLES means we have two tuples which
  627.      *      satisified the merge clause so we join them and then
  628.      *     proceed to get the next inner tuple (EXEC_NEXT_INNER).
  629.      * ********************************
  630.      */
  631.     case EXEC_MJ_JOINTUPLES:
  632.         MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");
  633.         set_mj_JoinState(mergestate, EXEC_MJ_NEXTINNER);
  634.         
  635.         qualResult = ExecQual(qual, econtext);
  636.         MJ_DEBUG_QUAL(qual, qualResult);
  637.         
  638.         if (qualResult) {
  639.         /* ----------------
  640.          *  qualification succeeded.  now form the desired
  641.          *  projection tuple and return the slot containing it.
  642.          * ----------------
  643.          */
  644.         ProjectionInfo projInfo;
  645.         TupleTableSlot result;
  646.         bool           isDone;
  647.         
  648.         MJ_printf("ExecMergeJoin: **** returning tuple ****\n");
  649.         
  650.         projInfo = get_cs_ProjInfo((CommonState) mergestate);
  651.         
  652.         result = ExecProject(projInfo, &isDone);
  653.         set_cs_TupFromTlist((CommonState)mergestate, !isDone);
  654.         return result;
  655.         }
  656.         break;
  657.         
  658.     /* ********************************
  659.      *    EXEC_MJ_NEXTINNER means advance the inner scan
  660.      *      to the next tuple.  If the tuple is not nil, we then
  661.      *      proceed to test it against the join qualification.
  662.      * ********************************
  663.      */
  664.     case EXEC_MJ_NEXTINNER:
  665.         MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\n");
  666.         
  667.         /* ----------------
  668.          *    now we get the next inner tuple, if any
  669.          * ----------------
  670.          */
  671.         innerTupleSlot = ExecProcNode(innerPlan);
  672.         MJ_DEBUG_PROC_NODE(innerTupleSlot);
  673.         set_ecxt_innertuple(econtext, innerTupleSlot);
  674.         
  675.         if (TupIsNull((Pointer) innerTupleSlot))
  676.         {
  677.         set_mj_JoinState(mergestate, EXEC_MJ_NEXTOUTER);
  678.         }
  679.         else
  680.         {
  681.         set_mj_JoinState(mergestate, EXEC_MJ_JOINTEST);
  682.         }
  683.         break;
  684.         
  685.     /* ********************************
  686.      *    EXEC_MJ_NEXTOUTER means
  687.      *
  688.      *          outer    inner
  689.      *   outer tuple -  5      5  - marked tuple    
  690.      *            5     5
  691.      *            6     6  - inner tuple
  692.      *            7     7
  693.      *
  694.      *    we know we just bumped into
  695.      *    the first inner tuple > current outer tuple
  696.      *      so get a new outer tuple and then proceed to test
  697.      *    it against the marked tuple (EXEC_MJ_TESTOUTER)
  698.      * ********************************
  699.      */
  700.     case EXEC_MJ_NEXTOUTER:
  701.         MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n");
  702.         
  703.         outerTupleSlot = ExecProcNode(outerPlan);
  704.         MJ_DEBUG_PROC_NODE(outerTupleSlot);
  705.         set_ecxt_outertuple(econtext, outerTupleSlot);
  706.         
  707.         /* ----------------
  708.          *  if the outer tuple is null then we know
  709.          *  we are done with the join
  710.          * ----------------
  711.          */
  712.             if (TupIsNull((Pointer) outerTupleSlot)) {
  713.         MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n");
  714.         return NULL;
  715.         }
  716.         
  717.         set_mj_JoinState(mergestate, EXEC_MJ_TESTOUTER);
  718.         break;
  719.         
  720.     /* ********************************
  721.      *    EXEC_MJ_TESTOUTER
  722.      *      If the new outer tuple and the marked tuple satisify
  723.      *      the merge clause then we know we have duplicates in
  724.      *      the outer scan so we have to restore the inner scan
  725.      *      to the marked tuple and proceed to join the new outer
  726.      *      tuples with the inner tuples (EXEC_MJ_JOINTEST)
  727.      *
  728.      *    This is the case when
  729.      *
  730.      *              outer    inner
  731.      *                4      5  - marked tuple    
  732.      *         outer tuple -  5      5  
  733.      *     new outer tuple -  5     5
  734.      *                6     8  - inner tuple
  735.      *                7    12
  736.      *
  737.      *        new outer tuple = marked tuple
  738.      *
  739.      *      If the outer tuple fails the test, then we know we have
  740.      *      to proceed to skip outer tuples until outer >= inner
  741.      *      (EXEC_MJ_SKIPOUTER).
  742.      *
  743.      *    This is the case when
  744.      *
  745.      *              outer    inner
  746.      *                   5      5  - marked tuple    
  747.      *         outer tuple -  5     5
  748.      *     new outer tuple -  6     8  - inner tuple
  749.      *                7    12
  750.      *
  751.      *        new outer tuple > marked tuple
  752.      *
  753.      * ********************************
  754.      */
  755.     case EXEC_MJ_TESTOUTER:
  756.         MJ_printf("ExecMergeJoin: EXEC_MJ_TESTOUTER\n");
  757.         
  758.         /* ----------------
  759.          *  here we compare the outer tuple with the marked inner tuple
  760.          *  by using the marked tuple in place of the inner tuple.
  761.          * ----------------
  762.          */
  763.         innerTupleSlot =  get_ecxt_innertuple(econtext);
  764.         markedTupleSlot = get_mj_MarkedTupleSlot(mergestate);
  765.         set_ecxt_innertuple(econtext, markedTupleSlot);
  766.         
  767.         qualResult = ExecQual(mergeclauses, econtext);
  768.         MJ_DEBUG_QUAL(mergeclauses, qualResult);
  769.         
  770.         if (qualResult) {
  771.         /* ----------------
  772.          *  the merge clause matched so now we juggle the slots
  773.          *  back the way they were and proceed to JOINTEST.
  774.          * ----------------
  775.          */
  776.         set_ecxt_innertuple(econtext, innerTupleSlot);
  777.  
  778.         RestoreInnerTuple((Pointer) innerTupleSlot,(Pointer)
  779.                   markedTupleSlot);
  780.         
  781.         ExecRestrPos(innerPlan);
  782.         set_mj_JoinState(mergestate, EXEC_MJ_JOINTEST);
  783.         
  784.         } else {
  785.         /* ----------------
  786.          *  if the inner tuple was nil and the new outer
  787.          *  tuple didn't match the marked outer tuple then
  788.          *  we may have the case:
  789.          *
  790.          *        outer    inner
  791.          *            4      4   - marked tuple
  792.          *    new outer - 5     4
  793.          *            6    nil  - inner tuple
  794.          *            7
  795.          *
  796.          *  which means that all subsequent outer tuples will be
  797.          *  larger than our inner tuples.  
  798.          * ----------------
  799.          */
  800.         if (TupIsNull((Pointer) innerTupleSlot)) {
  801.             MJ_printf("ExecMergeJoin: **** wierd case 1 ****\n");
  802.             return NULL;
  803.         }
  804.         
  805.         /* ----------------
  806.          *  restore the inner tuple and continue on to
  807.          *  skip outer tuples.
  808.          * ----------------
  809.          */
  810.         set_ecxt_innertuple(econtext, innerTupleSlot);
  811.         set_mj_JoinState(mergestate, EXEC_MJ_SKIPOUTER);
  812.         }
  813.         break;
  814.         
  815.     /* ********************************
  816.      *    EXEC_MJ_SKIPOUTER means skip over tuples in the outer plan
  817.      *      until we find an outer tuple > current inner tuple.
  818.      *
  819.      *    For example:
  820.      *
  821.      *              outer    inner
  822.      *                     5      5  
  823.      *                 5     5
  824.      *         outer tuple -  6     8  - inner tuple
  825.      *                7    12
  826.      *                8    14
  827.      *
  828.      *        we have to advance the outer scan
  829.      *        until we find the outer 8.
  830.      *
  831.      * ********************************
  832.      */
  833.     case EXEC_MJ_SKIPOUTER:
  834.         MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER\n");
  835.         /* ----------------
  836.          *    before we advance, make sure the current tuples
  837.          *  do not satisify the mergeclauses.  If they do, then
  838.          *  we update the marked tuple and go join them.
  839.          * ----------------
  840.          */
  841.         qualResult = ExecQual(mergeclauses, econtext);
  842.         MJ_DEBUG_QUAL(mergeclauses, qualResult);
  843.  
  844.         if (qualResult) {
  845.         ExecMarkPos(innerPlan);
  846.         innerTupleSlot = get_ecxt_innertuple(econtext);
  847.  
  848.         MarkInnerTuple(innerTupleSlot, mergestate);
  849.         
  850.         set_mj_JoinState(mergestate, EXEC_MJ_JOINTUPLES);
  851.         break;
  852.         }
  853.  
  854.         /* ----------------
  855.          *    ok, now test the skip qualification
  856.          * ----------------
  857.          */
  858.         compareResult = MergeCompare(mergeclauses,
  859.                      outerSkipQual,
  860.                      econtext);
  861.         
  862.         MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult);
  863.  
  864.         /* ----------------
  865.          *    compareResult is true as long as we should
  866.          *  continue skipping tuples.
  867.          * ----------------
  868.          */
  869.         if (compareResult) {
  870.         
  871.         outerTupleSlot = ExecProcNode(outerPlan);
  872.         MJ_DEBUG_PROC_NODE(outerTupleSlot);
  873.         set_ecxt_outertuple(econtext, outerTupleSlot);
  874.         
  875.         /* ----------------
  876.          *  if the outer tuple is null then we know
  877.          *  we are done with the join
  878.          * ----------------
  879.          */
  880.         if (TupIsNull((Pointer) outerTupleSlot)) {
  881.             MJ_printf("ExecMergeJoin: **** outerTuple is nil ****\n");
  882.             return NULL;
  883.                 }
  884.         /* ----------------
  885.          *  otherwise test the new tuple against the skip qual.
  886.          *  (we remain in the EXEC_MJ_SKIPOUTER state)
  887.          * ----------------
  888.          */
  889.         break;
  890.         }
  891.         
  892.         /* ----------------
  893.          *    now check the inner skip qual to see if we
  894.          *  should now skip inner tuples... if we fail the
  895.          *  inner skip qual, then we know we have a new pair
  896.          *  of matching tuples.
  897.          * ----------------
  898.          */
  899.         compareResult = MergeCompare(mergeclauses,
  900.                      innerSkipQual,
  901.                      econtext);
  902.  
  903.         MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult);
  904.  
  905.         if (compareResult)
  906.         {
  907.         set_mj_JoinState(mergestate, EXEC_MJ_SKIPINNER);
  908.         }
  909.         else
  910.         {
  911.         set_mj_JoinState(mergestate, EXEC_MJ_JOINMARK);
  912.         }
  913.         break;
  914.  
  915.     /* ********************************
  916.      *    EXEC_MJ_SKIPINNER means skip over tuples in the inner plan
  917.      *      until we find an inner tuple > current outer tuple.
  918.      *
  919.      *    For example:
  920.      *
  921.      *              outer    inner
  922.      *                     5      5   
  923.      *                 5     5
  924.      *         outer tuple - 12     8 - inner tuple
  925.      *               14    10
  926.      *               17    12
  927.      *
  928.      *        we have to advance the inner scan
  929.      *        until we find the inner 12.
  930.      *
  931.      * ********************************
  932.      */
  933.     case EXEC_MJ_SKIPINNER:
  934.         MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER\n");
  935.         /* ----------------
  936.          *    before we advance, make sure the current tuples
  937.          *  do not satisify the mergeclauses.  If they do, then
  938.          *  we update the marked tuple and go join them.
  939.          * ----------------
  940.          */
  941.         qualResult = ExecQual(mergeclauses, econtext);
  942.         MJ_DEBUG_QUAL(mergeclauses, qualResult);
  943.         
  944.         if (qualResult) {
  945.         ExecMarkPos(innerPlan);
  946.         innerTupleSlot = get_ecxt_innertuple(econtext);
  947.  
  948.         MarkInnerTuple(innerTupleSlot, mergestate);
  949.         
  950.         set_mj_JoinState(mergestate, EXEC_MJ_JOINTUPLES);
  951.         break;
  952.         }
  953.  
  954.         /* ----------------
  955.          *    ok, now test the skip qualification
  956.          * ----------------
  957.          */
  958.         compareResult = MergeCompare(mergeclauses,
  959.                      innerSkipQual,
  960.                      econtext);
  961.         
  962.         MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult);
  963.         
  964.         /* ----------------
  965.          *    compareResult is true as long as we should
  966.          *  continue skipping tuples.
  967.          * ----------------
  968.          */
  969.         if (compareResult) {
  970.         /* ----------------
  971.          *  now try and get a new inner tuple
  972.          * ----------------
  973.          */
  974.         innerTupleSlot = ExecProcNode(innerPlan);
  975.         MJ_DEBUG_PROC_NODE(innerTupleSlot);
  976.         set_ecxt_innertuple(econtext, innerTupleSlot);
  977.         
  978.         /* ----------------
  979.          *  if the inner tuple is null then we know
  980.          *  we have to restore the inner scan
  981.          *  and advance to the next outer tuple
  982.          * ----------------
  983.          */
  984.         if (TupIsNull((Pointer) innerTupleSlot)) {
  985.             markedTupleSlot = get_mj_MarkedTupleSlot(mergestate);
  986.             if (TupIsNull((Pointer) markedTupleSlot)) {
  987.             /* ----------------
  988.              *  this is an interesting case.. all our
  989.              *  inner tuples are smaller then our outer
  990.              *  tuples so we never found an inner tuple
  991.              *  to mark.
  992.              *
  993.              *          outer    inner
  994.              *   outer tuple -  5      4     
  995.              *            5     4
  996.              *            6    nil  - inner tuple
  997.              *            7
  998.              *
  999.              *  This means the join should end.
  1000.              * ----------------
  1001.              */
  1002.             MJ_printf("ExecMergeJoin: **** wierd case 2 ****\n");
  1003.             return NULL;
  1004.             }
  1005.             set_ecxt_innertuple(econtext, markedTupleSlot);
  1006.             ExecRestrPos(innerPlan);
  1007.             
  1008.             set_mj_JoinState(mergestate, EXEC_MJ_NEXTOUTER);
  1009.             break;
  1010.         }
  1011.         
  1012.         /* ----------------
  1013.          *  otherwise test the new tuple against the skip qual.
  1014.          *  (we remain in the EXEC_MJ_SKIPINNER state)
  1015.          * ----------------
  1016.          */
  1017.         break;
  1018.         }
  1019.         
  1020.         /* ----------------
  1021.          *  compare finally failed and we have stopped skipping
  1022.          *  inner tuples so now check the outer skip qual
  1023.          *  to see if we should now skip outer tuples...
  1024.          * ----------------
  1025.          */
  1026.         compareResult = MergeCompare(mergeclauses,
  1027.                      outerSkipQual,
  1028.                      econtext);
  1029.         
  1030.         MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult);
  1031.         
  1032.         if (compareResult)
  1033.         {
  1034.         set_mj_JoinState(mergestate, EXEC_MJ_SKIPOUTER);
  1035.         }
  1036.         else
  1037.         {
  1038.         set_mj_JoinState(mergestate, EXEC_MJ_JOINMARK);
  1039.         }
  1040.         
  1041.         break;
  1042.         
  1043.     /* ********************************
  1044.      *    if we get here it means our code is fucked up and
  1045.      *      so we just end the join prematurely.
  1046.      * ********************************
  1047.      */
  1048.     default:
  1049.         elog(NOTICE, "ExecMergeJoin: invalid join state. aborting");
  1050.         return NULL;
  1051.     }
  1052.     }
  1053. }
  1054.  
  1055. /* ----------------------------------------------------------------
  1056.  *       ExecInitMergeJoin
  1057.  *
  1058.  * old comments
  1059.  *       Creates the run-time state information for the node and
  1060.  *       sets the relation id to contain relevant decriptors.
  1061.  * ----------------------------------------------------------------
  1062.  */
  1063.  
  1064. /**** xxref:
  1065.  *           ExecInitNode
  1066.  ****/
  1067. List
  1068. ExecInitMergeJoin(node, estate, parent)
  1069.     MergeJoin     node;
  1070.     EState     estate;
  1071.     Plan    parent;
  1072. {
  1073.     MergeJoinState    mergestate;
  1074.     List        joinclauses;
  1075.     List        rightorder;
  1076.     List        leftorder;
  1077.     ObjectId        rightsortop;
  1078.     ObjectId        leftsortop;
  1079.     ObjectId        sortop;
  1080.     
  1081.     List        OSortopI;
  1082.     List        ISortopO;
  1083.     
  1084.     ParamListInfo       paraminfo;
  1085.     ExprContext            econtext;
  1086.     int            baseid;
  1087.         
  1088.     MJ1_printf("ExecInitMergeJoin: %s\n",
  1089.            "initializing node");
  1090.     
  1091.     /* ----------------
  1092.      *  assign the node's execution state and 
  1093.      *    get the range table and direction from it
  1094.      * ----------------
  1095.      */
  1096.     set_state((Plan) node, (EStatePtr)estate);
  1097.     
  1098.     /* ----------------
  1099.      *    create new merge state for node
  1100.      * ----------------
  1101.      */
  1102.     mergestate = MakeMergeJoinState(LispNil, LispNil, 0, NULL);
  1103.     set_mergestate(node, mergestate);
  1104.     
  1105.     /* ----------------
  1106.      *  Miscellanious initialization
  1107.      *
  1108.      *         +    assign node's base_id
  1109.      *       +    assign debugging hooks and
  1110.      *       +    create expression context for node
  1111.      * ----------------
  1112.      */
  1113.     ExecAssignNodeBaseInfo(estate, (BaseNode) mergestate, parent);
  1114.     ExecAssignDebugHooks((Plan) node, (BaseNode) mergestate);
  1115.     ExecAssignExprContext(estate, (CommonState) mergestate);
  1116.  
  1117. #define MERGEJOIN_NSLOTS 2
  1118.     /* ----------------
  1119.      *    tuple table initialization
  1120.      * ----------------
  1121.      */
  1122.     ExecInitResultTupleSlot(estate, (CommonState) mergestate);
  1123.     ExecInitMarkedTupleSlot(estate,  mergestate);
  1124.     
  1125.     /* ----------------
  1126.      *    get merge sort operators.
  1127.      *
  1128.      *  XXX for now we assume all quals in the joinclauses were
  1129.      *      sorted with the same operator in both the inner and
  1130.      *      outer relations. -cim 11/2/89
  1131.      * ----------------
  1132.      */
  1133.     joinclauses = get_mergeclauses(node);
  1134.     rightorder =  get_mergerightorder(node);
  1135.     leftorder =   get_mergeleftorder(node);
  1136.     
  1137.     rightsortop = get_opcode(CAR(rightorder));
  1138.     leftsortop =  get_opcode(CAR(leftorder));
  1139.     
  1140.     if (leftsortop != rightsortop)
  1141.     elog(NOTICE, "ExecInitMergeJoin: %s",
  1142.          "left and right sortop's are unequal!");
  1143.     
  1144.     sortop = rightsortop;
  1145.     
  1146.     /* ----------------
  1147.      *    form merge skip qualifications
  1148.      *
  1149.      *  XXX MJform routines need to be extended
  1150.      *        to take a list of sortops.. -cim 11/2/89
  1151.      * ----------------
  1152.      */
  1153.     OSortopI = MJFormOSortopI(joinclauses, sortop);
  1154.     ISortopO = MJFormISortopO(joinclauses, sortop);
  1155.     set_mj_OSortopI(mergestate, OSortopI);
  1156.     set_mj_ISortopO(mergestate, ISortopO);
  1157.  
  1158.     MJ_printf("\nExecInitMergeJoin: OSortopI is ");
  1159.     MJ_lispDisplay(OSortopI);
  1160.     MJ_printf("\nExecInitMergeJoin: ISortopO is ");
  1161.     MJ_lispDisplay(ISortopO);
  1162.     MJ_printf("\n");
  1163.     
  1164.     /* ----------------
  1165.      *    initialize join state
  1166.      * ----------------
  1167.      */
  1168.     set_mj_JoinState(mergestate, EXEC_MJ_INITIALIZE);
  1169.     
  1170.     /* ----------------
  1171.      *    initialize subplans
  1172.      * ----------------
  1173.      */
  1174.     ExecInitNode((Plan) get_outerPlan((Plan) node), estate, (Plan) node);
  1175.     ExecInitNode((Plan) get_innerPlan((Plan) node), estate, (Plan) node);
  1176.         
  1177.     /* ----------------
  1178.      *     initialize tuple type and projection info
  1179.      * ----------------
  1180.      */
  1181.     ExecAssignResultTypeFromTL((Plan) node, (CommonState)mergestate);
  1182.     ExecAssignProjectionInfo((Plan) node, (CommonState)mergestate);
  1183.     
  1184.     set_cs_TupFromTlist((CommonState)mergestate, false);
  1185.     /* ----------------
  1186.      *    initialization successful
  1187.      * ----------------
  1188.      */
  1189.     MJ1_printf("ExecInitMergeJoin: %s\n",
  1190.            "node initialized");
  1191.     return
  1192.     LispTrue;
  1193. }
  1194.  
  1195. int
  1196. ExecCountSlotsMergeJoin(node)
  1197.     Plan node;
  1198. {
  1199.     return ExecCountSlotsNode(get_outerPlan(node)) +
  1200.        ExecCountSlotsNode(get_innerPlan(node)) +
  1201.        MERGEJOIN_NSLOTS;
  1202. }
  1203.  
  1204. /* ----------------------------------------------------------------
  1205.  *       ExecEndMergeJoin
  1206.  *
  1207.  * old comments
  1208.  *       frees storage allocated through C routines.
  1209.  * ----------------------------------------------------------------
  1210.  */
  1211.  
  1212. /**** xxref:
  1213.  *           ExecEndNode
  1214.  ****/
  1215. void
  1216. ExecEndMergeJoin(node)
  1217.     MergeJoin     node;
  1218. {
  1219.     MergeJoinState    mergestate;
  1220.     Pointer            tupValue;
  1221.     
  1222.     MJ1_printf("ExecEndMergeJoin: %s\n",
  1223.            "ending node processing");
  1224.         
  1225.     /* ----------------
  1226.      *    get state information from the node
  1227.      * ----------------
  1228.      */
  1229.     mergestate = get_mergestate(node);
  1230.  
  1231.     /* ----------------
  1232.      *    Free the projection info and the scan attribute info
  1233.      *
  1234.      *  Note: we don't ExecFreeResultType(mergestate) 
  1235.      *        because the rule manager depends on the tupType
  1236.      *          returned by ExecMain().  So for now, this
  1237.      *          is freed at end-transaction time.  -cim 6/2/91     
  1238.      * ----------------
  1239.      */    
  1240.     ExecFreeProjectionInfo((CommonState)mergestate);
  1241.     
  1242.     /* ----------------
  1243.      *    shut down the subplans
  1244.      * ----------------
  1245.      */
  1246.     ExecEndNode((Plan) get_innerPlan((Plan) node));
  1247.     ExecEndNode((Plan) get_outerPlan((Plan) node));
  1248.  
  1249.     /* ----------------
  1250.      *    clean out the tuple table so that we don't try and
  1251.      *  pfree the marked tuples..  see HACK ALERT at the top of
  1252.      *  this file.
  1253.      * ----------------
  1254.      */
  1255.     ExecClearTuple((Pointer) get_cs_ResultTupleSlot((CommonState)mergestate));
  1256.     ExecClearTuple((Pointer) get_mj_MarkedTupleSlot(mergestate));
  1257.     
  1258.     MJ1_printf("ExecEndMergeJoin: %s\n",
  1259.            "node processing ended");
  1260. }
  1261.  
  1262.