From 4642bc2d746bf42a37dbd96b5519d2765f26f82b Mon Sep 17 00:00:00 2001
From: Mingli Zhang <avamingli@gmail.com>
Date: Fri, 12 Aug 2022 22:35:29 +0800
Subject: [PATCH vn] Fix HashJoin crash.

In ExecChooseHashTableSize(), commit b154ee63bb uses func pg_nextpower2_size_t
whose param must not be 0.

There is a potential risk that hash_table_bytes < bucket_size in some corner cases.

Reproduce sql:

--create a wide enough table to reproduce the bug
DO language 'plpgsql'
$$
DECLARE var_sql text := 'CREATE TABLE t_1600_columns('
 || string_agg('field' || i::text || ' varchar(255)', ',') || ');'
 FROM generate_series(1,1600) As i;
BEGIN
 EXECUTE var_sql;
END;
$$ ;

create table j1(field1 text);
set work_mem = 64;
set hash_mem_multiplier = 1;
set enable_nestloop = off;
set enable_mergejoin = off;

explain select * from j1 inner join t_1600_columns using(field1);

server closed the connection unexpectedly
 This probably means the server terminated abnormally
 before or while processing the request.
The connection to the server was lost. Attempting reset: Succeeded
---
 src/backend/executor/nodeHash.c         |  5 ++++-
 src/test/regress/expected/join_hash.out | 28 +++++++++++++++++++++++++
 src/test/regress/sql/join_hash.sql      | 24 +++++++++++++++++++++
 3 files changed, 56 insertions(+), 1 deletion(-)

diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 123079c16c..268b6ee9dd 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -832,7 +832,10 @@ ExecChooseHashTableSize(double ntuples, int tupwidth, bool useskew,
 		 * overhead for the hash code, pointer to the next tuple, etc.
 		 */
 		bucket_size = (tupsize * NTUP_PER_BUCKET + sizeof(HashJoinTuple));
-		sbuckets = pg_nextpower2_size_t(hash_table_bytes / bucket_size);
+		if (hash_table_bytes < bucket_size)
+			sbuckets = 1;
+		else
+			sbuckets = pg_nextpower2_size_t(hash_table_bytes / bucket_size);
 		sbuckets = Min(sbuckets, max_pointers);
 		nbuckets = (int) sbuckets;
 		nbuckets = pg_nextpower2_32(nbuckets);
diff --git a/src/test/regress/expected/join_hash.out b/src/test/regress/expected/join_hash.out
index 3ec07bc1af..b13b824f7f 100644
--- a/src/test/regress/expected/join_hash.out
+++ b/src/test/regress/expected/join_hash.out
@@ -1031,3 +1031,31 @@ WHERE
 (1 row)
 
 ROLLBACK;
+-- Verify that hash join on wide table with little memory.
+BEGIN;
+-- create a wide enough table to reproduce the bug.
+DO language 'plpgsql'
+$$
+DECLARE var_sql text := 'CREATE TABLE t_1600_columns('
+ || string_agg('field' || i::text || ' varchar(255)', ',') || ');'
+ FROM generate_series(1,1600) As i;
+BEGIN
+ EXECUTE var_sql;
+END;
+$$ ;
+create table j1(field1 text);
+set local work_mem = 64;
+set local hash_mem_multiplier = 1;
+set local enable_nestloop = off;
+set local enable_mergejoin = off;
+explain (costs off) select * from j1 inner join t_1600_columns using(field1);
+                        QUERY PLAN                        
+----------------------------------------------------------
+ Hash Join
+   Hash Cond: ((t_1600_columns.field1)::text = j1.field1)
+   ->  Seq Scan on t_1600_columns
+   ->  Hash
+         ->  Seq Scan on j1
+(5 rows)
+
+ROLLBACK;
diff --git a/src/test/regress/sql/join_hash.sql b/src/test/regress/sql/join_hash.sql
index 77dbc182d5..bd4172d541 100644
--- a/src/test/regress/sql/join_hash.sql
+++ b/src/test/regress/sql/join_hash.sql
@@ -556,3 +556,27 @@ WHERE
     AND hjtest_1.a <> hjtest_2.b;
 
 ROLLBACK;
+
+-- Verify that hash join on wide table with little memory.
+BEGIN;
+-- create a wide enough table to reproduce the bug.
+DO language 'plpgsql'
+$$
+DECLARE var_sql text := 'CREATE TABLE t_1600_columns('
+ || string_agg('field' || i::text || ' varchar(255)', ',') || ');'
+ FROM generate_series(1,1600) As i;
+BEGIN
+ EXECUTE var_sql;
+END;
+$$ ;
+
+create table j1(field1 text);
+
+set local work_mem = 64;
+set local hash_mem_multiplier = 1;
+set local enable_nestloop = off;
+set local enable_mergejoin = off;
+
+explain (costs off) select * from j1 inner join t_1600_columns using(field1);
+
+ROLLBACK;
\ No newline at end of file
-- 
2.34.1

