1 /*-------------------------------------------------------------------------
4 * routines supporting merge joins
6 * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.63 2003/11/29 19:51:48 pgsql 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,
113 * Make modifiable copies of the qualList.
115 ltexprs = (List *) copyObject((Node *) qualList);
116 gtexprs = (List *) copyObject((Node *) qualList);
119 * Scan both lists in parallel, so that we can update the operators
120 * with the minimum number of syscache searches.
123 foreach(gtcdr, gtexprs)
125 OpExpr *ltop = (OpExpr *) lfirst(ltcdr);
126 OpExpr *gtop = (OpExpr *) lfirst(gtcdr);
129 * The two ops should be identical, so use either one for lookup.
131 if (!IsA(ltop, OpExpr))
132 elog(ERROR, "mergejoin clause is not an OpExpr");
135 * Lookup the operators, and replace the data in the copied
138 op_mergejoin_crossops(ltop->opno,
144 ltcdr = lnext(ltcdr);
148 * Prepare both lists for execution.
150 *ltQuals = (List *) ExecInitExpr((Expr *) ltexprs, parent);
151 *gtQuals = (List *) ExecInitExpr((Expr *) gtexprs, parent);
154 /* ----------------------------------------------------------------
157 * Compare the keys according to 'compareQual' which is of the
158 * form: { (key1a > key2a) (key1b > key2b) ... }.
160 * (actually, it could also be of the form (key1a < key2a)...)
162 * This is different from calling ExecQual because ExecQual returns
163 * true only if ALL the comparison clauses are satisfied.
164 * However, there is an order of significance among the keys with
165 * the first keys being most significant. Therefore, the clauses
166 * are evaluated in order and the 'compareQual' is satisfied
167 * if (key1i > key2i) is true and (key1j = key2j) for 0 < j < i.
168 * We use the original mergeclause items to detect equality.
169 * ----------------------------------------------------------------
172 MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
175 MemoryContext oldContext;
180 * Do expression eval in short-lived context.
182 oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
185 * for each pair of clauses, test them until our compare conditions
186 * are satisfied. if we reach the end of the list, none of our key
187 * greater-than conditions were satisfied so we return false.
189 result = false; /* assume 'false' result */
192 foreach(clause, compareQual)
198 * first test if our compare clause is satisfied. if so then
201 * A NULL result is considered false.
203 const_value = ExecEvalExpr((ExprState *) lfirst(clause),
208 if (DatumGetBool(const_value) && !isNull)
215 * ok, the compare clause failed so we test if the keys are
216 * equal... if key1 != key2, we return false. otherwise
217 * key1 = key2 so we move on to the next pair of keys.
220 const_value = ExecEvalExpr((ExprState *) lfirst(eqclause),
225 if (!DatumGetBool(const_value) || isNull)
226 break; /* return false */
228 eqclause = lnext(eqclause);
231 MemoryContextSwitchTo(oldContext);
236 /* ----------------------------------------------------------------
239 * This function is called through the MJ_dump() macro
240 * when EXEC_MERGEJOINDEBUG is defined
241 * ----------------------------------------------------------------
243 #ifdef EXEC_MERGEJOINDEBUG
246 ExecMergeTupleDumpOuter(MergeJoinState *mergestate)
248 TupleTableSlot *outerSlot = mergestate->mj_OuterTupleSlot;
250 printf("==== outer tuple ====\n");
251 if (TupIsNull(outerSlot))
254 MJ_debugtup(outerSlot->val,
255 outerSlot->ttc_tupleDescriptor);
259 ExecMergeTupleDumpInner(MergeJoinState *mergestate)
261 TupleTableSlot *innerSlot = mergestate->mj_InnerTupleSlot;
263 printf("==== inner tuple ====\n");
264 if (TupIsNull(innerSlot))
267 MJ_debugtup(innerSlot->val,
268 innerSlot->ttc_tupleDescriptor);
272 ExecMergeTupleDumpMarked(MergeJoinState *mergestate)
274 TupleTableSlot *markedSlot = mergestate->mj_MarkedTupleSlot;
276 printf("==== marked tuple ====\n");
277 if (TupIsNull(markedSlot))
280 MJ_debugtup(markedSlot->val,
281 markedSlot->ttc_tupleDescriptor);
285 ExecMergeTupleDump(MergeJoinState *mergestate)
287 printf("******** ExecMergeTupleDump ********\n");
289 ExecMergeTupleDumpOuter(mergestate);
290 ExecMergeTupleDumpInner(mergestate);
291 ExecMergeTupleDumpMarked(mergestate);
293 printf("******** \n");
297 /* ----------------------------------------------------------------
301 * Details of the merge-join routines:
303 * (1) ">" and "<" operators
305 * Merge-join is done by joining the inner and outer tuples satisfying
306 * the join clauses of the form ((= outerKey innerKey) ...).
307 * The join clauses is provided by the query planner and may contain
308 * more than one (= outerKey innerKey) clauses (for composite key).
310 * However, the query executor needs to know whether an outer
311 * tuple is "greater/smaller" than an inner tuple so that it can
312 * "synchronize" the two relations. For e.g., consider the following
315 * outer: (0 ^1 1 2 5 5 5 6 6 7) current tuple: 1
316 * inner: (1 ^3 5 5 5 5 6) current tuple: 3
318 * To continue the merge-join, the executor needs to scan both inner
319 * and outer relations till the matching tuples 5. It needs to know
320 * that currently inner tuple 3 is "greater" than outer tuple 1 and
321 * therefore it should scan the outer relation first to find a
322 * matching tuple and so on.
324 * Therefore, when initializing the merge-join node, the executor
325 * creates the "greater/smaller" clause by substituting the "="
326 * operator in the join clauses with the corresponding ">" operator.
327 * The opposite "smaller/greater" clause is formed by substituting "<".
329 * Note: prior to v6.5, the relational clauses were formed using the
330 * sort op used to sort the inner relation, which of course would fail
331 * if the outer and inner keys were of different data types.
332 * In the current code, we instead assume that operators named "<" and ">"
333 * will do the right thing. This should be true since the mergejoin "="
334 * operator's pg_operator entry will have told the planner to sort by
335 * "<" for each of the left and right sides.
337 * (2) repositioning inner "cursor"
339 * Consider the above relations and suppose that the executor has
340 * just joined the first outer "5" with the last inner "5". The
341 * next step is of course to join the second outer "5" with all
342 * the inner "5's". This requires repositioning the inner "cursor"
343 * to point at the first inner "5". This is done by "marking" the
344 * first inner 5 and restore the "cursor" to it before joining
345 * with the second outer 5. The access method interface provides
346 * routines to mark and restore to a tuple.
347 * ----------------------------------------------------------------
350 ExecMergeJoin(MergeJoinState *node)
353 ScanDirection direction;
361 PlanState *innerPlan;
362 TupleTableSlot *innerTupleSlot;
363 PlanState *outerPlan;
364 TupleTableSlot *outerTupleSlot;
365 ExprContext *econtext;
370 * get information from node
372 estate = node->js.ps.state;
373 direction = estate->es_direction;
374 innerPlan = innerPlanState(node);
375 outerPlan = outerPlanState(node);
376 econtext = node->js.ps.ps_ExprContext;
377 mergeclauses = node->mergeclauses;
378 joinqual = node->js.joinqual;
379 otherqual = node->js.ps.qual;
381 switch (node->js.jointype)
401 elog(ERROR, "unrecognized join type: %d",
402 (int) node->js.jointype);
403 doFillOuter = false; /* keep compiler quiet */
408 if (ScanDirectionIsForward(direction))
410 outerSkipQual = node->mj_OuterSkipQual;
411 innerSkipQual = node->mj_InnerSkipQual;
415 outerSkipQual = node->mj_InnerSkipQual;
416 innerSkipQual = node->mj_OuterSkipQual;
420 * Check to see if we're still projecting out tuples from a previous
421 * join tuple (because there is a function-returning-set in the
422 * projection expressions). If so, try to project another one.
424 if (node->js.ps.ps_TupFromTlist)
426 TupleTableSlot *result;
429 result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
430 if (isDone == ExprMultipleResult)
432 /* Done with that source tuple... */
433 node->js.ps.ps_TupFromTlist = false;
437 * Reset per-tuple memory context to free any expression evaluation
438 * storage allocated in the previous tuple cycle. Note this can't
439 * happen until we're done projecting out tuples from a join tuple.
441 ResetExprContext(econtext);
444 * ok, everything is setup.. let's go to work
449 * get the current state of the join and do things accordingly.
450 * Note: The join states are highlighted with 32-* comments for
451 * improved readability.
455 switch (node->mj_JoinState)
458 * EXEC_MJ_INITIALIZE means that this is the first time
459 * ExecMergeJoin() has been called and so we have to fetch
460 * the first tuple for both outer and inner subplans. If
461 * we fail to get a tuple here, then that subplan is
462 * empty, and we either end the join or go to one of the
463 * fill-remaining-tuples states.
465 case EXEC_MJ_INITIALIZE:
466 MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE\n");
468 outerTupleSlot = ExecProcNode(outerPlan);
469 node->mj_OuterTupleSlot = outerTupleSlot;
470 if (TupIsNull(outerTupleSlot))
472 MJ_printf("ExecMergeJoin: outer subplan is empty\n");
476 * Need to emit right-join tuples for remaining
477 * inner tuples. We set MatchedInner = true to
478 * force the ENDOUTER state to advance inner.
480 node->mj_JoinState = EXEC_MJ_ENDOUTER;
481 node->mj_MatchedInner = true;
484 /* Otherwise we're done. */
488 innerTupleSlot = ExecProcNode(innerPlan);
489 node->mj_InnerTupleSlot = innerTupleSlot;
490 if (TupIsNull(innerTupleSlot))
492 MJ_printf("ExecMergeJoin: inner subplan is empty\n");
496 * Need to emit left-join tuples for all outer
497 * tuples, including the one we just fetched. We
498 * set MatchedOuter = false to force the ENDINNER
499 * state to emit this tuple before advancing
502 node->mj_JoinState = EXEC_MJ_ENDINNER;
503 node->mj_MatchedOuter = false;
506 /* Otherwise we're done. */
511 * OK, we have the initial tuples. Begin by skipping
512 * unmatched inner tuples.
514 node->mj_JoinState = EXEC_MJ_SKIPINNER_BEGIN;
518 * EXEC_MJ_JOINMARK means we have just found a new outer
519 * tuple and a possible matching inner tuple. This is the
520 * case after the INITIALIZE, SKIPOUTER or SKIPINNER
523 case EXEC_MJ_JOINMARK:
524 MJ_printf("ExecMergeJoin: EXEC_MJ_JOINMARK\n");
526 ExecMarkPos(innerPlan);
528 MarkInnerTuple(node->mj_InnerTupleSlot, node);
530 node->mj_JoinState = EXEC_MJ_JOINTEST;
534 * EXEC_MJ_JOINTEST means we have two tuples which might
535 * satisfy the merge clause, so we test them.
537 * If they do satisfy, then we join them and move on to the
538 * next inner tuple (EXEC_MJ_JOINTUPLES).
540 * If they do not satisfy then advance to next outer tuple.
542 case EXEC_MJ_JOINTEST:
543 MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n");
545 ResetExprContext(econtext);
547 outerTupleSlot = node->mj_OuterTupleSlot;
548 econtext->ecxt_outertuple = outerTupleSlot;
549 innerTupleSlot = node->mj_InnerTupleSlot;
550 econtext->ecxt_innertuple = innerTupleSlot;
552 qualResult = ExecQual(mergeclauses, econtext, false);
553 MJ_DEBUG_QUAL(mergeclauses, qualResult);
556 node->mj_JoinState = EXEC_MJ_JOINTUPLES;
558 node->mj_JoinState = EXEC_MJ_NEXTOUTER;
562 * EXEC_MJ_JOINTUPLES means we have two tuples which
563 * satisfied the merge clause so we join them and then
564 * proceed to get the next inner tuple (EXEC_NEXT_INNER).
566 case EXEC_MJ_JOINTUPLES:
567 MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");
569 node->mj_JoinState = EXEC_MJ_NEXTINNER;
572 * Check the extra qual conditions to see if we actually
573 * want to return this join tuple. If not, can proceed
574 * with merge. We must distinguish the additional
575 * joinquals (which must pass to consider the tuples
576 * "matched" for outer-join logic) from the otherquals
577 * (which must pass before we actually return the tuple).
579 * We don't bother with a ResetExprContext here, on the
580 * assumption that we just did one before checking the
581 * merge qual. One per tuple should be sufficient. Also,
582 * the econtext's tuple pointers were set up before
583 * checking the merge qual, so we needn't do it again.
585 if (node->js.jointype == JOIN_IN &&
586 node->mj_MatchedOuter)
590 qualResult = (joinqual == NIL ||
591 ExecQual(joinqual, econtext, false));
592 MJ_DEBUG_QUAL(joinqual, qualResult);
597 node->mj_MatchedOuter = true;
598 node->mj_MatchedInner = true;
600 qualResult = (otherqual == NIL ||
601 ExecQual(otherqual, econtext, false));
602 MJ_DEBUG_QUAL(otherqual, qualResult);
607 * qualification succeeded. now form the desired
608 * projection tuple and return the slot containing
611 TupleTableSlot *result;
614 MJ_printf("ExecMergeJoin: returning tuple\n");
616 result = ExecProject(node->js.ps.ps_ProjInfo,
619 if (isDone != ExprEndResult)
621 node->js.ps.ps_TupFromTlist =
622 (isDone == ExprMultipleResult);
630 * EXEC_MJ_NEXTINNER means advance the inner scan to the
631 * next tuple. If the tuple is not nil, we then proceed to
632 * test it against the join qualification.
634 * Before advancing, we check to see if we must emit an
635 * outer-join fill tuple for this inner tuple.
637 case EXEC_MJ_NEXTINNER:
638 MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\n");
640 if (doFillInner && !node->mj_MatchedInner)
643 * Generate a fake join tuple with nulls for the outer
644 * tuple, and return it if it passes the non-join
647 node->mj_MatchedInner = true; /* do it only once */
649 ResetExprContext(econtext);
651 outerTupleSlot = node->mj_NullOuterTupleSlot;
652 econtext->ecxt_outertuple = outerTupleSlot;
653 innerTupleSlot = node->mj_InnerTupleSlot;
654 econtext->ecxt_innertuple = innerTupleSlot;
656 if (ExecQual(otherqual, econtext, false))
659 * qualification succeeded. now form the desired
660 * projection tuple and return the slot containing
663 TupleTableSlot *result;
666 MJ_printf("ExecMergeJoin: returning fill tuple\n");
668 result = ExecProject(node->js.ps.ps_ProjInfo,
671 if (isDone != ExprEndResult)
673 node->js.ps.ps_TupFromTlist =
674 (isDone == ExprMultipleResult);
681 * now we get the next inner tuple, if any
683 innerTupleSlot = ExecProcNode(innerPlan);
684 node->mj_InnerTupleSlot = innerTupleSlot;
685 MJ_DEBUG_PROC_NODE(innerTupleSlot);
686 node->mj_MatchedInner = false;
688 if (TupIsNull(innerTupleSlot))
689 node->mj_JoinState = EXEC_MJ_NEXTOUTER;
691 node->mj_JoinState = EXEC_MJ_JOINTEST;
694 /*-------------------------------------------
695 * EXEC_MJ_NEXTOUTER means
698 * outer tuple - 5 5 - marked tuple
703 * we know we just bumped into the
704 * first inner tuple > current outer tuple
705 * so get a new outer tuple and then
706 * proceed to test it against the marked tuple
707 * (EXEC_MJ_TESTOUTER)
709 * Before advancing, we check to see if we must emit an
710 * outer-join fill tuple for this outer tuple.
711 *------------------------------------------------
713 case EXEC_MJ_NEXTOUTER:
714 MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n");
716 if (doFillOuter && !node->mj_MatchedOuter)
719 * Generate a fake join tuple with nulls for the inner
720 * tuple, and return it if it passes the non-join
723 node->mj_MatchedOuter = true; /* do it only once */
725 ResetExprContext(econtext);
727 outerTupleSlot = node->mj_OuterTupleSlot;
728 econtext->ecxt_outertuple = outerTupleSlot;
729 innerTupleSlot = node->mj_NullInnerTupleSlot;
730 econtext->ecxt_innertuple = innerTupleSlot;
732 if (ExecQual(otherqual, econtext, false))
735 * qualification succeeded. now form the desired
736 * projection tuple and return the slot containing
739 TupleTableSlot *result;
742 MJ_printf("ExecMergeJoin: returning fill tuple\n");
744 result = ExecProject(node->js.ps.ps_ProjInfo,
747 if (isDone != ExprEndResult)
749 node->js.ps.ps_TupFromTlist =
750 (isDone == ExprMultipleResult);
757 * now we get the next outer tuple, if any
759 outerTupleSlot = ExecProcNode(outerPlan);
760 node->mj_OuterTupleSlot = outerTupleSlot;
761 MJ_DEBUG_PROC_NODE(outerTupleSlot);
762 node->mj_MatchedOuter = false;
765 * if the outer tuple is null then we are done with the
766 * join, unless we have inner tuples we need to null-fill.
768 if (TupIsNull(outerTupleSlot))
770 MJ_printf("ExecMergeJoin: end of outer subplan\n");
771 innerTupleSlot = node->mj_InnerTupleSlot;
772 if (doFillInner && !TupIsNull(innerTupleSlot))
775 * Need to emit right-join tuples for remaining
778 node->mj_JoinState = EXEC_MJ_ENDOUTER;
781 /* Otherwise we're done. */
785 node->mj_JoinState = EXEC_MJ_TESTOUTER;
788 /*--------------------------------------------------------
789 * EXEC_MJ_TESTOUTER If the new outer tuple and the marked
790 * tuple satisfy the merge clause then we know we have
791 * duplicates in the outer scan so we have to restore the
792 * inner scan to the marked tuple and proceed to join the
793 * new outer tuples with the inner tuples (EXEC_MJ_JOINTEST)
795 * This is the case when
799 * new outer tuple - 5 5
803 * new outer tuple = marked tuple
805 * If the outer tuple fails the test, then we know we have
806 * to proceed to skip outer tuples until outer >= inner
807 * (EXEC_MJ_SKIPOUTER).
809 * This is the case when
814 * new outer tuple - 6 8 - inner tuple
818 * new outer tuple > marked tuple
820 *---------------------------------------------------------
822 case EXEC_MJ_TESTOUTER:
823 MJ_printf("ExecMergeJoin: EXEC_MJ_TESTOUTER\n");
826 * here we compare the outer tuple with the marked inner
829 ResetExprContext(econtext);
831 outerTupleSlot = node->mj_OuterTupleSlot;
832 econtext->ecxt_outertuple = outerTupleSlot;
833 innerTupleSlot = node->mj_MarkedTupleSlot;
834 econtext->ecxt_innertuple = innerTupleSlot;
836 qualResult = ExecQual(mergeclauses, econtext, false);
837 MJ_DEBUG_QUAL(mergeclauses, qualResult);
842 * the merge clause matched so now we restore the
843 * inner scan position to the first mark, and loop
844 * back to JOINTEST. Actually, since we know the
845 * mergeclause matches, we can skip JOINTEST and go
846 * straight to JOINTUPLES.
848 * NOTE: we do not need to worry about the MatchedInner
849 * state for the rescanned inner tuples. We know all
850 * of them will match this new outer tuple and
851 * therefore won't be emitted as fill tuples. This
852 * works *only* because we require the extra joinquals
853 * to be nil when doing a right or full join ---
854 * otherwise some of the rescanned tuples might fail
855 * the extra joinquals.
857 ExecRestrPos(innerPlan);
858 node->mj_JoinState = EXEC_MJ_JOINTUPLES;
863 * if the inner tuple was nil and the new outer
864 * tuple didn't match the marked outer tuple then
870 * 6 nil - inner tuple
873 * which means that all subsequent outer tuples will be
874 * larger than our marked inner tuples. So we're done.
877 innerTupleSlot = node->mj_InnerTupleSlot;
878 if (TupIsNull(innerTupleSlot))
883 * Need to emit left-join tuples for remaining
886 node->mj_JoinState = EXEC_MJ_ENDINNER;
889 /* Otherwise we're done. */
893 /* continue on to skip outer tuples */
894 node->mj_JoinState = EXEC_MJ_SKIPOUTER_BEGIN;
898 /*----------------------------------------------------------
899 * EXEC_MJ_SKIPOUTER means skip over tuples in the outer plan
900 * until we find an outer tuple >= current inner tuple.
907 * outer tuple - 6 8 - inner tuple
911 * we have to advance the outer scan
912 * until we find the outer 8.
914 * To avoid redundant tests, we divide this into three
915 * sub-states: BEGIN, TEST, ADVANCE.
916 *----------------------------------------------------------
918 case EXEC_MJ_SKIPOUTER_BEGIN:
919 MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_BEGIN\n");
922 * before we advance, make sure the current tuples do not
923 * satisfy the mergeclauses. If they do, then we update
924 * the marked tuple and go join them.
926 ResetExprContext(econtext);
928 outerTupleSlot = node->mj_OuterTupleSlot;
929 econtext->ecxt_outertuple = outerTupleSlot;
930 innerTupleSlot = node->mj_InnerTupleSlot;
931 econtext->ecxt_innertuple = innerTupleSlot;
933 qualResult = ExecQual(mergeclauses, econtext, false);
934 MJ_DEBUG_QUAL(mergeclauses, qualResult);
938 ExecMarkPos(innerPlan);
940 MarkInnerTuple(innerTupleSlot, node);
942 node->mj_JoinState = EXEC_MJ_JOINTUPLES;
946 node->mj_JoinState = EXEC_MJ_SKIPOUTER_TEST;
949 case EXEC_MJ_SKIPOUTER_TEST:
950 MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_TEST\n");
953 * ok, now test the skip qualification
955 outerTupleSlot = node->mj_OuterTupleSlot;
956 econtext->ecxt_outertuple = outerTupleSlot;
957 innerTupleSlot = node->mj_InnerTupleSlot;
958 econtext->ecxt_innertuple = innerTupleSlot;
960 compareResult = MergeCompare(mergeclauses,
964 MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult);
967 * compareResult is true as long as we should continue
968 * skipping outer tuples.
972 node->mj_JoinState = EXEC_MJ_SKIPOUTER_ADVANCE;
977 * now check the inner skip qual to see if we should now
978 * skip inner tuples... if we fail the inner skip qual,
979 * then we know we have a new pair of matching tuples.
981 compareResult = MergeCompare(mergeclauses,
985 MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult);
988 node->mj_JoinState = EXEC_MJ_SKIPINNER_BEGIN;
990 node->mj_JoinState = EXEC_MJ_JOINMARK;
994 * Before advancing, we check to see if we must emit an
995 * outer-join fill tuple for this outer tuple.
997 case EXEC_MJ_SKIPOUTER_ADVANCE:
998 MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_ADVANCE\n");
1000 if (doFillOuter && !node->mj_MatchedOuter)
1003 * Generate a fake join tuple with nulls for the inner
1004 * tuple, and return it if it passes the non-join
1007 node->mj_MatchedOuter = true; /* do it only once */
1009 ResetExprContext(econtext);
1011 outerTupleSlot = node->mj_OuterTupleSlot;
1012 econtext->ecxt_outertuple = outerTupleSlot;
1013 innerTupleSlot = node->mj_NullInnerTupleSlot;
1014 econtext->ecxt_innertuple = innerTupleSlot;
1016 if (ExecQual(otherqual, econtext, false))
1019 * qualification succeeded. now form the desired
1020 * projection tuple and return the slot containing
1023 TupleTableSlot *result;
1024 ExprDoneCond isDone;
1026 MJ_printf("ExecMergeJoin: returning fill tuple\n");
1028 result = ExecProject(node->js.ps.ps_ProjInfo,
1031 if (isDone != ExprEndResult)
1033 node->js.ps.ps_TupFromTlist =
1034 (isDone == ExprMultipleResult);
1041 * now we get the next outer tuple, if any
1043 outerTupleSlot = ExecProcNode(outerPlan);
1044 node->mj_OuterTupleSlot = outerTupleSlot;
1045 MJ_DEBUG_PROC_NODE(outerTupleSlot);
1046 node->mj_MatchedOuter = false;
1049 * if the outer tuple is null then we are done with the
1050 * join, unless we have inner tuples we need to null-fill.
1052 if (TupIsNull(outerTupleSlot))
1054 MJ_printf("ExecMergeJoin: end of outer subplan\n");
1055 innerTupleSlot = node->mj_InnerTupleSlot;
1056 if (doFillInner && !TupIsNull(innerTupleSlot))
1059 * Need to emit right-join tuples for remaining
1062 node->mj_JoinState = EXEC_MJ_ENDOUTER;
1065 /* Otherwise we're done. */
1070 * otherwise test the new tuple against the skip qual.
1072 node->mj_JoinState = EXEC_MJ_SKIPOUTER_TEST;
1075 /*-----------------------------------------------------------
1076 * EXEC_MJ_SKIPINNER means skip over tuples in the inner plan
1077 * until we find an inner tuple >= current outer tuple.
1084 * outer tuple - 12 8 - inner tuple
1088 * we have to advance the inner scan
1089 * until we find the inner 12.
1091 * To avoid redundant tests, we divide this into three
1092 * sub-states: BEGIN, TEST, ADVANCE.
1093 *-------------------------------------------------------
1095 case EXEC_MJ_SKIPINNER_BEGIN:
1096 MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_BEGIN\n");
1099 * before we advance, make sure the current tuples do not
1100 * satisfy the mergeclauses. If they do, then we update
1101 * the marked tuple and go join them.
1103 ResetExprContext(econtext);
1105 outerTupleSlot = node->mj_OuterTupleSlot;
1106 econtext->ecxt_outertuple = outerTupleSlot;
1107 innerTupleSlot = node->mj_InnerTupleSlot;
1108 econtext->ecxt_innertuple = innerTupleSlot;
1110 qualResult = ExecQual(mergeclauses, econtext, false);
1111 MJ_DEBUG_QUAL(mergeclauses, qualResult);
1115 ExecMarkPos(innerPlan);
1117 MarkInnerTuple(innerTupleSlot, node);
1119 node->mj_JoinState = EXEC_MJ_JOINTUPLES;
1123 node->mj_JoinState = EXEC_MJ_SKIPINNER_TEST;
1126 case EXEC_MJ_SKIPINNER_TEST:
1127 MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_TEST\n");
1130 * ok, now test the skip qualification
1132 outerTupleSlot = node->mj_OuterTupleSlot;
1133 econtext->ecxt_outertuple = outerTupleSlot;
1134 innerTupleSlot = node->mj_InnerTupleSlot;
1135 econtext->ecxt_innertuple = innerTupleSlot;
1137 compareResult = MergeCompare(mergeclauses,
1141 MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult);
1144 * compareResult is true as long as we should continue
1145 * skipping inner tuples.
1149 node->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE;
1154 * now check the outer skip qual to see if we should now
1155 * skip outer tuples... if we fail the outer skip qual,
1156 * then we know we have a new pair of matching tuples.
1158 compareResult = MergeCompare(mergeclauses,
1162 MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult);
1165 node->mj_JoinState = EXEC_MJ_SKIPOUTER_BEGIN;
1167 node->mj_JoinState = EXEC_MJ_JOINMARK;
1171 * Before advancing, we check to see if we must emit an
1172 * outer-join fill tuple for this inner tuple.
1174 case EXEC_MJ_SKIPINNER_ADVANCE:
1175 MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_ADVANCE\n");
1177 if (doFillInner && !node->mj_MatchedInner)
1180 * Generate a fake join tuple with nulls for the outer
1181 * tuple, and return it if it passes the non-join
1184 node->mj_MatchedInner = true; /* do it only once */
1186 ResetExprContext(econtext);
1188 outerTupleSlot = node->mj_NullOuterTupleSlot;
1189 econtext->ecxt_outertuple = outerTupleSlot;
1190 innerTupleSlot = node->mj_InnerTupleSlot;
1191 econtext->ecxt_innertuple = innerTupleSlot;
1193 if (ExecQual(otherqual, econtext, false))
1196 * qualification succeeded. now form the desired
1197 * projection tuple and return the slot containing
1200 TupleTableSlot *result;
1201 ExprDoneCond isDone;
1203 MJ_printf("ExecMergeJoin: returning fill tuple\n");
1205 result = ExecProject(node->js.ps.ps_ProjInfo,
1208 if (isDone != ExprEndResult)
1210 node->js.ps.ps_TupFromTlist =
1211 (isDone == ExprMultipleResult);
1218 * now we get the next inner tuple, if any
1220 innerTupleSlot = ExecProcNode(innerPlan);
1221 node->mj_InnerTupleSlot = innerTupleSlot;
1222 MJ_DEBUG_PROC_NODE(innerTupleSlot);
1223 node->mj_MatchedInner = false;
1226 * if the inner tuple is null then we are done with the
1227 * join, unless we have outer tuples we need to null-fill.
1229 if (TupIsNull(innerTupleSlot))
1231 MJ_printf("ExecMergeJoin: end of inner subplan\n");
1232 outerTupleSlot = node->mj_OuterTupleSlot;
1233 if (doFillOuter && !TupIsNull(outerTupleSlot))
1236 * Need to emit left-join tuples for remaining
1239 node->mj_JoinState = EXEC_MJ_ENDINNER;
1242 /* Otherwise we're done. */
1247 * otherwise test the new tuple against the skip qual.
1249 node->mj_JoinState = EXEC_MJ_SKIPINNER_TEST;
1253 * EXEC_MJ_ENDOUTER means we have run out of outer tuples,
1254 * but are doing a right/full join and therefore must
1255 * null- fill any remaing unmatched inner tuples.
1257 case EXEC_MJ_ENDOUTER:
1258 MJ_printf("ExecMergeJoin: EXEC_MJ_ENDOUTER\n");
1260 Assert(doFillInner);
1262 if (!node->mj_MatchedInner)
1265 * Generate a fake join tuple with nulls for the outer
1266 * tuple, and return it if it passes the non-join
1269 node->mj_MatchedInner = true; /* do it only once */
1271 ResetExprContext(econtext);
1273 outerTupleSlot = node->mj_NullOuterTupleSlot;
1274 econtext->ecxt_outertuple = outerTupleSlot;
1275 innerTupleSlot = node->mj_InnerTupleSlot;
1276 econtext->ecxt_innertuple = innerTupleSlot;
1278 if (ExecQual(otherqual, econtext, false))
1281 * qualification succeeded. now form the desired
1282 * projection tuple and return the slot containing
1285 TupleTableSlot *result;
1286 ExprDoneCond isDone;
1288 MJ_printf("ExecMergeJoin: returning fill tuple\n");
1290 result = ExecProject(node->js.ps.ps_ProjInfo,
1293 if (isDone != ExprEndResult)
1295 node->js.ps.ps_TupFromTlist =
1296 (isDone == ExprMultipleResult);
1303 * now we get the next inner tuple, if any
1305 innerTupleSlot = ExecProcNode(innerPlan);
1306 node->mj_InnerTupleSlot = innerTupleSlot;
1307 MJ_DEBUG_PROC_NODE(innerTupleSlot);
1308 node->mj_MatchedInner = false;
1310 if (TupIsNull(innerTupleSlot))
1312 MJ_printf("ExecMergeJoin: end of inner subplan\n");
1316 /* Else remain in ENDOUTER state and process next tuple. */
1320 * EXEC_MJ_ENDINNER means we have run out of inner tuples,
1321 * but are doing a left/full join and therefore must null-
1322 * fill any remaing unmatched outer tuples.
1324 case EXEC_MJ_ENDINNER:
1325 MJ_printf("ExecMergeJoin: EXEC_MJ_ENDINNER\n");
1327 Assert(doFillOuter);
1329 if (!node->mj_MatchedOuter)
1332 * Generate a fake join tuple with nulls for the inner
1333 * tuple, and return it if it passes the non-join
1336 node->mj_MatchedOuter = true; /* do it only once */
1338 ResetExprContext(econtext);
1340 outerTupleSlot = node->mj_OuterTupleSlot;
1341 econtext->ecxt_outertuple = outerTupleSlot;
1342 innerTupleSlot = node->mj_NullInnerTupleSlot;
1343 econtext->ecxt_innertuple = innerTupleSlot;
1345 if (ExecQual(otherqual, econtext, false))
1348 * qualification succeeded. now form the desired
1349 * projection tuple and return the slot containing
1352 TupleTableSlot *result;
1353 ExprDoneCond isDone;
1355 MJ_printf("ExecMergeJoin: returning fill tuple\n");
1357 result = ExecProject(node->js.ps.ps_ProjInfo,
1360 if (isDone != ExprEndResult)
1362 node->js.ps.ps_TupFromTlist =
1363 (isDone == ExprMultipleResult);
1370 * now we get the next outer tuple, if any
1372 outerTupleSlot = ExecProcNode(outerPlan);
1373 node->mj_OuterTupleSlot = outerTupleSlot;
1374 MJ_DEBUG_PROC_NODE(outerTupleSlot);
1375 node->mj_MatchedOuter = false;
1377 if (TupIsNull(outerTupleSlot))
1379 MJ_printf("ExecMergeJoin: end of outer subplan\n");
1383 /* Else remain in ENDINNER state and process next tuple. */
1387 * broken state value?
1390 elog(ERROR, "unrecognized mergejoin state: %d",
1391 (int) node->mj_JoinState);
1396 /* ----------------------------------------------------------------
1398 * ----------------------------------------------------------------
1401 ExecInitMergeJoin(MergeJoin *node, EState *estate)
1403 MergeJoinState *mergestate;
1405 MJ1_printf("ExecInitMergeJoin: %s\n",
1406 "initializing node");
1409 * create state structure
1411 mergestate = makeNode(MergeJoinState);
1412 mergestate->js.ps.plan = (Plan *) node;
1413 mergestate->js.ps.state = estate;
1416 * Miscellaneous initialization
1418 * create expression context for node
1420 ExecAssignExprContext(estate, &mergestate->js.ps);
1423 * initialize child expressions
1425 mergestate->js.ps.targetlist = (List *)
1426 ExecInitExpr((Expr *) node->join.plan.targetlist,
1427 (PlanState *) mergestate);
1428 mergestate->js.ps.qual = (List *)
1429 ExecInitExpr((Expr *) node->join.plan.qual,
1430 (PlanState *) mergestate);
1431 mergestate->js.jointype = node->join.jointype;
1432 mergestate->js.joinqual = (List *)
1433 ExecInitExpr((Expr *) node->join.joinqual,
1434 (PlanState *) mergestate);
1435 mergestate->mergeclauses = (List *)
1436 ExecInitExpr((Expr *) node->mergeclauses,
1437 (PlanState *) mergestate);
1440 * initialize child nodes
1442 outerPlanState(mergestate) = ExecInitNode(outerPlan(node), estate);
1443 innerPlanState(mergestate) = ExecInitNode(innerPlan(node), estate);
1445 #define MERGEJOIN_NSLOTS 4
1448 * tuple table initialization
1450 ExecInitResultTupleSlot(estate, &mergestate->js.ps);
1452 mergestate->mj_MarkedTupleSlot = ExecInitExtraTupleSlot(estate);
1453 ExecSetSlotDescriptor(mergestate->mj_MarkedTupleSlot,
1454 ExecGetResultType(innerPlanState(mergestate)),
1457 switch (node->join.jointype)
1463 mergestate->mj_NullInnerTupleSlot =
1464 ExecInitNullTupleSlot(estate,
1465 ExecGetResultType(innerPlanState(mergestate)));
1468 mergestate->mj_NullOuterTupleSlot =
1469 ExecInitNullTupleSlot(estate,
1470 ExecGetResultType(outerPlanState(mergestate)));
1473 * Can't handle right or full join with non-nil extra
1474 * joinclauses. This should have been caught by planner.
1476 if (node->join.joinqual != NIL)
1478 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1479 errmsg("RIGHT JOIN is only supported with merge-joinable join conditions")));
1482 mergestate->mj_NullOuterTupleSlot =
1483 ExecInitNullTupleSlot(estate,
1484 ExecGetResultType(outerPlanState(mergestate)));
1485 mergestate->mj_NullInnerTupleSlot =
1486 ExecInitNullTupleSlot(estate,
1487 ExecGetResultType(innerPlanState(mergestate)));
1490 * Can't handle right or full join with non-nil extra
1493 if (node->join.joinqual != NIL)
1495 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1496 errmsg("FULL JOIN is only supported with merge-joinable join conditions")));
1499 elog(ERROR, "unrecognized join type: %d",
1500 (int) node->join.jointype);
1504 * initialize tuple type and projection info
1506 ExecAssignResultTypeFromTL(&mergestate->js.ps);
1507 ExecAssignProjectionInfo(&mergestate->js.ps);
1510 * form merge skip qualifications
1512 MJFormSkipQuals(node->mergeclauses,
1513 &mergestate->mj_OuterSkipQual,
1514 &mergestate->mj_InnerSkipQual,
1515 (PlanState *) mergestate);
1517 MJ_printf("\nExecInitMergeJoin: OuterSkipQual is ");
1518 MJ_nodeDisplay(mergestate->mj_OuterSkipQual);
1519 MJ_printf("\nExecInitMergeJoin: InnerSkipQual is ");
1520 MJ_nodeDisplay(mergestate->mj_InnerSkipQual);
1524 * initialize join state
1526 mergestate->mj_JoinState = EXEC_MJ_INITIALIZE;
1527 mergestate->js.ps.ps_TupFromTlist = false;
1528 mergestate->mj_MatchedOuter = false;
1529 mergestate->mj_MatchedInner = false;
1530 mergestate->mj_OuterTupleSlot = NULL;
1531 mergestate->mj_InnerTupleSlot = NULL;
1534 * initialization successful
1536 MJ1_printf("ExecInitMergeJoin: %s\n",
1537 "node initialized");
1543 ExecCountSlotsMergeJoin(MergeJoin *node)
1545 return ExecCountSlotsNode(outerPlan((Plan *) node)) +
1546 ExecCountSlotsNode(innerPlan((Plan *) node)) +
1550 /* ----------------------------------------------------------------
1554 * frees storage allocated through C routines.
1555 * ----------------------------------------------------------------
1558 ExecEndMergeJoin(MergeJoinState *node)
1560 MJ1_printf("ExecEndMergeJoin: %s\n",
1561 "ending node processing");
1564 * Free the exprcontext
1566 ExecFreeExprContext(&node->js.ps);
1569 * clean out the tuple table
1571 ExecClearTuple(node->js.ps.ps_ResultTupleSlot);
1572 ExecClearTuple(node->mj_MarkedTupleSlot);
1575 * shut down the subplans
1577 ExecEndNode(innerPlanState(node));
1578 ExecEndNode(outerPlanState(node));
1580 MJ1_printf("ExecEndMergeJoin: %s\n",
1581 "node processing ended");
1585 ExecReScanMergeJoin(MergeJoinState *node, ExprContext *exprCtxt)
1587 ExecClearTuple(node->mj_MarkedTupleSlot);
1589 node->mj_JoinState = EXEC_MJ_INITIALIZE;
1590 node->js.ps.ps_TupFromTlist = false;
1591 node->mj_MatchedOuter = false;
1592 node->mj_MatchedInner = false;
1593 node->mj_OuterTupleSlot = NULL;
1594 node->mj_InnerTupleSlot = NULL;
1597 * if chgParam of subnodes is not null then plans will be re-scanned
1598 * by first ExecProcNode.
1600 if (((PlanState *) node)->lefttree->chgParam == NULL)
1601 ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
1602 if (((PlanState *) node)->righttree->chgParam == NULL)
1603 ExecReScan(((PlanState *) node)->righttree, exprCtxt);