LCOV - code coverage report
Current view: top level - src/backend/executor - nodeNestloop.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 97 98 99.0 %
Date: 2017-09-29 15:12:54 Functions: 4 4 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * nodeNestloop.c
       4             :  *    routines to support nest-loop joins
       5             :  *
       6             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/executor/nodeNestloop.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : /*
      16             :  *   INTERFACE ROUTINES
      17             :  *      ExecNestLoop     - process a nestloop join of two plans
      18             :  *      ExecInitNestLoop - initialize the join
      19             :  *      ExecEndNestLoop  - shut down the join
      20             :  */
      21             : 
      22             : #include "postgres.h"
      23             : 
      24             : #include "executor/execdebug.h"
      25             : #include "executor/nodeNestloop.h"
      26             : #include "miscadmin.h"
      27             : #include "utils/memutils.h"
      28             : 
      29             : 
      30             : /* ----------------------------------------------------------------
      31             :  *      ExecNestLoop(node)
      32             :  *
      33             :  * old comments
      34             :  *      Returns the tuple joined from inner and outer tuples which
      35             :  *      satisfies the qualification clause.
      36             :  *
      37             :  *      It scans the inner relation to join with current outer tuple.
      38             :  *
      39             :  *      If none is found, next tuple from the outer relation is retrieved
      40             :  *      and the inner relation is scanned from the beginning again to join
      41             :  *      with the outer tuple.
      42             :  *
      43             :  *      NULL is returned if all the remaining outer tuples are tried and
      44             :  *      all fail to join with the inner tuples.
      45             :  *
      46             :  *      NULL is also returned if there is no tuple from inner relation.
      47             :  *
      48             :  *      Conditions:
      49             :  *        -- outerTuple contains current tuple from outer relation and
      50             :  *           the right son(inner relation) maintains "cursor" at the tuple
      51             :  *           returned previously.
      52             :  *              This is achieved by maintaining a scan position on the outer
      53             :  *              relation.
      54             :  *
      55             :  *      Initial States:
      56             :  *        -- the outer child and the inner child
      57             :  *             are prepared to return the first tuple.
      58             :  * ----------------------------------------------------------------
      59             :  */
      60             : static TupleTableSlot *
      61     8149270 : ExecNestLoop(PlanState *pstate)
      62             : {
      63     8149270 :     NestLoopState *node = castNode(NestLoopState, pstate);
      64             :     NestLoop   *nl;
      65             :     PlanState  *innerPlan;
      66             :     PlanState  *outerPlan;
      67             :     TupleTableSlot *outerTupleSlot;
      68             :     TupleTableSlot *innerTupleSlot;
      69             :     ExprState  *joinqual;
      70             :     ExprState  *otherqual;
      71             :     ExprContext *econtext;
      72             :     ListCell   *lc;
      73             : 
      74     8149270 :     CHECK_FOR_INTERRUPTS();
      75             : 
      76             :     /*
      77             :      * get information from the node
      78             :      */
      79             :     ENL1_printf("getting info from node");
      80             : 
      81     8149269 :     nl = (NestLoop *) node->js.ps.plan;
      82     8149269 :     joinqual = node->js.joinqual;
      83     8149269 :     otherqual = node->js.ps.qual;
      84     8149269 :     outerPlan = outerPlanState(node);
      85     8149269 :     innerPlan = innerPlanState(node);
      86     8149269 :     econtext = node->js.ps.ps_ExprContext;
      87             : 
      88             :     /*
      89             :      * Reset per-tuple memory context to free any expression evaluation
      90             :      * storage allocated in the previous tuple cycle.
      91             :      */
      92     8149269 :     ResetExprContext(econtext);
      93             : 
      94             :     /*
      95             :      * Ok, everything is setup for the join so now loop until we return a
      96             :      * qualifying join tuple.
      97             :      */
      98             :     ENL1_printf("entering main loop");
      99             : 
     100             :     for (;;)
     101             :     {
     102             :         /*
     103             :          * If we don't have an outer tuple, get the next one and reset the
     104             :          * inner scan.
     105             :          */
     106     8471065 :         if (node->nl_NeedNewOuter)
     107             :         {
     108             :             ENL1_printf("getting new outer tuple");
     109       38842 :             outerTupleSlot = ExecProcNode(outerPlan);
     110             : 
     111             :             /*
     112             :              * if there are no more outer tuples, then the join is complete..
     113             :              */
     114       38842 :             if (TupIsNull(outerTupleSlot))
     115             :             {
     116             :                 ENL1_printf("no outer tuple, ending join");
     117        2632 :                 return NULL;
     118             :             }
     119             : 
     120             :             ENL1_printf("saving new outer tuple information");
     121       36210 :             econtext->ecxt_outertuple = outerTupleSlot;
     122       36210 :             node->nl_NeedNewOuter = false;
     123       36210 :             node->nl_MatchedOuter = false;
     124             : 
     125             :             /*
     126             :              * fetch the values of any outer Vars that must be passed to the
     127             :              * inner scan, and store them in the appropriate PARAM_EXEC slots.
     128             :              */
     129       67020 :             foreach(lc, nl->nestParams)
     130             :             {
     131       30810 :                 NestLoopParam *nlp = (NestLoopParam *) lfirst(lc);
     132       30810 :                 int         paramno = nlp->paramno;
     133             :                 ParamExecData *prm;
     134             : 
     135       30810 :                 prm = &(econtext->ecxt_param_exec_vals[paramno]);
     136             :                 /* Param value should be an OUTER_VAR var */
     137       30810 :                 Assert(IsA(nlp->paramval, Var));
     138       30810 :                 Assert(nlp->paramval->varno == OUTER_VAR);
     139       30810 :                 Assert(nlp->paramval->varattno > 0);
     140       61620 :                 prm->value = slot_getattr(outerTupleSlot,
     141       30810 :                                           nlp->paramval->varattno,
     142             :                                           &(prm->isnull));
     143             :                 /* Flag parameter value as changed */
     144       30810 :                 innerPlan->chgParam = bms_add_member(innerPlan->chgParam,
     145             :                                                      paramno);
     146             :             }
     147             : 
     148             :             /*
     149             :              * now rescan the inner plan
     150             :              */
     151             :             ENL1_printf("rescanning inner plan");
     152       36210 :             ExecReScan(innerPlan);
     153             :         }
     154             : 
     155             :         /*
     156             :          * we have an outerTuple, try to get the next inner tuple.
     157             :          */
     158             :         ENL1_printf("getting new inner tuple");
     159             : 
     160     8468433 :         innerTupleSlot = ExecProcNode(innerPlan);
     161     8468433 :         econtext->ecxt_innertuple = innerTupleSlot;
     162             : 
     163     8468433 :         if (TupIsNull(innerTupleSlot))
     164             :         {
     165             :             ENL1_printf("no inner tuple, need new outer tuple");
     166             : 
     167       31766 :             node->nl_NeedNewOuter = true;
     168             : 
     169       45242 :             if (!node->nl_MatchedOuter &&
     170       26373 :                 (node->js.jointype == JOIN_LEFT ||
     171       12897 :                  node->js.jointype == JOIN_ANTI))
     172             :             {
     173             :                 /*
     174             :                  * We are doing an outer join and there were no join matches
     175             :                  * for this outer tuple.  Generate a fake join tuple with
     176             :                  * nulls for the inner tuple, and return it if it passes the
     177             :                  * non-join quals.
     178             :                  */
     179         604 :                 econtext->ecxt_innertuple = node->nl_NullInnerTupleSlot;
     180             : 
     181             :                 ENL1_printf("testing qualification for outer-join tuple");
     182             : 
     183         604 :                 if (otherqual == NULL || ExecQual(otherqual, econtext))
     184             :                 {
     185             :                     /*
     186             :                      * qualification was satisfied so we project and return
     187             :                      * the slot containing the result tuple using
     188             :                      * ExecProject().
     189             :                      */
     190             :                     ENL1_printf("qualification succeeded, projecting tuple");
     191             : 
     192         585 :                     return ExecProject(node->js.ps.ps_ProjInfo);
     193             :                 }
     194             :                 else
     195          19 :                     InstrCountFiltered2(node, 1);
     196             :             }
     197             : 
     198             :             /*
     199             :              * Otherwise just return to top of loop for a new outer tuple.
     200             :              */
     201       31181 :             continue;
     202             :         }
     203             : 
     204             :         /*
     205             :          * at this point we have a new pair of inner and outer tuples so we
     206             :          * test the inner and outer tuples to see if they satisfy the node's
     207             :          * qualification.
     208             :          *
     209             :          * Only the joinquals determine MatchedOuter status, but all quals
     210             :          * must pass to actually return the tuple.
     211             :          */
     212             :         ENL1_printf("testing qualification");
     213             : 
     214     8436667 :         if (ExecQual(joinqual, econtext))
     215             :         {
     216     8147315 :             node->nl_MatchedOuter = true;
     217             : 
     218             :             /* In an antijoin, we never return a matched tuple */
     219     8147315 :             if (node->js.jointype == JOIN_ANTI)
     220             :             {
     221        1257 :                 node->nl_NeedNewOuter = true;
     222        1257 :                 continue;       /* return to top of loop */
     223             :             }
     224             : 
     225             :             /*
     226             :              * If we only need to join to the first matching inner tuple, then
     227             :              * consider returning this one, but after that continue with next
     228             :              * outer tuple.
     229             :              */
     230     8146058 :             if (node->js.single_match)
     231        3163 :                 node->nl_NeedNewOuter = true;
     232             : 
     233     8146058 :             if (otherqual == NULL || ExecQual(otherqual, econtext))
     234             :             {
     235             :                 /*
     236             :                  * qualification was satisfied so we project and return the
     237             :                  * slot containing the result tuple using ExecProject().
     238             :                  */
     239             :                 ENL1_printf("qualification succeeded, projecting tuple");
     240             : 
     241     8146052 :                 return ExecProject(node->js.ps.ps_ProjInfo);
     242             :             }
     243             :             else
     244           6 :                 InstrCountFiltered2(node, 1);
     245             :         }
     246             :         else
     247      289352 :             InstrCountFiltered1(node, 1);
     248             : 
     249             :         /*
     250             :          * Tuple fails qual, so free per-tuple memory and try again.
     251             :          */
     252      289358 :         ResetExprContext(econtext);
     253             : 
     254             :         ENL1_printf("qualification failed, looping");
     255      321796 :     }
     256             : }
     257             : 
     258             : /* ----------------------------------------------------------------
     259             :  *      ExecInitNestLoop
     260             :  * ----------------------------------------------------------------
     261             :  */
     262             : NestLoopState *
     263        2225 : ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
     264             : {
     265             :     NestLoopState *nlstate;
     266             : 
     267             :     /* check for unsupported flags */
     268        2225 :     Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
     269             : 
     270             :     NL1_printf("ExecInitNestLoop: %s\n",
     271             :                "initializing node");
     272             : 
     273             :     /*
     274             :      * create state structure
     275             :      */
     276        2225 :     nlstate = makeNode(NestLoopState);
     277        2225 :     nlstate->js.ps.plan = (Plan *) node;
     278        2225 :     nlstate->js.ps.state = estate;
     279        2225 :     nlstate->js.ps.ExecProcNode = ExecNestLoop;
     280             : 
     281             :     /*
     282             :      * Miscellaneous initialization
     283             :      *
     284             :      * create expression context for node
     285             :      */
     286        2225 :     ExecAssignExprContext(estate, &nlstate->js.ps);
     287             : 
     288             :     /*
     289             :      * initialize child expressions
     290             :      */
     291        2225 :     nlstate->js.ps.qual =
     292        2225 :         ExecInitQual(node->join.plan.qual, (PlanState *) nlstate);
     293        2225 :     nlstate->js.jointype = node->join.jointype;
     294        2225 :     nlstate->js.joinqual =
     295        2225 :         ExecInitQual(node->join.joinqual, (PlanState *) nlstate);
     296             : 
     297             :     /*
     298             :      * initialize child nodes
     299             :      *
     300             :      * If we have no parameters to pass into the inner rel from the outer,
     301             :      * tell the inner child that cheap rescans would be good.  If we do have
     302             :      * such parameters, then there is no point in REWIND support at all in the
     303             :      * inner child, because it will always be rescanned with fresh parameter
     304             :      * values.
     305             :      */
     306        2225 :     outerPlanState(nlstate) = ExecInitNode(outerPlan(node), estate, eflags);
     307        2225 :     if (node->nestParams == NIL)
     308        1198 :         eflags |= EXEC_FLAG_REWIND;
     309             :     else
     310        1027 :         eflags &= ~EXEC_FLAG_REWIND;
     311        2225 :     innerPlanState(nlstate) = ExecInitNode(innerPlan(node), estate, eflags);
     312             : 
     313             :     /*
     314             :      * tuple table initialization
     315             :      */
     316        2225 :     ExecInitResultTupleSlot(estate, &nlstate->js.ps);
     317             : 
     318             :     /*
     319             :      * detect whether we need only consider the first matching inner tuple
     320             :      */
     321        3542 :     nlstate->js.single_match = (node->join.inner_unique ||
     322        1317 :                                 node->join.jointype == JOIN_SEMI);
     323             : 
     324             :     /* set up null tuples for outer joins, if needed */
     325        2225 :     switch (node->join.jointype)
     326             :     {
     327             :         case JOIN_INNER:
     328             :         case JOIN_SEMI:
     329        1685 :             break;
     330             :         case JOIN_LEFT:
     331             :         case JOIN_ANTI:
     332         540 :             nlstate->nl_NullInnerTupleSlot =
     333         540 :                 ExecInitNullTupleSlot(estate,
     334         540 :                                       ExecGetResultType(innerPlanState(nlstate)));
     335         540 :             break;
     336             :         default:
     337           0 :             elog(ERROR, "unrecognized join type: %d",
     338             :                  (int) node->join.jointype);
     339             :     }
     340             : 
     341             :     /*
     342             :      * initialize tuple type and projection info
     343             :      */
     344        2225 :     ExecAssignResultTypeFromTL(&nlstate->js.ps);
     345        2225 :     ExecAssignProjectionInfo(&nlstate->js.ps, NULL);
     346             : 
     347             :     /*
     348             :      * finally, wipe the current outer tuple clean.
     349             :      */
     350        2225 :     nlstate->nl_NeedNewOuter = true;
     351        2225 :     nlstate->nl_MatchedOuter = false;
     352             : 
     353             :     NL1_printf("ExecInitNestLoop: %s\n",
     354             :                "node initialized");
     355             : 
     356        2225 :     return nlstate;
     357             : }
     358             : 
     359             : /* ----------------------------------------------------------------
     360             :  *      ExecEndNestLoop
     361             :  *
     362             :  *      closes down scans and frees allocated storage
     363             :  * ----------------------------------------------------------------
     364             :  */
     365             : void
     366        2221 : ExecEndNestLoop(NestLoopState *node)
     367             : {
     368             :     NL1_printf("ExecEndNestLoop: %s\n",
     369             :                "ending node processing");
     370             : 
     371             :     /*
     372             :      * Free the exprcontext
     373             :      */
     374        2221 :     ExecFreeExprContext(&node->js.ps);
     375             : 
     376             :     /*
     377             :      * clean out the tuple table
     378             :      */
     379        2221 :     ExecClearTuple(node->js.ps.ps_ResultTupleSlot);
     380             : 
     381             :     /*
     382             :      * close down subplans
     383             :      */
     384        2221 :     ExecEndNode(outerPlanState(node));
     385        2221 :     ExecEndNode(innerPlanState(node));
     386             : 
     387             :     NL1_printf("ExecEndNestLoop: %s\n",
     388             :                "node processing ended");
     389        2221 : }
     390             : 
     391             : /* ----------------------------------------------------------------
     392             :  *      ExecReScanNestLoop
     393             :  * ----------------------------------------------------------------
     394             :  */
     395             : void
     396         933 : ExecReScanNestLoop(NestLoopState *node)
     397             : {
     398         933 :     PlanState  *outerPlan = outerPlanState(node);
     399             : 
     400             :     /*
     401             :      * If outerPlan->chgParam is not null then plan will be automatically
     402             :      * re-scanned by first ExecProcNode.
     403             :      */
     404         933 :     if (outerPlan->chgParam == NULL)
     405          30 :         ExecReScan(outerPlan);
     406             : 
     407             :     /*
     408             :      * innerPlan is re-scanned for each new outer tuple and MUST NOT be
     409             :      * re-scanned from here or you'll get troubles from inner index scans when
     410             :      * outer Vars are used as run-time keys...
     411             :      */
     412             : 
     413         933 :     node->nl_NeedNewOuter = true;
     414         933 :     node->nl_MatchedOuter = false;
     415         933 : }

Generated by: LCOV version 1.11