From 2e3b23ab5f41a8e68e3556dc01fa8eda1dea4e24 Mon Sep 17 00:00:00 2001 From: Zhang Mingli Date: Tue, 2 Jan 2024 09:57:29 +0800 Subject: [PATCH] [AQUMV] Support HAVING clause in origin query. Support HAVING clause of origin query if it could be computed from materialized views. create incremental materialized view mv as select c1 as mc1, c2 as mc2, c3 as mc3 from t1 where c1 > 90; Origin query: select c1, c3, avg(c2) from t1 where c1 > 90 group by (c1, c3) having avg(c2) > 95; Could be rewritten to: select mc1, mc3, avg(mc2) from mv group by (mc1, mc3) having avg(mc2) > 95; For HAVING quals have aggregations, we process them in Aggrefs of target list. For HAVING quals don't have aggregations, they may be pushed down to jointree's quals and would be processed in post_quals. Authored-by: Zhang Mingli avamingli@gmail.com --- src/backend/optimizer/plan/aqumv.c | 20 ++-- src/test/regress/expected/aqumv.out | 145 ++++++++++++++++++++++++++++ src/test/regress/sql/aqumv.sql | 40 ++++++++ 3 files changed, 199 insertions(+), 6 deletions(-) diff --git a/src/backend/optimizer/plan/aqumv.c b/src/backend/optimizer/plan/aqumv.c index 5ab2e87f126..818bebc89e9 100644 --- a/src/backend/optimizer/plan/aqumv.c +++ b/src/backend/optimizer/plan/aqumv.c @@ -121,7 +121,6 @@ answer_query_using_materialized_views(PlannerInfo *root, (parse->rowMarks != NIL) || parse->hasWindowFuncs || parse->hasDistinctOn || - (parse->havingQual != NULL) || parse->hasModifyingCTE || parse->sortClause || (parse->parentStmtType == PARENTSTMTTYPE_REFRESH_MATVIEW) || @@ -344,13 +343,19 @@ answer_query_using_materialized_views(PlannerInfo *root, continue; /* - * We have successfully processed target list, all columns in Aggrefs could be - * computed from mvQuery. - * It's safe to set hasAggs here. + * We have successfully processed target list, and all columns in Aggrefs + * could be computed from mvQuery. */ mvQuery->hasAggs = parse->hasAggs; mvQuery->groupClause = parse->groupClause; mvQuery->groupingSets = parse->groupingSets; + /* + * For HAVING quals have aggregations, we have already processed them in + * Aggrefs during aqumv_process_targetlist(). + * For HAVING quals don't have aggregations, they may be pushed down to + * jointree's quals and would be processed in post_quals later. + */ + mvQuery->havingQual = parse->havingQual; /* * AQUMV @@ -359,7 +364,6 @@ answer_query_using_materialized_views(PlannerInfo *root, * We assume that the selection predicates of view and query expressions * have been converted into conjunctive normal form(CNF) before we process * them. - * AQUMV_MVP: no having quals now. */ preprocess_qual_conditions(subroot, (Node *) mvQuery->jointree); @@ -388,7 +392,11 @@ answer_query_using_materialized_views(PlannerInfo *root, * NB: Update processed_tlist again in case that tlist has been changed. */ preprocess_targetlist(subroot); - preprocess_aggrefs(subroot, (Node *) subroot->processed_tlist); + if(mvQuery->hasAggs) + { + preprocess_aggrefs(subroot, (Node *) subroot->processed_tlist); + preprocess_aggrefs(subroot, mvQuery->havingQual); + } /* * AQUMV diff --git a/src/test/regress/expected/aqumv.out b/src/test/regress/expected/aqumv.out index 0d7782963bd..0cf24f6fb1b 100644 --- a/src/test/regress/expected/aqumv.out +++ b/src/test/regress/expected/aqumv.out @@ -1433,6 +1433,151 @@ select c1, c3, count(c2) from aqumv_t3 where c1 > 90 group by cube(c1, c3); | 95 | 1 (32 rows) +abort; +-- +-- Test HAVING clause +-- +begin; +create table aqumv_t4(c1 int, c2 int, c3 int) distributed by (c1); +insert into aqumv_t4 select i, i+1, i+2 from generate_series(1, 100) i; +insert into aqumv_t4 values (91, NULL, 95); +analyze aqumv_t4; +create incremental materialized view aqumv_mvt4_0 as + select c1 as mc1, c2 as mc2, c3 as mc3 + from aqumv_t4 where c1 > 90; +analyze aqumv_mvt4_0; +-- HAVING clause pushed down to where quals. +set local enable_answer_query_using_materialized_views = off; +explain(costs off, verbose) +select c1, c3 from aqumv_t4 where c1 > 90 group by (c1, c3) having c3 > 97 ; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: c1, c3 + -> GroupAggregate + Output: c1, c3 + Group Key: aqumv_t4.c1, aqumv_t4.c3 + -> Sort + Output: c1, c3 + Sort Key: aqumv_t4.c1, aqumv_t4.c3 + -> Seq Scan on public.aqumv_t4 + Output: c1, c3 + Filter: ((aqumv_t4.c1 > 90) AND (aqumv_t4.c3 > 97)) + Settings: enable_answer_query_using_materialized_views = 'off', optimizer = 'off' + Optimizer: Postgres query optimizer +(13 rows) + +select c1, c3 from aqumv_t4 where c1 > 90 group by (c1, c3) having c3 > 97 ; + c1 | c3 +-----+----- + 96 | 98 + 100 | 102 + 97 | 99 + 99 | 101 + 98 | 100 +(5 rows) + +set local enable_answer_query_using_materialized_views = on; +explain(costs off, verbose) +select c1, c3 from aqumv_t4 where c1 > 90 group by (c1, c3) having c3 > 97 ; + QUERY PLAN +---------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: mc1, mc3 + -> HashAggregate + Output: mc1, mc3 + Group Key: aqumv_mvt4_0.mc1, aqumv_mvt4_0.mc3 + -> Seq Scan on public.aqumv_mvt4_0 + Output: mc1, mc2, mc3 + Filter: (aqumv_mvt4_0.mc3 > 97) + Settings: enable_answer_query_using_materialized_views = 'on', optimizer = 'off' + Optimizer: Postgres query optimizer +(10 rows) + +select c1, c3 from aqumv_t4 where c1 > 90 group by (c1, c3) having c3 > 97 ; + c1 | c3 +-----+----- + 100 | 102 + 96 | 98 + 98 | 100 + 99 | 101 + 97 | 99 +(5 rows) + +-- quals kept in HAVING clause. +set local enable_answer_query_using_materialized_views = off; +explain(costs off, verbose) +select c1, c3, avg(c2) from aqumv_t4 where c1 > 90 group by (c1, c3) having avg(c2) > 95; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: c1, c3, (avg(c2)) + -> HashAggregate + Output: c1, c3, avg(c2) + Group Key: aqumv_t4.c1, aqumv_t4.c3 + Filter: (avg(aqumv_t4.c2) > '95'::numeric) + -> Seq Scan on public.aqumv_t4 + Output: c1, c2, c3 + Filter: (aqumv_t4.c1 > 90) + Settings: enable_answer_query_using_materialized_views = 'off', optimizer = 'off' + Optimizer: Postgres query optimizer +(11 rows) + +select c1, c3, avg(c2) from aqumv_t4 where c1 > 90 group by (c1, c3) having avg(c2) > 95; + c1 | c3 | avg +-----+-----+---------------------- + 99 | 101 | 100.0000000000000000 + 97 | 99 | 98.0000000000000000 + 98 | 100 | 99.0000000000000000 + 95 | 97 | 96.0000000000000000 + 100 | 102 | 101.0000000000000000 + 96 | 98 | 97.0000000000000000 +(6 rows) + +set local enable_answer_query_using_materialized_views = on; +explain(costs off, verbose) +select c1, c3, avg(c2) from aqumv_t4 where c1 > 90 group by (c1, c3) having avg(c2) > 95; + QUERY PLAN +---------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: mc1, mc3, (avg(mc2)) + -> HashAggregate + Output: mc1, mc3, avg(mc2) + Group Key: aqumv_mvt4_0.mc1, aqumv_mvt4_0.mc3 + Filter: (avg(aqumv_mvt4_0.mc2) > '95'::numeric) + -> Seq Scan on public.aqumv_mvt4_0 + Output: mc1, mc2, mc3 + Settings: enable_answer_query_using_materialized_views = 'on', optimizer = 'off' + Optimizer: Postgres query optimizer +(10 rows) + +select c1, c3, avg(c2) from aqumv_t4 where c1 > 90 group by (c1, c3) having avg(c2) > 95; + c1 | c3 | avg +-----+-----+---------------------- + 98 | 100 | 99.0000000000000000 + 95 | 97 | 96.0000000000000000 + 99 | 101 | 100.0000000000000000 + 97 | 99 | 98.0000000000000000 + 100 | 102 | 101.0000000000000000 + 96 | 98 | 97.0000000000000000 +(6 rows) + +-- duplicated having quals with where quals. +explain(costs off, verbose) +select c1, c3, avg(c2) from aqumv_t4 where c1 > 90 group by (c1, c3) having c1 > 90; + QUERY PLAN +---------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: mc1, mc3, (avg(mc2)) + -> HashAggregate + Output: mc1, mc3, avg(mc2) + Group Key: aqumv_mvt4_0.mc1, aqumv_mvt4_0.mc3 + -> Seq Scan on public.aqumv_mvt4_0 + Output: mc1, mc2, mc3 + Settings: enable_answer_query_using_materialized_views = 'on', optimizer = 'off' + Optimizer: Postgres query optimizer +(9 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 36200ab7ce6..c812727096d 100644 --- a/src/test/regress/sql/aqumv.sql +++ b/src/test/regress/sql/aqumv.sql @@ -369,6 +369,46 @@ select c1, c3, count(c2) from aqumv_t3 where c1 > 90 group by cube(c1, c3); select c1, c3, count(c2) from aqumv_t3 where c1 > 90 group by cube(c1, c3); abort; +-- +-- Test HAVING clause +-- +begin; +create table aqumv_t4(c1 int, c2 int, c3 int) distributed by (c1); +insert into aqumv_t4 select i, i+1, i+2 from generate_series(1, 100) i; +insert into aqumv_t4 values (91, NULL, 95); +analyze aqumv_t4; + +create incremental materialized view aqumv_mvt4_0 as + select c1 as mc1, c2 as mc2, c3 as mc3 + from aqumv_t4 where c1 > 90; +analyze aqumv_mvt4_0; + +-- HAVING clause pushed down to where quals. +set local enable_answer_query_using_materialized_views = off; +explain(costs off, verbose) +select c1, c3 from aqumv_t4 where c1 > 90 group by (c1, c3) having c3 > 97 ; +select c1, c3 from aqumv_t4 where c1 > 90 group by (c1, c3) having c3 > 97 ; +set local enable_answer_query_using_materialized_views = on; +explain(costs off, verbose) +select c1, c3 from aqumv_t4 where c1 > 90 group by (c1, c3) having c3 > 97 ; +select c1, c3 from aqumv_t4 where c1 > 90 group by (c1, c3) having c3 > 97 ; + +-- quals kept in HAVING clause. +set local enable_answer_query_using_materialized_views = off; +explain(costs off, verbose) +select c1, c3, avg(c2) from aqumv_t4 where c1 > 90 group by (c1, c3) having avg(c2) > 95; +select c1, c3, avg(c2) from aqumv_t4 where c1 > 90 group by (c1, c3) having avg(c2) > 95; +set local enable_answer_query_using_materialized_views = on; +explain(costs off, verbose) +select c1, c3, avg(c2) from aqumv_t4 where c1 > 90 group by (c1, c3) having avg(c2) > 95; +select c1, c3, avg(c2) from aqumv_t4 where c1 > 90 group by (c1, c3) having avg(c2) > 95; + +-- duplicated having quals with where quals. +explain(costs off, verbose) +select c1, c3, avg(c2) from aqumv_t4 where c1 > 90 group by (c1, c3) having c1 > 90; + +abort; + reset optimizer; reset enable_answer_query_using_materialized_views; drop table aqumv_t1 cascade;