uqa_sql/expr/enums/
functions.rs1use std::cmp::Ordering;
10
11use uqa_core::{EnumValue, Value};
12
13use super::{enum_endpoint, enum_label, enum_range, EnumLabelCatalog};
14use crate::ast::EnumFunctionOperation;
15use crate::error::{Result, SQLError};
16use crate::expr::hashing::hash_bytes_uint32_extended;
17
18fn enum_argument(value: &Value, type_oid: u32) -> Result<Option<&EnumValue>> {
19 match value {
20 Value::Null => Ok(None),
21 Value::Enum(label) if label.type_oid() == type_oid => Ok(Some(label)),
22 other => Err(SQLError::Internal(format!(
23 "enum support function bound to type OID {type_oid} received {other:?}"
24 ))),
25 }
26}
27
28fn ordering_value(ordering: Ordering) -> Value {
29 Value::Int(match ordering {
30 Ordering::Less => -1,
31 Ordering::Equal => 0,
32 Ordering::Greater => 1,
33 })
34}
35
36pub fn enum_function_value(
38 catalog: Option<&dyn EnumLabelCatalog>,
39 operation: EnumFunctionOperation,
40 type_oid: u32,
41 arguments: &[Value],
42) -> Result<Value> {
43 let arity = match operation {
44 EnumFunctionOperation::First
45 | EnumFunctionOperation::Last
46 | EnumFunctionOperation::Range
47 | EnumFunctionOperation::Hash => 1,
48 _ => 2,
49 };
50 if arguments.len() != arity {
51 return Err(SQLError::Internal(format!(
52 "{} received {} arguments",
53 operation.label(),
54 arguments.len()
55 )));
56 }
57 if operation.is_strict() && arguments.iter().any(|value| matches!(value, Value::Null)) {
58 return Ok(Value::Null);
59 }
60 let first = enum_argument(&arguments[0], type_oid)?;
61 let lost_argument =
62 || SQLError::Internal(format!("{} lost a strict argument", operation.label()));
63 match operation {
64 EnumFunctionOperation::First => enum_endpoint(catalog, type_oid, false),
65 EnumFunctionOperation::Last => enum_endpoint(catalog, type_oid, true),
66 EnumFunctionOperation::Range => enum_range(catalog, type_oid, None, None),
67 EnumFunctionOperation::BoundedRange => enum_range(
68 catalog,
69 type_oid,
70 first,
71 enum_argument(&arguments[1], type_oid)?,
72 ),
73 EnumFunctionOperation::Hash => {
75 let oid = enum_label(catalog, first.ok_or_else(lost_argument)?)?.oid;
76 let hash = hash_bytes_uint32_extended(oid, 0) as u32;
77 Ok(Value::Int(i64::from(hash as i32)))
78 }
79 EnumFunctionOperation::ExtendedHash => {
80 let Value::Int(seed) = arguments[1] else {
81 return Err(SQLError::Internal(format!(
82 "hashenumextended received seed {:?}",
83 arguments[1]
84 )));
85 };
86 let oid = enum_label(catalog, first.ok_or_else(lost_argument)?)?.oid;
87 Ok(Value::Int(
88 hash_bytes_uint32_extended(oid, seed as u64) as i64
89 ))
90 }
91 _ => {
92 let (Some(left), Some(right)) = (first, enum_argument(&arguments[1], type_oid)?) else {
93 return Err(lost_argument());
94 };
95 comparison_value(operation, left, right)
96 }
97 }
98}
99
100fn comparison_value(
102 operation: EnumFunctionOperation,
103 left: &EnumValue,
104 right: &EnumValue,
105) -> Result<Value> {
106 let ordering = left.key().cmp(right.key());
107 Ok(match operation {
108 EnumFunctionOperation::Compare => ordering_value(ordering),
109 EnumFunctionOperation::Equal => Value::Bool(ordering.is_eq()),
110 EnumFunctionOperation::NotEqual => Value::Bool(ordering.is_ne()),
111 EnumFunctionOperation::Less => Value::Bool(ordering.is_lt()),
112 EnumFunctionOperation::Greater => Value::Bool(ordering.is_gt()),
113 EnumFunctionOperation::LessEqual => Value::Bool(ordering.is_le()),
114 EnumFunctionOperation::GreaterEqual => Value::Bool(ordering.is_ge()),
115 EnumFunctionOperation::Smaller => {
116 Value::Enum(if ordering.is_le() { left } else { right }.clone())
117 }
118 EnumFunctionOperation::Larger => {
119 Value::Enum(if ordering.is_ge() { left } else { right }.clone())
120 }
121 other => {
122 return Err(SQLError::Internal(format!(
123 "{} is not an enum comparison",
124 other.label()
125 )))
126 }
127 })
128}