Skip to main content

vortex_array/expr/analysis/
infallible.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use crate::expr::Expression;
5use crate::expr::analysis::BooleanLabels;
6use crate::expr::label_tree;
7
8/// Label each expression with whether its entire subtree is infallible.
9///
10/// A subtree is infallible only when the node's scalar function and every child subtree are
11/// infallible. See [`crate::scalar_fn::ScalarFnVTable::is_infallible`] for the scalar-function
12/// contract.
13pub fn label_infallible(expr: &Expression) -> BooleanLabels<'_> {
14    label_tree(
15        expr,
16        |expr| match expr {
17            Expression::Scalar { scalar_fn, .. } => scalar_fn.signature().is_infallible(),
18            // The scope itself cannot fail.
19            Expression::Root => true,
20        },
21        |acc, &child| acc & child,
22    )
23}
24
25#[cfg(test)]
26mod tests {
27    use super::*;
28    use crate::expr::checked_add;
29    use crate::expr::col;
30    use crate::expr::eq;
31    use crate::expr::is_null;
32    use crate::expr::lit;
33    use crate::expr::merge_opts;
34    use crate::expr::not;
35    use crate::scalar_fn::fns::merge::DuplicateHandling;
36
37    #[test]
38    fn not_is_infallible() {
39        let expr = not(col("x"));
40        let labels = label_infallible(&expr);
41        assert_eq!(labels.get(&expr), Some(&true));
42    }
43
44    #[test]
45    fn checked_add_defaults_to_fallible() {
46        let expr = checked_add(col("a"), col("b"));
47        let labels = label_infallible(&expr);
48        assert_eq!(labels.get(&expr), Some(&false));
49    }
50
51    #[test]
52    fn eq_is_infallible() {
53        let expr = eq(col("a"), lit(5));
54        let labels = label_infallible(&expr);
55        assert_eq!(labels.get(&expr), Some(&true));
56    }
57
58    #[test]
59    fn merge_with_error_handling_is_fallible() {
60        let expr = merge_opts([col("a"), col("b")], DuplicateHandling::Error);
61        let labels = label_infallible(&expr);
62        assert_eq!(labels.get(&expr), Some(&false));
63    }
64
65    #[test]
66    fn merge_with_rightmost_handling_is_infallible() {
67        let expr = merge_opts([col("a"), col("b")], DuplicateHandling::RightMost);
68        let labels = label_infallible(&expr);
69        assert_eq!(labels.get(&expr), Some(&true));
70    }
71
72    #[test]
73    fn nested_with_fallible_child() {
74        let child = checked_add(col("a"), col("b"));
75        let expr = not(child.clone());
76        let labels = label_infallible(&expr);
77        assert_eq!(labels.get(&child), Some(&false));
78        assert_eq!(labels.get(&expr), Some(&false));
79    }
80
81    #[test]
82    fn nested_without_fallible_child() {
83        let child = is_null(col("x"));
84        let expr = not(child.clone());
85        let labels = label_infallible(&expr);
86        assert_eq!(labels.get(&child), Some(&true));
87        assert_eq!(labels.get(&expr), Some(&true));
88    }
89}