Add first code to help with outer joins.

Enable by defining
 CFLAGS+= -DENABLE_OUTER_JOINS -DEXEC_MERGEJOINDEBUG
in your Makefile.custom
This commit is contained in:
Thomas G. Lockhart 1999-02-23 07:35:09 +00:00
parent 97287e1d13
commit 6d73a8c0cb

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* Essential operation of the merge join algorithm is as follows: * 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 { - * Join { -
* get initial outer and inner tuples INITIALIZE * get initial outer and inner tuples INITIALIZE
@ -57,23 +57,12 @@
* Skip Outer SKIPOUTER * 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 * of a state machine. At each state, we do something and then
* proceed to another state. This state is stored in the node's * proceed to another state. This state is stored in the node's
* execution state information and is preserved across calls to * execution state information and is preserved across calls to
* ExecMergeJoin. -cim 10/31/89 * 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" #include "postgres.h"
@ -85,6 +74,7 @@
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/psort.h" #include "utils/psort.h"
static bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext); static bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext);
#define MarkInnerTuple(innerTupleSlot, mergestate) \ #define MarkInnerTuple(innerTupleSlot, mergestate) \
@ -95,6 +85,7 @@ static bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
true) \ true) \
) )
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* MJFormOSortopI * MJFormOSortopI
* *
@ -296,6 +287,9 @@ MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
#ifdef EXEC_MERGEJOINDEBUG #ifdef EXEC_MERGEJOINDEBUG
void
ExecMergeTupleDumpInner(ExprContext *econtext);
void void
ExecMergeTupleDumpInner(ExprContext *econtext) ExecMergeTupleDumpInner(ExprContext *econtext)
{ {
@ -306,10 +300,13 @@ ExecMergeTupleDumpInner(ExprContext *econtext)
if (TupIsNull(innerSlot)) if (TupIsNull(innerSlot))
printf("(nil)\n"); printf("(nil)\n");
else else
debugtup(innerSlot->val, MJ_debugtup(innerSlot->val,
innerSlot->ttc_tupleDescriptor); innerSlot->ttc_tupleDescriptor);
} }
void
ExecMergeTupleDumpOuter(ExprContext *econtext);
void void
ExecMergeTupleDumpOuter(ExprContext *econtext) ExecMergeTupleDumpOuter(ExprContext *econtext)
{ {
@ -320,10 +317,14 @@ ExecMergeTupleDumpOuter(ExprContext *econtext)
if (TupIsNull(outerSlot)) if (TupIsNull(outerSlot))
printf("(nil)\n"); printf("(nil)\n");
else else
debugtup(outerSlot->val, MJ_debugtup(outerSlot->val,
outerSlot->ttc_tupleDescriptor); outerSlot->ttc_tupleDescriptor);
} }
void
ExecMergeTupleDumpMarked(ExprContext *econtext,
MergeJoinState *mergestate);
void void
ExecMergeTupleDumpMarked(ExprContext *econtext, ExecMergeTupleDumpMarked(ExprContext *econtext,
MergeJoinState *mergestate) MergeJoinState *mergestate)
@ -336,10 +337,13 @@ ExecMergeTupleDumpMarked(ExprContext *econtext,
if (TupIsNull(markedSlot)) if (TupIsNull(markedSlot))
printf("(nil)\n"); printf("(nil)\n");
else else
debugtup(markedSlot->val, MJ_debugtup(markedSlot->val,
markedSlot->ttc_tupleDescriptor); markedSlot->ttc_tupleDescriptor);
} }
void
ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate);
void void
ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate) ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate)
{ {
@ -351,7 +355,6 @@ ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate)
printf("******** \n"); printf("******** \n");
} }
#endif #endif
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
@ -423,6 +426,14 @@ ExecMergeJoin(MergeJoin *node)
ExprContext *econtext; 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 * get information from node
* ---------------- * ----------------
@ -475,14 +486,12 @@ ExecMergeJoin(MergeJoin *node)
switch (mergestate->mj_JoinState) switch (mergestate->mj_JoinState)
{ {
/* --------------------------------------------------- /*********************************
* EXEC_MJ_INITIALIZE * EXEC_MJ_INITIALIZE means that this is the first time
* means that this is the first time ExecMergeJoin() has * ExecMergeJoin() has been called and so we have to initialize
* been called and so we have to initialize the inner, * the inner, outer and marked tuples as well as various stuff
* outer and marked tuples as well as various stuff in the * in the expression context.
* expression context. *********************************/
* ---------------------------------------------------
*/
case EXEC_MJ_INITIALIZE: case EXEC_MJ_INITIALIZE:
MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE\n"); MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE\n");
/* ---------------- /* ----------------
@ -522,13 +531,11 @@ ExecMergeJoin(MergeJoin *node)
mergestate->mj_JoinState = EXEC_MJ_SKIPINNER; mergestate->mj_JoinState = EXEC_MJ_SKIPINNER;
break; break;
/* --------------------------------------------------- /*********************************
* EXEC_MJ_JOINMARK * EXEC_MJ_JOINMARK means we have just found a new outer tuple
* means we have just found a new outer tuple and a possible * and a possible matching inner tuple. This is the case after
* matching inner tuple. This is the case after the * the INITIALIZE, SKIPOUTER or SKIPINNER states.
* INITIALIZE, SKIPOUTER or SKIPINNER states. *********************************/
* ----------------------------------------------------
*/
case EXEC_MJ_JOINMARK: case EXEC_MJ_JOINMARK:
MJ_printf("ExecMergeJoin: EXEC_MJ_JOINMARK\n"); MJ_printf("ExecMergeJoin: EXEC_MJ_JOINMARK\n");
ExecMarkPos(innerPlan); ExecMarkPos(innerPlan);
@ -538,17 +545,15 @@ ExecMergeJoin(MergeJoin *node)
mergestate->mj_JoinState = EXEC_MJ_JOINTEST; mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
break; break;
/* ---------------------------------------------------- /*********************************
* EXEC_MJ_JOINTEST * EXEC_MJ_JOINTEST means we have two tuples which might
* means we have two tuples which might satisify the merge * satisfy the merge clause, so we test them.
* 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). * 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: case EXEC_MJ_JOINTEST:
MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n"); MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n");
@ -561,13 +566,11 @@ ExecMergeJoin(MergeJoin *node)
mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER; mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER;
break; break;
/* ---------------------------------------------------- /*********************************
* EXEC_MJ_JOINTUPLES * EXEC_MJ_JOINTUPLES means we have two tuples which
* means we have two tuples which satisified the merge * satisified the merge clause so we join them and then
* clause so we join them and then proceed to get the next * proceed to get the next inner tuple (EXEC_NEXT_INNER).
* inner tuple (EXEC_NEXT_INNER). *********************************/
* ----------------------------------------------------
*/
case EXEC_MJ_JOINTUPLES: case EXEC_MJ_JOINTUPLES:
MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n"); MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");
mergestate->mj_JoinState = EXEC_MJ_NEXTINNER; mergestate->mj_JoinState = EXEC_MJ_NEXTINNER;
@ -596,13 +599,11 @@ ExecMergeJoin(MergeJoin *node)
} }
break; break;
/* -------------------------------------------------- /*********************************
* EXEC_MJ_NEXTINNER * EXEC_MJ_NEXTINNER means advance the inner scan to
* means advance the inner scan to the next tuple. If the * the next tuple. If the tuple is not nil, we then
* tuple is not nil, we then proceed to test it against * proceed to test it against the join qualification.
* the join qualification. *********************************/
* ----------------------------------------------------
*/
case EXEC_MJ_NEXTINNER: case EXEC_MJ_NEXTINNER:
MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\n"); MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\n");
@ -620,21 +621,21 @@ ExecMergeJoin(MergeJoin *node)
mergestate->mj_JoinState = EXEC_MJ_JOINTEST; mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
break; break;
/* -------------------------------------------------- /*********************************
* EXEC_MJ_NEXTOUTER * EXEC_MJ_NEXTOUTER means
* means *
* outer inner * outer inner
* outer tuple - 5 5 - marked tuple * outer tuple - 5 5 - marked tuple
* 5 5 * 5 5
* 6 6 - inner tuple * 6 6 - inner tuple
* 7 7 * 7 7
* *
* we know we just bumped into the first inner tuple > * we know we just bumped into the
* current outer tuple so get a new outer tuple and then * first inner tuple > current outer tuple
* so get a new outer tuple and then
* proceed to test it against the marked tuple * proceed to test it against the marked tuple
* (EXEC_MJ_TESTOUTER) * (EXEC_MJ_TESTOUTER)
* ------------------------------------------------- *********************************/
*/
case EXEC_MJ_NEXTOUTER: case EXEC_MJ_NEXTOUTER:
MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n"); MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n");
@ -656,13 +657,12 @@ ExecMergeJoin(MergeJoin *node)
mergestate->mj_JoinState = EXEC_MJ_TESTOUTER; mergestate->mj_JoinState = EXEC_MJ_TESTOUTER;
break; break;
/* --------------------------------------------------- /*********************************
* EXEC_MJ_TESTOUTER * EXEC_MJ_TESTOUTER If the new outer tuple and the marked
* If the new outer tuple and the marked tuple satisify the * tuple satisfy the merge clause then we know we have
* merge clause then we know we have duplicates in the * duplicates in the outer scan so we have to restore the
* outer scan so we have to restore the inner scan to the * inner scan to the marked tuple and proceed to join the
* marked tuple and proceed to join the new outer tuples * new outer tuples with the inner tuples (EXEC_MJ_JOINTEST)
* with the inner tuples (EXEC_MJ_JOINTEST)
* *
* This is the case when * This is the case when
* outer inner * outer inner
@ -689,8 +689,7 @@ ExecMergeJoin(MergeJoin *node)
* *
* new outer tuple > marked tuple * new outer tuple > marked tuple
* *
* ----------------------------------------------------------- *********************************/
*/
case EXEC_MJ_TESTOUTER: case EXEC_MJ_TESTOUTER:
MJ_printf("ExecMergeJoin: EXEC_MJ_TESTOUTER\n"); MJ_printf("ExecMergeJoin: EXEC_MJ_TESTOUTER\n");
@ -744,7 +743,15 @@ ExecMergeJoin(MergeJoin *node)
*/ */
if (TupIsNull(innerTupleSlot)) 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; return NULL;
} }
@ -753,10 +760,9 @@ ExecMergeJoin(MergeJoin *node)
} }
break; break;
/* -------------------------------------------------- /*********************************
* EXEC_MJ_SKIPOUTER * EXEC_MJ_SKIPOUTER means skip over tuples in the outer plan
* means skip over tuples in the outer plan until we find * until we find an outer tuple > current inner tuple.
* an outer tuple > current inner tuple.
* *
* For example: * For example:
* *
@ -767,15 +773,14 @@ ExecMergeJoin(MergeJoin *node)
* 7 12 * 7 12
* 8 14 * 8 14
* *
* We have to advance the outer scan until we find the outer * we have to advance the outer scan
* 8. * until we find the outer 8.
* ------------------------------------------------ *********************************/
*/
case EXEC_MJ_SKIPOUTER: case EXEC_MJ_SKIPOUTER:
MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER\n"); MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER\n");
/* ---------------- /* ----------------
* before we advance, make sure the current tuples * 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. * we update the marked tuple and go join them.
* ---------------- * ----------------
*/ */
@ -809,6 +814,17 @@ ExecMergeJoin(MergeJoin *node)
*/ */
if (compareResult) 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); outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
MJ_DEBUG_PROC_NODE(outerTupleSlot); MJ_DEBUG_PROC_NODE(outerTupleSlot);
@ -851,12 +867,12 @@ ExecMergeJoin(MergeJoin *node)
mergestate->mj_JoinState = EXEC_MJ_JOINMARK; mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
break; break;
/* ------------------------------------------------ /*********************************
* EXEC_MJ_SKIPINNER * EXEC_MJ_SKIPINNER means skip over tuples in the inner plan
* means skip over tuples in the inner plan until we find * until we find an inner tuple > current outer tuple.
* an inner tuple > current outer tuple.
* *
* For example: * For example:
*
* outer inner * outer inner
* 5 5 * 5 5
* 5 5 * 5 5
@ -864,15 +880,15 @@ ExecMergeJoin(MergeJoin *node)
* 14 10 * 14 10
* 17 12 * 17 12
* *
* We have to advance the inner scan until we find the inner * we have to advance the inner scan
* 12. * until we find the inner 12.
* --------------------------------------------------- *
*/ *********************************/
case EXEC_MJ_SKIPINNER: case EXEC_MJ_SKIPINNER:
MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER\n"); MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER\n");
/* ---------------- /* ----------------
* before we advance, make sure the current tuples * 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. * we update the marked tuple and go join them.
* ---------------- * ----------------
*/ */
@ -906,6 +922,18 @@ ExecMergeJoin(MergeJoin *node)
*/ */
if (compareResult) 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 * now try and get a new inner tuple
* ---------------- * ----------------
@ -937,7 +965,7 @@ ExecMergeJoin(MergeJoin *node)
* This means the join should end. * This means the join should end.
* ---------------- * ----------------
*/ */
MJ_printf("ExecMergeJoin: **** wierd case 2 ****\n"); MJ_printf("ExecMergeJoin: **** weird case 2 ****\n");
return NULL; return NULL;
} }
@ -968,10 +996,109 @@ ExecMergeJoin(MergeJoin *node)
break; break;
/* #ifdef ENABLE_OUTER_JOINS
* If we get here it means our code is messed up and so we /*********************************
* just end the join prematurely. * 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: default:
elog(NOTICE, "ExecMergeJoin: invalid join state. aborting"); elog(NOTICE, "ExecMergeJoin: invalid join state. aborting");
return NULL; return NULL;