]> granicus.if.org Git - postgresql/blobdiff - src/backend/executor/nodeUnique.c
Remove cvs keywords from all files.
[postgresql] / src / backend / executor / nodeUnique.c
index 8e81b95d8a83a26e0a76a2fa2344195190c27019..1af981378c8839b80711015cc4a73906e0f4ca63 100644 (file)
 /*-------------------------------------------------------------------------
  *
- * nodeUnique.c--
- *    Routines to handle unique'ing of queries where appropriate
+ * nodeUnique.c
+ *       Routines to handle unique'ing of queries where appropriate
  *
- * Copyright (c) 1994, Regents of the University of California
+ * Unique is a very simple node type that just filters out duplicate
+ * tuples from a stream of sorted tuples from its subplan.     It's essentially
+ * a dumbed-down form of Group: the duplicate-removal functionality is
+ * identical.  However, Unique doesn't do projection nor qual checking,
+ * so it's marginally more efficient for cases where neither is needed.
+ * (It's debatable whether the savings justifies carrying two plan node
+ * types, though.)
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.2 1996/07/19 06:27:59 scrappy Exp $
+ *       src/backend/executor/nodeUnique.c
  *
  *-------------------------------------------------------------------------
  */
 /*
  * INTERFACE ROUTINES
- *             ExecUnique      - generate a unique'd temporary relation
- *             ExecInitUnique  - initialize node and subnodes..
- *             ExecEndUnique   - shutdown node and subnodes
+ *             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.
- *
+ *             Assumes tuples returned from subplan arrive in
+ *             sorted order.
  */
+
+#include "postgres.h"
+
 #include "executor/executor.h"
 #include "executor/nodeUnique.h"
-#include "optimizer/clauses.h"
-#include "access/printtup.h" /* for typtoout() */
-#include "utils/builtins.h"  /* for namecpy()*/
+#include "utils/memutils.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
+ *             ExecUnique
  * ----------------------------------------------------------------
  */
-static bool    /* true if tuples are identical, false otherwise */
-ExecIdenticalTuples(TupleTableSlot *t1, TupleTableSlot *t2)
+TupleTableSlot *                               /* return: a tuple or NULL */
+ExecUnique(UniqueState *node)
 {
-    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;
+       Unique     *plannode = (Unique *) node->ps.plan;
+       TupleTableSlot *resultTupleSlot;
+       TupleTableSlot *slot;
+       PlanState  *outerPlan;
+
+       /*
+        * get information from the node
+        */
+       outerPlan = outerPlanState(node);
+       resultTupleSlot = node->ps.ps_ResultTupleSlot;
+
+       /*
+        * now loop, returning only non-duplicate tuples. We assume that the
+        * tuples arrive in sorted order so we can detect duplicates easily. The
+        * first tuple of each group is returned.
+        */
+       for (;;)
+       {
+               /*
+                * fetch a tuple from the outer subplan
+                */
+               slot = ExecProcNode(outerPlan);
+               if (TupIsNull(slot))
+               {
+                       /* end of subplan, so we're done */
+                       ExecClearTuple(resultTupleSlot);
+                       return NULL;
+               }
+
+               /*
+                * Always return the first tuple from the subplan.
+                */
+               if (TupIsNull(resultTupleSlot))
+                       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, resultTupleSlot,
+                                                        plannode->numCols, plannode->uniqColIdx,
+                                                        node->eqfunctions,
+                                                        node->tempContext))
+                       break;
+       }
+
+       /*
+        * We have a new tuple different from the previous saved tuple (if any).
+        * Save it and return it.  We must copy it because the source subplan
+        * won't guarantee that this source tuple is still accessible after
+        * fetching the next source tuple.
+        */
+       return ExecCopySlot(resultTupleSlot, slot);
 }
 
 /* ----------------------------------------------------------------
- *     ExecUnique
+ *             ExecInitUnique
  *
- *     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.
+ *             This initializes the unique node state structures and
+ *             the node's subplan.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *               /* return: a tuple or NULL */
-ExecUnique(Unique *node)
+UniqueState *
+ExecInitUnique(Unique *node, EState *estate, int eflags)
 {
-    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;
+       UniqueState *uniquestate;
+
+       /* check for unsupported flags */
+       Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
 
-    if (uniqueAttr) {
-      tupDesc = ExecGetResultType(uniquestate);
-      typoutput = typtoout((Oid)tupDesc->attrs[uniqueAttrNum-1]->atttypid);
-    }
-      
-    /* ----------------
-     * 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
-        * ----------------
+       /*
+        * create state structure
         */
-       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.  
-        * ----------------
+       uniquestate = makeNode(UniqueState);
+       uniquestate->ps.plan = (Plan *) node;
+       uniquestate->ps.state = estate;
+
+       /*
+        * Miscellaneous initialization
+        *
+        * Unique nodes have no ExprContext initialization because they never call
+        * ExecQual or ExecProject.  But they do need a per-tuple memory context
+        * anyway for calling execTuplesMatch.
         */
-       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.
-        * ----------------
+       uniquestate->tempContext =
+               AllocSetContextCreate(CurrentMemoryContext,
+                                                         "Unique",
+                                                         ALLOCSET_DEFAULT_MINSIZE,
+                                                         ALLOCSET_DEFAULT_INITSIZE,
+                                                         ALLOCSET_DEFAULT_MAXSIZE);
+
+       /*
+        * Tuple table initialization
         */
+       ExecInitResultTupleSlot(estate, &uniquestate->ps);
 
-       if (uniqueAttr) {
-         /* to check equality, we check to see if the typoutput
-            of the attributes are equal */
-         bool isNull1,isNull2;
-         char *attr1, *attr2;
-         char *val1, *val2;
+       /*
+        * then initialize outer plan
+        */
+       outerPlanState(uniquestate) = ExecInitNode(outerPlan(node), estate, eflags);
 
-         attr1 = heap_getattr(slot->val, InvalidBuffer, 
-                              uniqueAttrNum, tupDesc,&isNull1);
-         attr2 = heap_getattr(resultTupleSlot->val, InvalidBuffer, 
-                              uniqueAttrNum, tupDesc,&isNull2);
+       /*
+        * unique nodes do no projections, so initialize projection info for this
+        * node appropriately
+        */
+       ExecAssignResultTypeFromTL(&uniquestate->ps);
+       uniquestate->ps.ps_ProjInfo = NULL;
 
-         if (isNull1 == isNull2) {
-           if (isNull1) /* both are null, they are equal */
-             continue;
-           val1 = fmgr(typoutput, attr1, gettypelem(tupDesc->attrs[uniqueAttrNum-1]->atttypid));
-           val2 = fmgr(typoutput, attr2, gettypelem(tupDesc->attrs[uniqueAttrNum-1]->atttypid));
-           /* now, val1 and val2 are ascii representations so we can
-              use strcmp for comparison */
-           if (strcmp(val1,val2) == 0) /* they are equal */
-             continue;
-           else
-             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;
+       /*
+        * Precompute fmgr lookup data for inner loop
+        */
+       uniquestate->eqfunctions =
+               execTuplesMatchPrepare(node->numCols,
+                                                          node->uniqOperators);
+
+       return uniquestate;
 }
 
 /* ----------------------------------------------------------------
- *     ExecInitUnique
+ *             ExecEndUnique
  *
- *     This initializes the unique node state structures and
- *     the node's subplan.
+ *             This shuts down the subplan and frees resources allocated
+ *             to this node.
  * ----------------------------------------------------------------
  */
-bool   /* return: initialization status */
-ExecInitUnique(Unique *node, EState *estate, Plan *parent)
+void
+ExecEndUnique(UniqueState *node)
 {
-    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;
+       /* clean up tuple table */
+       ExecClearTuple(node->ps.ps_ResultTupleSlot);
 
-      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;
+       MemoryContextDelete(node->tempContext);
 
-    /* ----------------
-     * all done.
-     * ----------------
-     */
-    return TRUE;
+       ExecEndNode(outerPlanState(node));
 }
 
-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)
+ExecReScanUnique(UniqueState *node)
 {
-    UniqueState            *uniquestate;
-    
-    uniquestate = node->uniquestate;
-    ExecEndNode(outerPlan((Plan *) node), (Plan*)node);
-    ExecClearTuple(uniquestate->cs_ResultTupleSlot);
-} 
+       /* must clear result tuple so first input tuple is returned */
+       ExecClearTuple(node->ps.ps_ResultTupleSlot);
+
+       /*
+        * if chgParam of subnode is not null then plan will be re-scanned by
+        * first ExecProcNode.
+        */
+       if (node->ps.lefttree->chgParam == NULL)
+               ExecReScan(node->ps.lefttree);
+}