Add support for cross-type hashing in hashed subplans (hashed IN/NOT IN cases
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 6 Feb 2007 02:59:15 +0000 (02:59 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 6 Feb 2007 02:59:15 +0000 (02:59 +0000)
that aren't turned into true joins).  Since this is the last missing bit of
infrastructure, go ahead and fill out the hash integer_ops and float_ops
opfamilies with cross-type operators.  The operator family project is now
DONE ... er, except for documentation ...

src/backend/executor/execGrouping.c
src/backend/executor/nodeSubplan.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/util/pathnode.c
src/include/catalog/catversion.h
src/include/catalog/pg_amop.h
src/include/catalog/pg_operator.h
src/include/executor/executor.h
src/include/nodes/execnodes.h
src/test/regress/expected/opr_sanity.out
src/test/regress/sql/opr_sanity.sql

index 08391bcc45946937033a48f49711dd95e328c0e6..e6c9cf2a7d460b293d28df7643bf0c9bedf15f92 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/execGrouping.c,v 1.24 2007/01/30 01:33:36 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/execGrouping.c,v 1.25 2007/02/06 02:59:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -178,7 +178,7 @@ execTuplesUnequal(TupleTableSlot *slot1,
 /*
  * execTuplesMatchPrepare
  *     Look up the equality functions needed for execTuplesMatch or
- *     execTuplesUnequal.
+ *     execTuplesUnequal, given an array of equality operator OIDs.
  *
  * The result is a palloc'd array.
  */
@@ -208,6 +208,8 @@ execTuplesMatchPrepare(int numCols,
  * This is similar to execTuplesMatchPrepare, but we also need to find the
  * hash functions associated with the equality operators.  *eqFunctions and
  * *hashFunctions receive the palloc'd result arrays.
+ *
+ * Note: we expect that the given operators are not cross-type comparisons.
  */
 void
 execTuplesHashPrepare(int numCols,
@@ -232,7 +234,7 @@ execTuplesHashPrepare(int numCols,
                                   &left_hash_function, &right_hash_function))
            elog(ERROR, "could not find hash function for hash operator %u",
                 eq_opr);
-       /* For the moment, we're not supporting cross-type cases here */
+       /* We're not supporting cross-type cases here */
        Assert(left_hash_function == right_hash_function);
        fmgr_info(eq_function, &(*eqFunctions)[i]);
        fmgr_info(right_hash_function, &(*hashFunctions)[i]);
@@ -259,7 +261,9 @@ execTuplesHashPrepare(int numCols,
  * tablecxt: memory context in which to store table and table entries
  * tempcxt: short-lived context for evaluation hash and comparison functions
  *
- * The function arrays may be made with execTuplesHashPrepare().
+ * The function arrays may be made with execTuplesHashPrepare().  Note they
+ * are not cross-type functions, but expect to see the table datatype(s)
+ * on both sides.
  *
  * Note that keyColIdx, eqfunctions, and hashfunctions must be allocated in
  * storage that will live as long as the hashtable does.
@@ -282,13 +286,15 @@ BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
 
    hashtable->numCols = numCols;
    hashtable->keyColIdx = keyColIdx;
-   hashtable->eqfunctions = eqfunctions;
-   hashtable->hashfunctions = hashfunctions;
+   hashtable->tab_hash_funcs = hashfunctions;
+   hashtable->tab_eq_funcs = eqfunctions;
    hashtable->tablecxt = tablecxt;
    hashtable->tempcxt = tempcxt;
    hashtable->entrysize = entrysize;
    hashtable->tableslot = NULL;    /* will be made on first lookup */
    hashtable->inputslot = NULL;
+   hashtable->in_hash_funcs = NULL;
+   hashtable->cur_eq_funcs = NULL;
 
    MemSet(&hash_ctl, 0, sizeof(hash_ctl));
    hash_ctl.keysize = sizeof(TupleHashEntryData);
@@ -305,7 +311,7 @@ BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
 
 /*
  * Find or create a hashtable entry for the tuple group containing the
- * given tuple.
+ * given tuple.  The tuple must be the same type as the hashtable entries.
  *
  * If isnew is NULL, we do not create new entries; we return NULL if no
  * match is found.
@@ -351,6 +357,9 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
     * invoke this code re-entrantly.
     */
    hashtable->inputslot = slot;
+   hashtable->in_hash_funcs = hashtable->tab_hash_funcs;
+   hashtable->cur_eq_funcs = hashtable->tab_eq_funcs;
+
    saveCurHT = CurTupleHashTable;
    CurTupleHashTable = hashtable;
 
@@ -394,6 +403,55 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
    return entry;
 }
 
+/*
+ * Search for a hashtable entry matching the given tuple.  No entry is
+ * created if there's not a match.  This is similar to the non-creating
+ * case of LookupTupleHashEntry, except that it supports cross-type
+ * comparisons, in which the given tuple is not of the same type as the
+ * table entries.  The caller must provide the hash functions to use for
+ * the input tuple, as well as the equality functions, since these may be
+ * different from the table's internal functions.
+ */
+TupleHashEntry
+FindTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
+                  FmgrInfo *eqfunctions,
+                  FmgrInfo *hashfunctions)
+{
+   TupleHashEntry entry;
+   MemoryContext oldContext;
+   TupleHashTable saveCurHT;
+   TupleHashEntryData dummy;
+
+   /* Need to run the hash functions in short-lived context */
+   oldContext = MemoryContextSwitchTo(hashtable->tempcxt);
+
+   /*
+    * Set up data needed by hash and match functions
+    *
+    * We save and restore CurTupleHashTable just in case someone manages to
+    * invoke this code re-entrantly.
+    */
+   hashtable->inputslot = slot;
+   hashtable->in_hash_funcs = hashfunctions;
+   hashtable->cur_eq_funcs = eqfunctions;
+
+   saveCurHT = CurTupleHashTable;
+   CurTupleHashTable = hashtable;
+
+   /* Search the hash table */
+   dummy.firstTuple = NULL;    /* flag to reference inputslot */
+   entry = (TupleHashEntry) hash_search(hashtable->hashtab,
+                                        &dummy,
+                                        HASH_FIND,
+                                        NULL);
+
+   CurTupleHashTable = saveCurHT;
+
+   MemoryContextSwitchTo(oldContext);
+
+   return entry;
+}
+
 /*
  * Compute the hash value for a tuple
  *
@@ -418,6 +476,7 @@ TupleHashTableHash(const void *key, Size keysize)
    TupleHashTable hashtable = CurTupleHashTable;
    int         numCols = hashtable->numCols;
    AttrNumber *keyColIdx = hashtable->keyColIdx;
+   FmgrInfo   *hashfunctions;
    uint32      hashkey = 0;
    int         i;
 
@@ -425,6 +484,7 @@ TupleHashTableHash(const void *key, Size keysize)
    {
        /* Process the current input tuple for the table */
        slot = hashtable->inputslot;
+       hashfunctions = hashtable->in_hash_funcs;
    }
    else
    {
@@ -432,6 +492,7 @@ TupleHashTableHash(const void *key, Size keysize)
        /* (this case never actually occurs in current dynahash.c code) */
        slot = hashtable->tableslot;
        ExecStoreMinimalTuple(tuple, slot, false);
+       hashfunctions = hashtable->tab_hash_funcs;
    }
 
    for (i = 0; i < numCols; i++)
@@ -449,7 +510,7 @@ TupleHashTableHash(const void *key, Size keysize)
        {
            uint32      hkey;
 
-           hkey = DatumGetUInt32(FunctionCall1(&hashtable->hashfunctions[i],
+           hkey = DatumGetUInt32(FunctionCall1(&hashfunctions[i],
                                                attr));
            hashkey ^= hkey;
        }
@@ -493,11 +554,12 @@ TupleHashTableMatch(const void *key1, const void *key2, Size keysize)
    Assert(tuple2 == NULL);
    slot2 = hashtable->inputslot;
 
-   if (execTuplesMatch(slot1,
-                       slot2,
+   /* For crosstype comparisons, the inputslot must be first */
+   if (execTuplesMatch(slot2,
+                       slot1,
                        hashtable->numCols,
                        hashtable->keyColIdx,
-                       hashtable->eqfunctions,
+                       hashtable->cur_eq_funcs,
                        hashtable->tempcxt))
        return 0;
    else
index 0e840802eb0fcd138692e5b31e1c2923f1f7a1af..32167a94efbff491b18797034acac2422c90ad5d 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.84 2007/02/02 00:07:03 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.85 2007/02/06 02:59:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -139,7 +139,10 @@ ExecHashSubPlan(SubPlanState *node,
    if (slotNoNulls(slot))
    {
        if (node->havehashrows &&
-           LookupTupleHashEntry(node->hashtable, slot, NULL) != NULL)
+           FindTupleHashEntry(node->hashtable,
+                              slot,
+                              node->cur_eq_funcs,
+                              node->lhs_hash_funcs) != NULL)
        {
            ExecClearTuple(slot);
            return BoolGetDatum(true);
@@ -453,8 +456,8 @@ buildSubPlanHash(SubPlanState *node)
 
    node->hashtable = BuildTupleHashTable(ncols,
                                          node->keyColIdx,
-                                         node->eqfunctions,
-                                         node->hashfunctions,
+                                         node->tab_eq_funcs,
+                                         node->tab_hash_funcs,
                                          nbuckets,
                                          sizeof(TupleHashEntryData),
                                          node->tablecxt,
@@ -472,8 +475,8 @@ buildSubPlanHash(SubPlanState *node)
        }
        node->hashnulls = BuildTupleHashTable(ncols,
                                              node->keyColIdx,
-                                             node->eqfunctions,
-                                             node->hashfunctions,
+                                             node->tab_eq_funcs,
+                                             node->tab_hash_funcs,
                                              nbuckets,
                                              sizeof(TupleHashEntryData),
                                              node->tablecxt,
@@ -573,9 +576,9 @@ findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot)
    while ((entry = ScanTupleHashTable(&hashiter)) != NULL)
    {
        ExecStoreMinimalTuple(entry->firstTuple, hashtable->tableslot, false);
-       if (!execTuplesUnequal(hashtable->tableslot, slot,
+       if (!execTuplesUnequal(slot, hashtable->tableslot,
                               numCols, keyColIdx,
-                              hashtable->eqfunctions,
+                              hashtable->cur_eq_funcs,
                               hashtable->tempcxt))
            return true;
    }
@@ -653,8 +656,10 @@ ExecInitSubPlan(SubPlanState *node, EState *estate, int eflags)
    node->tablecxt = NULL;
    node->innerecontext = NULL;
    node->keyColIdx = NULL;
-   node->eqfunctions = NULL;
-   node->hashfunctions = NULL;
+   node->tab_hash_funcs = NULL;
+   node->tab_eq_funcs = NULL;
+   node->lhs_hash_funcs = NULL;
+   node->cur_eq_funcs = NULL;
 
    /*
     * create an EState for the subplan
@@ -781,8 +786,10 @@ ExecInitSubPlan(SubPlanState *node, EState *estate, int eflags)
 
        lefttlist = righttlist = NIL;
        leftptlist = rightptlist = NIL;
-       node->eqfunctions = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
-       node->hashfunctions = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
+       node->tab_hash_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
+       node->tab_eq_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
+       node->lhs_hash_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
+       node->cur_eq_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
        i = 1;
        foreach(l, oplist)
        {
@@ -792,6 +799,7 @@ ExecInitSubPlan(SubPlanState *node, EState *estate, int eflags)
            Expr       *expr;
            TargetEntry *tle;
            GenericExprState *tlestate;
+           Oid         rhs_eq_oper;
            Oid         left_hashfn;
            Oid         right_hashfn;
 
@@ -827,18 +835,24 @@ ExecInitSubPlan(SubPlanState *node, EState *estate, int eflags)
            righttlist = lappend(righttlist, tlestate);
            rightptlist = lappend(rightptlist, tle);
 
-           /* Lookup the combining function */
-           fmgr_info(opexpr->opfuncid, &node->eqfunctions[i - 1]);
-           node->eqfunctions[i - 1].fn_expr = (Node *) opexpr;
+           /* Lookup the equality function (potentially cross-type) */
+           fmgr_info(opexpr->opfuncid, &node->cur_eq_funcs[i - 1]);
+           node->cur_eq_funcs[i - 1].fn_expr = (Node *) opexpr;
+
+           /* Look up the equality function for the RHS type */
+           if (!get_compatible_hash_operators(opexpr->opno,
+                                              NULL, &rhs_eq_oper))
+               elog(ERROR, "could not find compatible hash operator for operator %u",
+                    opexpr->opno);
+           fmgr_info(get_opcode(rhs_eq_oper), &node->tab_eq_funcs[i - 1]);
 
            /* Lookup the associated hash functions */
            if (!get_op_hash_functions(opexpr->opno,
                                       &left_hashfn, &right_hashfn))
                elog(ERROR, "could not find hash function for hash operator %u",
                     opexpr->opno);
-           /* For the moment, not supporting cross-type cases */
-           Assert(left_hashfn == right_hashfn);
-           fmgr_info(right_hashfn, &node->hashfunctions[i - 1]);
+           fmgr_info(left_hashfn, &node->lhs_hash_funcs[i - 1]);
+           fmgr_info(right_hashfn, &node->tab_hash_funcs[i - 1]);
 
            i++;
        }
index 7339445e04611422d92d618e43036ca8f2233fa2..e79991a0f60b039d89e661f7150b5e62b485ee8b 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.117 2007/01/10 18:06:03 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.118 2007/02/06 02:59:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -598,17 +598,13 @@ subplan_is_hashable(SubLink *slink, SubPlan *node)
        return false;
 
    /*
-    * The combining operators must be hashable, strict, and self-commutative.
+    * The combining operators must be hashable and strict.
     * The need for hashability is obvious, since we want to use hashing.
     * Without strictness, behavior in the presence of nulls is too
-    * unpredictable.  (We actually must assume even more than plain
-    * strictness, see nodeSubplan.c for details.)  And commutativity ensures
-    * that the left and right datatypes are the same; this allows us to
-    * assume that the combining operators are equality for the righthand
-    * datatype, so that they can be used to compare righthand tuples as well
-    * as comparing lefthand to righthand tuples.  (This last restriction
-    * could be relaxed by using two different sets of operators with the hash
-    * table, but there is no obvious usefulness to that at present.)
+    * unpredictable.  We actually must assume even more than plain
+    * strictness: they can't yield NULL for non-null inputs, either
+    * (see nodeSubplan.c).  However, hash indexes and hash joins assume
+    * that too.
     */
    if (IsA(slink->testexpr, OpExpr))
    {
@@ -644,8 +640,7 @@ hash_ok_operator(OpExpr *expr)
    if (!HeapTupleIsValid(tup))
        elog(ERROR, "cache lookup failed for operator %u", opid);
    optup = (Form_pg_operator) GETSTRUCT(tup);
-   if (!optup->oprcanhash || optup->oprcom != opid ||
-       !func_strict(optup->oprcode))
+   if (!optup->oprcanhash || !func_strict(optup->oprcode))
    {
        ReleaseSysCache(tup);
        return false;
index 5832d145ef09d151b3a1b7a6e0429740f106b6ff..81f7c99e96310a6584b7999bed6f5656d5b469c9 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.137 2007/01/20 20:45:39 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.138 2007/02/06 02:59:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1070,7 +1070,7 @@ distinct_col_search(int colno, List *colnos, List *opids)
  * We assume hashed aggregation will work if each IN operator is marked
  * hashjoinable.  If the IN operators are cross-type, this could conceivably
  * fail: the aggregation will need a hashable equality operator for the RHS
- * datatype --- but it's pretty hard to conceive of a hash opclass that has
+ * datatype --- but it's pretty hard to conceive of a hash opfamily that has
  * cross-type hashing without support for hashing the individual types, so
  * we don't expend cycles here to support the case.  We could check
  * get_compatible_hash_operator() instead of just op_hashjoinable(), but the
index 6aebea61dd990317570d1294bcd9316245d76f37..154b56b32ddf6ad791aaa9b0ceb120cba092402f 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.380 2007/02/05 04:22:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.381 2007/02/06 02:59:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 200702041
+#define CATALOG_VERSION_NO 200702051
 
 #endif
index 01e47df7d9cb31e23c6760b09b7d855d327e3004..c1023df6ea922bd45a7ef8277bdf03b9eeb9d758 100644 (file)
@@ -29,7 +29,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_amop.h,v 1.78 2007/01/28 16:16:52 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_amop.h,v 1.79 2007/02/06 02:59:12 tgl Exp $
  *
  * NOTES
  *  the genbki.sh script reads this file and generates .bki
@@ -510,12 +510,20 @@ DATA(insert ( 435   1082 1082 1 f 1093    405 ));
 /* float_ops */
 DATA(insert (  1971   700 700 1 f  620 405 ));
 DATA(insert (  1971   701 701 1 f  670 405 ));
+DATA(insert (  1971   700 701 1 f 1120 405 ));
+DATA(insert (  1971   701 700 1 f 1130 405 ));
 /* network_ops */
 DATA(insert (  1975   869 869 1 f 1201 405 ));
 /* integer_ops */
 DATA(insert (  1977   21 21 1 f    94  405 ));
 DATA(insert (  1977   23 23 1 f    96  405 ));
 DATA(insert (  1977   20 20 1 f    410 405 ));
+DATA(insert (  1977   21 23 1 f    532 405 ));
+DATA(insert (  1977   21 20 1 f   1862 405 ));
+DATA(insert (  1977   23 21 1 f    533 405 ));
+DATA(insert (  1977   23 20 1 f    15  405 ));
+DATA(insert (  1977   20 21 1 f   1868 405 ));
+DATA(insert (  1977   20 23 1 f    416 405 ));
 /* interval_ops */
 DATA(insert (  1983   1186 1186 1 f 1330   405 ));
 /* macaddr_ops */
index 6411da05c3a63390c46879f8a9fb1478f51f28ed..e53f74b5193a774105bc7c936c8ff38c3e4c80a9 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_operator.h,v 1.149 2007/01/28 16:16:52 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_operator.h,v 1.150 2007/02/06 02:59:12 tgl Exp $
  *
  * NOTES
  *   the genbki.sh script reads this file and generates .bki
@@ -89,7 +89,7 @@ typedef FormData_pg_operator *Form_pg_operator;
  * ----------------
  */
 
-DATA(insert OID =  15 ( "="           PGNSP PGUID b t f    23  20  16 416  36 int48eq eqsel eqjoinsel ));
+DATA(insert OID =  15 ( "="           PGNSP PGUID b t t    23  20  16 416  36 int48eq eqsel eqjoinsel ));
 DATA(insert OID =  36 ( "<>"      PGNSP PGUID b f f    23  20  16 417  15 int48ne neqsel neqjoinsel ));
 DATA(insert OID =  37 ( "<"           PGNSP PGUID b f f    23  20  16 419  82 int48lt scalarltsel scalarltjoinsel ));
 DATA(insert OID =  76 ( ">"           PGNSP PGUID b f f    23  20  16 418  80 int48gt scalargtsel scalargtjoinsel ));
@@ -139,7 +139,7 @@ DATA(insert OID = 413 ( ">"        PGNSP PGUID b f f    20  20  16 412 414 int8gt scalar
 DATA(insert OID = 414 ( "<="      PGNSP PGUID b f f    20  20  16 415 413 int8le scalarltsel scalarltjoinsel ));
 DATA(insert OID = 415 ( ">="      PGNSP PGUID b f f    20  20  16 414 412 int8ge scalargtsel scalargtjoinsel ));
 
-DATA(insert OID = 416 ( "="           PGNSP PGUID b t f    20  23  16  15 417 int84eq eqsel eqjoinsel ));
+DATA(insert OID = 416 ( "="           PGNSP PGUID b t t    20  23  16  15 417 int84eq eqsel eqjoinsel ));
 DATA(insert OID = 417 ( "<>"      PGNSP PGUID b f f    20  23  16  36 416 int84ne neqsel neqjoinsel ));
 DATA(insert OID = 418 ( "<"           PGNSP PGUID b f f    20  23  16  76 430 int84lt scalarltsel scalarltjoinsel ));
 DATA(insert OID = 419 ( ">"           PGNSP PGUID b f f    20  23  16  37 420 int84gt scalargtsel scalargtjoinsel ));
@@ -194,8 +194,8 @@ DATA(insert OID = 528 (  "/"       PGNSP PGUID b f f    23  23  23   0   0 int4div - - ));
 DATA(insert OID = 529 (  "%"      PGNSP PGUID b f f    21  21  21   0   0 int2mod - - ));
 DATA(insert OID = 530 (  "%"      PGNSP PGUID b f f    23  23  23   0   0 int4mod - - ));
 DATA(insert OID = 531 (  "<>"     PGNSP PGUID b f f    25  25  16 531  98 textne neqsel neqjoinsel ));
-DATA(insert OID = 532 (  "="      PGNSP PGUID b t f    21  23  16 533 538 int24eq eqsel eqjoinsel ));
-DATA(insert OID = 533 (  "="      PGNSP PGUID b t f    23  21  16 532 539 int42eq eqsel eqjoinsel ));
+DATA(insert OID = 532 (  "="      PGNSP PGUID b t t    21  23  16 533 538 int24eq eqsel eqjoinsel ));
+DATA(insert OID = 533 (  "="      PGNSP PGUID b t t    23  21  16 532 539 int42eq eqsel eqjoinsel ));
 DATA(insert OID = 534 (  "<"      PGNSP PGUID b f f    21  23  16 537 542 int24lt scalarltsel scalarltjoinsel ));
 DATA(insert OID = 535 (  "<"      PGNSP PGUID b f f    23  21  16 536 543 int42lt scalarltsel scalarltjoinsel ));
 DATA(insert OID = 536 (  ">"      PGNSP PGUID b f f    21  23  16 535 540 int24gt scalargtsel scalargtjoinsel ));
@@ -479,7 +479,7 @@ DATA(insert OID = 1116 (  "+"       PGNSP PGUID b f f 700 701 701 1126   0 float48pl -
 DATA(insert OID = 1117 (  "-"      PGNSP PGUID b f f 700 701 701  0     0 float48mi - - ));
 DATA(insert OID = 1118 (  "/"      PGNSP PGUID b f f 700 701 701  0     0 float48div - - ));
 DATA(insert OID = 1119 (  "*"      PGNSP PGUID b f f 700 701 701 1129   0 float48mul - - ));
-DATA(insert OID = 1120 (  "="      PGNSP PGUID b t f  700  701  16 1130 1121 float48eq eqsel eqjoinsel ));
+DATA(insert OID = 1120 (  "="      PGNSP PGUID b t t  700  701  16 1130 1121 float48eq eqsel eqjoinsel ));
 DATA(insert OID = 1121 (  "<>"     PGNSP PGUID b f f  700  701  16 1131 1120 float48ne neqsel neqjoinsel ));
 DATA(insert OID = 1122 (  "<"      PGNSP PGUID b f f  700  701  16 1133 1125 float48lt scalarltsel scalarltjoinsel ));
 DATA(insert OID = 1123 (  ">"      PGNSP PGUID b f f  700  701  16 1132 1124 float48gt scalargtsel scalargtjoinsel ));
@@ -491,7 +491,7 @@ DATA(insert OID = 1126 (  "+"       PGNSP PGUID b f f 701 700 701 1116   0 float84pl -
 DATA(insert OID = 1127 (  "-"      PGNSP PGUID b f f 701 700 701  0     0 float84mi - - ));
 DATA(insert OID = 1128 (  "/"      PGNSP PGUID b f f 701 700 701  0     0 float84div - - ));
 DATA(insert OID = 1129 (  "*"      PGNSP PGUID b f f 701 700 701 1119   0 float84mul - - ));
-DATA(insert OID = 1130 (  "="      PGNSP PGUID b t f  701  700  16 1120 1131 float84eq eqsel eqjoinsel ));
+DATA(insert OID = 1130 (  "="      PGNSP PGUID b t t  701  700  16 1120 1131 float84eq eqsel eqjoinsel ));
 DATA(insert OID = 1131 (  "<>"     PGNSP PGUID b f f  701  700  16 1121 1130 float84ne neqsel neqjoinsel ));
 DATA(insert OID = 1132 (  "<"      PGNSP PGUID b f f  701  700  16 1123 1135 float84lt scalarltsel scalarltjoinsel ));
 DATA(insert OID = 1133 (  ">"      PGNSP PGUID b f f  701  700  16 1122 1134 float84gt scalargtsel scalargtjoinsel ));
@@ -717,14 +717,14 @@ DATA(insert OID = 1809 (  ">="      PGNSP PGUID b f f 1562 1562 16 1808 1806 varbit
 
 DATA(insert OID = 1849 (  "+"     PGNSP PGUID b f f 1186 1083 1083  1800 0 interval_pl_time - - ));
 
-DATA(insert OID = 1862 ( "="      PGNSP PGUID b t f    21  20  16 1868  1863 int28eq eqsel eqjoinsel ));
+DATA(insert OID = 1862 ( "="      PGNSP PGUID b t t    21  20  16 1868  1863 int28eq eqsel eqjoinsel ));
 DATA(insert OID = 1863 ( "<>"     PGNSP PGUID b f f    21  20  16 1869  1862 int28ne neqsel neqjoinsel ));
 DATA(insert OID = 1864 ( "<"      PGNSP PGUID b f f    21  20  16 1871  1867 int28lt scalarltsel scalarltjoinsel ));
 DATA(insert OID = 1865 ( ">"      PGNSP PGUID b f f    21  20  16 1870  1866 int28gt scalargtsel scalargtjoinsel ));
 DATA(insert OID = 1866 ( "<="     PGNSP PGUID b f f    21  20  16 1873  1865 int28le scalarltsel scalarltjoinsel ));
 DATA(insert OID = 1867 ( ">="     PGNSP PGUID b f f    21  20  16 1872  1864 int28ge scalargtsel scalargtjoinsel ));
 
-DATA(insert OID = 1868 ( "="      PGNSP PGUID b t f    20  21  16  1862 1869 int82eq eqsel eqjoinsel ));
+DATA(insert OID = 1868 ( "="      PGNSP PGUID b t t    20  21  16  1862 1869 int82eq eqsel eqjoinsel ));
 DATA(insert OID = 1869 ( "<>"     PGNSP PGUID b f f    20  21  16  1863 1868 int82ne neqsel neqjoinsel ));
 DATA(insert OID = 1870 ( "<"      PGNSP PGUID b f f    20  21  16  1865 1873 int82lt scalarltsel scalarltjoinsel ));
 DATA(insert OID = 1871 ( ">"      PGNSP PGUID b f f    20  21  16  1864 1872 int82gt scalargtsel scalargtjoinsel ));
@@ -858,7 +858,7 @@ DATA(insert OID = 2553 (  "+"      PGNSP PGUID b f f    1186 1114 1114 2066 0 interva
 DATA(insert OID = 2554 (  "+"     PGNSP PGUID b f f    1186 1184 1184 1327 0 interval_pl_timestamptz - - ));
 DATA(insert OID = 2555 (  "+"     PGNSP PGUID b f f    23   1082 1082 1100 0 integer_pl_date - - ));
 
-/* new operators for Y-direction rtree opclasses */
+/* new operators for Y-direction rtree opfamilies */
 DATA(insert OID = 2570 (  "<<|"    PGNSP PGUID b f f 603 603   16   0   0 box_below positionsel positionjoinsel ));
 DATA(insert OID = 2571 (  "&<|"    PGNSP PGUID b f f 603 603   16   0   0 box_overbelow positionsel positionjoinsel ));
 DATA(insert OID = 2572 (  "|&>"    PGNSP PGUID b f f 603 603   16   0   0 box_overabove positionsel positionjoinsel ));
index 1800f4cb039999c22a32683e86a5d740d028301b..bfbe1ba2f3886739d872c6258c0d2bb5a3c62ab9 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.135 2007/02/02 00:07:03 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.136 2007/02/06 02:59:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -99,6 +99,10 @@ extern TupleHashTable BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
 extern TupleHashEntry LookupTupleHashEntry(TupleHashTable hashtable,
                     TupleTableSlot *slot,
                     bool *isnew);
+extern TupleHashEntry FindTupleHashEntry(TupleHashTable hashtable,
+                                        TupleTableSlot *slot,
+                                        FmgrInfo *eqfunctions,
+                                        FmgrInfo *hashfunctions);
 
 /*
  * prototypes from functions in execJunk.c
index da1ae708722932bd4ca446073a64f2a818130522..35a0ab3a6071408f8073411577ce83a9f7e44ae9 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.166 2007/01/05 22:19:55 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.167 2007/02/06 02:59:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -367,6 +367,16 @@ typedef struct ExecRowMark
  *              Tuple Hash Tables
  *
  * All-in-memory tuple hash tables are used for a number of purposes.
+ *
+ * Note: tab_hash_funcs are for the key datatype(s) stored in the table,
+ * and tab_eq_funcs are non-cross-type equality operators for those types.
+ * Normally these are the only functions used, but FindTupleHashEntry()
+ * supports searching a hashtable using cross-data-type hashing.  For that,
+ * the caller must supply hash functions for the LHS datatype as well as
+ * the cross-type equality operators to use.  in_hash_funcs and cur_eq_funcs
+ * are set to point to the caller's function arrays while doing such a search.
+ * During LookupTupleHashEntry(), they point to tab_hash_funcs and
+ * tab_eq_funcs respectively.
  * ----------------------------------------------------------------
  */
 typedef struct TupleHashEntryData *TupleHashEntry;
@@ -384,13 +394,16 @@ typedef struct TupleHashTableData
    HTAB       *hashtab;        /* underlying dynahash table */
    int         numCols;        /* number of columns in lookup key */
    AttrNumber *keyColIdx;      /* attr numbers of key columns */
-   FmgrInfo   *eqfunctions;    /* lookup data for comparison functions */
-   FmgrInfo   *hashfunctions;  /* lookup data for hash functions */
+   FmgrInfo   *tab_hash_funcs; /* hash functions for table datatype(s) */
+   FmgrInfo   *tab_eq_funcs;   /* equality functions for table datatype(s) */
    MemoryContext tablecxt;     /* memory context containing table */
    MemoryContext tempcxt;      /* context for function evaluations */
    Size        entrysize;      /* actual size to make each hash entry */
    TupleTableSlot *tableslot;  /* slot for referencing table entries */
+   /* The following fields are set transiently for each table search: */
    TupleTableSlot *inputslot;  /* current input tuple's slot */
+   FmgrInfo   *in_hash_funcs;  /* hash functions for input datatype(s) */
+   FmgrInfo   *cur_eq_funcs;   /* equality functions for input vs. table */
 } TupleHashTableData;
 
 typedef HASH_SEQ_STATUS TupleHashIterator;
@@ -585,8 +598,10 @@ typedef struct SubPlanState
    MemoryContext tablecxt;     /* memory context containing tables */
    ExprContext *innerecontext; /* working context for comparisons */
    AttrNumber *keyColIdx;      /* control data for hash tables */
-   FmgrInfo   *eqfunctions;    /* comparison functions for hash tables */
-   FmgrInfo   *hashfunctions;  /* lookup data for hash functions */
+   FmgrInfo   *tab_hash_funcs; /* hash functions for table datatype(s) */
+   FmgrInfo   *tab_eq_funcs;   /* equality functions for table datatype(s) */
+   FmgrInfo   *lhs_hash_funcs; /* hash functions for lefthand datatype(s) */
+   FmgrInfo   *cur_eq_funcs;   /* equality functions for LHS vs. table */
 } SubPlanState;
 
 /* ----------------
index fcbfe3cad510ac9a612c53be1531e90c42ca9ecd..2bc25de0525a4c5335bdb7074748a4c6a24c81af 100644 (file)
@@ -829,13 +829,16 @@ WHERE p1.amopopr = p2.oid AND p2.oprcode = p3.oid AND
 ------------+---------+---------+--------
 (0 rows)
 
--- Multiple-datatype btree opclasses should provide closed sets of equality
+-- Multiple-datatype btree opfamilies should provide closed sets of equality
 -- operators; that is if you provide int2 = int4 and int4 = int8 then you
--- must also provide int2 = int8 (and commutators of all these).  This is
--- necessary because the planner tries to deduce additional qual clauses from
+-- should also provide int2 = int8 (and commutators of all these).  This is
+-- important because the planner tries to deduce additional qual clauses from
 -- transitivity of mergejoinable operators.  If there are clauses
--- int2var = int4var and int4var = int8var, the planner will deduce
--- int2var = int8var ... and it had better have a way to represent it.
+-- int2var = int4var and int4var = int8var, the planner will want to deduce
+-- int2var = int8var ... so there should be a way to represent that.  While
+-- a missing cross-type operator is now only an efficiency loss rather than
+-- an error condition, it still seems reasonable to insist that all built-in
+-- opfamilies be complete.
 -- check commutative closure
 SELECT p1.amoplefttype, p1.amoprighttype
 FROM pg_amop AS p1
@@ -870,6 +873,28 @@ WHERE p1.amopfamily = p2.amopfamily AND
 --------------+---------------+---------------
 (0 rows)
 
+-- We also expect that built-in multiple-datatype hash opfamilies provide
+-- complete sets of cross-type operators.  Again, this isn't required, but
+-- it is reasonable to expect it for built-in opfamilies.
+-- if same family has x=x and y=y, it should have x=y
+SELECT p1.amoplefttype, p2.amoplefttype
+FROM pg_amop AS p1, pg_amop AS p2
+WHERE p1.amopfamily = p2.amopfamily AND
+    p1.amoplefttype = p1.amoprighttype AND
+    p2.amoplefttype = p2.amoprighttype AND
+    p1.amopmethod = (SELECT oid FROM pg_am WHERE amname = 'hash') AND
+    p2.amopmethod = (SELECT oid FROM pg_am WHERE amname = 'hash') AND
+    p1.amopstrategy = 1 AND p2.amopstrategy = 1 AND
+    p1.amoplefttype != p2.amoplefttype AND
+    NOT EXISTS(SELECT 1 FROM pg_amop p3 WHERE
+                 p3.amopfamily = p1.amopfamily AND
+                 p3.amoplefttype = p1.amoplefttype AND
+                 p3.amoprighttype = p2.amoplefttype AND
+                 p3.amopstrategy = 1);
+ amoplefttype | amoplefttype 
+--------------+--------------
+(0 rows)
+
 -- **************** pg_amproc ****************
 -- Look for illegal values in pg_amproc fields
 SELECT p1.amprocfamily, p1.amprocnum
index cbf9baf6728c17d17b8c5f0374a29c764eb5fae2..9496422458eebb2b0f56e9c125b33d50ed0e27de 100644 (file)
@@ -656,13 +656,16 @@ WHERE p1.amopopr = p2.oid AND p2.oprcode = p3.oid AND
     p1.amoplefttype != p1.amoprighttype AND
     p3.provolatile = 'v';
 
--- Multiple-datatype btree opclasses should provide closed sets of equality
+-- Multiple-datatype btree opfamilies should provide closed sets of equality
 -- operators; that is if you provide int2 = int4 and int4 = int8 then you
--- must also provide int2 = int8 (and commutators of all these).  This is
--- necessary because the planner tries to deduce additional qual clauses from
+-- should also provide int2 = int8 (and commutators of all these).  This is
+-- important because the planner tries to deduce additional qual clauses from
 -- transitivity of mergejoinable operators.  If there are clauses
--- int2var = int4var and int4var = int8var, the planner will deduce
--- int2var = int8var ... and it had better have a way to represent it.
+-- int2var = int4var and int4var = int8var, the planner will want to deduce
+-- int2var = int8var ... so there should be a way to represent that.  While
+-- a missing cross-type operator is now only an efficiency loss rather than
+-- an error condition, it still seems reasonable to insist that all built-in
+-- opfamilies be complete.
 
 -- check commutative closure
 SELECT p1.amoplefttype, p1.amoprighttype
@@ -692,6 +695,27 @@ WHERE p1.amopfamily = p2.amopfamily AND
                  p3.amoprighttype = p2.amoprighttype AND
                  p3.amopstrategy = 3);
 
+-- We also expect that built-in multiple-datatype hash opfamilies provide
+-- complete sets of cross-type operators.  Again, this isn't required, but
+-- it is reasonable to expect it for built-in opfamilies.
+
+-- if same family has x=x and y=y, it should have x=y
+SELECT p1.amoplefttype, p2.amoplefttype
+FROM pg_amop AS p1, pg_amop AS p2
+WHERE p1.amopfamily = p2.amopfamily AND
+    p1.amoplefttype = p1.amoprighttype AND
+    p2.amoplefttype = p2.amoprighttype AND
+    p1.amopmethod = (SELECT oid FROM pg_am WHERE amname = 'hash') AND
+    p2.amopmethod = (SELECT oid FROM pg_am WHERE amname = 'hash') AND
+    p1.amopstrategy = 1 AND p2.amopstrategy = 1 AND
+    p1.amoplefttype != p2.amoplefttype AND
+    NOT EXISTS(SELECT 1 FROM pg_amop p3 WHERE
+                 p3.amopfamily = p1.amopfamily AND
+                 p3.amoplefttype = p1.amoplefttype AND
+                 p3.amoprighttype = p2.amoplefttype AND
+                 p3.amopstrategy = 1);
+
+
 -- **************** pg_amproc ****************
 
 -- Look for illegal values in pg_amproc fields