/* ---------------------------------------------------------------- * ExecNestLoop(node) * * old comments * Returns the tuple joined from inner and outer tuples which * satisfies the qualification clause. * * It scans the inner relation to join with current outer tuple. * * If none is found, next tuple form the outer relation is retrieved * and the inner relation is scanned from the beginning again to join * with the outer tuple. * * Nil is returned if all the remaining outer tuples are tried and * all fail to join with the inner tuples. * * Nil is also returned if there is no tuple from inner realtion. * * Conditions: * -- outerTuple contains current tuple from outer relation and * the right son(inner realtion) maintains "cursor" at the tuple * returned previously. * This is achieved by maintaining a scan position on the outer * relation. * * Initial States: * -- the outer child and the inner child * are prepared to return the first tuple. * ---------------------------------------------------------------- */ TupleTableSlot * ExecNestLoop(NestLoop *node, Plan* parent) { NestLoopState *nlstate; Plan *innerPlan; Plan *outerPlan; bool needNewOuterTuple; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; List *qual; bool qualResult; ExprContext *econtext; /* ---------------- * get information from the node * ---------------- */ ENL1_printf("getting info from node"); nlstate = node->nlstate; qual = node->join.qual; outerPlan = outerPlan(&node->join); innerPlan = innerPlan(&node->join); /* ---------------- * initialize expression context * ---------------- */ econtext = nlstate->jstate.cs_ExprContext; /* ---------------- * get the current outer tuple * ---------------- */ outerTupleSlot = nlstate->jstate.cs_OuterTupleSlot; econtext->ecxt_outertuple = outerTupleSlot; /* ---------------- * Ok, everything is setup for the join so now loop until * we return a qualifying join tuple.. * ---------------- */ if (nlstate->jstate.cs_TupFromTlist) { TupleTableSlot *result; bool isDone; result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone); if (!isDone) return result; } ENL1_printf("entering main loop"); for(;;) { /* ---------------- * The essential idea now is to get the next inner tuple * and join it with the current outer tuple. * ---------------- */ needNewOuterTuple = false; /* ---------------- * If outer tuple is not null then that means * we are in the middle of a scan and we should * restore our previously saved scan position. * ---------------- */ if (! TupIsNull(outerTupleSlot)) { ENL1_printf("have outer tuple, restoring outer plan"); ExecRestrPos(outerPlan); } else { ENL1_printf("outer tuple is nil, need new outer tuple"); needNewOuterTuple = true; } /* ---------------- * if we have an outerTuple, try to get the next inner tuple. * ---------------- */ if (!needNewOuterTuple) { ENL1_printf("getting new inner tuple"); innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node); econtext->ecxt_innertuple = innerTupleSlot; if (TupIsNull(innerTupleSlot)) { ENL1_printf("no inner tuple, need new outer tuple"); needNewOuterTuple = true; } } /* ---------------- * loop until we have a new outer tuple and a new * inner tuple. * ---------------- */ while (needNewOuterTuple) { /* ---------------- * now try to get the next outer tuple * ---------------- */ ENL1_printf("getting new outer tuple"); outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node); econtext->ecxt_outertuple = outerTupleSlot; /* ---------------- * if there are no more outer tuples, then the join * is complete.. * ---------------- */ if (TupIsNull(outerTupleSlot)) { ENL1_printf("no outer tuple, ending join"); return NULL; } /* ---------------- * we have a new outer tuple so we mark our position * in the outer scan and save the outer tuple in the * NestLoop state * ---------------- */ ENL1_printf("saving new outer tuple information"); ExecMarkPos(outerPlan); nlstate->jstate.cs_OuterTupleSlot = outerTupleSlot; /* ---------------- * now rescan the inner plan and get a new inner tuple * ---------------- */ ENL1_printf("rescanning inner plan"); /* * The scan key of the inner plan might depend on the current * outer tuple (e.g. in index scans), that's why we pass our * expr context. */ ExecReScan(innerPlan, econtext, parent); ENL1_printf("getting new inner tuple"); innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node); econtext->ecxt_innertuple = innerTupleSlot; if (TupIsNull(innerTupleSlot)) { ENL1_printf("couldn't get inner tuple - need new outer tuple"); } else { ENL1_printf("got inner and outer tuples"); needNewOuterTuple = false; } } /* while (needNewOuterTuple) */ /* ---------------- * at this point we have a new pair of inner and outer * tuples so we test the inner and outer tuples to see * if they satisify the node's qualification. * ---------------- */ ENL1_printf("testing qualification"); qualResult = ExecQual((List*)qual, econtext); if (qualResult) { /* ---------------- * qualification was satisified so we project and * return the slot containing the result tuple * using ExecProject(). * ---------------- */ ProjectionInfo *projInfo; TupleTableSlot *result; bool isDone; ENL1_printf("qualification succeeded, projecting tuple"); projInfo = nlstate->jstate.cs_ProjInfo; result = ExecProject(projInfo, &isDone); nlstate->jstate.cs_TupFromTlist = !isDone; return result; } /* ---------------- * qualification failed so we have to try again.. * ---------------- */ ENL1_printf("qualification failed, looping"); } }
/* ---------------------------------------------------------------- * ExecNestLoop(node) * * old comments * Returns the tuple joined from inner and outer tuples which * satisfies the qualification clause. * * It scans the inner relation to join with current outer tuple. * * If none is found, next tuple from the outer relation is retrieved * and the inner relation is scanned from the beginning again to join * with the outer tuple. * * NULL is returned if all the remaining outer tuples are tried and * all fail to join with the inner tuples. * * NULL is also returned if there is no tuple from inner relation. * * Conditions: * -- outerTuple contains current tuple from outer relation and * the right son(inner relation) maintains "cursor" at the tuple * returned previously. * This is achieved by maintaining a scan position on the outer * relation. * * Initial States: * -- the outer child and the inner child * are prepared to return the first tuple. * ---------------------------------------------------------------- */ TupleTableSlot * ExecNestLoop(NestLoopState *node) { PlanState *innerPlan; PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; List *joinqual; List *otherqual; ExprContext *econtext; /* * get information from the node */ ENL1_printf("getting info from node"); joinqual = node->js.joinqual; otherqual = node->js.ps.qual; outerPlan = outerPlanState(node); innerPlan = innerPlanState(node); econtext = node->js.ps.ps_ExprContext; /* * get the current outer tuple */ outerTupleSlot = node->js.ps.ps_OuterTupleSlot; econtext->ecxt_outertuple = outerTupleSlot; /* * 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; } /* * If we're doing an IN join, we want to return at most one row per outer * tuple; so we can stop scanning the inner scan if we matched on the * previous try. */ if (node->js.jointype == JOIN_IN && node->nl_MatchedOuter) node->nl_NeedNewOuter = true; /* * 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 for the join so now loop until we return a * qualifying join tuple. */ ENL1_printf("entering main loop"); for (;;) { /* * If we don't have an outer tuple, get the next one and reset the * inner scan. */ if (node->nl_NeedNewOuter) { ENL1_printf("getting new outer tuple"); outerTupleSlot = ExecProcNode(outerPlan); /* * if there are no more outer tuples, then the join is complete.. */ if (TupIsNull(outerTupleSlot)) { ENL1_printf("no outer tuple, ending join"); return NULL; } ENL1_printf("saving new outer tuple information"); node->js.ps.ps_OuterTupleSlot = outerTupleSlot; econtext->ecxt_outertuple = outerTupleSlot; node->nl_NeedNewOuter = false; node->nl_MatchedOuter = false; /* * now rescan the inner plan */ ENL1_printf("rescanning inner plan"); /* * The scan key of the inner plan might depend on the current * outer tuple (e.g. in index scans), that's why we pass our expr * context. */ ExecReScan(innerPlan, econtext); } /* * we have an outerTuple, try to get the next inner tuple. */ ENL1_printf("getting new inner tuple"); innerTupleSlot = ExecProcNode(innerPlan); econtext->ecxt_innertuple = innerTupleSlot; if (TupIsNull(innerTupleSlot)) { ENL1_printf("no inner tuple, need new outer tuple"); node->nl_NeedNewOuter = true; if (!node->nl_MatchedOuter && node->js.jointype == JOIN_LEFT) { /* * We are doing an outer join and there were no join matches * for this outer tuple. Generate a fake join tuple with * nulls for the inner tuple, and return it if it passes the * non-join quals. */ econtext->ecxt_innertuple = node->nl_NullInnerTupleSlot; ENL1_printf("testing qualification for outer-join tuple"); if (ExecQual(otherqual, econtext, false)) { /* * qualification was satisfied so we project and return * the slot containing the result tuple using * ExecProject(). */ TupleTableSlot *result; ExprDoneCond isDone; ENL1_printf("qualification succeeded, projecting tuple"); result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { node->js.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); return result; } } } /* * Otherwise just return to top of loop for a new outer tuple. */ continue; } /* * at this point we have a new pair of inner and outer tuples so we * test the inner and outer tuples to see if they satisfy the node's * qualification. * * Only the joinquals determine MatchedOuter status, but all quals * must pass to actually return the tuple. */ ENL1_printf("testing qualification"); if (ExecQual(joinqual, econtext, false)) { node->nl_MatchedOuter = true; if (otherqual == NIL || ExecQual(otherqual, econtext, false)) { /* * qualification was satisfied so we project and return the * slot containing the result tuple using ExecProject(). */ TupleTableSlot *result; ExprDoneCond isDone; ENL1_printf("qualification succeeded, projecting tuple"); result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { node->js.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); return result; } } /* If we didn't return a tuple, may need to set NeedNewOuter */ if (node->js.jointype == JOIN_IN) node->nl_NeedNewOuter = true; } /* * Tuple fails qual, so free per-tuple memory and try again. */ ResetExprContext(econtext); ENL1_printf("qualification failed, looping"); } }