Add first code to help with outer joins.
authorThomas G. Lockhart
Tue, 23 Feb 1999 07:35:09 +0000 (07:35 +0000)
committerThomas G. Lockhart
Tue, 23 Feb 1999 07:35:09 +0000 (07:35 +0000)
Enable by defining
 CFLAGS+= -DENABLE_OUTER_JOINS -DEXEC_MERGEJOINDEBUG
in your Makefile.custom

src/backend/executor/nodeMergejoin.c

index 1d85605c4a57edf508266bf1b02b9de718c186ba..db22520bc6b72c150768a96fb1b8a30ccc4b8c56 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.22 1999/02/22 19:40:10 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.23 1999/02/23 07:35:09 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,7 +19,7 @@
  *
  * NOTES
  *     Essential operation of the merge join algorithm is as follows:
- *     (** indicates the tuples satisify the merge clause).
+ *     (** indicates the tuples satisfy the merge clause).
  *
  *     Join {                                                 -
  *         get initial outer and inner tuples              INITIALIZE
  *             Skip Outer                                  SKIPOUTER
  *     }                                                      -
  *
- *     Currently, the merge join operation is coded in the fashion
+ *     The merge join operation is coded in the fashion
  *     of a state machine.  At each state, we do something and then
  *     proceed to another state.  This state is stored in the node's
  *     execution state information and is preserved across calls to
  *     ExecMergeJoin. -cim 10/31/89
  *
- *     Warning:  This code is known to fail for inequality operations
- *               and is being redesigned.  Specifically, = and > work
- *               but the logic is not correct for <.  Since mergejoins
- *               are no better then nestloops for inequalitys, the planner
- *               should not plan them anyways.  Alternatively, the
- *               planner could just exchange the inner/outer relations
- *               if it ever sees a <... -cim 7/1/90
- *
- *     Update:   The executor tuple table has long since alleviated the
- *               problem described above -cim 4/23/91
- *
  */
 #include "postgres.h"
 
@@ -85,6 +74,7 @@
 #include "utils/lsyscache.h"
 #include "utils/psort.h"
 
+
 static bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext);
 
 #define MarkInnerTuple(innerTupleSlot, mergestate) \
@@ -95,6 +85,7 @@ static bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
                   true) \
 )
 
+
 /* ----------------------------------------------------------------
  *     MJFormOSortopI
  *
@@ -296,6 +287,9 @@ MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
  * ----------------------------------------------------------------
  */
 #ifdef EXEC_MERGEJOINDEBUG
+void
+ExecMergeTupleDumpInner(ExprContext *econtext);
+
 void
 ExecMergeTupleDumpInner(ExprContext *econtext)
 {
@@ -306,10 +300,13 @@ ExecMergeTupleDumpInner(ExprContext *econtext)
    if (TupIsNull(innerSlot))
        printf("(nil)\n");
    else
-       debugtup(innerSlot->val,
-                innerSlot->ttc_tupleDescriptor);
+       MJ_debugtup(innerSlot->val,
+                   innerSlot->ttc_tupleDescriptor);
 }
 
+void
+ExecMergeTupleDumpOuter(ExprContext *econtext);
+
 void
 ExecMergeTupleDumpOuter(ExprContext *econtext)
 {
@@ -320,10 +317,14 @@ ExecMergeTupleDumpOuter(ExprContext *econtext)
    if (TupIsNull(outerSlot))
        printf("(nil)\n");
    else
-       debugtup(outerSlot->val,
-                outerSlot->ttc_tupleDescriptor);
+       MJ_debugtup(outerSlot->val,
+                   outerSlot->ttc_tupleDescriptor);
 }
 
+void
+ExecMergeTupleDumpMarked(ExprContext *econtext,
+                        MergeJoinState *mergestate);
+
 void
 ExecMergeTupleDumpMarked(ExprContext *econtext,
                         MergeJoinState *mergestate)
@@ -336,10 +337,13 @@ ExecMergeTupleDumpMarked(ExprContext *econtext,
    if (TupIsNull(markedSlot))
        printf("(nil)\n");
    else
-       debugtup(markedSlot->val,
-                markedSlot->ttc_tupleDescriptor);
+       MJ_debugtup(markedSlot->val,
+                   markedSlot->ttc_tupleDescriptor);
 }
 
+void
+ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate);
+
 void
 ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate)
 {
@@ -351,7 +355,6 @@ ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate)
 
    printf("******** \n");
 }
-
 #endif
 
 /* ----------------------------------------------------------------
@@ -423,6 +426,14 @@ ExecMergeJoin(MergeJoin *node)
 
    ExprContext *econtext;
 
+#ifdef ENABLE_OUTER_JOINS
+   /* These should be set from the expression context!
+    * - thomas 1999-02-20
+    */
+   static bool isLeftJoin = true;
+   static bool isRightJoin = false;
+#endif
+
    /* ----------------
     *  get information from node
     * ----------------
@@ -475,14 +486,12 @@ ExecMergeJoin(MergeJoin *node)
        switch (mergestate->mj_JoinState)
        {
 
-               /* ---------------------------------------------------
-                * EXEC_MJ_INITIALIZE
-                * means that this is the first time ExecMergeJoin() has
-                * been called and so we have to initialize the inner,
-                * outer and marked tuples as well as various stuff in the
-                * expression context.
-                * ---------------------------------------------------
-                */
+               /*********************************
+                * EXEC_MJ_INITIALIZE means that this is the first time
+                * ExecMergeJoin() has been called and so we have to initialize
+                * the inner, outer and marked tuples as well as various stuff
+                * in the expression context.
+                *********************************/
            case EXEC_MJ_INITIALIZE:
                MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE\n");
                /* ----------------
@@ -522,13 +531,11 @@ ExecMergeJoin(MergeJoin *node)
                mergestate->mj_JoinState = EXEC_MJ_SKIPINNER;
                break;
 
-               /* ---------------------------------------------------
-                * EXEC_MJ_JOINMARK
-                * means we have just found a new outer tuple and a possible
-                * matching inner tuple. This is the case after the
-                * INITIALIZE, SKIPOUTER or SKIPINNER states.
-                * ----------------------------------------------------
-                */
+               /*********************************
+                * EXEC_MJ_JOINMARK means we have just found a new outer tuple
+                * and a possible matching inner tuple. This is the case after
+                * the INITIALIZE, SKIPOUTER or SKIPINNER states.
+                *********************************/
            case EXEC_MJ_JOINMARK:
                MJ_printf("ExecMergeJoin: EXEC_MJ_JOINMARK\n");
                ExecMarkPos(innerPlan);
@@ -538,17 +545,15 @@ ExecMergeJoin(MergeJoin *node)
                mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
                break;
 
-               /* ----------------------------------------------------
-                * EXEC_MJ_JOINTEST
-                * means we have two tuples which might satisify the merge
-                * clause, so we test them.
+               /*********************************
+                * EXEC_MJ_JOINTEST means we have two tuples which might
+                * satisfy the merge clause, so we test them.
                 *
-                * If they do satisify, then we join them and move on to the
+                * If they do satisfy, then we join them and move on to the
                 * next inner tuple (EXEC_MJ_JOINTUPLES).
                 *
-                * If they do not satisify then advance to next outer tuple.
-                * ------------------------------------------------------
-                */
+                * If they do not satisfy then advance to next outer tuple.
+                *********************************/
            case EXEC_MJ_JOINTEST:
                MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n");
 
@@ -561,13 +566,11 @@ ExecMergeJoin(MergeJoin *node)
                    mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER;
                break;
 
-               /* ----------------------------------------------------
-                * EXEC_MJ_JOINTUPLES
-                * means we have two tuples which satisified the merge
-                * clause so we join them and then proceed to get the next
-                * inner tuple (EXEC_NEXT_INNER).
-                * ----------------------------------------------------
-                */
+               /*********************************
+                * EXEC_MJ_JOINTUPLES means we have two tuples which
+                * satisified the merge clause so we join them and then
+                * proceed to get the next inner tuple (EXEC_NEXT_INNER).
+                *********************************/
            case EXEC_MJ_JOINTUPLES:
                MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");
                mergestate->mj_JoinState = EXEC_MJ_NEXTINNER;
@@ -596,13 +599,11 @@ ExecMergeJoin(MergeJoin *node)
                }
                break;
 
-               /* --------------------------------------------------
-                * EXEC_MJ_NEXTINNER
-                * means advance the inner scan to the next tuple.  If the
-                * tuple is not nil, we then proceed to test it against
-                * the join qualification.
-                * ----------------------------------------------------
-                */
+               /*********************************
+                * EXEC_MJ_NEXTINNER means advance the inner scan to
+                * the next tuple. If the tuple is not nil, we then
+                * proceed to test it against the join qualification.
+                *********************************/
            case EXEC_MJ_NEXTINNER:
                MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\n");
 
@@ -620,21 +621,21 @@ ExecMergeJoin(MergeJoin *node)
                    mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
                break;
 
-               /* --------------------------------------------------
-                * EXEC_MJ_NEXTOUTER
-                * means
-                *                outer inner
-                *   outer tuple -  5     5  - marked tuple
-                *                  5     5
-                *                  6     6  - inner tuple
-                *                  7     7
+               /*********************************
+                * EXEC_MJ_NEXTOUTER means
+                *
+                *              outer inner
+                * outer tuple -  5     5  - marked tuple
+                *                5     5
+                *                6     6  - inner tuple
+                *                7     7
                 *
-                * we know we just bumped into the first inner tuple >
-                * current outer tuple so get a new outer tuple and then
+                * we know we just bumped into the 
+                * first inner tuple > current outer tuple
+                * so get a new outer tuple and then
                 * proceed to test it against the marked tuple
                 * (EXEC_MJ_TESTOUTER)
-                * -------------------------------------------------
-                */
+                *********************************/
            case EXEC_MJ_NEXTOUTER:
                MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n");
 
@@ -656,13 +657,12 @@ ExecMergeJoin(MergeJoin *node)
                mergestate->mj_JoinState = EXEC_MJ_TESTOUTER;
                break;
 
-               /* ---------------------------------------------------
-                * EXEC_MJ_TESTOUTER
-                * If the new outer tuple and the marked tuple satisify the
-                * merge clause then we know we have duplicates in the
-                * outer scan so we have to restore the inner scan to the
-                * marked tuple and proceed to join the new outer tuples
-                * with the inner tuples (EXEC_MJ_JOINTEST)
+               /*********************************
+                * EXEC_MJ_TESTOUTER If the new outer tuple and the marked
+                * tuple satisfy the merge clause then we know we have
+                * duplicates in the outer scan so we have to restore the
+                * inner scan to the marked tuple and proceed to join the
+                * new outer tuples with the inner tuples (EXEC_MJ_JOINTEST)
                 *
                 * This is the case when
                 *                        outer inner
@@ -687,10 +687,9 @@ ExecMergeJoin(MergeJoin *node)
                 *                          7    12
                 *
                 *
-                *          new outer tuple > marked tuple
+                *       new outer tuple > marked tuple
                 *
-                * -----------------------------------------------------------
-                */
+                *********************************/
            case EXEC_MJ_TESTOUTER:
                MJ_printf("ExecMergeJoin: EXEC_MJ_TESTOUTER\n");
 
@@ -732,11 +731,11 @@ ExecMergeJoin(MergeJoin *node)
                     *  tuple didn't match the marked outer tuple then
                     *  we may have the case:
                     *
-                    *              outer   inner
-                    *                  4     4   - marked tuple
-                    *      new outer - 5     4
-                    *                  6    nil  - inner tuple
-                    *                  7
+                    *           outer inner
+                    *             4     4  - marked tuple
+                    * new outer - 5     4
+                    *             6    nil - inner tuple
+                    *             7
                     *
                     *  which means that all subsequent outer tuples will be
                     *  larger than our inner tuples.
@@ -744,7 +743,15 @@ ExecMergeJoin(MergeJoin *node)
                     */
                    if (TupIsNull(innerTupleSlot))
                    {
-                       MJ_printf("ExecMergeJoin: **** wierd case 1 ****\n");
+#ifdef ENABLE_OUTER_JOINS
+                       if (isLeftJoin)
+                       {
+                           /* continue on to null fill outer tuples */
+                           mergestate->mj_JoinState = EXEC_MJ_FILLOUTER;
+                           break;
+                       }
+#endif
+                       MJ_printf("ExecMergeJoin: **** weird case 1 ****\n");
                        return NULL;
                    }
 
@@ -753,29 +760,27 @@ ExecMergeJoin(MergeJoin *node)
                }
                break;
 
-               /* --------------------------------------------------
-                * EXEC_MJ_SKIPOUTER
-                * means skip over tuples in the outer plan until we find
-                * an outer tuple > current inner tuple.
+               /*********************************
+                * EXEC_MJ_SKIPOUTER means skip over tuples in the outer plan
+                * until we find an outer tuple > current inner tuple.
                 *
                 * For example:
                 *
-                *                        outer inner
-                *                          5     5
-                *                          5     5
-                *           outer tuple -  6     8  - inner tuple
-                *                          7    12
-                *                          8    14
+                *              outer inner
+                *                5     5
+                *                5     5
+                * outer tuple -  6     8  - inner tuple
+                *                7    12
+                *                8    14
                 *
-                * We have to advance the outer scan until we find the outer
-                * 8.
-                * ------------------------------------------------
-                */
+                * we have to advance the outer scan
+                * until we find the outer 8.
+                *********************************/
            case EXEC_MJ_SKIPOUTER:
                MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER\n");
                /* ----------------
                 *  before we advance, make sure the current tuples
-                *  do not satisify the mergeclauses.  If they do, then
+                *  do not satisfy the mergeclauses.  If they do, then
                 *  we update the marked tuple and go join them.
                 * ----------------
                 */
@@ -809,6 +814,17 @@ ExecMergeJoin(MergeJoin *node)
                 */
                if (compareResult)
                {
+#ifdef ENABLE_OUTER_JOINS
+                   /* ----------------
+                    *  if this is a left or full outer join, then fill
+                    * ----------------
+                    */
+                   if (isLeftJoin)
+                   {
+                       mergestate->mj_JoinState = EXEC_MJ_FILLOUTER;
+                       break;
+                   }
+#endif
 
                    outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
                    MJ_DEBUG_PROC_NODE(outerTupleSlot);
@@ -851,28 +867,28 @@ ExecMergeJoin(MergeJoin *node)
                    mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
                break;
 
-               /* ------------------------------------------------
-                * EXEC_MJ_SKIPINNER
-                * means skip over tuples in the inner plan until we find
-                * an inner tuple > current outer tuple.
+               /*********************************
+                * EXEC_MJ_SKIPINNER means skip over tuples in the inner plan
+                * until we find an inner tuple > current outer tuple.
                 *
                 * For example:
-                *                        outer inner
-                *                          5     5
-                *                          5     5
-                *           outer tuple - 12     8 - inner tuple
-                *                         14    10
-                *                         17    12
                 *
-                * We have to advance the inner scan until we find the inner
-                * 12.
-                * ---------------------------------------------------
-                */
+                *              outer inner
+                *                5     5
+                *                5     5
+                * outer tuple - 12     8  - inner tuple
+                *               14    10
+                *               17    12
+                *
+                * we have to advance the inner scan
+                * until we find the inner 12.
+                *
+                *********************************/
            case EXEC_MJ_SKIPINNER:
                MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER\n");
                /* ----------------
                 *  before we advance, make sure the current tuples
-                *  do not satisify the mergeclauses.  If they do, then
+                *  do not satisfy the mergeclauses.  If they do, then
                 *  we update the marked tuple and go join them.
                 * ----------------
                 */
@@ -906,6 +922,18 @@ ExecMergeJoin(MergeJoin *node)
                 */
                if (compareResult)
                {
+#ifdef ENABLE_OUTER_JOINS
+                   /* ----------------
+                    *  if this is a right or full outer join, then fill
+                    * ----------------
+                    */
+                   if (isRightJoin)
+                   {
+                       mergestate->mj_JoinState = EXEC_MJ_FILLINNER;
+                       break;
+                   }
+#endif
+
                    /* ----------------
                     *  now try and get a new inner tuple
                     * ----------------
@@ -937,7 +965,7 @@ ExecMergeJoin(MergeJoin *node)
                         *  This means the join should end.
                         * ----------------
                         */
-                       MJ_printf("ExecMergeJoin: **** wierd case 2 ****\n");
+                       MJ_printf("ExecMergeJoin: **** weird case 2 ****\n");
                        return NULL;
                    }
 
@@ -968,10 +996,109 @@ ExecMergeJoin(MergeJoin *node)
 
                break;
 
-               /*
-                * If we get here it means our code is messed up and so we
-                * just end the join prematurely.
+#ifdef ENABLE_OUTER_JOINS
+               /*********************************
+                * EXEC_MJ_FILLINNER means we have an unmatched inner tuple
+                * which must be null-expanded into the projection tuple.
+                * get the next inner tuple and reset markers (EXEC_MJ_JOINMARK).
+                *********************************/
+           case EXEC_MJ_FILLINNER:
+               MJ_printf("ExecMergeJoin: EXEC_MJ_FILLINNER\n");
+               mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
+
+               /* ----------------
+                *  project the inner tuple into the result
+                * ----------------
                 */
+               MJ_printf("ExecMergeJoin: project inner tuple into the result (not yet implemented)\n");
+
+               /* ----------------
+                *  now skip this inner tuple
+                * ----------------
+                */
+               innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
+               MJ_DEBUG_PROC_NODE(innerTupleSlot);
+               econtext->ecxt_innertuple = innerTupleSlot;
+
+               /* ----------------
+                *  if the inner tuple is null then we know
+                *  we have to restore the inner scan
+                *  and advance to the next outer tuple
+                * ----------------
+                */
+               if (TupIsNull(innerTupleSlot))
+               {
+                   if (isLeftJoin && !TupIsNull(outerTupleSlot))
+                   {
+                       mergestate->mj_JoinState = EXEC_MJ_FILLOUTER;
+                       MJ_printf("ExecMergeJoin: try to complete outer fill\n");
+                       break;
+                   }
+
+                   MJ_printf("ExecMergeJoin: **** weird case 2 ****\n");
+                   return NULL;
+               }
+
+               /* ----------------
+                *  otherwise test the new tuple against the skip qual.
+                *  (we move to the EXEC_MJ_JOINMARK state)
+                * ----------------
+                */
+               break;
+
+               /*********************************
+                * EXEC_MJ_FILLOUTER means we have an unmatched outer tuple
+                * which must be null-expanded into the projection tuple.
+                * get the next outer tuple and reset markers (EXEC_MJ_JOINMARK).
+                *********************************/
+           case EXEC_MJ_FILLOUTER:
+               MJ_printf("ExecMergeJoin: EXEC_MJ_FILLOUTER\n");
+               mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
+
+               /* ----------------
+                *  project the outer tuple into the result
+                * ----------------
+                */
+               MJ_printf("ExecMergeJoin: project outer tuple into the result (not yet implemented)\n");
+
+               /* ----------------
+                *  now skip this outer tuple
+                * ----------------
+                */
+               outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
+               MJ_DEBUG_PROC_NODE(outerTupleSlot);
+               econtext->ecxt_outertuple = outerTupleSlot;
+
+               /* ----------------
+                *  if the outer tuple is null then we know
+                *  we are done with the left half of the join
+                * ----------------
+                */
+               if (TupIsNull(outerTupleSlot))
+               {
+                   if (isRightJoin && !TupIsNull(innerTupleSlot))
+                   {
+                       mergestate->mj_JoinState = EXEC_MJ_FILLINNER;
+                       MJ_printf("ExecMergeJoin: try to complete inner fill\n");
+                       break;
+                   }
+
+                   MJ_printf("ExecMergeJoin: **** outerTuple is nil ****\n");
+                   return NULL;
+               }
+
+               /* ----------------
+                *  otherwise test the new tuple against the skip qual.
+                *  (we move to the EXEC_MJ_JOINMARK state)
+                * ----------------
+                */
+               break;
+#endif
+
+               /*********************************
+                * if we get here it means our code is fouled up
+                * and so we just end the join prematurely.
+                *********************************/
            default:
                elog(NOTICE, "ExecMergeJoin: invalid join state. aborting");
                return NULL;