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        };
67        let item = quote! {
68            fn add_int16<'a>(a: Datum<'a>, b: Datum<'a>) -> Result<Datum<'a>, EvalError> {
69                a.unwrap_int16()
70                    .checked_add(b.unwrap_int16())
71                    .ok_or(EvalError::NumericFieldOverflow)
72                    .map(Datum::from)
73            }
74        };
75        let (output, input) = super::test_sqlfunc(attr, item);
76        insta::assert_snapshot!("add_int16", output, &input);
77    }
78
79    #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri
80    #[mz_ore::test]
81    fn insta_test_unary() {
82        let attr = quote! {};
83        let item = quote! {
84            fn unary_fn<'a>(a: Datum<'a>) -> bool {
85                unimplemented!()
86            }
87        };
88        let (output, input) = super::test_sqlfunc(attr, item);
89        insta::assert_snapshot!("unary_fn", output, &input);
90    }
91
92    #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri
93    #[mz_ore::test]
94    fn insta_test_unary_arena() {
95        let attr = quote! {};
96        let item = quote! {
97            fn unary_fn<'a>(a: Datum<'a>, temp_storage: &RowArena) -> bool {
98                unimplemented!()
99            }
100        };
101        let (output, input) = super::test_sqlfunc(attr, item);
102        insta::assert_snapshot!("unary_arena_fn", output, &input);
103    }
104
105    #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri
106    #[mz_ore::test]
107    fn insta_test_unary_ref() {
108        let attr = quote! {};
109        let item = quote! {
110            fn unary_fn<'a>(a: &i16) -> bool {
111                unimplemented!()
112            }
113        };
114        let (output, input) = super::test_sqlfunc(attr, item);
115        insta::assert_snapshot!("unary_ref", output, &input);
116    }
117
118    #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri
119    #[mz_ore::test]
120    fn insta_test_complex_output_type() {
121        let attr = quote! {
122                is_monotone = (true, true),
123                output_type = "Option<bool>",
124                is_infix_op = true,
125                sqlname = "test",
126                propagates_nulls = true
127        };
128        let item = quote! {
129            fn complex_output_type_fn<'a>(a: Datum<'a>, b: Datum<'a>) -> Result<Datum<'a>, EvalError> {
130                unimplemented!()
131            }
132        };
133        let (output, input) = super::test_sqlfunc(attr, item);
134        insta::assert_snapshot!("complex_type", output, &input);
135    }
136
137    #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri
138    #[mz_ore::test]
139    fn insta_test_binary_arena() {
140        let attr = quote! {};
141        let item = quote! {
142            fn unary_fn<'a>(a: Datum<'a>, b: u16, temp_storage: &RowArena) -> bool {
143                unimplemented!()
144            }
145        };
146        let (output, input) = super::test_sqlfunc(attr, item);
147        insta::assert_snapshot!("binary_arena_fn", output, &input);
148    }
149}