/*------------------------------------------------------------------------- * * nodeUnique.c * Routines to handle unique'ing of queries where appropriate * * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.29 2000/05/30 00:49:45 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES * ExecUnique - generate a unique'd temporary relation * ExecInitUnique - initialize node and subnodes.. * ExecEndUnique - shutdown node and subnodes * * NOTES * Assumes tuples returned from subplan arrive in * sorted order. * */ #include "postgres.h" #include "access/heapam.h" #include "executor/executor.h" #include "executor/nodeGroup.h" #include "executor/nodeUnique.h" /* ---------------------------------------------------------------- * ExecUnique * * This is a very simple node which filters out duplicate * tuples from a stream of sorted tuples from a subplan. * ---------------------------------------------------------------- */ TupleTableSlot * /* return: a tuple or NULL */ ExecUnique(Unique *node) { UniqueState *uniquestate; TupleTableSlot *resultTupleSlot; TupleTableSlot *slot; Plan *outerPlan; TupleDesc tupDesc; /* ---------------- * get information from the node * ---------------- */ uniquestate = node->uniquestate; outerPlan = outerPlan((Plan *) node); resultTupleSlot = uniquestate->cstate.cs_ResultTupleSlot; tupDesc = ExecGetResultType(&uniquestate->cstate); /* ---------------- * now loop, returning only non-duplicate tuples. * We assume that the tuples arrive in sorted order * so we can detect duplicates easily. * ---------------- */ for (;;) { /* ---------------- * fetch a tuple from the outer subplan * ---------------- */ slot = ExecProcNode(outerPlan, (Plan *) node); if (TupIsNull(slot)) return NULL; /* ---------------- * Always return the first tuple from the subplan. * ---------------- */ if (uniquestate->priorTuple == NULL) break; /* ---------------- * Else test if the new tuple and the previously returned * tuple match. If so then we loop back and fetch * another new tuple from the subplan. * ---------------- */ if (!execTuplesMatch(slot->val, uniquestate->priorTuple, tupDesc, node->numCols, node->uniqColIdx, uniquestate->eqfunctions)) break; } /* ---------------- * We have a new tuple different from the previous saved tuple (if any). * Save it and return it. Note that we make two copies of the tuple: * one to keep for our own future comparisons, and one to return to the * caller. We need to copy the tuple returned by the subplan to avoid * holding buffer refcounts, and we need our own copy because the caller * may alter the resultTupleSlot (eg via ExecRemoveJunk). * ---------------- */ if (uniquestate->priorTuple != NULL) heap_freetuple(uniquestate->priorTuple); uniquestate->priorTuple = heap_copytuple(slot->val); ExecStoreTuple(heap_copytuple(slot->val), resultTupleSlot, InvalidBuffer, true); return resultTupleSlot; } /* ---------------------------------------------------------------- * ExecInitUnique * * This initializes the unique node state structures and * the node's subplan. * ---------------------------------------------------------------- */ bool /* return: initialization status */ ExecInitUnique(Unique *node, EState *estate, Plan *parent) { UniqueState *uniquestate; Plan *outerPlan; /* ---------------- * assign execution state to node * ---------------- */ node->plan.state = estate; /* ---------------- * create new UniqueState for node * ---------------- */ uniquestate = makeNode(UniqueState); node->uniquestate = uniquestate; uniquestate->priorTuple = NULL; /* ---------------- * Miscellaneous initialization * * + assign node's base_id * + assign debugging hooks and * * Unique nodes have no ExprContext initialization because * they never call ExecQual or ExecTargetList. * ---------------- */ ExecAssignNodeBaseInfo(estate, &uniquestate->cstate, parent); #define UNIQUE_NSLOTS 1 /* ------------ * Tuple table initialization * ------------ */ ExecInitResultTupleSlot(estate, &uniquestate->cstate); /* ---------------- * then initialize outer plan * ---------------- */ outerPlan = outerPlan((Plan *) node); ExecInitNode(outerPlan, estate, (Plan *) node); /* ---------------- * unique nodes do no projections, so initialize * projection info for this node appropriately * ---------------- */ ExecAssignResultTypeFromOuterPlan((Plan *) node, &uniquestate->cstate); uniquestate->cstate.cs_ProjInfo = NULL; /* * Precompute fmgr lookup data for inner loop */ uniquestate->eqfunctions = execTuplesMatchPrepare(ExecGetResultType(&uniquestate->cstate), node->numCols, node->uniqColIdx); return TRUE; } int ExecCountSlotsUnique(Unique *node) { return ExecCountSlotsNode(outerPlan(node)) + ExecCountSlotsNode(innerPlan(node)) + UNIQUE_NSLOTS; } /* ---------------------------------------------------------------- * ExecEndUnique * * This shuts down the subplan and frees resources allocated * to this node. * ---------------------------------------------------------------- */ void ExecEndUnique(Unique *node) { UniqueState *uniquestate = node->uniquestate; ExecEndNode(outerPlan((Plan *) node), (Plan *) node); /* clean up tuple table */ ExecClearTuple(uniquestate->cstate.cs_ResultTupleSlot); if (uniquestate->priorTuple != NULL) { heap_freetuple(uniquestate->priorTuple); uniquestate->priorTuple = NULL; } } void ExecReScanUnique(Unique *node, ExprContext *exprCtxt, Plan *parent) { UniqueState *uniquestate = node->uniquestate; ExecClearTuple(uniquestate->cstate.cs_ResultTupleSlot); if (uniquestate->priorTuple != NULL) { heap_freetuple(uniquestate->priorTuple); uniquestate->priorTuple = NULL; } /* * if chgParam of subnode is not null then plan will be re-scanned by * first ExecProcNode. */ if (((Plan *) node)->lefttree->chgParam == NULL) ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node); }