LCOV - differential code coverage report
Current view: top level - src/backend/executor - nodeNestloop.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage HEAD vs 15 Lines: 98.0 % 99 97 2 97
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 4 4 4
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * nodeNestloop.c
       4                 :  *    routines to support nest-loop joins
       5                 :  *
       6                 :  * Portions Copyright (c) 1996-2023, 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 CBC     1391519 : ExecNestLoop(PlanState *pstate)
      62                 : {
      63         1391519 :     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         1391519 :     CHECK_FOR_INTERRUPTS();
      75                 : 
      76                 :     /*
      77                 :      * get information from the node
      78                 :      */
      79                 :     ENL1_printf("getting info from node");
      80                 : 
      81         1391519 :     nl = (NestLoop *) node->js.ps.plan;
      82         1391519 :     joinqual = node->js.joinqual;
      83         1391519 :     otherqual = node->js.ps.qual;
      84         1391519 :     outerPlan = outerPlanState(node);
      85         1391519 :     innerPlan = innerPlanState(node);
      86         1391519 :     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         1391519 :     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         5001986 :         if (node->nl_NeedNewOuter)
     107                 :         {
     108                 :             ENL1_printf("getting new outer tuple");
     109          641887 :             outerTupleSlot = ExecProcNode(outerPlan);
     110                 : 
     111                 :             /*
     112                 :              * if there are no more outer tuples, then the join is complete..
     113                 :              */
     114          641887 :             if (TupIsNull(outerTupleSlot))
     115                 :             {
     116                 :                 ENL1_printf("no outer tuple, ending join");
     117           36348 :                 return NULL;
     118                 :             }
     119                 : 
     120                 :             ENL1_printf("saving new outer tuple information");
     121          605539 :             econtext->ecxt_outertuple = outerTupleSlot;
     122          605539 :             node->nl_NeedNewOuter = false;
     123          605539 :             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          955039 :             foreach(lc, nl->nestParams)
     130                 :             {
     131          349500 :                 NestLoopParam *nlp = (NestLoopParam *) lfirst(lc);
     132          349500 :                 int         paramno = nlp->paramno;
     133                 :                 ParamExecData *prm;
     134                 : 
     135          349500 :                 prm = &(econtext->ecxt_param_exec_vals[paramno]);
     136                 :                 /* Param value should be an OUTER_VAR var */
     137          349500 :                 Assert(IsA(nlp->paramval, Var));
     138          349500 :                 Assert(nlp->paramval->varno == OUTER_VAR);
     139          349500 :                 Assert(nlp->paramval->varattno > 0);
     140          699000 :                 prm->value = slot_getattr(outerTupleSlot,
     141          349500 :                                           nlp->paramval->varattno,
     142                 :                                           &(prm->isnull));
     143                 :                 /* Flag parameter value as changed */
     144          349500 :                 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          605539 :             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         4965638 :         innerTupleSlot = ExecProcNode(innerPlan);
     161         4965613 :         econtext->ecxt_innertuple = innerTupleSlot;
     162                 : 
     163         4965613 :         if (TupIsNull(innerTupleSlot))
     164                 :         {
     165                 :             ENL1_printf("no inner tuple, need new outer tuple");
     166                 : 
     167          546278 :             node->nl_NeedNewOuter = true;
     168                 : 
     169          546278 :             if (!node->nl_MatchedOuter &&
     170          421129 :                 (node->js.jointype == JOIN_LEFT ||
     171          410850 :                  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          247603 :                 econtext->ecxt_innertuple = node->nl_NullInnerTupleSlot;
     180                 : 
     181                 :                 ENL1_printf("testing qualification for outer-join tuple");
     182                 : 
     183          247603 :                 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          246945 :                     return ExecProject(node->js.ps.ps_ProjInfo);
     193                 :                 }
     194                 :                 else
     195             658 :                     InstrCountFiltered2(node, 1);
     196                 :             }
     197                 : 
     198                 :             /*
     199                 :              * Otherwise just return to top of loop for a new outer tuple.
     200                 :              */
     201          299333 :             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         4419335 :         if (ExecQual(joinqual, econtext))
     215                 :         {
     216         1119684 :             node->nl_MatchedOuter = true;
     217                 : 
     218                 :             /* In an antijoin, we never return a matched tuple */
     219         1119684 :             if (node->js.jointype == JOIN_ANTI)
     220                 :             {
     221            6127 :                 node->nl_NeedNewOuter = true;
     222            6127 :                 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         1113557 :             if (node->js.single_match)
     231           53034 :                 node->nl_NeedNewOuter = true;
     232                 : 
     233         1113557 :             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         1108201 :                 return ExecProject(node->js.ps.ps_ProjInfo);
     242                 :             }
     243                 :             else
     244            5356 :                 InstrCountFiltered2(node, 1);
     245                 :         }
     246                 :         else
     247         3299651 :             InstrCountFiltered1(node, 1);
     248                 : 
     249                 :         /*
     250                 :          * Tuple fails qual, so free per-tuple memory and try again.
     251                 :          */
     252         3305007 :         ResetExprContext(econtext);
     253                 : 
     254                 :         ENL1_printf("qualification failed, looping");
     255                 :     }
     256                 : }
     257                 : 
     258                 : /* ----------------------------------------------------------------
     259                 :  *      ExecInitNestLoop
     260                 :  * ----------------------------------------------------------------
     261                 :  */
     262                 : NestLoopState *
     263           34217 : ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
     264                 : {
     265                 :     NestLoopState *nlstate;
     266                 : 
     267                 :     /* check for unsupported flags */
     268           34217 :     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           34217 :     nlstate = makeNode(NestLoopState);
     277           34217 :     nlstate->js.ps.plan = (Plan *) node;
     278           34217 :     nlstate->js.ps.state = estate;
     279           34217 :     nlstate->js.ps.ExecProcNode = ExecNestLoop;
     280                 : 
     281                 :     /*
     282                 :      * Miscellaneous initialization
     283                 :      *
     284                 :      * create expression context for node
     285                 :      */
     286           34217 :     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           34217 :     outerPlanState(nlstate) = ExecInitNode(outerPlan(node), estate, eflags);
     298           34217 :     if (node->nestParams == NIL)
     299           17181 :         eflags |= EXEC_FLAG_REWIND;
     300                 :     else
     301           17036 :         eflags &= ~EXEC_FLAG_REWIND;
     302           34217 :     innerPlanState(nlstate) = ExecInitNode(innerPlan(node), estate, eflags);
     303                 : 
     304                 :     /*
     305                 :      * Initialize result slot, type and projection.
     306                 :      */
     307           34217 :     ExecInitResultTupleSlotTL(&nlstate->js.ps, &TTSOpsVirtual);
     308           34217 :     ExecAssignProjectionInfo(&nlstate->js.ps, NULL);
     309                 : 
     310                 :     /*
     311                 :      * initialize child expressions
     312                 :      */
     313           34217 :     nlstate->js.ps.qual =
     314           34217 :         ExecInitQual(node->join.plan.qual, (PlanState *) nlstate);
     315           34217 :     nlstate->js.jointype = node->join.jointype;
     316           34217 :     nlstate->js.joinqual =
     317           34217 :         ExecInitQual(node->join.joinqual, (PlanState *) nlstate);
     318                 : 
     319                 :     /*
     320                 :      * detect whether we need only consider the first matching inner tuple
     321                 :      */
     322           52035 :     nlstate->js.single_match = (node->join.inner_unique ||
     323           17818 :                                 node->join.jointype == JOIN_SEMI);
     324                 : 
     325                 :     /* set up null tuples for outer joins, if needed */
     326           34217 :     switch (node->join.jointype)
     327                 :     {
     328           24727 :         case JOIN_INNER:
     329                 :         case JOIN_SEMI:
     330           24727 :             break;
     331            9490 :         case JOIN_LEFT:
     332                 :         case JOIN_ANTI:
     333            9490 :             nlstate->nl_NullInnerTupleSlot =
     334            9490 :                 ExecInitNullTupleSlot(estate,
     335            9490 :                                       ExecGetResultType(innerPlanState(nlstate)),
     336                 :                                       &TTSOpsVirtual);
     337            9490 :             break;
     338 UBC           0 :         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 CBC       34217 :     nlstate->nl_NeedNewOuter = true;
     347           34217 :     nlstate->nl_MatchedOuter = false;
     348                 : 
     349                 :     NL1_printf("ExecInitNestLoop: %s\n",
     350                 :                "node initialized");
     351                 : 
     352           34217 :     return nlstate;
     353                 : }
     354                 : 
     355                 : /* ----------------------------------------------------------------
     356                 :  *      ExecEndNestLoop
     357                 :  *
     358                 :  *      closes down scans and frees allocated storage
     359                 :  * ----------------------------------------------------------------
     360                 :  */
     361                 : void
     362           34120 : ExecEndNestLoop(NestLoopState *node)
     363                 : {
     364                 :     NL1_printf("ExecEndNestLoop: %s\n",
     365                 :                "ending node processing");
     366                 : 
     367                 :     /*
     368                 :      * Free the exprcontext
     369                 :      */
     370           34120 :     ExecFreeExprContext(&node->js.ps);
     371                 : 
     372                 :     /*
     373                 :      * clean out the tuple table
     374                 :      */
     375           34120 :     ExecClearTuple(node->js.ps.ps_ResultTupleSlot);
     376                 : 
     377                 :     /*
     378                 :      * close down subplans
     379                 :      */
     380           34120 :     ExecEndNode(outerPlanState(node));
     381           34120 :     ExecEndNode(innerPlanState(node));
     382                 : 
     383                 :     NL1_printf("ExecEndNestLoop: %s\n",
     384                 :                "node processing ended");
     385           34120 : }
     386                 : 
     387                 : /* ----------------------------------------------------------------
     388                 :  *      ExecReScanNestLoop
     389                 :  * ----------------------------------------------------------------
     390                 :  */
     391                 : void
     392            5323 : ExecReScanNestLoop(NestLoopState *node)
     393                 : {
     394            5323 :     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            5323 :     if (outerPlan->chgParam == NULL)
     401             260 :         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            5323 :     node->nl_NeedNewOuter = true;
     410            5323 :     node->nl_MatchedOuter = false;
     411            5323 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a