nodedb_sql/resolver/expr/
value.rs1use sqlparser::ast::Value;
4
5use crate::error::{Result, SqlError};
6use crate::types::*;
7
8pub fn convert_value(val: &Value) -> Result<SqlValue> {
22 match val {
23 Value::Number(n, _) => {
24 if let Ok(i) = n.parse::<i64>() {
25 Ok(SqlValue::Int(i))
26 } else if n.contains('e') || n.contains('E') {
27 match n.parse::<f64>() {
28 Ok(f) => Ok(SqlValue::Float(f)),
29 Err(_) => Ok(SqlValue::String(n.clone())),
30 }
31 } else if n.contains('.') {
32 if let Ok(d) = rust_decimal::Decimal::from_str_exact(n) {
34 Ok(SqlValue::Decimal(d))
35 } else if let Ok(f) = n.parse::<f64>() {
36 Ok(SqlValue::Float(f))
37 } else {
38 Ok(SqlValue::String(n.clone()))
39 }
40 } else if n.bytes().all(|b| b.is_ascii_digit()) {
41 integer_literal(n)
42 } else if let Ok(f) = n.parse::<f64>() {
43 Ok(SqlValue::Float(f))
44 } else {
45 Ok(SqlValue::String(n.clone()))
46 }
47 }
48 Value::SingleQuotedString(s) => Ok(SqlValue::String(s.clone())),
49 Value::Boolean(b) => Ok(SqlValue::Bool(*b)),
50 Value::Null => Ok(SqlValue::Null),
51 Value::HexStringLiteral(s) => Ok(SqlValue::Bytes(decode_hex_literal(s)?)),
54 _ => Err(SqlError::Unsupported {
55 detail: format!("value literal: {val}"),
56 }),
57 }
58}
59
60fn integer_literal(digits: &str) -> Result<SqlValue> {
62 if let Ok(u) = digits.parse::<u64>() {
63 return Ok(SqlValue::Decimal(rust_decimal::Decimal::from(u)));
64 }
65 rust_decimal::Decimal::from_str_exact(digits)
66 .map(SqlValue::Decimal)
67 .map_err(|_| SqlError::NumericLiteralOutOfRange {
68 literal: digits.to_string(),
69 })
70}
71
72fn decode_hex_literal(s: &str) -> Result<Vec<u8>> {
75 parse_hex_bytes(s).map_err(|reason| SqlError::Parse {
76 detail: format!("hex literal {reason}: X'{s}'"),
77 })
78}
79
80pub(crate) fn parse_hex_bytes(s: &str) -> std::result::Result<Vec<u8>, &'static str> {
86 if !s.len().is_multiple_of(2) {
87 return Err("must have an even number of digits");
88 }
89 (0..s.len())
90 .step_by(2)
91 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).map_err(|_| "contains an invalid hex digit"))
92 .collect()
93}
94
95pub(super) fn parse_interval_to_micros(s: &str) -> Option<i64> {
100 let s = s.trim();
101 if s.is_empty() {
102 return None;
103 }
104
105 if let Some(dur) = nodedb_types::NdbDuration::parse(s) {
107 return Some(dur.micros);
108 }
109
110 if let Ok(ms) = nodedb_types::kv_parsing::parse_interval_to_ms(s) {
112 return Some(ms as i64 * 1000); }
114
115 None
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 fn number(n: &str) -> Value {
123 Value::Number(n.to_string(), false)
124 }
125
126 #[test]
127 fn integer_literal_past_i64_stays_exact() {
128 assert_eq!(
129 convert_value(&number("9223372036854775807")).unwrap(),
130 SqlValue::Int(i64::MAX)
131 );
132 assert_eq!(
133 convert_value(&number("18446744073709551615")).unwrap(),
134 SqlValue::Decimal(rust_decimal::Decimal::from(u64::MAX))
135 );
136 assert_eq!(
138 convert_value(&number("79228162514264337593543950335")).unwrap(),
139 SqlValue::Decimal(rust_decimal::Decimal::MAX)
140 );
141 }
142
143 #[test]
144 fn an_exponent_literal_is_a_float_and_a_fraction_is_a_decimal() {
145 assert_eq!(
146 convert_value(&number("1.5e0")).unwrap(),
147 SqlValue::Float(1.5)
148 );
149 assert_eq!(convert_value(&number("2E0")).unwrap(), SqlValue::Float(2.0));
150 assert_eq!(
151 convert_value(&number("1e300")).unwrap(),
152 SqlValue::Float(1e300)
153 );
154 assert_eq!(
155 convert_value(&number("1.5")).unwrap(),
156 SqlValue::Decimal(rust_decimal::Decimal::new(15, 1))
157 );
158 }
159
160 #[test]
161 fn integer_literal_past_decimal_range_is_an_error() {
162 let err = convert_value(&number("79228162514264337593543950336")).unwrap_err();
163 assert!(
164 matches!(err, SqlError::NumericLiteralOutOfRange { ref literal }
165 if literal == "79228162514264337593543950336"),
166 "{err:?}"
167 );
168 }
169
170 #[test]
171 fn parse_interval_sql_word_forms() {
172 assert_eq!(parse_interval_to_micros("1 hour"), Some(3_600_000_000));
173 assert_eq!(parse_interval_to_micros("5 days"), Some(5 * 86_400_000_000));
174 assert_eq!(
175 parse_interval_to_micros("30 minutes"),
176 Some(30 * 60_000_000)
177 );
178 assert_eq!(
179 parse_interval_to_micros("2 hours 30 minutes"),
180 Some(9_000_000_000)
181 );
182 assert_eq!(parse_interval_to_micros("1 week"), Some(604_800_000_000));
183 assert_eq!(parse_interval_to_micros("100 milliseconds"), Some(100_000));
184 }
185
186 #[test]
187 fn parse_interval_shorthand() {
188 assert_eq!(parse_interval_to_micros("1h"), Some(3_600_000_000));
189 assert_eq!(parse_interval_to_micros("30m"), Some(30 * 60_000_000));
190 assert_eq!(parse_interval_to_micros("1h30m"), Some(5_400_000_000));
191 assert_eq!(parse_interval_to_micros("500ms"), Some(500_000));
192 }
193
194 #[test]
195 fn parse_interval_invalid() {
196 assert_eq!(parse_interval_to_micros(""), None);
197 assert_eq!(parse_interval_to_micros("abc"), None);
198 }
199}