shared by nodeGroup, nodeAgg, and soon nodeSubplan.
# Makefile for executor
#
# IDENTIFICATION
-# $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.19 2002/05/12 23:43:02 tgl Exp $
+# $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.20 2003/01/10 23:54:24 tgl Exp $
#
#-------------------------------------------------------------------------
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
-OBJS = execAmi.o execJunk.o execMain.o \
+OBJS = execAmi.o execGrouping.o execJunk.o execMain.o \
execProcnode.o execQual.o execScan.o execTuples.o \
execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o nodeHash.o \
nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * execGrouping.c
+ * executor utility routines for grouping, hashing, and aggregation
+ *
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/execGrouping.c,v 1.1 2003/01/10 23:54:24 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/hash.h"
+#include "access/heapam.h"
+#include "executor/executor.h"
+#include "parser/parse_oper.h"
+#include "utils/memutils.h"
+
+
+/*****************************************************************************
+ * Utility routines for grouping tuples together
+ *
+ * These routines actually implement SQL's notion of "distinct/not distinct".
+ * Two tuples match if they are not distinct in all the compared columns,
+ * i.e., the column values are either both null, or both non-null and equal.
+ *****************************************************************************/
+
+/*
+ * execTuplesMatch
+ * Return true if two tuples match in all the indicated fields.
+ * This is used to detect group boundaries in nodeGroup and nodeAgg,
+ * 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
+ * evalContext: short-term memory context for executing the functions
+ *
+ * NB: evalContext is reset each time!
+ */
+bool
+execTuplesMatch(HeapTuple tuple1,
+ HeapTuple tuple2,
+ TupleDesc tupdesc,
+ int numCols,
+ AttrNumber *matchColIdx,
+ FmgrInfo *eqfunctions,
+ MemoryContext evalContext)
+{
+ MemoryContext oldContext;
+ bool result;
+ int i;
+
+ /* Reset and switch into the temp context. */
+ MemoryContextReset(evalContext);
+ oldContext = MemoryContextSwitchTo(evalContext);
+
+ /*
+ * 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 if we are dealing with
+ * sorted input.
+ */
+ result = true;
+
+ for (i = numCols; --i >= 0;)
+ {
+ AttrNumber att = matchColIdx[i];
+ Datum attr1,
+ attr2;
+ bool isNull1,
+ isNull2;
+
+ attr1 = heap_getattr(tuple1,
+ att,
+ tupdesc,
+ &isNull1);
+
+ attr2 = heap_getattr(tuple2,
+ att,
+ tupdesc,
+ &isNull2);
+
+ if (isNull1 != isNull2)
+ {
+ result = false; /* one null and one not; they aren't equal */
+ break;
+ }
+
+ if (isNull1)
+ continue; /* both are null, treat as equal */
+
+ /* Apply the type-specific equality function */
+
+ if (!DatumGetBool(FunctionCall2(&eqfunctions[i],
+ attr1, attr2)))
+ {
+ result = false; /* they aren't equal */
+ break;
+ }
+ }
+
+ MemoryContextSwitchTo(oldContext);
+
+ return result;
+}
+
+
+/*
+ * execTuplesMatchPrepare
+ * Look up the equality functions needed for execTuplesMatch.
+ * The result is a palloc'd array.
+ */
+FmgrInfo *
+execTuplesMatchPrepare(TupleDesc tupdesc,
+ int numCols,
+ AttrNumber *matchColIdx)
+{
+ FmgrInfo *eqfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
+ int i;
+
+ for (i = 0; i < numCols; i++)
+ {
+ AttrNumber att = matchColIdx[i];
+ Oid typid = tupdesc->attrs[att - 1]->atttypid;
+ Oid eq_function;
+
+ eq_function = equality_oper_funcid(typid);
+ fmgr_info(eq_function, &eqfunctions[i]);
+ }
+
+ return eqfunctions;
+}
+
+
+/*****************************************************************************
+ * Utility routines for hashing
+ *****************************************************************************/
+
+/*
+ * ComputeHashFunc
+ *
+ * the hash function for hash joins (also used for hash aggregation)
+ *
+ * XXX this probably ought to be replaced with datatype-specific
+ * hash functions, such as those already implemented for hash indexes.
+ */
+uint32
+ComputeHashFunc(Datum key, int typLen, bool byVal)
+{
+ unsigned char *k;
+
+ if (byVal)
+ {
+ /*
+ * If it's a by-value data type, just hash the whole Datum value.
+ * This assumes that datatypes narrower than Datum are
+ * consistently padded (either zero-extended or sign-extended, but
+ * not random bits) to fill Datum; see the XXXGetDatum macros in
+ * postgres.h. NOTE: it would not work to do hash_any(&key, len)
+ * since this would get the wrong bytes on a big-endian machine.
+ */
+ k = (unsigned char *) &key;
+ typLen = sizeof(Datum);
+ }
+ else
+ {
+ if (typLen > 0)
+ {
+ /* fixed-width pass-by-reference type */
+ k = (unsigned char *) DatumGetPointer(key);
+ }
+ else if (typLen == -1)
+ {
+ /*
+ * It's a varlena type, so 'key' points to a "struct varlena".
+ * NOTE: VARSIZE returns the "real" data length plus the
+ * sizeof the "vl_len" attribute of varlena (the length
+ * information). 'key' points to the beginning of the varlena
+ * struct, so we have to use "VARDATA" to find the beginning
+ * of the "real" data. Also, we have to be careful to detoast
+ * the datum if it's toasted. (We don't worry about freeing
+ * the detoasted copy; that happens for free when the
+ * per-tuple memory context is reset in ExecHashGetBucket.)
+ */
+ struct varlena *vkey = PG_DETOAST_DATUM(key);
+
+ typLen = VARSIZE(vkey) - VARHDRSZ;
+ k = (unsigned char *) VARDATA(vkey);
+ }
+ else if (typLen == -2)
+ {
+ /* It's a null-terminated C string */
+ typLen = strlen(DatumGetCString(key)) + 1;
+ k = (unsigned char *) DatumGetPointer(key);
+ }
+ else
+ {
+ elog(ERROR, "ComputeHashFunc: Invalid typLen %d", typLen);
+ k = NULL; /* keep compiler quiet */
+ }
+ }
+
+ return DatumGetUInt32(hash_any(k, typLen));
+}
+
+
+/*****************************************************************************
+ * Utility routines for all-in-memory hash tables
+ *
+ * These routines build hash tables for grouping tuples together (eg, for
+ * hash aggregation). There is one entry for each not-distinct set of tuples
+ * presented.
+ *****************************************************************************/
+
+/*
+ * Construct an empty TupleHashTable
+ *
+ * numCols, keyColIdx: identify the tuple fields to use as lookup key
+ * eqfunctions: equality comparison functions to use
+ * nbuckets: number of buckets to make
+ * entrysize: size of each entry (at least sizeof(TupleHashEntryData))
+ * tablecxt: memory context in which to store table and table entries
+ * tempcxt: short-lived context for evaluation hash and comparison functions
+ *
+ * The eqfunctions array may be made with execTuplesMatchPrepare().
+ *
+ * Note that keyColIdx and eqfunctions must be allocated in storage that
+ * will live as long as the hashtable does.
+ */
+TupleHashTable
+BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
+ FmgrInfo *eqfunctions,
+ int nbuckets, Size entrysize,
+ MemoryContext tablecxt, MemoryContext tempcxt)
+{
+ TupleHashTable hashtable;
+ Size tabsize;
+
+ Assert(nbuckets > 0);
+ Assert(entrysize >= sizeof(TupleHashEntryData));
+
+ tabsize = sizeof(TupleHashTableData) +
+ (nbuckets - 1) * sizeof(TupleHashEntry);
+ hashtable = (TupleHashTable) MemoryContextAllocZero(tablecxt, tabsize);
+
+ hashtable->numCols = numCols;
+ hashtable->keyColIdx = keyColIdx;
+ hashtable->eqfunctions = eqfunctions;
+ hashtable->tablecxt = tablecxt;
+ hashtable->tempcxt = tempcxt;
+ hashtable->entrysize = entrysize;
+ hashtable->nbuckets = nbuckets;
+
+ return hashtable;
+}
+
+/*
+ * Find or create a hashtable entry for the tuple group containing the
+ * given tuple.
+ *
+ * On return, *isnew is true if the entry is newly created, false if it
+ * existed already. Any extra space in a new entry has been zeroed.
+ */
+TupleHashEntry
+LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
+ bool *isnew)
+{
+ int numCols = hashtable->numCols;
+ AttrNumber *keyColIdx = hashtable->keyColIdx;
+ HeapTuple tuple = slot->val;
+ TupleDesc tupdesc = slot->ttc_tupleDescriptor;
+ uint32 hashkey = 0;
+ int i;
+ int bucketno;
+ TupleHashEntry entry;
+ MemoryContext oldContext;
+
+ /* Need to run the hash function in short-lived context */
+ oldContext = MemoryContextSwitchTo(hashtable->tempcxt);
+
+ for (i = 0; i < numCols; i++)
+ {
+ AttrNumber att = keyColIdx[i];
+ Datum attr;
+ bool isNull;
+
+ /* rotate hashkey left 1 bit at each step */
+ hashkey = (hashkey << 1) | ((hashkey & 0x80000000) ? 1 : 0);
+
+ attr = heap_getattr(tuple, att, tupdesc, &isNull);
+ if (isNull)
+ continue; /* treat nulls as having hash key 0 */
+ hashkey ^= ComputeHashFunc(attr,
+ (int) tupdesc->attrs[att - 1]->attlen,
+ tupdesc->attrs[att - 1]->attbyval);
+ }
+ bucketno = hashkey % (uint32) hashtable->nbuckets;
+
+ for (entry = hashtable->buckets[bucketno];
+ entry != NULL;
+ entry = entry->next)
+ {
+ /* Quick check using hashkey */
+ if (entry->hashkey != hashkey)
+ continue;
+ if (execTuplesMatch(entry->firstTuple,
+ tuple,
+ tupdesc,
+ numCols, keyColIdx,
+ hashtable->eqfunctions,
+ hashtable->tempcxt))
+ {
+ MemoryContextSwitchTo(oldContext);
+ *isnew = false;
+ return entry;
+ }
+ }
+
+ /* Not there, so build a new one */
+ MemoryContextSwitchTo(hashtable->tablecxt);
+
+ entry = (TupleHashEntry) palloc0(hashtable->entrysize);
+
+ entry->hashkey = hashkey;
+ entry->firstTuple = heap_copytuple(tuple);
+
+ entry->next = hashtable->buckets[bucketno];
+ hashtable->buckets[bucketno] = entry;
+
+ MemoryContextSwitchTo(oldContext);
+
+ *isnew = true;
+
+ return entry;
+}
+
+/*
+ * Walk through all the entries of a hash table, in no special order.
+ * Returns NULL when no more entries remain.
+ *
+ * Iterator state must be initialized with ResetTupleHashIterator() macro.
+ */
+TupleHashEntry
+ScanTupleHashTable(TupleHashTable hashtable, TupleHashIterator *state)
+{
+ TupleHashEntry entry;
+
+ entry = state->next_entry;
+ while (entry == NULL)
+ {
+ if (state->next_bucket >= hashtable->nbuckets)
+ {
+ /* No more entries in hashtable, so done */
+ return NULL;
+ }
+ entry = hashtable->buckets[state->next_bucket++];
+ }
+ state->next_entry = entry->next;
+
+ return entry;
+}
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.101 2002/12/15 16:17:46 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.102 2003/01/10 23:54:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "catalog/pg_operator.h"
#include "executor/executor.h"
#include "executor/nodeAgg.h"
-#include "executor/nodeGroup.h"
-#include "executor/nodeHash.h"
#include "miscadmin.h"
#include "optimizer/clauses.h"
#include "parser/parse_coerce.h"
* distinct set of GROUP BY column values. We compute the hash key from
* the GROUP BY columns.
*/
+typedef struct AggHashEntryData *AggHashEntry;
+
typedef struct AggHashEntryData
{
- AggHashEntry next; /* next entry in same hash bucket */
- uint32 hashkey; /* exact hash key of this entry */
- HeapTuple firstTuple; /* copy of first tuple in this group */
+ TupleHashEntryData shared; /* common header for hash table entries */
/* per-aggregate transition status array - must be last! */
AggStatePerGroupData pergroup[1]; /* VARIABLE LENGTH ARRAY */
} AggHashEntryData; /* VARIABLE LENGTH STRUCT */
-typedef struct AggHashTableData
-{
- int nbuckets; /* number of buckets in hash table */
- AggHashEntry buckets[1]; /* VARIABLE LENGTH ARRAY */
-} AggHashTableData; /* VARIABLE LENGTH STRUCT */
-
static void initialize_aggregates(AggState *aggstate,
AggStatePerAgg peragg,
build_hash_table(AggState *aggstate)
{
Agg *node = (Agg *) aggstate->ss.ps.plan;
- AggHashTable hashtable;
- Size tabsize;
+ MemoryContext tmpmem = aggstate->tmpcontext->ecxt_per_tuple_memory;
+ Size entrysize;
Assert(node->aggstrategy == AGG_HASHED);
Assert(node->numGroups > 0);
- tabsize = sizeof(AggHashTableData) +
- (node->numGroups - 1) * sizeof(AggHashEntry);
- hashtable = (AggHashTable) MemoryContextAlloc(aggstate->aggcontext,
- tabsize);
- MemSet(hashtable, 0, tabsize);
- hashtable->nbuckets = node->numGroups;
- aggstate->hashtable = hashtable;
+
+ entrysize = sizeof(AggHashEntryData) +
+ (aggstate->numaggs - 1) * sizeof(AggStatePerGroupData);
+
+ aggstate->hashtable = BuildTupleHashTable(node->numCols,
+ node->grpColIdx,
+ aggstate->eqfunctions,
+ node->numGroups,
+ entrysize,
+ aggstate->aggcontext,
+ tmpmem);
}
/*
static AggHashEntry
lookup_hash_entry(AggState *aggstate, TupleTableSlot *slot)
{
- Agg *node = (Agg *) aggstate->ss.ps.plan;
- AggHashTable hashtable = aggstate->hashtable;
- MemoryContext tmpmem = aggstate->tmpcontext->ecxt_per_tuple_memory;
- HeapTuple tuple = slot->val;
- TupleDesc tupdesc = slot->ttc_tupleDescriptor;
- uint32 hashkey = 0;
- int i;
- int bucketno;
- AggHashEntry entry;
- MemoryContext oldContext;
- Size entrysize;
-
- /* Need to run the hash function in short-lived context */
- oldContext = MemoryContextSwitchTo(tmpmem);
-
- for (i = 0; i < node->numCols; i++)
- {
- AttrNumber att = node->grpColIdx[i];
- Datum attr;
- bool isNull;
+ AggHashEntry entry;
+ bool isnew;
- /* rotate hashkey left 1 bit at each step */
- hashkey = (hashkey << 1) | ((hashkey & 0x80000000) ? 1 : 0);
+ entry = (AggHashEntry) LookupTupleHashEntry(aggstate->hashtable,
+ slot,
+ &isnew);
- attr = heap_getattr(tuple, att, tupdesc, &isNull);
- if (isNull)
- continue; /* treat nulls as having hash key 0 */
- hashkey ^= ComputeHashFunc(attr,
- (int) tupdesc->attrs[att - 1]->attlen,
- tupdesc->attrs[att - 1]->attbyval);
- }
- bucketno = hashkey % (uint32) hashtable->nbuckets;
-
- for (entry = hashtable->buckets[bucketno];
- entry != NULL;
- entry = entry->next)
+ if (isnew)
{
- /* Quick check using hashkey */
- if (entry->hashkey != hashkey)
- continue;
- if (execTuplesMatch(entry->firstTuple,
- tuple,
- tupdesc,
- node->numCols, node->grpColIdx,
- aggstate->eqfunctions,
- tmpmem))
- {
- MemoryContextSwitchTo(oldContext);
- return entry;
- }
+ /* initialize aggregates for new tuple group */
+ initialize_aggregates(aggstate, aggstate->peragg, entry->pergroup);
}
- /* Not there, so build a new one */
- MemoryContextSwitchTo(aggstate->aggcontext);
- entrysize = sizeof(AggHashEntryData) +
- (aggstate->numaggs - 1) * sizeof(AggStatePerGroupData);
- entry = (AggHashEntry) palloc0(entrysize);
-
- entry->hashkey = hashkey;
- entry->firstTuple = heap_copytuple(tuple);
-
- entry->next = hashtable->buckets[bucketno];
- hashtable->buckets[bucketno] = entry;
-
- MemoryContextSwitchTo(oldContext);
-
- /* initialize aggregates for new tuple group */
- initialize_aggregates(aggstate, aggstate->peragg, entry->pergroup);
-
return entry;
}
aggstate->table_filled = true;
/* Initialize to walk the hash table */
- aggstate->next_hash_entry = NULL;
- aggstate->next_hash_bucket = 0;
+ ResetTupleHashIterator(&aggstate->hashiter);
}
/*
bool *aggnulls;
AggStatePerAgg peragg;
AggStatePerGroup pergroup;
- AggHashTable hashtable;
+ TupleHashTable hashtable;
AggHashEntry entry;
TupleTableSlot *firstSlot;
TupleTableSlot *resultSlot;
/*
* Find the next entry in the hash table
*/
- entry = aggstate->next_hash_entry;
- while (entry == NULL)
+ entry = (AggHashEntry) ScanTupleHashTable(hashtable,
+ &aggstate->hashiter);
+ if (entry == NULL)
{
- if (aggstate->next_hash_bucket >= hashtable->nbuckets)
- {
- /* No more entries in hashtable, so done */
- aggstate->agg_done = TRUE;
- return NULL;
- }
- entry = hashtable->buckets[aggstate->next_hash_bucket++];
+ /* No more entries in hashtable, so done */
+ aggstate->agg_done = TRUE;
+ return NULL;
}
- aggstate->next_hash_entry = entry->next;
/*
* Clear the per-output-tuple context for each group
* Store the copied first input tuple in the tuple table slot
* reserved for it, so that it can be used in ExecProject.
*/
- ExecStoreTuple(entry->firstTuple,
+ ExecStoreTuple(entry->shared.firstTuple,
firstSlot,
InvalidBuffer,
false);
numaggs = 1;
}
+ /*
+ * If we are grouping, precompute fmgr lookup data for inner loop
+ */
+ if (node->numCols > 0)
+ {
+ aggstate->eqfunctions =
+ execTuplesMatchPrepare(ExecGetScanType(&aggstate->ss),
+ node->numCols,
+ node->grpColIdx);
+ }
+
/*
* Set up aggregate-result storage in the output expr context, and also
* allocate my private per-agg working storage
aggstate->pergroup = pergroup;
}
- /*
- * If we are grouping, precompute fmgr lookup data for inner loop
- */
- if (node->numCols > 0)
- {
- aggstate->eqfunctions =
- execTuplesMatchPrepare(ExecGetScanType(&aggstate->ss),
- node->numCols,
- node->grpColIdx);
- }
-
/*
* Perform lookups of aggregate function info, and initialize the
* unchanging fields of the per-agg data
* locate group boundaries.
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.53 2002/12/15 16:17:46 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.54 2003/01/10 23:54:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
-#include "catalog/pg_operator.h"
#include "executor/executor.h"
#include "executor/nodeGroup.h"
-#include "parser/parse_oper.h"
-#include "utils/builtins.h"
-#include "utils/lsyscache.h"
-#include "utils/syscache.h"
/*
((PlanState *) node)->lefttree->chgParam == NULL)
ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
}
-
-/*****************************************************************************
- * Code shared with nodeUnique.c and nodeAgg.c
- *****************************************************************************/
-
-/*
- * execTuplesMatch
- * Return true if two tuples match in all the indicated fields.
- * This is used to detect group boundaries in nodeGroup and nodeAgg,
- * 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
- * evalContext: short-term memory context for executing the functions
- *
- * NB: evalContext is reset each time!
- */
-bool
-execTuplesMatch(HeapTuple tuple1,
- HeapTuple tuple2,
- TupleDesc tupdesc,
- int numCols,
- AttrNumber *matchColIdx,
- FmgrInfo *eqfunctions,
- MemoryContext evalContext)
-{
- MemoryContext oldContext;
- bool result;
- int i;
-
- /* Reset and switch into the temp context. */
- MemoryContextReset(evalContext);
- oldContext = MemoryContextSwitchTo(evalContext);
-
- /*
- * 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 if we are dealing with
- * sorted input.
- */
- result = true;
-
- for (i = numCols; --i >= 0;)
- {
- AttrNumber att = matchColIdx[i];
- Datum attr1,
- attr2;
- bool isNull1,
- isNull2;
-
- attr1 = heap_getattr(tuple1,
- att,
- tupdesc,
- &isNull1);
-
- attr2 = heap_getattr(tuple2,
- att,
- tupdesc,
- &isNull2);
-
- if (isNull1 != isNull2)
- {
- result = false; /* one null and one not; they aren't equal */
- break;
- }
-
- if (isNull1)
- continue; /* both are null, treat as equal */
-
- /* Apply the type-specific equality function */
-
- if (!DatumGetBool(FunctionCall2(&eqfunctions[i],
- attr1, attr2)))
- {
- result = false; /* they aren't equal */
- break;
- }
- }
-
- MemoryContextSwitchTo(oldContext);
-
- return result;
-}
-
-/*
- * execTuplesMatchPrepare
- * Look up the equality functions needed for execTuplesMatch.
- * The result is a palloc'd array.
- */
-FmgrInfo *
-execTuplesMatchPrepare(TupleDesc tupdesc,
- int numCols,
- AttrNumber *matchColIdx)
-{
- FmgrInfo *eqfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
- int i;
-
- for (i = 0; i < numCols; i++)
- {
- AttrNumber att = matchColIdx[i];
- Oid typid = tupdesc->attrs[att - 1]->atttypid;
- Oid eq_function;
-
- eq_function = equality_oper_funcid(typid);
- fmgr_info(eq_function, &eqfunctions[i]);
- }
-
- return eqfunctions;
-}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.73 2002/12/30 15:21:18 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.74 2003/01/10 23:54:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
#include "postgres.h"
-#include
-#include
-
-#include "access/hash.h"
#include "executor/execdebug.h"
#include "executor/nodeHash.h"
#include "executor/nodeHashjoin.h"
return NULL;
}
-/* ----------------------------------------------------------------
- * ComputeHashFunc
- *
- * the hash function for hash joins (also used for hash aggregation)
- *
- * XXX this probably ought to be replaced with datatype-specific
- * hash functions, such as those already implemented for hash indexes.
- * ----------------------------------------------------------------
- */
-uint32
-ComputeHashFunc(Datum key, int typLen, bool byVal)
-{
- unsigned char *k;
-
- if (byVal)
- {
- /*
- * If it's a by-value data type, just hash the whole Datum value.
- * This assumes that datatypes narrower than Datum are
- * consistently padded (either zero-extended or sign-extended, but
- * not random bits) to fill Datum; see the XXXGetDatum macros in
- * postgres.h. NOTE: it would not work to do hash_any(&key, len)
- * since this would get the wrong bytes on a big-endian machine.
- */
- k = (unsigned char *) &key;
- typLen = sizeof(Datum);
- }
- else
- {
- if (typLen > 0)
- {
- /* fixed-width pass-by-reference type */
- k = (unsigned char *) DatumGetPointer(key);
- }
- else if (typLen == -1)
- {
- /*
- * It's a varlena type, so 'key' points to a "struct varlena".
- * NOTE: VARSIZE returns the "real" data length plus the
- * sizeof the "vl_len" attribute of varlena (the length
- * information). 'key' points to the beginning of the varlena
- * struct, so we have to use "VARDATA" to find the beginning
- * of the "real" data. Also, we have to be careful to detoast
- * the datum if it's toasted. (We don't worry about freeing
- * the detoasted copy; that happens for free when the
- * per-tuple memory context is reset in ExecHashGetBucket.)
- */
- struct varlena *vkey = PG_DETOAST_DATUM(key);
-
- typLen = VARSIZE(vkey) - VARHDRSZ;
- k = (unsigned char *) VARDATA(vkey);
- }
- else if (typLen == -2)
- {
- /* It's a null-terminated C string */
- typLen = strlen(DatumGetCString(key)) + 1;
- k = (unsigned char *) DatumGetPointer(key);
- }
- else
- {
- elog(ERROR, "ComputeHashFunc: Invalid typLen %d", typLen);
- k = NULL; /* keep compiler quiet */
- }
- }
-
- return DatumGetUInt32(hash_any(k, typLen));
-}
-
/* ----------------------------------------------------------------
* ExecHashTableReset
*
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeSetOp.c,v 1.8 2002/12/15 16:17:46 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeSetOp.c,v 1.9 2003/01/10 23:54:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "access/heapam.h"
#include "executor/executor.h"
-#include "executor/nodeGroup.h"
#include "executor/nodeSetOp.h"
+
/* ----------------------------------------------------------------
* ExecSetOp
* ----------------------------------------------------------------
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.36 2002/12/15 16:17:46 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.37 2003/01/10 23:54:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "access/heapam.h"
#include "executor/executor.h"
-#include "executor/nodeGroup.h"
#include "executor/nodeUnique.h"
+
/* ----------------------------------------------------------------
* ExecUnique
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: executor.h,v 1.85 2002/12/15 21:01:34 tgl Exp $
+ * $Id: executor.h,v 1.86 2003/01/10 23:54:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern void ExecRestrPos(PlanState *node);
extern bool ExecSupportsMarkRestore(NodeTag plantype);
+/*
+ * prototypes from functions in execGrouping.c
+ */
+extern bool execTuplesMatch(HeapTuple tuple1,
+ HeapTuple tuple2,
+ TupleDesc tupdesc,
+ int numCols,
+ AttrNumber *matchColIdx,
+ FmgrInfo *eqfunctions,
+ MemoryContext evalContext);
+extern FmgrInfo *execTuplesMatchPrepare(TupleDesc tupdesc,
+ int numCols,
+ AttrNumber *matchColIdx);
+extern uint32 ComputeHashFunc(Datum key, int typLen, bool byVal);
+extern TupleHashTable BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
+ FmgrInfo *eqfunctions,
+ int nbuckets, Size entrysize,
+ MemoryContext tablecxt,
+ MemoryContext tempcxt);
+extern TupleHashEntry LookupTupleHashEntry(TupleHashTable hashtable,
+ TupleTableSlot *slot,
+ bool *isnew);
+extern TupleHashEntry ScanTupleHashTable(TupleHashTable hashtable,
+ TupleHashIterator *state);
+
/*
* prototypes from functions in execJunk.c
*/
/*-------------------------------------------------------------------------
*
* nodeAgg.h
- *
+ * prototypes for nodeAgg.c
*
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: nodeAgg.h,v 1.18 2002/12/05 15:50:36 tgl Exp $
+ * $Id: nodeAgg.h,v 1.19 2003/01/10 23:54:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: nodeGroup.h,v 1.23 2002/12/05 15:50:37 tgl Exp $
+ * $Id: nodeGroup.h,v 1.24 2003/01/10 23:54:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern void ExecEndGroup(GroupState *node);
extern void ExecReScanGroup(GroupState *node, ExprContext *exprCtxt);
-extern bool execTuplesMatch(HeapTuple tuple1,
- HeapTuple tuple2,
- TupleDesc tupdesc,
- int numCols,
- AttrNumber *matchColIdx,
- FmgrInfo *eqfunctions,
- MemoryContext evalContext);
-extern FmgrInfo *execTuplesMatchPrepare(TupleDesc tupdesc,
- int numCols,
- AttrNumber *matchColIdx);
-
#endif /* NODEGROUP_H */
/*-------------------------------------------------------------------------
*
* nodeHash.h
- *
+ * prototypes for nodeHash.c
*
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: nodeHash.h,v 1.28 2002/12/30 15:21:23 tgl Exp $
+ * $Id: nodeHash.h,v 1.29 2003/01/10 23:54:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
int *virtualbuckets,
int *physicalbuckets,
int *numbatches);
-extern uint32 ComputeHashFunc(Datum key, int typLen, bool byVal);
#endif /* NODEHASH_H */
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: execnodes.h,v 1.89 2003/01/10 21:08:15 tgl Exp $
+ * $Id: execnodes.h,v 1.90 2003/01/10 23:54:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
} EState;
+/* ----------------------------------------------------------------
+ * Tuple Hash Tables
+ *
+ * All-in-memory tuple hash tables are used for a number of purposes.
+ * ----------------------------------------------------------------
+ */
+typedef struct TupleHashEntryData *TupleHashEntry;
+typedef struct TupleHashTableData *TupleHashTable;
+
+typedef struct TupleHashEntryData
+{
+ TupleHashEntry next; /* next entry in same hash bucket */
+ uint32 hashkey; /* exact hash key of this entry */
+ HeapTuple firstTuple; /* copy of first tuple in this group */
+ /* there may be additional data beyond the end of this struct */
+} TupleHashEntryData; /* VARIABLE LENGTH STRUCT */
+
+typedef struct TupleHashTableData
+{
+ int numCols; /* number of columns in lookup key */
+ AttrNumber *keyColIdx; /* attr numbers of key columns */
+ FmgrInfo *eqfunctions; /* lookup data for comparison functions */
+ MemoryContext tablecxt; /* memory context containing table */
+ MemoryContext tempcxt; /* context for function evaluations */
+ Size entrysize; /* actual size to make each hash entry */
+ int nbuckets; /* number of buckets in hash table */
+ TupleHashEntry buckets[1]; /* VARIABLE LENGTH ARRAY */
+} TupleHashTableData; /* VARIABLE LENGTH STRUCT */
+
+typedef struct
+{
+ TupleHashEntry next_entry; /* next entry in current chain */
+ int next_bucket; /* next chain */
+} TupleHashIterator;
+
+#define ResetTupleHashIterator(iter) \
+ ((iter)->next_entry = NULL, \
+ (iter)->next_bucket = 0)
+
+
/* ----------------------------------------------------------------
* Expression State Trees
*
* SubPlanState node
* ----------------
*/
-/* this struct is private in nodeSubplan.c: */
-typedef struct SubPlanHashTableData *SubPlanHashTable;
-
typedef struct SubPlanState
{
ExprState xprstate;
bool needShutdown; /* TRUE = need to shutdown subplan */
HeapTuple curTuple; /* copy of most recent tuple from subplan */
/* these are used when hashing the subselect's output: */
- SubPlanHashTable hashtable; /* hash table for no-nulls subselect rows */
- SubPlanHashTable hashnulls; /* hash table for rows with null(s) */
+ TupleHashTable hashtable; /* hash table for no-nulls subselect rows */
+ TupleHashTable hashnulls; /* hash table for rows with null(s) */
} SubPlanState;
/* ----------------
/* these structs are private in nodeAgg.c: */
typedef struct AggStatePerAggData *AggStatePerAgg;
typedef struct AggStatePerGroupData *AggStatePerGroup;
-typedef struct AggHashEntryData *AggHashEntry;
-typedef struct AggHashTableData *AggHashTable;
typedef struct AggState
{
AggStatePerGroup pergroup; /* per-Aggref-per-group working state */
HeapTuple grp_firstTuple; /* copy of first tuple of current group */
/* these fields are used in AGG_HASHED mode: */
- AggHashTable hashtable; /* hash table with one entry per group */
+ TupleHashTable hashtable; /* hash table with one entry per group */
bool table_filled; /* hash table filled yet? */
- AggHashEntry next_hash_entry; /* next entry in current chain */
- int next_hash_bucket; /* next chain */
+ TupleHashIterator hashiter; /* for iterating through hash table */
} AggState;
/* ----------------