From d7854e36dbede70bb11f07a13f5634e65400e58c Mon Sep 17 00:00:00 2001 From: Tomas Vondra Date: Mon, 27 Jul 2026 17:42:09 +0200 Subject: [PATCH v9 18/21] Add a multi-column FK join to the simple test Tests 2-way join join matching a multi-column FK constraint. --- src/test/regress/expected/hashjoin_bloom.out | 66 ++++++++++++++++++++ src/test/regress/sql/hashjoin_bloom.sql | 38 +++++++++++ 2 files changed, 104 insertions(+) diff --git a/src/test/regress/expected/hashjoin_bloom.out b/src/test/regress/expected/hashjoin_bloom.out index fd6d895f4cc..793aff233a3 100644 --- a/src/test/regress/expected/hashjoin_bloom.out +++ b/src/test/regress/expected/hashjoin_bloom.out @@ -61,3 +61,69 @@ WHERE d.r < 0.5; DROP TABLE bloom_simple_dim; DROP TABLE bloom_simple_fact; +-- a schema with multi-column foreign key, to test estimates +CREATE TABLE bloom_multi_dim (id1 int, id2 int, r real, primary key (id1, id2)); +CREATE TABLE bloom_multi_fact (id1 int, id2 int, padding text, foreign key (id1, id2) references bloom_multi_dim(id1,id2)); +INSERT INTO bloom_multi_dim SELECT i, i, (i / 1000.0) FROM generate_series(1,1000) s(i); +INSERT INTO bloom_multi_fact +SELECT + 1 + mod(i, 1000), + 1 + mod(i, 1000), + md5(i::text) +FROM generate_series(1,30000) s(i); +VACUUM ANALYZE; +-- simple query, no join is selective enough for a filter +EXPLAIN (ANALYZE, TIMING OFF, SUMMARY OFF, BUFFERS OFF) +SELECT * +FROM bloom_multi_fact f +JOIN bloom_multi_dim d ON (f.id1 = d.id1 AND f.id2 = d.id2); + QUERY PLAN +---------------------------------------------------------------------------------------------------------------- + Hash Join (cost=31.00..769.50 rows=30000 width=53) (actual rows=30000.00 loops=1) + Hash Cond: ((f.id1 = d.id1) AND (f.id2 = d.id2)) + -> Seq Scan on bloom_multi_fact f (cost=0.00..581.00 rows=30000 width=41) (actual rows=30000.00 loops=1) + -> Hash (cost=16.00..16.00 rows=1000 width=12) (actual rows=1000.00 loops=1) + Buckets: 1024 Batches: 1 Memory Usage: 51kB + -> Seq Scan on bloom_multi_dim d (cost=0.00..16.00 rows=1000 width=12) (actual rows=1000.00 loops=1) +(6 rows) + +-- join is 75% selective (not enough for a filter to be created) +EXPLAIN (ANALYZE, TIMING OFF, SUMMARY OFF, BUFFERS OFF) +SELECT * +FROM bloom_multi_fact f +JOIN bloom_multi_dim d ON (f.id1 = d.id1 AND f.id2 = d.id2) +WHERE d.r < 0.75; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------- + Hash Join (cost=29.73..768.24 rows=22470 width=53) (actual rows=22470.00 loops=1) + Hash Cond: ((f.id1 = d.id1) AND (f.id2 = d.id2)) + -> Seq Scan on bloom_multi_fact f (cost=0.00..581.00 rows=30000 width=41) (actual rows=30000.00 loops=1) + -> Hash (cost=18.50..18.50 rows=749 width=12) (actual rows=749.00 loops=1) + Buckets: 1024 Batches: 1 Memory Usage: 41kB + -> Seq Scan on bloom_multi_dim d (cost=0.00..18.50 rows=749 width=12) (actual rows=749.00 loops=1) + Filter: (r < '0.75'::double precision) + Rows Removed by Filter: 251 +(8 rows) + +-- join is 50% selective (enough for a filter to be pushed down) +EXPLAIN (ANALYZE, TIMING OFF, SUMMARY OFF, BUFFERS OFF) +SELECT * +FROM bloom_multi_fact f +JOIN bloom_multi_dim d ON (f.id1 = d.id1 AND f.id2 = d.id2) +WHERE d.r < 0.5; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------- + Hash Join (cost=25.98..685.58 rows=14970 width=53) (actual rows=14970.00 loops=1) + Hash Cond: ((f.id1 = d.id1) AND (f.id2 = d.id2)) + -> Seq Scan on bloom_multi_fact f (cost=0.00..581.00 rows=14970 width=41) (actual rows=14970.00 loops=1) + Bloom Filter 1: keys=(id1, id2) expected=49.9% checked=29999 rejected=15030 (50.1%) + -> Hash (cost=18.50..18.50 rows=499 width=12) (actual rows=499.00 loops=1) + Buckets: 1024 Batches: 1 Memory Usage: 30kB + Bloom Filter 1: bits=8388608 hashes=10 memory=1024kB checked=29999 rejected=15030 + -> Seq Scan on bloom_multi_dim d (cost=0.00..18.50 rows=499 width=12) (actual rows=499.00 loops=1) + Filter: (r < '0.5'::double precision) + Rows Removed by Filter: 501 +(10 rows) + +DROP TABLE bloom_multi_fact; +DROP TABLE bloom_multi_dim; diff --git a/src/test/regress/sql/hashjoin_bloom.sql b/src/test/regress/sql/hashjoin_bloom.sql index f0a2a9cc496..a91e88dcf8a 100644 --- a/src/test/regress/sql/hashjoin_bloom.sql +++ b/src/test/regress/sql/hashjoin_bloom.sql @@ -38,3 +38,41 @@ WHERE d.r < 0.5; DROP TABLE bloom_simple_dim; DROP TABLE bloom_simple_fact; + +-- a schema with multi-column foreign key, to test estimates +CREATE TABLE bloom_multi_dim (id1 int, id2 int, r real, primary key (id1, id2)); +CREATE TABLE bloom_multi_fact (id1 int, id2 int, padding text, foreign key (id1, id2) references bloom_multi_dim(id1,id2)); + +INSERT INTO bloom_multi_dim SELECT i, i, (i / 1000.0) FROM generate_series(1,1000) s(i); + +INSERT INTO bloom_multi_fact +SELECT + 1 + mod(i, 1000), + 1 + mod(i, 1000), + md5(i::text) +FROM generate_series(1,30000) s(i); + +VACUUM ANALYZE; + +-- simple query, no join is selective enough for a filter +EXPLAIN (ANALYZE, TIMING OFF, SUMMARY OFF, BUFFERS OFF) +SELECT * +FROM bloom_multi_fact f +JOIN bloom_multi_dim d ON (f.id1 = d.id1 AND f.id2 = d.id2); + +-- join is 75% selective (not enough for a filter to be created) +EXPLAIN (ANALYZE, TIMING OFF, SUMMARY OFF, BUFFERS OFF) +SELECT * +FROM bloom_multi_fact f +JOIN bloom_multi_dim d ON (f.id1 = d.id1 AND f.id2 = d.id2) +WHERE d.r < 0.75; + +-- join is 50% selective (enough for a filter to be pushed down) +EXPLAIN (ANALYZE, TIMING OFF, SUMMARY OFF, BUFFERS OFF) +SELECT * +FROM bloom_multi_fact f +JOIN bloom_multi_dim d ON (f.id1 = d.id1 AND f.id2 = d.id2) +WHERE d.r < 0.5; + +DROP TABLE bloom_multi_fact; +DROP TABLE bloom_multi_dim; -- 2.50.1 (Apple Git-155)