240 lines
6.2 KiB
C
240 lines
6.2 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* 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);
|
|
|
|
}
|