Skip to main content

uqa_sql/expr/
array_transform.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Shared `PostgreSQL` 18 signatures for array transformations.
8
9use super::{validate_named_argument_order, Result, Value};
10
11/// Map call-order arguments onto the declared `array_sort` and `array_reverse` slots. `None` means the arity or a named argument does not select a catalogued overload.
12pub fn argument_positions(
13    name: &str,
14    argument_names: &[Option<&str>],
15) -> Result<Option<Vec<usize>>> {
16    validate_named_argument_order(argument_names.iter().copied())?;
17    let lower = name.to_ascii_lowercase();
18    let function = lower.strip_prefix("pg_catalog.").unwrap_or(&lower);
19    let parameter_names: &[Option<&str>] = match (function, argument_names.len()) {
20        ("array_reverse", 1) | ("array_sort", 1) => &[None],
21        ("array_sort", 2) => &[Some("array"), Some("descending")],
22        ("array_sort", 3) => &[Some("array"), Some("descending"), Some("nulls_first")],
23        _ => return Ok(None),
24    };
25    let mut occupied = vec![false; parameter_names.len()];
26    let mut positions = Vec::with_capacity(argument_names.len());
27    let mut positional = 0;
28    for argument_name in argument_names {
29        let position = if let Some(argument_name) = argument_name {
30            parameter_names
31                .iter()
32                .position(|candidate| *candidate == Some(*argument_name))
33        } else {
34            let position = positional;
35            positional += 1;
36            Some(position)
37        };
38        let Some(position) = position.filter(|position| *position < occupied.len()) else {
39            return Ok(None);
40        };
41        if occupied[position] {
42            return Ok(None);
43        }
44        occupied[position] = true;
45        positions.push(position);
46    }
47    Ok(occupied.into_iter().all(|slot| slot).then_some(positions))
48}
49
50pub(super) fn reorder_named_values(
51    function: &str,
52    call_args: &[(Option<String>, Value)],
53) -> Option<Vec<Value>> {
54    let argument_names = call_args
55        .iter()
56        .map(|(name, _)| name.as_deref())
57        .collect::<Vec<_>>();
58    let positions = argument_positions(function, &argument_names)
59        .ok()
60        .flatten()?;
61    let mut values = vec![None; call_args.len()];
62    for ((_, value), position) in call_args.iter().zip(positions) {
63        values[position] = Some(value.clone());
64    }
65    values.into_iter().collect()
66}