From aff035a77917f9aa80437f013d2edd92e6aeb0ca Mon Sep 17 00:00:00 2001 From: Andrey Sokolov Date: Thu, 22 Jan 2026 17:11:56 +0300 Subject: [PATCH 1/2] Set join_collapse_limit default value to 13 The default value of join_collapse_limit was 20. When this value is set and the query contains about 20 joins (see added test), Postgres query optimizer cannot build a plan during hours and consumes a lot of memory, because the planner checks a lot of possible ways to join the tables. When join_collapse_limit is 13, the query plan is built in reasonable time. Cherry-picked from Apache Cloudberry: https://github.com/apache/cloudberry/commit/b245e4d758a8a1d0c3fb0269b088d51a6ba5e728 --- src/backend/utils/misc/guc.c | 2 +- src/backend/utils/misc/postgresql.conf.sample | 2 +- src/test/regress/expected/bfv_joins.out | 42 +++++++++++++++++++ .../regress/expected/bfv_joins_optimizer.out | 42 +++++++++++++++++++ src/test/regress/sql/bfv_joins.sql | 39 +++++++++++++++++ 5 files changed, 125 insertions(+), 2 deletions(-) diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index ca59816aaba..2af98411408 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -1706,7 +1706,7 @@ static struct config_int ConfigureNamesInt[] = "list of no more than this many items would result.") }, &join_collapse_limit, - 20, 1, INT_MAX, + 13, 1, INT_MAX, NULL, NULL, NULL }, { diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index f1d2e1a5ef8..c0e9d0e3c1c 100755 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -308,7 +308,7 @@ max_prepared_transactions = 250 # can be 0 or more #cursor_tuple_fraction = 0.1 # range 0.0-1.0 #from_collapse_limit = 20 -#join_collapse_limit = 20 # 1 disables collapsing of explicit +#join_collapse_limit = 13 # 1 disables collapsing of explicit # JOIN clauses #gp_segments_for_planner = 0 # if 0, actual number of segments is used diff --git a/src/test/regress/expected/bfv_joins.out b/src/test/regress/expected/bfv_joins.out index ff6143aa29b..b4b58fa3f6c 100644 --- a/src/test/regress/expected/bfv_joins.out +++ b/src/test/regress/expected/bfv_joins.out @@ -4195,6 +4195,48 @@ cross join lateral union select a+1, b from t1 where a+1 < 10) x; ERROR: Passing parameters across motion is not supported. (cdbmutate.c:3701) +-- Check that Postgres Planner can build a plan with 20 joins in reasonable time +do $$ +begin + for i in 1..20 loop + execute 'create table tj' || i || '(id int)'; + end loop; +end +$$; +set optimizer to off; +select trunc(extract(epoch from now())) unix_time1 \gset +select * +from tj1 + join tj2 on tj1.id = tj2.id + join tj3 on tj2.id = tj3.id + join tj4 on tj3.id = tj4.id + join tj5 on tj4.id = tj5.id + join tj6 on tj5.id = tj6.id + join tj7 on tj6.id = tj7.id + join tj8 on tj7.id = tj8.id + join tj9 on tj8.id = tj9.id + join tj10 on tj9.id = tj10.id + join tj11 on tj10.id = tj11.id + join tj12 on tj11.id = tj12.id + join tj13 on tj12.id = tj13.id + join tj14 on tj13.id = tj14.id + join tj15 on tj14.id = tj15.id + join tj16 on tj15.id = tj16.id + join tj17 on tj16.id = tj17.id + join tj18 on tj17.id = tj18.id + join tj19 on tj18.id = tj19.id + join tj20 on tj19.id = tj20.id; + id | id | id | id | id | id | id | id | id | id | id | id | id | id | id | id | id | id | id | id +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+---- +(0 rows) + +select (trunc(extract(epoch from now())) - :unix_time1) < 100 is_ok; + is_ok +------- + t +(1 row) + +reset optimizer; -- Clean up. None of the objects we create are very interesting to keep around. reset search_path; set client_min_messages='warning'; diff --git a/src/test/regress/expected/bfv_joins_optimizer.out b/src/test/regress/expected/bfv_joins_optimizer.out index 1e3b2b9816a..9f76a6551e7 100644 --- a/src/test/regress/expected/bfv_joins_optimizer.out +++ b/src/test/regress/expected/bfv_joins_optimizer.out @@ -4188,6 +4188,48 @@ cross join lateral union select a+1, b from t1 where a+1 < 10) x; ERROR: Passing parameters across motion is not supported. (cdbmutate.c:3701) +-- Check that Postgres Planner can build a plan with 20 joins in reasonable time +do $$ +begin + for i in 1..20 loop + execute 'create table tj' || i || '(id int)'; + end loop; +end +$$; +set optimizer to off; +select trunc(extract(epoch from now())) unix_time1 \gset +select * +from tj1 + join tj2 on tj1.id = tj2.id + join tj3 on tj2.id = tj3.id + join tj4 on tj3.id = tj4.id + join tj5 on tj4.id = tj5.id + join tj6 on tj5.id = tj6.id + join tj7 on tj6.id = tj7.id + join tj8 on tj7.id = tj8.id + join tj9 on tj8.id = tj9.id + join tj10 on tj9.id = tj10.id + join tj11 on tj10.id = tj11.id + join tj12 on tj11.id = tj12.id + join tj13 on tj12.id = tj13.id + join tj14 on tj13.id = tj14.id + join tj15 on tj14.id = tj15.id + join tj16 on tj15.id = tj16.id + join tj17 on tj16.id = tj17.id + join tj18 on tj17.id = tj18.id + join tj19 on tj18.id = tj19.id + join tj20 on tj19.id = tj20.id; + id | id | id | id | id | id | id | id | id | id | id | id | id | id | id | id | id | id | id | id +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+---- +(0 rows) + +select (trunc(extract(epoch from now())) - :unix_time1) < 100 is_ok; + is_ok +------- + t +(1 row) + +reset optimizer; -- Clean up. None of the objects we create are very interesting to keep around. reset search_path; set client_min_messages='warning'; diff --git a/src/test/regress/sql/bfv_joins.sql b/src/test/regress/sql/bfv_joins.sql index ae1b5a1456a..0204308619a 100644 --- a/src/test/regress/sql/bfv_joins.sql +++ b/src/test/regress/sql/bfv_joins.sql @@ -645,6 +645,45 @@ cross join lateral union select a+1, b from t1 where a+1 < 10) x; +-- Check that Postgres Planner can build a plan with 20 joins in reasonable time +do $$ +begin + for i in 1..20 loop + execute 'create table tj' || i || '(id int)'; + end loop; +end +$$; + +set optimizer to off; + +select trunc(extract(epoch from now())) unix_time1 \gset + +select * +from tj1 + join tj2 on tj1.id = tj2.id + join tj3 on tj2.id = tj3.id + join tj4 on tj3.id = tj4.id + join tj5 on tj4.id = tj5.id + join tj6 on tj5.id = tj6.id + join tj7 on tj6.id = tj7.id + join tj8 on tj7.id = tj8.id + join tj9 on tj8.id = tj9.id + join tj10 on tj9.id = tj10.id + join tj11 on tj10.id = tj11.id + join tj12 on tj11.id = tj12.id + join tj13 on tj12.id = tj13.id + join tj14 on tj13.id = tj14.id + join tj15 on tj14.id = tj15.id + join tj16 on tj15.id = tj16.id + join tj17 on tj16.id = tj17.id + join tj18 on tj17.id = tj18.id + join tj19 on tj18.id = tj19.id + join tj20 on tj19.id = tj20.id; + +select (trunc(extract(epoch from now())) - :unix_time1) < 100 is_ok; + +reset optimizer; + -- Clean up. None of the objects we create are very interesting to keep around. reset search_path; set client_min_messages='warning'; From ac1509d053d14d8281a13ce1ccffb0942ab45c5e Mon Sep 17 00:00:00 2001 From: Alena Rybakina Date: Tue, 10 Mar 2026 14:03:48 +0300 Subject: [PATCH 2/2] Suppress DISTRIBUTED BY notices in bfv_joins test --- src/test/regress/expected/bfv_joins.out | 2 +- src/test/regress/expected/bfv_joins_optimizer.out | 2 +- src/test/regress/sql/bfv_joins.sql | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/regress/expected/bfv_joins.out b/src/test/regress/expected/bfv_joins.out index b4b58fa3f6c..1e2ba31f80d 100644 --- a/src/test/regress/expected/bfv_joins.out +++ b/src/test/regress/expected/bfv_joins.out @@ -4199,7 +4199,7 @@ ERROR: Passing parameters across motion is not supported. (cdbmutate.c:3701) do $$ begin for i in 1..20 loop - execute 'create table tj' || i || '(id int)'; + execute 'create table tj' || i || '(id int) distributed by (id)'; end loop; end $$; diff --git a/src/test/regress/expected/bfv_joins_optimizer.out b/src/test/regress/expected/bfv_joins_optimizer.out index 9f76a6551e7..67a29083e42 100644 --- a/src/test/regress/expected/bfv_joins_optimizer.out +++ b/src/test/regress/expected/bfv_joins_optimizer.out @@ -4192,7 +4192,7 @@ ERROR: Passing parameters across motion is not supported. (cdbmutate.c:3701) do $$ begin for i in 1..20 loop - execute 'create table tj' || i || '(id int)'; + execute 'create table tj' || i || '(id int) distributed by (id)'; end loop; end $$; diff --git a/src/test/regress/sql/bfv_joins.sql b/src/test/regress/sql/bfv_joins.sql index 0204308619a..9a7a8c57ac1 100644 --- a/src/test/regress/sql/bfv_joins.sql +++ b/src/test/regress/sql/bfv_joins.sql @@ -649,7 +649,7 @@ cross join lateral do $$ begin for i in 1..20 loop - execute 'create table tj' || i || '(id int)'; + execute 'create table tj' || i || '(id int) distributed by (id)'; end loop; end $$;