Skip to main content

mz_expr_derive_impl/
lib.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//! Implementations of proc macros to derive SQL function traits.
11//!
12//! This is separate from the actual proc macro to allow exporting the
13//! function defining the proc macro itself. Proc macro crates cannot
14//! export anything but proc macros.
15
16mod sqlfunc;
17
18pub use sqlfunc::sqlfunc;
19
20/// Non-exported version of `sqlfunc` for testing purposes, accepting proc_macro2 token streams.
21#[cfg(any(feature = "test", test))]
22fn sqlfunc_for_test(
23    attr: proc_macro2::TokenStream,
24    item: proc_macro2::TokenStream,
25) -> darling::Result<proc_macro2::TokenStream> {
26    sqlfunc(attr, item, false)
27}
28
29#[cfg(any(feature = "test", test))]
30pub fn test_sqlfunc(
31    attr: proc_macro2::TokenStream,
32    item: proc_macro2::TokenStream,
33) -> (String, String) {
34    fn rust_fmt(input: &str) -> String {
35        let file = syn::parse_file(input).unwrap();
36        prettyplease::unparse(&file)
37    }
38
39    let input = rust_fmt(&format!("#[sqlfunc({attr})]\n{item}"));
40    let output = rust_fmt(
41        &sqlfunc_for_test(attr, item)
42            .unwrap_or_else(|err| err.write_errors())
43            .to_string(),
44    );
45    (output, input)
46}
47
48#[cfg(any(feature = "test", test))]
49pub fn test_sqlfunc_str(attr: &str, item: &str) -> (String, String) {
50    test_sqlfunc(attr.parse().unwrap(), item.parse().unwrap())
51}
52
53#[cfg(test)]
54mod test {
55    use quote::quote;
56
57    #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri
58    #[mz_ore::test]
59    fn insta_test_add_int16() {
60        let attr = quote! {
61                is_monotone = (true, true),
62                output_type = i16,
63                is_infix_op = true,
64                sqlname = "+",
65                propagates_nulls = true,
66                test = true,
67        };
68        let item = quote! {
69            fn add_int16<'a>(a: Datum<'a>, b: Datum<'a>) -> Result<Datum<'a>, EvalError> {
70                a.unwrap_int16()
71                    .checked_add(b.unwrap_int16())
72                    .ok_or(EvalError::NumericFieldOverflow)
73                    .map(Datum::from)
74            }
75        };
76        let (output, input) = super::test_sqlfunc(attr, item);
77        insta::assert_snapshot!("add_int16", output, &input);
78    }
79
80    #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri
81    #[mz_ore::test]
82    fn insta_test_unary() {
83        let attr = quote! {test = true};
84        let item = quote! {
85            fn unary_fn<'a>(a: Datum<'a>) -> bool {
86                unimplemented!()
87            }
88        };
89        let (output, input) = super::test_sqlfunc(attr, item);
90        insta::assert_snapshot!("unary_fn", output, &input);
91    }
92
93    #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri
94    #[mz_ore::test]
95    fn insta_test_unary_arena() {
96        let attr = quote! {test = true};
97        let item = quote! {
98            fn unary_fn<'a>(a: Datum<'a>, temp_storage: &RowArena) -> bool {
99                unimplemented!()
100            }
101        };
102        let (output, input) = super::test_sqlfunc(attr, item);
103        insta::assert_snapshot!("unary_arena_fn", output, &input);
104    }
105
106    #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri
107    #[mz_ore::test]
108    fn insta_test_unary_ref() {
109        let attr = quote! {test = true};
110        let item = quote! {
111            fn unary_fn<'a>(a: &i16) -> bool {
112                unimplemented!()
113            }
114        };
115        let (output, input) = super::test_sqlfunc(attr, item);
116        insta::assert_snapshot!("unary_ref", output, &input);
117    }
118
119    #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri
120    #[mz_ore::test]
121    fn insta_test_complex_output_type() {
122        let attr = quote! {
123                is_monotone = (true, true),
124                output_type = "Option<bool>",
125                is_infix_op = true,
126                sqlname = "test",
127                propagates_nulls = true,
128                test = true,
129        };
130        let item = quote! {
131            fn complex_output_type_fn<'a>(
132                a: Datum<'a>,
133                b: Datum<'a>,
134            ) -> Result<Datum<'a>, EvalError> {
135                unimplemented!()
136            }
137        };
138        let (output, input) = super::test_sqlfunc(attr, item);
139        insta::assert_snapshot!("complex_type", output, &input);
140    }
141
142    #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri
143    #[mz_ore::test]
144    fn insta_test_binary_arena() {
145        let attr = quote! {test = true};
146        let item = quote! {
147            fn unary_fn<'a>(a: Datum<'a>, b: u16, temp_storage: &RowArena) -> bool {
148                unimplemented!()
149            }
150        };
151        let (output, input) = super::test_sqlfunc(attr, item);
152        insta::assert_snapshot!("binary_arena_fn", output, &input);
153    }
154}