📄 nodemergejoin.c
字号:
* Set the next state machine state. The right things will * happen whether we return this join tuple or just fall * through to continue the state machine execution. */ node->mj_JoinState = EXEC_MJ_NEXTINNER; /* * Check the extra qual conditions to see if we actually want * to return this join tuple. If not, can proceed with merge. * We must distinguish the additional joinquals (which must * pass to consider the tuples "matched" for outer-join logic) * from the otherquals (which must pass before we actually * return the tuple). * * We don't bother with a ResetExprContext here, on the * assumption that we just did one while checking the merge * qual. One per tuple should be sufficient. We do have to * set up the econtext links to the tuples for ExecQual to * use. */ outerTupleSlot = node->mj_OuterTupleSlot; econtext->ecxt_outertuple = outerTupleSlot; innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; if (node->js.jointype == JOIN_IN && node->mj_MatchedOuter) qualResult = false; else { qualResult = (joinqual == NIL || ExecQual(joinqual, econtext, false)); MJ_DEBUG_QUAL(joinqual, qualResult); } if (qualResult) { node->mj_MatchedOuter = true; node->mj_MatchedInner = true; qualResult = (otherqual == NIL || ExecQual(otherqual, econtext, false)); MJ_DEBUG_QUAL(otherqual, qualResult); if (qualResult) { /* * qualification succeeded. now form the desired * projection tuple and return the slot containing it. */ TupleTableSlot *result; ExprDoneCond isDone; MJ_printf("ExecMergeJoin: returning tuple\n"); result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { node->js.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); return result; } } } 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. * * Before advancing, we check to see if we must emit an * outer-join fill tuple for this inner tuple. */ case EXEC_MJ_NEXTINNER: MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\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. */ TupleTableSlot *result; node->mj_MatchedInner = true; /* do it only once */ result = MJFillInner(node); if (result) return result; } /* * now we get the next inner tuple, if any. If there's none, * advance to next outer tuple (which may be able to join to * previously marked tuples). */ innerTupleSlot = ExecProcNode(innerPlan); node->mj_InnerTupleSlot = innerTupleSlot; MJ_DEBUG_PROC_NODE(innerTupleSlot); node->mj_MatchedInner = false; if (TupIsNull(innerTupleSlot)) { node->mj_JoinState = EXEC_MJ_NEXTOUTER; break; } /* * Load up the new inner tuple's comparison values. If we * see that it contains a NULL and hence can't match any * outer tuple, we can skip the comparison and assume the * new tuple is greater than current outer. */ if (!MJEvalInnerValues(node, innerTupleSlot)) { node->mj_JoinState = EXEC_MJ_NEXTOUTER; break; } /* * Test the new inner tuple to see if it matches outer. * * If they do match, then we join them and move on to the next * inner tuple (EXEC_MJ_JOINTUPLES). * * If they do not match then advance to next outer tuple. */ compareResult = MJCompare(node); MJ_DEBUG_COMPARE(compareResult); if (compareResult == 0) node->mj_JoinState = EXEC_MJ_JOINTUPLES; else { Assert(compareResult < 0); node->mj_JoinState = EXEC_MJ_NEXTOUTER; } break; /*------------------------------------------- * 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 (or possibly * the end of the inner stream) * so get a new outer tuple and then * proceed to test it against the marked tuple * (EXEC_MJ_TESTOUTER) * * Before advancing, we check to see if we must emit an * outer-join fill tuple for this outer tuple. *------------------------------------------------ */ case EXEC_MJ_NEXTOUTER: MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n"); if (doFillOuter && !node->mj_MatchedOuter) { /* * Generate a fake join tuple with nulls for the inner * tuple, and return it if it passes the non-join quals. */ TupleTableSlot *result; node->mj_MatchedOuter = true; /* do it only once */ result = MJFillOuter(node); if (result) 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 the outer tuple is null then we are done with the join, * unless we have inner tuples we need to null-fill. */ if (TupIsNull(outerTupleSlot)) { MJ_printf("ExecMergeJoin: end of outer subplan\n"); innerTupleSlot = node->mj_InnerTupleSlot; if (doFillInner && !TupIsNull(innerTupleSlot)) { /* * Need to emit right-join tuples for remaining inner * tuples. */ node->mj_JoinState = EXEC_MJ_ENDOUTER; break; } /* Otherwise we're done. */ return NULL; } /* Compute join values and check for unmatchability */ if (MJEvalOuterValues(node)) { /* Go test the new tuple against the marked tuple */ node->mj_JoinState = EXEC_MJ_TESTOUTER; } else { /* Can't match, so fetch next outer tuple */ node->mj_JoinState = EXEC_MJ_NEXTOUTER; } break; /*-------------------------------------------------------- * 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 tuple with the inner tuples. * * This is the case when * outer inner * 4 5 - marked tuple * outer tuple - 5 5 * new outer tuple - 5 5 * 6 8 - inner tuple * 7 12 * * new outer tuple == marked tuple * * If the outer tuple fails the test, then we are done * with the marked tuples, and we have to look for a * match to the current inner tuple. So we will * proceed to skip outer tuples until outer >= inner * (EXEC_MJ_SKIP_TEST). * * This is the case when * * outer inner * 5 5 - marked tuple * outer tuple - 5 5 * new outer tuple - 6 8 - inner tuple * 7 12 * * new outer tuple > marked tuple * *--------------------------------------------------------- */ case EXEC_MJ_TESTOUTER: MJ_printf("ExecMergeJoin: EXEC_MJ_TESTOUTER\n"); /* * Here we must compare the outer tuple with the marked inner * tuple. (We can ignore the result of MJEvalInnerValues, * since the marked inner tuple is certainly matchable.) */ innerTupleSlot = node->mj_MarkedTupleSlot; (void) MJEvalInnerValues(node, innerTupleSlot); compareResult = MJCompare(node); MJ_DEBUG_COMPARE(compareResult); if (compareResult == 0) { /* * the merge clause matched so now we restore the inner * scan position to the first mark, and go join that tuple * (and any following ones) to the new outer. * * NOTE: we do not need to worry about the MatchedInner * state for the rescanned inner tuples. We know all of * them will match this new outer tuple and therefore * won't be emitted as fill tuples. This works *only* * because we require the extra joinquals to be nil when * doing a right or full join --- otherwise some of the * rescanned tuples might fail the extra joinquals. */ ExecRestrPos(innerPlan); /* * ExecRestrPos probably should give us back a new Slot, * but since it doesn't, use the marked slot. (The * previously returned mj_InnerTupleSlot cannot be assumed * to hold the required tuple.) */ node->mj_InnerTupleSlot = innerTupleSlot; /* we need not do MJEvalInnerValues again */ node->mj_JoinState = EXEC_MJ_JOINTUPLES; } else { /* ---------------- * if the new outer tuple didn't match the marked inner * tuple then we have a case like: * * outer inner * 4 4 - marked tuple * new outer - 5 4 * 6 5 - inner tuple * 7 * * which means that all subsequent outer tuples will be * larger than our marked inner tuples. So we need not * revisit any of the marked tuples but can proceed to * look for a match to the current inner. If there's * no more inners, we are done. * ---------------- */ Assert(compareResult > 0); innerTupleSlot = node->mj_InnerTupleSlot; if (TupIsNull(innerTupleSlot)) { if (doFillOuter) { /* * Need to emit left-join tuples for remaining * outer tuples. */ node->mj_JoinState = EXEC_MJ_ENDINNER; break; } /* Otherwise we're done. */ return NULL; } /* reload comparison data for current inner */ if (MJEvalInnerValues(node, innerTupleSlot)) { /* proceed to compare it to the current outer */ node->mj_JoinState = EXEC_MJ_SKIP_TEST; } else { /* * current inner can't possibly match any outer; * better to advance the inner scan than the outer. */ node->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE; } } break; /*---------------------------------------------------------- * EXEC_MJ_SKIP means compare tuples and if they do not * match, skip whichever is lesser. * * For example: * * 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. * * On the other hand: * * 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_SKIP_TEST: MJ_printf("ExecMergeJoin: EXEC_MJ_SKIP_TEST\n"); /* * before we advance, make sure the current tuples do not * satisfy the mergeclauses. If they do, then we update the * marked tuple position and go join them. */ compareResult = MJCompare(node); MJ_DEBUG_COMPARE(compareResult); if (compareResult == 0) { ExecMarkPos(innerPlan); MarkInnerTuple(node->mj_InnerTupleSlot, node); node->mj_JoinState = EXEC_MJ_JOINTUPLES; } else if (compareResult < 0) node->mj_JoinState = EXEC_MJ_SKIPOUTER_ADVANCE; else /* compareResult > 0 */ node->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE; break; /* * Before advancing, we check to see if we must emit an * outer-join fill tuple for this outer tuple. */ case EXEC_MJ_SKIPOUTER_ADVANCE: MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_ADVANCE\n"); if (doFillOuter && !node->mj_MatchedOuter) { /* * Generate a fake join tuple with nulls for the inner * tuple, and return it if it passes the non-join quals. */ TupleTableSlot *result; node->mj_MatchedOuter = true; /* do it only once */ result = MJFillOuter(node); if (result)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -