]> granicus.if.org Git - postgresql/blobdiff - src/backend/executor/nodeNestloop.c
Make some small planner API cleanups.
[postgresql] / src / backend / executor / nodeNestloop.c
index 2bd26938fcc5d63fb200f09b5b91092827e18529..fc6667ef8262f2de2c7e3a2079ac528bd72629a9 100644 (file)
@@ -3,12 +3,12 @@
  * nodeNestloop.c
  *       routines to support nest-loop joins
  *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.26 2002/06/20 20:29:28 momjian Exp $
+ *       src/backend/executor/nodeNestloop.c
  *
  *-------------------------------------------------------------------------
  */
@@ -23,6 +23,7 @@
 
 #include "executor/execdebug.h"
 #include "executor/nodeNestloop.h"
+#include "miscadmin.h"
 #include "utils/memutils.h"
 
 
  *                        are prepared to return the first tuple.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecNestLoop(NestLoop *node)
+static TupleTableSlot *
+ExecNestLoop(PlanState *pstate)
 {
-       NestLoopState *nlstate;
-       Plan       *innerPlan;
-       Plan       *outerPlan;
+       NestLoopState *node = castNode(NestLoopState, pstate);
+       NestLoop   *nl;
+       PlanState  *innerPlan;
+       PlanState  *outerPlan;
        TupleTableSlot *outerTupleSlot;
        TupleTableSlot *innerTupleSlot;
-       List       *joinqual;
-       List       *otherqual;
+       ExprState  *joinqual;
+       ExprState  *otherqual;
        ExprContext *econtext;
+       ListCell   *lc;
+
+       CHECK_FOR_INTERRUPTS();
 
        /*
         * get information from the node
         */
        ENL1_printf("getting info from node");
 
-       nlstate = node->nlstate;
-       joinqual = node->join.joinqual;
-       otherqual = node->join.plan.qual;
-       outerPlan = outerPlan((Plan *) node);
-       innerPlan = innerPlan((Plan *) node);
-       econtext = nlstate->jstate.cs_ExprContext;
-
-       /*
-        * get the current outer tuple
-        */
-       outerTupleSlot = nlstate->jstate.cs_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 (nlstate->jstate.cs_TupFromTlist)
-       {
-               TupleTableSlot *result;
-               ExprDoneCond isDone;
-
-               result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
-               if (isDone == ExprMultipleResult)
-                       return result;
-               /* Done with that source tuple... */
-               nlstate->jstate.cs_TupFromTlist = false;
-       }
+       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;
 
        /*
         * 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.
+        * storage allocated in the previous tuple cycle.
         */
        ResetExprContext(econtext);
 
@@ -122,14 +103,13 @@ ExecNestLoop(NestLoop *node)
                 * If we don't have an outer tuple, get the next one and reset the
                 * inner scan.
                 */
-               if (nlstate->nl_NeedNewOuter)
+               if (node->nl_NeedNewOuter)
                {
                        ENL1_printf("getting new outer tuple");
-                       outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
+                       outerTupleSlot = ExecProcNode(outerPlan);
 
                        /*
-                        * if there are no more outer tuples, then the join is
-                        * complete..
+                        * if there are no more outer tuples, then the join is complete..
                         */
                        if (TupIsNull(outerTupleSlot))
                        {
@@ -138,22 +118,38 @@ ExecNestLoop(NestLoop *node)
                        }
 
                        ENL1_printf("saving new outer tuple information");
-                       nlstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
                        econtext->ecxt_outertuple = outerTupleSlot;
-                       nlstate->nl_NeedNewOuter = false;
-                       nlstate->nl_MatchedOuter = false;
+                       node->nl_NeedNewOuter = false;
+                       node->nl_MatchedOuter = false;
 
                        /*
-                        * now rescan the inner plan
+                        * fetch the values of any outer Vars that must be passed to the
+                        * inner scan, and store them in the appropriate PARAM_EXEC slots.
                         */
-                       ENL1_printf("rescanning inner plan");
+                       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);
+                       }
 
                        /*
-                        * 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.
+                        * now rescan the inner plan
                         */
-                       ExecReScan(innerPlan, econtext, (Plan *) node);
+                       ENL1_printf("rescanning inner plan");
+                       ExecReScan(innerPlan);
                }
 
                /*
@@ -161,49 +157,42 @@ ExecNestLoop(NestLoop *node)
                 */
                ENL1_printf("getting new inner tuple");
 
-               innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
+               innerTupleSlot = ExecProcNode(innerPlan);
                econtext->ecxt_innertuple = innerTupleSlot;
 
                if (TupIsNull(innerTupleSlot))
                {
                        ENL1_printf("no inner tuple, need new outer tuple");
 
-                       nlstate->nl_NeedNewOuter = true;
+                       node->nl_NeedNewOuter = true;
 
-                       if (!nlstate->nl_MatchedOuter &&
-                               node->join.jointype == JOIN_LEFT)
+                       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.
+                                * 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 = nlstate->nl_NullInnerTupleSlot;
+                               econtext->ecxt_innertuple = node->nl_NullInnerTupleSlot;
 
                                ENL1_printf("testing qualification for outer-join tuple");
 
-                               if (ExecQual(otherqual, econtext, false))
+                               if (otherqual == NULL || ExecQual(otherqual, econtext))
                                {
                                        /*
-                                        * qualification was satisfied so we project and
-                                        * return the slot containing the result tuple using
+                                        * 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(nlstate->jstate.cs_ProjInfo, &isDone);
-
-                                       if (isDone != ExprEndResult)
-                                       {
-                                               nlstate->jstate.cs_TupFromTlist =
-                                                       (isDone == ExprMultipleResult);
-                                               return result;
-                                       }
+                                       return ExecProject(node->js.ps.ps_ProjInfo);
                                }
+                               else
+                                       InstrCountFiltered2(node, 1);
                        }
 
                        /*
@@ -213,41 +202,49 @@ ExecNestLoop(NestLoop *node)
                }
 
                /*
-                * 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.
+                * 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))
+               if (ExecQual(joinqual, econtext))
                {
-                       nlstate->nl_MatchedOuter = true;
+                       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 */
+                       }
+
+                       /*
+                        * 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.single_match)
+                               node->nl_NeedNewOuter = true;
 
-                       if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+                       if (otherqual == NULL || ExecQual(otherqual, econtext))
                        {
                                /*
-                                * qualification was satisfied so we project and return
-                                * the slot containing the result tuple using
-                                * ExecProject().
+                                * 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(nlstate->jstate.cs_ProjInfo, &isDone);
-
-                               if (isDone != ExprEndResult)
-                               {
-                                       nlstate->jstate.cs_TupFromTlist =
-                                               (isDone == ExprMultipleResult);
-                                       return result;
-                               }
+                               return ExecProject(node->js.ps.ps_ProjInfo);
                        }
+                       else
+                               InstrCountFiltered2(node, 1);
                }
+               else
+                       InstrCountFiltered1(node, 1);
 
                /*
                 * Tuple fails qual, so free per-tuple memory and try again.
@@ -260,90 +257,99 @@ ExecNestLoop(NestLoop *node)
 
 /* ----------------------------------------------------------------
  *             ExecInitNestLoop
- *
- *             Creates the run-time state information for the nestloop node
- *             produced by the planner and initailizes inner and outer relations
- *             (child nodes).
  * ----------------------------------------------------------------
  */
-bool
-ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
+NestLoopState *
+ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 {
        NestLoopState *nlstate;
 
+       /* check for unsupported flags */
+       Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
+
        NL1_printf("ExecInitNestLoop: %s\n",
                           "initializing node");
 
        /*
-        * assign execution state to node
-        */
-       node->join.plan.state = estate;
-
-       /*
-        * create new nest loop state
+        * create state structure
         */
        nlstate = makeNode(NestLoopState);
-       node->nlstate = nlstate;
+       nlstate->js.ps.plan = (Plan *) node;
+       nlstate->js.ps.state = estate;
+       nlstate->js.ps.ExecProcNode = ExecNestLoop;
 
        /*
         * Miscellaneous initialization
         *
         * create expression context for node
         */
-       ExecAssignExprContext(estate, &nlstate->jstate);
+       ExecAssignExprContext(estate, &nlstate->js.ps);
+
+       /*
+        * 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);
 
        /*
-        * now initialize children
+        * Initialize result slot, type and projection.
         */
-       ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node);
-       ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node);
+       ExecInitResultTupleSlotTL(&nlstate->js.ps, &TTSOpsVirtual);
+       ExecAssignProjectionInfo(&nlstate->js.ps, NULL);
 
-#define NESTLOOP_NSLOTS 2
+       /*
+        * initialize child expressions
+        */
+       nlstate->js.ps.qual =
+               ExecInitQual(node->join.plan.qual, (PlanState *) nlstate);
+       nlstate->js.jointype = node->join.jointype;
+       nlstate->js.joinqual =
+               ExecInitQual(node->join.joinqual, (PlanState *) nlstate);
 
        /*
-        * tuple table initialization
+        * detect whether we need only consider the first matching inner tuple
         */
-       ExecInitResultTupleSlot(estate, &nlstate->jstate);
+       nlstate->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:
+               case JOIN_SEMI:
                        break;
                case JOIN_LEFT:
+               case JOIN_ANTI:
                        nlstate->nl_NullInnerTupleSlot =
                                ExecInitNullTupleSlot(estate,
-                                                          ExecGetTupType(innerPlan((Plan *) node)));
+                                                                         ExecGetResultType(innerPlanState(nlstate)),
+                                                                         &TTSOpsVirtual);
                        break;
                default:
-                       elog(ERROR, "ExecInitNestLoop: unsupported join type %d",
+                       elog(ERROR, "unrecognized join type: %d",
                                 (int) node->join.jointype);
        }
 
-       /*
-        * initialize tuple type and projection info
-        */
-       ExecAssignResultTypeFromTL((Plan *) node, &nlstate->jstate);
-       ExecAssignProjectionInfo((Plan *) node, &nlstate->jstate);
-
        /*
         * finally, wipe the current outer tuple clean.
         */
-       nlstate->jstate.cs_OuterTupleSlot = NULL;
-       nlstate->jstate.cs_TupFromTlist = false;
        nlstate->nl_NeedNewOuter = true;
        nlstate->nl_MatchedOuter = false;
 
        NL1_printf("ExecInitNestLoop: %s\n",
                           "node initialized");
-       return TRUE;
-}
 
-int
-ExecCountSlotsNestLoop(NestLoop *node)
-{
-       return ExecCountSlotsNode(outerPlan(node)) +
-               ExecCountSlotsNode(innerPlan(node)) +
-               NESTLOOP_NSLOTS;
+       return nlstate;
 }
 
 /* ----------------------------------------------------------------
@@ -353,38 +359,26 @@ ExecCountSlotsNestLoop(NestLoop *node)
  * ----------------------------------------------------------------
  */
 void
-ExecEndNestLoop(NestLoop *node)
+ExecEndNestLoop(NestLoopState *node)
 {
-       NestLoopState *nlstate;
-
        NL1_printf("ExecEndNestLoop: %s\n",
                           "ending node processing");
 
        /*
-        * get info from the node
+        * Free the exprcontext
         */
-       nlstate = node->nlstate;
+       ExecFreeExprContext(&node->js.ps);
 
        /*
-        * Free the projection info
-        *
-        * Note: we don't ExecFreeResultType(nlstate) because the rule manager
-        * depends on the tupType returned by ExecMain().  So for now, this is
-        * freed at end-transaction time.  -cim 6/2/91
+        * clean out the tuple table
         */
-       ExecFreeProjectionInfo(&nlstate->jstate);
-       ExecFreeExprContext(&nlstate->jstate);
+       ExecClearTuple(node->js.ps.ps_ResultTupleSlot);
 
        /*
         * close down subplans
         */
-       ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
-       ExecEndNode(innerPlan((Plan *) node), (Plan *) node);
-
-       /*
-        * clean out the tuple table
-        */
-       ExecClearTuple(nlstate->jstate.cs_ResultTupleSlot);
+       ExecEndNode(outerPlanState(node));
+       ExecEndNode(innerPlanState(node));
 
        NL1_printf("ExecEndNestLoop: %s\n",
                           "node processing ended");
@@ -395,24 +389,23 @@ ExecEndNestLoop(NestLoop *node)
  * ----------------------------------------------------------------
  */
 void
-ExecReScanNestLoop(NestLoop *node, ExprContext *exprCtxt, Plan *parent)
+ExecReScanNestLoop(NestLoopState *node)
 {
-       NestLoopState *nlstate = node->nlstate;
-       Plan       *outerPlan = outerPlan((Plan *) node);
+       PlanState  *outerPlan = outerPlanState(node);
 
        /*
         * If outerPlan->chgParam is not null then plan will be automatically
-        * re-scanned by first ExecProcNode. 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...
+        * re-scanned by first ExecProcNode.
         */
        if (outerPlan->chgParam == NULL)
-               ExecReScan(outerPlan, exprCtxt, (Plan *) node);
+               ExecReScan(outerPlan);
 
-       /* let outerPlan to free its result tuple ... */
-       nlstate->jstate.cs_OuterTupleSlot = NULL;
-       nlstate->jstate.cs_TupFromTlist = false;
-       nlstate->nl_NeedNewOuter = true;
-       nlstate->nl_MatchedOuter = false;
+       /*
+        * 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->nl_NeedNewOuter = true;
+       node->nl_MatchedOuter = false;
 }