📄 nodemergejoin.c
字号:
break; /*----------------------------------------------------------- * 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. * * To avoid redundant tests, we divide this into three * sub-states: BEGIN, TEST, ADVANCE. *------------------------------------------------------- */ case EXEC_MJ_SKIPINNER_BEGIN: MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_BEGIN\n"); /* * before we advance, make sure the current tuples do not * satisfy the mergeclauses. If they do, then we update * the marked tuple and go join them. */ ResetExprContext(econtext); outerTupleSlot = node->mj_OuterTupleSlot; econtext->ecxt_outertuple = outerTupleSlot; innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; qualResult = ExecQual(mergeclauses, econtext, false); MJ_DEBUG_QUAL(mergeclauses, qualResult); if (qualResult) { ExecMarkPos(innerPlan); MarkInnerTuple(innerTupleSlot, node); node->mj_JoinState = EXEC_MJ_JOINTUPLES; break; } node->mj_JoinState = EXEC_MJ_SKIPINNER_TEST; break; case EXEC_MJ_SKIPINNER_TEST: MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_TEST\n"); /* * ok, now test the skip qualification */ outerTupleSlot = node->mj_OuterTupleSlot; econtext->ecxt_outertuple = outerTupleSlot; innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; compareResult = MergeCompare(mergeclauses, innerSkipQual, econtext); MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult); /* * compareResult is true as long as we should continue * skipping inner tuples. */ if (compareResult) { node->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE; break; } /* * now check the outer skip qual to see if we should now * skip outer tuples... if we fail the outer skip qual, * then we know we have a new pair of matching tuples. */ compareResult = MergeCompare(mergeclauses, outerSkipQual, econtext); MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult); if (compareResult) node->mj_JoinState = EXEC_MJ_SKIPOUTER_BEGIN; else node->mj_JoinState = EXEC_MJ_JOINMARK; break; /* * Before advancing, we check to see if we must emit an * outer-join fill tuple for this inner tuple. */ case EXEC_MJ_SKIPINNER_ADVANCE: MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_ADVANCE\n"); if (doFillInner && !node->mj_MatchedInner) { /* * Generate a fake join tuple with nulls for the outer * tuple, and return it if it passes the non-join * quals. */ node->mj_MatchedInner = true; /* do it only once */ ResetExprContext(econtext); outerTupleSlot = node->mj_NullOuterTupleSlot; econtext->ecxt_outertuple = outerTupleSlot; innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = 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 fill tuple\n"); result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { node->js.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); return result; } } } /* * now we get the next inner tuple, if any */ innerTupleSlot = ExecProcNode(innerPlan); node->mj_InnerTupleSlot = innerTupleSlot; MJ_DEBUG_PROC_NODE(innerTupleSlot); node->mj_MatchedInner = false; /* * if the inner tuple is null then we are done with the * join, unless we have outer tuples we need to null-fill. */ if (TupIsNull(innerTupleSlot)) { MJ_printf("ExecMergeJoin: end of inner subplan\n"); outerTupleSlot = node->mj_OuterTupleSlot; if (doFillOuter && !TupIsNull(outerTupleSlot)) { /* * Need to emit left-join tuples for remaining * outer tuples. */ node->mj_JoinState = EXEC_MJ_ENDINNER; break; } /* Otherwise we're done. */ return NULL; } /* * otherwise test the new tuple against the skip qual. */ node->mj_JoinState = EXEC_MJ_SKIPINNER_TEST; break; /* * EXEC_MJ_ENDOUTER means we have run out of outer tuples, * but are doing a right/full join and therefore must * null- fill any remaing unmatched inner tuples. */ case EXEC_MJ_ENDOUTER: MJ_printf("ExecMergeJoin: EXEC_MJ_ENDOUTER\n"); Assert(doFillInner); if (!node->mj_MatchedInner) { /* * Generate a fake join tuple with nulls for the outer * tuple, and return it if it passes the non-join * quals. */ node->mj_MatchedInner = true; /* do it only once */ ResetExprContext(econtext); outerTupleSlot = node->mj_NullOuterTupleSlot; econtext->ecxt_outertuple = outerTupleSlot; innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = 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 fill tuple\n"); result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { node->js.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); return result; } } } /* * now we get the next inner tuple, if any */ innerTupleSlot = ExecProcNode(innerPlan); node->mj_InnerTupleSlot = innerTupleSlot; MJ_DEBUG_PROC_NODE(innerTupleSlot); node->mj_MatchedInner = false; if (TupIsNull(innerTupleSlot)) { MJ_printf("ExecMergeJoin: end of inner subplan\n"); return NULL; } /* Else remain in ENDOUTER state and process next tuple. */ break; /* * EXEC_MJ_ENDINNER means we have run out of inner tuples, * but are doing a left/full join and therefore must null- * fill any remaing unmatched outer tuples. */ case EXEC_MJ_ENDINNER: MJ_printf("ExecMergeJoin: EXEC_MJ_ENDINNER\n"); Assert(doFillOuter); if (!node->mj_MatchedOuter) { /* * Generate a fake join tuple with nulls for the inner * tuple, and return it if it passes the non-join * quals. */ node->mj_MatchedOuter = true; /* do it only once */ ResetExprContext(econtext); outerTupleSlot = node->mj_OuterTupleSlot; econtext->ecxt_outertuple = outerTupleSlot; innerTupleSlot = node->mj_NullInnerTupleSlot; econtext->ecxt_innertuple = 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 fill tuple\n"); result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { node->js.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); return result; } } } /* * now we get the next outer tuple, if any */ outerTupleSlot = ExecProcNode(outerPlan); node->mj_OuterTupleSlot = outerTupleSlot; MJ_DEBUG_PROC_NODE(outerTupleSlot); node->mj_MatchedOuter = false; if (TupIsNull(outerTupleSlot)) { MJ_printf("ExecMergeJoin: end of outer subplan\n"); return NULL; } /* Else remain in ENDINNER state and process next tuple. */ break; /* * broken state value? */ default: elog(ERROR, "unrecognized mergejoin state: %d", (int) node->mj_JoinState); } }}/* ---------------------------------------------------------------- * ExecInitMergeJoin * ---------------------------------------------------------------- */MergeJoinState *ExecInitMergeJoin(MergeJoin *node, EState *estate){ MergeJoinState *mergestate; MJ1_printf("ExecInitMergeJoin: %s\n", "initializing node"); /* * create state structure */ mergestate = makeNode(MergeJoinState); mergestate->js.ps.plan = (Plan *) node; mergestate->js.ps.state = estate; /* * Miscellaneous initialization * * create expression context for node */ ExecAssignExprContext(estate, &mergestate->js.ps); /* * initialize child expressions */ mergestate->js.ps.targetlist = (List *) ExecInitExpr((Expr *) node->join.plan.targetlist, (PlanState *) mergestate); mergestate->js.ps.qual = (List *) ExecInitExpr((Expr *) node->join.plan.qual, (PlanState *) mergestate); mergestate->js.jointype = node->join.jointype; mergestate->js.joinqual = (List *) ExecInitExpr((Expr *) node->join.joinqual, (PlanState *) mergestate); mergestate->mergeclauses = (List *) ExecInitExpr((Expr *) node->mergeclauses, (PlanState *) mergestate); /* * initialize child nodes */ outerPlanState(mergestate) = ExecInitNode(outerPlan(node), estate); innerPlanState(mergestate) = ExecInitNode(innerPlan(node), estate);#define MERGEJOIN_NSLOTS 4 /* * tuple table initialization */ ExecInitResultTupleSlot(estate, &mergestate->js.ps); mergestate->mj_MarkedTupleSlot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(mergestate->mj_MarkedTupleSlot, ExecGetResultType(innerPlanState(mergestate)), false); switch (node->join.jointype) { case JOIN_INNER: case JOIN_IN: break; case JOIN_LEFT: mergestate->mj_NullInnerTupleSlot = ExecInitNullTupleSlot(estate, ExecGetResultType(innerPlanState(mergestate))); break; case JOIN_RIGHT: mergestate->mj_NullOuterTupleSlot = ExecInitNullTupleSlot(estate, ExecGetResultType(outerPlanState(mergestate))); /* * Can't handle right or full join with non-nil extra * joinclauses. This should have been caught by planner. */ if (node->join.joinqual != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("RIGHT JOIN is only supported with merge-joinable join conditions"))); break; case JOIN_FULL: mergestate->mj_NullOuterTupleSlot = ExecInitNullTupleSlot(estate, ExecGetResultType(outerPlanState(mergestate))); mergestate->mj_NullInnerTupleSlot = ExecInitNullTupleSlot(estate, ExecGetResultType(innerPlanState(mergestate))); /* * Can't handle right or full join with non-nil extra * joinclauses. */ if (node->join.joinqual != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("FULL JOIN is only supported with merge-joinable join conditions"))); break; default: elog(ERROR, "unrecognized join type: %d", (int) node->join.jointype); } /* * initialize tuple type and projection info */ ExecAssignResultTypeFromTL(&mergestate->js.ps); ExecAssignProjectionInfo(&mergestate->js.ps); /* * form merge skip qualifications */ MJFormSkipQuals(node->mergeclauses, &mergestate->mj_OuterSkipQual, &mergestate->mj_InnerSkipQual, (PlanState *) mergestate); MJ_printf("\nExecInitMergeJoin: OuterSkipQual is "); MJ_nodeDisplay(mergestate->mj_OuterSkipQual); MJ_printf("\nExecInitMergeJoin: InnerSkipQual is "); MJ_nodeDisplay(mergestate->mj_InnerSkipQual); MJ_printf("\n"); /* * initialize join state */ mergestate->mj_JoinState = EXEC_MJ_INITIALIZE; mergestate->js.ps.ps_TupFromTlist = false; mergestate->mj_MatchedOuter = false; mergestate->mj_MatchedInner = false; mergestate->mj_OuterTupleSlot = NULL; mergestate->mj_InnerTupleSlot = NULL; /* * initialization successful */ MJ1_printf("ExecInitMergeJoin: %s\n", "node initialized"); return mergestate;}intExecCountSlotsMergeJoin(MergeJoin *node){ return ExecCountSlotsNode(outerPlan((Plan *) node)) + ExecCountSlotsNode(innerPlan((Plan *) node)) + MERGEJOIN_NSLOTS;}/* ---------------------------------------------------------------- * ExecEndMergeJoin * * old comments * frees storage allocated through C routines. * ---------------------------------------------------------------- */voidExecEndMergeJoin(MergeJoinState *node){ MJ1_printf("ExecEndMergeJoin: %s\n", "ending node processing"); /* * Free the exprcontext */ ExecFreeExprContext(&node->js.ps); /* * clean out the tuple table */ ExecClearTuple(node->js.ps.ps_ResultTupleSlot); ExecClearTuple(node->mj_MarkedTupleSlot); /* * shut down the subplans */ ExecEndNode(innerPlanState(node)); ExecEndNode(outerPlanState(node)); MJ1_printf("ExecEndMergeJoin: %s\n", "node processing ended");}voidExecReScanMergeJoin(MergeJoinState *node, ExprContext *exprCtxt){ ExecClearTuple(node->mj_MarkedTupleSlot); node->mj_JoinState = EXEC_MJ_INITIALIZE; node->js.ps.ps_TupFromTlist = false; node->mj_MatchedOuter = false; node->mj_MatchedInner = false; node->mj_OuterTupleSlot = NULL; node->mj_InnerTupleSlot = NULL; /* * if chgParam of subnodes is not null then plans will be re-scanned * by first ExecProcNode. */ if (((PlanState *) node)->lefttree->chgParam == NULL) ExecReScan(((PlanState *) node)->lefttree, exprCtxt); if (((PlanState *) node)->righttree->chgParam == NULL) ExecReScan(((PlanState *) node)->righttree, exprCtxt);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -