risingwave_frontend/optimizer/rule/apply_union_transpose_rule.rs
1// Copyright 2025 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use itertools::Itertools;
16
17use super::prelude::{PlanRef, *};
18use crate::optimizer::plan_node::{LogicalApply, LogicalUnion, PlanTreeNode, PlanTreeNodeBinary};
19
20/// Transpose `LogicalApply` and `LogicalUnion`.
21///
22/// Before:
23///
24/// ```text
25/// LogicalApply
26/// / \
27/// Domain LogicalUnion
28/// / \
29/// T1 T2
30/// ```
31///
32/// After:
33///
34/// ```text
35/// LogicalUnion
36/// / \
37/// LogicalApply LogicalApply
38/// / \ / \
39/// Domain T1 Domain T2
40/// ```
41pub struct ApplyUnionTransposeRule {}
42impl Rule<Logical> for ApplyUnionTransposeRule {
43 fn apply(&self, plan: PlanRef) -> Option<PlanRef> {
44 let apply: &LogicalApply = plan.as_logical_apply()?;
45 if apply.max_one_row() {
46 return None;
47 }
48 let left = apply.left();
49 let right = apply.right();
50 let union: &LogicalUnion = right.as_logical_union()?;
51
52 let new_inputs = union
53 .inputs()
54 .into_iter()
55 .map(|input| apply.clone_with_left_right(left.clone(), input).into())
56 .collect_vec();
57 Some(union.clone_with_inputs(&new_inputs))
58 }
59}
60
61impl ApplyUnionTransposeRule {
62 pub fn create() -> BoxedRule {
63 Box::new(ApplyUnionTransposeRule {})
64 }
65}