Commit bff0422b authored by Tom Lane's avatar Tom Lane

Revise hash join and hash aggregation code to use the same datatype-

specific hash functions used by hash indexes, rather than the old
not-datatype-aware ComputeHashFunc routine.  This makes it safe to do
hash joining on several datatypes that previously couldn't use hashing.
The sets of datatypes that are hash indexable and hash joinable are now
exactly the same, whereas before each had some that weren't in the other.
parent 0dda75f6
<!-- <!--
Documentation of the system catalogs, directed toward PostgreSQL developers Documentation of the system catalogs, directed toward PostgreSQL developers
$Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.71 2003/05/28 16:03:55 tgl Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.72 2003/06/22 22:04:54 tgl Exp $
--> -->
<chapter id="catalogs"> <chapter id="catalogs">
...@@ -2525,7 +2525,7 @@ ...@@ -2525,7 +2525,7 @@
<entry><structfield>oprcanhash</structfield></entry> <entry><structfield>oprcanhash</structfield></entry>
<entry><type>bool</type></entry> <entry><type>bool</type></entry>
<entry></entry> <entry></entry>
<entry>This operator supports hash joins.</entry> <entry>This operator supports hash joins</entry>
</row> </row>
<row> <row>
......
<!-- <!--
$Header: /cvsroot/pgsql/doc/src/sgml/xfunc.sgml,v 1.68 2003/05/29 20:40:36 tgl Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/xfunc.sgml,v 1.69 2003/06/22 22:04:54 tgl Exp $
--> -->
<sect1 id="xfunc"> <sect1 id="xfunc">
...@@ -1442,11 +1442,10 @@ concat_text(PG_FUNCTION_ARGS) ...@@ -1442,11 +1442,10 @@ concat_text(PG_FUNCTION_ARGS)
<listitem> <listitem>
<para> <para>
Always zero the bytes of your structures using Always zero the bytes of your structures using
<function>memset</function> or <function>bzero</function>. <function>memset</function>. Without this, it's difficult to
Several routines (such as the hash access method, hash joins, support hash indexes or hash joins, as you must pick out only
and the sort algorithm) compute functions of the raw bits the significant bits of your data structure to compute a hash.
contained in your structure. Even if you initialize all Even if you initialize all fields of your structure, there may be
fields of your structure, there may be several bytes of
alignment padding (holes in the structure) that may contain alignment padding (holes in the structure) that may contain
garbage values. garbage values.
</para> </para>
......
<!-- <!--
$Header: /cvsroot/pgsql/doc/src/sgml/xoper.sgml,v 1.23 2003/04/10 01:22:45 petere Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/xoper.sgml,v 1.24 2003/06/22 22:04:54 tgl Exp $
--> -->
<sect1 id="xoper"> <sect1 id="xoper">
...@@ -315,46 +315,34 @@ table1.column1 OP table2.column2 ...@@ -315,46 +315,34 @@ table1.column1 OP table2.column2
same hash code. If two values get put in different hash buckets, the same hash code. If two values get put in different hash buckets, the
join will never compare them at all, implicitly assuming that the join will never compare them at all, implicitly assuming that the
result of the join operator must be false. So it never makes sense result of the join operator must be false. So it never makes sense
to specify <literal>HASHES</literal> for operators that do not represent equality. to specify <literal>HASHES</literal> for operators that do not represent
equality.
</para> </para>
<para> <para>
In fact, logical equality is not good enough either; the operator To be marked <literal>HASHES</literal>, the join operator must appear
had better represent pure bitwise equality, because the hash in a hash index operator class. This is not enforced when you create
function will be computed on the memory representation of the the operator, since of course the referencing operator class couldn't
values regardless of what the bits mean. For example, the exist yet. But attempts to use the operator in hash joins will fail
polygon operator <literal>~=</literal>, which checks whether two at runtime if no such operator class exists. The system needs the
polygons are the same, is not bitwise equality, because two operator class to find the datatype-specific hash function for the
polygons can be considered the same even if their vertices are operator's input datatype. Of course, you must also supply a suitable
specified in a different order. What this means is that a join hash function before you can create the operator class.
using <literal>~=</literal> between polygon fields would yield
different results if implemented as a hash join than if
implemented another way, because a large fraction of the pairs
that should match will hash to different values and will never be
compared by the hash join. But if the optimizer chooses to use a
different kind of join, all the pairs that the operator
<literal>~=</literal> says are the same will be found. We don't
want that kind of inconsistency, so we don't mark the polygon
operator <literal>~=</literal> as hashable.
</para> </para>
<para> <para>
There are also machine-dependent ways in which a hash join might fail Care should be exercised when preparing a hash function, because there
to do the right thing. For example, if your data type are machine-dependent ways in which it might fail to do the right thing.
is a structure in which there may be uninteresting pad bits, it's unsafe For example, if your data type is a structure in which there may be
to mark the equality operator <literal>HASHES</>. (Unless you write uninteresting pad bits, you can't simply pass the whole structure to
your other operators and functions to ensure that the unused bits are always zero, which is the recommended strategy.) <function>hash_any</>. (Unless you write your other operators and
Another example is that the floating-point data types are unsafe for hash functions to ensure that the unused bits are always zero, which is the
joins. On machines that meet the <acronym>IEEE</> floating-point standard, negative recommended strategy.)
zero and positive zero are different values (different bit patterns) but Another example is that on machines that meet the <acronym>IEEE</>
they are defined to compare equal. So, if the equality operator on floating-point data types were marked floating-point standard, negative zero and positive zero are different
<literal>HASHES</>, a negative zero and a positive zero would probably not be matched up values (different bit patterns) but they are defined to compare equal.
by a hash join, but they would be matched up by any other join process. If a float value might contain negative zero then extra steps are needed
</para> to ensure it generates the same hash value as positive zero.
<para>
The bottom line is that you should probably only use <literal>HASHES</literal> for
equality operators that are (or could be) implemented by <function>memcmp()</function>.
</para> </para>
<note> <note>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashfunc.c,v 1.35 2002/09/04 20:31:09 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/access/hash/hashfunc.c,v 1.36 2003/06/22 22:04:54 tgl Exp $
* *
* NOTES * NOTES
* These functions are stored in pg_amproc. For each operator class * These functions are stored in pg_amproc. For each operator class
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "access/hash.h" #include "access/hash.h"
/* Note: this is used for both "char" and boolean datatypes */
Datum Datum
hashchar(PG_FUNCTION_ARGS) hashchar(PG_FUNCTION_ARGS)
{ {
...@@ -58,6 +59,14 @@ hashfloat4(PG_FUNCTION_ARGS) ...@@ -58,6 +59,14 @@ hashfloat4(PG_FUNCTION_ARGS)
{ {
float4 key = PG_GETARG_FLOAT4(0); float4 key = PG_GETARG_FLOAT4(0);
/*
* On IEEE-float machines, minus zero and zero have different bit patterns
* but should compare as equal. We must ensure that they have the same
* hash value, which is most easily done this way:
*/
if (key == (float4) 0)
PG_RETURN_UINT32(0);
return hash_any((unsigned char *) &key, sizeof(key)); return hash_any((unsigned char *) &key, sizeof(key));
} }
...@@ -66,6 +75,14 @@ hashfloat8(PG_FUNCTION_ARGS) ...@@ -66,6 +75,14 @@ hashfloat8(PG_FUNCTION_ARGS)
{ {
float8 key = PG_GETARG_FLOAT8(0); float8 key = PG_GETARG_FLOAT8(0);
/*
* On IEEE-float machines, minus zero and zero have different bit patterns
* but should compare as equal. We must ensure that they have the same
* hash value, which is most easily done this way:
*/
if (key == (float8) 0)
PG_RETURN_UINT32(0);
return hash_any((unsigned char *) &key, sizeof(key)); return hash_any((unsigned char *) &key, sizeof(key));
} }
...@@ -77,11 +94,6 @@ hashoidvector(PG_FUNCTION_ARGS) ...@@ -77,11 +94,6 @@ hashoidvector(PG_FUNCTION_ARGS)
return hash_any((unsigned char *) key, INDEX_MAX_KEYS * sizeof(Oid)); return hash_any((unsigned char *) key, INDEX_MAX_KEYS * sizeof(Oid));
} }
/*
* Note: hashint2vector currently can't be used as a user hash table
* hash function, because it has no pg_proc entry. We only need it
* for catcache indexing.
*/
Datum Datum
hashint2vector(PG_FUNCTION_ARGS) hashint2vector(PG_FUNCTION_ARGS)
{ {
...@@ -102,6 +114,26 @@ hashname(PG_FUNCTION_ARGS) ...@@ -102,6 +114,26 @@ hashname(PG_FUNCTION_ARGS)
return hash_any((unsigned char *) key, keylen); return hash_any((unsigned char *) key, keylen);
} }
Datum
hashtext(PG_FUNCTION_ARGS)
{
text *key = PG_GETARG_TEXT_P(0);
Datum result;
/*
* Note: this is currently identical in behavior to hashvarlena,
* but it seems likely that we may need to do something different
* in non-C locales. (See also hashbpchar, if so.)
*/
result = hash_any((unsigned char *) VARDATA(key),
VARSIZE(key) - VARHDRSZ);
/* Avoid leaking memory for toasted inputs */
PG_FREE_IF_COPY(key, 0);
return result;
}
/* /*
* hashvarlena() can be used for any varlena datatype in which there are * hashvarlena() can be used for any varlena datatype in which there are
* no non-significant bits, ie, distinct bitpatterns never compare as equal. * no non-significant bits, ie, distinct bitpatterns never compare as equal.
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execGrouping.c,v 1.2 2003/01/12 04:03:34 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/execGrouping.c,v 1.3 2003/06/22 22:04:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
#include "executor/executor.h" #include "executor/executor.h"
#include "parser/parse_oper.h" #include "parser/parse_oper.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
/***************************************************************************** /*****************************************************************************
...@@ -213,76 +215,46 @@ execTuplesMatchPrepare(TupleDesc tupdesc, ...@@ -213,76 +215,46 @@ execTuplesMatchPrepare(TupleDesc tupdesc,
return eqfunctions; return eqfunctions;
} }
/*****************************************************************************
* Utility routines for hashing
*****************************************************************************/
/* /*
* ComputeHashFunc * execTuplesHashPrepare
* Look up the equality and hashing functions needed for a TupleHashTable.
* *
* the hash function for hash joins (also used for hash aggregation) * This is similar to execTuplesMatchPrepare, but we also need to find the
* * hash functions associated with the equality operators. *eqfunctions and
* XXX this probably ought to be replaced with datatype-specific * *hashfunctions receive the palloc'd result arrays.
* hash functions, such as those already implemented for hash indexes.
*/ */
uint32 void
ComputeHashFunc(Datum key, int typLen, bool byVal) execTuplesHashPrepare(TupleDesc tupdesc,
int numCols,
AttrNumber *matchColIdx,
FmgrInfo **eqfunctions,
FmgrInfo **hashfunctions)
{ {
unsigned char *k; int i;
if (byVal) *eqfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
{ *hashfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
/*
* If it's a by-value data type, just hash the whole Datum value. for (i = 0; i < numCols; i++)
* 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) AttrNumber att = matchColIdx[i];
{ Oid typid = tupdesc->attrs[att - 1]->atttypid;
/* fixed-width pass-by-reference type */ Operator optup;
k = (unsigned char *) DatumGetPointer(key); Oid eq_opr;
} Oid eq_function;
else if (typLen == -1) Oid hash_function;
{
/* optup = equality_oper(typid, false);
* It's a varlena type, so 'key' points to a "struct varlena". eq_opr = oprid(optup);
* NOTE: VARSIZE returns the "real" data length plus the eq_function = oprfuncid(optup);
* sizeof the "vl_len" attribute of varlena (the length ReleaseSysCache(optup);
* information). 'key' points to the beginning of the varlena hash_function = get_op_hash_function(eq_opr);
* struct, so we have to use "VARDATA" to find the beginning if (!OidIsValid(hash_function))
* of the "real" data. Also, we have to be careful to detoast elog(ERROR, "Could not find hash function for hash operator %u",
* the datum if it's toasted. (We don't worry about freeing eq_opr);
* the detoasted copy; that happens for free when the fmgr_info(eq_function, &(*eqfunctions)[i]);
* per-tuple memory context is reset in ExecHashGetBucket.) fmgr_info(hash_function, &(*hashfunctions)[i]);
*/
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));
} }
...@@ -299,19 +271,21 @@ ComputeHashFunc(Datum key, int typLen, bool byVal) ...@@ -299,19 +271,21 @@ ComputeHashFunc(Datum key, int typLen, bool byVal)
* *
* numCols, keyColIdx: identify the tuple fields to use as lookup key * numCols, keyColIdx: identify the tuple fields to use as lookup key
* eqfunctions: equality comparison functions to use * eqfunctions: equality comparison functions to use
* hashfunctions: datatype-specific hashing functions to use
* nbuckets: number of buckets to make * nbuckets: number of buckets to make
* entrysize: size of each entry (at least sizeof(TupleHashEntryData)) * entrysize: size of each entry (at least sizeof(TupleHashEntryData))
* tablecxt: memory context in which to store table and table entries * tablecxt: memory context in which to store table and table entries
* tempcxt: short-lived context for evaluation hash and comparison functions * tempcxt: short-lived context for evaluation hash and comparison functions
* *
* The eqfunctions array may be made with execTuplesMatchPrepare(). * The function arrays may be made with execTuplesHashPrepare().
* *
* Note that keyColIdx and eqfunctions must be allocated in storage that * Note that keyColIdx, eqfunctions, and hashfunctions must be allocated in
* will live as long as the hashtable does. * storage that will live as long as the hashtable does.
*/ */
TupleHashTable TupleHashTable
BuildTupleHashTable(int numCols, AttrNumber *keyColIdx, BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
FmgrInfo *eqfunctions, FmgrInfo *eqfunctions,
FmgrInfo *hashfunctions,
int nbuckets, Size entrysize, int nbuckets, Size entrysize,
MemoryContext tablecxt, MemoryContext tempcxt) MemoryContext tablecxt, MemoryContext tempcxt)
{ {
...@@ -328,6 +302,7 @@ BuildTupleHashTable(int numCols, AttrNumber *keyColIdx, ...@@ -328,6 +302,7 @@ BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
hashtable->numCols = numCols; hashtable->numCols = numCols;
hashtable->keyColIdx = keyColIdx; hashtable->keyColIdx = keyColIdx;
hashtable->eqfunctions = eqfunctions; hashtable->eqfunctions = eqfunctions;
hashtable->hashfunctions = hashfunctions;
hashtable->tablecxt = tablecxt; hashtable->tablecxt = tablecxt;
hashtable->tempcxt = tempcxt; hashtable->tempcxt = tempcxt;
hashtable->entrysize = entrysize; hashtable->entrysize = entrysize;
...@@ -375,11 +350,15 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot, ...@@ -375,11 +350,15 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
hashkey = (hashkey << 1) | ((hashkey & 0x80000000) ? 1 : 0); hashkey = (hashkey << 1) | ((hashkey & 0x80000000) ? 1 : 0);
attr = heap_getattr(tuple, att, tupdesc, &isNull); attr = heap_getattr(tuple, att, tupdesc, &isNull);
if (isNull)
continue; /* treat nulls as having hash key 0 */ if (!isNull) /* treat nulls as having hash key 0 */
hashkey ^= ComputeHashFunc(attr, {
(int) tupdesc->attrs[att - 1]->attlen, uint32 hkey;
tupdesc->attrs[att - 1]->attbyval);
hkey = DatumGetUInt32(FunctionCall1(&hashtable->hashfunctions[i],
attr));
hashkey ^= hkey;
}
} }
bucketno = hashkey % (uint32) hashtable->nbuckets; bucketno = hashkey % (uint32) hashtable->nbuckets;
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.106 2003/06/06 15:04:01 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.107 2003/06/22 22:04:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -582,6 +582,7 @@ build_hash_table(AggState *aggstate) ...@@ -582,6 +582,7 @@ build_hash_table(AggState *aggstate)
aggstate->hashtable = BuildTupleHashTable(node->numCols, aggstate->hashtable = BuildTupleHashTable(node->numCols,
node->grpColIdx, node->grpColIdx,
aggstate->eqfunctions, aggstate->eqfunctions,
aggstate->hashfunctions,
node->numGroups, node->numGroups,
entrysize, entrysize,
aggstate->aggcontext, aggstate->aggcontext,
...@@ -1035,6 +1036,7 @@ ExecInitAgg(Agg *node, EState *estate) ...@@ -1035,6 +1036,7 @@ ExecInitAgg(Agg *node, EState *estate)
aggstate->aggs = NIL; aggstate->aggs = NIL;
aggstate->numaggs = 0; aggstate->numaggs = 0;
aggstate->eqfunctions = NULL; aggstate->eqfunctions = NULL;
aggstate->hashfunctions = NULL;
aggstate->peragg = NULL; aggstate->peragg = NULL;
aggstate->agg_done = false; aggstate->agg_done = false;
aggstate->pergroup = NULL; aggstate->pergroup = NULL;
...@@ -1123,14 +1125,23 @@ ExecInitAgg(Agg *node, EState *estate) ...@@ -1123,14 +1125,23 @@ ExecInitAgg(Agg *node, EState *estate)
} }
/* /*
* If we are grouping, precompute fmgr lookup data for inner loop * If we are grouping, precompute fmgr lookup data for inner loop.
* We need both equality and hashing functions to do it by hashing,
* but only equality if not hashing.
*/ */
if (node->numCols > 0) if (node->numCols > 0)
{ {
aggstate->eqfunctions = if (node->aggstrategy == AGG_HASHED)
execTuplesMatchPrepare(ExecGetScanType(&aggstate->ss), execTuplesHashPrepare(ExecGetScanType(&aggstate->ss),
node->numCols, node->numCols,
node->grpColIdx); node->grpColIdx,
&aggstate->eqfunctions,
&aggstate->hashfunctions);
else
aggstate->eqfunctions =
execTuplesMatchPrepare(ExecGetScanType(&aggstate->ss),
node->numCols,
node->grpColIdx);
} }
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.75 2003/03/27 16:51:27 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.76 2003/06/22 22:04:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -192,7 +192,7 @@ ExecEndHash(HashState *node) ...@@ -192,7 +192,7 @@ ExecEndHash(HashState *node)
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
HashJoinTable HashJoinTable
ExecHashTableCreate(Hash *node) ExecHashTableCreate(Hash *node, List *hashOperators)
{ {
HashJoinTable hashtable; HashJoinTable hashtable;
Plan *outerNode; Plan *outerNode;
...@@ -201,7 +201,7 @@ ExecHashTableCreate(Hash *node) ...@@ -201,7 +201,7 @@ ExecHashTableCreate(Hash *node)
int nbatch; int nbatch;
int nkeys; int nkeys;
int i; int i;
List *hk; List *ho;
MemoryContext oldcxt; MemoryContext oldcxt;
/* /*
...@@ -237,17 +237,20 @@ ExecHashTableCreate(Hash *node) ...@@ -237,17 +237,20 @@ ExecHashTableCreate(Hash *node)
hashtable->outerBatchSize = NULL; hashtable->outerBatchSize = NULL;
/* /*
* Get info about the datatypes of the hash keys. * Get info about the hash functions to be used for each hash key.
*/ */
nkeys = length(node->hashkeys); nkeys = length(hashOperators);
hashtable->typLens = (int16 *) palloc(nkeys * sizeof(int16)); hashtable->hashfunctions = (FmgrInfo *) palloc(nkeys * sizeof(FmgrInfo));
hashtable->typByVals = (bool *) palloc(nkeys * sizeof(bool));
i = 0; i = 0;
foreach(hk, node->hashkeys) foreach(ho, hashOperators)
{ {
get_typlenbyval(exprType(lfirst(hk)), Oid hashfn;
&hashtable->typLens[i],
&hashtable->typByVals[i]); hashfn = get_op_hash_function(lfirsto(ho));
if (!OidIsValid(hashfn))
elog(ERROR, "Could not find hash function for hash operator %u",
lfirsto(ho));
fmgr_info(hashfn, &hashtable->hashfunctions[i]);
i++; i++;
} }
...@@ -520,7 +523,7 @@ ExecHashGetBucket(HashJoinTable hashtable, ...@@ -520,7 +523,7 @@ ExecHashGetBucket(HashJoinTable hashtable,
/* /*
* We reset the eval context each time to reclaim any memory leaked in * We reset the eval context each time to reclaim any memory leaked in
* the hashkey expressions or ComputeHashFunc itself. * the hashkey expressions.
*/ */
ResetExprContext(econtext); ResetExprContext(econtext);
...@@ -545,9 +548,11 @@ ExecHashGetBucket(HashJoinTable hashtable, ...@@ -545,9 +548,11 @@ ExecHashGetBucket(HashJoinTable hashtable,
*/ */
if (!isNull) /* treat nulls as having hash key 0 */ if (!isNull) /* treat nulls as having hash key 0 */
{ {
hashkey ^= ComputeHashFunc(keyval, uint32 hkey;
(int) hashtable->typLens[i],
hashtable->typByVals[i]); hkey = DatumGetUInt32(FunctionCall1(&hashtable->hashfunctions[i],
keyval));
hashkey ^= hkey;
} }
i++; i++;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.51 2003/05/30 20:23:10 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.52 2003/06/22 22:04:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -117,7 +117,8 @@ ExecHashJoin(HashJoinState *node) ...@@ -117,7 +117,8 @@ ExecHashJoin(HashJoinState *node)
* create the hash table * create the hash table
*/ */
Assert(hashtable == NULL); Assert(hashtable == NULL);
hashtable = ExecHashTableCreate((Hash *) hashNode->ps.plan); hashtable = ExecHashTableCreate((Hash *) hashNode->ps.plan,
node->hj_HashOperators);
node->hj_HashTable = hashtable; node->hj_HashTable = hashtable;
/* /*
...@@ -305,6 +306,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate) ...@@ -305,6 +306,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate)
Plan *outerNode; Plan *outerNode;
Hash *hashNode; Hash *hashNode;
List *hclauses; List *hclauses;
List *hoperators;
List *hcl; List *hcl;
/* /*
...@@ -406,8 +408,9 @@ ExecInitHashJoin(HashJoin *node, EState *estate) ...@@ -406,8 +408,9 @@ ExecInitHashJoin(HashJoin *node, EState *estate)
/* /*
* The planner already made a list of the inner hashkeys for us, * The planner already made a list of the inner hashkeys for us,
* but we also need a list of the outer hashkeys. Each list of * but we also need a list of the outer hashkeys, as well as a list
* exprs must then be prepared for execution. * of the hash operator OIDs. Both lists of exprs must then be prepared
* for execution.
*/ */
hjstate->hj_InnerHashKeys = (List *) hjstate->hj_InnerHashKeys = (List *)
ExecInitExpr((Expr *) hashNode->hashkeys, ExecInitExpr((Expr *) hashNode->hashkeys,
...@@ -416,13 +419,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate) ...@@ -416,13 +419,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate)
hjstate->hj_InnerHashKeys; hjstate->hj_InnerHashKeys;
hclauses = NIL; hclauses = NIL;
hoperators = NIL;
foreach(hcl, node->hashclauses) foreach(hcl, node->hashclauses)
{ {
hclauses = lappend(hclauses, get_leftop(lfirst(hcl))); OpExpr *hclause = (OpExpr *) lfirst(hcl);
Assert(IsA(hclause, OpExpr));
hclauses = lappend(hclauses, get_leftop((Expr *) hclause));
hoperators = lappendo(hoperators, hclause->opno);
} }
hjstate->hj_OuterHashKeys = (List *) hjstate->hj_OuterHashKeys = (List *)
ExecInitExpr((Expr *) hclauses, ExecInitExpr((Expr *) hclauses,
(PlanState *) hjstate); (PlanState *) hjstate);
hjstate->hj_HashOperators = hoperators;
hjstate->js.ps.ps_OuterTupleSlot = NULL; hjstate->js.ps.ps_OuterTupleSlot = NULL;
hjstate->js.ps.ps_TupFromTlist = false; hjstate->js.ps.ps_TupFromTlist = false;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.46 2003/06/06 15:04:01 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.47 2003/06/22 22:04:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -519,6 +519,7 @@ buildSubPlanHash(SubPlanState *node) ...@@ -519,6 +519,7 @@ buildSubPlanHash(SubPlanState *node)
node->hashtable = BuildTupleHashTable(ncols, node->hashtable = BuildTupleHashTable(ncols,
node->keyColIdx, node->keyColIdx,
node->eqfunctions, node->eqfunctions,
node->hashfunctions,
nbuckets, nbuckets,
sizeof(TupleHashEntryData), sizeof(TupleHashEntryData),
node->tablecxt, node->tablecxt,
...@@ -537,6 +538,7 @@ buildSubPlanHash(SubPlanState *node) ...@@ -537,6 +538,7 @@ buildSubPlanHash(SubPlanState *node)
node->hashnulls = BuildTupleHashTable(ncols, node->hashnulls = BuildTupleHashTable(ncols,
node->keyColIdx, node->keyColIdx,
node->eqfunctions, node->eqfunctions,
node->hashfunctions,
nbuckets, nbuckets,
sizeof(TupleHashEntryData), sizeof(TupleHashEntryData),
node->tablecxt, node->tablecxt,
...@@ -700,6 +702,7 @@ ExecInitSubPlan(SubPlanState *node, EState *estate) ...@@ -700,6 +702,7 @@ ExecInitSubPlan(SubPlanState *node, EState *estate)
node->innerecontext = NULL; node->innerecontext = NULL;
node->keyColIdx = NULL; node->keyColIdx = NULL;
node->eqfunctions = NULL; node->eqfunctions = NULL;
node->hashfunctions = NULL;
/* /*
* create an EState for the subplan * create an EState for the subplan
...@@ -797,11 +800,12 @@ ExecInitSubPlan(SubPlanState *node, EState *estate) ...@@ -797,11 +800,12 @@ ExecInitSubPlan(SubPlanState *node, EState *estate)
* ExecTypeFromTL). * ExecTypeFromTL).
* *
* We also extract the combining operators themselves to initialize * We also extract the combining operators themselves to initialize
* the equality functions for the hash tables. * the equality and hashing functions for the hash tables.
*/ */
lefttlist = righttlist = NIL; lefttlist = righttlist = NIL;
leftptlist = rightptlist = NIL; leftptlist = rightptlist = NIL;
node->eqfunctions = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo)); node->eqfunctions = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
node->hashfunctions = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
i = 1; i = 1;
foreach(lexpr, node->exprs) foreach(lexpr, node->exprs)
{ {
...@@ -811,6 +815,7 @@ ExecInitSubPlan(SubPlanState *node, EState *estate) ...@@ -811,6 +815,7 @@ ExecInitSubPlan(SubPlanState *node, EState *estate)
Expr *expr; Expr *expr;
TargetEntry *tle; TargetEntry *tle;
GenericExprState *tlestate; GenericExprState *tlestate;
Oid hashfn;
Assert(IsA(fstate, FuncExprState)); Assert(IsA(fstate, FuncExprState));
Assert(IsA(opexpr, OpExpr)); Assert(IsA(opexpr, OpExpr));
...@@ -850,6 +855,13 @@ ExecInitSubPlan(SubPlanState *node, EState *estate) ...@@ -850,6 +855,13 @@ ExecInitSubPlan(SubPlanState *node, EState *estate)
fmgr_info(opexpr->opfuncid, &node->eqfunctions[i-1]); fmgr_info(opexpr->opfuncid, &node->eqfunctions[i-1]);
node->eqfunctions[i-1].fn_expr = (Node *) opexpr; node->eqfunctions[i-1].fn_expr = (Node *) opexpr;
/* Lookup the associated hash function */
hashfn = get_op_hash_function(opexpr->opno);
if (!OidIsValid(hashfn))
elog(ERROR, "Could not find hash function for hash operator %u",
opexpr->opno);
fmgr_info(hashfn, &node->hashfunctions[i-1]);
i++; i++;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.97 2003/05/26 00:11:27 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.98 2003/06/22 22:04:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -704,6 +704,8 @@ bpcharcmp(PG_FUNCTION_ARGS) ...@@ -704,6 +704,8 @@ bpcharcmp(PG_FUNCTION_ARGS)
/* /*
* bpchar needs a specialized hash function because we want to ignore * bpchar needs a specialized hash function because we want to ignore
* trailing blanks in comparisons. * trailing blanks in comparisons.
*
* XXX is there any need for locale-specific behavior here?
*/ */
Datum Datum
hashbpchar(PG_FUNCTION_ARGS) hashbpchar(PG_FUNCTION_ARGS)
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.103 2003/05/27 17:49:46 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.104 2003/06/22 22:04:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -81,22 +81,6 @@ ...@@ -81,22 +81,6 @@
/* Cache management header --- pointer is NULL until created */ /* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL; static CatCacheHeader *CacheHdr = NULL;
/*
* EQPROC is used in CatalogCacheInitializeCache to find the equality
* functions for system types that are used as cache key fields.
* See also GetCCHashFunc, which should support the same set of types.
*
* XXX this should be replaced by catalog lookups,
* but that seems to pose considerable risk of circularity...
*/
static const Oid eqproc[] = {
F_BOOLEQ, InvalidOid, F_CHAREQ, F_NAMEEQ, InvalidOid,
F_INT2EQ, F_INT2VECTOREQ, F_INT4EQ, F_OIDEQ, F_TEXTEQ,
F_OIDEQ, InvalidOid, InvalidOid, InvalidOid, F_OIDVECTOREQ
};
#define EQPROC(SYSTEMTYPEOID) eqproc[(SYSTEMTYPEOID)-BOOLOID]
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys, static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
ScanKey cur_skey); ScanKey cur_skey);
...@@ -119,24 +103,46 @@ static HeapTuple build_dummy_tuple(CatCache *cache, int nkeys, ScanKey skeys); ...@@ -119,24 +103,46 @@ static HeapTuple build_dummy_tuple(CatCache *cache, int nkeys, ScanKey skeys);
* internal support functions * internal support functions
*/ */
static PGFunction /*
GetCCHashFunc(Oid keytype) * Look up the hash and equality functions for system types that are used
* as cache key fields.
*
* XXX this should be replaced by catalog lookups,
* but that seems to pose considerable risk of circularity...
*/
static void
GetCCHashEqFuncs(Oid keytype, PGFunction *hashfunc, RegProcedure *eqfunc)
{ {
switch (keytype) switch (keytype)
{ {
case BOOLOID: case BOOLOID:
*hashfunc = hashchar;
*eqfunc = F_BOOLEQ;
break;
case CHAROID: case CHAROID:
return hashchar; *hashfunc = hashchar;
*eqfunc = F_CHAREQ;
break;
case NAMEOID: case NAMEOID:
return hashname; *hashfunc = hashname;
*eqfunc = F_NAMEEQ;
break;
case INT2OID: case INT2OID:
return hashint2; *hashfunc = hashint2;
*eqfunc = F_INT2EQ;
break;
case INT2VECTOROID: case INT2VECTOROID:
return hashint2vector; *hashfunc = hashint2vector;
*eqfunc = F_INT2VECTOREQ;
break;
case INT4OID: case INT4OID:
return hashint4; *hashfunc = hashint4;
*eqfunc = F_INT4EQ;
break;
case TEXTOID: case TEXTOID:
return hashvarlena; *hashfunc = hashtext;
*eqfunc = F_TEXTEQ;
break;
case OIDOID: case OIDOID:
case REGPROCOID: case REGPROCOID:
case REGPROCEDUREOID: case REGPROCEDUREOID:
...@@ -144,13 +150,17 @@ GetCCHashFunc(Oid keytype) ...@@ -144,13 +150,17 @@ GetCCHashFunc(Oid keytype)
case REGOPERATOROID: case REGOPERATOROID:
case REGCLASSOID: case REGCLASSOID:
case REGTYPEOID: case REGTYPEOID:
return hashoid; *hashfunc = hashoid;
*eqfunc = F_OIDEQ;
break;
case OIDVECTOROID: case OIDVECTOROID:
return hashoidvector; *hashfunc = hashoidvector;
*eqfunc = F_OIDVECTOREQ;
break;
default: default:
elog(FATAL, "GetCCHashFunc: type %u unsupported as catcache key", elog(FATAL, "GetCCHashEqFuncs: type %u unsupported as catcache key",
keytype); keytype);
return (PGFunction) NULL; break;
} }
} }
...@@ -941,16 +951,16 @@ CatalogCacheInitializeCache(CatCache *cache) ...@@ -941,16 +951,16 @@ CatalogCacheInitializeCache(CatCache *cache)
keytype = OIDOID; keytype = OIDOID;
} }
cache->cc_hashfunc[i] = GetCCHashFunc(keytype); GetCCHashEqFuncs(keytype,
&cache->cc_hashfunc[i],
&cache->cc_skey[i].sk_procedure);
cache->cc_isname[i] = (keytype == NAMEOID); cache->cc_isname[i] = (keytype == NAMEOID);
/* /*
* If GetCCHashFunc liked the type, safe to index into eqproc[] * Do equality-function lookup (we assume this won't need a catalog
* lookup for any supported type)
*/ */
cache->cc_skey[i].sk_procedure = EQPROC(keytype);
/* Do function lookup */
fmgr_info_cxt(cache->cc_skey[i].sk_procedure, fmgr_info_cxt(cache->cc_skey[i].sk_procedure,
&cache->cc_skey[i].sk_func, &cache->cc_skey[i].sk_func,
CacheMemoryContext); CacheMemoryContext);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.95 2003/05/26 00:11:27 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.96 2003/06/22 22:04:54 tgl Exp $
* *
* NOTES * NOTES
* Eventually, the index information should go through here, too. * Eventually, the index information should go through here, too.
...@@ -16,8 +16,10 @@ ...@@ -16,8 +16,10 @@
#include "postgres.h" #include "postgres.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "access/hash.h"
#include "access/tupmacs.h" #include "access/tupmacs.h"
#include "catalog/pg_amop.h" #include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_namespace.h" #include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h" #include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h" #include "catalog/pg_operator.h"
...@@ -28,6 +30,7 @@ ...@@ -28,6 +30,7 @@
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "utils/array.h" #include "utils/array.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/catcache.h"
#include "utils/datum.h" #include "utils/datum.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/syscache.h" #include "utils/syscache.h"
...@@ -106,6 +109,72 @@ get_opclass_member(Oid opclass, int16 strategy) ...@@ -106,6 +109,72 @@ get_opclass_member(Oid opclass, int16 strategy)
return result; return result;
} }
/*
* get_op_hash_function
* Get the OID of the datatype-specific hash function associated with
* a hashable equality operator.
*
* Returns InvalidOid if no hash function can be found. (This indicates
* that the operator should not have been marked oprcanhash.)
*/
Oid
get_op_hash_function(Oid opno)
{
CatCList *catlist;
int i;
HeapTuple tuple;
Oid opclass = InvalidOid;
/*
* Search pg_amop to see if the target operator is registered as the "="
* operator of any hash opclass. If the operator is registered in
* multiple opclasses, assume we can use the associated hash function
* from any one.
*/
catlist = SearchSysCacheList(AMOPOPID, 1,
ObjectIdGetDatum(opno),
0, 0, 0);
for (i = 0; i < catlist->n_members; i++)
{
Form_pg_amop aform;
tuple = &catlist->members[i]->tuple;
aform = (Form_pg_amop) GETSTRUCT(tuple);
if (aform->amopstrategy == HTEqualStrategyNumber &&
opclass_is_hash(aform->amopclaid))
{
opclass = aform->amopclaid;
break;
}
}
ReleaseSysCacheList(catlist);
if (OidIsValid(opclass))
{
/* Found a suitable opclass, get its hash support function */
tuple = SearchSysCache(AMPROCNUM,
ObjectIdGetDatum(opclass),
Int16GetDatum(HASHPROC),
0, 0);
if (HeapTupleIsValid(tuple))
{
Form_pg_amproc aform = (Form_pg_amproc) GETSTRUCT(tuple);
RegProcedure result;
result = aform->amproc;
ReleaseSysCache(tuple);
Assert(RegProcedureIsValid(result));
return result;
}
}
/* Didn't find a match... */
return InvalidOid;
}
/* ---------- ATTRIBUTE CACHES ---------- */ /* ---------- ATTRIBUTE CACHES ---------- */
...@@ -284,6 +353,31 @@ opclass_is_btree(Oid opclass) ...@@ -284,6 +353,31 @@ opclass_is_btree(Oid opclass)
return result; return result;
} }
/*
* opclass_is_hash
*
* Returns TRUE iff the specified opclass is associated with the
* hash index access method.
*/
bool
opclass_is_hash(Oid opclass)
{
HeapTuple tp;
Form_pg_opclass cla_tup;
bool result;
tp = SearchSysCache(CLAOID,
ObjectIdGetDatum(opclass),
0, 0, 0);
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for opclass %u", opclass);
cla_tup = (Form_pg_opclass) GETSTRUCT(tp);
result = (cla_tup->opcamid == HASH_AM_OID);
ReleaseSysCache(tp);
return result;
}
/* ---------- OPERATOR CACHE ---------- */ /* ---------- OPERATOR CACHE ---------- */
/* /*
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: hash.h,v 1.47 2002/06/20 20:29:42 momjian Exp $ * $Id: hash.h,v 1.48 2003/06/22 22:04:54 tgl Exp $
* *
* NOTES * NOTES
* modeled after Margo Seltzer's hash implementation for unix. * modeled after Margo Seltzer's hash implementation for unix.
...@@ -251,9 +251,11 @@ extern Datum hashbulkdelete(PG_FUNCTION_ARGS); ...@@ -251,9 +251,11 @@ extern Datum hashbulkdelete(PG_FUNCTION_ARGS);
/* /*
* Datatype-specific hash functions in hashfunc.c. * Datatype-specific hash functions in hashfunc.c.
* *
* These support both hash indexes and hash joins.
*
* NOTE: some of these are also used by catcache operations, without * NOTE: some of these are also used by catcache operations, without
* any direct connection to hash indexes. Also, the common hash_any * any direct connection to hash indexes. Also, the common hash_any
* routine is also used by dynahash tables and hash joins. * routine is also used by dynahash tables.
*/ */
extern Datum hashchar(PG_FUNCTION_ARGS); extern Datum hashchar(PG_FUNCTION_ARGS);
extern Datum hashint2(PG_FUNCTION_ARGS); extern Datum hashint2(PG_FUNCTION_ARGS);
...@@ -265,6 +267,7 @@ extern Datum hashfloat8(PG_FUNCTION_ARGS); ...@@ -265,6 +267,7 @@ extern Datum hashfloat8(PG_FUNCTION_ARGS);
extern Datum hashoidvector(PG_FUNCTION_ARGS); extern Datum hashoidvector(PG_FUNCTION_ARGS);
extern Datum hashint2vector(PG_FUNCTION_ARGS); extern Datum hashint2vector(PG_FUNCTION_ARGS);
extern Datum hashname(PG_FUNCTION_ARGS); extern Datum hashname(PG_FUNCTION_ARGS);
extern Datum hashtext(PG_FUNCTION_ARGS);
extern Datum hashvarlena(PG_FUNCTION_ARGS); extern Datum hashvarlena(PG_FUNCTION_ARGS);
extern Datum hash_any(register const unsigned char *k, register int keylen); extern Datum hash_any(register const unsigned char *k, register int keylen);
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: catversion.h,v 1.198 2003/06/06 15:04:02 tgl Exp $ * $Id: catversion.h,v 1.199 2003/06/22 22:04:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200306051 #define CATALOG_VERSION_NO 200306221
#endif #endif
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: pg_am.h,v 1.25 2003/03/10 22:28:19 tgl Exp $ * $Id: pg_am.h,v 1.26 2003/06/22 22:04:55 tgl Exp $
* *
* NOTES * NOTES
* the genbki.sh script reads this file and generates .bki * the genbki.sh script reads this file and generates .bki
...@@ -107,6 +107,7 @@ DESCR("b-tree index access method"); ...@@ -107,6 +107,7 @@ DESCR("b-tree index access method");
#define BTREE_AM_OID 403 #define BTREE_AM_OID 403
DATA(insert OID = 405 ( hash PGUID 1 1 0 f f f t hashgettuple hashinsert hashbeginscan hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete - hashcostestimate )); DATA(insert OID = 405 ( hash PGUID 1 1 0 f f f t hashgettuple hashinsert hashbeginscan hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete - hashcostestimate ));
DESCR("hash index access method"); DESCR("hash index access method");
#define HASH_AM_OID 405
DATA(insert OID = 783 ( gist PGUID 100 7 0 f t f f gistgettuple gistinsert gistbeginscan gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete - gistcostestimate )); DATA(insert OID = 783 ( gist PGUID 100 7 0 f t f f gistgettuple gistinsert gistbeginscan gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete - gistcostestimate ));
DESCR("GiST index access method"); DESCR("GiST index access method");
#define GIST_AM_OID 783 #define GIST_AM_OID 783
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: pg_amop.h,v 1.49 2003/05/26 00:11:27 tgl Exp $ * $Id: pg_amop.h,v 1.50 2003/06/22 22:04:55 tgl Exp $
* *
* NOTES * NOTES
* the genbki.sh script reads this file and generates .bki * the genbki.sh script reads this file and generates .bki
...@@ -465,5 +465,27 @@ DATA(insert ( 2001 1 f 1550 )); ...@@ -465,5 +465,27 @@ DATA(insert ( 2001 1 f 1550 ));
DATA(insert ( 2004 1 f 98 )); DATA(insert ( 2004 1 f 98 ));
/* timestamp_ops */ /* timestamp_ops */
DATA(insert ( 2040 1 f 2060 )); DATA(insert ( 2040 1 f 2060 ));
/* bool_ops */
DATA(insert ( 2222 1 f 91 ));
/* bytea_ops */
DATA(insert ( 2223 1 f 1955 ));
/* int2vector_ops */
DATA(insert ( 2224 1 f 386 ));
/* xid_ops */
DATA(insert ( 2225 1 f 352 ));
/* cid_ops */
DATA(insert ( 2226 1 f 385 ));
/* abstime_ops */
DATA(insert ( 2227 1 f 560 ));
/* reltime_ops */
DATA(insert ( 2228 1 f 566 ));
/* text_pattern_ops */
DATA(insert ( 2229 1 f 2316 ));
/* varchar_pattern_ops */
DATA(insert ( 2230 1 f 2316 ));
/* bpchar_pattern_ops */
DATA(insert ( 2231 1 f 2328 ));
/* name_pattern_ops */
DATA(insert ( 2232 1 f 2334 ));
#endif /* PG_AMOP_H */ #endif /* PG_AMOP_H */
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: pg_amproc.h,v 1.37 2003/05/26 00:11:27 tgl Exp $ * $Id: pg_amproc.h,v 1.38 2003/06/22 22:04:55 tgl Exp $
* *
* NOTES * NOTES
* the genbki.sh script reads this file and generates .bki * the genbki.sh script reads this file and generates .bki
...@@ -127,11 +127,22 @@ DATA(insert ( 1985 1 399 )); ...@@ -127,11 +127,22 @@ DATA(insert ( 1985 1 399 ));
DATA(insert ( 1987 1 455 )); DATA(insert ( 1987 1 455 ));
DATA(insert ( 1990 1 453 )); DATA(insert ( 1990 1 453 ));
DATA(insert ( 1992 1 457 )); DATA(insert ( 1992 1 457 ));
DATA(insert ( 1995 1 456 )); DATA(insert ( 1995 1 400 ));
DATA(insert ( 1997 1 452 )); DATA(insert ( 1997 1 452 ));
DATA(insert ( 1999 1 452 )); DATA(insert ( 1999 1 452 ));
DATA(insert ( 2001 1 1696 )); DATA(insert ( 2001 1 1696 ));
DATA(insert ( 2004 1 456 )); DATA(insert ( 2004 1 400 ));
DATA(insert ( 2040 1 452 )); DATA(insert ( 2040 1 452 ));
DATA(insert ( 2222 1 454 ));
DATA(insert ( 2223 1 456 ));
DATA(insert ( 2224 1 398 ));
DATA(insert ( 2225 1 450 ));
DATA(insert ( 2226 1 450 ));
DATA(insert ( 2227 1 450 ));
DATA(insert ( 2228 1 450 ));
DATA(insert ( 2229 1 456 ));
DATA(insert ( 2230 1 456 ));
DATA(insert ( 2231 1 456 ));
DATA(insert ( 2232 1 455 ));
#endif /* PG_AMPROC_H */ #endif /* PG_AMPROC_H */
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: pg_opclass.h,v 1.50 2003/05/28 16:04:00 tgl Exp $ * $Id: pg_opclass.h,v 1.51 2003/06/22 22:04:55 tgl Exp $
* *
* NOTES * NOTES
* the genbki.sh script reads this file and generates .bki * the genbki.sh script reads this file and generates .bki
...@@ -155,5 +155,16 @@ DATA(insert OID = 2097 ( 403 bpchar_pattern_ops PGNSP PGUID 1042 f 0 )); ...@@ -155,5 +155,16 @@ DATA(insert OID = 2097 ( 403 bpchar_pattern_ops PGNSP PGUID 1042 f 0 ));
#define BPCHAR_PATTERN_BTREE_OPS_OID 2097 #define BPCHAR_PATTERN_BTREE_OPS_OID 2097
DATA(insert OID = 2098 ( 403 name_pattern_ops PGNSP PGUID 19 f 0 )); DATA(insert OID = 2098 ( 403 name_pattern_ops PGNSP PGUID 19 f 0 ));
#define NAME_PATTERN_BTREE_OPS_OID 2098 #define NAME_PATTERN_BTREE_OPS_OID 2098
DATA(insert OID = 2222 ( 405 bool_ops PGNSP PGUID 16 t 0 ));
DATA(insert OID = 2223 ( 405 bytea_ops PGNSP PGUID 17 t 0 ));
DATA(insert OID = 2224 ( 405 int2vector_ops PGNSP PGUID 22 t 0 ));
DATA(insert OID = 2225 ( 405 xid_ops PGNSP PGUID 28 t 0 ));
DATA(insert OID = 2226 ( 405 cid_ops PGNSP PGUID 29 t 0 ));
DATA(insert OID = 2227 ( 405 abstime_ops PGNSP PGUID 702 t 0 ));
DATA(insert OID = 2228 ( 405 reltime_ops PGNSP PGUID 703 t 0 ));
DATA(insert OID = 2229 ( 405 text_pattern_ops PGNSP PGUID 25 f 0 ));
DATA(insert OID = 2230 ( 405 varchar_pattern_ops PGNSP PGUID 1043 f 0 ));
DATA(insert OID = 2231 ( 405 bpchar_pattern_ops PGNSP PGUID 1042 f 0 ));
DATA(insert OID = 2232 ( 405 name_pattern_ops PGNSP PGUID 19 f 0 ));
#endif /* PG_OPCLASS_H */ #endif /* PG_OPCLASS_H */
This diff is collapsed.
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: pg_proc.h,v 1.303 2003/06/11 09:23:55 petere Exp $ * $Id: pg_proc.h,v 1.304 2003/06/22 22:04:55 tgl Exp $
* *
* NOTES * NOTES
* The script catalog/genbki.sh reads this file and generates .bki * The script catalog/genbki.sh reads this file and generates .bki
...@@ -836,10 +836,14 @@ DATA(insert OID = 454 ( hashchar PGNSP PGUID 12 f f t f i 1 23 "18" hashch ...@@ -836,10 +836,14 @@ DATA(insert OID = 454 ( hashchar PGNSP PGUID 12 f f t f i 1 23 "18" hashch
DESCR("hash"); DESCR("hash");
DATA(insert OID = 455 ( hashname PGNSP PGUID 12 f f t f i 1 23 "19" hashname - _null_ )); DATA(insert OID = 455 ( hashname PGNSP PGUID 12 f f t f i 1 23 "19" hashname - _null_ ));
DESCR("hash"); DESCR("hash");
DATA(insert OID = 400 ( hashtext PGNSP PGUID 12 f f t f i 1 23 "25" hashtext - _null_ ));
DESCR("hash");
DATA(insert OID = 456 ( hashvarlena PGNSP PGUID 12 f f t f i 1 23 "2281" hashvarlena - _null_ )); DATA(insert OID = 456 ( hashvarlena PGNSP PGUID 12 f f t f i 1 23 "2281" hashvarlena - _null_ ));
DESCR("hash any varlena type"); DESCR("hash any varlena type");
DATA(insert OID = 457 ( hashoidvector PGNSP PGUID 12 f f t f i 1 23 "30" hashoidvector - _null_ )); DATA(insert OID = 457 ( hashoidvector PGNSP PGUID 12 f f t f i 1 23 "30" hashoidvector - _null_ ));
DESCR("hash"); DESCR("hash");
DATA(insert OID = 398 ( hashint2vector PGNSP PGUID 12 f f t f i 1 23 "22" hashint2vector - _null_ ));
DESCR("hash");
DATA(insert OID = 399 ( hashmacaddr PGNSP PGUID 12 f f t f i 1 23 "829" hashmacaddr - _null_ )); DATA(insert OID = 399 ( hashmacaddr PGNSP PGUID 12 f f t f i 1 23 "829" hashmacaddr - _null_ ));
DESCR("hash"); DESCR("hash");
DATA(insert OID = 458 ( text_larger PGNSP PGUID 12 f f t f i 2 25 "25 25" text_larger - _null_ )); DATA(insert OID = 458 ( text_larger PGNSP PGUID 12 f f t f i 2 25 "25 25" text_larger - _null_ ));
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: executor.h,v 1.94 2003/05/06 20:26:28 tgl Exp $ * $Id: executor.h,v 1.95 2003/06/22 22:04:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -57,9 +57,14 @@ extern bool execTuplesUnequal(HeapTuple tuple1, ...@@ -57,9 +57,14 @@ extern bool execTuplesUnequal(HeapTuple tuple1,
extern FmgrInfo *execTuplesMatchPrepare(TupleDesc tupdesc, extern FmgrInfo *execTuplesMatchPrepare(TupleDesc tupdesc,
int numCols, int numCols,
AttrNumber *matchColIdx); AttrNumber *matchColIdx);
extern uint32 ComputeHashFunc(Datum key, int typLen, bool byVal); extern void execTuplesHashPrepare(TupleDesc tupdesc,
int numCols,
AttrNumber *matchColIdx,
FmgrInfo **eqfunctions,
FmgrInfo **hashfunctions);
extern TupleHashTable BuildTupleHashTable(int numCols, AttrNumber *keyColIdx, extern TupleHashTable BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
FmgrInfo *eqfunctions, FmgrInfo *eqfunctions,
FmgrInfo *hashfunctions,
int nbuckets, Size entrysize, int nbuckets, Size entrysize,
MemoryContext tablecxt, MemoryContext tablecxt,
MemoryContext tempcxt); MemoryContext tempcxt);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: hashjoin.h,v 1.27 2002/11/30 00:08:20 tgl Exp $ * $Id: hashjoin.h,v 1.28 2003/06/22 22:04:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -69,13 +69,12 @@ typedef struct HashTableData ...@@ -69,13 +69,12 @@ typedef struct HashTableData
* file */ * file */
/* /*
* Info about the datatypes being hashed. We assume that the inner and * Info about the datatype-specific hash functions for the datatypes
* outer sides of each hashclause are the same type, or at least * being hashed. We assume that the inner and outer sides of each
* binary-compatible types. Each of these fields points to an array * hashclause are the same type, or at least share the same hash function.
* of the same length as the number of hash keys. * This is an array of the same length as the number of hash keys.
*/ */
int16 *typLens; FmgrInfo *hashfunctions; /* lookup data for hash functions */
bool *typByVals;
/* /*
* During 1st scan of inner relation, we get tuples from executor. If * During 1st scan of inner relation, we get tuples from executor. If
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: nodeHash.h,v 1.29 2003/01/10 23:54:24 tgl Exp $ * $Id: nodeHash.h,v 1.30 2003/06/22 22:04:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -22,7 +22,7 @@ extern TupleTableSlot *ExecHash(HashState *node); ...@@ -22,7 +22,7 @@ extern TupleTableSlot *ExecHash(HashState *node);
extern void ExecEndHash(HashState *node); extern void ExecEndHash(HashState *node);
extern void ExecReScanHash(HashState *node, ExprContext *exprCtxt); extern void ExecReScanHash(HashState *node, ExprContext *exprCtxt);
extern HashJoinTable ExecHashTableCreate(Hash *node); extern HashJoinTable ExecHashTableCreate(Hash *node, List *hashOperators);
extern void ExecHashTableDestroy(HashJoinTable hashtable); extern void ExecHashTableDestroy(HashJoinTable hashtable);
extern void ExecHashTableInsert(HashJoinTable hashtable, extern void ExecHashTableInsert(HashJoinTable hashtable,
ExprContext *econtext, ExprContext *econtext,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: execnodes.h,v 1.98 2003/05/28 16:04:00 tgl Exp $ * $Id: execnodes.h,v 1.99 2003/06/22 22:04:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -353,6 +353,7 @@ typedef struct TupleHashTableData ...@@ -353,6 +353,7 @@ typedef struct TupleHashTableData
int numCols; /* number of columns in lookup key */ int numCols; /* number of columns in lookup key */
AttrNumber *keyColIdx; /* attr numbers of key columns */ AttrNumber *keyColIdx; /* attr numbers of key columns */
FmgrInfo *eqfunctions; /* lookup data for comparison functions */ FmgrInfo *eqfunctions; /* lookup data for comparison functions */
FmgrInfo *hashfunctions; /* lookup data for hash functions */
MemoryContext tablecxt; /* memory context containing table */ MemoryContext tablecxt; /* memory context containing table */
MemoryContext tempcxt; /* context for function evaluations */ MemoryContext tempcxt; /* context for function evaluations */
Size entrysize; /* actual size to make each hash entry */ Size entrysize; /* actual size to make each hash entry */
...@@ -521,6 +522,7 @@ typedef struct SubPlanState ...@@ -521,6 +522,7 @@ typedef struct SubPlanState
ExprContext *innerecontext; /* working context for comparisons */ ExprContext *innerecontext; /* working context for comparisons */
AttrNumber *keyColIdx; /* control data for hash tables */ AttrNumber *keyColIdx; /* control data for hash tables */
FmgrInfo *eqfunctions; /* comparison functions for hash tables */ FmgrInfo *eqfunctions; /* comparison functions for hash tables */
FmgrInfo *hashfunctions; /* lookup data for hash functions */
} SubPlanState; } SubPlanState;
/* ---------------- /* ----------------
...@@ -900,6 +902,7 @@ typedef struct MergeJoinState ...@@ -900,6 +902,7 @@ typedef struct MergeJoinState
* unless OuterTupleSlot is nonempty!) * unless OuterTupleSlot is nonempty!)
* hj_OuterHashKeys the outer hash keys in the hashjoin condition * hj_OuterHashKeys the outer hash keys in the hashjoin condition
* hj_InnerHashKeys the inner hash keys in the hashjoin condition * hj_InnerHashKeys the inner hash keys in the hashjoin condition
* hj_HashOperators the join operators in the hashjoin condition
* hj_OuterTupleSlot tuple slot for outer tuples * hj_OuterTupleSlot tuple slot for outer tuples
* hj_HashTupleSlot tuple slot for hashed tuples * hj_HashTupleSlot tuple slot for hashed tuples
* hj_NullInnerTupleSlot prepared null tuple for left outer joins * hj_NullInnerTupleSlot prepared null tuple for left outer joins
...@@ -917,6 +920,7 @@ typedef struct HashJoinState ...@@ -917,6 +920,7 @@ typedef struct HashJoinState
HashJoinTuple hj_CurTuple; HashJoinTuple hj_CurTuple;
List *hj_OuterHashKeys; /* list of ExprState nodes */ List *hj_OuterHashKeys; /* list of ExprState nodes */
List *hj_InnerHashKeys; /* list of ExprState nodes */ List *hj_InnerHashKeys; /* list of ExprState nodes */
List *hj_HashOperators; /* list of operator OIDs */
TupleTableSlot *hj_OuterTupleSlot; TupleTableSlot *hj_OuterTupleSlot;
TupleTableSlot *hj_HashTupleSlot; TupleTableSlot *hj_HashTupleSlot;
TupleTableSlot *hj_NullInnerTupleSlot; TupleTableSlot *hj_NullInnerTupleSlot;
...@@ -992,6 +996,7 @@ typedef struct AggState ...@@ -992,6 +996,7 @@ typedef struct AggState
List *aggs; /* all Aggref nodes in targetlist & quals */ List *aggs; /* all Aggref nodes in targetlist & quals */
int numaggs; /* length of list (could be zero!) */ int numaggs; /* length of list (could be zero!) */
FmgrInfo *eqfunctions; /* per-grouping-field equality fns */ FmgrInfo *eqfunctions; /* per-grouping-field equality fns */
FmgrInfo *hashfunctions; /* per-grouping-field hash fns */
AggStatePerAgg peragg; /* per-Aggref information */ AggStatePerAgg peragg; /* per-Aggref information */
MemoryContext aggcontext; /* memory context for long-lived data */ MemoryContext aggcontext; /* memory context for long-lived data */
ExprContext *tmpcontext; /* econtext for input expressions */ ExprContext *tmpcontext; /* econtext for input expressions */
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: lsyscache.h,v 1.70 2003/05/26 00:11:28 tgl Exp $ * $Id: lsyscache.h,v 1.71 2003/06/22 22:04:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
extern bool op_in_opclass(Oid opno, Oid opclass); extern bool op_in_opclass(Oid opno, Oid opclass);
extern bool op_requires_recheck(Oid opno, Oid opclass); extern bool op_requires_recheck(Oid opno, Oid opclass);
extern Oid get_opclass_member(Oid opclass, int16 strategy); extern Oid get_opclass_member(Oid opclass, int16 strategy);
extern Oid get_op_hash_function(Oid opno);
extern char *get_attname(Oid relid, AttrNumber attnum); extern char *get_attname(Oid relid, AttrNumber attnum);
extern AttrNumber get_attnum(Oid relid, const char *attname); extern AttrNumber get_attnum(Oid relid, const char *attname);
extern Oid get_atttype(Oid relid, AttrNumber attnum); extern Oid get_atttype(Oid relid, AttrNumber attnum);
...@@ -25,6 +26,7 @@ extern int32 get_atttypmod(Oid relid, AttrNumber attnum); ...@@ -25,6 +26,7 @@ extern int32 get_atttypmod(Oid relid, AttrNumber attnum);
extern void get_atttypetypmod(Oid relid, AttrNumber attnum, extern void get_atttypetypmod(Oid relid, AttrNumber attnum,
Oid *typid, int32 *typmod); Oid *typid, int32 *typmod);
extern bool opclass_is_btree(Oid opclass); extern bool opclass_is_btree(Oid opclass);
extern bool opclass_is_hash(Oid opclass);
extern RegProcedure get_opcode(Oid opno); extern RegProcedure get_opcode(Oid opno);
extern char *get_opname(Oid opno); extern char *get_opname(Oid opno);
extern bool op_mergejoinable(Oid opno, Oid *leftOp, Oid *rightOp); extern bool op_mergejoinable(Oid opno, Oid *leftOp, Oid *rightOp);
......
...@@ -436,9 +436,6 @@ WHERE p1.oprlsortop != p1.oprrsortop AND ...@@ -436,9 +436,6 @@ WHERE p1.oprlsortop != p1.oprrsortop AND
-- Hashing only works on simple equality operators "type = sametype", -- Hashing only works on simple equality operators "type = sametype",
-- since the hash itself depends on the bitwise representation of the type. -- since the hash itself depends on the bitwise representation of the type.
-- Check that allegedly hashable operators look like they might be "=". -- Check that allegedly hashable operators look like they might be "=".
-- NOTE: as of 7.3, this search finds xideqint4. Since we do not mark
-- xid and int4 as binary-equivalent in pg_cast, there's no easy way to
-- recognize that case as OK; just leave that tuple in the expected output.
SELECT p1.oid, p1.oprname SELECT p1.oid, p1.oprname
FROM pg_operator AS p1 FROM pg_operator AS p1
WHERE p1.oprcanhash AND NOT WHERE p1.oprcanhash AND NOT
...@@ -447,8 +444,7 @@ WHERE p1.oprcanhash AND NOT ...@@ -447,8 +444,7 @@ WHERE p1.oprcanhash AND NOT
p1.oprcom = p1.oid); p1.oprcom = p1.oid);
oid | oprname oid | oprname
-----+--------- -----+---------
353 | = (0 rows)
(1 row)
-- In 6.5 we accepted hashable array equality operators when the array element -- In 6.5 we accepted hashable array equality operators when the array element
-- type is hashable. However, what we actually need to make hashjoin work on -- type is hashable. However, what we actually need to make hashjoin work on
...@@ -474,6 +470,17 @@ WHERE p1.oprcanhash AND p1.oprcode = p2.oid AND p2.proname = 'array_eq'; ...@@ -474,6 +470,17 @@ WHERE p1.oprcanhash AND p1.oprcode = p2.oid AND p2.proname = 'array_eq';
-----+--------- -----+---------
(0 rows) (0 rows)
-- Hashable operators should appear as members of hash index opclasses.
SELECT p1.oid, p1.oprname
FROM pg_operator AS p1
WHERE p1.oprcanhash AND NOT EXISTS
(SELECT 1 FROM pg_opclass op JOIN pg_amop p ON op.oid = amopclaid
WHERE opcamid = (SELECT oid FROM pg_am WHERE amname = 'hash') AND
amopopr = p1.oid);
oid | oprname
-----+---------
(0 rows)
-- Check that each operator defined in pg_operator matches its oprcode entry -- Check that each operator defined in pg_operator matches its oprcode entry
-- in pg_proc. Easiest to do this separately for each oprkind. -- in pg_proc. Easiest to do this separately for each oprkind.
SELECT p1.oid, p1.oprname, p2.oid, p2.proname SELECT p1.oid, p1.oprname, p2.oid, p2.proname
...@@ -793,3 +800,24 @@ WHERE p3.opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree') ...@@ -793,3 +800,24 @@ WHERE p3.opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree')
-----------+-----------+-----+---------+--------- -----------+-----------+-----+---------+---------
(0 rows) (0 rows)
-- For hash we can also do a little better: the support routines must be
-- of the form hash(something) returns int4. Ideally we'd check that the
-- opcintype is binary-coercible to the function's input, but there are
-- enough cases where that fails that I'll just leave out the check for now.
SELECT p1.amopclaid, p1.amprocnum,
p2.oid, p2.proname,
p3.opcname
FROM pg_amproc AS p1, pg_proc AS p2, pg_opclass AS p3
WHERE p3.opcamid = (SELECT oid FROM pg_am WHERE amname = 'hash')
AND p1.amopclaid = p3.oid AND p1.amproc = p2.oid AND
(opckeytype != 0
OR amprocnum != 1
OR proretset
OR prorettype != 23
OR pronargs != 1
-- OR NOT physically_coercible(opcintype, proargtypes[0])
);
amopclaid | amprocnum | oid | proname | opcname
-----------+-----------+-----+---------+---------
(0 rows)
...@@ -364,10 +364,6 @@ WHERE p1.oprlsortop != p1.oprrsortop AND ...@@ -364,10 +364,6 @@ WHERE p1.oprlsortop != p1.oprrsortop AND
-- since the hash itself depends on the bitwise representation of the type. -- since the hash itself depends on the bitwise representation of the type.
-- Check that allegedly hashable operators look like they might be "=". -- Check that allegedly hashable operators look like they might be "=".
-- NOTE: as of 7.3, this search finds xideqint4. Since we do not mark
-- xid and int4 as binary-equivalent in pg_cast, there's no easy way to
-- recognize that case as OK; just leave that tuple in the expected output.
SELECT p1.oid, p1.oprname SELECT p1.oid, p1.oprname
FROM pg_operator AS p1 FROM pg_operator AS p1
WHERE p1.oprcanhash AND NOT WHERE p1.oprcanhash AND NOT
...@@ -398,6 +394,16 @@ SELECT p1.oid, p1.oprname ...@@ -398,6 +394,16 @@ SELECT p1.oid, p1.oprname
FROM pg_operator AS p1, pg_proc AS p2 FROM pg_operator AS p1, pg_proc AS p2
WHERE p1.oprcanhash AND p1.oprcode = p2.oid AND p2.proname = 'array_eq'; WHERE p1.oprcanhash AND p1.oprcode = p2.oid AND p2.proname = 'array_eq';
-- Hashable operators should appear as members of hash index opclasses.
SELECT p1.oid, p1.oprname
FROM pg_operator AS p1
WHERE p1.oprcanhash AND NOT EXISTS
(SELECT 1 FROM pg_opclass op JOIN pg_amop p ON op.oid = amopclaid
WHERE opcamid = (SELECT oid FROM pg_am WHERE amname = 'hash') AND
amopopr = p1.oid);
-- Check that each operator defined in pg_operator matches its oprcode entry -- Check that each operator defined in pg_operator matches its oprcode entry
-- in pg_proc. Easiest to do this separately for each oprkind. -- in pg_proc. Easiest to do this separately for each oprkind.
...@@ -665,3 +671,22 @@ WHERE p3.opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree') ...@@ -665,3 +671,22 @@ WHERE p3.opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree')
OR pronargs != 2 OR pronargs != 2
OR NOT binary_coercible(opcintype, proargtypes[0]) OR NOT binary_coercible(opcintype, proargtypes[0])
OR proargtypes[0] != proargtypes[1]); OR proargtypes[0] != proargtypes[1]);
-- For hash we can also do a little better: the support routines must be
-- of the form hash(something) returns int4. Ideally we'd check that the
-- opcintype is binary-coercible to the function's input, but there are
-- enough cases where that fails that I'll just leave out the check for now.
SELECT p1.amopclaid, p1.amprocnum,
p2.oid, p2.proname,
p3.opcname
FROM pg_amproc AS p1, pg_proc AS p2, pg_opclass AS p3
WHERE p3.opcamid = (SELECT oid FROM pg_am WHERE amname = 'hash')
AND p1.amopclaid = p3.oid AND p1.amproc = p2.oid AND
(opckeytype != 0
OR amprocnum != 1
OR proretset
OR prorettype != 23
OR pronargs != 1
-- OR NOT physically_coercible(opcintype, proargtypes[0])
);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment