]> granicus.if.org Git - postgresql/commitdiff
Fix cross-type case in partial row matching for hashed subplans.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 11 Oct 2012 16:21:02 +0000 (12:21 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 11 Oct 2012 16:21:02 +0000 (12:21 -0400)
When hashing a subplan like "WHERE (a, b) NOT IN (SELECT x, y FROM ...)",
findPartialMatch() attempted to match rows using the hashtable's internal
equality operators, which of course are for x and y's datatypes.  What we
need to use are the potentially cross-type operators for a=x, b=y, etc.
Failure to do that leads to wrong answers or even crashes.  The scope for
problems is limited to cases where we have different types with compatible
hash functions (else we'd not be using a hashed subplan), but for example
int4 vs int8 can cause the problem.

Per bug #7597 from Bo Jensen.  This has been wrong since the hashed-subplan
code was written, so patch all the way back.

src/backend/executor/nodeSubplan.c
src/test/regress/expected/subselect.out
src/test/regress/sql/subselect.sql

index 4b8746f4f2611d84f80a97178c677002f5710f63..cc1a13da486c787e2244341db6ee44d5b66afba7 100644 (file)
@@ -44,7 +44,8 @@ static Datum ExecScanSubPlan(SubPlanState *node,
                                ExprContext *econtext,
                                bool *isNull);
 static void buildSubPlanHash(SubPlanState *node, ExprContext *econtext);
-static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
+static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot,
+                                FmgrInfo *eqfunctions);
 static bool slotAllNulls(TupleTableSlot *slot);
 static bool slotNoNulls(TupleTableSlot *slot);
 
@@ -151,7 +152,7 @@ ExecHashSubPlan(SubPlanState *node,
                        return BoolGetDatum(true);
                }
                if (node->havenullrows &&
-                       findPartialMatch(node->hashnulls, slot))
+                       findPartialMatch(node->hashnulls, slot, node->cur_eq_funcs))
                {
                        ExecClearTuple(slot);
                        *isNull = true;
@@ -184,14 +185,14 @@ ExecHashSubPlan(SubPlanState *node,
        }
        /* Scan partly-null table first, since more likely to get a match */
        if (node->havenullrows &&
-               findPartialMatch(node->hashnulls, slot))
+               findPartialMatch(node->hashnulls, slot, node->cur_eq_funcs))
        {
                ExecClearTuple(slot);
                *isNull = true;
                return BoolGetDatum(false);
        }
        if (node->havehashrows &&
-               findPartialMatch(node->hashtable, slot))
+               findPartialMatch(node->hashtable, slot, node->cur_eq_funcs))
        {
                ExecClearTuple(slot);
                *isNull = true;
@@ -571,9 +572,13 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
  * We have to scan the whole hashtable; we can't usefully use hashkeys
  * to guide probing, since we might get partial matches on tuples with
  * hashkeys quite unrelated to what we'd get from the given tuple.
+ *
+ * Caller must provide the equality functions to use, since in cross-type
+ * cases these are different from the hashtable's internal functions.
  */
 static bool
-findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot)
+findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot,
+                                FmgrInfo *eqfunctions)
 {
        int                     numCols = hashtable->numCols;
        AttrNumber *keyColIdx = hashtable->keyColIdx;
@@ -586,7 +591,7 @@ findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot)
                ExecStoreMinimalTuple(entry->firstTuple, hashtable->tableslot, false);
                if (!execTuplesUnequal(slot, hashtable->tableslot,
                                                           numCols, keyColIdx,
-                                                          hashtable->cur_eq_funcs,
+                                                          eqfunctions,
                                                           hashtable->tempcxt))
                {
                        TermTupleHashIterator(&hashiter);
index a4c3f992a953fd981ba1adca3ac3de3ad92bf0e1..bc67a3e7ffeb2b5c3d68c3821ad7dd310cc6961f 100644 (file)
@@ -546,6 +546,23 @@ from
 -----
 (0 rows)
 
+--
+-- Test case for cross-type partial matching in hashed subplan (bug #7597)
+--
+create temp table outer_7597 (f1 int4, f2 int4);
+insert into outer_7597 values (0, 0);
+insert into outer_7597 values (1, 0);
+insert into outer_7597 values (0, null);
+insert into outer_7597 values (1, null);
+create temp table inner_7597(c1 int8, c2 int8);
+insert into inner_7597 values(0, null);
+select * from outer_7597 where (f1, f2) not in (select * from inner_7597);
+ f1 | f2 
+----+----
+  1 |  0
+  1 |   
+(2 rows)
+
 --
 -- Test case for premature memory release during hashing of subplan output
 --
index e07a30ed03baca56bf3c740f5d9fc616c75370a7..cba449a203df65838e16bbddf0d1e880a10e42b6 100644 (file)
@@ -345,6 +345,21 @@ from
   join
   int4_tbl i4 on dummy = i4.f1;
 
+--
+-- Test case for cross-type partial matching in hashed subplan (bug #7597)
+--
+
+create temp table outer_7597 (f1 int4, f2 int4);
+insert into outer_7597 values (0, 0);
+insert into outer_7597 values (1, 0);
+insert into outer_7597 values (0, null);
+insert into outer_7597 values (1, null);
+
+create temp table inner_7597(c1 int8, c2 int8);
+insert into inner_7597 values(0, null);
+
+select * from outer_7597 where (f1, f2) not in (select * from inner_7597);
+
 --
 -- Test case for premature memory release during hashing of subplan output
 --