/*------------------------------------------------------------------------- * * 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.17 1998/02/26 04:31:34 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 #include "postgres.h" #include "fmgr.h" #include "executor/executor.h" #include "executor/nodeUnique.h" #include "optimizer/clauses.h" #include "access/heapam.h" #include "access/printtup.h" /* for typtoout() */ #include "utils/builtins.h" /* for namecpy() */ /* ---------------------------------------------------------------- * 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_hoff != h2->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_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; /* ---------------- * 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); typoutput = typtoout((Oid) tupDesc->attrs[uniqueAttrNum - 1]->atttypid); } else { /* keep compiler quiet */ tupDesc = NULL; typoutput = 0; } /* ---------------- * 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, gettypelem(tupDesc->attrs[uniqueAttrNum - 1]->atttypid), tupDesc->attrs[uniqueAttrNum - 1]->atttypmod); val2 = fmgr(typoutput, attr2, gettypelem(tupDesc->attrs[uniqueAttrNum - 1]->atttypid), 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); }