1 /*-------------------------------------------------------------------------
4 * routines supporting merge joins
6 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.53 2002/12/12 15:49:25 tgl Exp $
13 *-------------------------------------------------------------------------
17 * ExecMergeJoin mergejoin outer and inner relations.
18 * ExecInitMergeJoin creates and initializes run time states
19 * ExecEndMergeJoin cleans up the node.
22 * Essential operation of the merge join algorithm is as follows:
25 * get initial outer and inner tuples INITIALIZE
26 * Skip Inner SKIPINNER
27 * mark inner position JOINMARK
29 * while (outer == inner) { JOINTEST
30 * join tuples JOINTUPLES
31 * advance inner position NEXTINNER
33 * advance outer position NEXTOUTER
34 * if (outer == mark) { TESTOUTER
35 * restore inner position to mark TESTOUTER
38 * Skip Outer SKIPOUTER
39 * mark inner position JOINMARK
44 * Skip Outer { SKIPOUTER_BEGIN
45 * if (inner == outer) Join Tuples JOINTUPLES
46 * while (outer < inner) SKIPOUTER_TEST
47 * advance outer SKIPOUTER_ADVANCE
48 * if (outer > inner) SKIPOUTER_TEST
49 * Skip Inner SKIPINNER
52 * Skip Inner { SKIPINNER_BEGIN
53 * if (inner == outer) Join Tuples JOINTUPLES
54 * while (outer > inner) SKIPINNER_TEST
55 * advance inner SKIPINNER_ADVANCE
56 * if (outer < inner) SKIPINNER_TEST
57 * Skip Outer SKIPOUTER
60 * The merge join operation is coded in the fashion
61 * of a state machine. At each state, we do something and then
62 * proceed to another state. This state is stored in the node's
63 * execution state information and is preserved across calls to
64 * ExecMergeJoin. -cim 10/31/89
69 #include "access/heapam.h"
70 #include "access/printtup.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 new lists
95 * of the forms ((< expr expr) (< expr expr) ...) and
96 * ((> expr expr) (> expr expr) ...). These lists will be used
97 * by ExecMergeJoin() to determine if we should skip tuples.
98 * (We expect there to be suitable operators because the "=" operators
99 * were marked mergejoinable; however, there might be a different
100 * one needed in each qual clause.)
101 * ----------------------------------------------------------------
104 MJFormSkipQuals(List *qualList, List **ltQuals, List **gtQuals)
110 * Make modifiable copies of the qualList.
112 *ltQuals = (List *) copyObject((Node *) qualList);
113 *gtQuals = (List *) copyObject((Node *) qualList);
116 * Scan both lists in parallel, so that we can update the operators
117 * with the minimum number of syscache searches.
120 foreach(gtcdr, *gtQuals)
122 OpExpr *ltop = (OpExpr *) lfirst(ltcdr);
123 OpExpr *gtop = (OpExpr *) lfirst(gtcdr);
126 * The two ops should be identical, so use either one for lookup.
128 if (!IsA(ltop, OpExpr))
129 elog(ERROR, "MJFormSkipQuals: op not an OpExpr!");
132 * Lookup the operators, and replace the data in the copied
135 op_mergejoin_crossops(ltop->opno,
140 ltop->op_fcache = NULL;
141 gtop->op_fcache = NULL;
143 ltcdr = lnext(ltcdr);
147 /* ----------------------------------------------------------------
150 * Compare the keys according to 'compareQual' which is of the
151 * form: { (key1a > key2a) (key1b > key2b) ... }.
153 * (actually, it could also be of the form (key1a < key2a)...)
155 * This is different from calling ExecQual because ExecQual returns
156 * true only if ALL the comparison clauses are satisfied.
157 * However, there is an order of significance among the keys with
158 * the first keys being most significant. Therefore, the clauses
159 * are evaluated in order and the 'compareQual' is satisfied
160 * if (key1i > key2i) is true and (key1j = key2j) for 0 < j < i.
161 * We use the original mergeclause items to detect equality.
162 * ----------------------------------------------------------------
165 MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
168 MemoryContext oldContext;
173 * Do expression eval in short-lived context.
175 oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
178 * for each pair of clauses, test them until our compare conditions
179 * are satisfied. if we reach the end of the list, none of our key
180 * greater-than conditions were satisfied so we return false.
182 result = false; /* assume 'false' result */
185 foreach(clause, compareQual)
191 * first test if our compare clause is satisfied. if so then
194 * A NULL result is considered false.
196 const_value = ExecEvalExpr((Node *) lfirst(clause), econtext,
199 if (DatumGetBool(const_value) && !isNull)
206 * ok, the compare clause failed so we test if the keys are
207 * equal... if key1 != key2, we return false. otherwise
208 * key1 = key2 so we move on to the next pair of keys.
211 const_value = ExecEvalExpr((Node *) lfirst(eqclause),
216 if (!DatumGetBool(const_value) || isNull)
217 break; /* return false */
219 eqclause = lnext(eqclause);
222 MemoryContextSwitchTo(oldContext);
227 /* ----------------------------------------------------------------
230 * This function is called through the MJ_dump() macro
231 * when EXEC_MERGEJOINDEBUG is defined
232 * ----------------------------------------------------------------
234 #ifdef EXEC_MERGEJOINDEBUG
237 ExecMergeTupleDumpOuter(MergeJoinState *mergestate)
239 TupleTableSlot *outerSlot = mergestate->mj_OuterTupleSlot;
241 printf("==== outer tuple ====\n");
242 if (TupIsNull(outerSlot))
245 MJ_debugtup(outerSlot->val,
246 outerSlot->ttc_tupleDescriptor);
250 ExecMergeTupleDumpInner(MergeJoinState *mergestate)
252 TupleTableSlot *innerSlot = mergestate->mj_InnerTupleSlot;
254 printf("==== inner tuple ====\n");
255 if (TupIsNull(innerSlot))
258 MJ_debugtup(innerSlot->val,
259 innerSlot->ttc_tupleDescriptor);
263 ExecMergeTupleDumpMarked(MergeJoinState *mergestate)
265 TupleTableSlot *markedSlot = mergestate->mj_MarkedTupleSlot;
267 printf("==== marked tuple ====\n");
268 if (TupIsNull(markedSlot))
271 MJ_debugtup(markedSlot->val,
272 markedSlot->ttc_tupleDescriptor);
276 ExecMergeTupleDump(MergeJoinState *mergestate)
278 printf("******** ExecMergeTupleDump ********\n");
280 ExecMergeTupleDumpOuter(mergestate);
281 ExecMergeTupleDumpInner(mergestate);
282 ExecMergeTupleDumpMarked(mergestate);
284 printf("******** \n");
288 /* ----------------------------------------------------------------
292 * Details of the merge-join routines:
294 * (1) ">" and "<" operators
296 * Merge-join is done by joining the inner and outer tuples satisfying
297 * the join clauses of the form ((= outerKey innerKey) ...).
298 * The join clauses is provided by the query planner and may contain
299 * more than one (= outerKey innerKey) clauses (for composite key).
301 * However, the query executor needs to know whether an outer
302 * tuple is "greater/smaller" than an inner tuple so that it can
303 * "synchronize" the two relations. For e.g., consider the following
306 * outer: (0 ^1 1 2 5 5 5 6 6 7) current tuple: 1
307 * inner: (1 ^3 5 5 5 5 6) current tuple: 3
309 * To continue the merge-join, the executor needs to scan both inner
310 * and outer relations till the matching tuples 5. It needs to know
311 * that currently inner tuple 3 is "greater" than outer tuple 1 and
312 * therefore it should scan the outer relation first to find a
313 * matching tuple and so on.
315 * Therefore, when initializing the merge-join node, the executor
316 * creates the "greater/smaller" clause by substituting the "="
317 * operator in the join clauses with the corresponding ">" operator.
318 * The opposite "smaller/greater" clause is formed by substituting "<".
320 * Note: prior to v6.5, the relational clauses were formed using the
321 * sort op used to sort the inner relation, which of course would fail
322 * if the outer and inner keys were of different data types.
323 * In the current code, we instead assume that operators named "<" and ">"
324 * will do the right thing. This should be true since the mergejoin "="
325 * operator's pg_operator entry will have told the planner to sort by
326 * "<" for each of the left and right sides.
328 * (2) repositioning inner "cursor"
330 * Consider the above relations and suppose that the executor has
331 * just joined the first outer "5" with the last inner "5". The
332 * next step is of course to join the second outer "5" with all
333 * the inner "5's". This requires repositioning the inner "cursor"
334 * to point at the first inner "5". This is done by "marking" the
335 * first inner 5 and restore the "cursor" to it before joining
336 * with the second outer 5. The access method interface provides
337 * routines to mark and restore to a tuple.
338 * ----------------------------------------------------------------
341 ExecMergeJoin(MergeJoinState *node)
344 ScanDirection direction;
352 PlanState *innerPlan;
353 TupleTableSlot *innerTupleSlot;
354 PlanState *outerPlan;
355 TupleTableSlot *outerTupleSlot;
356 ExprContext *econtext;
361 * get information from node
363 estate = node->js.ps.state;
364 direction = estate->es_direction;
365 innerPlan = innerPlanState(node);
366 outerPlan = outerPlanState(node);
367 econtext = node->js.ps.ps_ExprContext;
368 mergeclauses = node->mergeclauses;
369 joinqual = node->js.joinqual;
370 otherqual = node->js.ps.qual;
372 switch (node->js.jointype)
391 elog(ERROR, "ExecMergeJoin: unsupported join type %d",
392 (int) node->js.jointype);
393 doFillOuter = false; /* keep compiler quiet */
398 if (ScanDirectionIsForward(direction))
400 outerSkipQual = node->mj_OuterSkipQual;
401 innerSkipQual = node->mj_InnerSkipQual;
405 outerSkipQual = node->mj_InnerSkipQual;
406 innerSkipQual = node->mj_OuterSkipQual;
410 * Check to see if we're still projecting out tuples from a previous
411 * join tuple (because there is a function-returning-set in the
412 * projection expressions). If so, try to project another one.
414 if (node->js.ps.ps_TupFromTlist)
416 TupleTableSlot *result;
419 result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
420 if (isDone == ExprMultipleResult)
422 /* Done with that source tuple... */
423 node->js.ps.ps_TupFromTlist = false;
427 * Reset per-tuple memory context to free any expression evaluation
428 * storage allocated in the previous tuple cycle. Note this can't
429 * happen until we're done projecting out tuples from a join tuple.
431 ResetExprContext(econtext);
434 * ok, everything is setup.. let's go to work
439 * get the current state of the join and do things accordingly.
440 * Note: The join states are highlighted with 32-* comments for
441 * improved readability.
445 switch (node->mj_JoinState)
448 * EXEC_MJ_INITIALIZE means that this is the first time
449 * ExecMergeJoin() has been called and so we have to fetch
450 * the first tuple for both outer and inner subplans. If
451 * we fail to get a tuple here, then that subplan is
452 * empty, and we either end the join or go to one of the
453 * fill-remaining-tuples states.
455 case EXEC_MJ_INITIALIZE:
456 MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE\n");
458 outerTupleSlot = ExecProcNode(outerPlan);
459 node->mj_OuterTupleSlot = outerTupleSlot;
460 if (TupIsNull(outerTupleSlot))
462 MJ_printf("ExecMergeJoin: outer subplan is empty\n");
466 * Need to emit right-join tuples for remaining
467 * inner tuples. We set MatchedInner = true to
468 * force the ENDOUTER state to advance inner.
470 node->mj_JoinState = EXEC_MJ_ENDOUTER;
471 node->mj_MatchedInner = true;
474 /* Otherwise we're done. */
478 innerTupleSlot = ExecProcNode(innerPlan);
479 node->mj_InnerTupleSlot = innerTupleSlot;
480 if (TupIsNull(innerTupleSlot))
482 MJ_printf("ExecMergeJoin: inner subplan is empty\n");
486 * Need to emit left-join tuples for all outer
487 * tuples, including the one we just fetched. We
488 * set MatchedOuter = false to force the ENDINNER
489 * state to emit this tuple before advancing
492 node->mj_JoinState = EXEC_MJ_ENDINNER;
493 node->mj_MatchedOuter = false;
496 /* Otherwise we're done. */
501 * OK, we have the initial tuples. Begin by skipping
502 * unmatched inner tuples.
504 node->mj_JoinState = EXEC_MJ_SKIPINNER_BEGIN;
508 * EXEC_MJ_JOINMARK means we have just found a new outer
509 * tuple and a possible matching inner tuple. This is the
510 * case after the INITIALIZE, SKIPOUTER or SKIPINNER
513 case EXEC_MJ_JOINMARK:
514 MJ_printf("ExecMergeJoin: EXEC_MJ_JOINMARK\n");
516 ExecMarkPos(innerPlan);
518 MarkInnerTuple(node->mj_InnerTupleSlot, node);
520 node->mj_JoinState = EXEC_MJ_JOINTEST;
524 * EXEC_MJ_JOINTEST means we have two tuples which might
525 * satisfy the merge clause, so we test them.
527 * If they do satisfy, then we join them and move on to the
528 * next inner tuple (EXEC_MJ_JOINTUPLES).
530 * If they do not satisfy then advance to next outer tuple.
532 case EXEC_MJ_JOINTEST:
533 MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n");
535 ResetExprContext(econtext);
537 outerTupleSlot = node->mj_OuterTupleSlot;
538 econtext->ecxt_outertuple = outerTupleSlot;
539 innerTupleSlot = node->mj_InnerTupleSlot;
540 econtext->ecxt_innertuple = innerTupleSlot;
542 qualResult = ExecQual(mergeclauses, econtext, false);
543 MJ_DEBUG_QUAL(mergeclauses, qualResult);
546 node->mj_JoinState = EXEC_MJ_JOINTUPLES;
548 node->mj_JoinState = EXEC_MJ_NEXTOUTER;
552 * EXEC_MJ_JOINTUPLES means we have two tuples which
553 * satisfied the merge clause so we join them and then
554 * proceed to get the next inner tuple (EXEC_NEXT_INNER).
556 case EXEC_MJ_JOINTUPLES:
557 MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");
559 node->mj_JoinState = EXEC_MJ_NEXTINNER;
562 * Check the extra qual conditions to see if we actually
563 * want to return this join tuple. If not, can proceed
564 * with merge. We must distinguish the additional
565 * joinquals (which must pass to consider the tuples
566 * "matched" for outer-join logic) from the otherquals
567 * (which must pass before we actually return the tuple).
569 * We don't bother with a ResetExprContext here, on the
570 * assumption that we just did one before checking the
571 * merge qual. One per tuple should be sufficient. Also,
572 * the econtext's tuple pointers were set up before
573 * checking the merge qual, so we needn't do it again.
575 qualResult = (joinqual == NIL ||
576 ExecQual(joinqual, econtext, false));
577 MJ_DEBUG_QUAL(joinqual, qualResult);
581 node->mj_MatchedOuter = true;
582 node->mj_MatchedInner = true;
584 qualResult = (otherqual == NIL ||
585 ExecQual(otherqual, econtext, false));
586 MJ_DEBUG_QUAL(otherqual, qualResult);
591 * qualification succeeded. now form the desired
592 * projection tuple and return the slot containing
595 TupleTableSlot *result;
598 MJ_printf("ExecMergeJoin: returning tuple\n");
600 result = ExecProject(node->js.ps.ps_ProjInfo,
603 if (isDone != ExprEndResult)
605 node->js.ps.ps_TupFromTlist =
606 (isDone == ExprMultipleResult);
614 * EXEC_MJ_NEXTINNER means advance the inner scan to the
615 * next tuple. If the tuple is not nil, we then proceed to
616 * test it against the join qualification.
618 * Before advancing, we check to see if we must emit an
619 * outer-join fill tuple for this inner tuple.
621 case EXEC_MJ_NEXTINNER:
622 MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\n");
624 if (doFillInner && !node->mj_MatchedInner)
627 * Generate a fake join tuple with nulls for the outer
628 * tuple, and return it if it passes the non-join
631 node->mj_MatchedInner = true; /* do it only once */
633 ResetExprContext(econtext);
635 outerTupleSlot = node->mj_NullOuterTupleSlot;
636 econtext->ecxt_outertuple = outerTupleSlot;
637 innerTupleSlot = node->mj_InnerTupleSlot;
638 econtext->ecxt_innertuple = innerTupleSlot;
640 if (ExecQual(otherqual, econtext, false))
643 * qualification succeeded. now form the desired
644 * projection tuple and return the slot containing
647 TupleTableSlot *result;
650 MJ_printf("ExecMergeJoin: returning fill tuple\n");
652 result = ExecProject(node->js.ps.ps_ProjInfo,
655 if (isDone != ExprEndResult)
657 node->js.ps.ps_TupFromTlist =
658 (isDone == ExprMultipleResult);
665 * now we get the next inner tuple, if any
667 innerTupleSlot = ExecProcNode(innerPlan);
668 node->mj_InnerTupleSlot = innerTupleSlot;
669 MJ_DEBUG_PROC_NODE(innerTupleSlot);
670 node->mj_MatchedInner = false;
672 if (TupIsNull(innerTupleSlot))
673 node->mj_JoinState = EXEC_MJ_NEXTOUTER;
675 node->mj_JoinState = EXEC_MJ_JOINTEST;
678 /*-------------------------------------------
679 * EXEC_MJ_NEXTOUTER means
682 * outer tuple - 5 5 - marked tuple
687 * we know we just bumped into the
688 * first inner tuple > current outer tuple
689 * so get a new outer tuple and then
690 * proceed to test it against the marked tuple
691 * (EXEC_MJ_TESTOUTER)
693 * Before advancing, we check to see if we must emit an
694 * outer-join fill tuple for this outer tuple.
695 *------------------------------------------------
697 case EXEC_MJ_NEXTOUTER:
698 MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n");
700 if (doFillOuter && !node->mj_MatchedOuter)
703 * Generate a fake join tuple with nulls for the inner
704 * tuple, and return it if it passes the non-join
707 node->mj_MatchedOuter = true; /* do it only once */
709 ResetExprContext(econtext);
711 outerTupleSlot = node->mj_OuterTupleSlot;
712 econtext->ecxt_outertuple = outerTupleSlot;
713 innerTupleSlot = node->mj_NullInnerTupleSlot;
714 econtext->ecxt_innertuple = innerTupleSlot;
716 if (ExecQual(otherqual, econtext, false))
719 * qualification succeeded. now form the desired
720 * projection tuple and return the slot containing
723 TupleTableSlot *result;
726 MJ_printf("ExecMergeJoin: returning fill tuple\n");
728 result = ExecProject(node->js.ps.ps_ProjInfo,
731 if (isDone != ExprEndResult)
733 node->js.ps.ps_TupFromTlist =
734 (isDone == ExprMultipleResult);
741 * now we get the next outer tuple, if any
743 outerTupleSlot = ExecProcNode(outerPlan);
744 node->mj_OuterTupleSlot = outerTupleSlot;
745 MJ_DEBUG_PROC_NODE(outerTupleSlot);
746 node->mj_MatchedOuter = false;
749 * if the outer tuple is null then we are done with the
750 * join, unless we have inner tuples we need to null-fill.
752 if (TupIsNull(outerTupleSlot))
754 MJ_printf("ExecMergeJoin: end of outer subplan\n");
755 innerTupleSlot = node->mj_InnerTupleSlot;
756 if (doFillInner && !TupIsNull(innerTupleSlot))
759 * Need to emit right-join tuples for remaining
762 node->mj_JoinState = EXEC_MJ_ENDOUTER;
765 /* Otherwise we're done. */
769 node->mj_JoinState = EXEC_MJ_TESTOUTER;
772 /*--------------------------------------------------------
773 * EXEC_MJ_TESTOUTER If the new outer tuple and the marked
774 * tuple satisfy the merge clause then we know we have
775 * duplicates in the outer scan so we have to restore the
776 * inner scan to the marked tuple and proceed to join the
777 * new outer tuples with the inner tuples (EXEC_MJ_JOINTEST)
779 * This is the case when
783 * new outer tuple - 5 5
787 * new outer tuple = marked tuple
789 * If the outer tuple fails the test, then we know we have
790 * to proceed to skip outer tuples until outer >= inner
791 * (EXEC_MJ_SKIPOUTER).
793 * This is the case when
798 * new outer tuple - 6 8 - inner tuple
802 * new outer tuple > marked tuple
804 *---------------------------------------------------------
806 case EXEC_MJ_TESTOUTER:
807 MJ_printf("ExecMergeJoin: EXEC_MJ_TESTOUTER\n");
810 * here we compare the outer tuple with the marked inner
813 ResetExprContext(econtext);
815 outerTupleSlot = node->mj_OuterTupleSlot;
816 econtext->ecxt_outertuple = outerTupleSlot;
817 innerTupleSlot = node->mj_MarkedTupleSlot;
818 econtext->ecxt_innertuple = innerTupleSlot;
820 qualResult = ExecQual(mergeclauses, econtext, false);
821 MJ_DEBUG_QUAL(mergeclauses, qualResult);
826 * the merge clause matched so now we restore the
827 * inner scan position to the first mark, and loop
828 * back to JOINTEST. Actually, since we know the
829 * mergeclause matches, we can skip JOINTEST and go
830 * straight to JOINTUPLES.
832 * NOTE: we do not need to worry about the MatchedInner
833 * state for the rescanned inner tuples. We know all
834 * of them will match this new outer tuple and
835 * therefore won't be emitted as fill tuples. This
836 * works *only* because we require the extra joinquals
837 * to be nil when doing a right or full join ---
838 * otherwise some of the rescanned tuples might fail
839 * the extra joinquals.
841 ExecRestrPos(innerPlan);
842 node->mj_JoinState = EXEC_MJ_JOINTUPLES;
847 * if the inner tuple was nil and the new outer
848 * tuple didn't match the marked outer tuple then
854 * 6 nil - inner tuple
857 * which means that all subsequent outer tuples will be
858 * larger than our marked inner tuples. So we're done.
861 innerTupleSlot = node->mj_InnerTupleSlot;
862 if (TupIsNull(innerTupleSlot))
867 * Need to emit left-join tuples for remaining
870 node->mj_JoinState = EXEC_MJ_ENDINNER;
873 /* Otherwise we're done. */
877 /* continue on to skip outer tuples */
878 node->mj_JoinState = EXEC_MJ_SKIPOUTER_BEGIN;
882 /*----------------------------------------------------------
883 * EXEC_MJ_SKIPOUTER means skip over tuples in the outer plan
884 * until we find an outer tuple >= current inner tuple.
891 * outer tuple - 6 8 - inner tuple
895 * we have to advance the outer scan
896 * until we find the outer 8.
898 * To avoid redundant tests, we divide this into three
899 * sub-states: BEGIN, TEST, ADVANCE.
900 *----------------------------------------------------------
902 case EXEC_MJ_SKIPOUTER_BEGIN:
903 MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_BEGIN\n");
906 * before we advance, make sure the current tuples do not
907 * satisfy the mergeclauses. If they do, then we update
908 * the marked tuple and go join them.
910 ResetExprContext(econtext);
912 outerTupleSlot = node->mj_OuterTupleSlot;
913 econtext->ecxt_outertuple = outerTupleSlot;
914 innerTupleSlot = node->mj_InnerTupleSlot;
915 econtext->ecxt_innertuple = innerTupleSlot;
917 qualResult = ExecQual(mergeclauses, econtext, false);
918 MJ_DEBUG_QUAL(mergeclauses, qualResult);
922 ExecMarkPos(innerPlan);
924 MarkInnerTuple(innerTupleSlot, node);
926 node->mj_JoinState = EXEC_MJ_JOINTUPLES;
930 node->mj_JoinState = EXEC_MJ_SKIPOUTER_TEST;
933 case EXEC_MJ_SKIPOUTER_TEST:
934 MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_TEST\n");
937 * ok, now test the skip qualification
939 outerTupleSlot = node->mj_OuterTupleSlot;
940 econtext->ecxt_outertuple = outerTupleSlot;
941 innerTupleSlot = node->mj_InnerTupleSlot;
942 econtext->ecxt_innertuple = innerTupleSlot;
944 compareResult = MergeCompare(mergeclauses,
948 MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult);
951 * compareResult is true as long as we should continue
952 * skipping outer tuples.
956 node->mj_JoinState = EXEC_MJ_SKIPOUTER_ADVANCE;
961 * now check the inner skip qual to see if we should now
962 * skip inner tuples... if we fail the inner skip qual,
963 * then we know we have a new pair of matching tuples.
965 compareResult = MergeCompare(mergeclauses,
969 MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult);
972 node->mj_JoinState = EXEC_MJ_SKIPINNER_BEGIN;
974 node->mj_JoinState = EXEC_MJ_JOINMARK;
978 * Before advancing, we check to see if we must emit an
979 * outer-join fill tuple for this outer tuple.
981 case EXEC_MJ_SKIPOUTER_ADVANCE:
982 MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_ADVANCE\n");
984 if (doFillOuter && !node->mj_MatchedOuter)
987 * Generate a fake join tuple with nulls for the inner
988 * tuple, and return it if it passes the non-join
991 node->mj_MatchedOuter = true; /* do it only once */
993 ResetExprContext(econtext);
995 outerTupleSlot = node->mj_OuterTupleSlot;
996 econtext->ecxt_outertuple = outerTupleSlot;
997 innerTupleSlot = node->mj_NullInnerTupleSlot;
998 econtext->ecxt_innertuple = innerTupleSlot;
1000 if (ExecQual(otherqual, econtext, false))
1003 * qualification succeeded. now form the desired
1004 * projection tuple and return the slot containing
1007 TupleTableSlot *result;
1008 ExprDoneCond isDone;
1010 MJ_printf("ExecMergeJoin: returning fill tuple\n");
1012 result = ExecProject(node->js.ps.ps_ProjInfo,
1015 if (isDone != ExprEndResult)
1017 node->js.ps.ps_TupFromTlist =
1018 (isDone == ExprMultipleResult);
1025 * now we get the next outer tuple, if any
1027 outerTupleSlot = ExecProcNode(outerPlan);
1028 node->mj_OuterTupleSlot = outerTupleSlot;
1029 MJ_DEBUG_PROC_NODE(outerTupleSlot);
1030 node->mj_MatchedOuter = false;
1033 * if the outer tuple is null then we are done with the
1034 * join, unless we have inner tuples we need to null-fill.
1036 if (TupIsNull(outerTupleSlot))
1038 MJ_printf("ExecMergeJoin: end of outer subplan\n");
1039 innerTupleSlot = node->mj_InnerTupleSlot;
1040 if (doFillInner && !TupIsNull(innerTupleSlot))
1043 * Need to emit right-join tuples for remaining
1046 node->mj_JoinState = EXEC_MJ_ENDOUTER;
1049 /* Otherwise we're done. */
1054 * otherwise test the new tuple against the skip qual.
1056 node->mj_JoinState = EXEC_MJ_SKIPOUTER_TEST;
1059 /*-----------------------------------------------------------
1060 * EXEC_MJ_SKIPINNER means skip over tuples in the inner plan
1061 * until we find an inner tuple >= current outer tuple.
1068 * outer tuple - 12 8 - inner tuple
1072 * we have to advance the inner scan
1073 * until we find the inner 12.
1075 * To avoid redundant tests, we divide this into three
1076 * sub-states: BEGIN, TEST, ADVANCE.
1077 *-------------------------------------------------------
1079 case EXEC_MJ_SKIPINNER_BEGIN:
1080 MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_BEGIN\n");
1083 * before we advance, make sure the current tuples do not
1084 * satisfy the mergeclauses. If they do, then we update
1085 * the marked tuple and go join them.
1087 ResetExprContext(econtext);
1089 outerTupleSlot = node->mj_OuterTupleSlot;
1090 econtext->ecxt_outertuple = outerTupleSlot;
1091 innerTupleSlot = node->mj_InnerTupleSlot;
1092 econtext->ecxt_innertuple = innerTupleSlot;
1094 qualResult = ExecQual(mergeclauses, econtext, false);
1095 MJ_DEBUG_QUAL(mergeclauses, qualResult);
1099 ExecMarkPos(innerPlan);
1101 MarkInnerTuple(innerTupleSlot, node);
1103 node->mj_JoinState = EXEC_MJ_JOINTUPLES;
1107 node->mj_JoinState = EXEC_MJ_SKIPINNER_TEST;
1110 case EXEC_MJ_SKIPINNER_TEST:
1111 MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_TEST\n");
1114 * ok, now test the skip qualification
1116 outerTupleSlot = node->mj_OuterTupleSlot;
1117 econtext->ecxt_outertuple = outerTupleSlot;
1118 innerTupleSlot = node->mj_InnerTupleSlot;
1119 econtext->ecxt_innertuple = innerTupleSlot;
1121 compareResult = MergeCompare(mergeclauses,
1125 MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult);
1128 * compareResult is true as long as we should continue
1129 * skipping inner tuples.
1133 node->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE;
1138 * now check the outer skip qual to see if we should now
1139 * skip outer tuples... if we fail the outer skip qual,
1140 * then we know we have a new pair of matching tuples.
1142 compareResult = MergeCompare(mergeclauses,
1146 MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult);
1149 node->mj_JoinState = EXEC_MJ_SKIPOUTER_BEGIN;
1151 node->mj_JoinState = EXEC_MJ_JOINMARK;
1155 * Before advancing, we check to see if we must emit an
1156 * outer-join fill tuple for this inner tuple.
1158 case EXEC_MJ_SKIPINNER_ADVANCE:
1159 MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_ADVANCE\n");
1161 if (doFillInner && !node->mj_MatchedInner)
1164 * Generate a fake join tuple with nulls for the outer
1165 * tuple, and return it if it passes the non-join
1168 node->mj_MatchedInner = true; /* do it only once */
1170 ResetExprContext(econtext);
1172 outerTupleSlot = node->mj_NullOuterTupleSlot;
1173 econtext->ecxt_outertuple = outerTupleSlot;
1174 innerTupleSlot = node->mj_InnerTupleSlot;
1175 econtext->ecxt_innertuple = innerTupleSlot;
1177 if (ExecQual(otherqual, econtext, false))
1180 * qualification succeeded. now form the desired
1181 * projection tuple and return the slot containing
1184 TupleTableSlot *result;
1185 ExprDoneCond isDone;
1187 MJ_printf("ExecMergeJoin: returning fill tuple\n");
1189 result = ExecProject(node->js.ps.ps_ProjInfo,
1192 if (isDone != ExprEndResult)
1194 node->js.ps.ps_TupFromTlist =
1195 (isDone == ExprMultipleResult);
1202 * now we get the next inner tuple, if any
1204 innerTupleSlot = ExecProcNode(innerPlan);
1205 node->mj_InnerTupleSlot = innerTupleSlot;
1206 MJ_DEBUG_PROC_NODE(innerTupleSlot);
1207 node->mj_MatchedInner = false;
1210 * if the inner tuple is null then we are done with the
1211 * join, unless we have outer tuples we need to null-fill.
1213 if (TupIsNull(innerTupleSlot))
1215 MJ_printf("ExecMergeJoin: end of inner subplan\n");
1216 outerTupleSlot = node->mj_OuterTupleSlot;
1217 if (doFillOuter && !TupIsNull(outerTupleSlot))
1220 * Need to emit left-join tuples for remaining
1223 node->mj_JoinState = EXEC_MJ_ENDINNER;
1226 /* Otherwise we're done. */
1231 * otherwise test the new tuple against the skip qual.
1233 node->mj_JoinState = EXEC_MJ_SKIPINNER_TEST;
1237 * EXEC_MJ_ENDOUTER means we have run out of outer tuples,
1238 * but are doing a right/full join and therefore must
1239 * null- fill any remaing unmatched inner tuples.
1241 case EXEC_MJ_ENDOUTER:
1242 MJ_printf("ExecMergeJoin: EXEC_MJ_ENDOUTER\n");
1244 Assert(doFillInner);
1246 if (!node->mj_MatchedInner)
1249 * Generate a fake join tuple with nulls for the outer
1250 * tuple, and return it if it passes the non-join
1253 node->mj_MatchedInner = true; /* do it only once */
1255 ResetExprContext(econtext);
1257 outerTupleSlot = node->mj_NullOuterTupleSlot;
1258 econtext->ecxt_outertuple = outerTupleSlot;
1259 innerTupleSlot = node->mj_InnerTupleSlot;
1260 econtext->ecxt_innertuple = innerTupleSlot;
1262 if (ExecQual(otherqual, econtext, false))
1265 * qualification succeeded. now form the desired
1266 * projection tuple and return the slot containing
1269 TupleTableSlot *result;
1270 ExprDoneCond isDone;
1272 MJ_printf("ExecMergeJoin: returning fill tuple\n");
1274 result = ExecProject(node->js.ps.ps_ProjInfo,
1277 if (isDone != ExprEndResult)
1279 node->js.ps.ps_TupFromTlist =
1280 (isDone == ExprMultipleResult);
1287 * now we get the next inner tuple, if any
1289 innerTupleSlot = ExecProcNode(innerPlan);
1290 node->mj_InnerTupleSlot = innerTupleSlot;
1291 MJ_DEBUG_PROC_NODE(innerTupleSlot);
1292 node->mj_MatchedInner = false;
1294 if (TupIsNull(innerTupleSlot))
1296 MJ_printf("ExecMergeJoin: end of inner subplan\n");
1300 /* Else remain in ENDOUTER state and process next tuple. */
1304 * EXEC_MJ_ENDINNER means we have run out of inner tuples,
1305 * but are doing a left/full join and therefore must null-
1306 * fill any remaing unmatched outer tuples.
1308 case EXEC_MJ_ENDINNER:
1309 MJ_printf("ExecMergeJoin: EXEC_MJ_ENDINNER\n");
1311 Assert(doFillOuter);
1313 if (!node->mj_MatchedOuter)
1316 * Generate a fake join tuple with nulls for the inner
1317 * tuple, and return it if it passes the non-join
1320 node->mj_MatchedOuter = true; /* do it only once */
1322 ResetExprContext(econtext);
1324 outerTupleSlot = node->mj_OuterTupleSlot;
1325 econtext->ecxt_outertuple = outerTupleSlot;
1326 innerTupleSlot = node->mj_NullInnerTupleSlot;
1327 econtext->ecxt_innertuple = innerTupleSlot;
1329 if (ExecQual(otherqual, econtext, false))
1332 * qualification succeeded. now form the desired
1333 * projection tuple and return the slot containing
1336 TupleTableSlot *result;
1337 ExprDoneCond isDone;
1339 MJ_printf("ExecMergeJoin: returning fill tuple\n");
1341 result = ExecProject(node->js.ps.ps_ProjInfo,
1344 if (isDone != ExprEndResult)
1346 node->js.ps.ps_TupFromTlist =
1347 (isDone == ExprMultipleResult);
1354 * now we get the next outer tuple, if any
1356 outerTupleSlot = ExecProcNode(outerPlan);
1357 node->mj_OuterTupleSlot = outerTupleSlot;
1358 MJ_DEBUG_PROC_NODE(outerTupleSlot);
1359 node->mj_MatchedOuter = false;
1361 if (TupIsNull(outerTupleSlot))
1363 MJ_printf("ExecMergeJoin: end of outer subplan\n");
1367 /* Else remain in ENDINNER state and process next tuple. */
1371 * if we get here it means our code is fouled up and so we
1372 * just end the join prematurely.
1375 elog(WARNING, "ExecMergeJoin: invalid join state %d, aborting",
1376 node->mj_JoinState);
1382 /* ----------------------------------------------------------------
1384 * ----------------------------------------------------------------
1387 ExecInitMergeJoin(MergeJoin *node, EState *estate)
1389 MergeJoinState *mergestate;
1391 MJ1_printf("ExecInitMergeJoin: %s\n",
1392 "initializing node");
1395 * create state structure
1397 mergestate = makeNode(MergeJoinState);
1398 mergestate->js.ps.plan = (Plan *) node;
1399 mergestate->js.ps.state = estate;
1402 * Miscellaneous initialization
1404 * create expression context for node
1406 ExecAssignExprContext(estate, &mergestate->js.ps);
1409 * initialize child expressions
1411 mergestate->js.ps.targetlist = (List *)
1412 ExecInitExpr((Node *) node->join.plan.targetlist,
1413 (PlanState *) mergestate);
1414 mergestate->js.ps.qual = (List *)
1415 ExecInitExpr((Node *) node->join.plan.qual,
1416 (PlanState *) mergestate);
1417 mergestate->js.jointype = node->join.jointype;
1418 mergestate->js.joinqual = (List *)
1419 ExecInitExpr((Node *) node->join.joinqual,
1420 (PlanState *) mergestate);
1421 mergestate->mergeclauses = (List *)
1422 ExecInitExpr((Node *) node->mergeclauses,
1423 (PlanState *) mergestate);
1426 * initialize child nodes
1428 outerPlanState(mergestate) = ExecInitNode(outerPlan(node), estate);
1429 innerPlanState(mergestate) = ExecInitNode(innerPlan(node), estate);
1431 #define MERGEJOIN_NSLOTS 4
1434 * tuple table initialization
1436 ExecInitResultTupleSlot(estate, &mergestate->js.ps);
1438 mergestate->mj_MarkedTupleSlot = ExecInitExtraTupleSlot(estate);
1439 ExecSetSlotDescriptor(mergestate->mj_MarkedTupleSlot,
1440 ExecGetTupType(innerPlanState(mergestate)),
1443 switch (node->join.jointype)
1448 mergestate->mj_NullInnerTupleSlot =
1449 ExecInitNullTupleSlot(estate,
1450 ExecGetTupType(innerPlanState(mergestate)));
1453 mergestate->mj_NullOuterTupleSlot =
1454 ExecInitNullTupleSlot(estate,
1455 ExecGetTupType(outerPlanState(mergestate)));
1458 * Can't handle right or full join with non-nil extra
1461 if (node->join.joinqual != NIL)
1462 elog(ERROR, "RIGHT JOIN is only supported with mergejoinable join conditions");
1465 mergestate->mj_NullOuterTupleSlot =
1466 ExecInitNullTupleSlot(estate,
1467 ExecGetTupType(outerPlanState(mergestate)));
1468 mergestate->mj_NullInnerTupleSlot =
1469 ExecInitNullTupleSlot(estate,
1470 ExecGetTupType(innerPlanState(mergestate)));
1473 * Can't handle right or full join with non-nil extra
1476 if (node->join.joinqual != NIL)
1477 elog(ERROR, "FULL JOIN is only supported with mergejoinable join conditions");
1480 elog(ERROR, "ExecInitMergeJoin: unsupported join type %d",
1481 (int) node->join.jointype);
1485 * initialize tuple type and projection info
1487 ExecAssignResultTypeFromTL(&mergestate->js.ps);
1488 ExecAssignProjectionInfo(&mergestate->js.ps);
1491 * form merge skip qualifications
1493 MJFormSkipQuals(node->mergeclauses,
1494 &mergestate->mj_OuterSkipQual,
1495 &mergestate->mj_InnerSkipQual);
1497 MJ_printf("\nExecInitMergeJoin: OuterSkipQual is ");
1498 MJ_nodeDisplay(mergestate->mj_OuterSkipQual);
1499 MJ_printf("\nExecInitMergeJoin: InnerSkipQual is ");
1500 MJ_nodeDisplay(mergestate->mj_InnerSkipQual);
1504 * initialize join state
1506 mergestate->mj_JoinState = EXEC_MJ_INITIALIZE;
1507 mergestate->js.ps.ps_TupFromTlist = false;
1508 mergestate->mj_MatchedOuter = false;
1509 mergestate->mj_MatchedInner = false;
1510 mergestate->mj_OuterTupleSlot = NULL;
1511 mergestate->mj_InnerTupleSlot = NULL;
1514 * initialization successful
1516 MJ1_printf("ExecInitMergeJoin: %s\n",
1517 "node initialized");
1523 ExecCountSlotsMergeJoin(MergeJoin *node)
1525 return ExecCountSlotsNode(outerPlan((Plan *) node)) +
1526 ExecCountSlotsNode(innerPlan((Plan *) node)) +
1530 /* ----------------------------------------------------------------
1534 * frees storage allocated through C routines.
1535 * ----------------------------------------------------------------
1538 ExecEndMergeJoin(MergeJoinState *node)
1540 MJ1_printf("ExecEndMergeJoin: %s\n",
1541 "ending node processing");
1544 * Free the projection info and the scan attribute info
1546 ExecFreeProjectionInfo(&node->js.ps);
1547 ExecFreeExprContext(&node->js.ps);
1550 * shut down the subplans
1552 ExecEndNode(innerPlanState(node));
1553 ExecEndNode(outerPlanState(node));
1556 * clean out the tuple table
1558 ExecClearTuple(node->js.ps.ps_ResultTupleSlot);
1559 ExecClearTuple(node->mj_MarkedTupleSlot);
1561 MJ1_printf("ExecEndMergeJoin: %s\n",
1562 "node processing ended");
1566 ExecReScanMergeJoin(MergeJoinState *node, ExprContext *exprCtxt)
1568 ExecClearTuple(node->mj_MarkedTupleSlot);
1570 node->mj_JoinState = EXEC_MJ_INITIALIZE;
1571 node->js.ps.ps_TupFromTlist = false;
1572 node->mj_MatchedOuter = false;
1573 node->mj_MatchedInner = false;
1574 node->mj_OuterTupleSlot = NULL;
1575 node->mj_InnerTupleSlot = NULL;
1578 * if chgParam of subnodes is not null then plans will be re-scanned
1579 * by first ExecProcNode.
1581 if (((PlanState *) node)->lefttree->chgParam == NULL)
1582 ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
1583 if (((PlanState *) node)->righttree->chgParam == NULL)
1584 ExecReScan(((PlanState *) node)->righttree, exprCtxt);