Skip to content
Snippets Groups Projects
Commit 6d73a8c0 authored by Thomas G. Lockhart's avatar Thomas G. Lockhart
Browse files

Add first code to help with outer joins.

Enable by defining
 CFLAGS+= -DENABLE_OUTER_JOINS -DEXEC_MERGEJOINDEBUG
in your Makefile.custom
parent 97287e1d
No related branches found
No related tags found
No related merge requests found
......@@ -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
......@@ -57,23 +57,12 @@
* 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;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment