uqa_sql/expr/
call_arguments.rs1use std::borrow::Cow;
10
11use uqa_core::{
12 memory::{Produced, ProductionControl, ProductionString, ProductionVec},
13 Value,
14};
15
16use crate::ast::{Expr, FunctionBinding, FunctionDispatch};
17use crate::error::{Result, SQLError};
18
19use super::context::EvalContext;
20use super::evaluator::eval;
21
22pub(super) fn normalized_function_name(name: &str) -> Cow<'_, str> {
23 normalized_function_name_with_control(name, &ProductionControl::uncontrolled())
24 .expect("ordinary function name normalization")
25 .into_uncontrolled()
26 .expect("ordinary function name has no retained owner")
27}
28
29pub(super) fn normalized_function_name_with_control<'a>(
30 name: &'a str,
31 control: &ProductionControl<'_>,
32) -> Result<Produced<Cow<'a, str>>> {
33 control.check()?;
34 let stripped = name.strip_prefix("pg_catalog.").unwrap_or(name);
35 if !stripped.bytes().any(|byte| byte.is_ascii_uppercase()) {
36 return control
37 .finish(Cow::Borrowed(stripped), control.empty_reservation())
38 .map_err(Into::into);
39 }
40 let mut output = ProductionString::new(*control);
41 output.reserve(stripped.len())?;
42 for character in stripped.chars() {
43 output.push(character.to_ascii_lowercase())?;
44 }
45 let (output, memory) = output.finish()?.into_parts();
46 control
47 .finish(Cow::Owned(output), memory)
48 .map_err(Into::into)
49}
50
51fn binding_dispatch(binding: Option<&FunctionBinding>) -> Option<FunctionDispatch> {
52 binding.and_then(|binding| binding.dispatch)
53}
54
55fn direct_variadic_argument_value(argument: &Expr) -> Option<&Expr> {
56 let Expr::Func { binding, args, .. } = argument else {
57 return None;
58 };
59 if binding_dispatch(binding.as_ref()) != Some(FunctionDispatch::VariadicArgument) {
60 return None;
61 }
62 let [value] = args.as_slice() else {
63 return None;
64 };
65 Some(value)
66}
67
68fn named_argument_value(argument: &Expr) -> Option<&Expr> {
69 let Expr::Func { binding, args, .. } = argument else {
70 return None;
71 };
72 if binding_dispatch(binding.as_ref()) == Some(FunctionDispatch::NamedArgument) {
73 args.get(1)
74 } else {
75 None
76 }
77}
78
79#[must_use]
81pub fn wrap_variadic_argument(mut argument: Expr) -> Expr {
82 if variadic_argument_value(&argument).is_some() {
83 return argument;
84 }
85 if let Expr::Func { binding, args, .. } = &mut argument {
86 if binding_dispatch(binding.as_ref()) == Some(FunctionDispatch::NamedArgument)
87 && args.len() == 2
88 {
89 let value = args.remove(1);
90 args.push(variadic_argument_marker(value));
91 return argument;
92 }
93 }
94 variadic_argument_marker(argument)
95}
96
97fn variadic_argument_marker(value: Expr) -> Expr {
98 let binding = FunctionBinding::dispatched(FunctionDispatch::VariadicArgument);
99 Expr::Func {
100 order_syntax: crate::ast::FunctionOrderSyntax::Ordinary,
101 name: binding.name.clone(),
102 binding: Some(binding),
103 args: vec![value],
104 distinct: false,
105 order_by: Vec::new(),
106 filter: None,
107 }
108}
109
110#[must_use]
112pub fn variadic_argument_value(argument: &Expr) -> Option<&Expr> {
113 let value = named_argument_value(argument).unwrap_or(argument);
114 direct_variadic_argument_value(value)
115}
116
117#[must_use]
119pub fn call_argument_value(argument: &Expr) -> &Expr {
120 let value = named_argument_value(argument).unwrap_or(argument);
121 direct_variadic_argument_value(value).unwrap_or(value)
122}
123
124pub fn validate_named_argument_order<'a>(
128 argument_names: impl IntoIterator<Item = Option<&'a str>>,
129) -> Result<()> {
130 validate_named_argument_order_with_control(argument_names, &ProductionControl::uncontrolled())
131}
132
133pub fn validate_named_argument_order_with_control<'a>(
134 argument_names: impl IntoIterator<Item = Option<&'a str>>,
135 control: &ProductionControl<'_>,
136) -> Result<()> {
137 control.check()?;
138 let mut saw_named = false;
139 let mut named = ProductionVec::new(*control);
140 for argument_name in argument_names {
141 control.check()?;
142 let Some(argument_name) = argument_name else {
143 if saw_named {
144 return Err(SQLError::Routine {
145 sqlstate: "42601".into(),
146 message: "positional argument cannot follow named argument".into(),
147 });
148 }
149 continue;
150 };
151 saw_named = true;
152 if named.contains(&argument_name) {
153 return Err(SQLError::Routine {
154 sqlstate: "42601".into(),
155 message: format!("argument name \"{argument_name}\" used more than once"),
156 });
157 }
158 named.push_copy(argument_name)?;
159 }
160 Ok(())
161}
162
163pub fn evaluate_call_args(
165 args: &[Expr],
166 ctx: &EvalContext<'_>,
167) -> Result<Vec<(Option<String>, Value)>> {
168 args.iter()
169 .map(|arg| match arg {
170 Expr::Func {
171 binding,
172 args: inner,
173 ..
174 } if binding_dispatch(binding.as_ref()) == Some(FunctionDispatch::NamedArgument) => {
175 let Some(Expr::Literal(Value::Str(arg_name))) = inner.first() else {
176 return Err(SQLError::Internal("named argument without a name".into()));
177 };
178 let value_expr = inner
179 .get(1)
180 .ok_or_else(|| SQLError::Internal("named argument without a value".into()))?;
181 Ok((
182 Some(arg_name.clone()),
183 evaluate_call_argument_value(value_expr, ctx)?,
184 ))
185 }
186 other => Ok((None, evaluate_call_argument_value(other, ctx)?)),
187 })
188 .collect()
189}
190
191fn evaluate_call_argument_value(argument: &Expr, ctx: &EvalContext<'_>) -> Result<Value> {
192 if let Expr::Func { binding, args, .. } = argument {
193 if binding_dispatch(binding.as_ref()) == Some(FunctionDispatch::VariadicArgument) {
194 let [value] = args.as_slice() else {
195 return Err(SQLError::Internal(
196 "VARIADIC argument marker must contain one value".into(),
197 ));
198 };
199 return eval(value, ctx);
200 }
201 }
202 eval(argument, ctx)
203}
204
205pub(super) fn evaluated_argument_names_with_control<'a>(
207 args: &'a [(Option<String>, Value)],
208 control: &ProductionControl<'_>,
209) -> Result<Produced<Vec<Option<&'a str>>>> {
210 let mut names = ProductionVec::new(*control);
211 names.reserve(args.len())?;
212 for (name, _) in args {
213 names.push_copy(name.as_deref())?;
214 }
215 Ok(names.finish()?)
216}