From 700f9822919af87977a3a6f5bee9bef8d79523a8 Mon Sep 17 00:00:00 2001 From: serprex <159546+serprex@users.noreply.github.com> Date: Wed, 1 Jul 2026 04:22:35 +0000 Subject: [PATCH] Disable MinMaxAgg without impacting overall query planner costs --- CHANGELOG.md | 6 +++++- src/fdw.c | 17 ++++++++++++++++- test/expected/binary_queries.out | 6 +++--- test/expected/binary_queries_1.out | 6 +++--- test/expected/binary_queries_2.out | 6 +++--- test/expected/binary_queries_3.out | 6 +++--- test/expected/binary_queries_4.out | 6 +++--- test/expected/binary_queries_5.out | 6 +++--- test/expected/binary_queries_6.out | 6 +++--- test/expected/http.out | 8 ++++---- test/expected/http_1.out | 8 ++++---- test/expected/http_2.out | 8 ++++---- test/expected/http_3.out | 8 ++++---- test/expected/http_4.out | 8 ++++---- test/expected/http_5.out | 8 ++++---- 15 files changed, 66 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 317c3f7d..eae59db5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,8 @@ All notable changes to this project will be documented in this file. It uses the over floats. Aggregates with an internal transition state (anything over `numeric`, `avg(bigint)`, `avg(interval)`) still fall back to local aggregation. Requires `enable_partitionwise_aggregate` ([#298]). +* Disabled remote MIN/MAX optimization by raising `MinMaxAggPath` cost + rather than understating foreign scan cost ([#310]). ### 🐞 Bug Fixes @@ -103,8 +105,10 @@ All notable changes to this project will be documented in this file. It uses the "ClickHouse/pg_clickhouse#303 Flush buffered data during binary insert" [#307]: https://github.com/ClickHouse/pg_clickhouse/pull/307 "ClickHouse/pg_clickhouse#307 Push down three-argument trim functions" - [#307]: https://github.com/ClickHouse/pg_clickhouse/pull/309 + [#309]: https://github.com/ClickHouse/pg_clickhouse/pull/309 "ClickHouse/pg_clickhouse#309 add binary support to clickhouse_raw_query, add clickhouse_query" + [#310]: https://github.com/ClickHouse/pg_clickhouse/pull/310 + "ClickHouse/pg_clickhouse#310 Disable MinMaxAgg without impacting overall query planner costs" ## [v0.3.2] — 2026-06-16 diff --git a/src/fdw.c b/src/fdw.c index 4aa5f0b1..41c31d22 100644 --- a/src/fdw.c +++ b/src/fdw.c @@ -1799,7 +1799,7 @@ estimate_path_cost_size( *p_rows = 1000; *p_width = 32; *p_startup_cost = 1.0; - *p_total_cost = 0.1 + coef; + *p_total_cost = 5.0 + coef; } /* @@ -2927,6 +2927,7 @@ add_foreign_grouping_paths( CHFdwRelationInfo* ifpinfo = input_rel->fdw_private; CHFdwRelationInfo* fpinfo = grouped_rel->fdw_private; ForeignPath* grouppath; + ListCell* lc; double rows; int width; Cost startup_cost; @@ -3009,6 +3010,20 @@ add_foreign_grouping_paths( ); /* no fdw_private */ #endif + /* + * For simple min/max queries Postgres adds MinMaxAggPath that rewrites as + * ORDER BY col LIMIT 1. LIMIT is invisible to FDW, causing full remote + * scan, so increase the cost to prevent this optimization. + */ + foreach (lc, grouped_rel->pathlist) { + Path* path = (Path*)lfirst(lc); + + if (IsA(path, MinMaxAggPath)) { + path->startup_cost += disable_cost; + path->total_cost += disable_cost; + } + } + /* Add generated path into grouped_rel by add_path(). */ add_path(grouped_rel, (Path*)grouppath); } diff --git a/test/expected/binary_queries.out b/test/expected/binary_queries.out index 7c01892e..404d9f98 100644 --- a/test/expected/binary_queries.out +++ b/test/expected/binary_queries.out @@ -298,9 +298,9 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON EXPLAIN SELECT DISTINCT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON (t1.c1 = t2.c1) order by t1.c1 LIMIT 10; QUERY PLAN ---------------------------------------------------------------- - Limit (cost=1.00..1.05 rows=10 width=32) - -> Unique (cost=1.00..5.60 rows=1000 width=32) - -> Foreign Scan (cost=1.00..0.60 rows=1000 width=32) + Limit (cost=1.00..1.09 rows=10 width=32) + -> Unique (cost=1.00..10.50 rows=1000 width=32) + -> Foreign Scan (cost=1.00..5.50 rows=1000 width=32) Relations: (ft2 t1) LEFT JOIN (ft1 t2) (4 rows) diff --git a/test/expected/binary_queries_1.out b/test/expected/binary_queries_1.out index 7625b293..30838498 100644 --- a/test/expected/binary_queries_1.out +++ b/test/expected/binary_queries_1.out @@ -298,9 +298,9 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON EXPLAIN SELECT DISTINCT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON (t1.c1 = t2.c1) order by t1.c1 LIMIT 10; QUERY PLAN ---------------------------------------------------------------- - Limit (cost=1.00..1.05 rows=10 width=32) - -> Unique (cost=1.00..5.60 rows=1000 width=32) - -> Foreign Scan (cost=1.00..0.60 rows=1000 width=32) + Limit (cost=1.00..1.09 rows=10 width=32) + -> Unique (cost=1.00..10.50 rows=1000 width=32) + -> Foreign Scan (cost=1.00..5.50 rows=1000 width=32) Relations: (ft2 t1) LEFT JOIN (ft1 t2) (4 rows) diff --git a/test/expected/binary_queries_2.out b/test/expected/binary_queries_2.out index 77f1b21e..27a766d0 100644 --- a/test/expected/binary_queries_2.out +++ b/test/expected/binary_queries_2.out @@ -298,9 +298,9 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON EXPLAIN SELECT DISTINCT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON (t1.c1 = t2.c1) order by t1.c1 LIMIT 10; QUERY PLAN ---------------------------------------------------------------- - Limit (cost=1.00..1.05 rows=10 width=8) - -> Unique (cost=1.00..5.60 rows=1000 width=8) - -> Foreign Scan (cost=1.00..0.60 rows=1000 width=32) + Limit (cost=1.00..1.09 rows=10 width=8) + -> Unique (cost=1.00..10.50 rows=1000 width=8) + -> Foreign Scan (cost=1.00..5.50 rows=1000 width=32) Relations: (ft2 t1) LEFT JOIN (ft1 t2) (4 rows) diff --git a/test/expected/binary_queries_3.out b/test/expected/binary_queries_3.out index 67fa4720..24ee0d16 100644 --- a/test/expected/binary_queries_3.out +++ b/test/expected/binary_queries_3.out @@ -298,9 +298,9 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON EXPLAIN SELECT DISTINCT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON (t1.c1 = t2.c1) order by t1.c1 LIMIT 10; QUERY PLAN ---------------------------------------------------------------- - Limit (cost=1.00..1.05 rows=10 width=8) - -> Unique (cost=1.00..5.60 rows=1000 width=8) - -> Foreign Scan (cost=1.00..0.60 rows=1000 width=32) + Limit (cost=1.00..1.09 rows=10 width=8) + -> Unique (cost=1.00..10.50 rows=1000 width=8) + -> Foreign Scan (cost=1.00..5.50 rows=1000 width=32) Relations: (ft2 t1) LEFT JOIN (ft1 t2) (4 rows) diff --git a/test/expected/binary_queries_4.out b/test/expected/binary_queries_4.out index 9cf21c22..14209a63 100644 --- a/test/expected/binary_queries_4.out +++ b/test/expected/binary_queries_4.out @@ -298,9 +298,9 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON EXPLAIN SELECT DISTINCT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON (t1.c1 = t2.c1) order by t1.c1 LIMIT 10; QUERY PLAN ---------------------------------------------------------------- - Limit (cost=1.00..1.05 rows=10 width=8) - -> Unique (cost=1.00..5.60 rows=1000 width=8) - -> Foreign Scan (cost=1.00..0.60 rows=1000 width=32) + Limit (cost=1.00..1.09 rows=10 width=8) + -> Unique (cost=1.00..10.50 rows=1000 width=8) + -> Foreign Scan (cost=1.00..5.50 rows=1000 width=32) Relations: (ft2 t1) LEFT JOIN (ft1 t2) (4 rows) diff --git a/test/expected/binary_queries_5.out b/test/expected/binary_queries_5.out index 320eda17..29a221ae 100644 --- a/test/expected/binary_queries_5.out +++ b/test/expected/binary_queries_5.out @@ -298,9 +298,9 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON EXPLAIN SELECT DISTINCT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON (t1.c1 = t2.c1) order by t1.c1 LIMIT 10; QUERY PLAN ---------------------------------------------------------------- - Limit (cost=1.00..1.05 rows=10 width=32) - -> Unique (cost=1.00..5.60 rows=1000 width=32) - -> Foreign Scan (cost=1.00..0.60 rows=1000 width=32) + Limit (cost=1.00..1.09 rows=10 width=32) + -> Unique (cost=1.00..10.50 rows=1000 width=32) + -> Foreign Scan (cost=1.00..5.50 rows=1000 width=32) Relations: (ft2 t1) LEFT JOIN (ft1 t2) (4 rows) diff --git a/test/expected/binary_queries_6.out b/test/expected/binary_queries_6.out index 9001e4c0..966992b9 100644 --- a/test/expected/binary_queries_6.out +++ b/test/expected/binary_queries_6.out @@ -298,9 +298,9 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON EXPLAIN SELECT DISTINCT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON (t1.c1 = t2.c1) order by t1.c1 LIMIT 10; QUERY PLAN ---------------------------------------------------------------- - Limit (cost=1.00..1.05 rows=10 width=32) - -> Unique (cost=1.00..5.60 rows=1000 width=32) - -> Foreign Scan (cost=1.00..0.60 rows=1000 width=32) + Limit (cost=1.00..1.09 rows=10 width=32) + -> Unique (cost=1.00..10.50 rows=1000 width=32) + -> Foreign Scan (cost=1.00..5.50 rows=1000 width=32) Relations: (ft2 t1) LEFT JOIN (ft1 t2) (4 rows) diff --git a/test/expected/http.out b/test/expected/http.out index 52011dff..b9fa0ae5 100644 --- a/test/expected/http.out +++ b/test/expected/http.out @@ -195,7 +195,7 @@ INSERT INTO ftcopy VALUES EXPLAIN (VERBOSE) SELECT * FROM ftcopy ORDER BY c1; QUERY PLAN --------------------------------------------------------------------------------------------- - Foreign Scan on public.ftcopy (cost=1.00..0.60 rows=1000 width=64) + Foreign Scan on public.ftcopy (cost=1.00..5.50 rows=1000 width=64) Output: c1, c2, c3, c4, c5, c6 Remote SQL: SELECT c1, c2, c3, c4, c5, c6 FROM http_test.tcopy ORDER BY c1 ASC NULLS LAST (3 rows) @@ -408,9 +408,9 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON EXPLAIN SELECT DISTINCT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON (t1.c1 = t2.c1) order by t1.c1 LIMIT 10; QUERY PLAN ---------------------------------------------------------------- - Limit (cost=1.00..1.05 rows=10 width=32) - -> Unique (cost=1.00..5.60 rows=1000 width=32) - -> Foreign Scan (cost=1.00..0.60 rows=1000 width=32) + Limit (cost=1.00..1.09 rows=10 width=32) + -> Unique (cost=1.00..10.50 rows=1000 width=32) + -> Foreign Scan (cost=1.00..5.50 rows=1000 width=32) Relations: (ft2 t1) LEFT JOIN (ft1 t2) (4 rows) diff --git a/test/expected/http_1.out b/test/expected/http_1.out index aaf9b041..e1bbd23a 100644 --- a/test/expected/http_1.out +++ b/test/expected/http_1.out @@ -195,7 +195,7 @@ INSERT INTO ftcopy VALUES EXPLAIN (VERBOSE) SELECT * FROM ftcopy ORDER BY c1; QUERY PLAN --------------------------------------------------------------------------------------------- - Foreign Scan on public.ftcopy (cost=1.00..0.60 rows=1000 width=64) + Foreign Scan on public.ftcopy (cost=1.00..5.50 rows=1000 width=64) Output: c1, c2, c3, c4, c5, c6 Remote SQL: SELECT c1, c2, c3, c4, c5, c6 FROM http_test.tcopy ORDER BY c1 ASC NULLS LAST (3 rows) @@ -408,9 +408,9 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON EXPLAIN SELECT DISTINCT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON (t1.c1 = t2.c1) order by t1.c1 LIMIT 10; QUERY PLAN ---------------------------------------------------------------- - Limit (cost=1.00..1.05 rows=10 width=32) - -> Unique (cost=1.00..5.60 rows=1000 width=32) - -> Foreign Scan (cost=1.00..0.60 rows=1000 width=32) + Limit (cost=1.00..1.09 rows=10 width=32) + -> Unique (cost=1.00..10.50 rows=1000 width=32) + -> Foreign Scan (cost=1.00..5.50 rows=1000 width=32) Relations: (ft2 t1) LEFT JOIN (ft1 t2) (4 rows) diff --git a/test/expected/http_2.out b/test/expected/http_2.out index 5fc6512b..565f86c0 100644 --- a/test/expected/http_2.out +++ b/test/expected/http_2.out @@ -195,7 +195,7 @@ INSERT INTO ftcopy VALUES EXPLAIN (VERBOSE) SELECT * FROM ftcopy ORDER BY c1; QUERY PLAN --------------------------------------------------------------------------------------------- - Foreign Scan on public.ftcopy (cost=1.00..0.60 rows=1000 width=64) + Foreign Scan on public.ftcopy (cost=1.00..5.50 rows=1000 width=64) Output: c1, c2, c3, c4, c5, c6 Remote SQL: SELECT c1, c2, c3, c4, c5, c6 FROM http_test.tcopy ORDER BY c1 ASC NULLS LAST (3 rows) @@ -408,9 +408,9 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON EXPLAIN SELECT DISTINCT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON (t1.c1 = t2.c1) order by t1.c1 LIMIT 10; QUERY PLAN ---------------------------------------------------------------- - Limit (cost=1.00..1.05 rows=10 width=8) - -> Unique (cost=1.00..5.60 rows=1000 width=8) - -> Foreign Scan (cost=1.00..0.60 rows=1000 width=32) + Limit (cost=1.00..1.09 rows=10 width=8) + -> Unique (cost=1.00..10.50 rows=1000 width=8) + -> Foreign Scan (cost=1.00..5.50 rows=1000 width=32) Relations: (ft2 t1) LEFT JOIN (ft1 t2) (4 rows) diff --git a/test/expected/http_3.out b/test/expected/http_3.out index f9f94b5c..2015a2e8 100644 --- a/test/expected/http_3.out +++ b/test/expected/http_3.out @@ -195,7 +195,7 @@ INSERT INTO ftcopy VALUES EXPLAIN (VERBOSE) SELECT * FROM ftcopy ORDER BY c1; QUERY PLAN --------------------------------------------------------------------------------------------- - Foreign Scan on public.ftcopy (cost=1.00..0.60 rows=1000 width=64) + Foreign Scan on public.ftcopy (cost=1.00..5.50 rows=1000 width=64) Output: c1, c2, c3, c4, c5, c6 Remote SQL: SELECT c1, c2, c3, c4, c5, c6 FROM http_test.tcopy ORDER BY c1 ASC NULLS LAST (3 rows) @@ -408,9 +408,9 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON EXPLAIN SELECT DISTINCT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON (t1.c1 = t2.c1) order by t1.c1 LIMIT 10; QUERY PLAN ---------------------------------------------------------------- - Limit (cost=1.00..1.05 rows=10 width=8) - -> Unique (cost=1.00..5.60 rows=1000 width=8) - -> Foreign Scan (cost=1.00..0.60 rows=1000 width=32) + Limit (cost=1.00..1.09 rows=10 width=8) + -> Unique (cost=1.00..10.50 rows=1000 width=8) + -> Foreign Scan (cost=1.00..5.50 rows=1000 width=32) Relations: (ft2 t1) LEFT JOIN (ft1 t2) (4 rows) diff --git a/test/expected/http_4.out b/test/expected/http_4.out index 1db6ab59..72abef05 100644 --- a/test/expected/http_4.out +++ b/test/expected/http_4.out @@ -195,7 +195,7 @@ INSERT INTO ftcopy VALUES EXPLAIN (VERBOSE) SELECT * FROM ftcopy ORDER BY c1; QUERY PLAN --------------------------------------------------------------------------------------------- - Foreign Scan on public.ftcopy (cost=1.00..0.60 rows=1000 width=64) + Foreign Scan on public.ftcopy (cost=1.00..5.50 rows=1000 width=64) Output: c1, c2, c3, c4, c5, c6 Remote SQL: SELECT c1, c2, c3, c4, c5, c6 FROM http_test.tcopy ORDER BY c1 ASC NULLS LAST (3 rows) @@ -408,9 +408,9 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON EXPLAIN SELECT DISTINCT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON (t1.c1 = t2.c1) order by t1.c1 LIMIT 10; QUERY PLAN ---------------------------------------------------------------- - Limit (cost=1.00..1.05 rows=10 width=8) - -> Unique (cost=1.00..5.60 rows=1000 width=8) - -> Foreign Scan (cost=1.00..0.60 rows=1000 width=32) + Limit (cost=1.00..1.09 rows=10 width=8) + -> Unique (cost=1.00..10.50 rows=1000 width=8) + -> Foreign Scan (cost=1.00..5.50 rows=1000 width=32) Relations: (ft2 t1) LEFT JOIN (ft1 t2) (4 rows) diff --git a/test/expected/http_5.out b/test/expected/http_5.out index c25ec915..edeff178 100644 --- a/test/expected/http_5.out +++ b/test/expected/http_5.out @@ -195,7 +195,7 @@ INSERT INTO ftcopy VALUES EXPLAIN (VERBOSE) SELECT * FROM ftcopy ORDER BY c1; QUERY PLAN --------------------------------------------------------------------------------------------- - Foreign Scan on public.ftcopy (cost=1.00..0.60 rows=1000 width=64) + Foreign Scan on public.ftcopy (cost=1.00..5.50 rows=1000 width=64) Output: c1, c2, c3, c4, c5, c6 Remote SQL: SELECT c1, c2, c3, c4, c5, c6 FROM http_test.tcopy ORDER BY c1 ASC NULLS LAST (3 rows) @@ -408,9 +408,9 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON EXPLAIN SELECT DISTINCT t1.c1, t2.c1 FROM ft2 t1 LEFT JOIN ft1 t2 ON (t1.c1 = t2.c1) order by t1.c1 LIMIT 10; QUERY PLAN ---------------------------------------------------------------- - Limit (cost=1.00..1.05 rows=10 width=32) - -> Unique (cost=1.00..5.60 rows=1000 width=32) - -> Foreign Scan (cost=1.00..0.60 rows=1000 width=32) + Limit (cost=1.00..1.09 rows=10 width=32) + -> Unique (cost=1.00..10.50 rows=1000 width=32) + -> Foreign Scan (cost=1.00..5.50 rows=1000 width=32) Relations: (ft2 t1) LEFT JOIN (ft1 t2) (4 rows)