diff options
Diffstat (limited to 'src/backend/executor/nodeMergejoin.c')
-rw-r--r-- | src/backend/executor/nodeMergejoin.c | 56 |
1 files changed, 40 insertions, 16 deletions
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 62784af304b..8c483bf4747 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -802,10 +802,11 @@ ExecMergeJoin(MergeJoinState *node) } /* - * In a semijoin, we'll consider returning the first - * match, but after that we're done with this outer tuple. + * If we only need to join to the first matching inner + * tuple, then consider returning this one, but after that + * continue with next outer tuple. */ - if (node->js.jointype == JOIN_SEMI) + if (node->js.single_match) node->mj_JoinState = EXEC_MJ_NEXTOUTER; qualResult = (otherqual == NULL || @@ -1050,6 +1051,10 @@ ExecMergeJoin(MergeJoinState *node) * scan position to the first mark, and go join that tuple * (and any following ones) to the new outer. * + * If we were able to determine mark and restore are not + * needed, then we don't have to back up; the current + * inner is already the first possible match. + * * 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 @@ -1062,16 +1067,19 @@ ExecMergeJoin(MergeJoinState *node) * forcing the merge clause to never match, so we never * get here. */ - ExecRestrPos(innerPlan); + if (!node->mj_SkipMarkRestore) + { + 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 */ + /* + * 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; } @@ -1172,7 +1180,8 @@ ExecMergeJoin(MergeJoinState *node) if (compareResult == 0) { - ExecMarkPos(innerPlan); + if (!node->mj_SkipMarkRestore) + ExecMarkPos(innerPlan); MarkInnerTuple(node->mj_InnerTupleSlot, node); @@ -1466,11 +1475,18 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) /* * initialize child nodes * - * inner child must support MARK/RESTORE. + * inner child must support MARK/RESTORE, unless we have detected that we + * don't need that. Note that skip_mark_restore must never be set if + * there are non-mergeclause joinquals, since the logic wouldn't work. */ + Assert(node->join.joinqual == NIL || !node->skip_mark_restore); + mergestate->mj_SkipMarkRestore = node->skip_mark_restore; + outerPlanState(mergestate) = ExecInitNode(outerPlan(node), estate, eflags); innerPlanState(mergestate) = ExecInitNode(innerPlan(node), estate, - eflags | EXEC_FLAG_MARK); + mergestate->mj_SkipMarkRestore ? + eflags : + (eflags | EXEC_FLAG_MARK)); /* * For certain types of inner child nodes, it is advantageous to issue @@ -1483,7 +1499,8 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) * only if eflags doesn't specify REWIND. */ if (IsA(innerPlan(node), Material) && - (eflags & EXEC_FLAG_REWIND) == 0) + (eflags & EXEC_FLAG_REWIND) == 0 && + !mergestate->mj_SkipMarkRestore) mergestate->mj_ExtraMarks = true; else mergestate->mj_ExtraMarks = false; @@ -1497,6 +1514,13 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) ExecSetSlotDescriptor(mergestate->mj_MarkedTupleSlot, ExecGetResultType(innerPlanState(mergestate))); + /* + * detect whether we need only consider the first matching inner tuple + */ + mergestate->js.single_match = (node->join.inner_unique || + node->join.jointype == JOIN_SEMI); + + /* set up null tuples for outer joins, if needed */ switch (node->join.jointype) { case JOIN_INNER: |