mz_expr/scalar/func/
macros.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// Convenience macro for generating `inverse` values.
11macro_rules! to_unary {
12    ($f:expr) => {
13        Some(crate::UnaryFunc::from($f))
14    };
15}
16
17#[cfg(test)]
18mod test {
19    use mz_expr_derive::sqlfunc;
20    use mz_repr::SqlScalarType;
21
22    use crate::EvalError;
23    use crate::scalar::func::LazyUnaryFunc;
24
25    #[sqlfunc(sqlname = "INFALLIBLE")]
26    fn infallible1(a: f32) -> f32 {
27        a
28    }
29
30    #[sqlfunc]
31    fn infallible2(a: Option<f32>) -> f32 {
32        a.unwrap_or_default()
33    }
34
35    #[sqlfunc]
36    fn infallible3(a: f32) -> Option<f32> {
37        Some(a)
38    }
39
40    #[mz_ore::test]
41    fn elision_rules_infallible() {
42        assert_eq!(format!("{}", Infallible1), "INFALLIBLE");
43        assert!(Infallible1.propagates_nulls());
44        assert!(!Infallible1.introduces_nulls());
45
46        assert!(!Infallible2.propagates_nulls());
47        assert!(!Infallible2.introduces_nulls());
48
49        assert!(Infallible3.propagates_nulls());
50        assert!(Infallible3.introduces_nulls());
51    }
52
53    #[mz_ore::test]
54    fn output_types_infallible() {
55        assert_eq!(
56            Infallible1.output_type(SqlScalarType::Float32.nullable(true)),
57            SqlScalarType::Float32.nullable(true)
58        );
59        assert_eq!(
60            Infallible1.output_type(SqlScalarType::Float32.nullable(false)),
61            SqlScalarType::Float32.nullable(false)
62        );
63
64        assert_eq!(
65            Infallible2.output_type(SqlScalarType::Float32.nullable(true)),
66            SqlScalarType::Float32.nullable(false)
67        );
68        assert_eq!(
69            Infallible2.output_type(SqlScalarType::Float32.nullable(false)),
70            SqlScalarType::Float32.nullable(false)
71        );
72
73        assert_eq!(
74            Infallible3.output_type(SqlScalarType::Float32.nullable(true)),
75            SqlScalarType::Float32.nullable(true)
76        );
77        assert_eq!(
78            Infallible3.output_type(SqlScalarType::Float32.nullable(false)),
79            SqlScalarType::Float32.nullable(true)
80        );
81    }
82
83    #[sqlfunc]
84    fn fallible1(a: f32) -> Result<f32, EvalError> {
85        Ok(a)
86    }
87
88    #[sqlfunc]
89    fn fallible2(a: Option<f32>) -> Result<f32, EvalError> {
90        Ok(a.unwrap_or_default())
91    }
92
93    #[sqlfunc]
94    fn fallible3(a: f32) -> Result<Option<f32>, EvalError> {
95        Ok(Some(a))
96    }
97
98    #[mz_ore::test]
99    fn elision_rules_fallible() {
100        assert!(Fallible1.propagates_nulls());
101        assert!(!Fallible1.introduces_nulls());
102
103        assert!(!Fallible2.propagates_nulls());
104        assert!(!Fallible2.introduces_nulls());
105
106        assert!(Fallible3.propagates_nulls());
107        assert!(Fallible3.introduces_nulls());
108    }
109
110    #[mz_ore::test]
111    fn output_types_fallible() {
112        assert_eq!(
113            Fallible1.output_type(SqlScalarType::Float32.nullable(true)),
114            SqlScalarType::Float32.nullable(true)
115        );
116        assert_eq!(
117            Fallible1.output_type(SqlScalarType::Float32.nullable(false)),
118            SqlScalarType::Float32.nullable(false)
119        );
120
121        assert_eq!(
122            Fallible2.output_type(SqlScalarType::Float32.nullable(true)),
123            SqlScalarType::Float32.nullable(false)
124        );
125        assert_eq!(
126            Fallible2.output_type(SqlScalarType::Float32.nullable(false)),
127            SqlScalarType::Float32.nullable(false)
128        );
129
130        assert_eq!(
131            Fallible3.output_type(SqlScalarType::Float32.nullable(true)),
132            SqlScalarType::Float32.nullable(true)
133        );
134        assert_eq!(
135            Fallible3.output_type(SqlScalarType::Float32.nullable(false)),
136            SqlScalarType::Float32.nullable(true)
137        );
138    }
139}
140
141/// Temporary macro that generates the equivalent of what enum_dispatch will do in the future. We
142/// need this manual macro implementation to delegate to the previous manual implementation for
143/// variants that use the old definitions.
144///
145/// Once everything is handled by this macro we can remove it and replace it with `enum_dispatch`
146macro_rules! derive_unary {
147    ($($name:ident),*) => {
148        #[derive(Ord, PartialOrd, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Hash, mz_lowertest::MzReflect)]
149        pub enum UnaryFunc {
150            $($name($name),)*
151        }
152
153        impl UnaryFunc {
154            pub fn eval<'a>(
155                &'a self,
156                datums: &[Datum<'a>],
157                temp_storage: &'a RowArena,
158                a: &'a MirScalarExpr,
159            ) -> Result<Datum<'a>, EvalError> {
160                match self {
161                    $(Self::$name(f) => f.eval(datums, temp_storage, a),)*
162                }
163            }
164
165            pub fn output_type(&self, input_type: SqlColumnType) -> SqlColumnType {
166                match self {
167                    $(Self::$name(f) => LazyUnaryFunc::output_type(f, input_type),)*
168                }
169            }
170            pub fn propagates_nulls(&self) -> bool {
171                match self {
172                    $(Self::$name(f) => LazyUnaryFunc::propagates_nulls(f),)*
173                }
174            }
175            pub fn introduces_nulls(&self) -> bool {
176                match self {
177                    $(Self::$name(f) => LazyUnaryFunc::introduces_nulls(f),)*
178                }
179            }
180            pub fn preserves_uniqueness(&self) -> bool {
181                match self {
182                    $(Self::$name(f) => LazyUnaryFunc::preserves_uniqueness(f),)*
183                }
184            }
185            pub fn inverse(&self) -> Option<UnaryFunc> {
186                match self {
187                    $(Self::$name(f) => LazyUnaryFunc::inverse(f),)*
188                }
189            }
190            pub fn is_monotone(&self) -> bool {
191                match self {
192                    $(Self::$name(f) => LazyUnaryFunc::is_monotone(f),)*
193                }
194            }
195            pub fn could_error(&self) -> bool {
196                match self {
197                    $(Self::$name(f) => LazyUnaryFunc::could_error(f),)*
198                }
199            }
200        }
201
202        impl fmt::Display for UnaryFunc {
203            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
204                match self {
205                    $(Self::$name(func) => func.fmt(f),)*
206                }
207            }
208        }
209
210        $(
211            impl From<$name> for crate::UnaryFunc {
212                fn from(variant: $name) -> Self {
213                    Self::$name(variant)
214                }
215            }
216        )*
217    }
218}
219
220/// This is not complete yet, pending conversion of all binary funcs to an implementation of
221/// `LazyBinaryFunc`.
222macro_rules! derive_binary_from {
223    ($($name:ident $( ( $variant:ident ) )?),* $(,)?) => {
224        $(
225            derive_binary_from!(from @ $name $( ( $variant ) )?);
226        )*
227    };
228    (from @ $name:ident ( $variant:ident ) ) => {
229        impl From<$variant> for crate::BinaryFunc {
230            fn from(variant: $variant) -> Self {
231                Self::$name(variant)
232            }
233        }
234    };
235    (from @ $name:ident) => {
236        derive_binary_from!(from @ $name ( $name ) );
237    };
238}