aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/nodeGroup.c
diff options
context:
space:
mode:
authorMarc G. Fournier <scrappy@hub.org>1996-07-09 06:22:35 +0000
committerMarc G. Fournier <scrappy@hub.org>1996-07-09 06:22:35 +0000
commitd31084e9d1118b25fd16580d9d8c2924b5740dff (patch)
tree3179e66307d54df9c7b966543550e601eb55e668 /src/backend/executor/nodeGroup.c
downloadpostgresql-PG95-1_01.tar.gz
postgresql-PG95-1_01.zip
Postgres95 1.01 Distribution - Virgin SourcesPG95-1_01
Diffstat (limited to 'src/backend/executor/nodeGroup.c')
-rw-r--r--src/backend/executor/nodeGroup.c407
1 files changed, 407 insertions, 0 deletions
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
new file mode 100644
index 00000000000..e9b0847b5f9
--- /dev/null
+++ b/src/backend/executor/nodeGroup.c
@@ -0,0 +1,407 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeGroup.c--
+ * Routines to handle group nodes (used for queries with GROUP BY clause).
+ *
+ * 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)
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "access/heapam.h"
+#include "catalog/catalog.h"
+#include "executor/executor.h"
+#include "executor/nodeGroup.h"
+
+static TupleTableSlot *ExecGroupEveryTuple(Group *node);
+static TupleTableSlot *ExecGroupOneTuple(Group *node);
+static bool sameGroup(TupleTableSlot *oldslot, TupleTableSlot *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
+ */
+static TupleTableSlot *
+ExecGroupEveryTuple(Group *node)
+{
+ GroupState *grpstate;
+ EState *estate;
+ ExprContext *econtext;
+
+ HeapTuple outerTuple = NULL;
+ TupleTableSlot *outerslot, *lastslot;
+ 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 = grpstate->csstate.cstate.cs_ExprContext;
+
+ if (grpstate->grp_useLastTuple) {
+ /*
+ * we haven't returned last tuple yet because it is not of the
+ * same group
+ */
+ grpstate->grp_useLastTuple = FALSE;
+
+ ExecStoreTuple(grpstate->grp_lastSlot->val,
+ grpstate->csstate.css_ScanTupleSlot,
+ grpstate->grp_lastSlot->ttc_buffer,
+ false);
+ } else {
+ outerslot = ExecProcNode(outerPlan(node), (Plan*)node);
+ if (outerslot)
+ outerTuple = outerslot->val;
+ if (!HeapTupleIsValid(outerTuple)) {
+ grpstate->grp_done = TRUE;
+ return NULL;
+ }
+
+ /* ----------------
+ * Compare with last tuple and see if this tuple is of
+ * the same group.
+ * ----------------
+ */
+ lastslot = grpstate->csstate.css_ScanTupleSlot;
+
+ if (lastslot->val != NULL &&
+ (!sameGroup(lastslot, outerslot,
+ node->numCols, node->grpColIdx,
+ ExecGetScanType(&grpstate->csstate)))) {
+/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/
+
+ grpstate->grp_useLastTuple = TRUE;
+
+ /* save it for next time */
+ grpstate->grp_lastSlot = outerslot;
+
+ /*
+ * signifies the end of the group
+ */
+ return NULL;
+ }
+
+ 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;
+ TupleTableSlot *outerslot, *lastslot;
+ 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;
+
+ if (grpstate->grp_useLastTuple) {
+ grpstate->grp_useLastTuple = FALSE;
+ ExecStoreTuple(grpstate->grp_lastSlot->val,
+ grpstate->csstate.css_ScanTupleSlot,
+ grpstate->grp_lastSlot->ttc_buffer,
+ false);
+ } else {
+ outerslot = ExecProcNode(outerPlan(node), (Plan*)node);
+ if (outerslot) outerTuple = outerslot->val;
+ if (!HeapTupleIsValid(outerTuple)) {
+ grpstate->grp_done = TRUE;
+ return NULL;
+ }
+ ExecStoreTuple(outerTuple,
+ grpstate->csstate.css_ScanTupleSlot,
+ outerslot->ttc_buffer,
+ false);
+ }
+ lastslot = grpstate->csstate.css_ScanTupleSlot;
+
+ /*
+ * find all tuples that belong to a group
+ */
+ for(;;) {
+ outerslot = ExecProcNode(outerPlan(node), (Plan*)node);
+ outerTuple = (outerslot) ? outerslot->val : NULL;
+ if (!HeapTupleIsValid(outerTuple)) {
+ /*
+ * we have at least one tuple (lastslot) if we reach here
+ */
+ grpstate->grp_done = TRUE;
+
+ /* return lastslot */
+ break;
+ }
+
+ /* ----------------
+ * Compare with last tuple and see if this tuple is of
+ * the same group.
+ * ----------------
+ */
+ if ((!sameGroup(lastslot, outerslot,
+ node->numCols, node->grpColIdx,
+ ExecGetScanType(&grpstate->csstate)))) {
+/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/
+
+ grpstate->grp_useLastTuple = TRUE;
+
+ /* save it for next time */
+ grpstate->grp_lastSlot = outerslot;
+
+ /* return lastslot */
+ break;
+ }
+
+ ExecStoreTuple(outerTuple,
+ grpstate->csstate.css_ScanTupleSlot,
+ outerslot->ttc_buffer,
+ false);
+
+ lastslot = grpstate->csstate.css_ScanTupleSlot;
+ }
+
+ ExecStoreTuple(lastslot->val,
+ grpstate->csstate.css_ScanTupleSlot,
+ lastslot->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 = lastslot;
+ resultSlot = ExecProject(projInfo, &isDone);
+
+ return resultSlot;
+}
+
+/* -----------------
+ * ExecInitGroup
+ *
+ * Creates the run-time information for the group node produced by the
+ * planner and initializes its outer subtree
+ * -----------------
+ */
+bool
+ExecInitGroup(Group *node, EState *estate, Plan *parent)
+{
+ GroupState *grpstate;
+ Plan *outerPlan;
+
+ /*
+ * assign the node's execution state
+ */
+ node->plan.state = estate;
+
+ /*
+ * create state structure
+ */
+ grpstate = makeNode(GroupState);
+ node->grpstate = grpstate;
+ grpstate->grp_useLastTuple = FALSE;
+ grpstate->grp_done = FALSE;
+
+ /*
+ * assign node's base id and create expression context
+ */
+ ExecAssignNodeBaseInfo(estate, &grpstate->csstate.cstate,
+ (Plan*) parent);
+ ExecAssignExprContext(estate, &grpstate->csstate.cstate);
+
+#define GROUP_NSLOTS 2
+ /*
+ * tuple table initialization
+ */
+ ExecInitScanTupleSlot(estate, &grpstate->csstate);
+ ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
+
+ /*
+ * initializes child nodes
+ */
+ outerPlan = outerPlan(node);
+ ExecInitNode(outerPlan, estate, (Plan *)node);
+
+ /* ----------------
+ * initialize tuple type.
+ * ----------------
+ */
+ ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate);
+
+ /*
+ * Initialize tuple type for both result and scan.
+ * This node does no projection
+ */
+ ExecAssignResultTypeFromTL((Plan*) node, &grpstate->csstate.cstate);
+ ExecAssignProjectionInfo((Plan*)node, &grpstate->csstate.cstate);
+
+ return TRUE;
+}
+
+int
+ExecCountSlotsGroup(Group *node)
+{
+ return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
+}
+
+/* ------------------------
+ * ExecEndGroup(node)
+ *
+ * -----------------------
+ */
+void
+ExecEndGroup(Group *node)
+{
+ GroupState *grpstate;
+ Plan *outerPlan;
+
+ grpstate = node->grpstate;
+
+ ExecFreeProjectionInfo(&grpstate->csstate.cstate);
+
+ outerPlan = outerPlan(node);
+ ExecEndNode(outerPlan, (Plan*)node);
+
+ /* clean up tuple table */
+ ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
+}
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+
+/*
+ * code swiped from nodeUnique.c
+ */
+static bool
+sameGroup(TupleTableSlot *oldslot,
+ TupleTableSlot *newslot,
+ int numCols,
+ AttrNumber *grpColIdx,
+ TupleDesc tupdesc)
+{
+ bool isNull1,isNull2;
+ char *attr1, *attr2;
+ char *val1, *val2;
+ int i;
+ AttrNumber att;
+ Oid typoutput;
+
+ for(i = 0; i < numCols; i++) {
+ att = grpColIdx[i];
+ typoutput = typtoout((Oid)tupdesc->attrs[att-1]->atttypid);
+
+ attr1 = heap_getattr(oldslot->val,
+ InvalidBuffer,
+ att,
+ tupdesc,
+ &isNull1);
+
+ attr2 = heap_getattr(newslot->val,
+ InvalidBuffer,
+ att,
+ tupdesc,
+ &isNull2);
+
+ if (isNull1 == isNull2) {
+ if (isNull1) /* both are null, they are equal */
+ continue;
+
+ val1 = fmgr(typoutput, attr1,
+ gettypelem(tupdesc->attrs[att-1]->atttypid));
+ val2 = fmgr(typoutput, attr2,
+ gettypelem(tupdesc->attrs[att-1]->atttypid));
+
+ /* now, val1 and val2 are ascii representations so we can
+ use strcmp for comparison */
+ if (strcmp(val1,val2) != 0)
+ return FALSE;
+ } else {
+ /* one is null and the other isn't, they aren't equal */
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}