Here is a small patch that reproduces the problem on two tables with
inheritance, and fixes it. I'll add it to the Commitfest.

On Tue, Jan 30, 2024 at 8:20 AM Ashutosh Bapat
<ashutosh.bapat....@gmail.com> wrote:
>
> On Mon, Jan 29, 2024 at 6:11 PM Alexander Kuzmenkov
> <akuzmen...@timescale.com> wrote:
> >
> > Hello hackers,
> >
> > While investigating some query plans, I noticed some code that seems
> > to be wrong: when create_merge_append_path() estimates the cost of
> > sorting an input, it calls cost_sort() passing subpath->parent->tuples
> > as the number of tuples. Shouldn't it use subpath->parent->rows or
> > even subpath->rows instead? The `tuples` variable doesn't account for
> > the filters on the relation, so this leads to incorrect cost estimates
> > when there are highly selective filters, and Sort + Append is chosen
> > instead of MergeAppend.
>
> All other callers of cost_sort() except plan_cluster_use_sort() are
> using rows instead of tuples. Even plan_cluster_use_sort() has
> rel->rows = rel->tuples, it's actually passing rows. So agree with
> your suggestion. However a test will be good since this code is quite
> old.
>
> --
> Best Wishes,
> Ashutosh Bapat
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 8dbf790e89..2e1ec41a54 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -1470,7 +1470,7 @@ create_merge_append_path(PlannerInfo *root,
 					  root,
 					  pathkeys,
 					  subpath->total_cost,
-					  subpath->parent->tuples,
+					  subpath->rows,
 					  subpath->pathtarget->width,
 					  0.0,
 					  work_mem,
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index 56a40d50f9..22e9ca0b10 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -1716,6 +1716,28 @@ order by t1.b limit 10;
 reset enable_nestloop;
 drop table matest0 cascade;
 NOTICE:  drop cascades to table matest1
+-- Check that merge append is chosen in presence of filters.
+create table ma0(a int primary key);
+create table ma1() inherits (ma0);
+insert into ma0 select generate_series(1, 10000);
+insert into ma1 select generate_series(1, 10000);
+analyze ma0;
+analyze ma1;
+explain (costs off) select * from ma0 where a < 1000 order by a;
+                    QUERY PLAN                     
+---------------------------------------------------
+ Merge Append
+   Sort Key: ma0.a
+   ->  Index Only Scan using ma0_pkey on ma0 ma0_1
+         Index Cond: (a < 1000)
+   ->  Sort
+         Sort Key: ma0_2.a
+         ->  Seq Scan on ma1 ma0_2
+               Filter: (a < 1000)
+(8 rows)
+
+drop table ma0 cascade;
+NOTICE:  drop cascades to table ma1
 --
 -- Test merge-append for UNION ALL append relations
 --
diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql
index 971aac54fd..811da462b7 100644
--- a/src/test/regress/sql/inherit.sql
+++ b/src/test/regress/sql/inherit.sql
@@ -624,6 +624,19 @@ reset enable_nestloop;
 
 drop table matest0 cascade;
 
+-- Check that merge append is chosen in presence of filters.
+create table ma0(a int primary key);
+create table ma1() inherits (ma0);
+insert into ma0 select generate_series(1, 10000);
+insert into ma1 select generate_series(1, 10000);
+analyze ma0;
+analyze ma1;
+
+explain (costs off) select * from ma0 where a < 1000 order by a;
+
+drop table ma0 cascade;
+
+
 --
 -- Test merge-append for UNION ALL append relations
 --

Reply via email to