diff --git a/benchmarks/sql_benchmarks/hj/benchmarks/q24.benchmark b/benchmarks/sql_benchmarks/hj/benchmarks/q24.benchmark new file mode 100644 index 0000000000000..2ea60f0f87009 --- /dev/null +++ b/benchmarks/sql_benchmarks/hj/benchmarks/q24.benchmark @@ -0,0 +1,31 @@ +name Q24 +group hj + +init sql_benchmarks/hj/init/set_config_no_stats.sql + +load sql_benchmarks/hj/init/load.sql + +assert I +SELECT count(*) > 0 FROM lineitem +---- +true + +expect_plan HashJoinExec + +run +-- Q24: single-hot-bucket long string-key inner join. +-- Build rows all share one long string key, so each matching probe row fans +-- out to the whole build side. count(*) focuses the benchmark on hash match +-- and equality filtering without buffering joined rows. +-- Thresholds zeroed to force Partitioned mode (simulates absent row-count stats). +SELECT count(*) +FROM ( + SELECT 'single_hot_bucket_string_join_key' as k + FROM supplier + WHERE s_suppkey <= 3000 +) s +JOIN ( + SELECT 'single_hot_bucket_string_join_key' as k + FROM lineitem + WHERE l_orderkey % 3000 = 0 +) l ON s.k = l.k; diff --git a/benchmarks/sql_benchmarks/hj/benchmarks/q25.benchmark b/benchmarks/sql_benchmarks/hj/benchmarks/q25.benchmark new file mode 100644 index 0000000000000..b29d6b959a853 --- /dev/null +++ b/benchmarks/sql_benchmarks/hj/benchmarks/q25.benchmark @@ -0,0 +1,33 @@ +name Q25 +group hj + +init sql_benchmarks/hj/init/set_config_no_stats.sql + +load sql_benchmarks/hj/init/load.sql + +assert I +SELECT count(*) > 0 FROM lineitem +---- +true + +expect_plan HashJoinExec + +run +-- Q25: skewed high-fanout multi-column string-key inner join. +-- This tracks candidate-pair filtering for composite keys: the first key is +-- skewed and the second long string key must also be checked before emitting +-- each match. count(*) isolates the match path. +-- Thresholds zeroed to force Partitioned mode (simulates absent row-count stats). +SELECT count(*) +FROM ( + SELECT CAST((s_suppkey % 256) + 1 AS INT) as k1, + 'multi_column_high_fanout_key' as k2 + FROM supplier + WHERE s_suppkey <= 20000 +) s +JOIN ( + SELECT CAST(1 AS INT) as k1, + 'multi_column_high_fanout_key' as k2 + FROM lineitem + WHERE l_orderkey % 250 = 0 +) l ON s.k1 = l.k1 AND s.k2 = l.k2; diff --git a/benchmarks/src/hj.rs b/benchmarks/src/hj.rs index 7d33bc3aa9e50..9a91e2714ac86 100644 --- a/benchmarks/src/hj.rs +++ b/benchmarks/src/hj.rs @@ -472,6 +472,52 @@ const HASH_QUERIES: &[HashJoinQuery] = &[ probe_size: "2.3M_long_keys_count", isolate_partitioned_join: true, }, + // Q24: single-hot-bucket long string-key inner join. + // Build rows all share one long string key, so each matching probe row fans + // out to the whole build side. The output is counted to focus on the hash + // match/equality path without buffering the joined rows. + HashJoinQuery { + sql: r###"SELECT count(*) + FROM ( + SELECT 'single_hot_bucket_string_join_key' as k + FROM supplier + WHERE s_suppkey <= 3000 + ) s + JOIN ( + SELECT 'single_hot_bucket_string_join_key' as k + FROM lineitem + WHERE l_orderkey % 3000 = 0 + ) l ON s.k = l.k"###, + density: 1.0, + prob_hit: 1.0, + build_size: "3K_(single_hot_bucket)", + probe_size: "20K_long_keys_count", + isolate_partitioned_join: true, + }, + // Q25: skewed high-fanout multi-column string-key inner join. + // This tracks the same candidate-pair filtering path for composite join + // keys, where the first key is skewed and the second long string key must + // also be checked before emitting each match. + HashJoinQuery { + sql: r###"SELECT count(*) + FROM ( + SELECT CAST((s_suppkey % 256) + 1 AS INT) as k1, + 'multi_column_high_fanout_key' as k2 + FROM supplier + WHERE s_suppkey <= 20000 + ) s + JOIN ( + SELECT CAST(1 AS INT) as k1, + 'multi_column_high_fanout_key' as k2 + FROM lineitem + WHERE l_orderkey % 250 = 0 + ) l ON s.k1 = l.k1 AND s.k2 = l.k2"###, + density: 1.0, + prob_hit: 1.0, + build_size: "20K_(fanout~78_multi_key)", + probe_size: "240K_multi_key_count", + isolate_partitioned_join: true, + }, ]; impl RunOpt {