1use darling::FromMeta;
11use proc_macro2::{Ident, TokenStream};
12use quote::quote;
13use syn::spanned::Spanned;
14use syn::{Expr, Lifetime, Lit};
15
16#[derive(Debug, Default, darling::FromMeta)]
18pub(crate) struct Modifiers {
19 is_monotone: Option<Expr>,
22 is_infinity_monotone: Option<Expr>,
26 sqlname: Option<SqlName>,
28 preserves_uniqueness: Option<Expr>,
30 inverse: Option<Expr>,
32 negate: Option<Expr>,
34 is_infix_op: Option<Expr>,
37 output_type: Option<syn::Path>,
39 output_type_expr: Option<Expr>,
41 could_error: Option<Expr>,
44 propagates_nulls: Option<Expr>,
46 introduces_nulls: Option<Expr>,
48 is_associative: Option<Expr>,
50 is_eliminable_cast: Option<Expr>,
52 test: Option<bool>,
54}
55
56#[derive(Debug)]
59enum SqlName {
60 Literal(syn::Lit),
62 Macro(syn::ExprMacro),
64}
65
66impl quote::ToTokens for SqlName {
67 fn to_tokens(&self, tokens: &mut TokenStream) {
68 let name = match self {
69 SqlName::Literal(lit) => quote! { #lit },
70 SqlName::Macro(mac) => quote! { #mac },
71 };
72 tokens.extend(name);
73 }
74}
75
76impl darling::FromMeta for SqlName {
77 fn from_value(value: &Lit) -> darling::Result<Self> {
78 Ok(Self::Literal(value.clone()))
79 }
80 fn from_expr(expr: &Expr) -> darling::Result<Self> {
81 match expr {
82 Expr::Lit(lit) => Self::from_value(&lit.lit),
83 Expr::Macro(mac) => Ok(Self::Macro(mac.clone())),
84 Expr::Group(mac) => Self::from_expr(&mac.expr),
87 _ => Err(darling::Error::unexpected_expr_type(expr)),
88 }
89 }
90}
91
92pub fn sqlfunc(
98 attr: TokenStream,
99 item: TokenStream,
100 include_test: bool,
101) -> darling::Result<TokenStream> {
102 let mut attr_args = darling::ast::NestedMeta::parse_meta_list(attr.clone())?;
103
104 let struct_ty = match attr_args.first() {
106 Some(darling::ast::NestedMeta::Meta(syn::Meta::Path(_))) => {
107 let darling::ast::NestedMeta::Meta(syn::Meta::Path(path)) = attr_args.remove(0) else {
108 unreachable!()
109 };
110 Some(path)
111 }
112 _ => None,
113 };
114
115 let modifiers = Modifiers::from_list(&attr_args).unwrap();
116 let generate_tests = modifiers.test.unwrap_or(false);
117 let func = syn::parse2::<syn::ItemFn>(item.clone())?;
118
119 let tokens = match determine_arity(&func) {
120 Arity::Nullary => Err(darling::Error::custom("Nullary functions not supported")),
121 Arity::Unary { arena: false } => unary_func(&func, modifiers),
122 Arity::Unary { arena: true } => Err(darling::Error::custom(
123 "Unary functions do not yet support RowArena.",
124 )),
125 Arity::Binary { arena } => binary_func(&func, modifiers, arena),
126 Arity::Variadic { arena, has_self } => {
127 variadic_func(&func, modifiers, struct_ty, arena, has_self)
128 }
129 }?;
130
131 let test = (generate_tests && include_test).then(|| generate_test(attr, item, &func.sig.ident));
132
133 Ok(quote! {
134 #tokens
135 #test
136 })
137}
138
139#[cfg(any(feature = "test", test))]
140fn generate_test(attr: TokenStream, item: TokenStream, name: &Ident) -> TokenStream {
141 let attr = attr.to_string();
142 let item = item.to_string();
143 let test_name = Ident::new(&format!("test_{}", name), name.span());
144 let fn_name = name.to_string();
145
146 quote! {
147 #[cfg(test)]
148 #[cfg_attr(miri, ignore)] #[mz_ore::test]
150 fn #test_name() {
151 let (output, input) = mz_expr_derive_impl::test_sqlfunc_str(#attr, #item);
152 insta::assert_snapshot!(#fn_name, output, &input);
153 }
154 }
155}
156
157#[cfg(not(any(feature = "test", test)))]
158fn generate_test(_attr: TokenStream, _item: TokenStream, _name: &Ident) -> TokenStream {
159 quote! {}
160}
161
162fn last_is_arena(func: &syn::ItemFn) -> bool {
164 func.sig.inputs.last().map_or(false, |last| {
165 if let syn::FnArg::Typed(pat) = last {
166 if let syn::Type::Reference(reference) = &*pat.ty {
167 if let syn::Type::Path(path) = &*reference.elem {
168 return path.path.is_ident("RowArena");
169 }
170 }
171 }
172 false
173 })
174}
175
176enum Arity {
178 Nullary,
179 Unary { arena: bool },
180 Binary { arena: bool },
181 Variadic { arena: bool, has_self: bool },
182}
183
184fn is_variadic_arg(arg: &syn::FnArg) -> bool {
188 if let syn::FnArg::Typed(pat) = arg {
189 if let syn::Type::Path(path) = &*pat.ty {
190 if let Some(segment) = path.path.segments.last() {
191 let ident = segment.ident.to_string();
192 return ident == "Variadic" || ident == "OptionalArg";
193 }
194 }
195 }
196 false
197}
198
199fn determine_arity(func: &syn::ItemFn) -> Arity {
205 let arena = last_is_arena(func);
206 let has_self = matches!(func.sig.inputs.first(), Some(syn::FnArg::Receiver(_)));
207
208 let mut effective_count = func.sig.inputs.len();
209 if arena {
210 effective_count -= 1;
211 }
212 if has_self {
213 effective_count -= 1;
214 }
215
216 let start = if has_self { 1 } else { 0 };
218 let end = if arena {
219 func.sig.inputs.len() - 1
220 } else {
221 func.sig.inputs.len()
222 };
223 let has_variadic_param = func
224 .sig
225 .inputs
226 .iter()
227 .skip(start)
228 .take(end - start)
229 .any(is_variadic_arg);
230
231 if has_variadic_param || effective_count >= 3 {
232 Arity::Variadic { arena, has_self }
233 } else {
234 match effective_count {
235 0 => Arity::Nullary,
236 1 => Arity::Unary { arena },
237 2 => Arity::Binary { arena },
238 _ => unreachable!(),
239 }
240 }
241}
242
243fn is_nullable_type(ty: &syn::Type) -> bool {
250 if let syn::Type::Path(type_path) = ty {
251 if let Some(last_segment) = type_path.path.segments.last() {
252 let ident = &last_segment.ident;
253 if ident == "Option" || ident == "Datum" {
254 return true;
255 }
256 if ident == "OptionalArg" {
257 if let syn::PathArguments::AngleBracketed(args) = &last_segment.arguments {
259 if let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() {
260 return is_nullable_type(inner_ty);
261 }
262 }
263 return false;
264 }
265 }
266 }
267 false
268}
269
270fn is_variadic_type(ty: &syn::Type) -> bool {
272 if let syn::Type::Path(type_path) = ty {
273 if let Some(last_segment) = type_path.path.segments.last() {
274 return last_segment.ident == "Variadic";
275 }
276 }
277 false
278}
279
280fn variadic_element_is_nullable(ty: &syn::Type) -> bool {
282 if let syn::Type::Path(type_path) = ty {
283 if let Some(last_segment) = type_path.path.segments.last() {
284 if let syn::PathArguments::AngleBracketed(args) = &last_segment.arguments {
285 if let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() {
286 return is_nullable_type(inner_ty);
287 }
288 }
289 }
290 }
291 false
292}
293
294fn non_nullable_position_checks(param_types: &[syn::Type]) -> Vec<TokenStream> {
300 let mut checks = Vec::new();
301 for (i, ty) in param_types.iter().enumerate() {
302 if is_variadic_type(ty) {
303 if !variadic_element_is_nullable(ty) {
304 checks.push(quote! { || input_types.iter().skip(#i).any(|t| t.nullable) });
305 }
306 } else if !is_nullable_type(ty) {
307 checks.push(quote! { || input_types.get(#i).map_or(false, |t| t.nullable) });
308 }
309 }
310 checks
311}
312
313fn camel_case(ident: &Ident) -> Ident {
314 let mut result = String::new();
315 let mut capitalize_next = true;
316 for c in ident.to_string().chars() {
317 if c == '_' {
318 capitalize_next = true;
319 } else if capitalize_next {
320 result.push(c.to_ascii_uppercase());
321 capitalize_next = false;
322 } else {
323 result.push(c);
324 }
325 }
326 Ident::new(&result, ident.span())
327}
328
329fn find_generic_type_params(func: &syn::ItemFn) -> Vec<Ident> {
332 func.sig
333 .generics
334 .params
335 .iter()
336 .filter_map(|p| {
337 if let syn::GenericParam::Type(tp) = p {
338 Some(tp.ident.clone())
339 } else {
340 None
341 }
342 })
343 .collect()
344}
345
346#[derive(Debug, Clone)]
348enum GenericUsage {
349 Absent,
351 Bare,
353 InContainer(syn::TypePath),
356}
357
358impl PartialEq for GenericUsage {
359 fn eq(&self, other: &Self) -> bool {
360 match (self, other) {
361 (GenericUsage::Absent, GenericUsage::Absent) => true,
362 (GenericUsage::Bare, GenericUsage::Bare) => true,
363 (GenericUsage::InContainer(a), GenericUsage::InContainer(b)) => {
364 container_idents_match(a, b)
365 }
366 _ => false,
367 }
368 }
369}
370
371impl Eq for GenericUsage {}
372
373fn container_idents_match(a: &syn::TypePath, b: &syn::TypePath) -> bool {
379 let a_idents: Vec<_> = a.path.segments.iter().map(|s| &s.ident).collect();
380 let b_idents: Vec<_> = b.path.segments.iter().map(|s| &s.ident).collect();
381 a_idents == b_idents
382}
383
384fn classify_generic_usage(ty: &syn::Type, generic_name: &Ident) -> GenericUsage {
391 match ty {
392 syn::Type::Path(type_path) => {
393 if type_path.path.is_ident(generic_name) {
394 return GenericUsage::Bare;
395 }
396 if let Some(last) = type_path.path.segments.last() {
397 let ident_str = last.ident.to_string();
398 if ident_str == "Option" || ident_str == "Result" || ident_str == "ExcludeNull" {
400 if let syn::PathArguments::AngleBracketed(args) = &last.arguments {
401 if let Some(syn::GenericArgument::Type(inner)) = args.args.first() {
402 return classify_generic_usage(inner, generic_name);
403 }
404 }
405 }
406 if let syn::PathArguments::AngleBracketed(args) = &last.arguments {
409 let has_generic_arg = args.args.iter().any(|arg| {
410 if let syn::GenericArgument::Type(inner) = arg {
411 type_contains_ident(inner, generic_name)
412 } else {
413 false
414 }
415 });
416 if has_generic_arg {
417 let erased = erase_generic_param(ty, generic_name);
419 if let syn::Type::Path(erased_path) = erased {
420 return GenericUsage::InContainer(erased_path);
421 }
422 }
423 for arg in &args.args {
427 if let syn::GenericArgument::Type(inner) = arg {
428 let inner_usage = classify_generic_usage(inner, generic_name);
429 if inner_usage != GenericUsage::Absent {
430 return inner_usage;
431 }
432 }
433 }
434 }
435 }
436 GenericUsage::Absent
437 }
438 syn::Type::Reference(r) => classify_generic_usage(&r.elem, generic_name),
439 syn::Type::Tuple(t) => {
440 let mut best = GenericUsage::Absent;
443 for elem in &t.elems {
444 let usage = classify_generic_usage(elem, generic_name);
445 match (&best, &usage) {
446 (GenericUsage::Absent, _) => best = usage,
447 (GenericUsage::Bare, u) if *u != GenericUsage::Absent => best = usage.clone(),
448 _ => {
449 if usage != GenericUsage::Absent && usage != best {
450 return GenericUsage::Bare;
452 }
453 }
454 }
455 }
456 best
457 }
458 _ => GenericUsage::Absent,
459 }
460}
461
462fn type_contains_ident(ty: &syn::Type, ident: &Ident) -> bool {
464 match ty {
465 syn::Type::Path(type_path) => {
466 if type_path.path.is_ident(ident) {
467 return true;
468 }
469 if let Some(last) = type_path.path.segments.last() {
470 if let syn::PathArguments::AngleBracketed(args) = &last.arguments {
471 return args.args.iter().any(|arg| {
472 if let syn::GenericArgument::Type(inner) = arg {
473 type_contains_ident(inner, ident)
474 } else {
475 false
476 }
477 });
478 }
479 }
480 false
481 }
482 syn::Type::Reference(r) => type_contains_ident(&r.elem, ident),
483 syn::Type::Tuple(t) => t.elems.iter().any(|e| type_contains_ident(e, ident)),
484 _ => false,
485 }
486}
487
488fn is_option_wrapped(ty: &syn::Type) -> bool {
490 if let syn::Type::Path(type_path) = ty {
491 if let Some(last) = type_path.path.segments.last() {
492 return last.ident == "Option";
493 }
494 }
495 false
496}
497
498fn derive_output_type_for_generics(
510 input_types: &[syn::Type],
511 output_ty: &syn::Type,
512 generic_names: &[Ident],
513 is_unary: bool,
514) -> darling::Result<Option<TokenStream>> {
515 let generic_name = match generic_names
517 .iter()
518 .find(|gn| classify_generic_usage(output_ty, gn) != GenericUsage::Absent)
519 {
520 Some(gn) => gn,
521 None => return Ok(None),
522 };
523 derive_output_type_for_generic(input_types, output_ty, generic_name, is_unary)
524}
525
526fn derive_output_type_for_generic(
532 input_types: &[syn::Type],
533 output_ty: &syn::Type,
534 generic_name: &Ident,
535 is_unary: bool,
536) -> darling::Result<Option<TokenStream>> {
537 let output_usage = classify_generic_usage(output_ty, generic_name);
538 if output_usage == GenericUsage::Absent {
539 return Ok(None);
540 }
541
542 let nullable = is_option_wrapped(output_ty);
543
544 let mut container_input: Option<(usize, GenericUsage)> = None;
547 for (i, ty) in input_types.iter().enumerate() {
548 let usage = classify_generic_usage(ty, generic_name);
549 match &usage {
550 GenericUsage::InContainer(_) => {
551 container_input = Some((i, usage));
552 break;
553 }
554 GenericUsage::Bare => {
555 if container_input.is_none() {
557 container_input = Some((i, usage));
558 }
559 }
560 GenericUsage::Absent => {}
561 }
562 }
563
564 let (pos, source_usage) = container_input.ok_or_else(|| {
565 darling::Error::custom(
566 "generic parameter T appears in the output type but not in any input type",
567 )
568 })?;
569
570 let input_access = if is_unary {
572 quote! { input_type }
573 } else {
574 let pos_lit = syn::Index::from(pos);
575 quote! { input_types[#pos_lit] }
576 };
577
578 let consistency_checks = if !is_unary {
582 let mut checks = Vec::new();
583 for (i, ty) in input_types.iter().enumerate() {
584 if i == pos {
585 continue;
586 }
587 let usage = classify_generic_usage(ty, generic_name);
588 if usage == GenericUsage::Absent {
589 continue;
590 }
591 let primary_elem = element_type_expr(&input_access, &source_usage);
592 let i_lit = syn::Index::from(i);
593 let other_access = quote! { input_types[#i_lit] };
594 let other_elem = element_type_expr(&other_access, &usage);
595 let generic_str = generic_name.to_string();
596 checks.push(quote! {
597 mz_ore::soft_assert_or_log!(
598 #primary_elem.base_eq(#other_elem),
599 "auto-derived sqlfunc output type inference found inconsistent \
600 SQL types for generic {} across inputs: {:?} vs {:?}; \
601 this indicates a bug in polymorphic coercion, builtin \
602 declaration, or sqlfunc inference",
603 #generic_str,
604 #primary_elem,
605 #other_elem,
606 );
607 });
608 }
609 quote! { #(#checks)* }
610 } else {
611 quote! {}
612 };
613
614 let expr = match (&output_usage, &source_usage) {
617 (GenericUsage::Bare, GenericUsage::InContainer(in_container)) => {
619 let in_c = elide_lifetimes(in_container);
620 quote! {
621 {
622 #consistency_checks
623 <#in_c as mz_repr::SqlContainerType>::unwrap_element_type(
624 &#input_access.scalar_type
625 ).clone().nullable(#nullable)
626 }
627 }
628 }
629 (GenericUsage::Bare, GenericUsage::Bare) => {
631 quote! {
632 {
633 #consistency_checks
634 #input_access.scalar_type.clone().nullable(#nullable)
635 }
636 }
637 }
638 (GenericUsage::InContainer(out_container), GenericUsage::InContainer(in_container)) => {
641 let out_c = elide_lifetimes(out_container);
642 let in_c = elide_lifetimes(in_container);
643 quote! {
644 {
645 #consistency_checks
646 <#out_c as mz_repr::SqlContainerType>::wrap_element_type(
647 <#in_c as mz_repr::SqlContainerType>::unwrap_element_type(
648 &#input_access.scalar_type
649 ).clone()
650 ).nullable(#nullable)
651 }
652 }
653 }
654 _ => {
656 return Err(darling::Error::custom(format!(
657 "cannot auto-derive output_type_expr: output uses T as {:?} but \
658 the first T-containing input uses T as {:?}",
659 output_usage, source_usage
660 )));
661 }
662 };
663
664 Ok(Some(expr))
665}
666
667fn element_type_expr(input_access: &TokenStream, usage: &GenericUsage) -> TokenStream {
670 match usage {
671 GenericUsage::Bare => {
672 quote! { &#input_access.scalar_type }
673 }
674 GenericUsage::InContainer(container) => {
675 let c = elide_lifetimes(container);
676 quote! {
677 <#c as mz_repr::SqlContainerType>::unwrap_element_type(
678 &#input_access.scalar_type
679 )
680 }
681 }
682 GenericUsage::Absent => unreachable!("element_type_expr called with Absent usage"),
683 }
684}
685
686fn elide_lifetimes(tp: &syn::TypePath) -> syn::TypePath {
693 let mut tp = tp.clone();
694 for segment in &mut tp.path.segments {
695 if let syn::PathArguments::AngleBracketed(args) = &mut segment.arguments {
696 for arg in &mut args.args {
697 match arg {
698 syn::GenericArgument::Lifetime(lt) => {
699 *lt = Lifetime::new("'_", lt.span());
700 }
701 syn::GenericArgument::Type(ty) => {
702 elide_lifetimes_in_type(ty);
703 }
704 _ => {}
705 }
706 }
707 }
708 }
709 tp
710}
711
712fn elide_lifetimes_in_type(ty: &mut syn::Type) {
714 match ty {
715 syn::Type::Path(tp) => {
716 *tp = elide_lifetimes(tp);
717 }
718 syn::Type::Reference(r) => {
719 if let Some(lt) = &mut r.lifetime {
720 *lt = Lifetime::new("'_", lt.span());
721 }
722 elide_lifetimes_in_type(&mut r.elem);
723 }
724 syn::Type::Tuple(t) => {
725 for elem in &mut t.elems {
726 elide_lifetimes_in_type(elem);
727 }
728 }
729 _ => {}
730 }
731}
732
733fn erase_generic_param(ty: &syn::Type, generic_name: &Ident) -> syn::Type {
738 match ty {
739 syn::Type::Path(type_path) => {
740 if type_path.path.is_ident(generic_name) {
741 return syn::parse_quote!(Datum<'a>);
742 }
743 let mut type_path = type_path.clone();
744 for segment in &mut type_path.path.segments {
745 if let syn::PathArguments::AngleBracketed(args) = &mut segment.arguments {
746 for arg in &mut args.args {
747 if let syn::GenericArgument::Type(inner) = arg {
748 *inner = erase_generic_param(inner, generic_name);
749 }
750 }
751 }
752 }
753 syn::Type::Path(type_path)
754 }
755 syn::Type::Reference(r) => {
756 let elem = Box::new(erase_generic_param(&r.elem, generic_name));
757 syn::Type::Reference(syn::TypeReference { elem, ..r.clone() })
758 }
759 syn::Type::Tuple(t) => {
760 let elems = t
761 .elems
762 .iter()
763 .map(|e| erase_generic_param(e, generic_name))
764 .collect();
765 syn::Type::Tuple(syn::TypeTuple { elems, ..t.clone() })
766 }
767 _ => ty.clone(),
768 }
769}
770
771fn erase_all_generic_params(ty: &syn::Type, generic_names: &[Ident]) -> syn::Type {
773 let mut ty = ty.clone();
774 for gn in generic_names {
775 ty = erase_generic_param(&ty, gn);
776 }
777 ty
778}
779
780fn arg_type(arg: &syn::ItemFn, nth: usize) -> Result<syn::Type, syn::Error> {
787 match &arg.sig.inputs[nth] {
788 syn::FnArg::Typed(pat) => {
789 if let syn::Type::Reference(r) = &*pat.ty {
791 if r.lifetime.is_none() {
792 let ty = syn::Type::Reference(syn::TypeReference {
793 lifetime: Some(Lifetime::new("'a", r.span())),
794 ..r.clone()
795 });
796 return Ok(ty);
797 }
798 }
799 Ok((*pat.ty).clone())
800 }
801 syn::FnArg::Receiver(_) => Err(syn::Error::new(
802 arg.sig.inputs[nth].span(),
803 "Unsupported argument type",
804 )),
805 }
806}
807
808fn patch_lifetimes(ty: &syn::Type) -> syn::Type {
811 match ty {
812 syn::Type::Reference(r) => {
813 let elem = Box::new(patch_lifetimes(&r.elem));
814 if r.lifetime.is_none() {
815 syn::Type::Reference(syn::TypeReference {
816 lifetime: Some(Lifetime::new("'a", r.span())),
817 elem,
818 ..r.clone()
819 })
820 } else {
821 syn::Type::Reference(syn::TypeReference { elem, ..r.clone() })
822 }
823 }
824 syn::Type::Tuple(t) => {
825 let elems = t.elems.iter().map(patch_lifetimes).collect();
826 syn::Type::Tuple(syn::TypeTuple { elems, ..t.clone() })
827 }
828 syn::Type::Path(p) => {
829 let mut p = p.clone();
830 for segment in &mut p.path.segments {
831 if let syn::PathArguments::AngleBracketed(args) = &mut segment.arguments {
832 for arg in &mut args.args {
833 if let syn::GenericArgument::Type(ty) = arg {
834 *ty = patch_lifetimes(ty);
835 }
836 }
837 }
838 }
839 syn::Type::Path(p)
840 }
841 _ => ty.clone(),
842 }
843}
844
845fn output_type(arg: &syn::ItemFn) -> Result<&syn::Type, syn::Error> {
848 match &arg.sig.output {
849 syn::ReturnType::Type(_, ty) => Ok(&*ty),
850 syn::ReturnType::Default => Err(syn::Error::new(
851 arg.sig.output.span(),
852 "Function needs to return a value",
853 )),
854 }
855}
856
857fn unary_func(func: &syn::ItemFn, modifiers: Modifiers) -> darling::Result<TokenStream> {
859 let fn_name = &func.sig.ident;
860 let struct_name = camel_case(&func.sig.ident);
861 let input_ty_raw = arg_type(func, 0)?;
862 let output_ty_raw = output_type(func)?;
863 let generic_params = find_generic_type_params(func);
864 let input_ty = erase_all_generic_params(&input_ty_raw, &generic_params);
866 let output_ty = erase_all_generic_params(output_ty_raw, &generic_params);
867 let Modifiers {
868 is_monotone,
869 sqlname,
870 preserves_uniqueness,
871 inverse,
872 is_infix_op,
873 output_type,
874 mut output_type_expr,
875 negate,
876 could_error,
877 propagates_nulls,
878 mut introduces_nulls,
879 is_associative,
880 is_eliminable_cast,
881 is_infinity_monotone: _,
882 test: _,
883 } = modifiers;
884
885 if !generic_params.is_empty() {
889 if output_type_expr.is_none() && output_type.is_none() {
890 if let Some(derived) = derive_output_type_for_generics(
891 &[input_ty_raw],
892 output_ty_raw,
893 &generic_params,
894 true,
895 )? {
896 output_type_expr = Some(syn::parse2(derived)?);
897 if introduces_nulls.is_none() {
898 let nullable = is_option_wrapped(output_ty_raw);
899 introduces_nulls = Some(syn::parse_quote!(#nullable));
900 }
901 }
902 }
903 }
904
905 if is_infix_op.is_some() {
906 return Err(darling::Error::unknown_field(
907 "is_infix_op not supported for unary functions",
908 ));
909 }
910 if output_type.is_some() && output_type_expr.is_some() {
911 return Err(darling::Error::unknown_field(
912 "output_type and output_type_expr cannot be used together",
913 ));
914 }
915 if output_type_expr.is_some() && introduces_nulls.is_none() {
916 return Err(darling::Error::unknown_field(
917 "output_type_expr requires introduces_nulls",
918 ));
919 }
920 if negate.is_some() {
921 return Err(darling::Error::unknown_field(
922 "negate not supported for unary functions",
923 ));
924 }
925 if propagates_nulls.is_some() {
926 return Err(darling::Error::unknown_field(
927 "propagates_nulls not supported for unary functions",
928 ));
929 }
930 if is_associative.is_some() {
931 return Err(darling::Error::unknown_field(
932 "is_associative not supported for unary functions",
933 ));
934 }
935
936 let preserves_uniqueness_fn = preserves_uniqueness.map(|preserves_uniqueness| {
937 quote! {
938 fn preserves_uniqueness(&self) -> bool {
939 #preserves_uniqueness
940 }
941 }
942 });
943
944 let inverse_fn = inverse.as_ref().map(|inverse| {
945 quote! {
946 fn inverse(&self) -> Option<crate::UnaryFunc> {
947 #inverse
948 }
949 }
950 });
951
952 let is_monotone_fn = is_monotone.map(|is_monotone| {
953 quote! {
954 fn is_monotone(&self) -> bool {
955 #is_monotone
956 }
957 }
958 });
959
960 let name = sqlname
961 .as_ref()
962 .map_or_else(|| quote! { stringify!(#fn_name) }, |name| quote! { #name });
963
964 let (mut output_type, mut introduces_nulls_fn) = if let Some(output_type) = output_type {
965 let introduces_nulls_fn = quote! {
966 fn introduces_nulls(&self) -> bool {
967 <#output_type as ::mz_repr::OutputDatumType<'_, ()>>::nullable()
968 }
969 };
970 let output_type = quote! { <#output_type>::as_column_type() };
971 (output_type, Some(introduces_nulls_fn))
972 } else {
973 (quote! { Self::Output::as_column_type() }, None)
974 };
975
976 if let Some(output_type_expr) = output_type_expr {
977 output_type = quote! { #output_type_expr };
978 }
979
980 if let Some(introduces_nulls) = introduces_nulls {
981 introduces_nulls_fn = Some(quote! {
982 fn introduces_nulls(&self) -> bool {
983 #introduces_nulls
984 }
985 });
986 }
987
988 let could_error_fn = could_error.map(|could_error| {
989 quote! {
990 fn could_error(&self) -> bool {
991 #could_error
992 }
993 }
994 });
995
996 let is_eliminable_cast_fn = is_eliminable_cast.map(|is_eliminable_cast| {
997 quote! {
998 fn is_eliminable_cast(&self) -> bool {
999 #is_eliminable_cast
1000 }
1001 }
1002 });
1003
1004 let result = quote! {
1005 #[derive(
1006 Ord, PartialOrd, Clone,
1007 Debug, Eq, PartialEq, serde::Serialize,
1008 serde::Deserialize, Hash, mz_lowertest::MzReflect,
1009 )]
1010 #[cfg_attr(any(test, feature = "proptest"), derive(proptest_derive::Arbitrary))]
1011 pub struct #struct_name;
1012
1013 impl crate::func::EagerUnaryFunc for #struct_name {
1014 type Input<'a> = #input_ty;
1015 type Output<'a> = #output_ty;
1016
1017 fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> {
1018 #fn_name(a)
1019 }
1020
1021 fn output_sql_type(
1022 &self,
1023 input_type: mz_repr::SqlColumnType
1024 ) -> mz_repr::SqlColumnType {
1025 use mz_repr::AsColumnType;
1026 let output = #output_type;
1027 let propagates_nulls = crate::func::EagerUnaryFunc::propagates_nulls(self);
1028 let nullable = output.nullable;
1029 output.nullable(nullable || (propagates_nulls && input_type.nullable))
1032 }
1033
1034 #could_error_fn
1035 #introduces_nulls_fn
1036 #inverse_fn
1037 #is_monotone_fn
1038 #preserves_uniqueness_fn
1039 #is_eliminable_cast_fn
1040 }
1041
1042 impl std::fmt::Display for #struct_name {
1043 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1044 f.write_str(#name)
1045 }
1046 }
1047
1048 #func
1049 };
1050 Ok(result)
1051}
1052
1053fn binary_func(
1055 func: &syn::ItemFn,
1056 modifiers: Modifiers,
1057 arena: bool,
1058) -> darling::Result<TokenStream> {
1059 let fn_name = &func.sig.ident;
1060 let struct_name = camel_case(&func.sig.ident);
1061 let input1_ty_raw = arg_type(func, 0)?;
1062 let input2_ty_raw = arg_type(func, 1)?;
1063 let output_ty_raw = output_type(func)?;
1064 let generic_params = find_generic_type_params(func);
1065 let input1_ty = erase_all_generic_params(&input1_ty_raw, &generic_params);
1067 let input2_ty = erase_all_generic_params(&input2_ty_raw, &generic_params);
1068 let output_ty = erase_all_generic_params(output_ty_raw, &generic_params);
1069
1070 let Modifiers {
1071 is_monotone,
1072 sqlname,
1073 preserves_uniqueness,
1074 inverse,
1075 is_infix_op,
1076 output_type,
1077 mut output_type_expr,
1078 negate,
1079 could_error,
1080 propagates_nulls,
1081 mut introduces_nulls,
1082 is_associative,
1083 is_eliminable_cast,
1084 is_infinity_monotone,
1085 test: _,
1086 } = modifiers;
1087
1088 if !generic_params.is_empty() {
1091 if output_type_expr.is_none() && output_type.is_none() {
1092 if let Some(derived) = derive_output_type_for_generics(
1093 &[input1_ty_raw, input2_ty_raw],
1094 output_ty_raw,
1095 &generic_params,
1096 false,
1097 )? {
1098 output_type_expr = Some(syn::parse2(derived)?);
1099 if introduces_nulls.is_none() {
1100 let nullable = is_option_wrapped(output_ty_raw);
1101 introduces_nulls = Some(syn::parse_quote!(#nullable));
1102 }
1103 }
1104 }
1105 }
1106
1107 if preserves_uniqueness.is_some() {
1108 return Err(darling::Error::unknown_field(
1109 "preserves_uniqueness not supported for binary functions",
1110 ));
1111 }
1112 if inverse.is_some() {
1113 return Err(darling::Error::unknown_field(
1114 "inverse not supported for binary functions",
1115 ));
1116 }
1117 if output_type.is_some() && output_type_expr.is_some() {
1118 return Err(darling::Error::unknown_field(
1119 "output_type and output_type_expr cannot be used together",
1120 ));
1121 }
1122 if output_type_expr.is_some() && introduces_nulls.is_none() {
1123 return Err(darling::Error::unknown_field(
1124 "output_type_expr requires introduces_nulls",
1125 ));
1126 }
1127 if is_associative.is_some() {
1128 return Err(darling::Error::unknown_field(
1129 "is_associative not supported for binary functions",
1130 ));
1131 }
1132 if is_eliminable_cast.is_some() {
1133 return Err(darling::Error::unknown_field(
1134 "is_eliminable_cast not supported for binary functions",
1135 ));
1136 }
1137
1138 let negate_fn = negate.map(|negate| {
1139 quote! {
1140 fn negate(&self) -> Option<crate::BinaryFunc> {
1141 #negate
1142 }
1143 }
1144 });
1145
1146 let is_monotone_fn = is_monotone.map(|is_monotone| {
1147 quote! {
1148 fn is_monotone(&self) -> (bool, bool) {
1149 #is_monotone
1150 }
1151 }
1152 });
1153
1154 let is_infinity_monotone_fn = is_infinity_monotone.map(|is_infinity_monotone| {
1155 quote! {
1156 fn is_infinity_monotone(&self) -> bool {
1157 #is_infinity_monotone
1158 }
1159 }
1160 });
1161
1162 let name = sqlname
1163 .as_ref()
1164 .map_or_else(|| quote! { stringify!(#fn_name) }, |name| quote! { #name });
1165
1166 let (mut output_type, mut introduces_nulls_fn) = if let Some(output_type) = output_type {
1167 let introduces_nulls_fn = quote! {
1168 fn introduces_nulls(&self) -> bool {
1169 <#output_type as ::mz_repr::OutputDatumType<'_, ()>>::nullable()
1170 }
1171 };
1172 let output_type = quote! { <#output_type>::as_column_type() };
1173 (output_type, Some(introduces_nulls_fn))
1174 } else {
1175 (quote! { Self::Output::as_column_type() }, None)
1176 };
1177
1178 if let Some(output_type_expr) = output_type_expr {
1179 output_type = quote! { #output_type_expr };
1180 }
1181
1182 if let Some(introduces_nulls) = introduces_nulls {
1183 introduces_nulls_fn = Some(quote! {
1184 fn introduces_nulls(&self) -> bool {
1185 #introduces_nulls
1186 }
1187 });
1188 }
1189
1190 let arena = if arena {
1191 quote! { , temp_storage }
1192 } else {
1193 quote! {}
1194 };
1195
1196 let could_error_fn = could_error.map(|could_error| {
1197 quote! {
1198 fn could_error(&self) -> bool {
1199 #could_error
1200 }
1201 }
1202 });
1203
1204 let is_infix_op_fn = is_infix_op.map(|is_infix_op| {
1205 quote! {
1206 fn is_infix_op(&self) -> bool {
1207 #is_infix_op
1208 }
1209 }
1210 });
1211
1212 let propagates_nulls_fn = propagates_nulls.map(|propagates_nulls| {
1213 quote! {
1214 fn propagates_nulls(&self) -> bool {
1215 #propagates_nulls
1216 }
1217 }
1218 });
1219
1220 let binary_non_nullable_checks =
1223 non_nullable_position_checks(&[input1_ty.clone(), input2_ty.clone()]);
1224
1225 let result = quote! {
1226 #[derive(
1227 Ord, PartialOrd, Clone,
1228 Debug, Eq, PartialEq, serde::Serialize,
1229 serde::Deserialize, Hash, mz_lowertest::MzReflect,
1230 )]
1231 #[cfg_attr(any(test, feature = "proptest"), derive(proptest_derive::Arbitrary))]
1232 pub struct #struct_name;
1233
1234 impl crate::func::binary::EagerBinaryFunc for #struct_name {
1235 type Input<'a> = (#input1_ty, #input2_ty);
1236 type Output<'a> = #output_ty;
1237
1238 fn call<'a>(
1239 &self,
1240 (a, b): Self::Input<'a>,
1241 temp_storage: &'a mz_repr::RowArena
1242 ) -> Self::Output<'a> {
1243 #fn_name(a, b #arena)
1244 }
1245
1246 fn output_sql_type(
1247 &self,
1248 input_types: &[mz_repr::SqlColumnType],
1249 ) -> mz_repr::SqlColumnType {
1250 use mz_repr::AsColumnType;
1251 let output = #output_type;
1252 let propagates_nulls =
1253 crate::func::binary::EagerBinaryFunc::propagates_nulls(self);
1254 let nullable = output.nullable;
1255 let non_nullable_input_is_nullable =
1262 false #(#binary_non_nullable_checks)*;
1263 let inputs_nullable = input_types.iter().any(|it| it.nullable);
1264 let is_null = nullable
1265 || non_nullable_input_is_nullable
1266 || (propagates_nulls && inputs_nullable);
1267 output.nullable(is_null)
1268 }
1269
1270 #could_error_fn
1271 #introduces_nulls_fn
1272 #is_infix_op_fn
1273 #is_monotone_fn
1274 #is_infinity_monotone_fn
1275 #negate_fn
1276 #propagates_nulls_fn
1277 }
1278
1279 impl std::fmt::Display for #struct_name {
1280 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1281 f.write_str(#name)
1282 }
1283 }
1284
1285 #func
1286
1287 };
1288 Ok(result)
1289}
1290
1291fn variadic_func(
1297 func: &syn::ItemFn,
1298 modifiers: Modifiers,
1299 struct_ty: Option<syn::Path>,
1300 arena: bool,
1301 has_self: bool,
1302) -> darling::Result<TokenStream> {
1303 let fn_name = &func.sig.ident;
1304 let output_ty_raw = output_type(func)?;
1305 let generic_params = find_generic_type_params(func);
1306 let output_ty = erase_all_generic_params(output_ty_raw, &generic_params);
1307 let struct_name = struct_ty
1308 .as_ref()
1309 .and_then(|ty| ty.segments.last())
1310 .map_or_else(|| camel_case(fn_name), |seg| seg.ident.clone());
1311
1312 let Modifiers {
1313 is_monotone,
1314 sqlname,
1315 preserves_uniqueness,
1316 inverse,
1317 is_infix_op,
1318 output_type,
1319 mut output_type_expr,
1320 negate,
1321 could_error,
1322 propagates_nulls,
1323 mut introduces_nulls,
1324 is_associative,
1325 is_eliminable_cast,
1326 is_infinity_monotone: _,
1327 test: _,
1328 } = modifiers;
1329
1330 if preserves_uniqueness.is_some() {
1332 return Err(darling::Error::unknown_field(
1333 "preserves_uniqueness not supported for variadic functions",
1334 ));
1335 }
1336 if inverse.is_some() {
1337 return Err(darling::Error::unknown_field(
1338 "inverse not supported for variadic functions",
1339 ));
1340 }
1341 if negate.is_some() {
1342 return Err(darling::Error::unknown_field(
1343 "negate not supported for variadic functions",
1344 ));
1345 }
1346 if is_eliminable_cast.is_some() {
1347 return Err(darling::Error::unknown_field(
1348 "is_eliminable_cast not supported for variadic functions",
1349 ));
1350 }
1351 if output_type.is_some() && output_type_expr.is_some() {
1352 return Err(darling::Error::unknown_field(
1353 "output_type and output_type_expr cannot be used together",
1354 ));
1355 }
1356 if output_type_expr.is_some() && introduces_nulls.is_none() {
1357 return Err(darling::Error::unknown_field(
1358 "output_type_expr requires introduces_nulls",
1359 ));
1360 }
1361
1362 let start = if has_self { 1 } else { 0 };
1364 let end = if arena {
1365 func.sig.inputs.len() - 1
1366 } else {
1367 func.sig.inputs.len()
1368 };
1369 let input_params: Vec<&syn::FnArg> = func
1370 .sig
1371 .inputs
1372 .iter()
1373 .skip(start)
1374 .take(end - start)
1375 .collect();
1376
1377 if input_params.is_empty() {
1378 return Err(darling::Error::custom(
1379 "variadic function must have at least one input parameter",
1380 ));
1381 }
1382
1383 let mut param_names = Vec::new();
1385 let mut param_types = Vec::new();
1386 for param in &input_params {
1387 match param {
1388 syn::FnArg::Typed(pat) => {
1389 if let syn::Pat::Ident(ident) = &*pat.pat {
1390 param_names.push(ident.ident.clone());
1391 } else {
1392 return Err(
1393 darling::Error::custom("unsupported parameter pattern").with_span(&pat.pat)
1394 );
1395 }
1396 param_types.push(patch_lifetimes(&pat.ty));
1397 }
1398 syn::FnArg::Receiver(_) => {
1399 return Err(darling::Error::custom("unexpected self parameter"));
1400 }
1401 }
1402 }
1403
1404 if !generic_params.is_empty() {
1407 if output_type_expr.is_none() && output_type.is_none() {
1408 if let Some(derived) = derive_output_type_for_generics(
1409 ¶m_types,
1410 output_ty_raw,
1411 &generic_params,
1412 false,
1413 )? {
1414 output_type_expr = Some(syn::parse2(derived)?);
1415 if introduces_nulls.is_none() {
1416 let nullable = is_option_wrapped(output_ty_raw);
1417 introduces_nulls = Some(syn::parse_quote!(#nullable));
1418 }
1419 }
1420 }
1421 }
1422
1423 for ty in &mut param_types {
1425 *ty = erase_all_generic_params(ty, &generic_params);
1426 }
1427
1428 let input_type: syn::Type = if param_types.len() == 1 {
1430 param_types[0].clone()
1431 } else {
1432 syn::parse_quote! { (#(#param_types),*) }
1433 };
1434
1435 let destructure = if param_names.len() == 1 {
1437 let name = ¶m_names[0];
1438 quote! { #name }
1439 } else {
1440 quote! { (#(#param_names),*) }
1441 };
1442
1443 let arena_arg = if arena {
1444 quote! { , temp_storage }
1445 } else {
1446 quote! {}
1447 };
1448
1449 let call_expr = if has_self {
1450 quote! { self.#fn_name(#(#param_names),* #arena_arg) }
1451 } else {
1452 quote! { #fn_name(#(#param_names),* #arena_arg) }
1453 };
1454
1455 let name = sqlname
1457 .as_ref()
1458 .map_or_else(|| quote! { stringify!(#fn_name) }, |name| quote! { #name });
1459
1460 let (mut output_type_code, mut introduces_nulls_fn) = if let Some(output_type) = output_type {
1461 let introduces_nulls_fn = quote! {
1462 fn introduces_nulls(&self) -> bool {
1463 <#output_type as ::mz_repr::OutputDatumType<'_, ()>>::nullable()
1464 }
1465 };
1466 let output_type_code = quote! { <#output_type>::as_column_type() };
1467 (output_type_code, Some(introduces_nulls_fn))
1468 } else {
1469 (quote! { Self::Output::as_column_type() }, None)
1470 };
1471
1472 if let Some(output_type_expr) = output_type_expr {
1473 output_type_code = quote! { #output_type_expr };
1474 }
1475
1476 if let Some(introduces_nulls) = introduces_nulls {
1477 introduces_nulls_fn = Some(quote! {
1478 fn introduces_nulls(&self) -> bool {
1479 #introduces_nulls
1480 }
1481 });
1482 }
1483
1484 let could_error_fn = could_error.map(|could_error| {
1485 quote! {
1486 fn could_error(&self) -> bool {
1487 #could_error
1488 }
1489 }
1490 });
1491
1492 let is_monotone_fn = is_monotone.map(|is_monotone| {
1493 quote! {
1494 fn is_monotone(&self) -> bool {
1495 #is_monotone
1496 }
1497 }
1498 });
1499
1500 let is_associative_fn = is_associative.map(|is_associative| {
1501 quote! {
1502 fn is_associative(&self) -> bool {
1503 #is_associative
1504 }
1505 }
1506 });
1507
1508 let is_infix_op_fn = is_infix_op.map(|is_infix_op| {
1509 quote! {
1510 fn is_infix_op(&self) -> bool {
1511 #is_infix_op
1512 }
1513 }
1514 });
1515
1516 let propagates_nulls_fn = propagates_nulls.map(|propagates_nulls| {
1517 quote! {
1518 fn propagates_nulls(&self) -> bool {
1519 #propagates_nulls
1520 }
1521 }
1522 });
1523
1524 let non_nullable_checks = non_nullable_position_checks(¶m_types);
1527
1528 let trait_impl = quote! {
1529 impl crate::func::variadic::EagerVariadicFunc for #struct_name {
1530 type Input<'a> = #input_type;
1531 type Output<'a> = #output_ty;
1532
1533 fn call<'a>(
1534 &self,
1535 #destructure: Self::Input<'a>,
1536 temp_storage: &'a mz_repr::RowArena,
1537 ) -> Self::Output<'a> {
1538 #call_expr
1539 }
1540
1541 fn output_type(
1542 &self,
1543 input_types: &[mz_repr::SqlColumnType],
1544 ) -> mz_repr::SqlColumnType {
1545 use mz_repr::AsColumnType;
1546 let output = #output_type_code;
1547 let propagates_nulls =
1548 crate::func::variadic::EagerVariadicFunc::propagates_nulls(self);
1549 let nullable = output.nullable;
1550 let non_nullable_input_is_nullable =
1557 false #(#non_nullable_checks)*;
1558 let inputs_nullable = input_types.iter().any(|it| it.nullable);
1559 output.nullable(
1560 nullable
1561 || non_nullable_input_is_nullable
1562 || (propagates_nulls && inputs_nullable)
1563 )
1564 }
1565
1566 #could_error_fn
1567 #introduces_nulls_fn
1568 #is_infix_op_fn
1569 #is_monotone_fn
1570 #is_associative_fn
1571 #propagates_nulls_fn
1572 }
1573 };
1574
1575 let display_impl = quote! {
1576 impl std::fmt::Display for #struct_name {
1577 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1578 f.write_str(#name)
1579 }
1580 }
1581 };
1582
1583 let result = if has_self {
1584 quote! {
1586 impl #struct_name {
1587 #func
1588 }
1589 #trait_impl
1590 #display_impl
1591 }
1592 } else {
1593 quote! {
1595 #[derive(
1596 Ord, PartialOrd, Clone,
1597 Debug, Eq, PartialEq, serde::Serialize,
1598 serde::Deserialize, Hash, mz_lowertest::MzReflect,
1599 )]
1600 #[cfg_attr(any(test, feature = "proptest"), derive(proptest_derive::Arbitrary))]
1601 pub struct #struct_name;
1602
1603 #trait_impl
1604 #display_impl
1605
1606 #func
1607 }
1608 };
1609
1610 Ok(result)
1611}