1 /*-------------------------------------------------------------------------
4 * routines supporting merge joins
6 * Portions Copyright (c) 1996-2001, 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.46 2001/10/25 05:49:29 momjian 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 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 .. ..) ...)
120 * first we make a copy of it. copyObject() makes a deep copy so let's
121 * 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
130 qual = lfirst(qualcdr);
135 op = (Oper *) qual->oper;
137 elog(ERROR, "MJFormSkipQual: op not an Oper!");
140 * Get the declared left and right operand types of the operator.
141 * Note we do *not* use the actual operand types, since those
142 * might be different in scenarios with binary-compatible data
143 * types. There should be "<" and ">" operators matching a
144 * mergejoinable "=" operator's declared operand types, but we
145 * might not find them if we search with the actual operand types.
147 optup = SearchSysCache(OPEROID,
148 ObjectIdGetDatum(op->opno),
150 if (!HeapTupleIsValid(optup)) /* shouldn't happen */
151 elog(ERROR, "MJFormSkipQual: operator %u not found", op->opno);
152 opform = (Form_pg_operator) GETSTRUCT(optup);
153 oprleft = opform->oprleft;
154 oprright = opform->oprright;
155 ReleaseSysCache(optup);
158 * Now look up the matching "<" or ">" operator. If there isn't
159 * one, whoever marked the "=" operator mergejoinable was a loser.
161 optup = SearchSysCache(OPERNAME,
162 PointerGetDatum(replaceopname),
163 ObjectIdGetDatum(oprleft),
164 ObjectIdGetDatum(oprright),
166 if (!HeapTupleIsValid(optup))
168 "MJFormSkipQual: mergejoin operator %u has no matching %s op",
169 op->opno, replaceopname);
170 opform = (Form_pg_operator) GETSTRUCT(optup);
173 * And replace the data in the copied operator node.
175 op->opno = optup->t_data->t_oid;
176 op->opid = opform->oprcode;
177 op->op_fcache = NULL;
178 ReleaseSysCache(optup);
184 /* ----------------------------------------------------------------
187 * Compare the keys according to 'compareQual' which is of the
188 * form: { (key1a > key2a) (key1b > key2b) ... }.
190 * (actually, it could also be of the form (key1a < key2a)...)
192 * This is different from calling ExecQual because ExecQual returns
193 * true only if ALL the comparison clauses are satisfied.
194 * However, there is an order of significance among the keys with
195 * the first keys being most significant. Therefore, the clauses
196 * are evaluated in order and the 'compareQual' is satisfied
197 * if (key1i > key2i) is true and (key1j = key2j) for 0 < j < i.
198 * We use the original mergeclause items to detect equality.
199 * ----------------------------------------------------------------
202 MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
205 MemoryContext oldContext;
210 * Do expression eval in short-lived context.
212 oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
215 * for each pair of clauses, test them until our compare conditions
216 * are satisfied. if we reach the end of the list, none of our key
217 * greater-than conditions were satisfied so we return false.
219 result = false; /* assume 'false' result */
222 foreach(clause, compareQual)
228 * first test if our compare clause is satisfied. if so then
231 * A NULL result is considered false.
233 const_value = ExecEvalExpr((Node *) lfirst(clause), econtext,
236 if (DatumGetBool(const_value) && !isNull)
243 * ok, the compare clause failed so we test if the keys are
244 * equal... if key1 != key2, we return false. otherwise
245 * key1 = key2 so we move on to the next pair of keys.
248 const_value = ExecEvalExpr((Node *) lfirst(eqclause),
253 if (!DatumGetBool(const_value) || isNull)
254 break; /* return false */
256 eqclause = lnext(eqclause);
259 MemoryContextSwitchTo(oldContext);
264 /* ----------------------------------------------------------------
267 * This function is called through the MJ_dump() macro
268 * when EXEC_MERGEJOINDEBUG is defined
269 * ----------------------------------------------------------------
271 #ifdef EXEC_MERGEJOINDEBUG
274 ExecMergeTupleDumpOuter(MergeJoinState *mergestate)
276 TupleTableSlot *outerSlot = mergestate->mj_OuterTupleSlot;
278 printf("==== outer tuple ====\n");
279 if (TupIsNull(outerSlot))
282 MJ_debugtup(outerSlot->val,
283 outerSlot->ttc_tupleDescriptor);
287 ExecMergeTupleDumpInner(MergeJoinState *mergestate)
289 TupleTableSlot *innerSlot = mergestate->mj_InnerTupleSlot;
291 printf("==== inner tuple ====\n");
292 if (TupIsNull(innerSlot))
295 MJ_debugtup(innerSlot->val,
296 innerSlot->ttc_tupleDescriptor);
300 ExecMergeTupleDumpMarked(MergeJoinState *mergestate)
302 TupleTableSlot *markedSlot = mergestate->mj_MarkedTupleSlot;
304 printf("==== marked tuple ====\n");
305 if (TupIsNull(markedSlot))
308 MJ_debugtup(markedSlot->val,
309 markedSlot->ttc_tupleDescriptor);
313 ExecMergeTupleDump(MergeJoinState *mergestate)
315 printf("******** ExecMergeTupleDump ********\n");
317 ExecMergeTupleDumpOuter(mergestate);
318 ExecMergeTupleDumpInner(mergestate);
319 ExecMergeTupleDumpMarked(mergestate);
321 printf("******** \n");
325 /* ----------------------------------------------------------------
329 * Details of the merge-join routines:
331 * (1) ">" and "<" operators
333 * Merge-join is done by joining the inner and outer tuples satisfying
334 * the join clauses of the form ((= outerKey innerKey) ...).
335 * The join clauses is provided by the query planner and may contain
336 * more than one (= outerKey innerKey) clauses (for composite key).
338 * However, the query executor needs to know whether an outer
339 * tuple is "greater/smaller" than an inner tuple so that it can
340 * "synchronize" the two relations. For e.g., consider the following
343 * outer: (0 ^1 1 2 5 5 5 6 6 7) current tuple: 1
344 * inner: (1 ^3 5 5 5 5 6) current tuple: 3
346 * To continue the merge-join, the executor needs to scan both inner
347 * and outer relations till the matching tuples 5. It needs to know
348 * that currently inner tuple 3 is "greater" than outer tuple 1 and
349 * therefore it should scan the outer relation first to find a
350 * matching tuple and so on.
352 * Therefore, when initializing the merge-join node, the executor
353 * creates the "greater/smaller" clause by substituting the "="
354 * operator in the join clauses with the corresponding ">" operator.
355 * The opposite "smaller/greater" clause is formed by substituting "<".
357 * Note: prior to v6.5, the relational clauses were formed using the
358 * sort op used to sort the inner relation, which of course would fail
359 * if the outer and inner keys were of different data types.
360 * In the current code, we instead assume that operators named "<" and ">"
361 * will do the right thing. This should be true since the mergejoin "="
362 * operator's pg_operator entry will have told the planner to sort by
363 * "<" for each of the left and right sides.
365 * (2) repositioning inner "cursor"
367 * Consider the above relations and suppose that the executor has
368 * just joined the first outer "5" with the last inner "5". The
369 * next step is of course to join the second outer "5" with all
370 * the inner "5's". This requires repositioning the inner "cursor"
371 * to point at the first inner "5". This is done by "marking" the
372 * first inner 5 and restore the "cursor" to it before joining
373 * with the second outer 5. The access method interface provides
374 * routines to mark and restore to a tuple.
375 * ----------------------------------------------------------------
378 ExecMergeJoin(MergeJoin *node)
381 MergeJoinState *mergestate;
382 ScanDirection direction;
391 TupleTableSlot *innerTupleSlot;
393 TupleTableSlot *outerTupleSlot;
394 ExprContext *econtext;
399 * get information from node
401 mergestate = node->mergestate;
402 estate = node->join.plan.state;
403 direction = estate->es_direction;
404 innerPlan = innerPlan((Plan *) node);
405 outerPlan = outerPlan((Plan *) node);
406 econtext = mergestate->jstate.cs_ExprContext;
407 mergeclauses = node->mergeclauses;
408 joinqual = node->join.joinqual;
409 otherqual = node->join.plan.qual;
411 switch (node->join.jointype)
430 elog(ERROR, "ExecMergeJoin: unsupported join type %d",
431 (int) node->join.jointype);
432 doFillOuter = false; /* keep compiler quiet */
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.
453 if (mergestate->jstate.cs_TupFromTlist)
455 TupleTableSlot *result;
458 result = ExecProject(mergestate->jstate.cs_ProjInfo, &isDone);
459 if (isDone == ExprMultipleResult)
461 /* Done with that source tuple... */
462 mergestate->jstate.cs_TupFromTlist = false;
466 * Reset per-tuple memory context to free any expression evaluation
467 * storage allocated in the previous tuple cycle. Note this can't
468 * happen until we're done projecting out tuples from a join tuple.
470 ResetExprContext(econtext);
473 * ok, everything is setup.. let's go to work
478 * get the current state of the join and do things accordingly.
479 * Note: The join states are highlighted with 32-* comments for
480 * improved readability.
484 switch (mergestate->mj_JoinState)
487 * EXEC_MJ_INITIALIZE means that this is the first time
488 * ExecMergeJoin() has been called and so we have to fetch
489 * the first tuple for both outer and inner subplans. If
490 * we fail to get a tuple here, then that subplan is
491 * empty, and we either end the join or go to one of the
492 * fill-remaining-tuples states.
494 case EXEC_MJ_INITIALIZE:
495 MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE\n");
497 outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
498 mergestate->mj_OuterTupleSlot = outerTupleSlot;
499 if (TupIsNull(outerTupleSlot))
501 MJ_printf("ExecMergeJoin: outer subplan is empty\n");
505 * Need to emit right-join tuples for remaining
506 * inner tuples. We set MatchedInner = true to
507 * force the ENDOUTER state to advance inner.
509 mergestate->mj_JoinState = EXEC_MJ_ENDOUTER;
510 mergestate->mj_MatchedInner = true;
513 /* Otherwise we're done. */
517 innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
518 mergestate->mj_InnerTupleSlot = innerTupleSlot;
519 if (TupIsNull(innerTupleSlot))
521 MJ_printf("ExecMergeJoin: inner subplan is empty\n");
525 * Need to emit left-join tuples for all outer
526 * tuples, including the one we just fetched. We
527 * set MatchedOuter = false to force the ENDINNER
528 * state to emit this tuple before advancing
531 mergestate->mj_JoinState = EXEC_MJ_ENDINNER;
532 mergestate->mj_MatchedOuter = false;
535 /* Otherwise we're done. */
540 * OK, we have the initial tuples. Begin by skipping
541 * unmatched inner tuples.
543 mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_BEGIN;
547 * EXEC_MJ_JOINMARK means we have just found a new outer
548 * tuple and a possible matching inner tuple. This is the
549 * case after the INITIALIZE, SKIPOUTER or SKIPINNER
552 case EXEC_MJ_JOINMARK:
553 MJ_printf("ExecMergeJoin: EXEC_MJ_JOINMARK\n");
555 ExecMarkPos(innerPlan);
557 MarkInnerTuple(mergestate->mj_InnerTupleSlot, mergestate);
559 mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
563 * EXEC_MJ_JOINTEST means we have two tuples which might
564 * satisfy the merge clause, so we test them.
566 * If they do satisfy, then we join them and move on to the
567 * next inner tuple (EXEC_MJ_JOINTUPLES).
569 * If they do not satisfy then advance to next outer tuple.
571 case EXEC_MJ_JOINTEST:
572 MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n");
574 ResetExprContext(econtext);
576 outerTupleSlot = mergestate->mj_OuterTupleSlot;
577 econtext->ecxt_outertuple = outerTupleSlot;
578 innerTupleSlot = mergestate->mj_InnerTupleSlot;
579 econtext->ecxt_innertuple = innerTupleSlot;
581 qualResult = ExecQual(mergeclauses, econtext, false);
582 MJ_DEBUG_QUAL(mergeclauses, qualResult);
585 mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
587 mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER;
591 * EXEC_MJ_JOINTUPLES means we have two tuples which
592 * satisfied the merge clause so we join them and then
593 * proceed to get the next inner tuple (EXEC_NEXT_INNER).
595 case EXEC_MJ_JOINTUPLES:
596 MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");
598 mergestate->mj_JoinState = EXEC_MJ_NEXTINNER;
601 * Check the extra qual conditions to see if we actually
602 * want to return this join tuple. If not, can proceed
603 * with merge. We must distinguish the additional
604 * joinquals (which must pass to consider the tuples
605 * "matched" for outer-join logic) from the otherquals
606 * (which must pass before we actually return the tuple).
608 * We don't bother with a ResetExprContext here, on the
609 * assumption that we just did one before checking the
610 * merge qual. One per tuple should be sufficient. Also,
611 * the econtext's tuple pointers were set up before
612 * checking the merge qual, so we needn't do it again.
614 qualResult = (joinqual == NIL ||
615 ExecQual(joinqual, econtext, false));
616 MJ_DEBUG_QUAL(joinqual, qualResult);
620 mergestate->mj_MatchedOuter = true;
621 mergestate->mj_MatchedInner = true;
623 qualResult = (otherqual == NIL ||
624 ExecQual(otherqual, econtext, false));
625 MJ_DEBUG_QUAL(otherqual, qualResult);
630 * qualification succeeded. now form the desired
631 * projection tuple and return the slot containing
634 TupleTableSlot *result;
637 MJ_printf("ExecMergeJoin: returning tuple\n");
639 result = ExecProject(mergestate->jstate.cs_ProjInfo,
642 if (isDone != ExprEndResult)
644 mergestate->jstate.cs_TupFromTlist =
645 (isDone == ExprMultipleResult);
653 * EXEC_MJ_NEXTINNER means advance the inner scan to the
654 * next tuple. If the tuple is not nil, we then proceed to
655 * test it against the join qualification.
657 * Before advancing, we check to see if we must emit an
658 * outer-join fill tuple for this inner tuple.
660 case EXEC_MJ_NEXTINNER:
661 MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\n");
663 if (doFillInner && !mergestate->mj_MatchedInner)
666 * Generate a fake join tuple with nulls for the outer
667 * tuple, and return it if it passes the non-join
670 mergestate->mj_MatchedInner = true; /* do it only once */
672 ResetExprContext(econtext);
674 outerTupleSlot = mergestate->mj_NullOuterTupleSlot;
675 econtext->ecxt_outertuple = outerTupleSlot;
676 innerTupleSlot = mergestate->mj_InnerTupleSlot;
677 econtext->ecxt_innertuple = innerTupleSlot;
679 if (ExecQual(otherqual, econtext, false))
682 * qualification succeeded. now form the desired
683 * projection tuple and return the slot containing
686 TupleTableSlot *result;
689 MJ_printf("ExecMergeJoin: returning fill tuple\n");
691 result = ExecProject(mergestate->jstate.cs_ProjInfo,
694 if (isDone != ExprEndResult)
696 mergestate->jstate.cs_TupFromTlist =
697 (isDone == ExprMultipleResult);
704 * now we get the next inner tuple, if any
706 innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
707 mergestate->mj_InnerTupleSlot = innerTupleSlot;
708 MJ_DEBUG_PROC_NODE(innerTupleSlot);
709 mergestate->mj_MatchedInner = false;
711 if (TupIsNull(innerTupleSlot))
712 mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER;
714 mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
717 /*-------------------------------------------
718 * EXEC_MJ_NEXTOUTER means
721 * outer tuple - 5 5 - marked tuple
726 * we know we just bumped into the
727 * first inner tuple > current outer tuple
728 * so get a new outer tuple and then
729 * proceed to test it against the marked tuple
730 * (EXEC_MJ_TESTOUTER)
732 * Before advancing, we check to see if we must emit an
733 * outer-join fill tuple for this outer tuple.
734 *------------------------------------------------
736 case EXEC_MJ_NEXTOUTER:
737 MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n");
739 if (doFillOuter && !mergestate->mj_MatchedOuter)
742 * Generate a fake join tuple with nulls for the inner
743 * tuple, and return it if it passes the non-join
746 mergestate->mj_MatchedOuter = true; /* do it only once */
748 ResetExprContext(econtext);
750 outerTupleSlot = mergestate->mj_OuterTupleSlot;
751 econtext->ecxt_outertuple = outerTupleSlot;
752 innerTupleSlot = mergestate->mj_NullInnerTupleSlot;
753 econtext->ecxt_innertuple = innerTupleSlot;
755 if (ExecQual(otherqual, econtext, false))
758 * qualification succeeded. now form the desired
759 * projection tuple and return the slot containing
762 TupleTableSlot *result;
765 MJ_printf("ExecMergeJoin: returning fill tuple\n");
767 result = ExecProject(mergestate->jstate.cs_ProjInfo,
770 if (isDone != ExprEndResult)
772 mergestate->jstate.cs_TupFromTlist =
773 (isDone == ExprMultipleResult);
780 * now we get the next outer tuple, if any
782 outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
783 mergestate->mj_OuterTupleSlot = outerTupleSlot;
784 MJ_DEBUG_PROC_NODE(outerTupleSlot);
785 mergestate->mj_MatchedOuter = false;
788 * if the outer tuple is null then we are done with the
789 * join, unless we have inner tuples we need to null-fill.
791 if (TupIsNull(outerTupleSlot))
793 MJ_printf("ExecMergeJoin: end of outer subplan\n");
794 innerTupleSlot = mergestate->mj_InnerTupleSlot;
795 if (doFillInner && !TupIsNull(innerTupleSlot))
798 * Need to emit right-join tuples for remaining
801 mergestate->mj_JoinState = EXEC_MJ_ENDOUTER;
804 /* Otherwise we're done. */
808 mergestate->mj_JoinState = EXEC_MJ_TESTOUTER;
811 /*--------------------------------------------------------
812 * EXEC_MJ_TESTOUTER If the new outer tuple and the marked
813 * tuple satisfy the merge clause then we know we have
814 * duplicates in the outer scan so we have to restore the
815 * inner scan to the marked tuple and proceed to join the
816 * new outer tuples with the inner tuples (EXEC_MJ_JOINTEST)
818 * This is the case when
822 * new outer tuple - 5 5
826 * new outer tuple = marked tuple
828 * If the outer tuple fails the test, then we know we have
829 * to proceed to skip outer tuples until outer >= inner
830 * (EXEC_MJ_SKIPOUTER).
832 * This is the case when
837 * new outer tuple - 6 8 - inner tuple
841 * new outer tuple > marked tuple
843 *---------------------------------------------------------
845 case EXEC_MJ_TESTOUTER:
846 MJ_printf("ExecMergeJoin: EXEC_MJ_TESTOUTER\n");
849 * here we compare the outer tuple with the marked inner
852 ResetExprContext(econtext);
854 outerTupleSlot = mergestate->mj_OuterTupleSlot;
855 econtext->ecxt_outertuple = outerTupleSlot;
856 innerTupleSlot = mergestate->mj_MarkedTupleSlot;
857 econtext->ecxt_innertuple = innerTupleSlot;
859 qualResult = ExecQual(mergeclauses, econtext, false);
860 MJ_DEBUG_QUAL(mergeclauses, qualResult);
865 * the merge clause matched so now we restore the
866 * inner scan position to the first mark, and loop
867 * back to JOINTEST. Actually, since we know the
868 * mergeclause matches, we can skip JOINTEST and go
869 * straight to JOINTUPLES.
871 * NOTE: we do not need to worry about the MatchedInner
872 * state for the rescanned inner tuples. We know all
873 * of them will match this new outer tuple and
874 * therefore won't be emitted as fill tuples. This
875 * works *only* because we require the extra joinquals
876 * to be nil when doing a right or full join ---
877 * otherwise some of the rescanned tuples might fail
878 * the extra joinquals.
880 ExecRestrPos(innerPlan);
881 mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
886 * if the inner tuple was nil and the new outer
887 * tuple didn't match the marked outer tuple then
893 * 6 nil - inner tuple
896 * which means that all subsequent outer tuples will be
897 * larger than our marked inner tuples. So we're done.
900 innerTupleSlot = mergestate->mj_InnerTupleSlot;
901 if (TupIsNull(innerTupleSlot))
906 * Need to emit left-join tuples for remaining
909 mergestate->mj_JoinState = EXEC_MJ_ENDINNER;
912 /* Otherwise we're done. */
916 /* continue on to skip outer tuples */
917 mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_BEGIN;
921 /*----------------------------------------------------------
922 * EXEC_MJ_SKIPOUTER means skip over tuples in the outer plan
923 * until we find an outer tuple >= current inner tuple.
930 * outer tuple - 6 8 - inner tuple
934 * we have to advance the outer scan
935 * until we find the outer 8.
937 * To avoid redundant tests, we divide this into three
938 * sub-states: BEGIN, TEST, ADVANCE.
939 *----------------------------------------------------------
941 case EXEC_MJ_SKIPOUTER_BEGIN:
942 MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_BEGIN\n");
945 * before we advance, make sure the current tuples do not
946 * satisfy the mergeclauses. If they do, then we update
947 * the marked tuple and go join them.
949 ResetExprContext(econtext);
951 outerTupleSlot = mergestate->mj_OuterTupleSlot;
952 econtext->ecxt_outertuple = outerTupleSlot;
953 innerTupleSlot = mergestate->mj_InnerTupleSlot;
954 econtext->ecxt_innertuple = innerTupleSlot;
956 qualResult = ExecQual(mergeclauses, econtext, false);
957 MJ_DEBUG_QUAL(mergeclauses, qualResult);
961 ExecMarkPos(innerPlan);
963 MarkInnerTuple(innerTupleSlot, mergestate);
965 mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
969 mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_TEST;
972 case EXEC_MJ_SKIPOUTER_TEST:
973 MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_TEST\n");
976 * ok, now test the skip qualification
978 outerTupleSlot = mergestate->mj_OuterTupleSlot;
979 econtext->ecxt_outertuple = outerTupleSlot;
980 innerTupleSlot = mergestate->mj_InnerTupleSlot;
981 econtext->ecxt_innertuple = innerTupleSlot;
983 compareResult = MergeCompare(mergeclauses,
987 MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult);
990 * compareResult is true as long as we should continue
991 * skipping outer tuples.
995 mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_ADVANCE;
1000 * now check the inner skip qual to see if we should now
1001 * skip inner tuples... if we fail the inner skip qual,
1002 * then we know we have a new pair of matching tuples.
1004 compareResult = MergeCompare(mergeclauses,
1008 MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult);
1011 mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_BEGIN;
1013 mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
1017 * Before advancing, we check to see if we must emit an
1018 * outer-join fill tuple for this outer tuple.
1020 case EXEC_MJ_SKIPOUTER_ADVANCE:
1021 MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_ADVANCE\n");
1023 if (doFillOuter && !mergestate->mj_MatchedOuter)
1026 * Generate a fake join tuple with nulls for the inner
1027 * tuple, and return it if it passes the non-join
1030 mergestate->mj_MatchedOuter = true; /* do it only once */
1032 ResetExprContext(econtext);
1034 outerTupleSlot = mergestate->mj_OuterTupleSlot;
1035 econtext->ecxt_outertuple = outerTupleSlot;
1036 innerTupleSlot = mergestate->mj_NullInnerTupleSlot;
1037 econtext->ecxt_innertuple = innerTupleSlot;
1039 if (ExecQual(otherqual, econtext, false))
1042 * qualification succeeded. now form the desired
1043 * projection tuple and return the slot containing
1046 TupleTableSlot *result;
1047 ExprDoneCond isDone;
1049 MJ_printf("ExecMergeJoin: returning fill tuple\n");
1051 result = ExecProject(mergestate->jstate.cs_ProjInfo,
1054 if (isDone != ExprEndResult)
1056 mergestate->jstate.cs_TupFromTlist =
1057 (isDone == ExprMultipleResult);
1064 * now we get the next outer tuple, if any
1066 outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
1067 mergestate->mj_OuterTupleSlot = outerTupleSlot;
1068 MJ_DEBUG_PROC_NODE(outerTupleSlot);
1069 mergestate->mj_MatchedOuter = false;
1072 * if the outer tuple is null then we are done with the
1073 * join, unless we have inner tuples we need to null-fill.
1075 if (TupIsNull(outerTupleSlot))
1077 MJ_printf("ExecMergeJoin: end of outer subplan\n");
1078 innerTupleSlot = mergestate->mj_InnerTupleSlot;
1079 if (doFillInner && !TupIsNull(innerTupleSlot))
1082 * Need to emit right-join tuples for remaining
1085 mergestate->mj_JoinState = EXEC_MJ_ENDOUTER;
1088 /* Otherwise we're done. */
1093 * otherwise test the new tuple against the skip qual.
1095 mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_TEST;
1098 /*-----------------------------------------------------------
1099 * EXEC_MJ_SKIPINNER means skip over tuples in the inner plan
1100 * until we find an inner tuple >= current outer tuple.
1107 * outer tuple - 12 8 - inner tuple
1111 * we have to advance the inner scan
1112 * until we find the inner 12.
1114 * To avoid redundant tests, we divide this into three
1115 * sub-states: BEGIN, TEST, ADVANCE.
1116 *-------------------------------------------------------
1118 case EXEC_MJ_SKIPINNER_BEGIN:
1119 MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_BEGIN\n");
1122 * before we advance, make sure the current tuples do not
1123 * satisfy the mergeclauses. If they do, then we update
1124 * the marked tuple and go join them.
1126 ResetExprContext(econtext);
1128 outerTupleSlot = mergestate->mj_OuterTupleSlot;
1129 econtext->ecxt_outertuple = outerTupleSlot;
1130 innerTupleSlot = mergestate->mj_InnerTupleSlot;
1131 econtext->ecxt_innertuple = innerTupleSlot;
1133 qualResult = ExecQual(mergeclauses, econtext, false);
1134 MJ_DEBUG_QUAL(mergeclauses, qualResult);
1138 ExecMarkPos(innerPlan);
1140 MarkInnerTuple(innerTupleSlot, mergestate);
1142 mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
1146 mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_TEST;
1149 case EXEC_MJ_SKIPINNER_TEST:
1150 MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_TEST\n");
1153 * ok, now test the skip qualification
1155 outerTupleSlot = mergestate->mj_OuterTupleSlot;
1156 econtext->ecxt_outertuple = outerTupleSlot;
1157 innerTupleSlot = mergestate->mj_InnerTupleSlot;
1158 econtext->ecxt_innertuple = innerTupleSlot;
1160 compareResult = MergeCompare(mergeclauses,
1164 MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult);
1167 * compareResult is true as long as we should continue
1168 * skipping inner tuples.
1172 mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE;
1177 * now check the outer skip qual to see if we should now
1178 * skip outer tuples... if we fail the outer skip qual,
1179 * then we know we have a new pair of matching tuples.
1181 compareResult = MergeCompare(mergeclauses,
1185 MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult);
1188 mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_BEGIN;
1190 mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
1194 * Before advancing, we check to see if we must emit an
1195 * outer-join fill tuple for this inner tuple.
1197 case EXEC_MJ_SKIPINNER_ADVANCE:
1198 MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_ADVANCE\n");
1200 if (doFillInner && !mergestate->mj_MatchedInner)
1203 * Generate a fake join tuple with nulls for the outer
1204 * tuple, and return it if it passes the non-join
1207 mergestate->mj_MatchedInner = true; /* do it only once */
1209 ResetExprContext(econtext);
1211 outerTupleSlot = mergestate->mj_NullOuterTupleSlot;
1212 econtext->ecxt_outertuple = outerTupleSlot;
1213 innerTupleSlot = mergestate->mj_InnerTupleSlot;
1214 econtext->ecxt_innertuple = innerTupleSlot;
1216 if (ExecQual(otherqual, econtext, false))
1219 * qualification succeeded. now form the desired
1220 * projection tuple and return the slot containing
1223 TupleTableSlot *result;
1224 ExprDoneCond isDone;
1226 MJ_printf("ExecMergeJoin: returning fill tuple\n");
1228 result = ExecProject(mergestate->jstate.cs_ProjInfo,
1231 if (isDone != ExprEndResult)
1233 mergestate->jstate.cs_TupFromTlist =
1234 (isDone == ExprMultipleResult);
1241 * now we get the next inner tuple, if any
1243 innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
1244 mergestate->mj_InnerTupleSlot = innerTupleSlot;
1245 MJ_DEBUG_PROC_NODE(innerTupleSlot);
1246 mergestate->mj_MatchedInner = false;
1249 * if the inner tuple is null then we are done with the
1250 * join, unless we have outer tuples we need to null-fill.
1252 if (TupIsNull(innerTupleSlot))
1254 MJ_printf("ExecMergeJoin: end of inner subplan\n");
1255 outerTupleSlot = mergestate->mj_OuterTupleSlot;
1256 if (doFillOuter && !TupIsNull(outerTupleSlot))
1259 * Need to emit left-join tuples for remaining
1262 mergestate->mj_JoinState = EXEC_MJ_ENDINNER;
1265 /* Otherwise we're done. */
1270 * otherwise test the new tuple against the skip qual.
1272 mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_TEST;
1276 * EXEC_MJ_ENDOUTER means we have run out of outer tuples,
1277 * but are doing a right/full join and therefore must
1278 * null- fill any remaing unmatched inner tuples.
1280 case EXEC_MJ_ENDOUTER:
1281 MJ_printf("ExecMergeJoin: EXEC_MJ_ENDOUTER\n");
1283 Assert(doFillInner);
1285 if (!mergestate->mj_MatchedInner)
1288 * Generate a fake join tuple with nulls for the outer
1289 * tuple, and return it if it passes the non-join
1292 mergestate->mj_MatchedInner = true; /* do it only once */
1294 ResetExprContext(econtext);
1296 outerTupleSlot = mergestate->mj_NullOuterTupleSlot;
1297 econtext->ecxt_outertuple = outerTupleSlot;
1298 innerTupleSlot = mergestate->mj_InnerTupleSlot;
1299 econtext->ecxt_innertuple = innerTupleSlot;
1301 if (ExecQual(otherqual, econtext, false))
1304 * qualification succeeded. now form the desired
1305 * projection tuple and return the slot containing
1308 TupleTableSlot *result;
1309 ExprDoneCond isDone;
1311 MJ_printf("ExecMergeJoin: returning fill tuple\n");
1313 result = ExecProject(mergestate->jstate.cs_ProjInfo,
1316 if (isDone != ExprEndResult)
1318 mergestate->jstate.cs_TupFromTlist =
1319 (isDone == ExprMultipleResult);
1326 * now we get the next inner tuple, if any
1328 innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
1329 mergestate->mj_InnerTupleSlot = innerTupleSlot;
1330 MJ_DEBUG_PROC_NODE(innerTupleSlot);
1331 mergestate->mj_MatchedInner = false;
1333 if (TupIsNull(innerTupleSlot))
1335 MJ_printf("ExecMergeJoin: end of inner subplan\n");
1339 /* Else remain in ENDOUTER state and process next tuple. */
1343 * EXEC_MJ_ENDINNER means we have run out of inner tuples,
1344 * but are doing a left/full join and therefore must null-
1345 * fill any remaing unmatched outer tuples.
1347 case EXEC_MJ_ENDINNER:
1348 MJ_printf("ExecMergeJoin: EXEC_MJ_ENDINNER\n");
1350 Assert(doFillOuter);
1352 if (!mergestate->mj_MatchedOuter)
1355 * Generate a fake join tuple with nulls for the inner
1356 * tuple, and return it if it passes the non-join
1359 mergestate->mj_MatchedOuter = true; /* do it only once */
1361 ResetExprContext(econtext);
1363 outerTupleSlot = mergestate->mj_OuterTupleSlot;
1364 econtext->ecxt_outertuple = outerTupleSlot;
1365 innerTupleSlot = mergestate->mj_NullInnerTupleSlot;
1366 econtext->ecxt_innertuple = innerTupleSlot;
1368 if (ExecQual(otherqual, econtext, false))
1371 * qualification succeeded. now form the desired
1372 * projection tuple and return the slot containing
1375 TupleTableSlot *result;
1376 ExprDoneCond isDone;
1378 MJ_printf("ExecMergeJoin: returning fill tuple\n");
1380 result = ExecProject(mergestate->jstate.cs_ProjInfo,
1383 if (isDone != ExprEndResult)
1385 mergestate->jstate.cs_TupFromTlist =
1386 (isDone == ExprMultipleResult);
1393 * now we get the next outer tuple, if any
1395 outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
1396 mergestate->mj_OuterTupleSlot = outerTupleSlot;
1397 MJ_DEBUG_PROC_NODE(outerTupleSlot);
1398 mergestate->mj_MatchedOuter = false;
1400 if (TupIsNull(outerTupleSlot))
1402 MJ_printf("ExecMergeJoin: end of outer subplan\n");
1406 /* Else remain in ENDINNER state and process next tuple. */
1410 * if we get here it means our code is fouled up and so we
1411 * just end the join prematurely.
1414 elog(NOTICE, "ExecMergeJoin: invalid join state %d, aborting",
1415 mergestate->mj_JoinState);
1421 /* ----------------------------------------------------------------
1425 * Creates the run-time state information for the node and
1426 * sets the relation id to contain relevant decriptors.
1427 * ----------------------------------------------------------------
1430 ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
1432 MergeJoinState *mergestate;
1435 MJ1_printf("ExecInitMergeJoin: %s\n",
1436 "initializing node");
1439 * assign the node's execution state and get the range table and
1442 node->join.plan.state = estate;
1445 * create new merge state for node
1447 mergestate = makeNode(MergeJoinState);
1448 node->mergestate = mergestate;
1451 * Miscellaneous initialization
1453 * create expression context for node
1455 ExecAssignExprContext(estate, &mergestate->jstate);
1458 * initialize subplans
1460 ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node);
1461 ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node);
1463 #define MERGEJOIN_NSLOTS 4
1466 * tuple table initialization
1468 ExecInitResultTupleSlot(estate, &mergestate->jstate);
1470 mergestate->mj_MarkedTupleSlot = ExecInitExtraTupleSlot(estate);
1471 ExecSetSlotDescriptor(mergestate->mj_MarkedTupleSlot,
1472 ExecGetTupType(innerPlan((Plan *) node)),
1475 switch (node->join.jointype)
1480 mergestate->mj_NullInnerTupleSlot =
1481 ExecInitNullTupleSlot(estate,
1482 ExecGetTupType(innerPlan((Plan *) node)));
1485 mergestate->mj_NullOuterTupleSlot =
1486 ExecInitNullTupleSlot(estate,
1487 ExecGetTupType(outerPlan((Plan *) node)));
1490 * Can't handle right or full join with non-nil extra
1493 if (node->join.joinqual != NIL)
1494 elog(ERROR, "RIGHT JOIN is only supported with mergejoinable join conditions");
1497 mergestate->mj_NullOuterTupleSlot =
1498 ExecInitNullTupleSlot(estate,
1499 ExecGetTupType(outerPlan((Plan *) node)));
1500 mergestate->mj_NullInnerTupleSlot =
1501 ExecInitNullTupleSlot(estate,
1502 ExecGetTupType(innerPlan((Plan *) node)));
1505 * Can't handle right or full join with non-nil extra
1508 if (node->join.joinqual != NIL)
1509 elog(ERROR, "FULL JOIN is only supported with mergejoinable join conditions");
1512 elog(ERROR, "ExecInitMergeJoin: unsupported join type %d",
1513 (int) node->join.jointype);
1517 * initialize tuple type and projection info
1519 ExecAssignResultTypeFromTL((Plan *) node, &mergestate->jstate);
1520 ExecAssignProjectionInfo((Plan *) node, &mergestate->jstate);
1523 * form merge skip qualifications
1525 joinclauses = node->mergeclauses;
1526 mergestate->mj_OuterSkipQual = MJFormSkipQual(joinclauses, "<");
1527 mergestate->mj_InnerSkipQual = MJFormSkipQual(joinclauses, ">");
1529 MJ_printf("\nExecInitMergeJoin: OuterSkipQual is ");
1530 MJ_nodeDisplay(mergestate->mj_OuterSkipQual);
1531 MJ_printf("\nExecInitMergeJoin: InnerSkipQual is ");
1532 MJ_nodeDisplay(mergestate->mj_InnerSkipQual);
1536 * initialize join state
1538 mergestate->mj_JoinState = EXEC_MJ_INITIALIZE;
1539 mergestate->jstate.cs_TupFromTlist = false;
1540 mergestate->mj_MatchedOuter = false;
1541 mergestate->mj_MatchedInner = false;
1542 mergestate->mj_OuterTupleSlot = NULL;
1543 mergestate->mj_InnerTupleSlot = NULL;
1546 * initialization successful
1548 MJ1_printf("ExecInitMergeJoin: %s\n",
1549 "node initialized");
1555 ExecCountSlotsMergeJoin(MergeJoin *node)
1557 return ExecCountSlotsNode(outerPlan((Plan *) node)) +
1558 ExecCountSlotsNode(innerPlan((Plan *) node)) +
1562 /* ----------------------------------------------------------------
1566 * frees storage allocated through C routines.
1567 * ----------------------------------------------------------------
1570 ExecEndMergeJoin(MergeJoin *node)
1572 MergeJoinState *mergestate;
1574 MJ1_printf("ExecEndMergeJoin: %s\n",
1575 "ending node processing");
1578 * get state information from the node
1580 mergestate = node->mergestate;
1583 * Free the projection info and the scan attribute info
1585 * Note: we don't ExecFreeResultType(mergestate) because the rule manager
1586 * depends on the tupType returned by ExecMain(). So for now, this is
1587 * freed at end-transaction time. -cim 6/2/91
1589 ExecFreeProjectionInfo(&mergestate->jstate);
1590 ExecFreeExprContext(&mergestate->jstate);
1593 * shut down the subplans
1595 ExecEndNode((Plan *) innerPlan((Plan *) node), (Plan *) node);
1596 ExecEndNode((Plan *) outerPlan((Plan *) node), (Plan *) node);
1599 * clean out the tuple table
1601 ExecClearTuple(mergestate->jstate.cs_ResultTupleSlot);
1602 ExecClearTuple(mergestate->mj_MarkedTupleSlot);
1604 MJ1_printf("ExecEndMergeJoin: %s\n",
1605 "node processing ended");
1609 ExecReScanMergeJoin(MergeJoin *node, ExprContext *exprCtxt, Plan *parent)
1611 MergeJoinState *mergestate = node->mergestate;
1613 ExecClearTuple(mergestate->mj_MarkedTupleSlot);
1615 mergestate->mj_JoinState = EXEC_MJ_INITIALIZE;
1616 mergestate->jstate.cs_TupFromTlist = false;
1617 mergestate->mj_MatchedOuter = false;
1618 mergestate->mj_MatchedInner = false;
1619 mergestate->mj_OuterTupleSlot = NULL;
1620 mergestate->mj_InnerTupleSlot = NULL;
1623 * if chgParam of subnodes is not null then plans will be re-scanned
1624 * by first ExecProcNode.
1626 if (((Plan *) node)->lefttree->chgParam == NULL)
1627 ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
1628 if (((Plan *) node)->righttree->chgParam == NULL)
1629 ExecReScan(((Plan *) node)->righttree, exprCtxt, (Plan *) node);