📄 nodemergejoin.c
字号:
/* NULL > non-NULL */ result = 1; break; } if (clause->risnull) { /* non-NULL < NULL */ result = -1; break; } if (clause->cmpstrategy == MERGEFUNC_LT) { InitFunctionCallInfoData(fcinfo, &(clause->eqfinfo), 2, NULL, NULL); fcinfo.arg[0] = clause->ldatum; fcinfo.arg[1] = clause->rdatum; fcinfo.argnull[0] = false; fcinfo.argnull[1] = false; fresult = FunctionCallInvoke(&fcinfo); if (fcinfo.isnull) { nulleqnull = true; continue; } else if (DatumGetBool(fresult)) { /* equal */ continue; } InitFunctionCallInfoData(fcinfo, &(clause->cmpfinfo), 2, NULL, NULL); fcinfo.arg[0] = clause->ldatum; fcinfo.arg[1] = clause->rdatum; fcinfo.argnull[0] = false; fcinfo.argnull[1] = false; fresult = FunctionCallInvoke(&fcinfo); if (fcinfo.isnull) { nulleqnull = true; continue; } else if (DatumGetBool(fresult)) { /* less than */ result = -1; break; } else { /* greater than */ result = 1; break; } } else /* must be MERGEFUNC_CMP */ { InitFunctionCallInfoData(fcinfo, &(clause->cmpfinfo), 2, NULL, NULL); fcinfo.arg[0] = clause->ldatum; fcinfo.arg[1] = clause->rdatum; fcinfo.argnull[0] = false; fcinfo.argnull[1] = false; fresult = FunctionCallInvoke(&fcinfo); if (fcinfo.isnull) { nulleqnull = true; continue; } else if (DatumGetInt32(fresult) == 0) { /* equal */ continue; } else if (DatumGetInt32(fresult) < 0) { /* less than */ result = -1; break; } else { /* greater than */ result = 1; break; } } } /* * If we had any null comparison results or NULL-vs-NULL inputs, we do not * want to report that the tuples are equal. Instead, if result is still * 0, change it to +1. This will result in advancing the inner side of * the join. */ if (nulleqnull && result == 0) result = 1; MemoryContextSwitchTo(oldContext); return result;}/* * Generate a fake join tuple with nulls for the inner tuple, * and return it if it passes the non-join quals. */static TupleTableSlot *MJFillOuter(MergeJoinState *node){ ExprContext *econtext = node->js.ps.ps_ExprContext; List *otherqual = node->js.ps.qual; ResetExprContext(econtext); econtext->ecxt_outertuple = node->mj_OuterTupleSlot; econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot; if (ExecQual(otherqual, econtext, false)) { /* * qualification succeeded. now form the desired projection tuple and * return the slot containing it. */ TupleTableSlot *result; ExprDoneCond isDone; MJ_printf("ExecMergeJoin: returning outer fill tuple\n"); result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { node->js.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); return result; } } return NULL;}/* * Generate a fake join tuple with nulls for the outer tuple, * and return it if it passes the non-join quals. */static TupleTableSlot *MJFillInner(MergeJoinState *node){ ExprContext *econtext = node->js.ps.ps_ExprContext; List *otherqual = node->js.ps.qual; ResetExprContext(econtext); econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot; econtext->ecxt_innertuple = node->mj_InnerTupleSlot; if (ExecQual(otherqual, econtext, false)) { /* * qualification succeeded. now form the desired projection tuple and * return the slot containing it. */ TupleTableSlot *result; ExprDoneCond isDone; MJ_printf("ExecMergeJoin: returning inner fill tuple\n"); result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { node->js.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); return result; } } return NULL;}/* ---------------------------------------------------------------- * ExecMergeTupleDump * * This function is called through the MJ_dump() macro * when EXEC_MERGEJOINDEBUG is defined * ---------------------------------------------------------------- */#ifdef EXEC_MERGEJOINDEBUGstatic voidExecMergeTupleDumpOuter(MergeJoinState *mergestate){ TupleTableSlot *outerSlot = mergestate->mj_OuterTupleSlot; printf("==== outer tuple ====\n"); if (TupIsNull(outerSlot)) printf("(nil)\n"); else MJ_debugtup(outerSlot);}static voidExecMergeTupleDumpInner(MergeJoinState *mergestate){ TupleTableSlot *innerSlot = mergestate->mj_InnerTupleSlot; printf("==== inner tuple ====\n"); if (TupIsNull(innerSlot)) printf("(nil)\n"); else MJ_debugtup(innerSlot);}static voidExecMergeTupleDumpMarked(MergeJoinState *mergestate){ TupleTableSlot *markedSlot = mergestate->mj_MarkedTupleSlot; printf("==== marked tuple ====\n"); if (TupIsNull(markedSlot)) printf("(nil)\n"); else MJ_debugtup(markedSlot);}static voidExecMergeTupleDump(MergeJoinState *mergestate){ printf("******** ExecMergeTupleDump ********\n"); ExecMergeTupleDumpOuter(mergestate); ExecMergeTupleDumpInner(mergestate); ExecMergeTupleDumpMarked(mergestate); printf("******** \n");}#endif/* ---------------------------------------------------------------- * ExecMergeJoin * ---------------------------------------------------------------- */TupleTableSlot *ExecMergeJoin(MergeJoinState *node){ EState *estate; List *joinqual; List *otherqual; bool qualResult; int compareResult; PlanState *innerPlan; TupleTableSlot *innerTupleSlot; PlanState *outerPlan; TupleTableSlot *outerTupleSlot; ExprContext *econtext; bool doFillOuter; bool doFillInner; /* * get information from node */ estate = node->js.ps.state; innerPlan = innerPlanState(node); outerPlan = outerPlanState(node); econtext = node->js.ps.ps_ExprContext; joinqual = node->js.joinqual; otherqual = node->js.ps.qual; doFillOuter = node->mj_FillOuter; doFillInner = node->mj_FillInner; /* * Check to see if we're still projecting out tuples from a previous join * tuple (because there is a function-returning-set in the projection * expressions). If so, try to project another one. */ if (node->js.ps.ps_TupFromTlist) { TupleTableSlot *result; ExprDoneCond isDone; result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); if (isDone == ExprMultipleResult) return result; /* Done with that source tuple... */ node->js.ps.ps_TupFromTlist = false; } /* * Reset per-tuple memory context to free any expression evaluation * storage allocated in the previous tuple cycle. Note this can't happen * until we're done projecting out tuples from a join tuple. */ ResetExprContext(econtext); /* * ok, everything is setup.. let's go to work */ for (;;) { MJ_dump(node); /* * get the current state of the join and do things accordingly. */ switch (node->mj_JoinState) { /* * EXEC_MJ_INITIALIZE_OUTER means that this is the first time * ExecMergeJoin() has been called and so we have to fetch the * first matchable tuple for both outer and inner subplans. We * do the outer side in INITIALIZE_OUTER state, then advance * to INITIALIZE_INNER state for the inner subplan. */ case EXEC_MJ_INITIALIZE_OUTER: MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE_OUTER\n"); outerTupleSlot = ExecProcNode(outerPlan); node->mj_OuterTupleSlot = outerTupleSlot; if (TupIsNull(outerTupleSlot)) { MJ_printf("ExecMergeJoin: nothing in outer subplan\n"); if (doFillInner) { /* * Need to emit right-join tuples for remaining inner * tuples. We set MatchedInner = true to force the * ENDOUTER state to advance inner. */ node->mj_JoinState = EXEC_MJ_ENDOUTER; node->mj_MatchedInner = true; break; } /* Otherwise we're done. */ return NULL; } /* Compute join values and check for unmatchability */ if (MJEvalOuterValues(node)) { /* OK to go get the first inner tuple */ node->mj_JoinState = EXEC_MJ_INITIALIZE_INNER; } else { /* Stay in same state to fetch next outer tuple */ if (doFillOuter) { /* * Generate a fake join tuple with nulls for the inner * tuple, and return it if it passes the non-join * quals. */ TupleTableSlot *result; result = MJFillOuter(node); if (result) return result; } } break; case EXEC_MJ_INITIALIZE_INNER: MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE_INNER\n"); innerTupleSlot = ExecProcNode(innerPlan); node->mj_InnerTupleSlot = innerTupleSlot; if (TupIsNull(innerTupleSlot)) { MJ_printf("ExecMergeJoin: nothing in inner subplan\n"); if (doFillOuter) { /* * Need to emit left-join tuples for all outer tuples, * including the one we just fetched. We set * MatchedOuter = false to force the ENDINNER state to * emit first tuple before advancing outer. */ node->mj_JoinState = EXEC_MJ_ENDINNER; node->mj_MatchedOuter = false; break; } /* Otherwise we're done. */ return NULL; } /* Compute join values and check for unmatchability */ if (MJEvalInnerValues(node, innerTupleSlot)) { /* * OK, we have the initial tuples. Begin by skipping * non-matching tuples. */ node->mj_JoinState = EXEC_MJ_SKIP_TEST; } else { /* Stay in same state to fetch next inner tuple */ if (doFillInner) { /* * Generate a fake join tuple with nulls for the outer * tuple, and return it if it passes the non-join * quals. */ TupleTableSlot *result; result = MJFillInner(node); if (result) return result; } } break; /* * EXEC_MJ_JOINTUPLES means we have two tuples which satisfied * the merge clause so we join them and then proceed to get * the next inner tuple (EXEC_MJ_NEXTINNER). */ case EXEC_MJ_JOINTUPLES: MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n"); /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -