uqa_sql/expr/call_dispatch/
production.rs1use super::named::builtin_named_args;
10use crate::{
11 ast::{FunctionBinding, FunctionDispatch},
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 eval_builtin_function_call_with_control(
27 name,
28 binding,
29 arguments,
30 control,
31 &super::EvalContext::new(None, &[]),
32 )
33}
34
35pub fn eval_builtin_function_call_with_control(
37 name: &str,
38 binding: Option<&FunctionBinding>,
39 arguments: Produced<Vec<(Option<String>, Value)>>,
40 control: &ProductionControl<'_>,
41 context: &super::EvalContext<'_>,
42) -> Result<Produced<Value>> {
43 if let Some(error) = binding.and_then(|binding| binding.resolution_error.as_ref()) {
44 return Err(error.sql_error());
45 }
46 control.check()?;
47 if binding.is_some_and(|binding| !binding.builtin) {
48 return Err(SQLError::Unsupported(
49 "bound user function requires a logical engine session".into(),
50 ));
51 }
52 if let (Some(binding), Some(hook)) = (binding, context.engine) {
53 hook.require_builtin_execute(binding)?;
54 }
55 let _interval_style = super::super::interval_style::call_scope(
56 binding.map_or(name, |binding| binding.name.as_str()),
57 &arguments,
58 context.engine,
59 control,
60 )?;
61 if let Some((binding, dispatch)) =
62 binding.and_then(|binding| binding.dispatch.map(|dispatch| (binding, dispatch)))
63 {
64 if arguments.iter().any(|(name, _)| name.is_some()) {
65 return Err(SQLError::Internal(format!(
66 "bound {} expression retained a named argument",
67 dispatch.label(),
68 )));
69 }
70 let evaluated = MovedArguments::new(arguments, control)?;
71 return eval_dispatched(binding, dispatch, &evaluated.values, control, context);
72 }
73 let name = binding.map_or(name, |binding| binding.name.as_str());
74 let normalized =
75 super::super::call_arguments::normalized_function_name_with_control(name, control)?;
76 let name = normalized.as_ref();
77 let value = if arguments.iter().any(|(name, _)| name.is_some()) {
78 let Some(positional) = builtin_named_args(name, &arguments, control)? else {
79 return Err(super::super::diagnostics::unknown_function_error(
80 name, &arguments,
81 ));
82 };
83 eval_scalar(name, &positional, control, context)?
84 } else {
85 let evaluated = MovedArguments::new(arguments, control)?;
86 eval_scalar(name, &evaluated.values, control, context)?
87 };
88 if matches!(*value, Value::Int(_) | Value::Float(_)) {
89 if let Some(binding) = binding {
90 if let Some(ty) = crate::fixed_builtin_return_type_with_control(binding, control)? {
91 if matches!(
92 *ty,
93 crate::ColumnType::SmallInteger
94 | crate::ColumnType::Integer
95 | crate::ColumnType::Real
96 ) {
97 let name = ty.sql_name_with_control(control)?;
98 return super::super::cast_value_from_with_control(
99 &value, &name, None, control,
100 );
101 }
102 }
103 }
104 }
105 Ok(value)
106}
107
108fn eval_dispatched(
109 binding: &FunctionBinding,
110 dispatch: FunctionDispatch,
111 arguments: &[Value],
112 control: &ProductionControl<'_>,
113 context: &super::EvalContext<'_>,
114) -> Result<Produced<Value>> {
115 if let FunctionDispatch::Enum {
116 operation,
117 type_oid,
118 } = dispatch
119 {
120 return super::super::enums::enum_function_value_with_control(
121 context
122 .engine
123 .and_then(super::super::EngineHook::enum_labels),
124 operation,
125 type_oid,
126 arguments,
127 context.enum_comparison_state(),
128 control,
129 );
130 }
131 if let Some(result) = super::super::scalar_postgres::eval_comparison_with_context(
132 dispatch, arguments, control, context,
133 ) {
134 return result;
135 }
136 super::super::builtin::eval_dispatched_builtin_with_control(
137 binding, dispatch, arguments, control, context,
138 )
139}
140
141fn eval_scalar(
142 name: &str,
143 arguments: &[Value],
144 control: &ProductionControl<'_>,
145 context: &super::EvalContext<'_>,
146) -> Result<Produced<Value>> {
147 if matches!(name, "coalesce" | "nullif" | "greatest" | "least") {
148 return super::super::scalar_core::selection_with_context(
149 name, arguments, control, context,
150 );
151 }
152 super::super::scalar_dispatch::eval_generated_scalar_function_with_context(
153 name, arguments, control, context,
154 )
155}
156
157struct MovedArguments {
159 values: Vec<Value>,
160 remaining: std::vec::IntoIter<(Option<String>, Value)>,
161 _memory: Option<MemoryReservation>,
162}
163
164impl MovedArguments {
165 fn new(
166 arguments: Produced<Vec<(Option<String>, Value)>>,
167 control: &ProductionControl<'_>,
168 ) -> Result<Self> {
169 let mut values = ProductionVec::new(*control);
170 values.reserve(arguments.len())?;
171 let (values, values_memory) = values.finish()?.into_parts();
172 let (arguments, argument_memory) = arguments.into_parts();
173 let mut output = Self {
174 values,
175 remaining: arguments.into_iter(),
176 _memory: control.combine(values_memory, argument_memory),
177 };
178 for (_, value) in output.remaining.by_ref() {
179 control.check()?;
180 output.values.push(value);
181 }
182 control.check()?;
183 Ok(output)
184 }
185}
186
187#[cfg(test)]
188mod tests;