]> granicus.if.org Git - postgresql/blobdiff - src/backend/executor/nodeGroup.c
Make some small planner API cleanups.
[postgresql] / src / backend / executor / nodeGroup.c
index 0ba4d679ed6e1119e974444606a654a391b3ba21..655084d7b564858b1a447b79be6905acd9352bf6 100644 (file)
@@ -3,7 +3,7 @@
  * nodeGroup.c
  *       Routines to handle group nodes (used for queries with GROUP BY clause).
  *
- * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  *       locate group boundaries.
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.46 2002/05/17 22:35:12 tgl Exp $
+ *       src/backend/executor/nodeGroup.c
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
 
-#include "access/heapam.h"
-#include "catalog/pg_operator.h"
 #include "executor/executor.h"
 #include "executor/nodeGroup.h"
-#include "parser/parse_oper.h"
-#include "utils/builtins.h"
-#include "utils/lsyscache.h"
-#include "utils/syscache.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
-static TupleTableSlot *ExecGroupEveryTuple(Group *node);
-static TupleTableSlot *ExecGroupOneTuple(Group *node);
 
-/* ---------------------------------------
+/*
  *      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 are 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;
-       TupleDesc       tupdesc;
-       HeapTuple       outerTuple = NULL;
-       HeapTuple       firsttuple;
+       TupleTableSlot *firsttupleslot;
        TupleTableSlot *outerslot;
-       ProjectionInfo *projInfo;
-       TupleTableSlot *resultSlot;
+
+       CHECK_FOR_INTERRUPTS();
 
        /*
         * get state info from node
         */
-       grpstate = node->grpstate;
-       if (grpstate->grp_done)
+       if (node->grp_done)
                return NULL;
-       estate = node->plan.state;
-       econtext = grpstate->csstate.cstate.cs_ExprContext;
-       tupdesc = ExecGetScanType(&grpstate->csstate);
+       econtext = node->ss.ps.ps_ExprContext;
 
        /*
-        * We need not call ResetExprContext here because execTuplesMatch will
-        * reset the per-tuple memory context once per input tuple.
+        * The ScanTupleSlot holds the (copied) first tuple of each group.
         */
+       firsttupleslot = node->ss.ss_ScanTupleSlot;
 
-       /* if we haven't returned first tuple of a 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.
+        */
 
-               /*
-                * note we rely on subplan to hold ownership of the tuple for as
-                * long as we need it; we don't copy it.
-                */
-               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;
-
-               firsttuple = grpstate->grp_firstTuple;
-               if (firsttuple == NULL)
-               {
-                       /* this should occur on the first call only */
-                       grpstate->grp_firstTuple = heap_copytuple(outerTuple);
-               }
-               else
-               {
-                       /*
-                        * Compare with first tuple and see if this tuple is of the
-                        * same group.
-                        */
-                       if (!execTuplesMatch(firsttuple, outerTuple,
-                                                                tupdesc,
-                                                                node->numCols, node->grpColIdx,
-                                                                grpstate->eqfunctions,
-                                                                econtext->ecxt_per_tuple_memory))
-                       {
-                               /*
-                                * No; save the tuple to return it next time, and return
-                                * NULL
-                                */
-                               grpstate->grp_useFirstTuple = TRUE;
-                               heap_freetuple(firsttuple);
-                               grpstate->grp_firstTuple = heap_copytuple(outerTuple);
-
-                               return NULL;    /* signifies the end of the group */
-                       }
-               }
+               /* Copy tuple into firsttupleslot */
+               ExecCopySlot(firsttupleslot, outerslot);
 
                /*
-                * note we rely on subplan to hold ownership of the tuple for as
-                * long as we need it; we don't copy it.
+                * Set it up as input for qual test and projection.  The expressions
+                * will access the input tuple as varno OUTER.
                 */
-               ExecStoreTuple(outerTuple,
-                                          grpstate->csstate.css_ScanTupleSlot,
-                                          InvalidBuffer, false);
-       }
-
-       /*
-        * form a projection tuple, store it in the result tuple slot and
-        * return it.
-        */
-       projInfo = grpstate->csstate.cstate.cs_ProjInfo;
+               econtext->ecxt_outertuple = firsttupleslot;
 
-       econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
-       resultSlot = ExecProject(projInfo, NULL);
-
-       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;
-       TupleDesc       tupdesc;
-       HeapTuple       outerTuple = NULL;
-       HeapTuple       firsttuple;
-       TupleTableSlot *outerslot;
-       ProjectionInfo *projInfo;
-       TupleTableSlot *resultSlot;
-
-       /*
-        * 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;
-       tupdesc = ExecGetScanType(&grpstate->csstate);
-
-       /*
-        * We need not call ResetExprContext here because execTuplesMatch will
-        * reset the per-tuple memory context once per input tuple.
-        */
-
-       firsttuple = grpstate->grp_firstTuple;
-       if (firsttuple == NULL)
-       {
-               /* this should occur on the first call only */
-               outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
-               if (TupIsNull(outerslot))
+               /*
+                * 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))
                {
-                       grpstate->grp_done = TRUE;
-                       return NULL;
+                       /*
+                        * Form and return a projection tuple using the first input tuple.
+                        */
+                       return ExecProject(node->ss.ps.ps_ProjInfo);
                }
-               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 (!execTuplesMatch(firsttuple, outerTuple,
-                                                        tupdesc,
-                                                        node->numCols, node->grpColIdx,
-                                                        grpstate->eqfunctions,
-                                                        econtext->ecxt_per_tuple_memory))
-                       break;
-       }
-
-       /*
-        * form a projection tuple, store it in the result tuple slot and
-        * return it.
-        */
-       projInfo = grpstate->csstate.cstate.cs_ProjInfo;
+               /* Copy tuple, set up as input for qual test and projection */
+               ExecCopySlot(firsttupleslot, outerslot);
+               econtext->ecxt_outertuple = firsttupleslot;
 
-       /*
-        * note we rely on subplan to hold ownership of the tuple for as long
-        * as we need it; we don't copy it.
-        */
-       ExecStoreTuple(firsttuple,
-                                  grpstate->csstate.css_ScanTupleSlot,
-                                  InvalidBuffer, false);
-       econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
-       resultSlot = ExecProject(projInfo, NULL);
-
-       /* save outerTuple if we are not done yet */
-       if (!grpstate->grp_done)
-       {
-               heap_freetuple(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;
 }
 
 /* -----------------
@@ -271,72 +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->grp_firstTuple = NULL;
+       grpstate->ss.ps.plan = (Plan *) node;
+       grpstate->ss.ps.state = estate;
+       grpstate->ss.ps.ExecProcNode = ExecGroup;
+       grpstate->grp_done = false;
 
        /*
         * create expression context
         */
-       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);
 
        /*
         * Precompute fmgr lookup data for inner loop
         */
-       grpstate->eqfunctions =
-               execTuplesMatchPrepare(ExecGetScanType(&grpstate->csstate),
+       grpstate->eqfunction =
+               execTuplesMatchPrepare(ExecGetResultType(outerPlanState(grpstate)),
                                                           node->numCols,
-                                                          node->grpColIdx);
-
-       return TRUE;
-}
+                                                          node->grpColIdx,
+                                                          node->grpOperators,
+                                                          &grpstate->ss.ps);
 
-int
-ExecCountSlotsGroup(Group *node)
-{
-       return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
+       return grpstate;
 }
 
 /* ------------------------
@@ -345,158 +223,32 @@ ExecCountSlotsGroup(Group *node)
  * -----------------------
  */
 void
-ExecEndGroup(Group *node)
+ExecEndGroup(GroupState *node)
 {
-       GroupState *grpstate;
-       Plan       *outerPlan;
-
-       grpstate = node->grpstate;
-
-       ExecFreeProjectionInfo(&grpstate->csstate.cstate);
-       ExecFreeExprContext(&grpstate->csstate.cstate);
+       PlanState  *outerPlan;
 
-       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)
-       {
-               heap_freetuple(grpstate->grp_firstTuple);
-               grpstate->grp_firstTuple = NULL;
-       }
-}
-
-void
-ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
-{
-       GroupState *grpstate = node->grpstate;
-
-       grpstate->grp_useFirstTuple = FALSE;
-       grpstate->grp_done = FALSE;
-       if (grpstate->grp_firstTuple != NULL)
-       {
-               heap_freetuple(grpstate->grp_firstTuple);
-               grpstate->grp_firstTuple = NULL;
-       }
+       ExecClearTuple(node->ss.ss_ScanTupleSlot);
 
-       if (((Plan *) node)->lefttree &&
-               ((Plan *) node)->lefttree->chgParam == NULL)
-               ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
+       outerPlan = outerPlanState(node);
+       ExecEndNode(outerPlan);
 }
 
-/*****************************************************************************
- *             Code shared with nodeUnique.c
- *****************************************************************************/
-
-/*
- * execTuplesMatch
- *             Return true if two tuples match in all the indicated fields.
- *             This is used to detect group boundaries in nodeGroup, and to
- *             decide whether two tuples are distinct or not in nodeUnique.
- *
- * tuple1, tuple2: the tuples to compare
- * tupdesc: tuple descriptor applying to both tuples
- * numCols: the number of attributes to be examined
- * matchColIdx: array of attribute column numbers
- * eqFunctions: array of fmgr lookup info for the equality functions to use
- * evalContext: short-term memory context for executing the functions
- *
- * NB: evalContext is reset each time!
- */
-bool
-execTuplesMatch(HeapTuple tuple1,
-                               HeapTuple tuple2,
-                               TupleDesc tupdesc,
-                               int numCols,
-                               AttrNumber *matchColIdx,
-                               FmgrInfo *eqfunctions,
-                               MemoryContext evalContext)
+void
+ExecReScanGroup(GroupState *node)
 {
-       MemoryContext oldContext;
-       bool            result;
-       int                     i;
+       PlanState  *outerPlan = outerPlanState(node);
 
-       /* Reset and switch into the temp context. */
-       MemoryContextReset(evalContext);
-       oldContext = MemoryContextSwitchTo(evalContext);
+       node->grp_done = false;
+       /* must clear first tuple */
+       ExecClearTuple(node->ss.ss_ScanTupleSlot);
 
        /*
-        * We cannot report a match without checking all the fields, but we
-        * can report a non-match as soon as we find unequal fields.  So,
-        * start comparing at the last field (least significant sort key).
-        * That's the most likely to be different...
+        * if chgParam of subnode is not null then plan will be re-scanned by
+        * first ExecProcNode.
         */
-       result = true;
-
-       for (i = numCols; --i >= 0;)
-       {
-               AttrNumber      att = matchColIdx[i];
-               Datum           attr1,
-                                       attr2;
-               bool            isNull1,
-                                       isNull2;
-
-               attr1 = heap_getattr(tuple1,
-                                                        att,
-                                                        tupdesc,
-                                                        &isNull1);
-
-               attr2 = heap_getattr(tuple2,
-                                                        att,
-                                                        tupdesc,
-                                                        &isNull2);
-
-               if (isNull1 != isNull2)
-               {
-                       result = false;         /* one null and one not; they aren't equal */
-                       break;
-               }
-
-               if (isNull1)
-                       continue;                       /* both are null, treat as equal */
-
-               /* Apply the type-specific equality function */
-
-               if (!DatumGetBool(FunctionCall2(&eqfunctions[i],
-                                                                               attr1, attr2)))
-               {
-                       result = false;         /* they aren't equal */
-                       break;
-               }
-       }
-
-       MemoryContextSwitchTo(oldContext);
-
-       return result;
-}
-
-/*
- * execTuplesMatchPrepare
- *             Look up the equality functions needed for execTuplesMatch.
- *             The result is a palloc'd array.
- */
-FmgrInfo *
-execTuplesMatchPrepare(TupleDesc tupdesc,
-                                          int numCols,
-                                          AttrNumber *matchColIdx)
-{
-       FmgrInfo   *eqfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
-       int                     i;
-
-       for (i = 0; i < numCols; i++)
-       {
-               AttrNumber      att = matchColIdx[i];
-               Oid                     typid = tupdesc->attrs[att - 1]->atttypid;
-               Oid                     eq_function;
-
-               eq_function = compatible_oper_funcid(makeList1(makeString("=")),
-                                                                                        typid, typid, true);
-               if (!OidIsValid(eq_function))
-                       elog(ERROR, "Unable to identify an equality operator for type %s",
-                                format_type_be(typid));
-               fmgr_info(eq_function, &eqfunctions[i]);
-       }
-
-       return eqfunctions;
+       if (outerPlan->chgParam == NULL)
+               ExecReScan(outerPlan);
 }