1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* nodeGroup.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* Routines to handle group nodes (used for queries with GROUP BY clause).
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2016-01-02 19:33:40 +01:00
|
|
|
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* DESCRIPTION
|
1997-09-07 07:04:48 +02:00
|
|
|
* The Group node is designed for handling queries with a GROUP BY clause.
|
2000-01-27 19:11:50 +01:00
|
|
|
* 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.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/executor/nodeGroup.c
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1997-01-10 21:19:49 +01:00
|
|
|
|
1996-10-31 11:12:26 +01:00
|
|
|
#include "postgres.h"
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "executor/executor.h"
|
|
|
|
#include "executor/nodeGroup.h"
|
|
|
|
|
|
|
|
|
2002-11-06 01:00:45 +01:00
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* ExecGroup -
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2002-11-06 01:00:45 +01:00
|
|
|
* Return one tuple for each group of matching input tuples.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
TupleTableSlot *
|
2002-12-05 16:50:39 +01:00
|
|
|
ExecGroup(GroupState *node)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
ExprContext *econtext;
|
2002-12-05 16:50:39 +01:00
|
|
|
int numCols;
|
|
|
|
AttrNumber *grpColIdx;
|
2005-03-16 22:38:10 +01:00
|
|
|
TupleTableSlot *firsttupleslot;
|
1998-02-18 13:40:44 +01:00
|
|
|
TupleTableSlot *outerslot;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* get state info from node
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-12-05 16:50:39 +01:00
|
|
|
if (node->grp_done)
|
1997-09-07 07:04:48 +02:00
|
|
|
return NULL;
|
2002-12-05 16:50:39 +01:00
|
|
|
econtext = node->ss.ps.ps_ExprContext;
|
|
|
|
numCols = ((Group *) node->ss.ps.plan)->numCols;
|
|
|
|
grpColIdx = ((Group *) node->ss.ps.plan)->grpColIdx;
|
2000-01-27 19:11:50 +01:00
|
|
|
|
2008-09-08 02:22:56 +02:00
|
|
|
/*
|
|
|
|
* Check to see if we're still projecting out tuples from a previous group
|
|
|
|
* tuple (because there is a function-returning-set in the projection
|
|
|
|
* expressions). If so, try to project another one.
|
|
|
|
*/
|
|
|
|
if (node->ss.ps.ps_TupFromTlist)
|
|
|
|
{
|
|
|
|
TupleTableSlot *result;
|
|
|
|
ExprDoneCond isDone;
|
|
|
|
|
|
|
|
result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone);
|
|
|
|
if (isDone == ExprMultipleResult)
|
|
|
|
return result;
|
|
|
|
/* Done with that source tuple... */
|
|
|
|
node->ss.ps.ps_TupFromTlist = false;
|
|
|
|
}
|
|
|
|
|
2005-03-16 22:38:10 +01:00
|
|
|
/*
|
|
|
|
* The ScanTupleSlot holds the (copied) first tuple of each group.
|
|
|
|
*/
|
|
|
|
firsttupleslot = node->ss.ss_ScanTupleSlot;
|
|
|
|
|
2000-07-12 04:37:39 +02:00
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* We need not call ResetExprContext here because execTuplesMatch will
|
|
|
|
* reset the per-tuple memory context once per input tuple.
|
2000-07-12 04:37:39 +02:00
|
|
|
*/
|
|
|
|
|
2005-03-11 00:21:26 +01:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* If first time through, acquire first input tuple and determine whether
|
|
|
|
* to return it or not.
|
2005-03-11 00:21:26 +01:00
|
|
|
*/
|
2005-03-16 22:38:10 +01:00
|
|
|
if (TupIsNull(firsttupleslot))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2002-12-05 16:50:39 +01:00
|
|
|
outerslot = ExecProcNode(outerPlanState(node));
|
1998-11-27 20:52:36 +01:00
|
|
|
if (TupIsNull(outerslot))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2005-03-11 00:21:26 +01:00
|
|
|
/* empty input, so return nothing */
|
2002-12-05 16:50:39 +01:00
|
|
|
node->grp_done = TRUE;
|
1997-09-07 07:04:48 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
2007-02-23 00:44:25 +01:00
|
|
|
/* Copy tuple into firsttupleslot */
|
2005-03-16 22:38:10 +01:00
|
|
|
ExecCopySlot(firsttupleslot, outerslot);
|
2007-02-23 00:44:25 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Set it up as input for qual test and projection. The expressions
|
|
|
|
* will access the input tuple as varno OUTER.
|
|
|
|
*/
|
|
|
|
econtext->ecxt_outertuple = firsttupleslot;
|
2005-10-15 04:49:52 +02:00
|
|
|
|
2005-03-11 00:21:26 +01:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Check the qual (HAVING clause); if the group does not match, ignore
|
|
|
|
* it and fall into scan loop.
|
2005-03-11 00:21:26 +01:00
|
|
|
*/
|
|
|
|
if (ExecQual(node->ss.ps.qual, econtext, false))
|
|
|
|
{
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Form and return a projection tuple using the first input tuple.
|
2005-03-11 00:21:26 +01:00
|
|
|
*/
|
2008-09-08 02:22:56 +02:00
|
|
|
TupleTableSlot *result;
|
|
|
|
ExprDoneCond isDone;
|
|
|
|
|
|
|
|
result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone);
|
|
|
|
|
|
|
|
if (isDone != ExprEndResult)
|
|
|
|
{
|
|
|
|
node->ss.ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
|
|
|
|
return result;
|
|
|
|
}
|
2005-03-11 00:21:26 +01:00
|
|
|
}
|
2011-09-22 17:29:18 +02:00
|
|
|
else
|
|
|
|
InstrCountFiltered1(node, 1);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
2005-03-11 00:21:26 +01:00
|
|
|
* This loop iterates once per input tuple group. At the head of the
|
2005-10-15 04:49:52 +02:00
|
|
|
* loop, we have finished processing the first tuple of the group and now
|
|
|
|
* need to scan over all the other group members.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
for (;;)
|
|
|
|
{
|
2005-03-11 00:21:26 +01:00
|
|
|
/*
|
|
|
|
* Scan over all remaining tuples that belong to this group
|
|
|
|
*/
|
|
|
|
for (;;)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2005-03-11 00:21:26 +01:00
|
|
|
outerslot = ExecProcNode(outerPlanState(node));
|
|
|
|
if (TupIsNull(outerslot))
|
|
|
|
{
|
|
|
|
/* no more groups, so we're done */
|
|
|
|
node->grp_done = TRUE;
|
|
|
|
return NULL;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-03-11 00:21:26 +01:00
|
|
|
/*
|
|
|
|
* Compare with first tuple and see if this tuple is of the same
|
|
|
|
* group. If so, ignore it and keep scanning.
|
|
|
|
*/
|
2005-03-16 22:38:10 +01:00
|
|
|
if (!execTuplesMatch(firsttupleslot, outerslot,
|
2005-03-11 00:21:26 +01:00
|
|
|
numCols, grpColIdx,
|
|
|
|
node->eqfunctions,
|
|
|
|
econtext->ecxt_per_tuple_memory))
|
|
|
|
break;
|
|
|
|
}
|
2005-10-15 04:49:52 +02:00
|
|
|
|
2000-01-27 19:11:50 +01:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* We have the first tuple of the next input group. See if we want to
|
|
|
|
* return it.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2005-03-16 22:38:10 +01:00
|
|
|
/* Copy tuple, set up as input for qual test and projection */
|
|
|
|
ExecCopySlot(firsttupleslot, outerslot);
|
2007-02-23 00:44:25 +01:00
|
|
|
econtext->ecxt_outertuple = firsttupleslot;
|
2005-10-15 04:49:52 +02:00
|
|
|
|
2005-03-11 00:21:26 +01:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Check the qual (HAVING clause); if the group does not match, ignore
|
|
|
|
* it and loop back to scan the rest of the group.
|
2005-03-11 00:21:26 +01:00
|
|
|
*/
|
|
|
|
if (ExecQual(node->ss.ps.qual, econtext, false))
|
|
|
|
{
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Form and return a projection tuple using the first input tuple.
|
2005-03-11 00:21:26 +01:00
|
|
|
*/
|
2008-09-08 02:22:56 +02:00
|
|
|
TupleTableSlot *result;
|
|
|
|
ExprDoneCond isDone;
|
|
|
|
|
|
|
|
result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone);
|
|
|
|
|
|
|
|
if (isDone != ExprEndResult)
|
|
|
|
{
|
|
|
|
node->ss.ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
|
|
|
|
return result;
|
|
|
|
}
|
2005-03-11 00:21:26 +01:00
|
|
|
}
|
2011-09-22 17:29:18 +02:00
|
|
|
else
|
|
|
|
InstrCountFiltered1(node, 1);
|
1998-02-18 13:40:44 +01:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* -----------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* ExecInitGroup
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* Creates the run-time information for the group node produced by the
|
|
|
|
* planner and initializes its outer subtree
|
1996-07-09 08:22:35 +02:00
|
|
|
* -----------------
|
|
|
|
*/
|
2002-12-05 16:50:39 +01:00
|
|
|
GroupState *
|
2006-02-28 05:10:28 +01:00
|
|
|
ExecInitGroup(Group *node, EState *estate, int eflags)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
GroupState *grpstate;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2006-02-28 05:10:28 +01:00
|
|
|
/* check for unsupported flags */
|
|
|
|
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
|
|
|
* create state structure
|
|
|
|
*/
|
|
|
|
grpstate = makeNode(GroupState);
|
2002-12-05 16:50:39 +01:00
|
|
|
grpstate->ss.ps.plan = (Plan *) node;
|
|
|
|
grpstate->ss.ps.state = estate;
|
2002-11-06 23:31:24 +01:00
|
|
|
grpstate->grp_done = FALSE;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
2000-07-12 04:37:39 +02:00
|
|
|
* create expression context
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-12-05 16:50:39 +01:00
|
|
|
ExecAssignExprContext(estate, &grpstate->ss.ps);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* tuple table initialization
|
|
|
|
*/
|
2002-12-05 16:50:39 +01:00
|
|
|
ExecInitScanTupleSlot(estate, &grpstate->ss);
|
|
|
|
ExecInitResultTupleSlot(estate, &grpstate->ss.ps);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
2002-12-05 16:50:39 +01:00
|
|
|
* initialize child expressions
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-12-05 16:50:39 +01:00
|
|
|
grpstate->ss.ps.targetlist = (List *)
|
2002-12-13 20:46:01 +01:00
|
|
|
ExecInitExpr((Expr *) node->plan.targetlist,
|
2002-12-05 16:50:39 +01:00
|
|
|
(PlanState *) grpstate);
|
|
|
|
grpstate->ss.ps.qual = (List *)
|
2002-12-13 20:46:01 +01:00
|
|
|
ExecInitExpr((Expr *) node->plan.qual,
|
2002-12-05 16:50:39 +01:00
|
|
|
(PlanState *) grpstate);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* initialize child nodes
|
|
|
|
*/
|
2006-02-28 05:10:28 +01:00
|
|
|
outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* initialize tuple type.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-12-05 16:50:39 +01:00
|
|
|
ExecAssignScanTypeFromOuterPlan(&grpstate->ss);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
2003-01-12 23:01:38 +01:00
|
|
|
* Initialize result tuple type and projection info.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-12-05 16:50:39 +01:00
|
|
|
ExecAssignResultTypeFromTL(&grpstate->ss.ps);
|
2007-02-02 01:07:03 +01:00
|
|
|
ExecAssignProjectionInfo(&grpstate->ss.ps, NULL);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2008-10-23 17:29:23 +02:00
|
|
|
grpstate->ss.ps.ps_TupFromTlist = false;
|
|
|
|
|
2000-01-27 19:11:50 +01:00
|
|
|
/*
|
|
|
|
* Precompute fmgr lookup data for inner loop
|
|
|
|
*/
|
|
|
|
grpstate->eqfunctions =
|
2007-01-10 19:06:05 +01:00
|
|
|
execTuplesMatchPrepare(node->numCols,
|
|
|
|
node->grpOperators);
|
2000-01-27 19:11:50 +01:00
|
|
|
|
2002-12-05 16:50:39 +01:00
|
|
|
return grpstate;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* ExecEndGroup(node)
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* -----------------------
|
|
|
|
*/
|
|
|
|
void
|
2002-12-05 16:50:39 +01:00
|
|
|
ExecEndGroup(GroupState *node)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2002-12-05 16:50:39 +01:00
|
|
|
PlanState *outerPlan;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2002-12-05 16:50:39 +01:00
|
|
|
ExecFreeExprContext(&node->ss.ps);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* clean up tuple table */
|
2002-12-05 16:50:39 +01:00
|
|
|
ExecClearTuple(node->ss.ss_ScanTupleSlot);
|
2002-12-15 17:17:59 +01:00
|
|
|
|
|
|
|
outerPlan = outerPlanState(node);
|
|
|
|
ExecEndNode(outerPlan);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2000-01-27 19:11:50 +01:00
|
|
|
void
|
2010-07-12 19:01:06 +02:00
|
|
|
ExecReScanGroup(GroupState *node)
|
2000-01-27 19:11:50 +01:00
|
|
|
{
|
2015-05-24 03:35:49 +02:00
|
|
|
PlanState *outerPlan = outerPlanState(node);
|
2015-05-04 22:13:07 +02:00
|
|
|
|
2002-12-05 16:50:39 +01:00
|
|
|
node->grp_done = FALSE;
|
2008-10-23 17:29:23 +02:00
|
|
|
node->ss.ps.ps_TupFromTlist = false;
|
2005-03-16 22:38:10 +01:00
|
|
|
/* must clear first tuple */
|
|
|
|
ExecClearTuple(node->ss.ss_ScanTupleSlot);
|
2000-01-27 19:11:50 +01:00
|
|
|
|
2010-07-12 19:01:06 +02:00
|
|
|
/*
|
|
|
|
* if chgParam of subnode is not null then plan will be re-scanned by
|
|
|
|
* first ExecProcNode.
|
|
|
|
*/
|
2015-05-04 22:13:07 +02:00
|
|
|
if (outerPlan->chgParam == NULL)
|
|
|
|
ExecReScan(outerPlan);
|
2000-01-27 19:11:50 +01:00
|
|
|
}
|