123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- # Copyright Materialize, Inc. and contributors. All rights reserved.
- #
- # Use of this software is governed by the Business Source License
- # included in the LICENSE file at the root of this repository.
- #
- # As of the Change Date specified in that file, in accordance with
- # the Business Source License, use of this software will be governed
- # by the Apache License, Version 2.0.
- #
- # Test proper fusion of MFPs in Reduce.
- # PR https://github.com/MaterializeInc/materialize/pull/23197
- #
- mode cockroach
- simple conn=mz_system,user=mz_system
- ALTER SYSTEM SET enable_reduce_mfp_fusion TO true;
- ----
- COMPLETE 0
- statement ok
- CREATE TABLE t (a int, b int);
- statement ok
- INSERT INTO t VALUES (1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (3, 1);
- # Illustrates a special-case MFP where we can completely absorb all
- # components, including a projection.
- statement ok
- CREATE MATERIALIZED VIEW mv_fusable_mfp_accumulable AS
- SELECT a, SUM(b)
- FROM t
- GROUP BY a
- HAVING ((COUNT(b) + 1) - a) > 2 AND (COUNT(b) + 1) / (1 + a) >= 1;
- query II
- SELECT * FROM mv_fusable_mfp_accumulable;
- ----
- 1 6
- query T multiline
- EXPLAIN PHYSICAL PLAN AS VERBOSE TEXT FOR
- MATERIALIZED VIEW mv_fusable_mfp_accumulable;
- ----
- materialize.public.mv_fusable_mfp_accumulable:
- Reduce::Accumulable
- simple_aggrs[0]=(0, 0, sum(#1{b}))
- simple_aggrs[1]=(1, 1, count(#1{b}))
- val_plan
- project=(#1, #1)
- key_plan
- project=(#0)
- mfp_after
- project=(#0, #1)
- filter=(((#3 - integer_to_bigint(#0{a})) > 2) AND ((#3 / integer_to_bigint((1 + #0{a}))) >= 1))
- map=((#2{"?column?"} + 1))
- Get::PassArrangements materialize.public.t
- raw=true
- Source materialize.public.t
- Target cluster: quickstart
- EOF
- # Illustrates a complex MFP scenario with all three components
- # active in the fused MFP.
- statement ok
- CREATE MATERIALIZED VIEW mv_complex_mfp_accumulable AS
- SELECT a, SUM(b) + 1
- FROM t
- GROUP BY a
- HAVING ((COUNT(b) + 1) - a) > 2 AND (COUNT(b) + 1) / (1 + a) >= 1;
- query II
- SELECT * FROM mv_complex_mfp_accumulable;
- ----
- 1 7
- query T multiline
- EXPLAIN PHYSICAL PLAN AS VERBOSE TEXT FOR
- MATERIALIZED VIEW mv_complex_mfp_accumulable;
- ----
- materialize.public.mv_complex_mfp_accumulable:
- Mfp
- project=(#0, #3)
- map=((#1{"?column?"} + 1))
- input_key=#0
- Reduce::Accumulable
- simple_aggrs[0]=(0, 0, sum(#1{b}))
- simple_aggrs[1]=(1, 1, count(#1{b}))
- val_plan
- project=(#1, #1)
- key_plan
- project=(#0)
- mfp_after
- filter=(((#3 - integer_to_bigint(#0{a})) > 2) AND ((#3 / integer_to_bigint((1 + #0{a}))) >= 1))
- map=((#2{"?column?"} + 1))
- Get::PassArrangements materialize.public.t
- raw=true
- Source materialize.public.t
- Target cluster: quickstart
- EOF
- # Check that we treat errors properly.
- statement ok
- INSERT INTO t VALUES (-1, 3);
- query error division by zero
- SELECT * FROM mv_fusable_mfp_accumulable;
- query error division by zero
- SELECT * FROM mv_complex_mfp_accumulable;
- statement ok
- DELETE FROM t WHERE a = -1;
- query II
- SELECT * FROM mv_fusable_mfp_accumulable;
- ----
- 1 6
- query II
- SELECT * FROM mv_complex_mfp_accumulable;
- ----
- 1 7
- # We test variations of the scenario above to cover reduction types.
- statement ok
- CREATE MATERIALIZED VIEW mv_complex_mfp_basic_single AS
- SELECT a, LIST_AGG(b ORDER BY b DESC)::text
- FROM t
- GROUP BY a
- HAVING
- ((LIST_AGG(b ORDER BY b DESC)[1] + 1) - a) > 2 AND
- (LIST_AGG(b ORDER BY b DESC)[1] + 1) / (1 + a) >= 1;
- query IT
- SELECT * FROM mv_complex_mfp_basic_single;
- ----
- 1 {3,2,1}
- statement ok
- INSERT INTO t VALUES (-1, 3);
- query error division by zero
- SELECT * FROM mv_complex_mfp_basic_single;
- statement ok
- DELETE FROM t WHERE a = -1;
- query IT
- SELECT * FROM mv_complex_mfp_basic_single;
- ----
- 1 {3,2,1}
- statement ok
- CREATE MATERIALIZED VIEW mv_complex_mfp_basic_distinct_single AS
- SELECT a, LIST_AGG(DISTINCT b ORDER BY b DESC)::text
- FROM t
- GROUP BY a
- HAVING
- ((LIST_AGG(DISTINCT b ORDER BY b DESC)[1] + 1) - a) > 2 AND
- (LIST_AGG(DISTINCT b ORDER BY b DESC)[1] + 1) / (1 + a) >= 1;
- query IT
- SELECT * FROM mv_complex_mfp_basic_distinct_single;
- ----
- 1 {3,2,1}
- statement ok
- INSERT INTO t VALUES (-1, 3);
- query error division by zero
- SELECT * FROM mv_complex_mfp_basic_distinct_single;
- statement ok
- DELETE FROM t WHERE a = -1;
- query IT
- SELECT * FROM mv_complex_mfp_basic_distinct_single;
- ----
- 1 {3,2,1}
- statement ok
- CREATE MATERIALIZED VIEW mv_complex_mfp_basic_multiple AS
- SELECT a, LIST_AGG(b ORDER BY b DESC)::text
- FROM t
- GROUP BY a
- HAVING
- ((ARRAY_AGG(b ORDER BY b DESC)[1] + 1) - a) > 2 AND
- (ARRAY_AGG(b ORDER BY b DESC)[1] + 1) / (1 + a) >= 1;
- query IT
- SELECT * FROM mv_complex_mfp_basic_multiple;
- ----
- 1 {3,2,1}
- statement ok
- INSERT INTO t VALUES (-1, 3);
- query error division by zero
- SELECT * FROM mv_complex_mfp_basic_multiple;
- statement ok
- DELETE FROM t WHERE a = -1;
- query IT
- SELECT * FROM mv_complex_mfp_basic_multiple;
- ----
- 1 {3,2,1}
- statement ok
- CREATE MATERIALIZED VIEW mv_complex_mfp_bucketed AS
- SELECT a, MAX(b) + 1
- FROM t
- GROUP BY a
- HAVING ((MAX(b) + 1) - a) > 2 AND (MAX(b) + 1) / (1 + a) >= 1;
- query II
- SELECT * FROM mv_complex_mfp_bucketed;
- ----
- 1 4
- # Note we use a one-shot SELECT to cover the monotonic case.
- query II
- SELECT a, MAX(b) + 1
- FROM t
- GROUP BY a
- HAVING ((MAX(b) + 1) - a) > 2 AND (MAX(b) + 1) / (1 + a) >= 1;
- ----
- 1 4
- statement ok
- INSERT INTO t VALUES (-1, 3);
- query error division by zero
- SELECT * FROM mv_complex_mfp_bucketed;
- query error division by zero
- SELECT a, MAX(b) + 1
- FROM t
- GROUP BY a
- HAVING ((MAX(b) + 1) - a) > 2 AND (MAX(b) + 1) / (1 + a) >= 1;
- statement ok
- DELETE FROM t WHERE a = -1;
- query II
- SELECT * FROM mv_complex_mfp_bucketed;
- ----
- 1 4
- query II
- SELECT a, MAX(b) + 1
- FROM t
- GROUP BY a
- HAVING ((MAX(b) + 1) - a) > 2 AND (MAX(b) + 1) / (1 + a) >= 1;
- ----
- 1 4
- statement ok
- CREATE MATERIALIZED VIEW mv_complex_mfp_collation AS
- SELECT a, MAX(b) + 1
- FROM t
- GROUP BY a
- HAVING ((COUNT(b) + 1) - a) > 2 AND (COUNT(b) + 1) / (1 + a) >= 1;
- query II
- SELECT * FROM mv_complex_mfp_collation;
- ----
- 1 4
- statement ok
- INSERT INTO t VALUES (-1, 3);
- query error division by zero
- SELECT * FROM mv_complex_mfp_collation;
- statement ok
- DELETE FROM t WHERE a = -1;
- query II
- SELECT * FROM mv_complex_mfp_collation;
- ----
- 1 4
- # NOTE(vmarcos): Even though placement of an Mfp that has parts that can be
- # fused on top of a Reduce::Distinct is valid LIR, we should not at present produce
- # this pattern via SQL. To see why, note that if the MFP has parts that could be fused,
- # then it must preserve the reduction key, i.e., all columns for the distinct, and
- # the fusable parts need to be: (a) free of temporal filters; (b) only contain map
- # and filter (i.e., essentially be a predicate). So, these fusable parts could just as
- # well be applied to the input to the Reduce::Distinct, and the optimizer will just
- # perform predicate pushdown to filter the input before it gets arranged. Additionally,
- # if it were the case that we would lift the predicate to enable reuse, then that would
- # mean that we could not fuse it into the reduction anyway.
|