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

Generated by: LCOV version 2.0-1