From 37cd2559966e72cc587397bb4680088c75242a5b Mon Sep 17 00:00:00 2001 From: Zhang Mingli Date: Thu, 23 May 2024 15:17:02 +0800 Subject: [PATCH] [AQUMV] Support LIMIT/OFFSET/FETCH clause on origin query. If origin query has LIMIT clause, we could process it on view query. We could process FETCH too as ORDER BY is supported on origin query. create incremental materialized view mv as select c1 as mc1, c2 as mc2, c3 as mc3, c4 as mc4 from t1 where c1 > 90; Origin querys: select c2 from t1 where c1 > 90 limit 3 offset 4; select c2 from t1 where c1 > 90 order by c3, c4 fetch first 3 rows with ties; Could be rewritten to: select mc2 from mv order by mc1 limit 3 offset 4; select mc2 from mv order by mc3, mc4 fetch first 3 rows with ties; Authored-by: Zhang Mingli avamingli@gmail.com --- src/backend/optimizer/plan/aqumv.c | 13 +- src/test/regress/expected/aqumv.out | 253 ++++++++++++++++++++++++++++ src/test/regress/sql/aqumv.sql | 53 ++++++ 3 files changed, 318 insertions(+), 1 deletion(-) diff --git a/src/backend/optimizer/plan/aqumv.c b/src/backend/optimizer/plan/aqumv.c index cefd868771e..238e719d7c1 100644 --- a/src/backend/optimizer/plan/aqumv.c +++ b/src/backend/optimizer/plan/aqumv.c @@ -209,6 +209,7 @@ answer_query_using_materialized_views(PlannerInfo *root, viewQuery->hasDistinctOn || viewQuery->hasModifyingCTE || viewQuery->hasSubLinks || + (limit_needed(viewQuery)) || (viewQuery->groupClause != NIL) || /* IVM doesn't support belows now, just in case. */ (viewQuery->rowMarks != NIL) || @@ -261,6 +262,14 @@ answer_query_using_materialized_views(PlannerInfo *root, subroot->aggtransinfos = NIL; subroot->parse = viewQuery; + /* + * AQUMV_FIXME: + * We copy from root currently, but it's not true + * if we support LIMIT node on view query. + */ + subroot->tuple_fraction = root->tuple_fraction; + subroot->limit_tuples = root->limit_tuples; + /* * AQUMV * We have to rewrite now before we do the real Equivalent @@ -345,6 +354,9 @@ answer_query_using_materialized_views(PlannerInfo *root, viewQuery->groupingSets = parse->groupingSets; viewQuery->sortClause = parse->sortClause; viewQuery->distinctClause = parse->distinctClause; + viewQuery->limitOption = parse->limitOption; + viewQuery->limitCount = parse->limitCount; + viewQuery->limitOffset = parse->limitOffset; /* * AQUMV @@ -398,7 +410,6 @@ answer_query_using_materialized_views(PlannerInfo *root, * We don't use STD_FUZZ_FACTOR for cost comparisons like compare_path_costs_fuzzily here. * The STD_FUZZ_FACTOR is used to reduce paths of a rel, and keep the significantly ones. * But in AQUMV, we always have only one best path of rel at the last to compare. - * TODO: limit clause and startup_cost. */ if (mv_final_rel->cheapest_total_path->total_cost < current_rel->cheapest_total_path->total_cost) { diff --git a/src/test/regress/expected/aqumv.out b/src/test/regress/expected/aqumv.out index 0694dbcae97..80ec2f2507c 100644 --- a/src/test/regress/expected/aqumv.out +++ b/src/test/regress/expected/aqumv.out @@ -2163,6 +2163,259 @@ select distinct on(c1 - 1) c1, c2 from aqumv_t6 where c1 > 90 order by c1 - 1, c (10 rows) \pset null '' +abort; +-- Test LIMIT +begin; +create table aqumv_t7(c1 int, c2 int, c3 int, c4 int) distributed by (c1); +insert into aqumv_t7 select i, i+1, i+2, i+3 from generate_series(1, 100) i; +insert into aqumv_t7 select i, i+1, i+2, i+3 from generate_series(1, 100) i; +analyze aqumv_t7; +create incremental materialized view aqumv_mvt7_0 as + select c3 as cm3, c1 as mc1, c2 as mc2 + from aqumv_t7 where c1 > 90; +analyze aqumv_mvt7_0; +-- LIMIT +set local enable_answer_query_using_materialized_views = off; +explain(costs off, verbose) +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 limit 3; + QUERY PLAN +----------------------------------------------------------------------------------- + Limit + Output: c2, c3 + -> Gather Motion 3:1 (slice1; segments: 3) + Output: c2, c3 + Merge Key: c2, c3 + -> Limit + Output: c2, c3 + -> Sort + Output: c2, c3 + Sort Key: aqumv_t7.c2, aqumv_t7.c3 + -> Seq Scan on public.aqumv_t7 + Output: c2, c3 + Filter: (aqumv_t7.c1 > 90) + Settings: enable_answer_query_using_materialized_views = 'off', optimizer = 'off' + Optimizer: Postgres query optimizer +(15 rows) + +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 limit 3; + c2 | c3 +----+---- + 92 | 93 + 92 | 93 + 93 | 94 +(3 rows) + +set local enable_answer_query_using_materialized_views = on; +explain(costs off, verbose) +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 limit 3; + QUERY PLAN +---------------------------------------------------------------------------------- + Limit + Output: mc2, cm3 + -> Gather Motion 3:1 (slice1; segments: 3) + Output: mc2, cm3 + Merge Key: mc2, cm3 + -> Limit + Output: mc2, cm3 + -> Sort + Output: mc2, cm3 + Sort Key: aqumv_mvt7_0.mc2, aqumv_mvt7_0.cm3 + -> Seq Scan on public.aqumv_mvt7_0 + Output: mc2, cm3 + Settings: enable_answer_query_using_materialized_views = 'on', optimizer = 'off' + Optimizer: Postgres query optimizer +(14 rows) + +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 limit 3; + c2 | c3 +----+---- + 92 | 93 + 92 | 93 + 93 | 94 +(3 rows) + +-- OFFSET +set local enable_answer_query_using_materialized_views = off; +explain(costs off, verbose) +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 limit 3 offset 4; + QUERY PLAN +----------------------------------------------------------------------------------- + Limit + Output: c2, c3 + -> Gather Motion 3:1 (slice1; segments: 3) + Output: c2, c3 + Merge Key: c2, c3 + -> Limit + Output: c2, c3 + -> Sort + Output: c2, c3 + Sort Key: aqumv_t7.c2, aqumv_t7.c3 + -> Seq Scan on public.aqumv_t7 + Output: c2, c3 + Filter: (aqumv_t7.c1 > 90) + Settings: enable_answer_query_using_materialized_views = 'off', optimizer = 'off' + Optimizer: Postgres query optimizer +(15 rows) + +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 limit 3 offset 4; + c2 | c3 +----+---- + 94 | 95 + 94 | 95 + 95 | 96 +(3 rows) + +set local enable_answer_query_using_materialized_views = on; +explain(costs off, verbose) +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 limit 3 offset 4; + QUERY PLAN +---------------------------------------------------------------------------------- + Limit + Output: mc2, cm3 + -> Gather Motion 3:1 (slice1; segments: 3) + Output: mc2, cm3 + Merge Key: mc2, cm3 + -> Limit + Output: mc2, cm3 + -> Sort + Output: mc2, cm3 + Sort Key: aqumv_mvt7_0.mc2, aqumv_mvt7_0.cm3 + -> Seq Scan on public.aqumv_mvt7_0 + Output: mc2, cm3 + Settings: enable_answer_query_using_materialized_views = 'on', optimizer = 'off' + Optimizer: Postgres query optimizer +(14 rows) + +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 limit 3 offset 4; + c2 | c3 +----+---- + 94 | 95 + 94 | 95 + 95 | 96 +(3 rows) + +-- FETCH +set local enable_answer_query_using_materialized_views = off; +explain(costs off, verbose) +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 fetch first 3 rows only; + QUERY PLAN +----------------------------------------------------------------------------------- + Limit + Output: c2, c3 + -> Gather Motion 3:1 (slice1; segments: 3) + Output: c2, c3 + Merge Key: c2, c3 + -> Limit + Output: c2, c3 + -> Sort + Output: c2, c3 + Sort Key: aqumv_t7.c2, aqumv_t7.c3 + -> Seq Scan on public.aqumv_t7 + Output: c2, c3 + Filter: (aqumv_t7.c1 > 90) + Settings: enable_answer_query_using_materialized_views = 'off', optimizer = 'off' + Optimizer: Postgres query optimizer +(15 rows) + +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 fetch first 3 rows only; + c2 | c3 +----+---- + 92 | 93 + 92 | 93 + 93 | 94 +(3 rows) + +set local enable_answer_query_using_materialized_views = on; +explain(costs off, verbose) +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 fetch first 3 rows only; + QUERY PLAN +---------------------------------------------------------------------------------- + Limit + Output: mc2, cm3 + -> Gather Motion 3:1 (slice1; segments: 3) + Output: mc2, cm3 + Merge Key: mc2, cm3 + -> Limit + Output: mc2, cm3 + -> Sort + Output: mc2, cm3 + Sort Key: aqumv_mvt7_0.mc2, aqumv_mvt7_0.cm3 + -> Seq Scan on public.aqumv_mvt7_0 + Output: mc2, cm3 + Settings: enable_answer_query_using_materialized_views = 'on', optimizer = 'off' + Optimizer: Postgres query optimizer +(14 rows) + +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 fetch first 3 rows only; + c2 | c3 +----+---- + 92 | 93 + 92 | 93 + 93 | 94 +(3 rows) + +-- WITH TIES +set local enable_answer_query_using_materialized_views = off; +explain(costs off, verbose) +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 fetch first 3 rows with ties; + QUERY PLAN +----------------------------------------------------------------------------------- + Limit + Output: c2, c3 + -> Gather Motion 3:1 (slice1; segments: 3) + Output: c2, c3 + Merge Key: c2, c3 + -> Limit + Output: c2, c3 + -> Sort + Output: c2, c3 + Sort Key: aqumv_t7.c2, aqumv_t7.c3 + -> Seq Scan on public.aqumv_t7 + Output: c2, c3 + Filter: (aqumv_t7.c1 > 90) + Settings: enable_answer_query_using_materialized_views = 'off', optimizer = 'off' + Optimizer: Postgres query optimizer +(15 rows) + +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 fetch first 3 rows with ties; + c2 | c3 +----+---- + 92 | 93 + 92 | 93 + 93 | 94 + 93 | 94 +(4 rows) + +set local enable_answer_query_using_materialized_views = on; +explain(costs off, verbose) +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 fetch first 3 rows with ties; + QUERY PLAN +---------------------------------------------------------------------------------- + Limit + Output: mc2, cm3 + -> Gather Motion 3:1 (slice1; segments: 3) + Output: mc2, cm3 + Merge Key: mc2, cm3 + -> Limit + Output: mc2, cm3 + -> Sort + Output: mc2, cm3 + Sort Key: aqumv_mvt7_0.mc2, aqumv_mvt7_0.cm3 + -> Seq Scan on public.aqumv_mvt7_0 + Output: mc2, cm3 + Settings: enable_answer_query_using_materialized_views = 'on', optimizer = 'off' + Optimizer: Postgres query optimizer +(14 rows) + +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 fetch first 3 rows with ties; + c2 | c3 +----+---- + 92 | 93 + 92 | 93 + 93 | 94 + 93 | 94 +(4 rows) + abort; reset optimizer; reset enable_answer_query_using_materialized_views; diff --git a/src/test/regress/sql/aqumv.sql b/src/test/regress/sql/aqumv.sql index 86af470541f..3fcb47ed631 100644 --- a/src/test/regress/sql/aqumv.sql +++ b/src/test/regress/sql/aqumv.sql @@ -527,6 +527,59 @@ select distinct on(c1 - 1) c1, c2 from aqumv_t6 where c1 > 90 order by c1 - 1, c \pset null '' abort; +-- Test LIMIT +begin; +create table aqumv_t7(c1 int, c2 int, c3 int, c4 int) distributed by (c1); +insert into aqumv_t7 select i, i+1, i+2, i+3 from generate_series(1, 100) i; +insert into aqumv_t7 select i, i+1, i+2, i+3 from generate_series(1, 100) i; +analyze aqumv_t7; + +create incremental materialized view aqumv_mvt7_0 as + select c3 as cm3, c1 as mc1, c2 as mc2 + from aqumv_t7 where c1 > 90; +analyze aqumv_mvt7_0; + +-- LIMIT +set local enable_answer_query_using_materialized_views = off; +explain(costs off, verbose) +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 limit 3; +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 limit 3; +set local enable_answer_query_using_materialized_views = on; +explain(costs off, verbose) +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 limit 3; +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 limit 3; + +-- OFFSET +set local enable_answer_query_using_materialized_views = off; +explain(costs off, verbose) +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 limit 3 offset 4; +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 limit 3 offset 4; +set local enable_answer_query_using_materialized_views = on; +explain(costs off, verbose) +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 limit 3 offset 4; +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 limit 3 offset 4; + +-- FETCH +set local enable_answer_query_using_materialized_views = off; +explain(costs off, verbose) +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 fetch first 3 rows only; +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 fetch first 3 rows only; +set local enable_answer_query_using_materialized_views = on; +explain(costs off, verbose) +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 fetch first 3 rows only; +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 fetch first 3 rows only; + +-- WITH TIES +set local enable_answer_query_using_materialized_views = off; +explain(costs off, verbose) +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 fetch first 3 rows with ties; +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 fetch first 3 rows with ties; +set local enable_answer_query_using_materialized_views = on; +explain(costs off, verbose) +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 fetch first 3 rows with ties; +select c2, c3 from aqumv_t7 where c1 > 90 order by c2, c3 fetch first 3 rows with ties; + +abort; reset optimizer; reset enable_answer_query_using_materialized_views;