uqa_sql/expr/
numeric_operator.rs1use crate::ast::{ColumnType, Expr, FunctionBinding, NumericOperator};
10use crate::type_resolution::numeric_operator_types_with_control;
11use crate::{RowSchema, SQLError};
12use uqa_core::{
13 memory::{Produced, ProductionControl, ProductionVec},
14 Value,
15};
16
17use super::{eval, EvalContext, Result};
18
19#[cfg(test)]
20mod tests;
21
22pub fn eval_numeric_operator(
23 operator: NumericOperator,
24 arguments: &[Value],
25 types: &[Option<ColumnType>],
26) -> Result<Value> {
27 eval_numeric_operator_with_control(
28 operator,
29 arguments,
30 types,
31 &ProductionControl::uncontrolled(),
32 )
33 .map(|value| {
34 value
35 .into_uncontrolled()
36 .expect("ordinary numeric result has no reservation")
37 })
38}
39
40pub fn eval_numeric_operator_with_control(
42 operator: NumericOperator,
43 arguments: &[Value],
44 types: &[Option<ColumnType>],
45 control: &ProductionControl<'_>,
46) -> Result<Produced<Value>> {
47 control.check()?;
48 if arguments.len() != types.len() {
49 return Err(SQLError::Internal(
50 "operator operand/type count differs".into(),
51 ));
52 }
53 let selected = numeric_operator_types_with_control(operator, types, control)?;
54 let mut converted = ProductionVec::new(*control);
55 converted.reserve(arguments.len())?;
56 for ((value, source), target) in arguments.iter().zip(types).zip(&selected.arguments) {
57 let target = target.sql_name_with_control(control)?;
58 let source = source
59 .as_ref()
60 .map(|ty| ty.sql_name_with_control(control))
61 .transpose()?;
62 converted.push_produced(super::cast_value_from_with_control(
63 value,
64 &target,
65 source.as_deref().map(String::as_str),
66 control,
67 )?)?;
68 }
69 let arguments = converted.finish()?;
70 if arguments.iter().any(|value| matches!(value, Value::Null)) {
71 return Ok(control.finish(Value::Null, control.empty_reservation())?);
72 }
73 let value = if operator == NumericOperator::Plus {
74 control.copy_value(&arguments[0])?
75 } else {
76 let name = match operator {
77 NumericOperator::Modulo => "mod",
78 NumericOperator::Power => "power",
79 NumericOperator::SquareRoot => "sqrt",
80 NumericOperator::CubeRoot => "cbrt",
81 NumericOperator::Absolute => "abs",
82 NumericOperator::Plus => unreachable!("unary plus copied its selected operand"),
83 };
84 super::scalar_core::eval_core_functions_with_control(name, &arguments, control)
85 .or_else(|| {
86 super::scalar_math::eval_math_functions_with_control(name, &arguments, control)
87 })
88 .expect("numeric syntax selects an existing numeric function")?
89 };
90 let target = selected.result.sql_name_with_control(control)?;
92 super::cast_value_from_with_control(&value, &target, None, control)
93}
94
95pub(super) fn eval_ast_operator(
96 operator: NumericOperator,
97 binding: &FunctionBinding,
98 arguments: &[Expr],
99 context: &EvalContext<'_>,
100) -> Result<Value> {
101 let values = arguments
102 .iter()
103 .map(|arg| eval(arg, context))
104 .collect::<Result<Vec<_>>>()?;
105 let types = if binding.argument_types.is_empty() {
106 arguments
107 .iter()
108 .zip(&values)
109 .map(|(arg, value)| {
110 let scalar = crate::plan::ExpressionPlan::lower(arg.clone()).scalar;
111 let ty = crate::common_context_expression_type(
112 &scalar,
113 &RowSchema::default(),
114 context.params,
115 None,
116 )?;
117 Ok(ty.or_else(|| {
118 (!matches!(arg, Expr::Literal(Value::Str(_) | Value::Null)))
119 .then(|| crate::type_resolution::value_type(value))
120 .flatten()
121 }))
122 })
123 .collect::<Result<Vec<_>>>()?
124 } else {
125 binding
126 .argument_types
127 .iter()
128 .map(|name| ColumnType::from_sql_name(name).map(Some))
129 .collect::<Result<Vec<_>>>()?
130 };
131 eval_numeric_operator(operator, &values, &types)
132}
133
134pub(super) fn eval_bound_operator_with_control(
135 operator: NumericOperator,
136 binding: &FunctionBinding,
137 arguments: &[Value],
138 control: &ProductionControl<'_>,
139) -> Result<Produced<Value>> {
140 control.check()?;
141 let mut types = ProductionVec::new(*control);
142 if binding.argument_types.is_empty() {
143 types.reserve(arguments.len())?;
144 for value in arguments {
145 let ty = crate::type_resolution::value_type_with_control(value, control)?;
146 let (ty, memory) = ty.map_or_else(
147 || (None, control.empty_reservation()),
148 |ty| {
149 let (ty, memory) = ty.into_parts();
150 (Some(ty), memory)
151 },
152 );
153 types.push_produced(control.finish(ty, memory)?)?;
154 }
155 } else {
156 types.reserve(binding.argument_types.len())?;
157 for name in &binding.argument_types {
158 let (ty, memory) = ColumnType::from_sql_name_with_control(name, control)?.into_parts();
159 types.push_produced(control.finish(Some(ty), memory)?)?;
160 }
161 }
162 let types = types.finish()?;
163 eval_numeric_operator_with_control(operator, arguments, &types, control)
164}
165
166#[cfg(test)]
167mod production_tests;