From 955294a26d92449ede3e3bb720a743696f45f3d8 Mon Sep 17 00:00:00 2001 From: Tomas Vondra Date: Mon, 27 Jul 2026 17:42:09 +0200 Subject: [PATCH v10 16/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 aa967d76063..23be0a2bcc8 100644 --- a/src/test/regress/expected/hashjoin_bloom.out +++ b/src/test/regress/expected/hashjoin_bloom.out @@ -63,3 +63,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.97..685.41 rows=14940 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=14940 width=41) (actual rows=14970.00 loops=1) + Bloom Filter 1: keys=(id1, id2) expected=49.8% checked=29999 rejected=15030 (50.1%) + -> Hash (cost=18.50..18.50 rows=498 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=498 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 47554daec4c..5f8cb7846b6 100644 --- a/src/test/regress/sql/hashjoin_bloom.sql +++ b/src/test/regress/sql/hashjoin_bloom.sql @@ -41,3 +41,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)