]> granicus.if.org Git - postgresql/blobdiff - src/backend/executor/nodeNestloop.c
Make some small planner API cleanups.
[postgresql] / src / backend / executor / nodeNestloop.c
index 5178bea4689c60de280a7f8513b63608b6fb4bf1..fc6667ef8262f2de2c7e3a2079ac528bd72629a9 100644 (file)
@@ -3,12 +3,12 @@
  * nodeNestloop.c
  *       routines to support nest-loop joins
  *
- * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/nodeNestloop.c,v 1.54 2009/09/27 21:10:53 tgl 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(NestLoopState *node)
+static TupleTableSlot *
+ExecNestLoop(PlanState *pstate)
 {
+       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");
 
+       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.
+        * storage allocated in the previous tuple cycle.
         */
        ResetExprContext(econtext);
 
@@ -134,16 +123,33 @@ ExecNestLoop(NestLoopState *node)
                        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);
+                       ENL1_printf("rescanning inner plan");
+                       ExecReScan(innerPlan);
                }
 
                /*
@@ -174,27 +180,19 @@ ExecNestLoop(NestLoopState *node)
 
                                ENL1_printf("testing qualification for outer-join tuple");
 
-                               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().
                                         */
-                                       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;
-                                       }
+                                       return ExecProject(node->js.ps.ps_ProjInfo);
                                }
+                               else
+                                       InstrCountFiltered2(node, 1);
                        }
 
                        /*
@@ -213,7 +211,7 @@ ExecNestLoop(NestLoopState *node)
                 */
                ENL1_printf("testing qualification");
 
-               if (ExecQual(joinqual, econtext, false))
+               if (ExecQual(joinqual, econtext))
                {
                        node->nl_MatchedOuter = true;
 
@@ -225,33 +223,28 @@ ExecNestLoop(NestLoopState *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->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().
                                 */
-                               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;
-                               }
+                               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.
@@ -283,6 +276,7 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
        nlstate = makeNode(NestLoopState);
        nlstate->js.ps.plan = (Plan *) node;
        nlstate->js.ps.state = estate;
+       nlstate->js.ps.ExecProcNode = ExecNestLoop;
 
        /*
         * Miscellaneous initialization
@@ -291,38 +285,44 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
         */
        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
         *
-        * Tell the inner child that cheap rescans would be good.  (This is
-        * unnecessary if we are doing nestloop with inner indexscan, because the
-        * rescan will always be with a fresh parameter --- but since
-        * nodeIndexscan doesn't actually care about REWIND, there's no point in
-        * dealing with that refinement.)
+        * 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);
-       innerPlanState(nlstate) = ExecInitNode(innerPlan(node), estate,
-                                                                                  eflags | EXEC_FLAG_REWIND);
+       if (node->nestParams == NIL)
+               eflags |= EXEC_FLAG_REWIND;
+       else
+               eflags &= ~EXEC_FLAG_REWIND;
+       innerPlanState(nlstate) = ExecInitNode(innerPlan(node), estate, eflags);
+
+       /*
+        * Initialize result slot, type and projection.
+        */
+       ExecInitResultTupleSlotTL(&nlstate->js.ps, &TTSOpsVirtual);
+       ExecAssignProjectionInfo(&nlstate->js.ps, NULL);
 
        /*
-        * tuple table initialization
+        * initialize child expressions
         */
-       ExecInitResultTupleSlot(estate, &nlstate->js.ps);
+       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);
 
+       /*
+        * detect whether we need only consider the first matching inner tuple
+        */
+       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:
@@ -332,23 +332,17 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
                case JOIN_ANTI:
                        nlstate->nl_NullInnerTupleSlot =
                                ExecInitNullTupleSlot(estate,
-                                                                ExecGetResultType(innerPlanState(nlstate)));
+                                                                         ExecGetResultType(innerPlanState(nlstate)),
+                                                                         &TTSOpsVirtual);
                        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;
 
@@ -395,20 +389,23 @@ ExecEndNestLoop(NestLoopState *node)
  * ----------------------------------------------------------------
  */
 void
-ExecReScanNestLoop(NestLoopState *node, ExprContext *exprCtxt)
+ExecReScanNestLoop(NestLoopState *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);
+               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;
 }