uqa_sql/semantics/partition/
order.rs1use super::PartitionContext;
10use crate::{
11 ast::{PartitionBound, PartitionRangeDatum},
12 SQLError,
13};
14use uqa_core::Value;
15
16#[cfg(test)]
17mod tests;
18
19#[derive(PartialEq, Eq, PartialOrd, Ord)]
20enum RangePoint {
21 MinValue,
22 Value(Value),
23 MaxValue,
24}
25
26#[derive(PartialEq, Eq, PartialOrd, Ord)]
28enum Position {
29 Range(Vec<RangePoint>),
31 List(Value),
33 Hash(i32, i32),
35 NullList,
37 Default,
38}
39
40fn position(context: &PartitionContext<'_>, bound: &PartitionBound) -> Result<Position, SQLError> {
41 let evaluate = |expression| context.expressions.evaluate_bound(expression, &[]);
42 Ok(match bound {
43 PartitionBound::Default => Position::Default,
44 PartitionBound::Range { lower, .. } => Position::Range(
45 lower
46 .iter()
47 .map(|datum| {
48 Ok(match datum {
49 PartitionRangeDatum::MinValue => RangePoint::MinValue,
50 PartitionRangeDatum::Value(expression) => {
51 RangePoint::Value(evaluate(expression)?)
52 }
53 PartitionRangeDatum::MaxValue => RangePoint::MaxValue,
54 })
55 })
56 .collect::<Result<_, SQLError>>()?,
57 ),
58 PartitionBound::List(values) => {
59 let mut smallest = None;
60 for value in values {
61 let value = evaluate(value)?;
62 if value != Value::Null && smallest.as_ref().is_none_or(|current| value < *current)
63 {
64 smallest = Some(value);
65 }
66 }
67 smallest.map_or(Position::NullList, Position::List)
68 }
69 PartitionBound::Hash { modulus, remainder } => Position::Hash(*modulus, *remainder),
70 })
71}
72
73pub fn partition_bound_order(
75 context: &PartitionContext<'_>,
76 partitions: Vec<(String, PartitionBound)>,
77) -> Result<Vec<String>, SQLError> {
78 let mut positioned = partitions
79 .into_iter()
80 .map(|(name, bound)| Ok((position(context, &bound)?, name)))
81 .collect::<Result<Vec<_>, SQLError>>()?;
82 positioned.sort_by(|left, right| left.0.cmp(&right.0));
83 Ok(positioned.into_iter().map(|(_, name)| name).collect())
84}