risingwave_frontend/optimizer/rule/except_merge_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 super::prelude::{PlanRef, *};
16use crate::optimizer::plan_node::{LogicalExcept, PlanTreeNode};
17
18/// Different from `UnionMergeRule` and `IntersectMergeRule`, `ExceptMergeRule` can only merge its
19/// left most one input.
20pub struct ExceptMergeRule {}
21impl Rule<Logical> for ExceptMergeRule {
22 fn apply(&self, plan: PlanRef) -> Option<PlanRef> {
23 let top_except: &LogicalExcept = plan.as_logical_except()?;
24 let top_all = top_except.all();
25 let top_except_inputs = top_except.inputs();
26 let (left_most_input, remain_vec) = top_except_inputs.split_at(1);
27
28 if let Some(bottom_except) = left_most_input[0].as_logical_except()
29 && bottom_except.all() == top_all
30 {
31 let mut new_inputs = vec![];
32 new_inputs.extend(bottom_except.inputs());
33 new_inputs.extend(remain_vec.iter().cloned());
34 Some(top_except.clone_with_inputs(&new_inputs))
35 } else {
36 None
37 }
38 }
39}
40
41impl ExceptMergeRule {
42 pub fn create() -> BoxedRule {
43 Box::new(ExceptMergeRule {})
44 }
45}