#include "postgres.h"
#include "executor/execdebug.h"
#include "executor/nodeNestloop.h"
#include "utils/memutils.h"
Go to the source code of this file.
Functions | |
TupleTableSlot * | ExecNestLoop (NestLoopState *node) |
NestLoopState * | ExecInitNestLoop (NestLoop *node, EState *estate, int eflags) |
void | ExecEndNestLoop (NestLoopState *node) |
void | ExecReScanNestLoop (NestLoopState *node) |
void ExecEndNestLoop | ( | NestLoopState * | node | ) |
Definition at line 397 of file nodeNestloop.c.
References ExecClearTuple(), ExecEndNode(), ExecFreeExprContext(), innerPlanState, NestLoopState::js, NL1_printf, outerPlanState, JoinState::ps, and PlanState::ps_ResultTupleSlot.
Referenced by ExecEndNode().
{ NL1_printf("ExecEndNestLoop: %s\n", "ending node processing"); /* * Free the exprcontext */ ExecFreeExprContext(&node->js.ps); /* * clean out the tuple table */ ExecClearTuple(node->js.ps.ps_ResultTupleSlot); /* * close down subplans */ ExecEndNode(outerPlanState(node)); ExecEndNode(innerPlanState(node)); NL1_printf("ExecEndNestLoop: %s\n", "node processing ended"); }
NestLoopState* ExecInitNestLoop | ( | NestLoop * | node, | |
EState * | estate, | |||
int | eflags | |||
) |
Definition at line 296 of file nodeNestloop.c.
References Assert, elog, ERROR, EXEC_FLAG_BACKWARD, EXEC_FLAG_MARK, ExecAssignExprContext(), ExecAssignProjectionInfo(), ExecAssignResultTypeFromTL(), ExecGetResultType(), ExecInitExpr(), ExecInitNode(), ExecInitNullTupleSlot(), ExecInitResultTupleSlot(), innerPlan, innerPlanState, NestLoop::join, JOIN_ANTI, JOIN_INNER, JOIN_LEFT, JOIN_SEMI, Join::joinqual, JoinState::joinqual, Join::jointype, JoinState::jointype, NestLoopState::js, makeNode, NestLoop::nestParams, NIL, NL1_printf, NestLoopState::nl_MatchedOuter, NestLoopState::nl_NeedNewOuter, NestLoopState::nl_NullInnerTupleSlot, NULL, outerPlan, outerPlanState, Join::plan, PlanState::plan, JoinState::ps, PlanState::ps_TupFromTlist, Plan::qual, PlanState::qual, PlanState::state, Plan::targetlist, and PlanState::targetlist.
Referenced by ExecInitNode().
{ NestLoopState *nlstate; /* check for unsupported flags */ Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK))); NL1_printf("ExecInitNestLoop: %s\n", "initializing node"); /* * create state structure */ nlstate = makeNode(NestLoopState); nlstate->js.ps.plan = (Plan *) node; nlstate->js.ps.state = estate; /* * Miscellaneous initialization * * create expression context for node */ ExecAssignExprContext(estate, &nlstate->js.ps); /* * initialize child expressions */ nlstate->js.ps.targetlist = (List *) ExecInitExpr((Expr *) node->join.plan.targetlist, (PlanState *) nlstate); nlstate->js.ps.qual = (List *) ExecInitExpr((Expr *) node->join.plan.qual, (PlanState *) nlstate); nlstate->js.jointype = node->join.jointype; nlstate->js.joinqual = (List *) ExecInitExpr((Expr *) node->join.joinqual, (PlanState *) nlstate); /* * initialize child nodes * * If we have no parameters to pass into the inner rel from the outer, * tell the inner child that cheap rescans would be good. If we do have * such parameters, then there is no point in REWIND support at all in the * inner child, because it will always be rescanned with fresh parameter * values. */ outerPlanState(nlstate) = ExecInitNode(outerPlan(node), estate, eflags); if (node->nestParams == NIL) eflags |= EXEC_FLAG_REWIND; else eflags &= ~EXEC_FLAG_REWIND; innerPlanState(nlstate) = ExecInitNode(innerPlan(node), estate, eflags); /* * tuple table initialization */ ExecInitResultTupleSlot(estate, &nlstate->js.ps); switch (node->join.jointype) { case JOIN_INNER: case JOIN_SEMI: break; case JOIN_LEFT: case JOIN_ANTI: nlstate->nl_NullInnerTupleSlot = ExecInitNullTupleSlot(estate, ExecGetResultType(innerPlanState(nlstate))); break; default: elog(ERROR, "unrecognized join type: %d", (int) node->join.jointype); } /* * initialize tuple type and projection info */ ExecAssignResultTypeFromTL(&nlstate->js.ps); ExecAssignProjectionInfo(&nlstate->js.ps, NULL); /* * finally, wipe the current outer tuple clean. */ nlstate->js.ps.ps_TupFromTlist = false; nlstate->nl_NeedNewOuter = true; nlstate->nl_MatchedOuter = false; NL1_printf("ExecInitNestLoop: %s\n", "node initialized"); return nlstate; }
TupleTableSlot* ExecNestLoop | ( | NestLoopState * | node | ) |
Definition at line 60 of file nodeNestloop.c.
References Assert, bms_add_member(), PlanState::chgParam, ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, ExprContext::ecxt_param_exec_vals, ENL1_printf, ExecProcNode(), ExecProject(), ExecQual(), ExecReScan(), ExprEndResult, ExprMultipleResult, innerPlanState, InstrCountFiltered1, InstrCountFiltered2, IsA, ParamExecData::isnull, JOIN_ANTI, JOIN_LEFT, JOIN_SEMI, JoinState::joinqual, JoinState::jointype, NestLoopState::js, lfirst, NestLoop::nestParams, NIL, NestLoopState::nl_MatchedOuter, NestLoopState::nl_NeedNewOuter, NestLoopState::nl_NullInnerTupleSlot, OUTER_VAR, outerPlanState, NestLoopParam::paramno, NestLoopParam::paramval, PlanState::plan, JoinState::ps, PlanState::ps_ExprContext, PlanState::ps_ProjInfo, PlanState::ps_TupFromTlist, PlanState::qual, ResetExprContext, slot_getattr(), TupIsNull, ParamExecData::value, Var::varattno, and Var::varno.
Referenced by ExecProcNode().
{ NestLoop *nl; PlanState *innerPlan; PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; List *joinqual; List *otherqual; ExprContext *econtext; ListCell *lc; /* * get information from the node */ ENL1_printf("getting info from node"); nl = (NestLoop *) node->js.ps.plan; joinqual = node->js.joinqual; otherqual = node->js.ps.qual; outerPlan = outerPlanState(node); innerPlan = innerPlanState(node); econtext = node->js.ps.ps_ExprContext; /* * 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 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"); econtext->ecxt_outertuple = outerTupleSlot; node->nl_NeedNewOuter = false; node->nl_MatchedOuter = false; /* * fetch the values of any outer Vars that must be passed to the * inner scan, and store them in the appropriate PARAM_EXEC slots. */ foreach(lc, nl->nestParams) { NestLoopParam *nlp = (NestLoopParam *) lfirst(lc); int paramno = nlp->paramno; ParamExecData *prm; prm = &(econtext->ecxt_param_exec_vals[paramno]); /* Param value should be an OUTER_VAR var */ Assert(IsA(nlp->paramval, Var)); Assert(nlp->paramval->varno == OUTER_VAR); Assert(nlp->paramval->varattno > 0); prm->value = slot_getattr(outerTupleSlot, nlp->paramval->varattno, &(prm->isnull)); /* Flag parameter value as changed */ innerPlan->chgParam = bms_add_member(innerPlan->chgParam, paramno); } /* * now rescan the inner plan */ ENL1_printf("rescanning inner plan"); ExecReScan(innerPlan); } /* * 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 || node->js.jointype == JOIN_ANTI)) { /* * 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 (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; } } else InstrCountFiltered2(node, 1); } /* * 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; /* In an antijoin, we never return a matched tuple */ if (node->js.jointype == JOIN_ANTI) { node->nl_NeedNewOuter = true; continue; /* return to top of loop */ } /* * In a semijoin, we'll consider returning the first match, but * after that we're done with this outer tuple. */ if (node->js.jointype == JOIN_SEMI) node->nl_NeedNewOuter = 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; } } else InstrCountFiltered2(node, 1); } else InstrCountFiltered1(node, 1); /* * Tuple fails qual, so free per-tuple memory and try again. */ ResetExprContext(econtext); ENL1_printf("qualification failed, looping"); } }
void ExecReScanNestLoop | ( | NestLoopState * | node | ) |
Definition at line 427 of file nodeNestloop.c.
References PlanState::chgParam, ExecReScan(), NestLoopState::js, NestLoopState::nl_MatchedOuter, NestLoopState::nl_NeedNewOuter, NULL, outerPlan, outerPlanState, JoinState::ps, and PlanState::ps_TupFromTlist.
Referenced by ExecReScan().
{ PlanState *outerPlan = outerPlanState(node); /* * If outerPlan->chgParam is not null then plan will be automatically * re-scanned by first ExecProcNode. */ if (outerPlan->chgParam == NULL) ExecReScan(outerPlan); /* * innerPlan is re-scanned for each new outer tuple and MUST NOT be * re-scanned from here or you'll get troubles from inner index scans when * outer Vars are used as run-time keys... */ node->js.ps.ps_TupFromTlist = false; node->nl_NeedNewOuter = true; node->nl_MatchedOuter = false; }