]> granicus.if.org Git - postgresql/blobdiff - src/backend/executor/nodeGroup.c
Make some small planner API cleanups.
[postgresql] / src / backend / executor / nodeGroup.c
index 55659a90254d28f67850bb127901c019a245db6e..655084d7b564858b1a447b79be6905acd9352bf6 100644 (file)
  * nodeGroup.c
  *       Routines to handle group nodes (used for queries with GROUP BY clause).
  *
- * Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * DESCRIPTION
  *       The Group node is designed for handling queries with a GROUP BY clause.
- *       It's outer plan must be a sort node. It assumes that the tuples it gets
- *       back from the outer plan is sorted in the order specified by the group
- *       columns. (ie. tuples from the same group are consecutive)
+ *       Its outer plan must deliver tuples that are sorted in the order
+ *       specified by the grouping columns (ie. tuples from the same group are
+ *       consecutive).  That way, we just have to compare adjacent tuples to
+ *       locate group boundaries.
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.26 1999/05/25 16:08:41 momjian Exp $
+ *       src/backend/executor/nodeGroup.c
  *
  *-------------------------------------------------------------------------
  */
-#include <string.h>
 
 #include "postgres.h"
-#include "fmgr.h"
 
-#include "access/heapam.h"
-#include "catalog/catalog.h"
-#include "access/printtup.h"
 #include "executor/executor.h"
 #include "executor/nodeGroup.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
-static TupleTableSlot *ExecGroupEveryTuple(Group *node);
-static TupleTableSlot *ExecGroupOneTuple(Group *node);
-static bool sameGroup(HeapTuple oldslot, HeapTuple newslot,
-                 int numCols, AttrNumber *grpColIdx, TupleDesc tupdesc);
 
-/* ---------------------------------------
+/*
  *      ExecGroup -
  *
- *             There are two modes in which tuples are returned by ExecGroup. If
- *             tuplePerGroup is TRUE, every tuple from the same group will be
- *             returned, followed by a NULL at the end of each group. This is
- *             useful for Agg node which needs to aggregate over tuples of the same
- *             group. (eg. SELECT salary, count{*} FROM emp GROUP BY salary)
- *
- *             If tuplePerGroup is FALSE, only one tuple per group is returned. The
- *             tuple returned contains only the group columns. NULL is returned only
- *             at the end when no more groups is present. This is useful when
- *             the query does not involve aggregates. (eg. SELECT salary FROM emp
- *             GROUP BY salary)
- * ------------------------------------------
- */
-TupleTableSlot *
-ExecGroup(Group *node)
-{
-       if (node->tuplePerGroup)
-               return ExecGroupEveryTuple(node);
-       else
-               return ExecGroupOneTuple(node);
-}
-
-/*
- * ExecGroupEveryTuple -
- *      return every tuple with a NULL between each group
+ *             Return one tuple for each group of matching input tuples.
  */
 static TupleTableSlot *
-ExecGroupEveryTuple(Group *node)
+ExecGroup(PlanState *pstate)
 {
-       GroupState *grpstate;
-       EState     *estate;
+       GroupState *node = castNode(GroupState, pstate);
        ExprContext *econtext;
-
-       HeapTuple       outerTuple = NULL;
-       HeapTuple       firsttuple;
+       TupleTableSlot *firsttupleslot;
        TupleTableSlot *outerslot;
-       ProjectionInfo *projInfo;
-       TupleTableSlot *resultSlot;
 
-       bool            isDone;
+       CHECK_FOR_INTERRUPTS();
 
-       /* ---------------------
-        *      get state info from node
-        * ---------------------
+       /*
+        * get state info from node
         */
-       grpstate = node->grpstate;
-       if (grpstate->grp_done)
+       if (node->grp_done)
                return NULL;
+       econtext = node->ss.ps.ps_ExprContext;
 
-       estate = node->plan.state;
-
-       econtext = grpstate->csstate.cstate.cs_ExprContext;
+       /*
+        * The ScanTupleSlot holds the (copied) first tuple of each group.
+        */
+       firsttupleslot = node->ss.ss_ScanTupleSlot;
 
-       /* if we haven't returned first tuple of new group yet ... */
-       if (grpstate->grp_useFirstTuple)
-       {
-               grpstate->grp_useFirstTuple = FALSE;
+       /*
+        * We need not call ResetExprContext here because ExecQualAndReset() will
+        * reset the per-tuple memory context once per input tuple.
+        */
 
-               ExecStoreTuple(grpstate->grp_firstTuple,
-                                          grpstate->csstate.css_ScanTupleSlot,
-                                          InvalidBuffer,
-                                          false);
-       }
-       else
+       /*
+        * If first time through, acquire first input tuple and determine whether
+        * to return it or not.
+        */
+       if (TupIsNull(firsttupleslot))
        {
-               outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
+               outerslot = ExecProcNode(outerPlanState(node));
                if (TupIsNull(outerslot))
                {
-                       grpstate->grp_done = TRUE;
+                       /* empty input, so return nothing */
+                       node->grp_done = true;
                        return NULL;
                }
-               outerTuple = outerslot->val;
+               /* Copy tuple into firsttupleslot */
+               ExecCopySlot(firsttupleslot, outerslot);
 
-               firsttuple = grpstate->grp_firstTuple;
-               /* this should occur on the first call only */
-               if (firsttuple == NULL)
-                       grpstate->grp_firstTuple = heap_copytuple(outerTuple);
-               else
-               {
+               /*
+                * Set it up as input for qual test and projection.  The expressions
+                * will access the input tuple as varno OUTER.
+                */
+               econtext->ecxt_outertuple = firsttupleslot;
 
+               /*
+                * Check the qual (HAVING clause); if the group does not match, ignore
+                * it and fall into scan loop.
+                */
+               if (ExecQual(node->ss.ps.qual, econtext))
+               {
                        /*
-                        * Compare with first tuple and see if this tuple is of the
-                        * same group.
+                        * Form and return a projection tuple using the first input tuple.
                         */
-                       if (!sameGroup(firsttuple, outerTuple,
-                                                  node->numCols, node->grpColIdx,
-                                                  ExecGetScanType(&grpstate->csstate)))
-                       {
-                               grpstate->grp_useFirstTuple = TRUE;
-                               pfree(firsttuple);
-                               grpstate->grp_firstTuple = heap_copytuple(outerTuple);
-
-                               return NULL;    /* signifies the end of the group */
-                       }
+                       return ExecProject(node->ss.ps.ps_ProjInfo);
                }
-
-               ExecStoreTuple(outerTuple,
-                                          grpstate->csstate.css_ScanTupleSlot,
-                                          outerslot->ttc_buffer,
-                                          false);
-       }
-
-       /* ----------------
-        *      form a projection tuple, store it in the result tuple
-        *      slot and return it.
-        * ----------------
-        */
-       projInfo = grpstate->csstate.cstate.cs_ProjInfo;
-
-       econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
-       resultSlot = ExecProject(projInfo, &isDone);
-
-       return resultSlot;
-}
-
-/*
- * ExecGroupOneTuple -
- *       returns one tuple per group, a NULL at the end when there are no more
- *       tuples.
- */
-static TupleTableSlot *
-ExecGroupOneTuple(Group *node)
-{
-       GroupState *grpstate;
-       EState     *estate;
-       ExprContext *econtext;
-
-       HeapTuple       outerTuple = NULL;
-       HeapTuple       firsttuple;
-       TupleTableSlot *outerslot;
-       ProjectionInfo *projInfo;
-       TupleTableSlot *resultSlot;
-
-       bool            isDone;
-
-       /* ---------------------
-        *      get state info from node
-        * ---------------------
-        */
-       grpstate = node->grpstate;
-       if (grpstate->grp_done)
-               return NULL;
-
-       estate = node->plan.state;
-
-       econtext = node->grpstate->csstate.cstate.cs_ExprContext;
-
-       firsttuple = grpstate->grp_firstTuple;
-       /* this should occur on the first call only */
-       if (firsttuple == NULL)
-       {
-               outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
-               if (TupIsNull(outerslot))
-               {
-                       grpstate->grp_done = TRUE;
-                       return NULL;
-               }
-               grpstate->grp_firstTuple = firsttuple =
-                       heap_copytuple(outerslot->val);
+               else
+                       InstrCountFiltered1(node, 1);
        }
 
        /*
-        * find all tuples that belong to a group
+        * This loop iterates once per input tuple group.  At the head of the
+        * loop, we have finished processing the first tuple of the group and now
+        * need to scan over all the other group members.
         */
        for (;;)
        {
-               outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
-               if (TupIsNull(outerslot))
+               /*
+                * Scan over all remaining tuples that belong to this group
+                */
+               for (;;)
                {
-                       grpstate->grp_done = TRUE;
-                       outerTuple = NULL;
-                       break;
+                       outerslot = ExecProcNode(outerPlanState(node));
+                       if (TupIsNull(outerslot))
+                       {
+                               /* no more groups, so we're done */
+                               node->grp_done = true;
+                               return NULL;
+                       }
+
+                       /*
+                        * Compare with first tuple and see if this tuple is of the same
+                        * group.  If so, ignore it and keep scanning.
+                        */
+                       econtext->ecxt_innertuple = firsttupleslot;
+                       econtext->ecxt_outertuple = outerslot;
+                       if (!ExecQualAndReset(node->eqfunction, econtext))
+                               break;
                }
-               outerTuple = outerslot->val;
 
-               /* ----------------
-                *      Compare with first tuple and see if this tuple is of
-                *      the same group.
-                * ----------------
+               /*
+                * We have the first tuple of the next input group.  See if we want to
+                * return it.
                 */
-               if ((!sameGroup(firsttuple, outerTuple,
-                                               node->numCols, node->grpColIdx,
-                                               ExecGetScanType(&grpstate->csstate))))
-                       break;
-       }
+               /* Copy tuple, set up as input for qual test and projection */
+               ExecCopySlot(firsttupleslot, outerslot);
+               econtext->ecxt_outertuple = firsttupleslot;
 
-       /* ----------------
-        *      form a projection tuple, store it in the result tuple
-        *      slot and return it.
-        * ----------------
-        */
-       projInfo = grpstate->csstate.cstate.cs_ProjInfo;
-
-       ExecStoreTuple(firsttuple,
-                                  grpstate->csstate.css_ScanTupleSlot,
-                                  InvalidBuffer,
-                                  false);
-       econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
-       resultSlot = ExecProject(projInfo, &isDone);
-
-       /* save outerTuple if we are not done yet */
-       if (!grpstate->grp_done)
-       {
-               pfree(firsttuple);
-               grpstate->grp_firstTuple = heap_copytuple(outerTuple);
+               /*
+                * Check the qual (HAVING clause); if the group does not match, ignore
+                * it and loop back to scan the rest of the group.
+                */
+               if (ExecQual(node->ss.ps.qual, econtext))
+               {
+                       /*
+                        * Form and return a projection tuple using the first input tuple.
+                        */
+                       return ExecProject(node->ss.ps.ps_ProjInfo);
+               }
+               else
+                       InstrCountFiltered1(node, 1);
        }
-
-       return resultSlot;
 }
 
 /* -----------------
@@ -253,66 +158,63 @@ ExecGroupOneTuple(Group *node)
  *     planner and initializes its outer subtree
  * -----------------
  */
-bool
-ExecInitGroup(Group *node, EState *estate, Plan *parent)
+GroupState *
+ExecInitGroup(Group *node, EState *estate, int eflags)
 {
        GroupState *grpstate;
-       Plan       *outerPlan;
+       const TupleTableSlotOps *tts_ops;
 
-       /*
-        * assign the node's execution state
-        */
-       node->plan.state = estate;
+       /* check for unsupported flags */
+       Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
 
        /*
         * create state structure
         */
        grpstate = makeNode(GroupState);
-       node->grpstate = grpstate;
-       grpstate->grp_useFirstTuple = FALSE;
-       grpstate->grp_done = FALSE;
+       grpstate->ss.ps.plan = (Plan *) node;
+       grpstate->ss.ps.state = estate;
+       grpstate->ss.ps.ExecProcNode = ExecGroup;
+       grpstate->grp_done = false;
 
        /*
-        * assign node's base id and create expression context
+        * create expression context
         */
-       ExecAssignNodeBaseInfo(estate, &grpstate->csstate.cstate,
-                                                  (Plan *) parent);
-       ExecAssignExprContext(estate, &grpstate->csstate.cstate);
-
-#define GROUP_NSLOTS 2
+       ExecAssignExprContext(estate, &grpstate->ss.ps);
 
        /*
-        * tuple table initialization
+        * initialize child nodes
         */
-       ExecInitScanTupleSlot(estate, &grpstate->csstate);
-       ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
+       outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags);
 
        /*
-        * initializes child nodes
+        * Initialize scan slot and type.
         */
-       outerPlan = outerPlan(node);
-       ExecInitNode(outerPlan, estate, (Plan *) node);
+       tts_ops = ExecGetResultSlotOps(outerPlanState(&grpstate->ss), NULL);
+       ExecCreateScanSlotFromOuterPlan(estate, &grpstate->ss, tts_ops);
 
-       /* ----------------
-        *      initialize tuple type.
-        * ----------------
+       /*
+        * Initialize result slot, type and projection.
         */
-       ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate);
+       ExecInitResultTupleSlotTL(&grpstate->ss.ps, &TTSOpsVirtual);
+       ExecAssignProjectionInfo(&grpstate->ss.ps, NULL);
 
        /*
-        * Initialize tuple type for both result and scan. This node does no
-        * projection
+        * initialize child expressions
         */
-       ExecAssignResultTypeFromTL((Plan *) node, &grpstate->csstate.cstate);
-       ExecAssignProjectionInfo((Plan *) node, &grpstate->csstate.cstate);
+       grpstate->ss.ps.qual =
+               ExecInitQual(node->plan.qual, (PlanState *) grpstate);
 
-       return TRUE;
-}
-
-int
-ExecCountSlotsGroup(Group *node)
-{
-       return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
+       /*
+        * Precompute fmgr lookup data for inner loop
+        */
+       grpstate->eqfunction =
+               execTuplesMatchPrepare(ExecGetResultType(outerPlanState(grpstate)),
+                                                          node->numCols,
+                                                          node->grpColIdx,
+                                                          node->grpOperators,
+                                                          &grpstate->ss.ps);
+
+       return grpstate;
 }
 
 /* ------------------------
@@ -321,110 +223,32 @@ ExecCountSlotsGroup(Group *node)
  * -----------------------
  */
 void
-ExecEndGroup(Group *node)
+ExecEndGroup(GroupState *node)
 {
-       GroupState *grpstate;
-       Plan       *outerPlan;
-
-       grpstate = node->grpstate;
+       PlanState  *outerPlan;
 
-       ExecFreeProjectionInfo(&grpstate->csstate.cstate);
-
-       outerPlan = outerPlan(node);
-       ExecEndNode(outerPlan, (Plan *) node);
+       ExecFreeExprContext(&node->ss.ps);
 
        /* clean up tuple table */
-       ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
-       if (grpstate->grp_firstTuple != NULL)
-       {
-               pfree(grpstate->grp_firstTuple);
-               grpstate->grp_firstTuple = NULL;
-       }
-}
-
-/*****************************************************************************
- *
- *****************************************************************************/
-
-/*
- * code swiped from nodeUnique.c
- */
-static bool
-sameGroup(HeapTuple oldtuple,
-                 HeapTuple newtuple,
-                 int numCols,
-                 AttrNumber *grpColIdx,
-                 TupleDesc tupdesc)
-{
-       bool            isNull1,
-                               isNull2;
-       Datum           attr1,
-                               attr2;
-       char       *val1,
-                          *val2;
-       int                     i;
-       AttrNumber      att;
-       Oid                     typoutput,
-                               typelem;
-
-       for (i = 0; i < numCols; i++)
-       {
-               att = grpColIdx[i];
-               getTypeOutAndElem((Oid) tupdesc->attrs[att - 1]->atttypid,
-                                                 &typoutput, &typelem);
-
-               attr1 = heap_getattr(oldtuple,
-                                                        att,
-                                                        tupdesc,
-                                                        &isNull1);
-
-               attr2 = heap_getattr(newtuple,
-                                                        att,
-                                                        tupdesc,
-                                                        &isNull2);
-
-               if (isNull1 == isNull2)
-               {
-                       if (isNull1)            /* both are null, they are equal */
-                               continue;
-
-                       val1 = fmgr(typoutput, attr1, typelem,
-                                               tupdesc->attrs[att - 1]->atttypmod);
-                       val2 = fmgr(typoutput, attr2, typelem,
-                                               tupdesc->attrs[att - 1]->atttypmod);
-
-                       /*
-                        * now, val1 and val2 are ascii representations so we can use
-                        * strcmp for comparison
-                        */
-                       if (strcmp(val1, val2) != 0)
-                       {
-                               pfree(val1);
-                               pfree(val2);
-                               return FALSE;
-                       }
-                       pfree(val1);
-                       pfree(val2);
-               }
-               else
-               {
-                       /* one is null and the other isn't, they aren't equal */
-                       return FALSE;
-               }
-       }
+       ExecClearTuple(node->ss.ss_ScanTupleSlot);
 
-       return TRUE;
+       outerPlan = outerPlanState(node);
+       ExecEndNode(outerPlan);
 }
 
 void
-ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
+ExecReScanGroup(GroupState *node)
 {
-       GroupState *grpstate = node->grpstate;
+       PlanState  *outerPlan = outerPlanState(node);
 
-       grpstate->grp_useFirstTuple = FALSE;
-       grpstate->grp_done = FALSE;
+       node->grp_done = false;
+       /* must clear first tuple */
+       ExecClearTuple(node->ss.ss_ScanTupleSlot);
 
-       if (((Plan *) node)->lefttree &&
-               ((Plan *) node)->lefttree->chgParam == NULL)
-               ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
+       /*
+        * if chgParam of subnode is not null then plan will be re-scanned by
+        * first ExecProcNode.
+        */
+       if (outerPlan->chgParam == NULL)
+               ExecReScan(outerPlan);
 }