Repository: risingwavelabs/risingwave
File: src/frontend/src/optimizer/rule/mv_selection_rule.rs (Rust)

Code before:
// Copyright 2026 RisingWave Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::optimizer::PlanRef;
use crate::optimizer::plan_node::{Logical, LogicalPlanRef, LogicalScan};
use crate::optimizer::rule::{BoxedRule, Rule};

pub struct MvSelectionRule;

impl MvSelectionRule {
    pub fn create() -> BoxedRule<Logical> {
        Box::new(Self)
    }
}

impl Rule<Logical> for MvSelectionRule {
    fn apply(&self, plan: PlanRef<Logical>) -> Option<PlanRef<Logical>> {
        let ctx = plan.ctx();
        for candidate in ctx.batch_mview_candidates().iter() {
            if plan == candidate.plan {
                let output_len = plan.schema().len();
                if output_len > candidate.table.columns().len() {
                    continue;
                }
                let output_col_idx = (0..output_len).collect();
                let scan = LogicalScan::create(candidate.table.clone(), ctx.clone(), None)
                    .clone_with_output_indices(output_col_idx);
                let rewritten: LogicalPlanRef = scan.into();
                return Some(rewritten);
            }
        }
        None
    }

Review comment (bug):
`GenericPlanRef` is imported but not used in this module, which will trigger an unused-import warning (and may fail CI if warnings are denied). Remove the import or use it explicitly if needed.
```suggestion
use crate::optimizer::plan_node::generic::{Agg, PlanAggCall};
```

Code after:
// Copyright 2026 RisingWave Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashMap;

use super::prelude::{PlanRef, *};
use crate::optimizer::optimizer_context::MaterializedViewCandidate;
use crate::optimizer::plan_node::generic::{Agg, GenericPlanRef, PlanAggCall};
use crate::optimizer::plan_node::{
    LogicalAgg, LogicalPlanRef, LogicalProject, LogicalScan, PlanTreeNodeUnary,
};
use crate::optimizer::rule::{BoxedRule, Rule};
use crate::utils::{ColIndexMapping, IndexSet};

pub struct MvSelectionRule;

impl MvSelectionRule {
    pub fn create() -> BoxedRule<Logical> {
        Box::new(Self)
    }
}

impl Rule<Logical> for MvSelectionRule {
    fn apply(&self, plan: PlanRef) -> Option<PlanRef> {
        let ctx = plan.ctx();
        for candidate in ctx.batch_mview_candidates().iter() {
            if let Some(rewritten) = Self::rewrite_with_candidate(&plan, candidate) {
                return Some(rewritten);
            }
        }
        None
    }
}