uqa_sql/expr/
array_transform.rs1use super::{validate_named_argument_order_with_control, Result, Value};
10use uqa_core::memory::{Produced, ProductionControl, ProductionVec};
11
12pub fn argument_positions(
14 name: &str,
15 argument_names: &[Option<&str>],
16) -> Result<Option<Vec<usize>>> {
17 argument_positions_with_control(name, argument_names, &ProductionControl::uncontrolled()).map(
18 |positions| {
19 positions.map(|positions| {
20 positions
21 .into_uncontrolled()
22 .expect("ordinary argument positions")
23 })
24 },
25 )
26}
27
28pub fn argument_positions_with_control(
29 name: &str,
30 argument_names: &[Option<&str>],
31 control: &ProductionControl<'_>,
32) -> Result<Option<Produced<Vec<usize>>>> {
33 control.check()?;
34 validate_named_argument_order_with_control(argument_names.iter().copied(), control)?;
35 let function = name
36 .get(..11)
37 .filter(|prefix| prefix.eq_ignore_ascii_case("pg_catalog."))
38 .map_or(name, |_| &name[11..]);
39 let parameter_names: &[Option<&str>] = if argument_names.len() == 1
40 && (function.eq_ignore_ascii_case("array_reverse")
41 || function.eq_ignore_ascii_case("array_sort"))
42 {
43 &[None]
44 } else if function.eq_ignore_ascii_case("array_sort") {
45 match argument_names.len() {
46 2 => &[Some("array"), Some("descending")],
47 3 => &[Some("array"), Some("descending"), Some("nulls_first")],
48 _ => return Ok(None),
49 }
50 } else {
51 return Ok(None);
52 };
53 let mut occupied = [false; 3];
54 let mut positions = ProductionVec::new(*control);
55 positions.reserve(argument_names.len())?;
56 let mut positional = 0;
57 for argument_name in argument_names {
58 let position = if let Some(argument_name) = argument_name {
59 parameter_names
60 .iter()
61 .position(|candidate| *candidate == Some(*argument_name))
62 } else {
63 let position = positional;
64 positional += 1;
65 Some(position)
66 };
67 let Some(position) = position.filter(|position| *position < parameter_names.len()) else {
68 return Ok(None);
69 };
70 if occupied[position] {
71 return Ok(None);
72 }
73 occupied[position] = true;
74 positions.push_copy(position)?;
75 }
76 Ok(occupied[..parameter_names.len()]
77 .iter()
78 .all(|slot| *slot)
79 .then(|| positions.finish())
80 .transpose()?)
81}
82
83pub(super) fn reorder_named_values_with_control(
84 function: &str,
85 call_args: &[(Option<String>, Value)],
86 control: &ProductionControl<'_>,
87) -> Result<Option<Produced<Vec<Value>>>> {
88 let names = super::call_arguments::evaluated_argument_names_with_control(call_args, control)?;
89 let positions = match argument_positions_with_control(function, &names, control) {
90 Ok(Some(positions)) => positions,
91 Err(error) if matches!(error.sqlstate(), Some("53200" | "57014")) => return Err(error),
92 Ok(None) | Err(_) => return Ok(None),
93 };
94 let mut values = [None; 3];
95 for ((_, value), position) in call_args.iter().zip(positions.iter().copied()) {
96 values[position] = Some(value);
97 }
98 let mut output = ProductionVec::new(*control);
99 output.reserve(call_args.len())?;
100 for value in &values[..call_args.len()] {
101 let Some(value) = value else { return Ok(None) };
102 output.push_produced(control.copy_value(value)?)?;
103 }
104 Ok(Some(output.finish()?))
105}