postgres/src/backend/executor/nodeUnique.c

372 lines
9.1 KiB
C

/*-------------------------------------------------------------------------
*
* nodeUnique.c
* Routines to handle unique'ing of queries where appropriate
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.24 1999/07/17 20:16:59 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 "access/printtup.h"
#include "executor/executor.h"
#include "executor/nodeUnique.h"
/* ----------------------------------------------------------------
* ExecIdenticalTuples
*
* This is a hack function used by ExecUnique to see if
* two tuples are identical. This should be provided
* by the heap tuple code but isn't. The real problem
* is that we assume we can byte compare tuples to determine
* if they are "equal". In fact, if we have user defined
* types there may be problems because it's possible that
* an ADT may have multiple representations with the
* same ADT value. -cim
* ----------------------------------------------------------------
*/
static bool /* true if tuples are identical, false
* otherwise */
ExecIdenticalTuples(TupleTableSlot *t1, TupleTableSlot *t2)
{
HeapTuple h1;
HeapTuple h2;
char *d1;
char *d2;
int len;
h1 = t1->val;
h2 = t2->val;
/* ----------------
* if tuples aren't the same length then they are
* obviously different (one may have null attributes).
* ----------------
*/
if (h1->t_len != h2->t_len)
return false;
/* ----------------
* if the tuples have different header offsets then
* they are different. This will prevent us from returning
* true when comparing tuples of one attribute where one of
* two we're looking at is null (t_len - t_hoff == 0).
* THE t_len FIELDS CAN BE THE SAME IN THIS CASE!!
* ----------------
*/
if (h1->t_data->t_hoff != h2->t_data->t_hoff)
return false;
/* ----------------
* ok, now get the pointers to the data and the
* size of the attribute portion of the tuple.
* ----------------
*/
d1 = (char *) GETSTRUCT(h1);
d2 = (char *) GETSTRUCT(h2);
len = (int) h1->t_len - (int) h1->t_data->t_hoff;
/* ----------------
* byte compare the data areas and return the result.
* ----------------
*/
if (memcmp(d1, d2, len) != 0)
return false;
return true;
}
/* ----------------------------------------------------------------
* ExecUnique
*
* This is a very simple node which filters out duplicate
* tuples from a stream of sorted tuples from a subplan.
*
* XXX see comments below regarding freeing tuples.
* ----------------------------------------------------------------
*/
TupleTableSlot * /* return: a tuple or NULL */
ExecUnique(Unique *node)
{
UniqueState *uniquestate;
TupleTableSlot *resultTupleSlot;
TupleTableSlot *slot;
Plan *outerPlan;
char *uniqueAttr;
AttrNumber uniqueAttrNum;
TupleDesc tupDesc;
Oid typoutput,
typelem;
/* ----------------
* get information from the node
* ----------------
*/
uniquestate = node->uniquestate;
outerPlan = outerPlan((Plan *) node);
resultTupleSlot = uniquestate->cs_ResultTupleSlot;
uniqueAttr = node->uniqueAttr;
uniqueAttrNum = node->uniqueAttrNum;
if (uniqueAttr)
{
tupDesc = ExecGetResultType(uniquestate);
getTypeOutAndElem((Oid) tupDesc->attrs[uniqueAttrNum - 1]->atttypid,
&typoutput, &typelem);
}
else
{ /* keep compiler quiet */
tupDesc = NULL;
typoutput = InvalidOid;
typelem = InvalidOid;
}
/* ----------------
* 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;
/* ----------------
* we use the result tuple slot to hold our saved tuples.
* if we haven't a saved tuple to compare our new tuple with,
* then we exit the loop. This new tuple as the saved tuple
* the next time we get here.
* ----------------
*/
if (TupIsNull(resultTupleSlot))
break;
/* ----------------
* now test if the new tuple and the previous
* tuple match. If so then we loop back and fetch
* another new tuple from the subplan.
* ----------------
*/
if (uniqueAttr)
{
/*
* to check equality, we check to see if the typoutput of the
* attributes are equal
*/
bool isNull1,
isNull2;
Datum attr1,
attr2;
char *val1,
*val2;
attr1 = heap_getattr(slot->val,
uniqueAttrNum, tupDesc, &isNull1);
attr2 = heap_getattr(resultTupleSlot->val,
uniqueAttrNum, tupDesc, &isNull2);
if (isNull1 == isNull2)
{
if (isNull1) /* both are null, they are equal */
continue;
val1 = fmgr(typoutput, attr1, typelem,
tupDesc->attrs[uniqueAttrNum - 1]->atttypmod);
val2 = fmgr(typoutput, attr2, typelem,
tupDesc->attrs[uniqueAttrNum - 1]->atttypmod);
/*
* now, val1 and val2 are ascii representations so we can
* use strcmp for comparison
*/
if (strcmp(val1, val2) == 0) /* they are equal */
{
pfree(val1);
pfree(val2);
continue;
}
pfree(val1);
pfree(val2);
break;
}
else
/* one is null and the other isn't, they aren't equal */
break;
}
else
{
if (!ExecIdenticalTuples(slot, resultTupleSlot))
break;
}
}
/* ----------------
* we have a new tuple different from the previous saved tuple
* so we save it in the saved tuple slot. We copy the tuple
* so we don't increment the buffer ref count.
* ----------------
*/
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;
char *uniqueAttr;
/* ----------------
* assign execution state to node
* ----------------
*/
node->plan.state = estate;
/* ----------------
* create new UniqueState for node
* ----------------
*/
uniquestate = makeNode(UniqueState);
node->uniquestate = uniquestate;
uniqueAttr = node->uniqueAttr;
/* ----------------
* Miscellanious 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, parent);
#define UNIQUE_NSLOTS 1
/* ------------
* Tuple table initialization
* ------------
*/
ExecInitResultTupleSlot(estate, uniquestate);
/* ----------------
* 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);
uniquestate->cs_ProjInfo = NULL;
if (uniqueAttr)
{
TupleDesc tupDesc;
int i = 0;
tupDesc = ExecGetResultType(uniquestate);
/*
* the parser should have ensured that uniqueAttr is a legal
* attribute name
*/
while (strcmp((tupDesc->attrs[i]->attname).data, uniqueAttr) != 0)
i++;
node->uniqueAttrNum = i + 1; /* attribute numbers start from 1 */
}
else
node->uniqueAttrNum = InvalidAttrNumber;
/* ----------------
* all done.
* ----------------
*/
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;
uniquestate = node->uniquestate;
ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
ExecClearTuple(uniquestate->cs_ResultTupleSlot);
}
void
ExecReScanUnique(Unique *node, ExprContext *exprCtxt, Plan *parent)
{
UniqueState *uniquestate = node->uniquestate;
ExecClearTuple(uniquestate->cs_ResultTupleSlot);
/*
* 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);
}