📄 nodegroup.c
字号:
/*------------------------------------------------------------------------- * * 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: /usr/local/cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.27 1999/07/11 01:57:32 tgl Exp $ * *------------------------------------------------------------------------- */#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"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 */static TupleTableSlot *ExecGroupEveryTuple(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 = grpstate->csstate.cstate.cs_ExprContext; /* if we haven't returned first tuple of new group yet ... */ if (grpstate->grp_useFirstTuple) { grpstate->grp_useFirstTuple = FALSE; ExecStoreTuple(grpstate->grp_firstTuple, grpstate->csstate.css_ScanTupleSlot, InvalidBuffer, false); } else { outerslot = ExecProcNode(outerPlan(node), (Plan *) node); if (TupIsNull(outerslot)) { grpstate->grp_done = TRUE; return NULL; } outerTuple = outerslot->val; firsttuple = grpstate->grp_firstTuple; /* this should occur on the first call only */ if (firsttuple == NULL) grpstate->grp_firstTuple = heap_copytuple(outerTuple); else { /* * Compare with first tuple and see if this tuple is of the * same group. */ 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 */ } } 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); } /* * find all tuples that belong to a group */ for (;;) { outerslot = ExecProcNode(outerPlan(node), (Plan *) node); if (TupIsNull(outerslot)) { grpstate->grp_done = TRUE; outerTuple = NULL; break; } outerTuple = outerslot->val; /* ---------------- * Compare with first tuple and see if this tuple is of * the same group. * ---------------- */ if ((!sameGroup(firsttuple, outerTuple, node->numCols, node->grpColIdx, ExecGetScanType(&grpstate->csstate)))) break; } /* ---------------- * 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); } return resultSlot;}/* ----------------- * ExecInitGroup * * Creates the run-time information for the group node produced by the * planner and initializes its outer subtree * ----------------- */boolExecInitGroup(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_useFirstTuple = FALSE; grpstate->grp_done = FALSE; grpstate->grp_firstTuple = NULL; /* * 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;}intExecCountSlotsGroup(Group *node){ return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;}/* ------------------------ * ExecEndGroup(node) * * ----------------------- */voidExecEndGroup(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); if (grpstate->grp_firstTuple != NULL) { pfree(grpstate->grp_firstTuple); grpstate->grp_firstTuple = NULL; }}/***************************************************************************** * *****************************************************************************//* * code swiped from nodeUnique.c */static boolsameGroup(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; } } return TRUE;}voidExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent){ GroupState *grpstate = node->grpstate; grpstate->grp_useFirstTuple = FALSE; grpstate->grp_done = FALSE; if (grpstate->grp_firstTuple != NULL) { pfree(grpstate->grp_firstTuple); grpstate->grp_firstTuple = NULL; } if (((Plan *) node)->lefttree && ((Plan *) node)->lefttree->chgParam == NULL) ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -