LCOV - code coverage report
Current view: top level - src/backend/executor - nodeGroup.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 58 62 93.5 %
Date: 2023-12-02 15:10:53 Functions: 4 4 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * nodeGroup.c
       4             :  *    Routines to handle group nodes (used for queries with GROUP BY clause).
       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             :  * DESCRIPTION
      11             :  *    The Group node is designed for handling queries with a GROUP BY clause.
      12             :  *    Its outer plan must deliver tuples that are sorted in the order
      13             :  *    specified by the grouping columns (ie. tuples from the same group are
      14             :  *    consecutive).  That way, we just have to compare adjacent tuples to
      15             :  *    locate group boundaries.
      16             :  *
      17             :  * IDENTIFICATION
      18             :  *    src/backend/executor/nodeGroup.c
      19             :  *
      20             :  *-------------------------------------------------------------------------
      21             :  */
      22             : 
      23             : #include "postgres.h"
      24             : 
      25             : #include "executor/executor.h"
      26             : #include "executor/nodeGroup.h"
      27             : #include "miscadmin.h"
      28             : #include "utils/memutils.h"
      29             : 
      30             : 
      31             : /*
      32             :  *   ExecGroup -
      33             :  *
      34             :  *      Return one tuple for each group of matching input tuples.
      35             :  */
      36             : static TupleTableSlot *
      37        7958 : ExecGroup(PlanState *pstate)
      38             : {
      39        7958 :     GroupState *node = castNode(GroupState, pstate);
      40             :     ExprContext *econtext;
      41             :     TupleTableSlot *firsttupleslot;
      42             :     TupleTableSlot *outerslot;
      43             : 
      44        7958 :     CHECK_FOR_INTERRUPTS();
      45             : 
      46             :     /*
      47             :      * get state info from node
      48             :      */
      49        7958 :     if (node->grp_done)
      50           0 :         return NULL;
      51        7958 :     econtext = node->ss.ps.ps_ExprContext;
      52             : 
      53             :     /*
      54             :      * The ScanTupleSlot holds the (copied) first tuple of each group.
      55             :      */
      56        7958 :     firsttupleslot = node->ss.ss_ScanTupleSlot;
      57             : 
      58             :     /*
      59             :      * We need not call ResetExprContext here because ExecQualAndReset() will
      60             :      * reset the per-tuple memory context once per input tuple.
      61             :      */
      62             : 
      63             :     /*
      64             :      * If first time through, acquire first input tuple and determine whether
      65             :      * to return it or not.
      66             :      */
      67        7958 :     if (TupIsNull(firsttupleslot))
      68             :     {
      69         160 :         outerslot = ExecProcNode(outerPlanState(node));
      70         160 :         if (TupIsNull(outerslot))
      71             :         {
      72             :             /* empty input, so return nothing */
      73          38 :             node->grp_done = true;
      74          38 :             return NULL;
      75             :         }
      76             :         /* Copy tuple into firsttupleslot */
      77         122 :         ExecCopySlot(firsttupleslot, outerslot);
      78             : 
      79             :         /*
      80             :          * Set it up as input for qual test and projection.  The expressions
      81             :          * will access the input tuple as varno OUTER.
      82             :          */
      83         122 :         econtext->ecxt_outertuple = firsttupleslot;
      84             : 
      85             :         /*
      86             :          * Check the qual (HAVING clause); if the group does not match, ignore
      87             :          * it and fall into scan loop.
      88             :          */
      89         122 :         if (ExecQual(node->ss.ps.qual, econtext))
      90             :         {
      91             :             /*
      92             :              * Form and return a projection tuple using the first input tuple.
      93             :              */
      94         122 :             return ExecProject(node->ss.ps.ps_ProjInfo);
      95             :         }
      96             :         else
      97           0 :             InstrCountFiltered1(node, 1);
      98             :     }
      99             : 
     100             :     /*
     101             :      * This loop iterates once per input tuple group.  At the head of the
     102             :      * loop, we have finished processing the first tuple of the group and now
     103             :      * need to scan over all the other group members.
     104             :      */
     105           0 :     for (;;)
     106             :     {
     107             :         /*
     108             :          * Scan over all remaining tuples that belong to this group
     109             :          */
     110             :         for (;;)
     111             :         {
     112       37688 :             outerslot = ExecProcNode(outerPlanState(node));
     113       37688 :             if (TupIsNull(outerslot))
     114             :             {
     115             :                 /* no more groups, so we're done */
     116         122 :                 node->grp_done = true;
     117         122 :                 return NULL;
     118             :             }
     119             : 
     120             :             /*
     121             :              * Compare with first tuple and see if this tuple is of the same
     122             :              * group.  If so, ignore it and keep scanning.
     123             :              */
     124       37566 :             econtext->ecxt_innertuple = firsttupleslot;
     125       37566 :             econtext->ecxt_outertuple = outerslot;
     126       37566 :             if (!ExecQualAndReset(node->eqfunction, econtext))
     127        7676 :                 break;
     128             :         }
     129             : 
     130             :         /*
     131             :          * We have the first tuple of the next input group.  See if we want to
     132             :          * return it.
     133             :          */
     134             :         /* Copy tuple, set up as input for qual test and projection */
     135        7676 :         ExecCopySlot(firsttupleslot, outerslot);
     136        7676 :         econtext->ecxt_outertuple = firsttupleslot;
     137             : 
     138             :         /*
     139             :          * Check the qual (HAVING clause); if the group does not match, ignore
     140             :          * it and loop back to scan the rest of the group.
     141             :          */
     142        7676 :         if (ExecQual(node->ss.ps.qual, econtext))
     143             :         {
     144             :             /*
     145             :              * Form and return a projection tuple using the first input tuple.
     146             :              */
     147        7676 :             return ExecProject(node->ss.ps.ps_ProjInfo);
     148             :         }
     149             :         else
     150           0 :             InstrCountFiltered1(node, 1);
     151             :     }
     152             : }
     153             : 
     154             : /* -----------------
     155             :  * ExecInitGroup
     156             :  *
     157             :  *  Creates the run-time information for the group node produced by the
     158             :  *  planner and initializes its outer subtree
     159             :  * -----------------
     160             :  */
     161             : GroupState *
     162         222 : ExecInitGroup(Group *node, EState *estate, int eflags)
     163             : {
     164             :     GroupState *grpstate;
     165             :     const TupleTableSlotOps *tts_ops;
     166             : 
     167             :     /* check for unsupported flags */
     168             :     Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
     169             : 
     170             :     /*
     171             :      * create state structure
     172             :      */
     173         222 :     grpstate = makeNode(GroupState);
     174         222 :     grpstate->ss.ps.plan = (Plan *) node;
     175         222 :     grpstate->ss.ps.state = estate;
     176         222 :     grpstate->ss.ps.ExecProcNode = ExecGroup;
     177         222 :     grpstate->grp_done = false;
     178             : 
     179             :     /*
     180             :      * create expression context
     181             :      */
     182         222 :     ExecAssignExprContext(estate, &grpstate->ss.ps);
     183             : 
     184             :     /*
     185             :      * initialize child nodes
     186             :      */
     187         222 :     outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags);
     188             : 
     189             :     /*
     190             :      * Initialize scan slot and type.
     191             :      */
     192         222 :     tts_ops = ExecGetResultSlotOps(outerPlanState(&grpstate->ss), NULL);
     193         222 :     ExecCreateScanSlotFromOuterPlan(estate, &grpstate->ss, tts_ops);
     194             : 
     195             :     /*
     196             :      * Initialize result slot, type and projection.
     197             :      */
     198         222 :     ExecInitResultTupleSlotTL(&grpstate->ss.ps, &TTSOpsVirtual);
     199         222 :     ExecAssignProjectionInfo(&grpstate->ss.ps, NULL);
     200             : 
     201             :     /*
     202             :      * initialize child expressions
     203             :      */
     204         222 :     grpstate->ss.ps.qual =
     205         222 :         ExecInitQual(node->plan.qual, (PlanState *) grpstate);
     206             : 
     207             :     /*
     208             :      * Precompute fmgr lookup data for inner loop
     209             :      */
     210         222 :     grpstate->eqfunction =
     211         222 :         execTuplesMatchPrepare(ExecGetResultType(outerPlanState(grpstate)),
     212             :                                node->numCols,
     213         222 :                                node->grpColIdx,
     214         222 :                                node->grpOperators,
     215         222 :                                node->grpCollations,
     216             :                                &grpstate->ss.ps);
     217             : 
     218         222 :     return grpstate;
     219             : }
     220             : 
     221             : /* ------------------------
     222             :  *      ExecEndGroup(node)
     223             :  *
     224             :  * -----------------------
     225             :  */
     226             : void
     227         222 : ExecEndGroup(GroupState *node)
     228             : {
     229             :     PlanState  *outerPlan;
     230             : 
     231         222 :     outerPlan = outerPlanState(node);
     232         222 :     ExecEndNode(outerPlan);
     233         222 : }
     234             : 
     235             : void
     236          24 : ExecReScanGroup(GroupState *node)
     237             : {
     238          24 :     PlanState  *outerPlan = outerPlanState(node);
     239             : 
     240          24 :     node->grp_done = false;
     241             :     /* must clear first tuple */
     242          24 :     ExecClearTuple(node->ss.ss_ScanTupleSlot);
     243             : 
     244             :     /*
     245             :      * if chgParam of subnode is not null then plan will be re-scanned by
     246             :      * first ExecProcNode.
     247             :      */
     248          24 :     if (outerPlan->chgParam == NULL)
     249          24 :         ExecReScan(outerPlan);
     250          24 : }

Generated by: LCOV version 1.14