mz_transform/
collect_notices.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! A transform that collects optimizer notices that don't naturally fit into other transforms.
11
12use mz_expr::{BinaryFunc, MirRelationExpr, MirScalarExpr};
13
14use crate::notice::EqualsNull;
15use crate::{TransformCtx, TransformError};
16
17/// A transform that collects optimizer notices that don't naturally fit into other transforms.
18///
19/// This transform scans the plan for patterns that should generate notices,
20/// such as `= NULL` comparisons, which are almost always mistakes.
21#[derive(Debug)]
22pub struct CollectNotices;
23
24impl crate::Transform for CollectNotices {
25    fn name(&self) -> &'static str {
26        "CollectNotices"
27    }
28
29    fn actually_perform_transform(
30        &self,
31        relation: &mut MirRelationExpr,
32        ctx: &mut TransformCtx,
33    ) -> Result<(), TransformError> {
34        let mut found_equals_null = false;
35
36        relation.visit_scalars(&mut |scalar: &MirScalarExpr| {
37            if !found_equals_null {
38                found_equals_null = Self::contains_equals_null(scalar);
39            }
40        });
41
42        if found_equals_null {
43            ctx.df_meta.push_optimizer_notice_dedup(EqualsNull);
44        }
45
46        Ok(())
47    }
48}
49
50impl CollectNotices {
51    /// Checks if the given scalar expression contains an `= NULL` or `<> NULL` comparison.
52    fn contains_equals_null(expr: &MirScalarExpr) -> bool {
53        let mut found = false;
54        expr.visit_pre(|e| {
55            if let MirScalarExpr::CallBinary { func, expr1, expr2 } = e {
56                if matches!(func, BinaryFunc::Eq(_) | BinaryFunc::NotEq(_)) {
57                    if expr1.is_literal_null() || expr2.is_literal_null() {
58                        found = true;
59                    }
60                }
61            }
62        });
63        found
64    }
65}