1use itertools::Itertools;
18use proc_macro2::{Ident, Span};
19use quote::{format_ident, quote};
20
21use super::*;
22
23impl FunctionAttr {
24 pub fn expand(&self) -> Vec<Self> {
26 if self
28 .args
29 .last()
30 .is_some_and(|arg| arg.starts_with("variadic"))
31 {
32 let mut attrs = Vec::new();
36 attrs.extend(
37 FunctionAttr {
38 args: {
39 let mut args = self.args.clone();
40 *args.last_mut().unwrap() = "...".to_owned();
41 args
42 },
43 ..self.clone()
44 }
45 .expand(),
46 );
47 attrs.extend(
48 FunctionAttr {
49 name: format!("{}_variadic", self.name),
50 args: {
51 let mut args = self.args.clone();
52 let last = args.last_mut().unwrap();
53 *last = last.strip_prefix("variadic ").unwrap().into();
54 args
55 },
56 ..self.clone()
57 }
58 .expand(),
59 );
60 return attrs;
61 }
62 let args = self.args.iter().map(|ty| types::expand_type_wildcard(ty));
63 let ret = types::expand_type_wildcard(&self.ret);
64 let mut attrs = Vec::new();
65 for (args, mut ret) in args.multi_cartesian_product().cartesian_product(ret) {
66 if ret == "auto" {
67 ret = types::min_compatible_type(&args);
68 }
69 let attr = FunctionAttr {
70 args: args.iter().map(|s| s.to_string()).collect(),
71 ret: ret.to_owned(),
72 ..self.clone()
73 };
74 attrs.push(attr);
75 }
76 attrs
77 }
78
79 fn generate_type_infer_fn(&self) -> Result<TokenStream2> {
81 if let Some(func) = &self.type_infer {
82 if func == "unreachable" {
83 return Ok(
84 quote! { |_| unreachable!("type inference for this function should be specially handled in frontend, and should not call sig.type_infer") },
85 );
86 }
87 return Ok(func.parse().unwrap());
89 } else if self.ret == "any" {
90 if let Some(i) = self.args.iter().position(|t| t == "any") {
92 return Ok(quote! { |args| Ok(args[#i].clone()) });
94 }
95 if let Some(i) = self.args.iter().position(|t| t == "anyarray") {
96 return Ok(quote! { |args| Ok(args[#i].as_list_elem().clone()) });
98 }
99 } else if self.ret == "anyarray" {
100 if let Some(i) = self.args.iter().position(|t| t == "anyarray") {
101 return Ok(quote! { |args| Ok(args[#i].clone()) });
103 }
104 if let Some(i) = self.args.iter().position(|t| t == "any") {
105 return Ok(quote! { |args| Ok(DataType::list(args[#i].clone())) });
107 }
108 } else if self.ret == "struct" {
109 if let Some(i) = self.args.iter().position(|t| t == "struct") {
110 return Ok(quote! { |args| Ok(args[#i].clone()) });
112 }
113 } else if self.ret == "anymap" {
114 if let Some(i) = self.args.iter().position(|t| t == "anymap") {
115 return Ok(quote! { |args| Ok(args[#i].clone()) });
117 }
118 } else {
119 let ty = data_type(&self.ret);
121 return Ok(quote! { |_| Ok(#ty) });
122 }
123 Err(Error::new(
124 Span::call_site(),
125 "type inference function cannot be automatically derived. You should provide: `type_infer = \"|args| Ok(...)\"`",
126 ))
127 }
128
129 pub fn generate_function_descriptor(
137 &self,
138 user_fn: &UserFunctionAttr,
139 build_fn: bool,
140 ) -> Result<TokenStream2> {
141 if self.is_table_function {
142 return self.generate_table_function_descriptor(user_fn, build_fn);
143 }
144 let name = self.name.clone();
145 let variadic = matches!(self.args.last(), Some(t) if t == "...");
146 let args = match variadic {
147 true => &self.args[..self.args.len() - 1],
148 false => &self.args[..],
149 }
150 .iter()
151 .map(|ty| sig_data_type(ty))
152 .collect_vec();
153 let ret = sig_data_type(&self.ret);
154
155 let pb_type = format_ident!("{}", utils::to_camel_case(&name));
156 let ctor_name = format_ident!("{}", self.ident_name());
157 let build_fn = if build_fn {
158 let name = format_ident!("{}", user_fn.name);
159 quote! { #name }
160 } else if self.rewritten {
161 quote! { |_, _| Err(ExprError::UnsupportedFunction(#name.into())) }
162 } else {
163 self.generate_build_scalar_function(user_fn, true)?
165 };
166 let type_infer_fn = self.generate_type_infer_fn()?;
167 let deprecated = self.deprecated;
168 let maybe_allow_deprecated = if deprecated {
169 quote! { #[allow(deprecated)] }
170 } else {
171 quote! {}
172 };
173
174 Ok(quote! {
175 #[risingwave_expr::codegen::linkme::distributed_slice(risingwave_expr::sig::FUNCTIONS)]
176 fn #ctor_name() -> risingwave_expr::sig::FuncSign {
177 use risingwave_common::types::{DataType, DataTypeName};
178 use risingwave_expr::sig::{FuncSign, SigDataType, FuncBuilder};
179
180 #maybe_allow_deprecated
181 FuncSign {
182 name: risingwave_pb::expr::expr_node::Type::#pb_type.into(),
183 inputs_type: vec![#(#args),*],
184 variadic: #variadic,
185 ret_type: #ret,
186 build: FuncBuilder::Scalar(#build_fn),
187 type_infer: #type_infer_fn,
188 deprecated: #deprecated,
189 }
190 }
191 })
192 }
193
194 fn generate_build_scalar_function(
199 &self,
200 user_fn: &UserFunctionAttr,
201 optimize_const: bool,
202 ) -> Result<TokenStream2> {
203 let variadic = matches!(self.args.last(), Some(t) if t == "...");
204 let num_args = self.args.len() - if variadic { 1 } else { 0 };
205 let fn_name = format_ident!("{}", user_fn.name);
206 let struct_name = match optimize_const {
207 true => format_ident!("{}OptimizeConst", utils::to_camel_case(&self.ident_name())),
208 false => format_ident!("{}", utils::to_camel_case(&self.ident_name())),
209 };
210 let async_struct_name = format_ident!("Async{}", struct_name);
211
212 let prebuilt_indices = match &self.prebuild {
227 Some(s) => (0..num_args)
228 .filter(|i| s.contains(&format!("${i}")))
229 .collect_vec(),
230 None => vec![],
231 };
232 let non_prebuilt_indices = match &self.prebuild {
233 Some(s) => (0..num_args)
234 .filter(|i| !s.contains(&format!("${i}")))
235 .collect_vec(),
236 _ => (0..num_args).collect_vec(),
237 };
238 let children_indices = match optimize_const {
239 #[allow(clippy::redundant_clone)] true => non_prebuilt_indices.clone(),
241 false => (0..num_args).collect_vec(),
242 };
243
244 fn idents(prefix: &str, indices: &[usize]) -> Vec<Ident> {
246 indices
247 .iter()
248 .map(|i| format_ident!("{prefix}{i}"))
249 .collect()
250 }
251 let inputs = idents("i", &children_indices);
252 let prebuilt_inputs = idents("i", &prebuilt_indices);
253 let non_prebuilt_inputs = idents("i", &non_prebuilt_indices);
254 let array_refs = idents("array", &children_indices);
255 let arrays = idents("a", &children_indices);
256 let datums = idents("v", &children_indices);
257 let arg_arrays = children_indices
258 .iter()
259 .map(|i| format_ident!("{}", types::array_type(&self.args[*i])));
260 let arg_arrays = arg_arrays.collect_vec();
261 let arg_types = children_indices.iter().map(|i| {
262 types::ref_type(&self.args[*i])
263 .parse::<TokenStream2>()
264 .unwrap()
265 });
266 let arg_types = arg_types.collect_vec();
267 let annotation: TokenStream2 = match user_fn.core_return_type.as_str() {
268 "T" | "T1" | "T2" | "T3" => format!(": Option<{}>", types::owned_type(&self.ret))
270 .parse()
271 .unwrap(),
272 _ => quote! {},
273 };
274 let ret_array_type = format_ident!("{}", types::array_type(&self.ret));
275 let builder_type = format_ident!("{}Builder", types::array_type(&self.ret));
276 let prebuilt_arg_type = match &self.prebuild {
277 Some(s) if optimize_const => s.split("::").next().unwrap().parse().unwrap(),
278 _ => quote! { () },
279 };
280 let prebuilt_arg_value = match &self.prebuild {
281 Some(s) => s
285 .replace('$', "i")
286 .parse()
287 .expect("invalid prebuild syntax"),
288 None => quote! { () },
289 };
290 let prebuild_const = if self.prebuild.is_some() && optimize_const {
291 let build_general = self.generate_build_scalar_function(user_fn, false)?;
292 quote! {{
293 let build_general = #build_general;
294 #(
295 let #prebuilt_inputs = match children[#prebuilt_indices].eval_const() {
297 Ok(s) => s,
298 Err(_) => return build_general(return_type, children),
300 };
301 let #prebuilt_inputs = match &#prebuilt_inputs {
303 Some(s) => s.as_scalar_ref_impl().try_into()?,
304 None => return Ok(risingwave_expr::expr::LiteralExpression::new(
306 return_type,
307 None,
308 ).boxed()),
309 };
310 )*
311 #prebuilt_arg_value
312 }}
313 } else {
314 quote! { () }
315 };
316
317 let check_children = match variadic {
319 true => quote! { risingwave_expr::ensure!(children.len() >= #num_args); },
320 false => quote! { risingwave_expr::ensure!(children.len() == #num_args); },
321 };
322
323 let eval_variadic_sync = variadic.then(|| {
325 quote! {
326 let mut columns = Vec::with_capacity(self.children.len() - #num_args);
327 for child in &self.children[#num_args..] {
328 columns.push(child.eval(input)?);
329 }
330 let variadic_input = DataChunk::new(columns, input.visibility().clone());
331 }
332 });
333 let eval_variadic_async = variadic.then(|| {
335 quote! {
336 let mut columns = Vec::with_capacity(self.children.len() - #num_args);
337 for child in &self.children[#num_args..] {
338 columns.push(child.eval(input).await?);
339 }
340 let variadic_input = DataChunk::new(columns, input.visibility().clone());
341 }
342 });
343 let eval_row_variadic_sync = variadic.then(|| {
345 quote! {
346 let mut row = Vec::with_capacity(self.children.len() - #num_args);
347 for child in &self.children[#num_args..] {
348 row.push(child.eval_row(input)?);
349 }
350 let variadic_row = OwnedRow::new(row);
351 }
352 });
353 let eval_row_variadic_async = variadic.then(|| {
355 quote! {
356 let mut row = Vec::with_capacity(self.children.len() - #num_args);
357 for child in &self.children[#num_args..] {
358 row.push(child.eval_row(input).await?);
359 }
360 let variadic_row = OwnedRow::new(row);
361 }
362 });
363
364 let generic = (self.ret == "boolean" && user_fn.generic == 3).then(|| {
365 let compatible_type = types::ref_type(types::min_compatible_type(&self.args))
367 .parse::<TokenStream2>()
368 .unwrap();
369 quote! { ::<_, _, #compatible_type> }
370 });
371 let prebuilt_arg = match (&self.prebuild, optimize_const) {
372 (Some(_), true) => quote! { &self.prebuilt_arg, },
374 (Some(_), false) => quote! { &#prebuilt_arg_value, },
376 (None, _) => quote! {},
378 };
379 let variadic_args = variadic.then(|| quote! { &variadic_row, });
380 let context = user_fn.context.then(|| quote! { &self.context, });
381 let writer = user_fn
382 .writer_type_kind
383 .is_some()
384 .then(|| quote! { &mut writer, });
385 let await_ = user_fn.async_.then(|| quote! { .await });
386
387 let record_error = {
388 #[allow(clippy::disallowed_methods)] let inputs_args = inputs
391 .iter()
392 .zip(user_fn.args_option.iter())
393 .map(|(input, opt)| {
394 if *opt {
395 quote! { #input.map(|s| ScalarRefImpl::from(s)) }
396 } else {
397 quote! { Some(ScalarRefImpl::from(#input)) }
398 }
399 });
400 let inputs_args = quote! {
401 let args: &[DatumRef<'_>] = &[#(#inputs_args),*];
402 let args = args.iter().copied();
403 };
404 let var_args = variadic.then(|| {
405 quote! {
406 let args = args.chain(variadic_row.iter());
407 }
408 });
409
410 quote! {
411 #inputs_args
412 #var_args
413 errors.push(ExprError::function(
414 stringify!(#fn_name),
415 args,
416 e,
417 ));
418 }
419 };
420
421 let mut output = quote! { #fn_name #generic(
424 #(#non_prebuilt_inputs,)*
425 #variadic_args
426 #prebuilt_arg
427 #context
428 #writer
429 ) #await_ };
430 output = match user_fn.return_type_kind {
433 _ if self.ret == "void" => quote! { { #output; Option::<i32>::None } },
435 ReturnTypeKind::T => quote! { Some(#output) },
436 ReturnTypeKind::Option => output,
437 ReturnTypeKind::Result => quote! {
438 match #output {
439 Ok(x) => Some(x),
440 Err(e) => {
441 #record_error
442 None
443 }
444 }
445 },
446 ReturnTypeKind::ResultOption => quote! {
447 match #output {
448 Ok(x) => x,
449 Err(e) => {
450 #record_error
451 None
452 }
453 }
454 },
455 };
456 if self.prebuild.is_some() {
459 output = quote! {
460 match (#(#inputs,)*) {
461 (#(Some(#inputs),)*) => #output,
462 _ => None,
463 }
464 };
465 } else {
466 #[allow(clippy::disallowed_methods)] let some_inputs = inputs
468 .iter()
469 .zip(user_fn.args_option.iter())
470 .map(|(input, opt)| {
471 if *opt {
472 quote! { #input }
473 } else {
474 quote! { Some(#input) }
475 }
476 });
477 output = quote! {
478 match (#(#inputs,)*) {
479 (#(#some_inputs,)*) => #output,
480 _ => None,
481 }
482 };
483 };
484 let append_output = match user_fn.writer_type_kind {
486 Some(WriterTypeKind::FmtWrite)
487 | Some(WriterTypeKind::IoWrite)
488 | Some(WriterTypeKind::ListWrite) => quote! {{
489 let mut writer = builder.writer();
490 if #output.is_some() {
491 writer.finish();
492 } else {
493 writer.rollback();
494 builder.append_null();
495 }
496 }},
497 Some(WriterTypeKind::JsonbbBuilder) => quote! {{
498 let mut writer_wrapper = builder.writer();
499 let mut writer = writer_wrapper.inner();
500 if #output.is_some() {
501 writer_wrapper.finish();
502 } else {
503 writer_wrapper.rollback();
504 builder.append_null();
505 }
506 }},
507 None if user_fn.core_return_type == "impl AsRef < [u8] >" => quote! {
508 builder.append(#output.as_ref().map(|s| s.as_ref()));
509 },
510 None => quote! {
511 let output #annotation = #output;
512 builder.append(output.as_ref().map(|s| s.as_scalar_ref()));
513 },
514 };
515 let row_output = match user_fn.writer_type_kind {
517 Some(WriterTypeKind::FmtWrite) => quote! {{
518 let mut writer = String::new();
519 #output.map(|_| writer.into())
520 }},
521 Some(WriterTypeKind::IoWrite) => quote! {{
522 let mut writer = Vec::new();
523 #output.map(|_| writer.into())
524 }},
525 Some(WriterTypeKind::JsonbbBuilder) => quote! {{
526 let mut writer = jsonbb::Builder::<Vec<u8>>::new();
527 #output.map(|_| JsonbVal::from(writer.finish()).into())
528 }},
529 Some(WriterTypeKind::ListWrite) => quote! {{
530 let mut writer = {
531 let DataType::List(list_ty) = &self.context.return_type else {
532 panic!("data type must be DataType::List");
533 };
534 list_ty.elem().create_array_builder(1)
535 };
536 #output.map(|_| ListValue::new(writer.finish()).into())
537 }},
538 None if user_fn.core_return_type == "impl AsRef < [u8] >" => quote! {
539 #output.map(|s| s.as_ref().into())
540 },
541 None => quote! {{
542 let output #annotation = #output;
543 output.map(|s| s.into())
544 }},
545 };
546 let eval = if let Some(batch_fn) = &self.batch_fn {
548 assert!(
549 !variadic,
550 "customized batch function is not supported for variadic functions"
551 );
552 let fn_name = format_ident!("{}", batch_fn);
554 quote! {
555 let c = #fn_name(#(#arrays),*);
556 Arc::new(c.into())
557 }
558 } else if (types::is_primitive(&self.ret) || self.ret == "boolean")
559 && user_fn.is_pure()
560 && !variadic
561 && self.prebuild.is_none()
562 {
563 match self.args.len() {
565 0 => quote! {
566 let c = #ret_array_type::from_iter_bitmap(
567 std::iter::repeat_with(|| #fn_name()).take(input.capacity()),
568 Bitmap::ones(input.capacity()),
569 );
570 Arc::new(c.into())
571 },
572 1 => quote! {
573 let c = #ret_array_type::from_iter_bitmap(
574 a0.raw_iter().map(|a| #fn_name(a)),
575 a0.null_bitmap().clone()
576 );
577 Arc::new(c.into())
578 },
579 2 => quote! {
580 #[allow(clippy::disallowed_methods)]
582 let c = #ret_array_type::from_iter_bitmap(
583 a0.raw_iter()
584 .zip(a1.raw_iter())
585 .map(|(a, b)| #fn_name #generic(a, b)),
586 a0.null_bitmap() & a1.null_bitmap(),
587 );
588 Arc::new(c.into())
589 },
590 n => todo!("SIMD optimization for {n} arguments"),
591 }
592 } else {
593 let let_variadic = variadic.then(|| {
595 quote! {
596 let variadic_row = variadic_input.row_at_unchecked_vis(i);
597 }
598 });
599 quote! {
600 let mut builder = #builder_type::with_type(input.capacity(), self.context.return_type.clone());
601
602 if input.is_vis_compacted() {
603 for i in 0..input.capacity() {
604 #(let #inputs = unsafe { #arrays.value_at_unchecked(i) };)*
605 #let_variadic
606 #append_output
607 }
608 } else {
609 for i in 0..input.capacity() {
610 if unsafe { !input.visibility().is_set_unchecked(i) } {
611 builder.append_null();
612 continue;
613 }
614 #(let #inputs = unsafe { #arrays.value_at_unchecked(i) };)*
615 #let_variadic
616 #append_output
617 }
618 }
619 Arc::new(builder.finish().into())
620 }
621 };
622
623 let sync_impl = if user_fn.async_ {
624 quote! {}
625 } else {
626 quote! {
627 #[derive(Debug)]
628 struct #struct_name {
629 context: Context,
630 children: Vec<Arc<dyn risingwave_expr::expr::SyncExpression>>,
631 prebuilt_arg: #prebuilt_arg_type,
632 }
633 impl risingwave_expr::expr::ExpressionInfo for #struct_name {
634 fn return_type(&self) -> DataType {
635 self.context.return_type.clone()
636 }
637 }
638 impl risingwave_expr::expr::SyncExpression for #struct_name {
639 fn eval(&self, input: &DataChunk) -> Result<ArrayRef> {
640 #(
641 let #array_refs = self.children[#children_indices].eval(input)?;
642 let #arrays: &#arg_arrays = #array_refs.as_ref().into();
643 )*
644 #eval_variadic_sync
645 let mut errors = vec![];
646 let array = { #eval };
647 if errors.is_empty() {
648 Ok(array)
649 } else {
650 Err(ExprError::Multiple(array, errors.into()))
651 }
652 }
653 fn eval_row(&self, input: &OwnedRow) -> Result<Datum> {
654 #(
655 let #datums = self.children[#children_indices].eval_row(input)?;
656 let #inputs: Option<#arg_types> = #datums.as_ref().map(|s| s.as_scalar_ref_impl().try_into().unwrap());
657 )*
658 #eval_row_variadic_sync
659 let mut errors: Vec<ExprError> = vec![];
660 let output = #row_output;
661 if let Some(err) = errors.into_iter().next() {
662 Err(err.into())
663 } else {
664 Ok(output)
665 }
666 }
667 }
668 }
669 };
670 let build_sync = if user_fn.async_ {
671 quote! {}
672 } else {
673 quote! {
674 use risingwave_expr::expr::try_into_sync_exprs;
675
676 let children = match try_into_sync_exprs(children) {
677 Ok(children) => {
678 return Ok(#struct_name {
679 context,
680 children,
681 prebuilt_arg,
682 }.boxed());
683 }
684 Err(children) => children,
685 };
686 }
687 };
688
689 Ok(quote! {
690 |return_type: DataType, children: Vec<risingwave_expr::expr::BoxedExpression>|
691 -> risingwave_expr::Result<risingwave_expr::expr::BoxedExpression>
692 {
693 use std::sync::Arc;
694 use risingwave_common::array::*;
695 use risingwave_common::types::*;
696 use risingwave_common::bitmap::Bitmap;
697 use risingwave_common::row::OwnedRow;
698 use risingwave_common::util::iter_util::ZipEqFast;
699
700 use risingwave_expr::expr::{Context, BoxedExpression, SyncExpressionBoxExt, AsyncExpressionBoxExt};
701 use risingwave_expr::{ExprError, Result};
702 use risingwave_expr::codegen::*;
703
704 #check_children
705 let prebuilt_arg = #prebuild_const;
706 let context = Context {
707 return_type,
708 arg_types: children.iter().map(|c| c.return_type()).collect(),
709 variadic: #variadic,
710 };
711
712 #sync_impl
713
714 #[derive(Debug)]
715 struct #async_struct_name {
716 context: Context,
717 children: Vec<BoxedExpression>,
718 prebuilt_arg: #prebuilt_arg_type,
719 }
720 impl risingwave_expr::expr::ExpressionInfo for #async_struct_name {
721 fn return_type(&self) -> DataType {
722 self.context.return_type.clone()
723 }
724 }
725 impl risingwave_expr::expr::AsyncExpression for #async_struct_name {
726 async fn eval(&self, input: &DataChunk) -> Result<ArrayRef> {
727 #(
728 let #array_refs = self.children[#children_indices].eval(input).await?;
729 let #arrays: &#arg_arrays = #array_refs.as_ref().into();
730 )*
731 #eval_variadic_async
732 let mut errors = vec![];
733 let array = { #eval };
734 if errors.is_empty() {
735 Ok(array)
736 } else {
737 Err(ExprError::Multiple(array, errors.into()))
738 }
739 }
740 async fn eval_row(&self, input: &OwnedRow) -> Result<Datum> {
741 #(
742 let #datums = self.children[#children_indices].eval_row(input).await?;
743 let #inputs: Option<#arg_types> = #datums.as_ref().map(|s| s.as_scalar_ref_impl().try_into().unwrap());
744 )*
745 #eval_row_variadic_async
746 let mut errors: Vec<ExprError> = vec![];
747 let output = #row_output;
748 if let Some(err) = errors.into_iter().next() {
749 Err(err.into())
750 } else {
751 Ok(output)
752 }
753 }
754 }
755
756 #build_sync
757
758 Ok(#async_struct_name {
759 context,
760 children,
761 prebuilt_arg,
762 }.boxed())
763 }
764 })
765 }
766
767 pub fn generate_aggregate_descriptor(
773 &self,
774 user_fn: &AggregateFnOrImpl,
775 build_fn: bool,
776 ) -> Result<TokenStream2> {
777 let name = self.name.clone();
778
779 let mut args = Vec::with_capacity(self.args.len());
780 for ty in &self.args {
781 args.push(sig_data_type(ty));
782 }
783 let ret = sig_data_type(&self.ret);
784 let state_type = match &self.state {
785 Some(ty) if ty != "ref" => {
786 let ty = data_type(ty);
787 quote! { Some(#ty) }
788 }
789 _ => quote! { None },
790 };
791 let append_only = match build_fn {
792 false => !user_fn.has_retract(),
793 true => self.append_only,
794 };
795
796 let pb_kind = format_ident!("{}", utils::to_camel_case(&name));
797 let ctor_name = match append_only {
798 false => format_ident!("{}", self.ident_name()),
799 true => format_ident!("{}_append_only", self.ident_name()),
800 };
801 let build_fn = if build_fn {
802 let name = format_ident!("{}", user_fn.as_fn().name);
803 quote! { #name }
804 } else if self.rewritten {
805 quote! { |_| Err(ExprError::UnsupportedFunction(#name.into())) }
806 } else {
807 self.generate_agg_build_fn(user_fn)?
808 };
809 let build_retractable = match append_only {
810 true => quote! { None },
811 false => quote! { Some(#build_fn) },
812 };
813 let build_append_only = match append_only {
814 false => quote! { None },
815 true => quote! { Some(#build_fn) },
816 };
817 let retractable_state_type = match append_only {
818 true => quote! { None },
819 false => state_type.clone(),
820 };
821 let append_only_state_type = match append_only {
822 false => quote! { None },
823 true => state_type,
824 };
825 let type_infer_fn = self.generate_type_infer_fn()?;
826 let deprecated = self.deprecated;
827 let maybe_allow_deprecated = if deprecated {
828 quote! { #[allow(deprecated)] }
829 } else {
830 quote! {}
831 };
832
833 Ok(quote! {
834 #[risingwave_expr::codegen::linkme::distributed_slice(risingwave_expr::sig::FUNCTIONS)]
835 fn #ctor_name() -> risingwave_expr::sig::FuncSign {
836 use risingwave_common::types::{DataType, DataTypeName};
837 use risingwave_expr::sig::{FuncSign, SigDataType, FuncBuilder};
838
839 #maybe_allow_deprecated
840 FuncSign {
841 name: risingwave_pb::expr::agg_call::PbKind::#pb_kind.into(),
842 inputs_type: vec![#(#args),*],
843 variadic: false,
844 ret_type: #ret,
845 build: FuncBuilder::Aggregate {
846 retractable: #build_retractable,
847 append_only: #build_append_only,
848 retractable_state_type: #retractable_state_type,
849 append_only_state_type: #append_only_state_type,
850 },
851 type_infer: #type_infer_fn,
852 deprecated: #deprecated,
853 }
854 }
855 })
856 }
857
858 fn generate_agg_build_fn(&self, user_fn: &AggregateFnOrImpl) -> Result<TokenStream2> {
860 let custom_state = user_fn.accumulate().first_mut_ref_arg.as_ref();
863 let state_type: TokenStream2 = match (custom_state, &self.state) {
864 (Some(s), _) => s.parse().unwrap(),
865 (_, Some(state)) if state == "ref" => types::ref_type(&self.ret).parse().unwrap(),
866 (_, Some(state)) if state != "ref" => types::owned_type(state).parse().unwrap(),
867 _ => types::owned_type(&self.ret).parse().unwrap(),
868 };
869 let let_arrays = self
870 .args
871 .iter()
872 .enumerate()
873 .map(|(i, arg)| {
874 let array = format_ident!("a{i}");
875 let array_type: TokenStream2 = types::array_type(arg).parse().unwrap();
876 quote! {
877 let #array: &#array_type = input.column_at(#i).as_ref().into();
878 }
879 })
880 .collect_vec();
881 let let_values = (0..self.args.len())
882 .map(|i| {
883 let v = format_ident!("v{i}");
884 let a = format_ident!("a{i}");
885 quote! { let #v = unsafe { #a.value_at_unchecked(row_id) }; }
886 })
887 .collect_vec();
888 let downcast_state = if custom_state.is_some() {
889 quote! { let mut state: &mut #state_type = state0.downcast_mut(); }
890 } else if let Some(s) = &self.state
891 && s == "ref"
892 {
893 quote! { let mut state: Option<#state_type> = state0.as_datum_mut().as_ref().map(|x| x.as_scalar_ref_impl().try_into().unwrap()); }
894 } else {
895 quote! { let mut state: Option<#state_type> = state0.as_datum_mut().take().map(|s| s.try_into().unwrap()); }
896 };
897 let restore_state = if custom_state.is_some() {
898 quote! {}
899 } else if let Some(s) = &self.state
900 && s == "ref"
901 {
902 quote! { *state0.as_datum_mut() = state.map(|x| x.to_owned_scalar().into()); }
903 } else {
904 quote! { *state0.as_datum_mut() = state.map(|s| s.into()); }
905 };
906 let create_state = if custom_state.is_some() {
907 quote! {
908 fn create_state(&self) -> Result<AggregateState> {
909 Ok(AggregateState::Any(Box::<#state_type>::default()))
910 }
911 }
912 } else if let Some(state) = &self.init_state {
913 let state: TokenStream2 = state.parse().unwrap();
914 quote! {
915 fn create_state(&self) -> Result<AggregateState> {
916 Ok(AggregateState::Datum(Some(#state.into())))
917 }
918 }
919 } else {
920 quote! {}
922 };
923 let args = (0..self.args.len()).map(|i| format_ident!("v{i}"));
924 let args = quote! { #(#args,)* };
925 let panic_on_retract = {
926 let msg = format!(
927 "attempt to retract on aggregate function {}, but it is append-only",
928 self.name
929 );
930 quote! { assert_eq!(op, Op::Insert, #msg); }
931 };
932 let mut next_state = match user_fn {
933 AggregateFnOrImpl::Fn(f) => {
934 let context = f.context.then(|| quote! { &self.context, });
935 let fn_name = format_ident!("{}", f.name);
936 match f.retract {
937 true => {
938 quote! { #fn_name(state, #args matches!(op, Op::Delete | Op::UpdateDelete) #context) }
939 }
940 false => quote! {{
941 #panic_on_retract
942 #fn_name(state, #args #context)
943 }},
944 }
945 }
946 AggregateFnOrImpl::Impl(i) => {
947 let retract = match i.retract {
948 Some(_) => quote! { self.function.retract(state, #args) },
949 None => panic_on_retract,
950 };
951 quote! {
952 if matches!(op, Op::Delete | Op::UpdateDelete) {
953 #retract
954 } else {
955 self.function.accumulate(state, #args)
956 }
957 }
958 }
959 };
960 next_state = match user_fn.accumulate().return_type_kind {
961 ReturnTypeKind::T => quote! { Some(#next_state) },
962 ReturnTypeKind::Option => next_state,
963 ReturnTypeKind::Result => quote! { Some(#next_state?) },
964 ReturnTypeKind::ResultOption => quote! { #next_state? },
965 };
966 if user_fn.accumulate().args_option.iter().all(|b| !b) {
967 match self.args.len() {
968 0 => {
969 next_state = quote! {
970 match state {
971 Some(state) => #next_state,
972 None => state,
973 }
974 };
975 }
976 1 => {
977 let first_state = if self.init_state.is_some() {
978 quote! { unreachable!() }
980 } else if let Some(s) = &self.state
981 && s == "ref"
982 {
983 quote! { Some(v0) }
985 } else if let AggregateFnOrImpl::Impl(impl_) = user_fn
986 && impl_.create_state.is_some()
987 {
988 quote! {{
990 let state = self.function.create_state();
991 #next_state
992 }}
993 } else {
994 quote! {{
995 let state = #state_type::default();
996 #next_state
997 }}
998 };
999 next_state = quote! {
1000 match (state, v0) {
1001 (Some(state), Some(v0)) => #next_state,
1002 (None, Some(v0)) => #first_state,
1003 (state, None) => state,
1004 }
1005 };
1006 }
1007 _ => todo!("multiple arguments are not supported for non-option function"),
1008 }
1009 }
1010 let update_state = if custom_state.is_some() {
1011 quote! { _ = #next_state; }
1012 } else {
1013 quote! { state = #next_state; }
1014 };
1015 let get_result = if custom_state.is_some() {
1016 quote! { Ok(state.downcast_ref::<#state_type>().into()) }
1017 } else if let AggregateFnOrImpl::Impl(impl_) = user_fn
1018 && impl_.finalize.is_some()
1019 {
1020 quote! {
1021 let state = match state.as_datum() {
1022 Some(s) => s.as_scalar_ref_impl().try_into().unwrap(),
1023 None => return Ok(None),
1024 };
1025 Ok(Some(self.function.finalize(state).into()))
1026 }
1027 } else {
1028 quote! { Ok(state.as_datum().clone()) }
1029 };
1030 let function_field = match user_fn {
1031 AggregateFnOrImpl::Fn(_) => quote! {},
1032 AggregateFnOrImpl::Impl(i) => {
1033 let struct_name = format_ident!("{}", i.struct_name);
1034 let generic = self.generic.as_ref().map(|g| {
1035 let g = format_ident!("{g}");
1036 quote! { <#g> }
1037 });
1038 quote! { function: #struct_name #generic, }
1039 }
1040 };
1041 let function_new = match user_fn {
1042 AggregateFnOrImpl::Fn(_) => quote! {},
1043 AggregateFnOrImpl::Impl(i) => {
1044 let struct_name = format_ident!("{}", i.struct_name);
1045 let generic = self.generic.as_ref().map(|g| {
1046 let g = format_ident!("{g}");
1047 quote! { ::<#g> }
1048 });
1049 quote! { function: #struct_name #generic :: default(), }
1050 }
1051 };
1052
1053 Ok(quote! {
1054 |agg| {
1055 use std::collections::HashSet;
1056 use std::ops::Range;
1057 use risingwave_common::array::*;
1058 use risingwave_common::types::*;
1059 use risingwave_common::bail;
1060 use risingwave_common::bitmap::Bitmap;
1061 use risingwave_common_estimate_size::EstimateSize;
1062
1063 use risingwave_expr::expr::Context;
1064 use risingwave_expr::Result;
1065 use risingwave_expr::aggregate::AggregateState;
1066 use risingwave_expr::codegen::async_trait;
1067
1068 let context = Context {
1069 return_type: agg.return_type.clone(),
1070 arg_types: agg.args.arg_types().to_owned(),
1071 variadic: false,
1072 };
1073
1074 struct Agg {
1075 context: Context,
1076 #function_field
1077 }
1078
1079 #[async_trait]
1080 impl risingwave_expr::aggregate::AggregateFunction for Agg {
1081 fn return_type(&self) -> DataType {
1082 self.context.return_type.clone()
1083 }
1084
1085 #create_state
1086
1087 async fn update(&self, state0: &mut AggregateState, input: &StreamChunk) -> Result<()> {
1088 #(#let_arrays)*
1089 #downcast_state
1090 for row_id in input.visibility().iter_ones() {
1091 let op = unsafe { *input.ops().get_unchecked(row_id) };
1092 #(#let_values)*
1093 #update_state
1094 }
1095 #restore_state
1096 Ok(())
1097 }
1098
1099 async fn update_range(&self, state0: &mut AggregateState, input: &StreamChunk, range: Range<usize>) -> Result<()> {
1100 assert!(range.end <= input.capacity());
1101 #(#let_arrays)*
1102 #downcast_state
1103 if input.is_vis_compacted() {
1104 for row_id in range {
1105 let op = unsafe { *input.ops().get_unchecked(row_id) };
1106 #(#let_values)*
1107 #update_state
1108 }
1109 } else {
1110 for row_id in input.visibility().iter_ones() {
1111 if row_id < range.start {
1112 continue;
1113 } else if row_id >= range.end {
1114 break;
1115 }
1116 let op = unsafe { *input.ops().get_unchecked(row_id) };
1117 #(#let_values)*
1118 #update_state
1119 }
1120 }
1121 #restore_state
1122 Ok(())
1123 }
1124
1125 async fn get_result(&self, state: &AggregateState) -> Result<Datum> {
1126 #get_result
1127 }
1128 }
1129
1130 Ok(Box::new(Agg {
1131 context,
1132 #function_new
1133 }))
1134 }
1135 })
1136 }
1137
1138 fn generate_table_function_descriptor(
1142 &self,
1143 user_fn: &UserFunctionAttr,
1144 build_fn: bool,
1145 ) -> Result<TokenStream2> {
1146 let name = self.name.clone();
1147 let mut args = Vec::with_capacity(self.args.len());
1148 for ty in &self.args {
1149 args.push(sig_data_type(ty));
1150 }
1151 let ret = sig_data_type(&self.ret);
1152
1153 let pb_type = format_ident!("{}", utils::to_camel_case(&name));
1154 let ctor_name = format_ident!("{}", self.ident_name());
1155 let build_fn = if build_fn {
1156 let name = format_ident!("{}", user_fn.name);
1157 quote! { #name }
1158 } else if self.rewritten {
1159 quote! { |_, _| Err(ExprError::UnsupportedFunction(#name.into())) }
1160 } else {
1161 self.generate_build_table_function(user_fn)?
1162 };
1163 let type_infer_fn = self.generate_type_infer_fn()?;
1164 let deprecated = self.deprecated;
1165 let maybe_allow_deprecated = if deprecated {
1166 quote! { #[allow(deprecated)] }
1167 } else {
1168 quote! {}
1169 };
1170
1171 Ok(quote! {
1172 #[risingwave_expr::codegen::linkme::distributed_slice(risingwave_expr::sig::FUNCTIONS)]
1173 fn #ctor_name() -> risingwave_expr::sig::FuncSign {
1174 use risingwave_common::types::{DataType, DataTypeName};
1175 use risingwave_expr::sig::{FuncSign, SigDataType, FuncBuilder};
1176
1177 #maybe_allow_deprecated
1178 FuncSign {
1179 name: risingwave_pb::expr::table_function::Type::#pb_type.into(),
1180 inputs_type: vec![#(#args),*],
1181 variadic: false,
1182 ret_type: #ret,
1183 build: FuncBuilder::Table(#build_fn),
1184 type_infer: #type_infer_fn,
1185 deprecated: #deprecated,
1186 }
1187 }
1188 })
1189 }
1190
1191 fn generate_build_table_function(&self, user_fn: &UserFunctionAttr) -> Result<TokenStream2> {
1192 let num_args = self.args.len();
1193 let return_types = output_types(&self.ret);
1194 let fn_name = format_ident!("{}", user_fn.name);
1195 let struct_name = format_ident!("{}", utils::to_camel_case(&self.ident_name()));
1196 let arg_ids = (0..num_args)
1197 .filter(|i| match &self.prebuild {
1198 Some(s) => !s.contains(&format!("${i}")),
1199 None => true,
1200 })
1201 .collect_vec();
1202 let const_ids = (0..num_args).filter(|i| match &self.prebuild {
1203 Some(s) => s.contains(&format!("${i}")),
1204 None => false,
1205 });
1206 let inputs: Vec<_> = arg_ids.iter().map(|i| format_ident!("i{i}")).collect();
1207 let all_child: Vec<_> = (0..num_args).map(|i| format_ident!("child{i}")).collect();
1208 let const_child: Vec<_> = const_ids.map(|i| format_ident!("child{i}")).collect();
1209 let child: Vec<_> = arg_ids.iter().map(|i| format_ident!("child{i}")).collect();
1210 let array_refs: Vec<_> = arg_ids.iter().map(|i| format_ident!("array{i}")).collect();
1211 let arrays: Vec<_> = arg_ids.iter().map(|i| format_ident!("a{i}")).collect();
1212 let arg_arrays = arg_ids
1213 .iter()
1214 .map(|i| format_ident!("{}", types::array_type(&self.args[*i])));
1215 let outputs = (0..return_types.len())
1216 .map(|i| format_ident!("o{i}"))
1217 .collect_vec();
1218 let builders = (0..return_types.len())
1219 .map(|i| format_ident!("builder{i}"))
1220 .collect_vec();
1221 let builder_types = return_types
1222 .iter()
1223 .map(|ty| format_ident!("{}Builder", types::array_type(ty)))
1224 .collect_vec();
1225 let return_types = if return_types.len() == 1 {
1226 vec![quote! { self.context.return_type.clone() }]
1227 } else {
1228 (0..return_types.len())
1229 .map(|i| quote! { self.context.return_type.as_struct().types().nth(#i).unwrap().clone() })
1230 .collect()
1231 };
1232 #[allow(clippy::disallowed_methods)]
1233 let optioned_outputs = user_fn
1234 .core_return_type
1235 .split(',')
1236 .map(|t| t.contains("Option"))
1237 .zip(&outputs)
1239 .map(|(optional, o)| match optional {
1240 false => quote! { Some(#o.as_scalar_ref()) },
1241 true => quote! { #o.map(|o| o.as_scalar_ref()) },
1242 })
1243 .collect_vec();
1244 let build_value_array = if return_types.len() == 1 {
1245 quote! { let [value_array] = value_arrays; }
1246 } else {
1247 quote! {
1248 let value_array = StructArray::new(
1249 self.context.return_type.as_struct().clone(),
1250 value_arrays.to_vec(),
1251 Bitmap::ones(len),
1252 ).into_ref();
1253 }
1254 };
1255 let context = user_fn.context.then(|| quote! { &self.context, });
1256 let prebuilt_arg = match &self.prebuild {
1257 Some(_) => quote! { &self.prebuilt_arg, },
1258 None => quote! {},
1259 };
1260 let prebuilt_arg_type = match &self.prebuild {
1261 Some(s) => s.split("::").next().unwrap().parse().unwrap(),
1262 None => quote! { () },
1263 };
1264 let prebuilt_arg_value = match &self.prebuild {
1265 Some(s) => s
1266 .replace('$', "child")
1267 .parse()
1268 .expect("invalid prebuild syntax"),
1269 None => quote! { () },
1270 };
1271 let iter = quote! { #fn_name(#(#inputs,)* #prebuilt_arg #context) };
1272 let mut iter = match user_fn.return_type_kind {
1273 ReturnTypeKind::T => quote! { #iter },
1274 ReturnTypeKind::Option => quote! { match #iter {
1275 Some(it) => it,
1276 None => continue,
1277 } },
1278 ReturnTypeKind::Result => quote! { match #iter {
1279 Ok(it) => it,
1280 Err(e) => {
1281 index_builder.append(Some(i as i32));
1282 #(#builders.append_null();)*
1283 error_builder.append_display(Some(e.as_report()));
1284 continue;
1285 }
1286 } },
1287 ReturnTypeKind::ResultOption => quote! { match #iter {
1288 Ok(Some(it)) => it,
1289 Ok(None) => continue,
1290 Err(e) => {
1291 index_builder.append(Some(i as i32));
1292 #(#builders.append_null();)*
1293 error_builder.append_display(Some(e.as_report()));
1294 continue;
1295 }
1296 } },
1297 };
1298 #[allow(clippy::disallowed_methods)] let some_inputs = inputs
1302 .iter()
1303 .zip(user_fn.args_option.iter())
1304 .map(|(input, opt)| {
1305 if *opt {
1306 quote! { #input }
1307 } else {
1308 quote! { Some(#input) }
1309 }
1310 });
1311 iter = quote! {
1312 match (#(#inputs,)*) {
1313 (#(#some_inputs,)*) => #iter,
1314 _ => continue,
1315 }
1316 };
1317 let iterator_item_type = user_fn.iterator_item_kind.clone().ok_or_else(|| {
1318 Error::new(
1319 user_fn.return_type_span,
1320 "expect `impl Iterator` in return type",
1321 )
1322 })?;
1323 let append_output = match iterator_item_type {
1324 ReturnTypeKind::T => quote! {
1325 let (#(#outputs),*) = output;
1326 #(#builders.append(#optioned_outputs);)* error_builder.append_null();
1327 },
1328 ReturnTypeKind::Option => quote! { match output {
1329 Some((#(#outputs),*)) => { #(#builders.append(#optioned_outputs);)* error_builder.append_null(); }
1330 None => { #(#builders.append_null();)* error_builder.append_null(); }
1331 } },
1332 ReturnTypeKind::Result => quote! { match output {
1333 Ok((#(#outputs),*)) => { #(#builders.append(#optioned_outputs);)* error_builder.append_null(); }
1334 Err(e) => { #(#builders.append_null();)* error_builder.append_display(Some(e.as_report())); }
1335 } },
1336 ReturnTypeKind::ResultOption => quote! { match output {
1337 Ok(Some((#(#outputs),*))) => { #(#builders.append(#optioned_outputs);)* error_builder.append_null(); }
1338 Ok(None) => { #(#builders.append_null();)* error_builder.append_null(); }
1339 Err(e) => { #(#builders.append_null();)* error_builder.append_display(Some(e.as_report())); }
1340 } },
1341 };
1342
1343 Ok(quote! {
1344 |return_type, chunk_size, children| {
1345 use risingwave_common::array::*;
1346 use risingwave_common::types::*;
1347 use risingwave_common::bitmap::Bitmap;
1348 use risingwave_common::util::iter_util::ZipEqFast;
1349 use risingwave_expr::expr::{BoxedExpression, Context};
1350 use risingwave_expr::{Result, ExprError};
1351 use risingwave_expr::codegen::*;
1352
1353 risingwave_expr::ensure!(children.len() == #num_args);
1354
1355 let context = Context {
1356 return_type: return_type.clone(),
1357 arg_types: children.iter().map(|c| c.return_type()).collect(),
1358 variadic: false,
1359 };
1360
1361 let mut iter = children.into_iter();
1362 #(let #all_child = iter.next().unwrap();)*
1363 #(
1364 let #const_child = #const_child.eval_const()?;
1365 let #const_child = match &#const_child {
1366 Some(s) => s.as_scalar_ref_impl().try_into()?,
1367 None => return Ok(risingwave_expr::table_function::empty(return_type)),
1369 };
1370 )*
1371
1372 #[derive(Debug)]
1373 struct #struct_name {
1374 context: Context,
1375 chunk_size: usize,
1376 #(#child: BoxedExpression,)*
1377 prebuilt_arg: #prebuilt_arg_type,
1378 }
1379 #[async_trait]
1380 impl risingwave_expr::table_function::TableFunction for #struct_name {
1381 fn return_type(&self) -> DataType {
1382 self.context.return_type.clone()
1383 }
1384 async fn eval<'a>(&'a self, input: &'a DataChunk) -> BoxStream<'a, Result<DataChunk>> {
1385 self.eval_inner(input)
1386 }
1387 }
1388 impl #struct_name {
1389 #[try_stream(boxed, ok = DataChunk, error = ExprError)]
1390 async fn eval_inner<'a>(&'a self, input: &'a DataChunk) {
1391 #(
1392 let #array_refs = self.#child.eval(input).await?;
1393 let #arrays: &#arg_arrays = #array_refs.as_ref().into();
1394 )*
1395
1396 let mut index_builder = I32ArrayBuilder::new(self.chunk_size);
1397 #(let mut #builders = #builder_types::with_type(self.chunk_size, #return_types);)*
1398 let mut error_builder = Utf8ArrayBuilder::new(self.chunk_size);
1399
1400 for i in 0..input.capacity() {
1401 if unsafe { !input.visibility().is_set_unchecked(i) } {
1402 continue;
1403 }
1404 #(let #inputs = unsafe { #arrays.value_at_unchecked(i) };)*
1405 for output in #iter {
1406 index_builder.append(Some(i as i32));
1407 #append_output
1408
1409 if index_builder.len() == self.chunk_size {
1410 let len = index_builder.len();
1411 let index_array = std::mem::replace(&mut index_builder, I32ArrayBuilder::new(self.chunk_size)).finish().into_ref();
1412 let value_arrays = [#(std::mem::replace(&mut #builders, #builder_types::with_type(self.chunk_size, #return_types)).finish().into_ref()),*];
1413 #build_value_array
1414 let error_array = std::mem::replace(&mut error_builder, Utf8ArrayBuilder::new(self.chunk_size)).finish().into_ref();
1415 if error_array.null_bitmap().any() {
1416 yield DataChunk::new(vec![index_array, value_array, error_array], self.chunk_size);
1417 } else {
1418 yield DataChunk::new(vec![index_array, value_array], self.chunk_size);
1419 }
1420 }
1421 }
1422 }
1423
1424 if index_builder.len() > 0 {
1425 let len = index_builder.len();
1426 let index_array = index_builder.finish().into_ref();
1427 let value_arrays = [#(#builders.finish().into_ref()),*];
1428 #build_value_array
1429 let error_array = error_builder.finish().into_ref();
1430 if error_array.null_bitmap().any() {
1431 yield DataChunk::new(vec![index_array, value_array, error_array], len);
1432 } else {
1433 yield DataChunk::new(vec![index_array, value_array], len);
1434 }
1435 }
1436 }
1437 }
1438
1439 Ok(Box::new(#struct_name {
1440 context,
1441 chunk_size,
1442 #(#child,)*
1443 prebuilt_arg: #prebuilt_arg_value,
1444 }))
1445 }
1446 })
1447 }
1448}
1449
1450fn sig_data_type(ty: &str) -> TokenStream2 {
1451 match ty {
1452 "any" => quote! { SigDataType::Any },
1453 "anyarray" => quote! { SigDataType::AnyArray },
1454 "anymap" => quote! { SigDataType::AnyMap },
1455 "vector" => quote! { SigDataType::Vector },
1456 "struct" => quote! { SigDataType::AnyStruct },
1457 _ if ty.starts_with("struct") && ty.contains("any") => quote! { SigDataType::AnyStruct },
1458 _ => {
1459 let datatype = data_type(ty);
1460 quote! { SigDataType::Exact(#datatype) }
1461 }
1462 }
1463}
1464
1465fn data_type(ty: &str) -> TokenStream2 {
1466 if let Some(ty) = ty.strip_suffix("[]") {
1467 let inner_type = data_type(ty);
1468 return quote! { DataType::list(#inner_type) };
1469 }
1470 if ty.starts_with("struct<") {
1471 return quote! { DataType::Struct(#ty.parse().expect("invalid struct type")) };
1472 }
1473 let variant = format_ident!("{}", types::data_type(ty));
1474 quote! { DataType::#variant }
1481}
1482
1483fn output_types(ty: &str) -> Vec<&str> {
1490 if let Some(s) = ty.strip_prefix("struct<")
1491 && let Some(args) = s.strip_suffix('>')
1492 {
1493 args.split(',')
1494 .map(|s| s.split_whitespace().nth(1).unwrap())
1495 .collect()
1496 } else {
1497 vec![ty]
1498 }
1499}