aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/execTuples.c4
-rw-r--r--src/backend/executor/nodeGroup.c210
-rw-r--r--src/backend/executor/nodeUnique.c232
3 files changed, 181 insertions, 265 deletions
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index f600a78fd24..1cbae3519a9 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.35 2000/01/26 05:56:22 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.36 2000/01/27 18:11:27 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -753,7 +753,7 @@ NodeGetResultTupleSlot(Plan *node)
{
UniqueState *uniquestate = ((Unique *) node)->uniquestate;
- slot = uniquestate->cs_ResultTupleSlot;
+ slot = uniquestate->cstate.cs_ResultTupleSlot;
}
break;
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index 017929424bc..cad023776d8 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -9,12 +9,13 @@
*
* DESCRIPTION
* The Group node is designed for handling queries with a GROUP BY clause.
- * It's outer plan must be a sort node. It assumes that the tuples it gets
- * back from the outer plan is sorted in the order specified by the group
- * columns. (ie. tuples from the same group are consecutive)
+ * Its outer plan must deliver tuples that are sorted in the order
+ * specified by the grouping columns (ie. tuples from the same group are
+ * consecutive). That way, we just have to compare adjacent tuples to
+ * locate group boundaries.
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.32 2000/01/26 05:56:22 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.33 2000/01/27 18:11:27 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -23,13 +24,14 @@
#include "access/heapam.h"
#include "access/printtup.h"
+#include "catalog/pg_operator.h"
#include "executor/executor.h"
#include "executor/nodeGroup.h"
+#include "parser/parse_oper.h"
+#include "parser/parse_type.h"
static TupleTableSlot *ExecGroupEveryTuple(Group *node);
static TupleTableSlot *ExecGroupOneTuple(Group *node);
-static bool sameGroup(HeapTuple oldslot, HeapTuple newslot,
- int numCols, AttrNumber *grpColIdx, TupleDesc tupdesc);
/* ---------------------------------------
* ExecGroup -
@@ -38,11 +40,11 @@ static bool sameGroup(HeapTuple oldslot, HeapTuple newslot,
* tuplePerGroup is TRUE, every tuple from the same group will be
* returned, followed by a NULL at the end of each group. This is
* useful for Agg node which needs to aggregate over tuples of the same
- * group. (eg. SELECT salary, count{*} FROM emp GROUP BY salary)
+ * group. (eg. SELECT salary, count(*) FROM emp GROUP BY salary)
*
* If tuplePerGroup is FALSE, only one tuple per group is returned. The
* tuple returned contains only the group columns. NULL is returned only
- * at the end when no more groups is present. This is useful when
+ * at the end when no more groups are present. This is useful when
* the query does not involve aggregates. (eg. SELECT salary FROM emp
* GROUP BY salary)
* ------------------------------------------
@@ -66,6 +68,7 @@ ExecGroupEveryTuple(Group *node)
GroupState *grpstate;
EState *estate;
ExprContext *econtext;
+ TupleDesc tupdesc;
HeapTuple outerTuple = NULL;
HeapTuple firsttuple;
@@ -87,6 +90,8 @@ ExecGroupEveryTuple(Group *node)
econtext = grpstate->csstate.cstate.cs_ExprContext;
+ tupdesc = ExecGetScanType(&grpstate->csstate);
+
/* if we haven't returned first tuple of new group yet ... */
if (grpstate->grp_useFirstTuple)
{
@@ -110,20 +115,25 @@ ExecGroupEveryTuple(Group *node)
outerTuple = outerslot->val;
firsttuple = grpstate->grp_firstTuple;
- /* this should occur on the first call only */
if (firsttuple == NULL)
+ {
+ /* this should occur on the first call only */
grpstate->grp_firstTuple = heap_copytuple(outerTuple);
+ }
else
{
-
/*
* Compare with first tuple and see if this tuple is of the
* same group.
*/
- if (!sameGroup(firsttuple, outerTuple,
- node->numCols, node->grpColIdx,
- ExecGetScanType(&grpstate->csstate)))
+ if (! execTuplesMatch(firsttuple, outerTuple,
+ tupdesc,
+ node->numCols, node->grpColIdx,
+ grpstate->eqfunctions))
{
+ /*
+ * No; save the tuple to return it next time, and return NULL
+ */
grpstate->grp_useFirstTuple = TRUE;
heap_freetuple(firsttuple);
grpstate->grp_firstTuple = heap_copytuple(outerTuple);
@@ -164,6 +174,7 @@ ExecGroupOneTuple(Group *node)
GroupState *grpstate;
EState *estate;
ExprContext *econtext;
+ TupleDesc tupdesc;
HeapTuple outerTuple = NULL;
HeapTuple firsttuple;
@@ -185,10 +196,12 @@ ExecGroupOneTuple(Group *node)
econtext = node->grpstate->csstate.cstate.cs_ExprContext;
+ tupdesc = ExecGetScanType(&grpstate->csstate);
+
firsttuple = grpstate->grp_firstTuple;
- /* this should occur on the first call only */
if (firsttuple == NULL)
{
+ /* this should occur on the first call only */
outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
if (TupIsNull(outerslot))
{
@@ -213,14 +226,14 @@ ExecGroupOneTuple(Group *node)
}
outerTuple = outerslot->val;
- /* ----------------
- * Compare with first tuple and see if this tuple is of
- * the same group.
- * ----------------
+ /*
+ * Compare with first tuple and see if this tuple is of the
+ * same group.
*/
- if ((!sameGroup(firsttuple, outerTuple,
- node->numCols, node->grpColIdx,
- ExecGetScanType(&grpstate->csstate))))
+ if (! execTuplesMatch(firsttuple, outerTuple,
+ tupdesc,
+ node->numCols, node->grpColIdx,
+ grpstate->eqfunctions))
break;
}
@@ -311,6 +324,14 @@ ExecInitGroup(Group *node, EState *estate, Plan *parent)
ExecAssignResultTypeFromTL((Plan *) node, &grpstate->csstate.cstate);
ExecAssignProjectionInfo((Plan *) node, &grpstate->csstate.cstate);
+ /*
+ * Precompute fmgr lookup data for inner loop
+ */
+ grpstate->eqfunctions =
+ execTuplesMatchPrepare(ExecGetScanType(&grpstate->csstate),
+ node->numCols,
+ node->grpColIdx);
+
return TRUE;
}
@@ -347,94 +368,121 @@ ExecEndGroup(Group *node)
}
}
+void
+ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
+{
+ GroupState *grpstate = node->grpstate;
+
+ grpstate->grp_useFirstTuple = FALSE;
+ grpstate->grp_done = FALSE;
+ if (grpstate->grp_firstTuple != NULL)
+ {
+ heap_freetuple(grpstate->grp_firstTuple);
+ grpstate->grp_firstTuple = NULL;
+ }
+
+ if (((Plan *) node)->lefttree &&
+ ((Plan *) node)->lefttree->chgParam == NULL)
+ ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
+}
+
/*****************************************************************************
- *
+ * Code shared with nodeUnique.c
*****************************************************************************/
/*
- * code swiped from nodeUnique.c
+ * execTuplesMatch
+ * Return true if two tuples match in all the indicated fields.
+ * This is used to detect group boundaries in nodeGroup, and to
+ * decide whether two tuples are distinct or not in nodeUnique.
+ *
+ * tuple1, tuple2: the tuples to compare
+ * tupdesc: tuple descriptor applying to both tuples
+ * numCols: the number of attributes to be examined
+ * matchColIdx: array of attribute column numbers
+ * eqFunctions: array of fmgr lookup info for the equality functions to use
*/
-static bool
-sameGroup(HeapTuple oldtuple,
- HeapTuple newtuple,
- int numCols,
- AttrNumber *grpColIdx,
- TupleDesc tupdesc)
+bool
+execTuplesMatch(HeapTuple tuple1,
+ HeapTuple tuple2,
+ TupleDesc tupdesc,
+ int numCols,
+ AttrNumber *matchColIdx,
+ FmgrInfo *eqfunctions)
{
- bool isNull1,
- isNull2;
- Datum attr1,
- attr2;
- char *val1,
- *val2;
int i;
- AttrNumber att;
- Oid typoutput,
- typelem;
- for (i = 0; i < numCols; i++)
+ /*
+ * We cannot report a match without checking all the fields, but we
+ * can report a non-match as soon as we find unequal fields. So,
+ * start comparing at the last field (least significant sort key).
+ * That's the most likely to be different...
+ */
+ for (i = numCols; --i >= 0; )
{
- att = grpColIdx[i];
- getTypeOutAndElem((Oid) tupdesc->attrs[att - 1]->atttypid,
- &typoutput, &typelem);
-
- attr1 = heap_getattr(oldtuple,
+ AttrNumber att = matchColIdx[i];
+ Datum attr1,
+ attr2;
+ bool isNull1,
+ isNull2;
+ Datum equal;
+
+ attr1 = heap_getattr(tuple1,
att,
tupdesc,
&isNull1);
- attr2 = heap_getattr(newtuple,
+ attr2 = heap_getattr(tuple2,
att,
tupdesc,
&isNull2);
- if (isNull1 == isNull2)
- {
- if (isNull1) /* both are null, they are equal */
- continue;
+ if (isNull1 != isNull2)
+ return FALSE; /* one null and one not; they aren't equal */
- val1 = fmgr(typoutput, attr1, typelem,
- tupdesc->attrs[att - 1]->atttypmod);
- val2 = fmgr(typoutput, attr2, typelem,
- tupdesc->attrs[att - 1]->atttypmod);
+ if (isNull1)
+ continue; /* both are null, treat as equal */
- /*
- * now, val1 and val2 are ascii representations so we can use
- * strcmp for comparison
- */
- if (strcmp(val1, val2) != 0)
- {
- pfree(val1);
- pfree(val2);
- return FALSE;
- }
- pfree(val1);
- pfree(val2);
- }
- else
- {
- /* one is null and the other isn't, they aren't equal */
+ /* Apply the type-specific equality function */
+
+ equal = (Datum) (*fmgr_faddr(& eqfunctions[i])) (attr1, attr2);
+
+ if (DatumGetInt32(equal) == 0)
return FALSE;
- }
}
return TRUE;
}
-void
-ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
+/*
+ * execTuplesMatchPrepare
+ * Look up the equality functions needed for execTuplesMatch.
+ * The result is a palloc'd array.
+ */
+FmgrInfo *
+execTuplesMatchPrepare(TupleDesc tupdesc,
+ int numCols,
+ AttrNumber *matchColIdx)
{
- GroupState *grpstate = node->grpstate;
+ FmgrInfo *eqfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
+ int i;
- grpstate->grp_useFirstTuple = FALSE;
- grpstate->grp_done = FALSE;
- if (grpstate->grp_firstTuple != NULL)
+ for (i = 0; i < numCols; i++)
{
- heap_freetuple(grpstate->grp_firstTuple);
- grpstate->grp_firstTuple = NULL;
+ AttrNumber att = matchColIdx[i];
+ Oid typid = tupdesc->attrs[att - 1]->atttypid;
+ Operator eq_operator;
+ Form_pg_operator pgopform;
+
+ eq_operator = oper("=", typid, typid, true);
+ if (!HeapTupleIsValid(eq_operator))
+ {
+ elog(ERROR, "Unable to identify an equality operator for type '%s'",
+ typeidTypeName(typid));
+ }
+ pgopform = (Form_pg_operator) GETSTRUCT(eq_operator);
+ fmgr_info(pgopform->oprcode, & eqfunctions[i]);
}
- if (((Plan *) node)->lefttree &&
- ((Plan *) node)->lefttree->chgParam == NULL)
- ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
+ return eqfunctions;
}
diff --git a/src/backend/executor/nodeUnique.c b/src/backend/executor/nodeUnique.c
index 6078e0f68a9..f9f1fe81ab3 100644
--- a/src/backend/executor/nodeUnique.c
+++ b/src/backend/executor/nodeUnique.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.26 2000/01/26 05:56:24 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.27 2000/01/27 18:11:27 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -29,79 +29,14 @@
#include "access/heapam.h"
#include "access/printtup.h"
#include "executor/executor.h"
+#include "executor/nodeGroup.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 */
@@ -111,11 +46,7 @@ ExecUnique(Unique *node)
TupleTableSlot *resultTupleSlot;
TupleTableSlot *slot;
Plan *outerPlan;
- char *uniqueAttr;
- AttrNumber uniqueAttrNum;
TupleDesc tupDesc;
- Oid typoutput,
- typelem;
/* ----------------
* get information from the node
@@ -123,22 +54,8 @@ ExecUnique(Unique *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;
- }
+ resultTupleSlot = uniquestate->cstate.cs_ResultTupleSlot;
+ tupDesc = ExecGetResultType(& uniquestate->cstate);
/* ----------------
* now loop, returning only non-duplicate tuples.
@@ -157,83 +74,38 @@ ExecUnique(Unique *node)
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.
+ * Always return the first tuple from the subplan.
* ----------------
*/
- if (TupIsNull(resultTupleSlot))
+ if (uniquestate->priorTuple == NULL)
break;
/* ----------------
- * now test if the new tuple and the previous
+ * 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 (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;
- }
-
+ 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
- * so we save it in the saved tuple slot. We copy the tuple
- * so we don't increment the buffer ref count.
+ * 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,
@@ -254,7 +126,6 @@ ExecInitUnique(Unique *node, EState *estate, Plan *parent)
{
UniqueState *uniquestate;
Plan *outerPlan;
- char *uniqueAttr;
/* ----------------
* assign execution state to node
@@ -268,10 +139,10 @@ ExecInitUnique(Unique *node, EState *estate, Plan *parent)
*/
uniquestate = makeNode(UniqueState);
node->uniquestate = uniquestate;
- uniqueAttr = node->uniqueAttr;
+ uniquestate->priorTuple = NULL;
/* ----------------
- * Miscellanious initialization
+ * Miscellaneous initialization
*
* + assign node's base_id
* + assign debugging hooks and
@@ -280,14 +151,14 @@ ExecInitUnique(Unique *node, EState *estate, Plan *parent)
* they never call ExecQual or ExecTargetList.
* ----------------
*/
- ExecAssignNodeBaseInfo(estate, uniquestate, parent);
+ ExecAssignNodeBaseInfo(estate, & uniquestate->cstate, parent);
#define UNIQUE_NSLOTS 1
/* ------------
* Tuple table initialization
* ------------
*/
- ExecInitResultTupleSlot(estate, uniquestate);
+ ExecInitResultTupleSlot(estate, & uniquestate->cstate);
/* ----------------
* then initialize outer plan
@@ -301,31 +172,17 @@ ExecInitUnique(Unique *node, EState *estate, Plan *parent)
* 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(NameStr(tupDesc->attrs[i]->attname), uniqueAttr) != 0)
- i++;
- node->uniqueAttrNum = i + 1; /* attribute numbers start from 1 */
- }
- else
- node->uniqueAttrNum = InvalidAttrNumber;
+ ExecAssignResultTypeFromOuterPlan((Plan *) node, & uniquestate->cstate);
+ uniquestate->cstate.cs_ProjInfo = NULL;
- /* ----------------
- * all done.
- * ----------------
+ /*
+ * Precompute fmgr lookup data for inner loop
*/
+ uniquestate->eqfunctions =
+ execTuplesMatchPrepare(ExecGetResultType(& uniquestate->cstate),
+ node->numCols,
+ node->uniqColIdx);
+
return TRUE;
}
@@ -347,11 +204,17 @@ ExecCountSlotsUnique(Unique *node)
void
ExecEndUnique(Unique *node)
{
- UniqueState *uniquestate;
+ UniqueState *uniquestate = node->uniquestate;
- uniquestate = node->uniquestate;
ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
- ExecClearTuple(uniquestate->cs_ResultTupleSlot);
+
+ /* clean up tuple table */
+ ExecClearTuple(uniquestate->cstate.cs_ResultTupleSlot);
+ if (uniquestate->priorTuple != NULL)
+ {
+ heap_freetuple(uniquestate->priorTuple);
+ uniquestate->priorTuple = NULL;
+ }
}
@@ -360,7 +223,12 @@ ExecReScanUnique(Unique *node, ExprContext *exprCtxt, Plan *parent)
{
UniqueState *uniquestate = node->uniquestate;
- ExecClearTuple(uniquestate->cs_ResultTupleSlot);
+ 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