]> granicus.if.org Git - postgresql/blobdiff - src/backend/executor/nodeNestloop.c
Make some small planner API cleanups.
[postgresql] / src / backend / executor / nodeNestloop.c
index f12f2f624f621d5dd599ec74d20161dca8cfd576..fc6667ef8262f2de2c7e3a2079ac528bd72629a9 100644 (file)
@@ -3,12 +3,12 @@
  * nodeNestloop.c
  *       routines to support nest-loop joins
  *
- * Portions Copyright (c) 1996-2006, 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.42 2006/03/05 15:58:26 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(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;
 
-       /*
-        * 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.
+        * storage allocated in the previous tuple cycle.
         */
        ResetExprContext(econtext);
 
@@ -144,22 +118,38 @@ ExecNestLoop(NestLoopState *node)
                        }
 
                        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
+                        * 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);
                }
 
                /*
@@ -177,7 +167,8 @@ ExecNestLoop(NestLoopState *node)
                        node->nl_NeedNewOuter = true;
 
                        if (!node->nl_MatchedOuter &&
-                               node->js.jointype == JOIN_LEFT)
+                               (node->js.jointype == JOIN_LEFT ||
+                                node->js.jointype == JOIN_ANTI))
                        {
                                /*
                                 * We are doing an outer join and there were no join matches
@@ -189,27 +180,19 @@ ExecNestLoop(NestLoopState *node)
 
                                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
                                         * 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);
                        }
 
                        /*
@@ -228,35 +211,40 @@ ExecNestLoop(NestLoopState *node)
                 */
                ENL1_printf("testing qualification");
 
-               if (ExecQual(joinqual, econtext, false))
+               if (ExecQual(joinqual, econtext))
                {
                        node->nl_MatchedOuter = true;
 
-                       if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+                       /* 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 == 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);
                        }
-
-                       /* If we didn't return a tuple, may need to set NeedNewOuter */
-                       if (node->js.jointype == JOIN_IN)
-                               node->nl_NeedNewOuter = true;
+                       else
+                               InstrCountFiltered2(node, 1);
                }
+               else
+                       InstrCountFiltered1(node, 1);
 
                /*
                 * Tuple fails qual, so free per-tuple memory and try again.
@@ -288,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
@@ -296,66 +285,64 @@ 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);
 
-#define NESTLOOP_NSLOTS 2
+       /*
+        * 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:
-               case JOIN_IN:
+               case JOIN_SEMI:
                        break;
                case JOIN_LEFT:
+               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);
-
        /*
         * finally, wipe the current outer tuple clean.
         */
-       nlstate->js.ps.ps_OuterTupleSlot = NULL;
-       nlstate->js.ps.ps_TupFromTlist = false;
        nlstate->nl_NeedNewOuter = true;
        nlstate->nl_MatchedOuter = false;
 
@@ -365,14 +352,6 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
        return nlstate;
 }
 
-int
-ExecCountSlotsNestLoop(NestLoop *node)
-{
-       return ExecCountSlotsNode(outerPlan(node)) +
-               ExecCountSlotsNode(innerPlan(node)) +
-               NESTLOOP_NSLOTS;
-}
-
 /* ----------------------------------------------------------------
  *             ExecEndNestLoop
  *
@@ -410,22 +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...
+        */
 
-       /* let outerPlan to free its result tuple ... */
-       node->js.ps.ps_OuterTupleSlot = NULL;
-       node->js.ps.ps_TupFromTlist = false;
        node->nl_NeedNewOuter = true;
        node->nl_MatchedOuter = false;
 }