1use std::cmp::Ordering;
2use std::convert::TryFrom;
3
4use crate::ast::ddl::VectorMetric;
5use crate::planner::ResolvedType;
6use serde::{Deserialize, Serialize};
7
8use super::error::{Result, StorageError};
9
10#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
12pub struct JsonValue(String);
13
14impl JsonValue {
15 pub fn parse(input: &str) -> std::result::Result<Self, serde_json::Error> {
16 let mut value: serde_json::Value = serde_json::from_str(input)?;
17 sort_json_keys(&mut value);
18 Ok(Self(
19 serde_json::to_string(&value).expect("JSON serialization cannot fail"),
20 ))
21 }
22
23 pub fn from_value(mut value: serde_json::Value) -> Self {
24 sort_json_keys(&mut value);
25 Self(serde_json::to_string(&value).expect("JSON serialization cannot fail"))
26 }
27
28 pub fn as_str(&self) -> &str {
29 &self.0
30 }
31
32 pub fn to_value(&self) -> serde_json::Value {
33 serde_json::from_str(&self.0).expect("JsonValue is validated on construction")
34 }
35}
36
37impl std::fmt::Display for JsonValue {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 f.write_str(&self.0)
40 }
41}
42
43fn sort_json_keys(value: &mut serde_json::Value) {
44 match value {
45 serde_json::Value::Array(values) => values.iter_mut().for_each(sort_json_keys),
46 serde_json::Value::Object(values) => {
47 let mut entries: Vec<_> = std::mem::take(values).into_iter().collect();
48 entries.sort_unstable_by(|left, right| left.0.cmp(&right.0));
49 for (key, mut value) in entries {
50 sort_json_keys(&mut value);
51 values.insert(key, value);
52 }
53 }
54 _ => {}
55 }
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
60pub struct DecimalValue {
61 pub coefficient: i128,
62 pub scale: u8,
63}
64
65impl DecimalValue {
66 pub fn new(coefficient: i128, scale: u8) -> Self {
67 Self { coefficient, scale }
68 }
69
70 pub fn parse(value: &str) -> Option<Self> {
71 let value = value.trim();
72 let (negative, unsigned) = match value.as_bytes().first() {
73 Some(b'-') => (true, &value[1..]),
74 Some(b'+') => (false, &value[1..]),
75 _ => (false, value),
76 };
77 let mut parts = unsigned.split('.');
78 let whole = parts.next()?;
79 let fraction = parts.next().unwrap_or("");
80 if parts.next().is_some()
81 || (whole.is_empty() && fraction.is_empty())
82 || !whole.bytes().all(|byte| byte.is_ascii_digit())
83 || !fraction.bytes().all(|byte| byte.is_ascii_digit())
84 || fraction.len() > 38
85 {
86 return None;
87 }
88 let digits = format!("{whole}{fraction}");
89 let coefficient = digits.parse::<i128>().ok()?;
90 Some(Self {
91 coefficient: if negative {
92 coefficient.checked_neg()?
93 } else {
94 coefficient
95 },
96 scale: fraction.len() as u8,
97 })
98 }
99
100 pub fn rescale(self, target_scale: u8) -> Option<Self> {
101 if target_scale == self.scale {
102 return Some(self);
103 }
104 if target_scale > self.scale {
105 return Some(Self::new(
106 self.coefficient
107 .checked_mul(decimal_power(target_scale - self.scale)?)?,
108 target_scale,
109 ));
110 }
111 let divisor = decimal_power(self.scale - target_scale)?;
112 let quotient = self.coefficient / divisor;
113 let remainder = self.coefficient % divisor;
114 let rounded = if remainder.abs().checked_mul(2)? >= divisor {
115 quotient.checked_add(self.coefficient.signum())?
116 } else {
117 quotient
118 };
119 Some(Self::new(rounded, target_scale))
120 }
121
122 pub fn fits_precision(self, precision: u8) -> bool {
123 decimal_digits(self.coefficient) <= usize::from(precision)
124 }
125
126 pub(crate) fn cmp_numeric(self, other: Self) -> Ordering {
127 if self.coefficient.signum() != other.coefficient.signum() {
128 return self.coefficient.signum().cmp(&other.coefficient.signum());
129 }
130 let negative = self.coefficient < 0;
131 let mut left = self.coefficient.unsigned_abs().to_string();
132 let mut right = other.coefficient.unsigned_abs().to_string();
133 let left_integer_digits = left.len() as i16 - i16::from(self.scale);
134 let right_integer_digits = right.len() as i16 - i16::from(other.scale);
135 let ordering = left_integer_digits
136 .cmp(&right_integer_digits)
137 .then_with(|| {
138 let scale = self.scale.max(other.scale);
139 left.extend(std::iter::repeat_n('0', usize::from(scale - self.scale)));
140 right.extend(std::iter::repeat_n('0', usize::from(scale - other.scale)));
141 left.cmp(&right)
142 });
143 if negative {
144 ordering.reverse()
145 } else {
146 ordering
147 }
148 }
149}
150
151pub(crate) fn decimal_power(scale: u8) -> Option<i128> {
152 10_i128.checked_pow(u32::from(scale))
153}
154
155fn decimal_digits(value: i128) -> usize {
156 value.unsigned_abs().to_string().len()
157}
158
159impl std::fmt::Display for DecimalValue {
160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161 let negative = self.coefficient < 0;
162 let digits = self.coefficient.unsigned_abs().to_string();
163 if self.scale == 0 {
164 return write!(f, "{}{}", if negative { "-" } else { "" }, digits);
165 }
166 let scale = usize::from(self.scale);
167 if digits.len() <= scale {
168 write!(
169 f,
170 "{}0.{:0>width$}",
171 if negative { "-" } else { "" },
172 digits,
173 width = scale
174 )
175 } else {
176 let split = digits.len() - scale;
177 write!(
178 f,
179 "{}{}.{}",
180 if negative { "-" } else { "" },
181 &digits[..split],
182 &digits[split..]
183 )
184 }
185 }
186}
187
188#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
190pub enum SqlValue {
191 Null,
192 Integer(i32),
193 BigInt(i64),
194 Float(f32),
195 Double(f64),
196 Text(String),
197 Blob(Vec<u8>),
198 Boolean(bool),
199 Timestamp(i64), Vector(Vec<f32>),
201 Date(i32), Time(i64), Interval { months: i32, days: i32, micros: i64 },
204 Decimal(DecimalValue),
205 Json(JsonValue),
206 Array(Vec<SqlValue>),
207 Map(Vec<(SqlValue, SqlValue)>),
208 Struct(Vec<(String, SqlValue)>),
209}
210
211impl SqlValue {
212 pub fn nested_json_text(&self) -> Option<String> {
214 matches!(self, Self::Array(_) | Self::Map(_) | Self::Struct(_))
215 .then(|| nested_json_value(self))?
216 .and_then(|value| serde_json::to_string(&value).ok())
217 }
218
219 pub fn temporal_text(&self) -> Option<String> {
221 match self {
222 Self::Date(days) => {
223 let epoch = chrono::NaiveDate::from_ymd_opt(1970, 1, 1).expect("valid epoch");
224 Some(
225 epoch
226 .checked_add_signed(chrono::Duration::days(i64::from(*days)))
227 .map(|date| date.format("%Y-%m-%d").to_string())
228 .unwrap_or_else(|| format!("{days} days since 1970-01-01")),
229 )
230 }
231 Self::Time(micros) => {
232 let formatted = u32::try_from(micros.div_euclid(1_000_000))
233 .ok()
234 .zip(u32::try_from(micros.rem_euclid(1_000_000)).ok())
235 .and_then(|(seconds, micros)| {
236 chrono::NaiveTime::from_num_seconds_from_midnight_opt(
237 seconds,
238 micros * 1_000,
239 )
240 })
241 .map(|time| time.format("%H:%M:%S%.6f").to_string())
242 .unwrap_or_else(|| format!("{micros} microseconds after midnight"));
243 Some(formatted)
244 }
245 Self::Interval {
246 months,
247 days,
248 micros,
249 } => Some(format!("{months} months {days} days {micros} microseconds")),
250 _ => None,
251 }
252 }
253
254 pub fn type_tag(&self) -> u8 {
256 match self {
257 SqlValue::Null => 0x00,
258 SqlValue::Integer(_) => 0x01,
259 SqlValue::BigInt(_) => 0x02,
260 SqlValue::Float(_) => 0x03,
261 SqlValue::Double(_) => 0x04,
262 SqlValue::Text(_) => 0x05,
263 SqlValue::Blob(_) => 0x06,
264 SqlValue::Boolean(_) => 0x07,
265 SqlValue::Timestamp(_) => 0x08,
266 SqlValue::Vector(_) => 0x09,
267 SqlValue::Date(_) => 0x0a,
268 SqlValue::Time(_) => 0x0b,
269 SqlValue::Interval { .. } => 0x0c,
270 SqlValue::Decimal(_) => 0x0d,
271 SqlValue::Json(_) => 0x0e,
272 SqlValue::Array(_) => 0x0f,
273 SqlValue::Map(_) => 0x10,
274 SqlValue::Struct(_) => 0x11,
275 }
276 }
277
278 pub fn is_null(&self) -> bool {
280 matches!(self, SqlValue::Null)
281 }
282
283 pub fn type_name(&self) -> &'static str {
285 match self {
286 SqlValue::Null => "Null",
287 SqlValue::Integer(_) => "Integer",
288 SqlValue::BigInt(_) => "BigInt",
289 SqlValue::Float(_) => "Float",
290 SqlValue::Double(_) => "Double",
291 SqlValue::Text(_) => "Text",
292 SqlValue::Blob(_) => "Blob",
293 SqlValue::Boolean(_) => "Boolean",
294 SqlValue::Timestamp(_) => "Timestamp",
295 SqlValue::Vector(_) => "Vector",
296 SqlValue::Date(_) => "Date",
297 SqlValue::Time(_) => "Time",
298 SqlValue::Interval { .. } => "Interval",
299 SqlValue::Decimal(_) => "Decimal",
300 SqlValue::Json(_) => "Json",
301 SqlValue::Array(_) => "Array",
302 SqlValue::Map(_) => "Map",
303 SqlValue::Struct(_) => "Struct",
304 }
305 }
306
307 pub fn resolved_type(&self) -> ResolvedType {
309 match self {
310 SqlValue::Null => ResolvedType::Null,
311 SqlValue::Integer(_) => ResolvedType::Integer,
312 SqlValue::BigInt(_) => ResolvedType::BigInt,
313 SqlValue::Float(_) => ResolvedType::Float,
314 SqlValue::Double(_) => ResolvedType::Double,
315 SqlValue::Text(_) => ResolvedType::Text,
316 SqlValue::Blob(_) => ResolvedType::Blob,
317 SqlValue::Boolean(_) => ResolvedType::Boolean,
318 SqlValue::Timestamp(_) => ResolvedType::Timestamp,
319 SqlValue::Vector(v) => ResolvedType::Vector {
320 dimension: v.len() as u32,
321 metric: VectorMetric::Cosine,
322 },
323 SqlValue::Date(_) => ResolvedType::Date,
324 SqlValue::Time(_) => ResolvedType::Time,
325 SqlValue::Interval { .. } => ResolvedType::Interval,
326 SqlValue::Decimal(value) => ResolvedType::Decimal {
327 precision: 38,
328 scale: value.scale,
329 },
330 SqlValue::Json(_) => ResolvedType::Json,
331 SqlValue::Array(values) => ResolvedType::Array(Box::new(
332 values
333 .iter()
334 .find(|value| !value.is_null())
335 .map(SqlValue::resolved_type)
336 .unwrap_or(ResolvedType::Null),
337 )),
338 SqlValue::Map(values) => ResolvedType::Map {
339 key: Box::new(
340 values
341 .iter()
342 .find(|(key, _)| !key.is_null())
343 .map(|(key, _)| key.resolved_type())
344 .unwrap_or(ResolvedType::Null),
345 ),
346 value: Box::new(
347 values
348 .iter()
349 .find(|(_, value)| !value.is_null())
350 .map(|(_, value)| value.resolved_type())
351 .unwrap_or(ResolvedType::Null),
352 ),
353 },
354 SqlValue::Struct(values) => ResolvedType::Struct(
355 values
356 .iter()
357 .map(|(name, value)| (name.clone(), value.resolved_type()))
358 .collect(),
359 ),
360 }
361 }
362}
363
364fn nested_json_value(value: &SqlValue) -> Option<serde_json::Value> {
365 use serde_json::{Map, Number, Value};
366 Some(match value {
367 SqlValue::Null => Value::Null,
368 SqlValue::Integer(value) => Value::Number(Number::from(*value)),
369 SqlValue::BigInt(value) => Value::Number(Number::from(*value)),
370 SqlValue::Float(value) => Value::Number(Number::from_f64(f64::from(*value))?),
371 SqlValue::Double(value) => Value::Number(Number::from_f64(*value)?),
372 SqlValue::Text(value) => Value::String(value.clone()),
373 SqlValue::Boolean(value) => Value::Bool(*value),
374 SqlValue::Decimal(value) => serde_json::from_str(&value.to_string()).ok()?,
375 SqlValue::Json(value) => value.to_value(),
376 SqlValue::Array(values) => Value::Array(
377 values
378 .iter()
379 .map(nested_json_value)
380 .collect::<Option<Vec<_>>>()?,
381 ),
382 SqlValue::Map(values) => {
383 if values
384 .iter()
385 .all(|(key, _)| matches!(key, SqlValue::Text(_)))
386 {
387 let mut output = Map::new();
388 for (key, value) in values {
389 let SqlValue::Text(key) = key else {
390 unreachable!()
391 };
392 output.insert(key.clone(), nested_json_value(value)?);
393 }
394 Value::Object(output)
395 } else {
396 Value::Array(
397 values
398 .iter()
399 .map(|(key, value)| {
400 Some(Value::Array(vec![
401 nested_json_value(key)?,
402 nested_json_value(value)?,
403 ]))
404 })
405 .collect::<Option<Vec<_>>>()?,
406 )
407 }
408 }
409 SqlValue::Struct(values) => Value::Object(
410 values
411 .iter()
412 .map(|(name, value)| Some((name.clone(), nested_json_value(value)?)))
413 .collect::<Option<Map<_, _>>>()?,
414 ),
415 SqlValue::Date(_) | SqlValue::Time(_) | SqlValue::Interval { .. } => {
416 Value::String(value.temporal_text()?)
417 }
418 SqlValue::Timestamp(value) => Value::Number(Number::from(*value)),
419 SqlValue::Vector(values) => Value::Array(
420 values
421 .iter()
422 .map(|value| Number::from_f64(f64::from(*value)).map(Value::Number))
423 .collect::<Option<Vec<_>>>()?,
424 ),
425 SqlValue::Blob(_) => return None,
426 })
427}
428
429impl PartialOrd for SqlValue {
430 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
431 use SqlValue::*;
432 match (self, other) {
433 (Null, _) | (_, Null) => None,
434 (Integer(a), Integer(b)) => Some(a.cmp(b)),
435 (BigInt(a), BigInt(b)) => Some(a.cmp(b)),
436 (Float(a), Float(b)) => a.partial_cmp(b),
437 (Double(a), Double(b)) => a.partial_cmp(b),
438 (Text(a), Text(b)) => Some(a.cmp(b)),
439 (Blob(a), Blob(b)) => Some(a.cmp(b)),
440 (Boolean(a), Boolean(b)) => Some(a.cmp(b)),
441 (Timestamp(a), Timestamp(b)) => Some(a.cmp(b)),
442 (Date(a), Date(b)) => Some(a.cmp(b)),
443 (Time(a), Time(b)) => Some(a.cmp(b)),
444 (
445 Interval {
446 months: am,
447 days: ad,
448 micros: au,
449 },
450 Interval {
451 months: bm,
452 days: bd,
453 micros: bu,
454 },
455 ) => Some((am, ad, au).cmp(&(bm, bd, bu))),
456 (Decimal(a), Decimal(b)) => Some(a.cmp_numeric(*b)),
457 (Json(a), Json(b)) if a == b => Some(Ordering::Equal),
458 (Json(_), Json(_)) => None,
459 (Vector(_), Vector(_)) => None,
461 _ => None,
462 }
463 }
464}
465
466macro_rules! impl_from_sqlvalue_try {
467 ($target:ty, $variant:ident) => {
468 impl TryFrom<SqlValue> for $target {
469 type Error = StorageError;
470 fn try_from(value: SqlValue) -> Result<Self> {
471 if let SqlValue::$variant(v) = value {
472 Ok(v.into())
473 } else {
474 Err(StorageError::TypeMismatch {
475 expected: stringify!($variant).to_string(),
476 actual: value.type_name().to_string(),
477 })
478 }
479 }
480 }
481 };
482}
483
484macro_rules! impl_from_primitive {
485 ($source:ty, $variant:ident) => {
486 impl From<$source> for SqlValue {
487 fn from(value: $source) -> Self {
488 SqlValue::$variant(value.into())
489 }
490 }
491 };
492}
493
494impl_from_primitive!(i32, Integer);
495impl_from_primitive!(i64, BigInt);
496impl_from_primitive!(f32, Float);
497impl_from_primitive!(f64, Double);
498impl_from_primitive!(bool, Boolean);
499impl_from_primitive!(String, Text);
500impl_from_primitive!(&str, Text);
501impl_from_primitive!(Vec<u8>, Blob);
502impl From<&[u8]> for SqlValue {
503 fn from(value: &[u8]) -> Self {
504 SqlValue::Blob(value.to_vec())
505 }
506}
507impl From<Vec<f32>> for SqlValue {
508 fn from(value: Vec<f32>) -> Self {
509 SqlValue::Vector(value)
510 }
511}
512
513impl_from_sqlvalue_try!(i32, Integer);
514impl_from_sqlvalue_try!(f32, Float);
515impl_from_sqlvalue_try!(f64, Double);
516impl_from_sqlvalue_try!(bool, Boolean);
517impl_from_sqlvalue_try!(String, Text);
518impl_from_sqlvalue_try!(Vec<u8>, Blob);
519
520impl TryFrom<SqlValue> for i64 {
521 type Error = StorageError;
522 fn try_from(value: SqlValue) -> Result<Self> {
523 match value {
524 SqlValue::BigInt(v) | SqlValue::Timestamp(v) => Ok(v),
525 other => Err(StorageError::TypeMismatch {
526 expected: "BigInt/Timestamp".to_string(),
527 actual: other.type_name().to_string(),
528 }),
529 }
530 }
531}
532
533impl TryFrom<SqlValue> for Vec<f32> {
534 type Error = StorageError;
535 fn try_from(value: SqlValue) -> Result<Self> {
536 if let SqlValue::Vector(v) = value {
537 Ok(v)
538 } else {
539 Err(StorageError::TypeMismatch {
540 expected: "Vector".to_string(),
541 actual: value.type_name().to_string(),
542 })
543 }
544 }
545}
546
547impl TryFrom<&SqlValue> for ResolvedType {
548 type Error = StorageError;
549 fn try_from(value: &SqlValue) -> Result<Self> {
550 Ok(value.resolved_type())
551 }
552}
553
554#[cfg(test)]
555mod tests {
556 use super::*;
557 use proptest::prelude::*;
558
559 #[test]
560 fn null_comparisons_return_none() {
561 assert!(SqlValue::Null.partial_cmp(&SqlValue::Null).is_none());
562 assert!(SqlValue::Null.partial_cmp(&SqlValue::Integer(1)).is_none());
563 }
564
565 #[test]
566 fn heterogeneous_comparisons_return_none() {
567 assert!(
568 SqlValue::Integer(1)
569 .partial_cmp(&SqlValue::Double(1.0))
570 .is_none()
571 );
572 assert!(
573 SqlValue::Text("a".into())
574 .partial_cmp(&SqlValue::Blob(vec![0x61]))
575 .is_none()
576 );
577 }
578
579 #[test]
580 fn temporal_text_preserves_out_of_range_public_values() {
581 assert_eq!(
582 SqlValue::Date(i32::MAX).temporal_text().as_deref(),
583 Some("2147483647 days since 1970-01-01")
584 );
585 assert_eq!(
586 SqlValue::Time(-1).temporal_text().as_deref(),
587 Some("-1 microseconds after midnight")
588 );
589 }
590
591 #[test]
592 fn same_type_comparisons_work() {
593 assert_eq!(
594 SqlValue::Integer(1).partial_cmp(&SqlValue::Integer(2)),
595 Some(Ordering::Less)
596 );
597 assert_eq!(
598 SqlValue::Text("b".into()).partial_cmp(&SqlValue::Text("a".into())),
599 Some(Ordering::Greater)
600 );
601 assert_eq!(
602 SqlValue::Boolean(false).partial_cmp(&SqlValue::Boolean(true)),
603 Some(Ordering::Less)
604 );
605 }
606
607 proptest! {
608 #[test]
609 fn integer_roundtrip(v in any::<i32>()) {
610 let sql: SqlValue = v.into();
611 let back = i32::try_from(sql.clone()).unwrap();
612 prop_assert_eq!(back, v);
613 prop_assert_eq!(sql.partial_cmp(&SqlValue::Integer(v)), Some(Ordering::Equal));
614 }
615
616 #[test]
617 fn bigint_roundtrip(v in any::<i64>()) {
618 let sql: SqlValue = SqlValue::BigInt(v);
619 let back = i64::try_from(sql.clone()).unwrap();
620 prop_assert_eq!(back, v);
621 }
622
623 #[test]
624 fn float_roundtrip_non_nan(v in any::<f32>().prop_filter("no NaN", |f| f.is_finite())) {
625 let sql: SqlValue = SqlValue::Float(v);
626 let back = f32::try_from(sql.clone()).unwrap();
627 prop_assert_eq!(back, v);
628 prop_assert_eq!(sql.partial_cmp(&SqlValue::Float(v)), Some(Ordering::Equal));
629 }
630
631 #[test]
632 fn text_roundtrip(s in ".*") {
633 let sql: SqlValue = SqlValue::Text(s.clone());
634 let back = String::try_from(sql.clone()).unwrap();
635 prop_assert_eq!(back, s.clone());
636 prop_assert_eq!(sql.partial_cmp(&SqlValue::Text(s)), Some(Ordering::Equal));
637 }
638
639 #[test]
640 fn blob_roundtrip(data in proptest::collection::vec(any::<u8>(), 0..64)) {
641 let sql: SqlValue = SqlValue::Blob(data.clone());
642 let back = Vec::<u8>::try_from(sql.clone()).unwrap();
643 prop_assert_eq!(back, data.clone());
644 prop_assert_eq!(sql.partial_cmp(&SqlValue::Blob(data)), Some(Ordering::Equal));
645 }
646 }
647}