LCOV - code coverage report
Current view: top level - src/backend/executor - nodeNestloop.c (source / functions) Hit Total Coverage
Test: PostgreSQL 12beta2 Lines: 91 92 98.9 %
Date: 2019-06-18 07:06:57 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-2019, 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     3649820 : ExecNestLoop(PlanState *pstate)
      62             : {
      63     3649820 :     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     3649820 :     CHECK_FOR_INTERRUPTS();
      75             : 
      76             :     /*
      77             :      * get information from the node
      78             :      */
      79             :     ENL1_printf("getting info from node");
      80             : 
      81     3649820 :     nl = (NestLoop *) node->js.ps.plan;
      82     3649820 :     joinqual = node->js.joinqual;
      83     3649820 :     otherqual = node->js.ps.qual;
      84     3649820 :     outerPlan = outerPlanState(node);
      85     3649820 :     innerPlan = innerPlanState(node);
      86     3649820 :     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     3649820 :     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    20104956 :         if (node->nl_NeedNewOuter)
     107             :         {
     108             :             ENL1_printf("getting new outer tuple");
     109      951814 :             outerTupleSlot = ExecProcNode(outerPlan);
     110             : 
     111             :             /*
     112             :              * if there are no more outer tuples, then the join is complete..
     113             :              */
     114      951814 :             if (TupIsNull(outerTupleSlot))
     115             :             {
     116             :                 ENL1_printf("no outer tuple, ending join");
     117       70512 :                 return NULL;
     118             :             }
     119             : 
     120             :             ENL1_printf("saving new outer tuple information");
     121      881302 :             econtext->ecxt_outertuple = outerTupleSlot;
     122      881302 :             node->nl_NeedNewOuter = false;
     123      881302 :             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     1076338 :             foreach(lc, nl->nestParams)
     130             :             {
     131      195036 :                 NestLoopParam *nlp = (NestLoopParam *) lfirst(lc);
     132      195036 :                 int         paramno = nlp->paramno;
     133             :                 ParamExecData *prm;
     134             : 
     135      195036 :                 prm = &(econtext->ecxt_param_exec_vals[paramno]);
     136             :                 /* Param value should be an OUTER_VAR var */
     137             :                 Assert(IsA(nlp->paramval, Var));
     138             :                 Assert(nlp->paramval->varno == OUTER_VAR);
     139             :                 Assert(nlp->paramval->varattno > 0);
     140      390072 :                 prm->value = slot_getattr(outerTupleSlot,
     141      195036 :                                           nlp->paramval->varattno,
     142             :                                           &(prm->isnull));
     143             :                 /* Flag parameter value as changed */
     144      195036 :                 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      881302 :             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    11806876 :         innerTupleSlot = ExecProcNode(innerPlan);
     161    11806868 :         econtext->ecxt_innertuple = innerTupleSlot;
     162             : 
     163    11806868 :         if (TupIsNull(innerTupleSlot))
     164             :         {
     165             :             ENL1_printf("no inner tuple, need new outer tuple");
     166             : 
     167      823918 :             node->nl_NeedNewOuter = true;
     168             : 
     169     1551424 :             if (!node->nl_MatchedOuter &&
     170     1025330 :                 (node->js.jointype == JOIN_LEFT ||
     171      297824 :                  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      664150 :                 econtext->ecxt_innertuple = node->nl_NullInnerTupleSlot;
     180             : 
     181             :                 ENL1_printf("testing qualification for outer-join tuple");
     182             : 
     183      664150 :                 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      245162 :                     return ExecProject(node->js.ps.ps_ProjInfo);
     193             :                 }
     194             :                 else
     195      418988 :                     InstrCountFiltered2(node, 1);
     196             :             }
     197             : 
     198             :             /*
     199             :              * Otherwise just return to top of loop for a new outer tuple.
     200             :              */
     201      578756 :             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    10982950 :         if (ExecQual(joinqual, econtext))
     215             :         {
     216     3352834 :             node->nl_MatchedOuter = true;
     217             : 
     218             :             /* In an antijoin, we never return a matched tuple */
     219     3352834 :             if (node->js.jointype == JOIN_ANTI)
     220             :             {
     221       11654 :                 node->nl_NeedNewOuter = true;
     222       11654 :                 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     3341180 :             if (node->js.single_match)
     231       45566 :                 node->nl_NeedNewOuter = true;
     232             : 
     233     3341180 :             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     3334138 :                 return ExecProject(node->js.ps.ps_ProjInfo);
     242             :             }
     243             :             else
     244        7042 :                 InstrCountFiltered2(node, 1);
     245             :         }
     246             :         else
     247     7630116 :             InstrCountFiltered1(node, 1);
     248             : 
     249             :         /*
     250             :          * Tuple fails qual, so free per-tuple memory and try again.
     251             :          */
     252     7637158 :         ResetExprContext(econtext);
     253             : 
     254             :         ENL1_printf("qualification failed, looping");
     255             :     }
     256             : }
     257             : 
     258             : /* ----------------------------------------------------------------
     259             :  *      ExecInitNestLoop
     260             :  * ----------------------------------------------------------------
     261             :  */
     262             : NestLoopState *
     263       31042 : ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
     264             : {
     265             :     NestLoopState *nlstate;
     266             : 
     267             :     /* check for unsupported flags */
     268             :     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       31042 :     nlstate = makeNode(NestLoopState);
     277       31042 :     nlstate->js.ps.plan = (Plan *) node;
     278       31042 :     nlstate->js.ps.state = estate;
     279       31042 :     nlstate->js.ps.ExecProcNode = ExecNestLoop;
     280             : 
     281             :     /*
     282             :      * Miscellaneous initialization
     283             :      *
     284             :      * create expression context for node
     285             :      */
     286       31042 :     ExecAssignExprContext(estate, &nlstate->js.ps);
     287             : 
     288             :     /*
     289             :      * initialize child nodes
     290             :      *
     291             :      * If we have no parameters to pass into the inner rel from the outer,
     292             :      * tell the inner child that cheap rescans would be good.  If we do have
     293             :      * such parameters, then there is no point in REWIND support at all in the
     294             :      * inner child, because it will always be rescanned with fresh parameter
     295             :      * values.
     296             :      */
     297       31042 :     outerPlanState(nlstate) = ExecInitNode(outerPlan(node), estate, eflags);
     298       31042 :     if (node->nestParams == NIL)
     299       14294 :         eflags |= EXEC_FLAG_REWIND;
     300             :     else
     301       16748 :         eflags &= ~EXEC_FLAG_REWIND;
     302       31042 :     innerPlanState(nlstate) = ExecInitNode(innerPlan(node), estate, eflags);
     303             : 
     304             :     /*
     305             :      * Initialize result slot, type and projection.
     306             :      */
     307       31042 :     ExecInitResultTupleSlotTL(&nlstate->js.ps, &TTSOpsVirtual);
     308       31042 :     ExecAssignProjectionInfo(&nlstate->js.ps, NULL);
     309             : 
     310             :     /*
     311             :      * initialize child expressions
     312             :      */
     313       31042 :     nlstate->js.ps.qual =
     314       31042 :         ExecInitQual(node->join.plan.qual, (PlanState *) nlstate);
     315       31042 :     nlstate->js.jointype = node->join.jointype;
     316       31042 :     nlstate->js.joinqual =
     317       31042 :         ExecInitQual(node->join.joinqual, (PlanState *) nlstate);
     318             : 
     319             :     /*
     320             :      * detect whether we need only consider the first matching inner tuple
     321             :      */
     322       45884 :     nlstate->js.single_match = (node->join.inner_unique ||
     323       14842 :                                 node->join.jointype == JOIN_SEMI);
     324             : 
     325             :     /* set up null tuples for outer joins, if needed */
     326       31042 :     switch (node->join.jointype)
     327             :     {
     328             :         case JOIN_INNER:
     329             :         case JOIN_SEMI:
     330       16976 :             break;
     331             :         case JOIN_LEFT:
     332             :         case JOIN_ANTI:
     333       14066 :             nlstate->nl_NullInnerTupleSlot =
     334       14066 :                 ExecInitNullTupleSlot(estate,
     335       14066 :                                       ExecGetResultType(innerPlanState(nlstate)),
     336             :                                       &TTSOpsVirtual);
     337       14066 :             break;
     338             :         default:
     339           0 :             elog(ERROR, "unrecognized join type: %d",
     340             :                  (int) node->join.jointype);
     341             :     }
     342             : 
     343             :     /*
     344             :      * finally, wipe the current outer tuple clean.
     345             :      */
     346       31042 :     nlstate->nl_NeedNewOuter = true;
     347       31042 :     nlstate->nl_MatchedOuter = false;
     348             : 
     349             :     NL1_printf("ExecInitNestLoop: %s\n",
     350             :                "node initialized");
     351             : 
     352       31042 :     return nlstate;
     353             : }
     354             : 
     355             : /* ----------------------------------------------------------------
     356             :  *      ExecEndNestLoop
     357             :  *
     358             :  *      closes down scans and frees allocated storage
     359             :  * ----------------------------------------------------------------
     360             :  */
     361             : void
     362       30962 : ExecEndNestLoop(NestLoopState *node)
     363             : {
     364             :     NL1_printf("ExecEndNestLoop: %s\n",
     365             :                "ending node processing");
     366             : 
     367             :     /*
     368             :      * Free the exprcontext
     369             :      */
     370       30962 :     ExecFreeExprContext(&node->js.ps);
     371             : 
     372             :     /*
     373             :      * clean out the tuple table
     374             :      */
     375       30962 :     ExecClearTuple(node->js.ps.ps_ResultTupleSlot);
     376             : 
     377             :     /*
     378             :      * close down subplans
     379             :      */
     380       30962 :     ExecEndNode(outerPlanState(node));
     381       30962 :     ExecEndNode(innerPlanState(node));
     382             : 
     383             :     NL1_printf("ExecEndNestLoop: %s\n",
     384             :                "node processing ended");
     385       30962 : }
     386             : 
     387             : /* ----------------------------------------------------------------
     388             :  *      ExecReScanNestLoop
     389             :  * ----------------------------------------------------------------
     390             :  */
     391             : void
     392       42790 : ExecReScanNestLoop(NestLoopState *node)
     393             : {
     394       42790 :     PlanState  *outerPlan = outerPlanState(node);
     395             : 
     396             :     /*
     397             :      * If outerPlan->chgParam is not null then plan will be automatically
     398             :      * re-scanned by first ExecProcNode.
     399             :      */
     400       42790 :     if (outerPlan->chgParam == NULL)
     401         284 :         ExecReScan(outerPlan);
     402             : 
     403             :     /*
     404             :      * innerPlan is re-scanned for each new outer tuple and MUST NOT be
     405             :      * re-scanned from here or you'll get troubles from inner index scans when
     406             :      * outer Vars are used as run-time keys...
     407             :      */
     408             : 
     409       42790 :     node->nl_NeedNewOuter = true;
     410       42790 :     node->nl_MatchedOuter = false;
     411       42790 : }

Generated by: LCOV version 1.13