* nodeGroup.c
* Routines to handle group nodes (used for queries with GROUP BY clause).
*
- * Portions Copyright (c) 1996-2006, 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
- * $PostgreSQL: pgsql/src/backend/executor/nodeGroup.c,v 1.65 2006/07/14 14:52:19 momjian Exp $
+ * src/backend/executor/nodeGroup.c
*
*-------------------------------------------------------------------------
*/
#include "executor/executor.h"
#include "executor/nodeGroup.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
*
* Return one tuple for each group of matching input tuples.
*/
-TupleTableSlot *
-ExecGroup(GroupState *node)
+static TupleTableSlot *
+ExecGroup(PlanState *pstate)
{
+ GroupState *node = castNode(GroupState, pstate);
ExprContext *econtext;
- int numCols;
- AttrNumber *grpColIdx;
TupleTableSlot *firsttupleslot;
TupleTableSlot *outerslot;
+ CHECK_FOR_INTERRUPTS();
+
/*
* get state info from node
*/
if (node->grp_done)
return NULL;
econtext = node->ss.ps.ps_ExprContext;
- numCols = ((Group *) node->ss.ps.plan)->numCols;
- grpColIdx = ((Group *) node->ss.ps.plan)->grpColIdx;
/*
* The ScanTupleSlot holds the (copied) first tuple of each group.
firsttupleslot = node->ss.ss_ScanTupleSlot;
/*
- * We need not call ResetExprContext here because execTuplesMatch will
+ * We need not call ResetExprContext here because ExecQualAndReset() will
* reset the per-tuple memory context once per input tuple.
*/
if (TupIsNull(outerslot))
{
/* empty input, so return nothing */
- node->grp_done = TRUE;
+ node->grp_done = true;
return NULL;
}
- /* Copy tuple, set up as input for qual test and projection */
+ /* Copy tuple into firsttupleslot */
ExecCopySlot(firsttupleslot, outerslot);
- econtext->ecxt_scantuple = firsttupleslot;
+
+ /*
+ * 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, false))
+ 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, NULL);
+ return ExecProject(node->ss.ps.ps_ProjInfo);
}
+ else
+ InstrCountFiltered1(node, 1);
}
/*
if (TupIsNull(outerslot))
{
/* no more groups, so we're done */
- node->grp_done = TRUE;
+ 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.
*/
- if (!execTuplesMatch(firsttupleslot, outerslot,
- numCols, grpColIdx,
- node->eqfunctions,
- econtext->ecxt_per_tuple_memory))
+ econtext->ecxt_innertuple = firsttupleslot;
+ econtext->ecxt_outertuple = outerslot;
+ if (!ExecQualAndReset(node->eqfunction, econtext))
break;
}
*/
/* Copy tuple, set up as input for qual test and projection */
ExecCopySlot(firsttupleslot, outerslot);
- econtext->ecxt_scantuple = firsttupleslot;
+ econtext->ecxt_outertuple = firsttupleslot;
/*
* 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, false))
+ 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, NULL);
+ return ExecProject(node->ss.ps.ps_ProjInfo);
}
+ else
+ InstrCountFiltered1(node, 1);
}
-
- /* NOTREACHED */
- return NULL;
}
/* -----------------
ExecInitGroup(Group *node, EState *estate, int eflags)
{
GroupState *grpstate;
+ const TupleTableSlotOps *tts_ops;
/* check for unsupported flags */
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
grpstate = makeNode(GroupState);
grpstate->ss.ps.plan = (Plan *) node;
grpstate->ss.ps.state = estate;
- grpstate->grp_done = FALSE;
+ grpstate->ss.ps.ExecProcNode = ExecGroup;
+ grpstate->grp_done = false;
/*
* create expression context
*/
ExecAssignExprContext(estate, &grpstate->ss.ps);
-#define GROUP_NSLOTS 2
-
/*
- * tuple table initialization
- */
- ExecInitScanTupleSlot(estate, &grpstate->ss);
- ExecInitResultTupleSlot(estate, &grpstate->ss.ps);
-
- /*
- * initialize child expressions
+ * initialize child nodes
*/
- grpstate->ss.ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->plan.targetlist,
- (PlanState *) grpstate);
- grpstate->ss.ps.qual = (List *)
- ExecInitExpr((Expr *) node->plan.qual,
- (PlanState *) grpstate);
+ outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags);
/*
- * initialize child nodes
+ * Initialize scan slot and type.
*/
- outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags);
+ tts_ops = ExecGetResultSlotOps(outerPlanState(&grpstate->ss), NULL);
+ ExecCreateScanSlotFromOuterPlan(estate, &grpstate->ss, tts_ops);
/*
- * initialize tuple type.
+ * Initialize result slot, type and projection.
*/
- ExecAssignScanTypeFromOuterPlan(&grpstate->ss);
+ ExecInitResultTupleSlotTL(&grpstate->ss.ps, &TTSOpsVirtual);
+ ExecAssignProjectionInfo(&grpstate->ss.ps, NULL);
/*
- * Initialize result tuple type and projection info.
+ * initialize child expressions
*/
- ExecAssignResultTypeFromTL(&grpstate->ss.ps);
- ExecAssignProjectionInfo(&grpstate->ss.ps);
+ grpstate->ss.ps.qual =
+ ExecInitQual(node->plan.qual, (PlanState *) grpstate);
/*
* Precompute fmgr lookup data for inner loop
*/
- grpstate->eqfunctions =
- execTuplesMatchPrepare(ExecGetScanType(&grpstate->ss),
+ grpstate->eqfunction =
+ execTuplesMatchPrepare(ExecGetResultType(outerPlanState(grpstate)),
node->numCols,
- node->grpColIdx);
+ node->grpColIdx,
+ node->grpOperators,
+ &grpstate->ss.ps);
return grpstate;
}
-int
-ExecCountSlotsGroup(Group *node)
-{
- return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
-}
-
/* ------------------------
* ExecEndGroup(node)
*
}
void
-ExecReScanGroup(GroupState *node, ExprContext *exprCtxt)
+ExecReScanGroup(GroupState *node)
{
- node->grp_done = FALSE;
+ PlanState *outerPlan = outerPlanState(node);
+
+ node->grp_done = false;
/* must clear first tuple */
ExecClearTuple(node->ss.ss_ScanTupleSlot);
- if (((PlanState *) node)->lefttree &&
- ((PlanState *) node)->lefttree->chgParam == NULL)
- ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
+ /*
+ * if chgParam of subnode is not null then plan will be re-scanned by
+ * first ExecProcNode.
+ */
+ if (outerPlan->chgParam == NULL)
+ ExecReScan(outerPlan);
}