Repository: risingwavelabs/risingwave
File: src/frontend/src/optimizer/plan_visitor/datafusion_execute_checker.rs (Rust)

Code before:

impl DataFusionExecuteChecker {
    /// Recursively checks all input plans and aggregates their [`CheckResult`]s,
    /// combining `supported` with logical AND and `have_iceberg_scan` with logical OR.
    fn check_inputs(&mut self, plan: &impl PlanTreeNode<Logical>) -> CheckResult {
        plan.inputs()
            .into_iter()
            .map(|input| self.visit(input))
            .fold(
                CheckResult {
                    supported: true,
                    have_iceberg_scan: false,
                },
                CheckResult::merge,
            )
    }
}

impl LogicalPlanVisitor for DataFusionExecuteChecker {
    type Result = CheckResult;

    type DefaultBehavior = impl DefaultBehavior<Self::Result>;

    fn default_behavior() -> Self::DefaultBehavior {
        Merge(CheckResult::merge)
    }

    fn visit_logical_agg(
        &mut self,
        plan: &crate::optimizer::plan_node::LogicalAgg,
    ) -> Self::Result {
        let mut res = self.check_inputs(plan);

        let have_grouping_sets = !plan.grouping_sets().is_empty();
        if have_grouping_sets {
            res.supported = false;
        }
        res
    }

    fn visit_logical_filter(
        &mut self,
        plan: &crate::optimizer::plan_node::LogicalFilter,
    ) -> Self::Result {
        self.check_inputs(plan)
    }

    fn visit_logical_project(
        &mut self,
        plan: &crate::optimizer::plan_node::LogicalProject,
    ) -> Self::Result {

Review comment (suggestion):
`default_behavior()` now uses `Merge(CheckResult::merge)`, which will treat any logical plan node that doesn't have an explicit `visit_logical_*` override as "supported" as long as all its inputs are supported. That makes `CheckResult.supported` unreliable and can cause `try_gen_datafusion_plan` to attempt DataFusion conversion on unsupported nodes (leading to avoidable conversion work + warning logs). Consider using a default behavior that *preserves* `have_iceberg_scan` from inputs but forces `supported = false` for unhandled node types (e.g., a custom `DefaultBehavior` that merges results and then flips `supported` to `false`), keeping the checker conservative while still detecting Iceberg scans through wrapper nodes like `LogicalShare`.

Code after:
                CheckResult {
                    supported: true,
                    have_iceberg_scan: false,
                },
                |mut acc, item| {
                    acc.supported &= item.supported;
                    acc.have_iceberg_scan |= item.have_iceberg_scan;
                    acc
                },
            )
    }
}

impl LogicalPlanVisitor for DataFusionExecuteChecker {
    type DefaultBehavior = DefaultValueBehavior;
    type Result = CheckResult;

    fn default_behavior() -> Self::DefaultBehavior {
        DefaultValueBehavior
    }

    fn visit_logical_agg(
        &mut self,
        plan: &crate::optimizer::plan_node::LogicalAgg,
    ) -> Self::Result {
        let mut res = self.check_inputs(plan);

        let have_grouping_sets = !plan.grouping_sets().is_empty();
        if have_grouping_sets {
            res.supported = false;
        }
        res
    }

    fn visit_logical_filter(
        &mut self,
        plan: &crate::optimizer::plan_node::LogicalFilter,
    ) -> Self::Result {
        self.check_inputs(plan)
    }

    fn visit_logical_project(
        &mut self,
        plan: &crate::optimizer::plan_node::LogicalProject,
    ) -> Self::Result {
        self.check_inputs(plan)
    }

    fn visit_logical_project_set(
        &mut self,
        plan: &crate::optimizer::plan_node::LogicalProjectSet,