]> granicus.if.org Git - postgresql/commitdiff
ExecHashRemoveNextSkewBucket must physically copy tuples to main hashtable.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 7 Feb 2016 17:29:17 +0000 (12:29 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 7 Feb 2016 17:29:32 +0000 (12:29 -0500)
Commit 45f6240a8fa9d355 added an assumption in ExecHashIncreaseNumBatches
and ExecHashIncreaseNumBuckets that they could find all tuples in the main
hash table by iterating over the "dense storage" introduced by that patch.
However, ExecHashRemoveNextSkewBucket continued its old practice of simply
re-linking deleted skew tuples into the main table's hashchains.  Hence,
such tuples got lost during any subsequent increase in nbatch or nbuckets,
and would never get joined, as reported in bug #13908 from Seth P.

I (tgl) think that the aforesaid commit has got multiple design issues
and should be reworked rather completely; but there is no time for that
right now, so band-aid the problem by making ExecHashRemoveNextSkewBucket
physically copy deleted skew tuples into the "dense storage" arena.

The added test case is able to exhibit the problem by means of fooling the
planner with a WHERE condition that it will underestimate the selectivity
of, causing the initial nbatch estimate to be too small.

Tomas Vondra and Tom Lane.  Thanks to David Johnston for initial
investigation into the bug report.

src/backend/executor/nodeHash.c
src/test/regress/expected/join.out
src/test/regress/sql/join.sql

index 47160e4aa0797fd5d03f0b85f9e7fb85d4722909..9ed09a7b0ca206d5bb54b79b9270ea09f2eadc25 100644 (file)
@@ -1575,8 +1575,19 @@ ExecHashRemoveNextSkewBucket(HashJoinTable hashtable)
                if (batchno == hashtable->curbatch)
                {
                        /* Move the tuple to the main hash table */
-                       hashTuple->next = hashtable->buckets[bucketno];
-                       hashtable->buckets[bucketno] = hashTuple;
+                       HashJoinTuple copyTuple;
+
+                       /*
+                        * We must copy the tuple into the dense storage, else it will not
+                        * be found by, eg, ExecHashIncreaseNumBatches.
+                        */
+                       copyTuple = (HashJoinTuple) dense_alloc(hashtable, tupleSize);
+                       memcpy(copyTuple, hashTuple, tupleSize);
+                       pfree(hashTuple);
+
+                       copyTuple->next = hashtable->buckets[bucketno];
+                       hashtable->buckets[bucketno] = copyTuple;
+
                        /* We have reduced skew space, but overall space doesn't change */
                        hashtable->spaceUsedSkew -= tupleSize;
                }
index 64f046ee0bb83235cc7708eee24893411b08a1f4..0ac21bb813e91770e3971362aa59b3a9f9aa7dc6 100644 (file)
@@ -2331,6 +2331,34 @@ select tt1.*, tt2.* from tt2 right join tt1 on tt1.joincol = tt2.joincol;
 reset enable_hashjoin;
 reset enable_nestloop;
 --
+-- regression test for bug #13908 (hash join with skew tuples & nbatch increase)
+--
+set work_mem to '64kB';
+set enable_mergejoin to off;
+explain (costs off)
+select count(*) from tenk1 a, tenk1 b
+  where a.hundred = b.thousand and (b.fivethous % 10) < 10;
+                         QUERY PLAN                         
+------------------------------------------------------------
+ Aggregate
+   ->  Hash Join
+         Hash Cond: (a.hundred = b.thousand)
+         ->  Index Only Scan using tenk1_hundred on tenk1 a
+         ->  Hash
+               ->  Seq Scan on tenk1 b
+                     Filter: ((fivethous % 10) < 10)
+(7 rows)
+
+select count(*) from tenk1 a, tenk1 b
+  where a.hundred = b.thousand and (b.fivethous % 10) < 10;
+ count  
+--------
+ 100000
+(1 row)
+
+reset work_mem;
+reset enable_mergejoin;
+--
 -- regression test for 8.2 bug with improper re-ordering of left joins
 --
 create temp table tt3(f1 int, f2 text);
index 0358d00e4045055dae952686bfd19a1a204014f6..fafbb3fb003a5500f72834ea6dc8cf5704dfe980 100644 (file)
@@ -463,6 +463,22 @@ select tt1.*, tt2.* from tt2 right join tt1 on tt1.joincol = tt2.joincol;
 reset enable_hashjoin;
 reset enable_nestloop;
 
+--
+-- regression test for bug #13908 (hash join with skew tuples & nbatch increase)
+--
+
+set work_mem to '64kB';
+set enable_mergejoin to off;
+
+explain (costs off)
+select count(*) from tenk1 a, tenk1 b
+  where a.hundred = b.thousand and (b.fivethous % 10) < 10;
+select count(*) from tenk1 a, tenk1 b
+  where a.hundred = b.thousand and (b.fivethous % 10) < 10;
+
+reset work_mem;
+reset enable_mergejoin;
+
 --
 -- regression test for 8.2 bug with improper re-ordering of left joins
 --