1 /*-------------------------------------------------------------------------
4 * routines supporting merge joins
6 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.37 2000/08/24 03:29:03 tgl Exp $
13 *-------------------------------------------------------------------------
17 * ExecMergeJoin mergejoin outer and inner relations.
18 * ExecInitMergeJoin creates and initializes run time states
19 * ExecEndMergeJoin cleand up the node.
22 * Essential operation of the merge join algorithm is as follows:
23 * (** indicates the tuples satisfy the merge clause).
26 * get initial outer and inner tuples INITIALIZE
27 * Skip Inner SKIPINNER
28 * mark inner position JOINMARK
30 * while (outer == inner) { JOINTEST
31 * join tuples JOINTUPLES
32 * advance inner position NEXTINNER
34 * advance outer position NEXTOUTER
35 * if (outer == mark) { TESTOUTER
36 * restore inner position to mark TESTOUTER
39 * Skip Outer SKIPOUTER
40 * mark inner position JOINMARK
45 * Skip Outer { SKIPOUTER
46 * if (inner == outer) Join Tuples JOINTUPLES
47 * while (outer < inner) SKIPOUTER
48 * advance outer SKIPOUTER
49 * if (outer > inner) SKIPOUTER
50 * Skip Inner SKIPINNER
53 * Skip Inner { SKIPINNER
54 * if (inner == outer) Join Tuples JOINTUPLES
55 * while (outer > inner) SKIPINNER
56 * advance inner SKIPINNER
57 * if (outer < inner) SKIPINNER
58 * Skip Outer SKIPOUTER
61 * The merge join operation is coded in the fashion
62 * of a state machine. At each state, we do something and then
63 * proceed to another state. This state is stored in the node's
64 * execution state information and is preserved across calls to
65 * ExecMergeJoin. -cim 10/31/89
70 #include "access/heapam.h"
71 #include "catalog/pg_operator.h"
72 #include "executor/execdebug.h"
73 #include "executor/execdefs.h"
74 #include "executor/nodeMergejoin.h"
75 #include "utils/lsyscache.h"
76 #include "utils/syscache.h"
79 static bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext);
81 #define MarkInnerTuple(innerTupleSlot, mergestate) \
83 ExecStoreTuple(heap_copytuple((innerTupleSlot)->val), \
84 (mergestate)->mj_MarkedTupleSlot, \
90 /* ----------------------------------------------------------------
93 * This takes the mergeclause which is a qualification of the
94 * form ((= expr expr) (= expr expr) ...) and forms a new
95 * qualification like ((> expr expr) (> expr expr) ...) which
96 * is used by ExecMergeJoin() in order to determine if we should
97 * skip tuples. The replacement operators are named either ">"
98 * or "<" according to the replaceopname parameter, and have the
99 * same operand data types as the "=" operators they replace.
100 * (We expect there to be such operators because the "=" operators
101 * were marked mergejoinable; however, there might be a different
102 * one needed in each qual clause.)
103 * ----------------------------------------------------------------
106 MJFormSkipQual(List *qualList, char *replaceopname)
113 Form_pg_operator opform;
118 * qualList is a list: ((op .. ..) ...)
119 * first we make a copy of it. copyObject() makes a deep copy
120 * so let's use it instead of the old fashoned lispCopy()...
123 qualCopy = (List *) copyObject((Node *) qualList);
125 foreach(qualcdr, qualCopy)
128 * first get the current (op .. ..) list
131 qual = lfirst(qualcdr);
137 op = (Oper *) qual->oper;
139 elog(ERROR, "MJFormSkipQual: op not an Oper!");
142 * Get the declared left and right operand types of the operator.
143 * Note we do *not* use the actual operand types, since those might
144 * be different in scenarios with binary-compatible data types.
145 * There should be "<" and ">" operators matching a mergejoinable
146 * "=" operator's declared operand types, but we might not find them
147 * if we search with the actual operand types.
150 optup = get_operator_tuple(op->opno);
151 if (!HeapTupleIsValid(optup)) /* shouldn't happen */
152 elog(ERROR, "MJFormSkipQual: operator %u not found", op->opno);
153 opform = (Form_pg_operator) GETSTRUCT(optup);
154 oprleft = opform->oprleft;
155 oprright = opform->oprright;
158 * Now look up the matching "<" or ">" operator. If there isn't one,
159 * whoever marked the "=" operator mergejoinable was a loser.
162 optup = SearchSysCacheTuple(OPERNAME,
163 PointerGetDatum(replaceopname),
164 ObjectIdGetDatum(oprleft),
165 ObjectIdGetDatum(oprright),
167 if (!HeapTupleIsValid(optup))
169 "MJFormSkipQual: mergejoin operator %u has no matching %s op",
170 op->opno, replaceopname);
171 opform = (Form_pg_operator) GETSTRUCT(optup);
174 * And replace the data in the copied operator node.
177 op->opno = optup->t_data->t_oid;
178 op->opid = opform->oprcode;
179 op->op_fcache = NULL;
185 /* ----------------------------------------------------------------
188 * Compare the keys according to 'compareQual' which is of the
189 * form: { (key1a > key2a) (key1b > key2b) ... }.
191 * (actually, it could also be of the form (key1a < key2a)...)
193 * This is different from calling ExecQual because ExecQual returns
194 * true only if ALL the comparison clauses are satisfied.
195 * However, there is an order of significance among the keys with
196 * the first keys being most significant. Therefore, the clauses
197 * are evaluated in order and the 'compareQual' is satisfied
198 * if (key1i > key2i) is true and (key1j = key2j) for 0 < j < i.
199 * We use the original mergeclause items to detect equality.
200 * ----------------------------------------------------------------
203 MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
206 MemoryContext oldContext;
211 * Do expression eval in short-lived context.
213 oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
216 * for each pair of clauses, test them until
217 * our compare conditions are satisfied.
218 * if we reach the end of the list, none of our key greater-than
219 * conditions were satisfied so we return false.
222 result = false; /* assume 'false' result */
225 foreach(clause, compareQual)
231 * first test if our compare clause is satisfied.
232 * if so then return true.
234 * A NULL result is considered false.
237 const_value = ExecEvalExpr((Node *) lfirst(clause), econtext,
240 if (DatumGetBool(const_value) && !isNull)
247 * ok, the compare clause failed so we test if the keys
248 * are equal... if key1 != key2, we return false.
249 * otherwise key1 = key2 so we move on to the next pair of keys.
252 const_value = ExecEvalExpr((Node *) lfirst(eqclause),
257 if (! DatumGetBool(const_value) || isNull)
258 break; /* return false */
260 eqclause = lnext(eqclause);
263 MemoryContextSwitchTo(oldContext);
268 /* ----------------------------------------------------------------
271 * This function is called through the MJ_dump() macro
272 * when EXEC_MERGEJOINDEBUG is defined
273 * ----------------------------------------------------------------
275 #ifdef EXEC_MERGEJOINDEBUG
277 ExecMergeTupleDumpInner(ExprContext *econtext);
280 ExecMergeTupleDumpInner(ExprContext *econtext)
282 TupleTableSlot *innerSlot;
284 printf("==== inner tuple ====\n");
285 innerSlot = econtext->ecxt_innertuple;
286 if (TupIsNull(innerSlot))
289 MJ_debugtup(innerSlot->val,
290 innerSlot->ttc_tupleDescriptor);
294 ExecMergeTupleDumpOuter(ExprContext *econtext);
297 ExecMergeTupleDumpOuter(ExprContext *econtext)
299 TupleTableSlot *outerSlot;
301 printf("==== outer tuple ====\n");
302 outerSlot = econtext->ecxt_outertuple;
303 if (TupIsNull(outerSlot))
306 MJ_debugtup(outerSlot->val,
307 outerSlot->ttc_tupleDescriptor);
310 void ExecMergeTupleDumpMarked(ExprContext *econtext,
311 MergeJoinState *mergestate);
314 ExecMergeTupleDumpMarked(ExprContext *econtext,
315 MergeJoinState *mergestate)
317 TupleTableSlot *markedSlot;
319 printf("==== marked tuple ====\n");
320 markedSlot = mergestate->mj_MarkedTupleSlot;
322 if (TupIsNull(markedSlot))
325 MJ_debugtup(markedSlot->val,
326 markedSlot->ttc_tupleDescriptor);
330 ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate);
333 ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate)
335 printf("******** ExecMergeTupleDump ********\n");
337 ExecMergeTupleDumpInner(econtext);
338 ExecMergeTupleDumpOuter(econtext);
339 ExecMergeTupleDumpMarked(econtext, mergestate);
341 printf("******** \n");
346 /* ----------------------------------------------------------------
350 * Details of the merge-join routines:
352 * (1) ">" and "<" operators
354 * Merge-join is done by joining the inner and outer tuples satisfying
355 * the join clauses of the form ((= outerKey innerKey) ...).
356 * The join clauses is provided by the query planner and may contain
357 * more than one (= outerKey innerKey) clauses (for composite key).
359 * However, the query executor needs to know whether an outer
360 * tuple is "greater/smaller" than an inner tuple so that it can
361 * "synchronize" the two relations. For e.g., consider the following
364 * outer: (0 ^1 1 2 5 5 5 6 6 7) current tuple: 1
365 * inner: (1 ^3 5 5 5 5 6) current tuple: 3
367 * To continue the merge-join, the executor needs to scan both inner
368 * and outer relations till the matching tuples 5. It needs to know
369 * that currently inner tuple 3 is "greater" than outer tuple 1 and
370 * therefore it should scan the outer relation first to find a
371 * matching tuple and so on.
373 * Therefore, when initializing the merge-join node, the executor
374 * creates the "greater/smaller" clause by substituting the "="
375 * operator in the join clauses with the corresponding ">" operator.
376 * The opposite "smaller/greater" clause is formed by substituting "<".
378 * Note: prior to v6.5, the relational clauses were formed using the
379 * sort op used to sort the inner relation, which of course would fail
380 * if the outer and inner keys were of different data types.
381 * In the current code, we instead assume that operators named "<" and ">"
382 * will do the right thing. This should be true since the mergejoin "="
383 * operator's pg_operator entry will have told the planner to sort by
384 * "<" for each of the left and right sides.
386 * (2) repositioning inner "cursor"
388 * Consider the above relations and suppose that the executor has
389 * just joined the first outer "5" with the last inner "5". The
390 * next step is of course to join the second outer "5" with all
391 * the inner "5's". This requires repositioning the inner "cursor"
392 * to point at the first inner "5". This is done by "marking" the
393 * first inner 5 and restore the "cursor" to it before joining
394 * with the second outer 5. The access method interface provides
395 * routines to mark and restore to a tuple.
396 * ----------------------------------------------------------------
399 ExecMergeJoin(MergeJoin *node)
402 MergeJoinState *mergestate;
403 ScanDirection direction;
411 TupleTableSlot *innerTupleSlot;
413 TupleTableSlot *outerTupleSlot;
414 ExprContext *econtext;
415 #ifdef ENABLE_OUTER_JOINS
417 * These should be set from the expression context! - thomas
420 static bool isLeftJoin = true;
421 static bool isRightJoin = false;
425 * get information from node
428 mergestate = node->mergestate;
429 estate = node->join.state;
430 direction = estate->es_direction;
431 innerPlan = innerPlan((Plan *) node);
432 outerPlan = outerPlan((Plan *) node);
433 econtext = mergestate->jstate.cs_ExprContext;
434 mergeclauses = node->mergeclauses;
435 qual = node->join.qual;
437 if (ScanDirectionIsForward(direction))
439 outerSkipQual = mergestate->mj_OuterSkipQual;
440 innerSkipQual = mergestate->mj_InnerSkipQual;
444 outerSkipQual = mergestate->mj_InnerSkipQual;
445 innerSkipQual = mergestate->mj_OuterSkipQual;
449 * Check to see if we're still projecting out tuples from a previous
450 * join tuple (because there is a function-returning-set in the
451 * projection expressions). If so, try to project another one.
454 if (mergestate->jstate.cs_TupFromTlist)
456 TupleTableSlot *result;
459 result = ExecProject(mergestate->jstate.cs_ProjInfo, &isDone);
460 if (isDone == ExprMultipleResult)
462 /* Done with that source tuple... */
463 mergestate->jstate.cs_TupFromTlist = false;
467 * Reset per-tuple memory context to free any expression evaluation
468 * storage allocated in the previous tuple cycle. Note this can't
469 * happen until we're done projecting out tuples from a join tuple.
472 ResetExprContext(econtext);
475 * ok, everything is setup.. let's go to work
481 * get the current state of the join and do things accordingly.
482 * Note: The join states are highlighted with 32-* comments for
483 * improved readability.
486 MJ_dump(econtext, mergestate);
488 switch (mergestate->mj_JoinState)
492 * EXEC_MJ_INITIALIZE means that this is the first time
493 * ExecMergeJoin() has been called and so we have to
494 * initialize the inner, outer and marked tuples as well
495 * as various stuff in the expression context.
497 case EXEC_MJ_INITIALIZE:
498 MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE\n");
501 * Note: at this point, if either of our inner or outer
502 * tuples are nil, then the join ends immediately because
503 * we know one of the subplans is empty.
505 innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
506 if (TupIsNull(innerTupleSlot))
508 MJ_printf("ExecMergeJoin: **** inner tuple is nil ****\n");
512 outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
513 if (TupIsNull(outerTupleSlot))
515 MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n");
520 * store the inner and outer tuple in the merge state
523 econtext->ecxt_innertuple = innerTupleSlot;
524 econtext->ecxt_outertuple = outerTupleSlot;
526 mergestate->mj_MarkedTupleSlot->ttc_tupleDescriptor =
527 innerTupleSlot->ttc_tupleDescriptor;
530 * initialize merge join state to skip inner tuples.
533 mergestate->mj_JoinState = EXEC_MJ_SKIPINNER;
537 * EXEC_MJ_JOINMARK means we have just found a new outer
538 * tuple and a possible matching inner tuple. This is the
539 * case after the INITIALIZE, SKIPOUTER or SKIPINNER
542 case EXEC_MJ_JOINMARK:
543 MJ_printf("ExecMergeJoin: EXEC_MJ_JOINMARK\n");
544 ExecMarkPos(innerPlan);
546 MarkInnerTuple(econtext->ecxt_innertuple, mergestate);
548 mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
552 * EXEC_MJ_JOINTEST means we have two tuples which might
553 * satisfy the merge clause, so we test them.
555 * If they do satisfy, then we join them and move on to the
556 * next inner tuple (EXEC_MJ_JOINTUPLES).
558 * If they do not satisfy then advance to next outer tuple.
560 case EXEC_MJ_JOINTEST:
561 MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n");
563 ResetExprContext(econtext);
565 qualResult = ExecQual((List *) mergeclauses, econtext, false);
566 MJ_DEBUG_QUAL(mergeclauses, qualResult);
569 mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
571 mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER;
575 * EXEC_MJ_JOINTUPLES means we have two tuples which
576 * satisfied the merge clause so we join them and then
577 * proceed to get the next inner tuple (EXEC_NEXT_INNER).
579 case EXEC_MJ_JOINTUPLES:
580 MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");
581 mergestate->mj_JoinState = EXEC_MJ_NEXTINNER;
584 * Check the qpqual to see if we actually want to return
585 * this join tuple. If not, can proceed with merge.
587 * (We don't bother with a ResetExprContext here, on the
588 * assumption that we just did one before checking the merge
589 * qual. One per tuple should be sufficient.)
591 qualResult = ExecQual((List *) qual, econtext, false);
592 MJ_DEBUG_QUAL(qual, qualResult);
597 * qualification succeeded. now form the desired
598 * projection tuple and return the slot containing it.
601 TupleTableSlot *result;
604 MJ_printf("ExecMergeJoin: **** returning tuple ****\n");
606 result = ExecProject(mergestate->jstate.cs_ProjInfo,
609 if (isDone != ExprEndResult)
611 mergestate->jstate.cs_TupFromTlist = (isDone == ExprMultipleResult);
618 * EXEC_MJ_NEXTINNER means advance the inner scan to the
619 * next tuple. If the tuple is not nil, we then proceed to
620 * test it against the join qualification.
622 case EXEC_MJ_NEXTINNER:
623 MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\n");
626 * now we get the next inner tuple, if any
629 innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
630 MJ_DEBUG_PROC_NODE(innerTupleSlot);
631 econtext->ecxt_innertuple = innerTupleSlot;
633 if (TupIsNull(innerTupleSlot))
634 mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER;
636 mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
639 /*-------------------------------------------
640 * EXEC_MJ_NEXTOUTER means
643 * outer tuple - 5 5 - marked tuple
648 * we know we just bumped into the
649 * first inner tuple > current outer tuple
650 * so get a new outer tuple and then
651 * proceed to test it against the marked tuple
652 * (EXEC_MJ_TESTOUTER)
653 *------------------------------------------------
655 case EXEC_MJ_NEXTOUTER:
656 MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n");
658 outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
659 MJ_DEBUG_PROC_NODE(outerTupleSlot);
660 econtext->ecxt_outertuple = outerTupleSlot;
663 * if the outer tuple is null then we know
664 * we are done with the join
667 if (TupIsNull(outerTupleSlot))
669 MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n");
673 mergestate->mj_JoinState = EXEC_MJ_TESTOUTER;
676 /*--------------------------------------------------------
677 * EXEC_MJ_TESTOUTER If the new outer tuple and the marked
678 * tuple satisfy the merge clause then we know we have
679 * duplicates in the outer scan so we have to restore the
680 * inner scan to the marked tuple and proceed to join the
681 * new outer tuples with the inner tuples (EXEC_MJ_JOINTEST)
683 * This is the case when
687 * new outer tuple - 5 5
691 * new outer tuple = marked tuple
693 * If the outer tuple fails the test, then we know we have
694 * to proceed to skip outer tuples until outer >= inner
695 * (EXEC_MJ_SKIPOUTER).
697 * This is the case when
702 * new outer tuple - 6 8 - inner tuple
706 * new outer tuple > marked tuple
708 *---------------------------------------------------------
710 case EXEC_MJ_TESTOUTER:
711 MJ_printf("ExecMergeJoin: EXEC_MJ_TESTOUTER\n");
714 * here we compare the outer tuple with the marked inner tuple
715 * by using the marked tuple in place of the inner tuple.
718 innerTupleSlot = econtext->ecxt_innertuple;
719 econtext->ecxt_innertuple = mergestate->mj_MarkedTupleSlot;
721 ResetExprContext(econtext);
723 qualResult = ExecQual((List *) mergeclauses, econtext, false);
724 MJ_DEBUG_QUAL(mergeclauses, qualResult);
730 * the merge clause matched so now we juggle the slots
731 * back the way they were and proceed to JOINTEST.
733 * I can't understand why we have to go to JOINTEST and
734 * compare outer tuple with the same inner one again
735 * -> go to JOINTUPLES... - vadim 02/27/98
738 ExecRestrPos(innerPlan);
739 mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
743 econtext->ecxt_innertuple = innerTupleSlot;
745 * if the inner tuple was nil and the new outer
746 * tuple didn't match the marked outer tuple then
747 * we may have the case:
752 * 6 nil - inner tuple
755 * which means that all subsequent outer tuples will be
756 * larger than our inner tuples.
759 if (TupIsNull(innerTupleSlot))
761 #ifdef ENABLE_OUTER_JOINS
764 /* continue on to null fill outer tuples */
765 mergestate->mj_JoinState = EXEC_MJ_FILLOUTER;
769 MJ_printf("ExecMergeJoin: **** weird case 1 ****\n");
773 /* continue on to skip outer tuples */
774 mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER;
778 /*----------------------------------------------------------
779 * EXEC_MJ_SKIPOUTER means skip over tuples in the outer plan
780 * until we find an outer tuple > current inner tuple.
787 * outer tuple - 6 8 - inner tuple
791 * we have to advance the outer scan
792 * until we find the outer 8.
793 *----------------------------------------------------------
795 case EXEC_MJ_SKIPOUTER:
796 MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER\n");
798 * before we advance, make sure the current tuples
799 * do not satisfy the mergeclauses. If they do, then
800 * we update the marked tuple and go join them.
803 ResetExprContext(econtext);
805 qualResult = ExecQual((List *) mergeclauses, econtext, false);
806 MJ_DEBUG_QUAL(mergeclauses, qualResult);
810 ExecMarkPos(innerPlan);
812 MarkInnerTuple(econtext->ecxt_innertuple, mergestate);
814 mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
819 * ok, now test the skip qualification
822 compareResult = MergeCompare(mergeclauses,
826 MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult);
829 * compareResult is true as long as we should
830 * continue skipping tuples.
835 #ifdef ENABLE_OUTER_JOINS
837 * if this is a left or full outer join, then fill
842 mergestate->mj_JoinState = EXEC_MJ_FILLOUTER;
847 outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
848 MJ_DEBUG_PROC_NODE(outerTupleSlot);
849 econtext->ecxt_outertuple = outerTupleSlot;
852 * if the outer tuple is null then we know
853 * we are done with the join
856 if (TupIsNull(outerTupleSlot))
858 MJ_printf("ExecMergeJoin: **** outerTuple is nil ****\n");
862 * otherwise test the new tuple against the skip qual.
863 * (we remain in the EXEC_MJ_SKIPOUTER state)
870 * now check the inner skip qual to see if we
871 * should now skip inner tuples... if we fail the
872 * inner skip qual, then we know we have a new pair
873 * of matching tuples.
876 compareResult = MergeCompare(mergeclauses,
880 MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult);
883 mergestate->mj_JoinState = EXEC_MJ_SKIPINNER;
885 mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
888 /*-----------------------------------------------------------
889 * EXEC_MJ_SKIPINNER means skip over tuples in the inner plan
890 * until we find an inner tuple > current outer tuple.
897 * outer tuple - 12 8 - inner tuple
901 * we have to advance the inner scan
902 * until we find the inner 12.
904 *-------------------------------------------------------
906 case EXEC_MJ_SKIPINNER:
907 MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER\n");
909 * before we advance, make sure the current tuples
910 * do not satisfy the mergeclauses. If they do, then
911 * we update the marked tuple and go join them.
914 ResetExprContext(econtext);
916 qualResult = ExecQual((List *) mergeclauses, econtext, false);
917 MJ_DEBUG_QUAL(mergeclauses, qualResult);
921 ExecMarkPos(innerPlan);
923 MarkInnerTuple(econtext->ecxt_innertuple, mergestate);
925 mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
930 * ok, now test the skip qualification
933 compareResult = MergeCompare(mergeclauses,
937 MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult);
940 * compareResult is true as long as we should
941 * continue skipping tuples.
946 #ifdef ENABLE_OUTER_JOINS
948 * if this is a right or full outer join, then fill
953 mergestate->mj_JoinState = EXEC_MJ_FILLINNER;
959 * now try and get a new inner tuple
962 innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
963 MJ_DEBUG_PROC_NODE(innerTupleSlot);
964 econtext->ecxt_innertuple = innerTupleSlot;
967 * if the inner tuple is null then we know
968 * we have to restore the inner scan
969 * and advance to the next outer tuple
972 if (TupIsNull(innerTupleSlot))
975 * this is an interesting case.. all our
976 * inner tuples are smaller then our outer
977 * tuples so we never found an inner tuple
983 * 6 nil - inner tuple
986 * This means the join should end.
989 MJ_printf("ExecMergeJoin: **** weird case 2 ****\n");
994 * otherwise test the new tuple against the skip qual.
995 * (we remain in the EXEC_MJ_SKIPINNER state)
1002 * compare finally failed and we have stopped skipping
1003 * inner tuples so now check the outer skip qual
1004 * to see if we should now skip outer tuples...
1007 compareResult = MergeCompare(mergeclauses,
1011 MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult);
1014 mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER;
1016 mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
1020 #ifdef ENABLE_OUTER_JOINS
1023 * EXEC_MJ_FILLINNER means we have an unmatched inner
1024 * tuple which must be null-expanded into the projection
1025 * tuple. get the next inner tuple and reset markers
1026 * (EXEC_MJ_JOINMARK).
1028 case EXEC_MJ_FILLINNER:
1029 MJ_printf("ExecMergeJoin: EXEC_MJ_FILLINNER\n");
1030 mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
1033 * project the inner tuple into the result
1036 MJ_printf("ExecMergeJoin: project inner tuple into the result (not yet implemented)\n");
1039 * now skip this inner tuple
1042 innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
1043 MJ_DEBUG_PROC_NODE(innerTupleSlot);
1044 econtext->ecxt_innertuple = innerTupleSlot;
1047 * if the inner tuple is null then we know
1048 * we have to restore the inner scan
1049 * and advance to the next outer tuple
1052 if (TupIsNull(innerTupleSlot))
1054 if (isLeftJoin && !TupIsNull(outerTupleSlot))
1056 mergestate->mj_JoinState = EXEC_MJ_FILLOUTER;
1057 MJ_printf("ExecMergeJoin: try to complete outer fill\n");
1061 MJ_printf("ExecMergeJoin: **** weird case 2 ****\n");
1066 * otherwise test the new tuple against the skip qual.
1067 * (we move to the EXEC_MJ_JOINMARK state)
1073 * EXEC_MJ_FILLOUTER means we have an unmatched outer
1074 * tuple which must be null-expanded into the projection
1075 * tuple. get the next outer tuple and reset markers
1076 * (EXEC_MJ_JOINMARK).
1078 case EXEC_MJ_FILLOUTER:
1079 MJ_printf("ExecMergeJoin: EXEC_MJ_FILLOUTER\n");
1080 mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
1083 * project the outer tuple into the result
1086 MJ_printf("ExecMergeJoin: project outer tuple into the result (not yet implemented)\n");
1089 * now skip this outer tuple
1092 outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
1093 MJ_DEBUG_PROC_NODE(outerTupleSlot);
1094 econtext->ecxt_outertuple = outerTupleSlot;
1097 * if the outer tuple is null then we know
1098 * we are done with the left half of the join
1101 if (TupIsNull(outerTupleSlot))
1103 if (isRightJoin && !TupIsNull(innerTupleSlot))
1105 mergestate->mj_JoinState = EXEC_MJ_FILLINNER;
1106 MJ_printf("ExecMergeJoin: try to complete inner fill\n");
1110 MJ_printf("ExecMergeJoin: **** outerTuple is nil ****\n");
1115 * otherwise test the new tuple against the skip qual.
1116 * (we move to the EXEC_MJ_JOINMARK state)
1123 * if we get here it means our code is fouled up and so we
1124 * just end the join prematurely.
1127 elog(NOTICE, "ExecMergeJoin: invalid join state. aborting");
1133 /* ----------------------------------------------------------------
1137 * Creates the run-time state information for the node and
1138 * sets the relation id to contain relevant decriptors.
1139 * ----------------------------------------------------------------
1142 ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
1144 MergeJoinState *mergestate;
1146 TupleTableSlot *mjSlot;
1148 MJ1_printf("ExecInitMergeJoin: %s\n",
1149 "initializing node");
1152 * assign the node's execution state and
1153 * get the range table and direction from it
1156 node->join.state = estate;
1159 * create new merge state for node
1162 mergestate = makeNode(MergeJoinState);
1163 mergestate->mj_OuterSkipQual = NIL;
1164 mergestate->mj_InnerSkipQual = NIL;
1165 mergestate->mj_JoinState = 0;
1166 mergestate->mj_MarkedTupleSlot = NULL;
1167 node->mergestate = mergestate;
1170 * Miscellaneous initialization
1172 * + create expression context for node
1175 ExecAssignExprContext(estate, &mergestate->jstate);
1177 #define MERGEJOIN_NSLOTS 2
1179 * tuple table initialization
1181 * XXX why aren't we getting a tuple table slot in the normal way?
1184 ExecInitResultTupleSlot(estate, &mergestate->jstate);
1185 mjSlot = makeNode(TupleTableSlot);
1187 mjSlot->ttc_shouldFree = true;
1188 mjSlot->ttc_descIsNew = true;
1189 mjSlot->ttc_tupleDescriptor = NULL;
1190 mjSlot->ttc_buffer = InvalidBuffer;
1191 mjSlot->ttc_whichplan = -1;
1192 mergestate->mj_MarkedTupleSlot = mjSlot;
1195 * form merge skip qualifications
1198 joinclauses = node->mergeclauses;
1199 mergestate->mj_OuterSkipQual = MJFormSkipQual(joinclauses, "<");
1200 mergestate->mj_InnerSkipQual = MJFormSkipQual(joinclauses, ">");
1202 MJ_printf("\nExecInitMergeJoin: OuterSkipQual is ");
1203 MJ_nodeDisplay(mergestate->mj_OuterSkipQual);
1204 MJ_printf("\nExecInitMergeJoin: InnerSkipQual is ");
1205 MJ_nodeDisplay(mergestate->mj_InnerSkipQual);
1209 * initialize join state
1212 mergestate->mj_JoinState = EXEC_MJ_INITIALIZE;
1215 * initialize subplans
1218 ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node);
1219 ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node);
1222 * initialize tuple type and projection info
1225 ExecAssignResultTypeFromTL((Plan *) node, &mergestate->jstate);
1226 ExecAssignProjectionInfo((Plan *) node, &mergestate->jstate);
1228 mergestate->jstate.cs_TupFromTlist = false;
1230 * initialization successful
1233 MJ1_printf("ExecInitMergeJoin: %s\n",
1234 "node initialized");
1240 ExecCountSlotsMergeJoin(MergeJoin *node)
1242 return ExecCountSlotsNode(outerPlan((Plan *) node)) +
1243 ExecCountSlotsNode(innerPlan((Plan *) node)) +
1247 /* ----------------------------------------------------------------
1251 * frees storage allocated through C routines.
1252 * ----------------------------------------------------------------
1255 ExecEndMergeJoin(MergeJoin *node)
1257 MergeJoinState *mergestate;
1259 MJ1_printf("ExecEndMergeJoin: %s\n",
1260 "ending node processing");
1263 * get state information from the node
1266 mergestate = node->mergestate;
1269 * Free the projection info and the scan attribute info
1271 * Note: we don't ExecFreeResultType(mergestate)
1272 * because the rule manager depends on the tupType
1273 * returned by ExecMain(). So for now, this
1274 * is freed at end-transaction time. -cim 6/2/91
1277 ExecFreeProjectionInfo(&mergestate->jstate);
1278 ExecFreeExprContext(&mergestate->jstate);
1281 * shut down the subplans
1284 ExecEndNode((Plan *) innerPlan((Plan *) node), (Plan *) node);
1285 ExecEndNode((Plan *) outerPlan((Plan *) node), (Plan *) node);
1288 * clean out the tuple table so that we don't try and
1289 * pfree the marked tuples.. see HACK ALERT at the top of
1293 ExecClearTuple(mergestate->jstate.cs_ResultTupleSlot);
1294 ExecClearTuple(mergestate->mj_MarkedTupleSlot);
1295 pfree(mergestate->mj_MarkedTupleSlot);
1296 mergestate->mj_MarkedTupleSlot = NULL;
1298 MJ1_printf("ExecEndMergeJoin: %s\n",
1299 "node processing ended");
1303 ExecReScanMergeJoin(MergeJoin *node, ExprContext *exprCtxt, Plan *parent)
1305 MergeJoinState *mergestate = node->mergestate;
1306 TupleTableSlot *mjSlot = mergestate->mj_MarkedTupleSlot;
1308 ExecClearTuple(mjSlot);
1309 mjSlot->ttc_tupleDescriptor = NULL;
1310 mjSlot->ttc_descIsNew = true;
1311 mjSlot->ttc_whichplan = -1;
1313 mergestate->mj_JoinState = EXEC_MJ_INITIALIZE;
1316 * if chgParam of subnodes is not null then plans will be re-scanned
1317 * by first ExecProcNode.
1319 if (((Plan *) node)->lefttree->chgParam == NULL)
1320 ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
1321 if (((Plan *) node)->righttree->chgParam == NULL)
1322 ExecReScan(((Plan *) node)->righttree, exprCtxt, (Plan *) node);