From a4c0f7cfeaec1f4de4e33e545da595fcf853322d Mon Sep 17 00:00:00 2001 From: Tomas Vondra Date: Mon, 27 Jul 2026 19:08:53 +0200 Subject: [PATCH v9 20/21] Test a snowflake join with multi-column FK join clauses --- .../expected/hashjoin_bloom_snowflake.out | 183 ++++++++++++++++++ .../regress/sql/hashjoin_bloom_snowflake.sql | 98 ++++++++++ 2 files changed, 281 insertions(+) diff --git a/src/test/regress/expected/hashjoin_bloom_snowflake.out b/src/test/regress/expected/hashjoin_bloom_snowflake.out index dbe7663614f..b06e42696d6 100644 --- a/src/test/regress/expected/hashjoin_bloom_snowflake.out +++ b/src/test/regress/expected/hashjoin_bloom_snowflake.out @@ -384,3 +384,186 @@ DROP TABLE bloom_snowflake_dim_2_1; DROP TABLE bloom_snowflake_dim_2_2; DROP TABLE bloom_snowflake_dim_2; DROP TABLE bloom_snowflake_fact; +-- snowflake queries on multi-column FK joins +CREATE TABLE bloom_snowflake_multi_dim_1_1 (a int, b int, r real, primary key (a, b)); +CREATE TABLE bloom_snowflake_multi_dim_1_2 (a int, b int, r real, primary key (a, b)); +CREATE TABLE bloom_snowflake_multi_dim_1 ( + a int, b int, + id11a int, id11b int, + id12a int, id12b int, + r real, + foreign key (id11a, id11b) references bloom_snowflake_multi_dim_1_1(a, b), + foreign key (id12a, id12b) references bloom_snowflake_multi_dim_1_2(a, b), + primary key (a, b)); +CREATE TABLE bloom_snowflake_multi_dim_2_1 (a int, b int, r real, primary key (a, b)); +CREATE TABLE bloom_snowflake_multi_dim_2_2 (a int, b int, r real, primary key (a, b)); +CREATE TABLE bloom_snowflake_multi_dim_2 ( + a int, b int, + id21a int, id21b int, + id22a int, id22b int, + r real, + foreign key (id21a, id21b) references bloom_snowflake_multi_dim_2_1(a, b), + foreign key (id22a, id22b) references bloom_snowflake_multi_dim_2_2(a, b), + primary key (a, b)); +CREATE TABLE bloom_snowflake_multi_fact ( + id1a int, id1b int, + id2a int, id2b int, + padding text, + foreign key (id1a, id1b) references bloom_snowflake_multi_dim_1(a, b), + foreign key (id2a, id2b) references bloom_snowflake_multi_dim_2(a, b)); +SELECT setseed(0.5); + setseed +--------- + +(1 row) + +INSERT INTO bloom_snowflake_multi_dim_1_1 SELECT i, i, random() FROM generate_series(1, 1000) s(i); +INSERT INTO bloom_snowflake_multi_dim_1_2 SELECT i, i, random() FROM generate_series(1, 1000) s(i); +WITH d AS (SELECT i, 1 + mod((100000 * random())::int, 1000) AS a, 1 + mod((100000 * random())::int, 1000) AS b FROM generate_series(1, 1000) s(i)) +INSERT INTO bloom_snowflake_multi_dim_1 SELECT i, i, a, a, b, b, random() FROM d; +INSERT INTO bloom_snowflake_multi_dim_2_1 SELECT i, i, random() FROM generate_series(1, 10000) s(i); +INSERT INTO bloom_snowflake_multi_dim_2_2 SELECT i, i, random() FROM generate_series(1, 10000) s(i); +WITH d AS (SELECT i, 1 + mod((100000 * random())::int, 1000) AS a, 1 + mod((100000 * random())::int, 1000) AS b FROM generate_series(1, 1000) s(i)) +INSERT INTO bloom_snowflake_multi_dim_2 SELECT i, i, a, a, b, b, random() FROM d; +WITH d AS (SELECT 1 + mod((100000 * random())::int, 1000) AS a, 1 + mod((100000 * random())::int, 1000) AS b, md5(i::text) AS p FROM generate_series(1, 100000) AS s(i)) +INSERT INTO bloom_snowflake_multi_fact +SELECT + a, a, b, b, p +FROM d; +SET default_statistics_target = 1000; +VACUUM ANALYZE; +-- increase the accepted build size (includes the fact) +SET bloom_filter_pushdown_max_build_relids = 4; +EXPLAIN (ANALYZE, TIMING OFF, SUMMARY OFF, BUFFERS OFF) +SELECT * +FROM bloom_snowflake_multi_fact f +JOIN bloom_snowflake_multi_dim_1 d1 ON (f.id1a = d1.a AND f.id1b = d1.b) +JOIN bloom_snowflake_multi_dim_1_1 d11 ON (d1.id11a = d11.a AND d1.id11b = d11.b) +JOIN bloom_snowflake_multi_dim_1_2 d12 ON (d1.id12a = d12.a AND d1.id12b = d12.b) +WHERE d11.r < 0.45 AND d12.r < 0.55; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------ + Hash Join (cost=76.27..2292.04 rows=24628 width=101) (actual rows=23530.00 loops=1) + Hash Cond: ((f.id1a = d1.a) AND (f.id1b = d1.b)) + -> Seq Scan on bloom_snowflake_multi_fact f (cost=0.00..2031.00 rows=24628 width=49) (actual rows=23531.00 loops=1) + Bloom Filter 3: keys=(id1a, id1b) expected=24.6% checked=99999 rejected=76469 (76.5%) + -> Hash (cost=72.58..72.58 rows=246 width=52) (actual rows=234.00 loops=1) + Buckets: 1024 Batches: 1 Memory Usage: 28kB + Bloom Filter 3: bits=8388608 hashes=10 memory=1024kB checked=99999 rejected=76469 + -> Hash Join (cost=52.00..72.58 rows=246 width=52) (actual rows=234.00 loops=1) + Hash Cond: ((d1.id12a = d12.a) AND (d1.id12b = d12.b)) + -> Hash Join (cost=25.09..44.38 rows=246 width=40) (actual rows=234.00 loops=1) + Hash Cond: ((d1.id11a = d11.a) AND (d1.id11b = d11.b)) + -> Seq Scan on bloom_snowflake_multi_dim_1 d1 (cost=0.00..18.00 rows=246 width=28) (actual rows=235.00 loops=1) + Bloom Filter 1: keys=(id11a, id11b) expected=43.9% checked=996 rejected=569 (57.1%) + Bloom Filter 2: keys=(id12a, id12b) expected=56.1% checked=431 rejected=196 (45.5%) + -> Hash (cost=18.50..18.50 rows=439 width=12) (actual rows=440.00 loops=1) + Buckets: 1024 Batches: 1 Memory Usage: 27kB + Bloom Filter 1: bits=8388608 hashes=10 memory=1024kB checked=996 rejected=569 + -> Seq Scan on bloom_snowflake_multi_dim_1_1 d11 (cost=0.00..18.50 rows=439 width=12) (actual rows=440.00 loops=1) + Filter: (r < '0.45'::double precision) + Rows Removed by Filter: 560 + -> Hash (cost=18.50..18.50 rows=561 width=12) (actual rows=562.00 loops=1) + Buckets: 1024 Batches: 1 Memory Usage: 33kB + Bloom Filter 2: bits=8388608 hashes=10 memory=1024kB checked=431 rejected=196 + -> Seq Scan on bloom_snowflake_multi_dim_1_2 d12 (cost=0.00..18.50 rows=561 width=12) (actual rows=562.00 loops=1) + Filter: (r < '0.55'::double precision) + Rows Removed by Filter: 438 +(26 rows) + +EXPLAIN (ANALYZE, TIMING OFF, SUMMARY OFF, BUFFERS OFF) +SELECT * +FROM bloom_snowflake_multi_fact f +JOIN bloom_snowflake_multi_dim_1 d1 ON (f.id1a = d1.a AND f.id1b = d1.b) +JOIN bloom_snowflake_multi_dim_1_1 d11 ON (d1.id11a = d11.a AND d1.id11b = d11.b) +JOIN bloom_snowflake_multi_dim_1_2 d12 ON (d1.id12a = d12.a AND d1.id12b = d12.b) +WHERE d11.r < 0.75 AND d12.r < 0.75; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------ + Hash Join (cost=94.81..2542.85 rows=55564 width=101) (actual rows=55598.00 loops=1) + Hash Cond: ((f.id1a = d1.a) AND (f.id1b = d1.b)) + -> Seq Scan on bloom_snowflake_multi_fact f (cost=0.00..2031.00 rows=55564 width=49) (actual rows=55598.00 loops=1) + Bloom Filter 1: keys=(id1a, id1b) expected=55.6% checked=99999 rejected=44402 (44.4%) + -> Hash (cost=86.47..86.47 rows=556 width=52) (actual rows=555.00 loops=1) + Buckets: 1024 Batches: 1 Memory Usage: 54kB + Bloom Filter 1: bits=8388608 hashes=10 memory=1024kB checked=99999 rejected=44402 + -> Hash Join (cost=59.36..86.47 rows=556 width=52) (actual rows=555.00 loops=1) + Hash Cond: ((d1.id12a = d12.a) AND (d1.id12b = d12.b)) + -> Hash Join (cost=29.51..52.76 rows=734 width=40) (actual rows=737.00 loops=1) + Hash Cond: ((d1.id11a = d11.a) AND (d1.id11b = d11.b)) + -> Seq Scan on bloom_snowflake_multi_dim_1 d1 (cost=0.00..18.00 rows=1000 width=28) (actual rows=1000.00 loops=1) + -> Hash (cost=18.50..18.50 rows=734 width=12) (actual rows=735.00 loops=1) + Buckets: 1024 Batches: 1 Memory Usage: 40kB + -> Seq Scan on bloom_snowflake_multi_dim_1_1 d11 (cost=0.00..18.50 rows=734 width=12) (actual rows=735.00 loops=1) + Filter: (r < '0.75'::double precision) + Rows Removed by Filter: 265 + -> Hash (cost=18.50..18.50 rows=757 width=12) (actual rows=758.00 loops=1) + Buckets: 1024 Batches: 1 Memory Usage: 41kB + -> Seq Scan on bloom_snowflake_multi_dim_1_2 d12 (cost=0.00..18.50 rows=757 width=12) (actual rows=758.00 loops=1) + Filter: (r < '0.75'::double precision) + Rows Removed by Filter: 242 +(22 rows) + +EXPLAIN (ANALYZE, TIMING OFF, SUMMARY OFF, BUFFERS OFF) +SELECT * +FROM bloom_snowflake_multi_fact f +JOIN bloom_snowflake_multi_dim_1 d1 ON (f.id1a = d1.a AND f.id1b = d1.b) +JOIN bloom_snowflake_multi_dim_1_1 d11 ON (d1.id11a = d11.a AND d1.id11b = d11.b) +JOIN bloom_snowflake_multi_dim_1_2 d12 ON (d1.id12a = d12.a AND d1.id12b = d12.b) +JOIN bloom_snowflake_multi_dim_2 d2 ON (f.id2a = d2.a AND f.id2b = d2.b) +JOIN bloom_snowflake_multi_dim_2_1 d21 ON (d2.id21a = d21.a AND d2.id21b = d21.b) +JOIN bloom_snowflake_multi_dim_2_2 d22 ON (d2.id22a = d22.a AND d2.id22b = d22.b) +WHERE d11.r < 0.75 AND d12.r < 0.75; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Hash Join (cost=331.99..3197.32 rows=55564 width=153) (actual rows=55598.00 loops=1) + Hash Cond: ((f.id1a = d1.a) AND (f.id1b = d1.b)) + -> Hash Join (cost=237.18..2685.47 rows=55564 width=101) (actual rows=55598.00 loops=1) + Hash Cond: ((f.id2a = d2.a) AND (f.id2b = d2.b)) + -> Seq Scan on bloom_snowflake_multi_fact f (cost=0.00..2031.00 rows=55564 width=49) (actual rows=55598.00 loops=1) + Bloom Filter 1: keys=(id1a, id1b) expected=55.6% checked=100000 rejected=44402 (44.4%) + -> Hash (cost=222.18..222.18 rows=1000 width=52) (actual rows=1000.00 loops=1) + Buckets: 1024 Batches: 1 Memory Usage: 91kB + -> Merge Join (cost=170.18..222.18 rows=1000 width=52) (actual rows=1000.00 loops=1) + Merge Cond: ((d22.a = d2.id22a) AND (d22.b = d2.id22b)) + -> Index Scan using bloom_snowflake_multi_dim_2_2_pkey on bloom_snowflake_multi_dim_2_2 d22 (cost=0.29..395.16 rows=10000 width=12) (actual rows=1001.00 loops=1) + Index Searches: 1 + -> Sort (cost=169.90..172.40 rows=1000 width=40) (actual rows=1000.00 loops=1) + Sort Key: d2.id22a, d2.id22b + Sort Method: quicksort Memory: 79kB + -> Merge Join (cost=68.16..120.07 rows=1000 width=40) (actual rows=1000.00 loops=1) + Merge Cond: ((d21.a = d2.id21a) AND (d21.b = d2.id21b)) + -> Index Scan using bloom_snowflake_multi_dim_2_1_pkey on bloom_snowflake_multi_dim_2_1 d21 (cost=0.29..395.16 rows=10000 width=12) (actual rows=1000.00 loops=1) + Index Searches: 1 + -> Sort (cost=67.83..70.33 rows=1000 width=28) (actual rows=1000.00 loops=1) + Sort Key: d2.id21a, d2.id21b + Sort Method: quicksort Memory: 71kB + -> Seq Scan on bloom_snowflake_multi_dim_2 d2 (cost=0.00..18.00 rows=1000 width=28) (actual rows=1000.00 loops=1) + -> Hash (cost=86.47..86.47 rows=556 width=52) (actual rows=555.00 loops=1) + Buckets: 1024 Batches: 1 Memory Usage: 54kB + Bloom Filter 1: bits=8388608 hashes=10 memory=1024kB checked=100000 rejected=44402 + -> Hash Join (cost=59.36..86.47 rows=556 width=52) (actual rows=555.00 loops=1) + Hash Cond: ((d1.id12a = d12.a) AND (d1.id12b = d12.b)) + -> Hash Join (cost=29.51..52.76 rows=734 width=40) (actual rows=737.00 loops=1) + Hash Cond: ((d1.id11a = d11.a) AND (d1.id11b = d11.b)) + -> Seq Scan on bloom_snowflake_multi_dim_1 d1 (cost=0.00..18.00 rows=1000 width=28) (actual rows=1000.00 loops=1) + -> Hash (cost=18.50..18.50 rows=734 width=12) (actual rows=735.00 loops=1) + Buckets: 1024 Batches: 1 Memory Usage: 40kB + -> Seq Scan on bloom_snowflake_multi_dim_1_1 d11 (cost=0.00..18.50 rows=734 width=12) (actual rows=735.00 loops=1) + Filter: (r < '0.75'::double precision) + Rows Removed by Filter: 265 + -> Hash (cost=18.50..18.50 rows=757 width=12) (actual rows=758.00 loops=1) + Buckets: 1024 Batches: 1 Memory Usage: 41kB + -> Seq Scan on bloom_snowflake_multi_dim_1_2 d12 (cost=0.00..18.50 rows=757 width=12) (actual rows=758.00 loops=1) + Filter: (r < '0.75'::double precision) + Rows Removed by Filter: 242 +(41 rows) + +RESET bloom_filter_pushdown_max_build_relids; +DROP TABLE bloom_snowflake_multi_fact; +DROP TABLE bloom_snowflake_multi_dim_1; +DROP TABLE bloom_snowflake_multi_dim_2; +DROP TABLE bloom_snowflake_multi_dim_1_1; +DROP TABLE bloom_snowflake_multi_dim_1_2; +DROP TABLE bloom_snowflake_multi_dim_2_1; +DROP TABLE bloom_snowflake_multi_dim_2_2; diff --git a/src/test/regress/sql/hashjoin_bloom_snowflake.sql b/src/test/regress/sql/hashjoin_bloom_snowflake.sql index e5dcb2dea05..d8119e1528f 100644 --- a/src/test/regress/sql/hashjoin_bloom_snowflake.sql +++ b/src/test/regress/sql/hashjoin_bloom_snowflake.sql @@ -137,3 +137,101 @@ DROP TABLE bloom_snowflake_dim_2_1; DROP TABLE bloom_snowflake_dim_2_2; DROP TABLE bloom_snowflake_dim_2; DROP TABLE bloom_snowflake_fact; + + + +-- snowflake queries on multi-column FK joins +CREATE TABLE bloom_snowflake_multi_dim_1_1 (a int, b int, r real, primary key (a, b)); +CREATE TABLE bloom_snowflake_multi_dim_1_2 (a int, b int, r real, primary key (a, b)); + +CREATE TABLE bloom_snowflake_multi_dim_1 ( + a int, b int, + id11a int, id11b int, + id12a int, id12b int, + r real, + foreign key (id11a, id11b) references bloom_snowflake_multi_dim_1_1(a, b), + foreign key (id12a, id12b) references bloom_snowflake_multi_dim_1_2(a, b), + primary key (a, b)); + +CREATE TABLE bloom_snowflake_multi_dim_2_1 (a int, b int, r real, primary key (a, b)); +CREATE TABLE bloom_snowflake_multi_dim_2_2 (a int, b int, r real, primary key (a, b)); + +CREATE TABLE bloom_snowflake_multi_dim_2 ( + a int, b int, + id21a int, id21b int, + id22a int, id22b int, + r real, + foreign key (id21a, id21b) references bloom_snowflake_multi_dim_2_1(a, b), + foreign key (id22a, id22b) references bloom_snowflake_multi_dim_2_2(a, b), + primary key (a, b)); + +CREATE TABLE bloom_snowflake_multi_fact ( + id1a int, id1b int, + id2a int, id2b int, + padding text, + foreign key (id1a, id1b) references bloom_snowflake_multi_dim_1(a, b), + foreign key (id2a, id2b) references bloom_snowflake_multi_dim_2(a, b)); + +SELECT setseed(0.5); + +INSERT INTO bloom_snowflake_multi_dim_1_1 SELECT i, i, random() FROM generate_series(1, 1000) s(i); +INSERT INTO bloom_snowflake_multi_dim_1_2 SELECT i, i, random() FROM generate_series(1, 1000) s(i); + +WITH d AS (SELECT i, 1 + mod((100000 * random())::int, 1000) AS a, 1 + mod((100000 * random())::int, 1000) AS b FROM generate_series(1, 1000) s(i)) +INSERT INTO bloom_snowflake_multi_dim_1 SELECT i, i, a, a, b, b, random() FROM d; + +INSERT INTO bloom_snowflake_multi_dim_2_1 SELECT i, i, random() FROM generate_series(1, 10000) s(i); +INSERT INTO bloom_snowflake_multi_dim_2_2 SELECT i, i, random() FROM generate_series(1, 10000) s(i); + +WITH d AS (SELECT i, 1 + mod((100000 * random())::int, 1000) AS a, 1 + mod((100000 * random())::int, 1000) AS b FROM generate_series(1, 1000) s(i)) +INSERT INTO bloom_snowflake_multi_dim_2 SELECT i, i, a, a, b, b, random() FROM d; + +WITH d AS (SELECT 1 + mod((100000 * random())::int, 1000) AS a, 1 + mod((100000 * random())::int, 1000) AS b, md5(i::text) AS p FROM generate_series(1, 100000) AS s(i)) +INSERT INTO bloom_snowflake_multi_fact +SELECT + a, a, b, b, p +FROM d; + +SET default_statistics_target = 1000; + +VACUUM ANALYZE; + +-- increase the accepted build size (includes the fact) +SET bloom_filter_pushdown_max_build_relids = 4; + +EXPLAIN (ANALYZE, TIMING OFF, SUMMARY OFF, BUFFERS OFF) +SELECT * +FROM bloom_snowflake_multi_fact f +JOIN bloom_snowflake_multi_dim_1 d1 ON (f.id1a = d1.a AND f.id1b = d1.b) +JOIN bloom_snowflake_multi_dim_1_1 d11 ON (d1.id11a = d11.a AND d1.id11b = d11.b) +JOIN bloom_snowflake_multi_dim_1_2 d12 ON (d1.id12a = d12.a AND d1.id12b = d12.b) +WHERE d11.r < 0.45 AND d12.r < 0.55; + +EXPLAIN (ANALYZE, TIMING OFF, SUMMARY OFF, BUFFERS OFF) +SELECT * +FROM bloom_snowflake_multi_fact f +JOIN bloom_snowflake_multi_dim_1 d1 ON (f.id1a = d1.a AND f.id1b = d1.b) +JOIN bloom_snowflake_multi_dim_1_1 d11 ON (d1.id11a = d11.a AND d1.id11b = d11.b) +JOIN bloom_snowflake_multi_dim_1_2 d12 ON (d1.id12a = d12.a AND d1.id12b = d12.b) +WHERE d11.r < 0.75 AND d12.r < 0.75; + +EXPLAIN (ANALYZE, TIMING OFF, SUMMARY OFF, BUFFERS OFF) +SELECT * +FROM bloom_snowflake_multi_fact f +JOIN bloom_snowflake_multi_dim_1 d1 ON (f.id1a = d1.a AND f.id1b = d1.b) +JOIN bloom_snowflake_multi_dim_1_1 d11 ON (d1.id11a = d11.a AND d1.id11b = d11.b) +JOIN bloom_snowflake_multi_dim_1_2 d12 ON (d1.id12a = d12.a AND d1.id12b = d12.b) +JOIN bloom_snowflake_multi_dim_2 d2 ON (f.id2a = d2.a AND f.id2b = d2.b) +JOIN bloom_snowflake_multi_dim_2_1 d21 ON (d2.id21a = d21.a AND d2.id21b = d21.b) +JOIN bloom_snowflake_multi_dim_2_2 d22 ON (d2.id22a = d22.a AND d2.id22b = d22.b) +WHERE d11.r < 0.75 AND d12.r < 0.75; + +RESET bloom_filter_pushdown_max_build_relids; + +DROP TABLE bloom_snowflake_multi_fact; +DROP TABLE bloom_snowflake_multi_dim_1; +DROP TABLE bloom_snowflake_multi_dim_2; +DROP TABLE bloom_snowflake_multi_dim_1_1; +DROP TABLE bloom_snowflake_multi_dim_1_2; +DROP TABLE bloom_snowflake_multi_dim_2_1; +DROP TABLE bloom_snowflake_multi_dim_2_2; -- 2.50.1 (Apple Git-155)