uqa_sql/expr/call_dispatch/
production.rs1use super::named::builtin_named_args;
10use crate::{
11 ast::FunctionBinding,
12 error::{Result, SQLError},
13};
14use uqa_core::{
15 memory::{MemoryReservation, Produced, ProductionControl, ProductionVec},
16 Value,
17};
18
19pub fn eval_generated_function_call_with_control(
21 name: &str,
22 binding: Option<&FunctionBinding>,
23 arguments: Produced<Vec<(Option<String>, Value)>>,
24 control: &ProductionControl<'_>,
25) -> Result<Produced<Value>> {
26 if let Some(error) = binding.and_then(|binding| binding.resolution_error.as_ref()) {
27 return Err(error.sql_error());
28 }
29 control.check()?;
30 if binding.is_some_and(|binding| !binding.builtin) {
31 return Err(SQLError::Unsupported(
32 "bound user function requires a logical engine session".into(),
33 ));
34 }
35 if let Some((binding, dispatch)) =
36 binding.and_then(|binding| binding.dispatch.map(|dispatch| (binding, dispatch)))
37 {
38 if arguments.iter().any(|(name, _)| name.is_some()) {
39 return Err(SQLError::Internal(format!(
40 "bound {} expression retained a named argument",
41 dispatch.label(),
42 )));
43 }
44 let evaluated = MovedArguments::new(arguments, control)?;
45 return super::super::builtin::eval_dispatched_builtin_with_control(
46 binding,
47 dispatch,
48 &evaluated.values,
49 control,
50 );
51 }
52 let name = binding.map_or(name, |binding| binding.name.as_str());
53 let normalized =
54 super::super::call_arguments::normalized_function_name_with_control(name, control)?;
55 let name = normalized.as_ref();
56 let value = if arguments.iter().any(|(name, _)| name.is_some()) {
57 let Some(positional) = builtin_named_args(name, &arguments, control)? else {
58 return Err(super::super::diagnostics::unknown_function_error(
59 name, &arguments,
60 ));
61 };
62 super::super::scalar_dispatch::eval_generated_scalar_function(name, &positional, control)?
63 } else {
64 let evaluated = MovedArguments::new(arguments, control)?;
65 super::super::scalar_dispatch::eval_generated_scalar_function(
66 name,
67 &evaluated.values,
68 control,
69 )?
70 };
71 if matches!(*value, Value::Int(_) | Value::Float(_)) {
72 if let Some(binding) = binding {
73 if let Some(ty) = crate::fixed_builtin_return_type_with_control(binding, control)? {
74 if matches!(
75 *ty,
76 crate::ColumnType::SmallInteger
77 | crate::ColumnType::Integer
78 | crate::ColumnType::Real
79 ) {
80 let name = ty.sql_name_with_control(control)?;
81 return super::super::cast_value_from_with_control(
82 &value, &name, None, control,
83 );
84 }
85 }
86 }
87 }
88 Ok(value)
89}
90
91struct MovedArguments {
93 values: Vec<Value>,
94 remaining: std::vec::IntoIter<(Option<String>, Value)>,
95 _memory: Option<MemoryReservation>,
96}
97
98impl MovedArguments {
99 fn new(
100 arguments: Produced<Vec<(Option<String>, Value)>>,
101 control: &ProductionControl<'_>,
102 ) -> Result<Self> {
103 let mut values = ProductionVec::new(*control);
104 values.reserve(arguments.len())?;
105 let (values, values_memory) = values.finish()?.into_parts();
106 let (arguments, argument_memory) = arguments.into_parts();
107 let mut output = Self {
108 values,
109 remaining: arguments.into_iter(),
110 _memory: control.combine(values_memory, argument_memory),
111 };
112 for (_, value) in output.remaining.by_ref() {
113 control.check()?;
114 output.values.push(value);
115 }
116 control.check()?;
117 Ok(output)
118 }
119}
120
121#[cfg(test)]
122mod tests;