Skip to main content

hematite/catalog/
types.rs

1//! Data types and runtime values for the relational layer.
2
3use std::cmp::Ordering;
4use std::fmt;
5
6use crate::error::{HematiteError, Result};
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash)]
9pub enum DataType {
10    Int8,
11    Int16,
12    Int,
13    Int64,
14    Int128,
15    UInt8,
16    UInt16,
17    UInt,
18    UInt64,
19    UInt128,
20    Text,
21    Char(u32),
22    VarChar(u32),
23    Binary(u32),
24    VarBinary(u32),
25    Enum(Vec<String>),
26    Boolean,
27    Float32,
28    Float,
29    Decimal {
30        precision: Option<u32>,
31        scale: Option<u32>,
32    },
33    Blob,
34    Date,
35    Time,
36    DateTime,
37    TimeWithTimeZone,
38    IntervalYearMonth,
39    IntervalDaySecond,
40}
41
42impl DataType {
43    pub fn size(&self) -> usize {
44        match self {
45            DataType::Int8 => 1,
46            DataType::Int16 => 2,
47            DataType::Int => 4,
48            DataType::Int64 => 8,
49            DataType::Int128 => 16,
50            DataType::UInt8 => 1,
51            DataType::UInt16 => 2,
52            DataType::UInt => 4,
53            DataType::UInt64 => 8,
54            DataType::UInt128 => 16,
55            DataType::Text => 255,
56            DataType::Char(length) | DataType::VarChar(length) => *length as usize,
57            DataType::Binary(length) | DataType::VarBinary(length) => *length as usize,
58            DataType::Enum(values) => values.iter().map(|value| value.len()).max().unwrap_or(0),
59            DataType::Boolean => 1,
60            DataType::Float32 => 4,
61            DataType::Float => 8,
62            DataType::Decimal { precision, .. } => precision.unwrap_or(32) as usize,
63            DataType::Blob => 4096,
64            DataType::Date => 4,
65            DataType::Time => 4,
66            DataType::DateTime => 8,
67            DataType::TimeWithTimeZone => 6,
68            DataType::IntervalYearMonth => 4,
69            DataType::IntervalDaySecond => 8,
70        }
71    }
72
73    pub fn name(&self) -> String {
74        match self {
75            DataType::Int8 => "INT8".to_string(),
76            DataType::Int16 => "INT16".to_string(),
77            DataType::Int => "INT".to_string(),
78            DataType::Int64 => "INT64".to_string(),
79            DataType::Int128 => "INT128".to_string(),
80            DataType::UInt8 => "UINT8".to_string(),
81            DataType::UInt16 => "UINT16".to_string(),
82            DataType::UInt => "UINT".to_string(),
83            DataType::UInt64 => "UINT64".to_string(),
84            DataType::UInt128 => "UINT128".to_string(),
85            DataType::Text => "TEXT".to_string(),
86            DataType::Char(length) => format!("CHAR({length})"),
87            DataType::VarChar(length) => format!("VARCHAR({length})"),
88            DataType::Binary(length) => format!("BINARY({length})"),
89            DataType::VarBinary(length) => format!("VARBINARY({length})"),
90            DataType::Enum(values) => format!(
91                "ENUM({})",
92                values
93                    .iter()
94                    .map(|value| format!("'{}'", value.replace('\'', "''")))
95                    .collect::<Vec<_>>()
96                    .join(", ")
97            ),
98            DataType::Boolean => "BOOLEAN".to_string(),
99            DataType::Float32 => "FLOAT32".to_string(),
100            DataType::Float => "FLOAT".to_string(),
101            DataType::Decimal { precision, scale } => {
102                format_numeric_type("DECIMAL", *precision, *scale)
103            }
104            DataType::Blob => "BLOB".to_string(),
105            DataType::Date => "DATE".to_string(),
106            DataType::Time => "TIME".to_string(),
107            DataType::DateTime => "DATETIME".to_string(),
108            DataType::TimeWithTimeZone => "TIME WITH TIME ZONE".to_string(),
109            DataType::IntervalYearMonth => "INTERVAL YEAR TO MONTH".to_string(),
110            DataType::IntervalDaySecond => "INTERVAL DAY TO SECOND".to_string(),
111        }
112    }
113
114    pub fn base_name(&self) -> &'static str {
115        match self {
116            DataType::Int8 => "INT8",
117            DataType::Int16 => "INT16",
118            DataType::Int => "INT",
119            DataType::Int64 => "INT64",
120            DataType::Int128 => "INT128",
121            DataType::UInt8 => "UINT8",
122            DataType::UInt16 => "UINT16",
123            DataType::UInt => "UINT",
124            DataType::UInt64 => "UINT64",
125            DataType::UInt128 => "UINT128",
126            DataType::Text => "TEXT",
127            DataType::Char(_) => "CHAR",
128            DataType::VarChar(_) => "VARCHAR",
129            DataType::Binary(_) => "BINARY",
130            DataType::VarBinary(_) => "VARBINARY",
131            DataType::Enum(_) => "ENUM",
132            DataType::Boolean => "BOOLEAN",
133            DataType::Float32 => "FLOAT32",
134            DataType::Float => "FLOAT",
135            DataType::Decimal { .. } => "DECIMAL",
136            DataType::Blob => "BLOB",
137            DataType::Date => "DATE",
138            DataType::Time => "TIME",
139            DataType::DateTime => "DATETIME",
140            DataType::TimeWithTimeZone => "TIME WITH TIME ZONE",
141            DataType::IntervalYearMonth => "INTERVAL YEAR TO MONTH",
142            DataType::IntervalDaySecond => "INTERVAL DAY TO SECOND",
143        }
144    }
145
146    pub fn decimal_constraints(&self) -> Option<(Option<u32>, Option<u32>)> {
147        match self {
148            DataType::Decimal { precision, scale } => Some((*precision, *scale)),
149            _ => None,
150        }
151    }
152}
153
154fn format_numeric_type(name: &str, precision: Option<u32>, scale: Option<u32>) -> String {
155    match (precision, scale) {
156        (Some(precision), Some(scale)) => format!("{name}({precision}, {scale})"),
157        (Some(precision), None) => format!("{name}({precision})"),
158        (None, _) => name.to_string(),
159    }
160}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq)]
163pub enum JournalMode {
164    Rollback,
165    Wal,
166}
167
168#[derive(Debug, Clone, PartialEq, Eq, Hash)]
169pub struct DecimalValue {
170    negative: bool,
171    digits: Vec<u8>,
172    scale: u32,
173}
174
175impl DecimalValue {
176    pub fn zero() -> Self {
177        Self {
178            negative: false,
179            digits: vec![0],
180            scale: 0,
181        }
182    }
183
184    pub fn parse(input: &str) -> Result<Self> {
185        let trimmed = input.trim();
186        if trimmed.is_empty() {
187            return Err(HematiteError::ParseError(
188                "Decimal value cannot be empty".to_string(),
189            ));
190        }
191
192        let (negative, digits) = match trimmed.as_bytes()[0] {
193            b'+' => (false, &trimmed[1..]),
194            b'-' => (true, &trimmed[1..]),
195            _ => (false, trimmed),
196        };
197
198        if digits.is_empty() {
199            return Err(HematiteError::ParseError(format!(
200                "Invalid decimal value '{}'",
201                input
202            )));
203        }
204
205        let mut parts = digits.split('.');
206        let integer = parts.next().unwrap_or_default();
207        let fraction = parts.next();
208        if parts.next().is_some()
209            || !integer.chars().all(|ch| ch.is_ascii_digit())
210            || fraction.is_some_and(|part| !part.chars().all(|ch| ch.is_ascii_digit()))
211        {
212            return Err(HematiteError::ParseError(format!(
213                "Invalid decimal value '{}'",
214                input
215            )));
216        }
217
218        let integer = integer.trim_start_matches('0');
219        let integer = if integer.is_empty() { "0" } else { integer };
220        let mut fraction = fraction.unwrap_or_default().to_string();
221        while fraction.ends_with('0') {
222            fraction.pop();
223        }
224
225        let mut combined = String::with_capacity(integer.len() + fraction.len());
226        combined.push_str(integer);
227        combined.push_str(&fraction);
228        let combined = combined.trim_start_matches('0');
229        let digits = if combined.is_empty() {
230            vec![0]
231        } else {
232            combined.bytes().map(|byte| byte - b'0').collect()
233        };
234
235        let negative = negative && !(digits.len() == 1 && digits[0] == 0);
236
237        Ok(Self {
238            negative,
239            digits,
240            scale: fraction.len() as u32,
241        })
242    }
243
244    pub fn from_i32(value: i32) -> Self {
245        Self::parse(&value.to_string()).expect("i32 string is always a valid decimal")
246    }
247
248    pub fn from_i64(value: i64) -> Self {
249        Self::parse(&value.to_string()).expect("i64 string is always a valid decimal")
250    }
251
252    pub fn from_i128(value: i128) -> Self {
253        Self::parse(&value.to_string()).expect("i128 string is always a valid decimal")
254    }
255
256    pub fn from_u32(value: u32) -> Self {
257        Self::parse(&value.to_string()).expect("u32 string is always a valid decimal")
258    }
259
260    pub fn from_u64(value: u64) -> Self {
261        Self::parse(&value.to_string()).expect("u64 string is always a valid decimal")
262    }
263
264    pub fn from_u128(value: u128) -> Self {
265        Self::parse(&value.to_string()).expect("u128 string is always a valid decimal")
266    }
267
268    pub fn from_f64(value: f64) -> Result<Self> {
269        if !value.is_finite() {
270            return Err(HematiteError::ParseError(
271                "Decimal value must be finite".to_string(),
272            ));
273        }
274        Self::parse(&value.to_string())
275    }
276
277    pub fn is_integral(&self) -> bool {
278        self.scale == 0
279    }
280
281    pub fn add(&self, other: &Self) -> Self {
282        let target_scale = self.scale.max(other.scale);
283        let left = scale_decimal_digits(&self.digits, self.scale, target_scale);
284        let right = scale_decimal_digits(&other.digits, other.scale, target_scale);
285
286        if self.negative == other.negative {
287            normalize_decimal_parts(
288                self.negative,
289                add_digit_vectors(&left, &right),
290                target_scale,
291            )
292        } else {
293            match compare_digit_vectors(&left, &right) {
294                Ordering::Greater => normalize_decimal_parts(
295                    self.negative,
296                    subtract_digit_vectors(&left, &right),
297                    target_scale,
298                ),
299                Ordering::Less => normalize_decimal_parts(
300                    other.negative,
301                    subtract_digit_vectors(&right, &left),
302                    target_scale,
303                ),
304                Ordering::Equal => Self::zero(),
305            }
306        }
307    }
308
309    pub fn subtract(&self, other: &Self) -> Self {
310        if other.is_zero() {
311            return self.clone();
312        }
313
314        let mut negated = other.clone();
315        negated.negative = !negated.negative;
316        self.add(&negated)
317    }
318
319    pub fn multiply(&self, other: &Self) -> Self {
320        normalize_decimal_parts(
321            self.negative ^ other.negative,
322            multiply_digit_vectors(&self.digits, &other.digits),
323            self.scale + other.scale,
324        )
325    }
326
327    pub fn divide(&self, other: &Self) -> Result<Self> {
328        if other.is_zero() {
329            return Err(HematiteError::ParseError("Division by zero".to_string()));
330        }
331
332        const DECIMAL_DIVISION_SCALE: u32 = 18;
333
334        let mut numerator = self.digits.clone();
335        numerator.resize(
336            numerator.len() + other.scale as usize + DECIMAL_DIVISION_SCALE as usize,
337            0,
338        );
339        let mut denominator = other.digits.clone();
340        denominator.resize(denominator.len() + self.scale as usize, 0);
341
342        let (mut quotient, remainder) = divide_digit_vectors(&numerator, &denominator);
343        if !is_zero_digit_vector(&remainder) {
344            let doubled_remainder = add_digit_vectors(&remainder, &remainder);
345            if compare_digit_vectors(&doubled_remainder, &denominator) != Ordering::Less {
346                quotient = increment_digit_vector(&quotient);
347            }
348        }
349
350        Ok(normalize_decimal_parts(
351            self.negative ^ other.negative,
352            quotient,
353            DECIMAL_DIVISION_SCALE,
354        ))
355    }
356
357    pub fn remainder(&self, other: &Self) -> Result<Self> {
358        if other.is_zero() {
359            return Err(HematiteError::ParseError("Division by zero".to_string()));
360        }
361
362        let target_scale = self.scale.max(other.scale);
363        let left = scale_decimal_digits(&self.digits, self.scale, target_scale);
364        let right = scale_decimal_digits(&other.digits, other.scale, target_scale);
365        let (_, remainder) = divide_digit_vectors(&left, &right);
366        Ok(normalize_decimal_parts(
367            self.negative,
368            remainder,
369            target_scale,
370        ))
371    }
372
373    pub fn negate(&self) -> Self {
374        if self.is_zero() {
375            Self::zero()
376        } else {
377            let mut negated = self.clone();
378            negated.negative = !negated.negative;
379            negated
380        }
381    }
382
383    pub fn precision(&self) -> u32 {
384        self.digits.len() as u32
385    }
386
387    pub fn scale(&self) -> u32 {
388        self.scale
389    }
390
391    pub fn is_zero(&self) -> bool {
392        self.digits.len() == 1 && self.digits[0] == 0
393    }
394
395    pub fn fits_precision_scale(&self, precision: Option<u32>, scale: Option<u32>) -> bool {
396        if let Some(scale) = scale {
397            if self.scale > scale {
398                return false;
399            }
400        }
401
402        if let Some(precision) = precision {
403            let max_digits = precision;
404            let digits = self.precision();
405            if digits > max_digits {
406                return false;
407            }
408            if let Some(scale) = scale {
409                let integer_digits = digits.saturating_sub(self.scale).max(1);
410                let max_integer_digits = precision.saturating_sub(scale).max(1);
411                if integer_digits > max_integer_digits {
412                    return false;
413                }
414            }
415        }
416
417        true
418    }
419
420    pub fn digit_bytes(&self) -> &[u8] {
421        &self.digits
422    }
423
424    pub fn negative(&self) -> bool {
425        self.negative
426    }
427
428    pub fn to_f64(&self) -> Option<f64> {
429        self.to_string().parse::<f64>().ok()
430    }
431
432    pub fn to_integral_u128(&self) -> Option<u128> {
433        if !self.is_integral() || self.negative {
434            return None;
435        }
436
437        let mut value = 0u128;
438        for digit in &self.digits {
439            value = value.checked_mul(10)?.checked_add(*digit as u128)?;
440        }
441        Some(value)
442    }
443
444    pub fn to_integral_i128(&self) -> Option<i128> {
445        if !self.is_integral() {
446            return None;
447        }
448
449        let magnitude = self.to_integral_u128_abs()?;
450        if self.negative {
451            if magnitude == (i128::MAX as u128) + 1 {
452                Some(i128::MIN)
453            } else {
454                i128::try_from(magnitude).ok().map(|value| -value)
455            }
456        } else {
457            i128::try_from(magnitude).ok()
458        }
459    }
460
461    fn to_integral_u128_abs(&self) -> Option<u128> {
462        if !self.is_integral() {
463            return None;
464        }
465
466        let mut value = 0u128;
467        for digit in &self.digits {
468            value = value.checked_mul(10)?.checked_add(*digit as u128)?;
469        }
470        Some(value)
471    }
472}
473
474fn normalize_decimal_parts(negative: bool, mut digits: Vec<u8>, mut scale: u32) -> DecimalValue {
475    trim_leading_digit_zeros(&mut digits);
476    while scale > 0 && digits.len() > 1 && digits.last() == Some(&0) {
477        digits.pop();
478        scale -= 1;
479    }
480    trim_leading_digit_zeros(&mut digits);
481    if is_zero_digit_vector(&digits) {
482        return DecimalValue::zero();
483    }
484
485    DecimalValue {
486        negative,
487        digits,
488        scale,
489    }
490}
491
492fn scale_decimal_digits(digits: &[u8], scale: u32, target_scale: u32) -> Vec<u8> {
493    let mut scaled = digits.to_vec();
494    scaled.resize(
495        scaled.len() + target_scale.saturating_sub(scale) as usize,
496        0,
497    );
498    scaled
499}
500
501fn compare_digit_vectors(left: &[u8], right: &[u8]) -> Ordering {
502    left.len().cmp(&right.len()).then_with(|| left.cmp(right))
503}
504
505fn add_digit_vectors(left: &[u8], right: &[u8]) -> Vec<u8> {
506    let mut result = Vec::with_capacity(left.len().max(right.len()) + 1);
507    let mut carry = 0u8;
508    let mut left_index = left.len();
509    let mut right_index = right.len();
510
511    while left_index > 0 || right_index > 0 || carry > 0 {
512        let left_digit = if left_index > 0 {
513            left_index -= 1;
514            left[left_index]
515        } else {
516            0
517        };
518        let right_digit = if right_index > 0 {
519            right_index -= 1;
520            right[right_index]
521        } else {
522            0
523        };
524        let total = left_digit + right_digit + carry;
525        result.push(total % 10);
526        carry = total / 10;
527    }
528
529    result.reverse();
530    result
531}
532
533fn subtract_digit_vectors(left: &[u8], right: &[u8]) -> Vec<u8> {
534    let mut result = Vec::with_capacity(left.len());
535    let mut borrow = 0i16;
536    let mut left_index = left.len();
537    let mut right_index = right.len();
538
539    while left_index > 0 {
540        left_index -= 1;
541        let left_digit = left[left_index] as i16 - borrow;
542        let right_digit = if right_index > 0 {
543            right_index -= 1;
544            right[right_index] as i16
545        } else {
546            0
547        };
548        if left_digit < right_digit {
549            result.push((left_digit + 10 - right_digit) as u8);
550            borrow = 1;
551        } else {
552            result.push((left_digit - right_digit) as u8);
553            borrow = 0;
554        }
555    }
556
557    result.reverse();
558    trim_leading_digit_zeros(&mut result);
559    result
560}
561
562fn multiply_digit_vectors(left: &[u8], right: &[u8]) -> Vec<u8> {
563    if is_zero_digit_vector(left) || is_zero_digit_vector(right) {
564        return vec![0];
565    }
566
567    let mut result = vec![0u32; left.len() + right.len()];
568    for (left_index, left_digit) in left.iter().enumerate().rev() {
569        for (right_index, right_digit) in right.iter().enumerate().rev() {
570            let slot = left_index + right_index + 1;
571            result[slot] += (*left_digit as u32) * (*right_digit as u32);
572        }
573    }
574
575    for index in (1..result.len()).rev() {
576        let carry = result[index] / 10;
577        result[index] %= 10;
578        result[index - 1] += carry;
579    }
580
581    let mut digits = result
582        .into_iter()
583        .map(|digit| digit as u8)
584        .collect::<Vec<_>>();
585    trim_leading_digit_zeros(&mut digits);
586    digits
587}
588
589fn divide_digit_vectors(numerator: &[u8], denominator: &[u8]) -> (Vec<u8>, Vec<u8>) {
590    debug_assert!(!is_zero_digit_vector(denominator));
591
592    let mut quotient = Vec::with_capacity(numerator.len().max(1));
593    let mut remainder = vec![0];
594
595    for digit in numerator {
596        if is_zero_digit_vector(&remainder) {
597            remainder[0] = *digit;
598        } else {
599            remainder.push(*digit);
600        }
601        trim_leading_digit_zeros(&mut remainder);
602
603        let mut quotient_digit = 0u8;
604        while compare_digit_vectors(&remainder, denominator) != Ordering::Less {
605            remainder = subtract_digit_vectors(&remainder, denominator);
606            quotient_digit += 1;
607        }
608        quotient.push(quotient_digit);
609    }
610
611    trim_leading_digit_zeros(&mut quotient);
612    trim_leading_digit_zeros(&mut remainder);
613    (quotient, remainder)
614}
615
616fn increment_digit_vector(digits: &[u8]) -> Vec<u8> {
617    add_digit_vectors(digits, &[1])
618}
619
620fn trim_leading_digit_zeros(digits: &mut Vec<u8>) {
621    while digits.len() > 1 && digits.first() == Some(&0) {
622        digits.remove(0);
623    }
624    if digits.is_empty() {
625        digits.push(0);
626    }
627}
628
629fn is_zero_digit_vector(digits: &[u8]) -> bool {
630    digits.len() == 1 && digits[0] == 0
631}
632
633impl fmt::Display for DecimalValue {
634    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
635        if self.negative && !self.is_zero() {
636            write!(f, "-")?;
637        }
638
639        let digits = self
640            .digits
641            .iter()
642            .map(|digit| char::from(b'0' + *digit))
643            .collect::<String>();
644
645        if self.scale == 0 {
646            return write!(f, "{digits}");
647        }
648
649        let split = digits.len().saturating_sub(self.scale as usize);
650        if split == 0 {
651            write!(f, "0.")?;
652            for _ in 0..self.scale as usize - digits.len() {
653                write!(f, "0")?;
654            }
655            write!(f, "{digits}")
656        } else {
657            write!(f, "{}.{}", &digits[..split], &digits[split..])
658        }
659    }
660}
661
662impl PartialOrd for DecimalValue {
663    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
664        Some(self.cmp(other))
665    }
666}
667
668impl Ord for DecimalValue {
669    fn cmp(&self, other: &Self) -> Ordering {
670        if self.negative != other.negative {
671            return if self.negative {
672                Ordering::Less
673            } else {
674                Ordering::Greater
675            };
676        }
677
678        let left_integer_digits = self.digits.len().saturating_sub(self.scale as usize).max(1);
679        let right_integer_digits = other
680            .digits
681            .len()
682            .saturating_sub(other.scale as usize)
683            .max(1);
684
685        let ordering = left_integer_digits
686            .cmp(&right_integer_digits)
687            .then_with(|| self.digits.cmp(&other.digits))
688            .then_with(|| self.scale.cmp(&other.scale).reverse());
689
690        if self.negative {
691            ordering.reverse()
692        } else {
693            ordering
694        }
695    }
696}
697
698#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
699pub struct DateValue {
700    days_since_epoch: i32,
701}
702
703impl DateValue {
704    pub fn epoch() -> Self {
705        Self {
706            days_since_epoch: 0,
707        }
708    }
709
710    pub fn parse(input: &str) -> Result<Self> {
711        let value = input.trim();
712        let parts = value.split('-').collect::<Vec<_>>();
713        if parts.len() != 3
714            || parts[0].len() != 4
715            || parts[1].len() != 2
716            || parts[2].len() != 2
717            || !parts
718                .iter()
719                .all(|part| part.chars().all(|ch| ch.is_ascii_digit()))
720        {
721            return Err(HematiteError::ParseError(format!(
722                "Invalid DATE value '{}'",
723                input
724            )));
725        }
726
727        let year = parts[0]
728            .parse::<i32>()
729            .map_err(|_| HematiteError::ParseError(format!("Invalid DATE value '{}'", input)))?;
730        let month = parts[1]
731            .parse::<u32>()
732            .map_err(|_| HematiteError::ParseError(format!("Invalid DATE value '{}'", input)))?;
733        let day = parts[2]
734            .parse::<u32>()
735            .map_err(|_| HematiteError::ParseError(format!("Invalid DATE value '{}'", input)))?;
736        validate_date_components(year, month, day, input)?;
737        Ok(Self {
738            days_since_epoch: days_from_civil(year, month, day),
739        })
740    }
741
742    pub fn from_days_since_epoch(days_since_epoch: i32) -> Self {
743        Self { days_since_epoch }
744    }
745
746    pub fn days_since_epoch(self) -> i32 {
747        self.days_since_epoch
748    }
749
750    pub fn components(self) -> (i32, u32, u32) {
751        civil_from_days(self.days_since_epoch)
752    }
753}
754
755impl fmt::Display for DateValue {
756    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
757        let (year, month, day) = self.components();
758        write!(f, "{year:04}-{month:02}-{day:02}")
759    }
760}
761
762#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
763pub struct TimeValue {
764    seconds_since_midnight: u32,
765}
766
767impl TimeValue {
768    pub fn midnight() -> Self {
769        Self {
770            seconds_since_midnight: 0,
771        }
772    }
773
774    pub fn parse(input: &str) -> Result<Self> {
775        let value = input.trim();
776        let (hour, minute, second) = parse_time_components(value, "TIME")?;
777        Ok(Self {
778            seconds_since_midnight: hour * 3_600 + minute * 60 + second,
779        })
780    }
781
782    pub fn from_seconds_since_midnight(seconds_since_midnight: u32) -> Self {
783        Self {
784            seconds_since_midnight: seconds_since_midnight % 86_400,
785        }
786    }
787
788    pub fn seconds_since_midnight(self) -> u32 {
789        self.seconds_since_midnight
790    }
791
792    pub fn components(self) -> (u32, u32, u32) {
793        let hour = self.seconds_since_midnight / 3_600;
794        let minute = (self.seconds_since_midnight % 3_600) / 60;
795        let second = self.seconds_since_midnight % 60;
796        (hour, minute, second)
797    }
798}
799
800impl fmt::Display for TimeValue {
801    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
802        let (hour, minute, second) = self.components();
803        write!(f, "{hour:02}:{minute:02}:{second:02}")
804    }
805}
806
807#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
808pub struct DateTimeValue {
809    seconds_since_epoch: i64,
810}
811
812impl DateTimeValue {
813    pub fn epoch() -> Self {
814        Self {
815            seconds_since_epoch: 0,
816        }
817    }
818
819    pub fn parse(input: &str) -> Result<Self> {
820        let value = input.trim();
821        let mut parts = value.split(' ');
822        let date = parts.next().unwrap_or_default();
823        let time = parts.next().unwrap_or_default();
824        if parts.next().is_some() {
825            return Err(HematiteError::ParseError(format!(
826                "Invalid DATETIME value '{}'",
827                input
828            )));
829        }
830        let date = DateValue::parse(date)?;
831        let (hour, minute, second) = parse_time_components(time, "DATETIME")?;
832
833        Ok(Self {
834            seconds_since_epoch: date.days_since_epoch as i64 * 86_400
835                + hour as i64 * 3_600
836                + minute as i64 * 60
837                + second as i64,
838        })
839    }
840
841    pub fn from_seconds_since_epoch(seconds_since_epoch: i64) -> Self {
842        Self {
843            seconds_since_epoch,
844        }
845    }
846
847    pub fn seconds_since_epoch(self) -> i64 {
848        self.seconds_since_epoch
849    }
850
851    pub fn components(self) -> (DateValue, TimeValue) {
852        let days = self.seconds_since_epoch.div_euclid(86_400) as i32;
853        let seconds = self.seconds_since_epoch.rem_euclid(86_400) as u32;
854        (
855            DateValue::from_days_since_epoch(days),
856            TimeValue::from_seconds_since_midnight(seconds),
857        )
858    }
859}
860
861impl fmt::Display for DateTimeValue {
862    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
863        let (date, time) = self.components();
864        let (year, month, day) = date.components();
865        let (hour, minute, second) = time.components();
866        write!(
867            f,
868            "{year:04}-{month:02}-{day:02} {hour:02}:{minute:02}:{second:02}"
869        )
870    }
871}
872
873#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
874pub struct TimeWithTimeZoneValue {
875    seconds_since_midnight: u32,
876    offset_minutes: i16,
877}
878
879impl TimeWithTimeZoneValue {
880    pub fn utc_midnight() -> Self {
881        Self {
882            seconds_since_midnight: 0,
883            offset_minutes: 0,
884        }
885    }
886
887    pub fn parse(input: &str) -> Result<Self> {
888        let value = input.trim();
889        let split = value
890            .rfind(['+', '-'])
891            .filter(|index| *index > 0)
892            .ok_or_else(|| {
893                HematiteError::ParseError(format!("Invalid TIME WITH TIME ZONE value '{}'", input))
894            })?;
895        let (time, offset) = value.split_at(split);
896        let time = TimeValue::parse(time).map_err(|_| {
897            HematiteError::ParseError(format!("Invalid TIME WITH TIME ZONE value '{}'", input))
898        })?;
899        let offset_minutes = parse_timezone_offset(offset, input)?;
900        Ok(Self {
901            seconds_since_midnight: time.seconds_since_midnight(),
902            offset_minutes,
903        })
904    }
905
906    pub fn from_parts(seconds_since_midnight: u32, offset_minutes: i16) -> Self {
907        Self {
908            seconds_since_midnight: seconds_since_midnight % 86_400,
909            offset_minutes,
910        }
911    }
912
913    pub fn seconds_since_midnight(self) -> u32 {
914        self.seconds_since_midnight
915    }
916
917    pub fn offset_minutes(self) -> i16 {
918        self.offset_minutes
919    }
920
921    pub fn time(self) -> TimeValue {
922        TimeValue::from_seconds_since_midnight(self.seconds_since_midnight)
923    }
924}
925
926impl fmt::Display for TimeWithTimeZoneValue {
927    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
928        let sign = if self.offset_minutes < 0 { '-' } else { '+' };
929        let offset = self.offset_minutes.unsigned_abs();
930        let offset_hours = offset / 60;
931        let offset_minutes = offset % 60;
932        write!(
933            f,
934            "{}{}{:02}:{:02}",
935            self.time(),
936            sign,
937            offset_hours,
938            offset_minutes
939        )
940    }
941}
942
943#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
944pub struct IntervalYearMonthValue {
945    total_months: i32,
946}
947
948impl IntervalYearMonthValue {
949    pub fn new(total_months: i32) -> Self {
950        Self { total_months }
951    }
952
953    pub fn parse(input: &str) -> Result<Self> {
954        let trimmed = input.trim();
955        if trimmed.is_empty() {
956            return Err(HematiteError::ParseError(
957                "Invalid INTERVAL YEAR TO MONTH value ''".to_string(),
958            ));
959        }
960
961        let (negative, digits) = match trimmed.as_bytes()[0] {
962            b'+' => (false, &trimmed[1..]),
963            b'-' => (true, &trimmed[1..]),
964            _ => (false, trimmed),
965        };
966        let (years, months) = digits.split_once('-').ok_or_else(|| {
967            HematiteError::ParseError(format!("Invalid INTERVAL YEAR TO MONTH value '{}'", input))
968        })?;
969        if years.is_empty()
970            || months.len() != 2
971            || !years.chars().all(|ch| ch.is_ascii_digit())
972            || !months.chars().all(|ch| ch.is_ascii_digit())
973        {
974            return Err(HematiteError::ParseError(format!(
975                "Invalid INTERVAL YEAR TO MONTH value '{}'",
976                input
977            )));
978        }
979
980        let years = years.parse::<i32>().map_err(|_| {
981            HematiteError::ParseError(format!("Invalid INTERVAL YEAR TO MONTH value '{}'", input))
982        })?;
983        let months = months.parse::<i32>().map_err(|_| {
984            HematiteError::ParseError(format!("Invalid INTERVAL YEAR TO MONTH value '{}'", input))
985        })?;
986        if !(0..12).contains(&months) {
987            return Err(HematiteError::ParseError(format!(
988                "Invalid INTERVAL YEAR TO MONTH value '{}'",
989                input
990            )));
991        }
992
993        let total_months = years
994            .checked_mul(12)
995            .and_then(|total| total.checked_add(months))
996            .ok_or_else(|| {
997                HematiteError::ParseError(
998                    "INTERVAL YEAR TO MONTH value overflowed supported range".to_string(),
999                )
1000            })?;
1001        Ok(Self {
1002            total_months: if negative {
1003                -total_months
1004            } else {
1005                total_months
1006            },
1007        })
1008    }
1009
1010    pub fn total_months(self) -> i32 {
1011        self.total_months
1012    }
1013}
1014
1015impl fmt::Display for IntervalYearMonthValue {
1016    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1017        let sign = if self.total_months < 0 { "-" } else { "" };
1018        let total_months = self.total_months.unsigned_abs();
1019        let years = total_months / 12;
1020        let months = total_months % 12;
1021        write!(f, "{sign}{years}-{months:02}")
1022    }
1023}
1024
1025#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
1026pub struct IntervalDaySecondValue {
1027    total_seconds: i64,
1028}
1029
1030impl IntervalDaySecondValue {
1031    pub fn new(total_seconds: i64) -> Self {
1032        Self { total_seconds }
1033    }
1034
1035    pub fn parse(input: &str) -> Result<Self> {
1036        let trimmed = input.trim();
1037        if trimmed.is_empty() {
1038            return Err(HematiteError::ParseError(
1039                "Invalid INTERVAL DAY TO SECOND value ''".to_string(),
1040            ));
1041        }
1042
1043        let (negative, digits) = match trimmed.as_bytes()[0] {
1044            b'+' => (false, &trimmed[1..]),
1045            b'-' => (true, &trimmed[1..]),
1046            _ => (false, trimmed),
1047        };
1048        let (days, time) = digits.split_once(' ').ok_or_else(|| {
1049            HematiteError::ParseError(format!("Invalid INTERVAL DAY TO SECOND value '{}'", input))
1050        })?;
1051        if days.is_empty() || !days.chars().all(|ch| ch.is_ascii_digit()) {
1052            return Err(HematiteError::ParseError(format!(
1053                "Invalid INTERVAL DAY TO SECOND value '{}'",
1054                input
1055            )));
1056        }
1057        let days = days.parse::<i64>().map_err(|_| {
1058            HematiteError::ParseError(format!("Invalid INTERVAL DAY TO SECOND value '{}'", input))
1059        })?;
1060        let (hour, minute, second) = parse_time_components(time, "INTERVAL DAY TO SECOND")?;
1061        let total_seconds = days
1062            .checked_mul(86_400)
1063            .and_then(|total| total.checked_add(hour as i64 * 3_600))
1064            .and_then(|total| total.checked_add(minute as i64 * 60))
1065            .and_then(|total| total.checked_add(second as i64))
1066            .ok_or_else(|| {
1067                HematiteError::ParseError(
1068                    "INTERVAL DAY TO SECOND value overflowed supported range".to_string(),
1069                )
1070            })?;
1071        Ok(Self {
1072            total_seconds: if negative {
1073                -total_seconds
1074            } else {
1075                total_seconds
1076            },
1077        })
1078    }
1079
1080    pub fn total_seconds(self) -> i64 {
1081        self.total_seconds
1082    }
1083}
1084
1085impl fmt::Display for IntervalDaySecondValue {
1086    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1087        let sign = if self.total_seconds < 0 { "-" } else { "" };
1088        let total_seconds = self.total_seconds.unsigned_abs();
1089        let days = total_seconds / 86_400;
1090        let remainder = total_seconds % 86_400;
1091        let hours = remainder / 3_600;
1092        let minutes = (remainder % 3_600) / 60;
1093        let seconds = remainder % 60;
1094        write!(f, "{sign}{days} {hours:02}:{minutes:02}:{seconds:02}")
1095    }
1096}
1097
1098#[derive(Debug, Clone, PartialEq)]
1099pub enum Value {
1100    Integer(i32),
1101    BigInt(i64),
1102    Int128(i128),
1103    UInteger(u32),
1104    UBigInt(u64),
1105    UInt128(u128),
1106    Text(String),
1107    Enum(String),
1108    Boolean(bool),
1109    Float32(f32),
1110    Float(f64),
1111    Decimal(DecimalValue),
1112    Blob(Vec<u8>),
1113    Date(DateValue),
1114    Time(TimeValue),
1115    DateTime(DateTimeValue),
1116    TimeWithTimeZone(TimeWithTimeZoneValue),
1117    IntervalYearMonth(IntervalYearMonthValue),
1118    IntervalDaySecond(IntervalDaySecondValue),
1119    Null,
1120}
1121
1122impl Value {
1123    pub fn data_type(&self) -> DataType {
1124        match self {
1125            Value::Integer(_) => DataType::Int,
1126            Value::BigInt(_) => DataType::Int64,
1127            Value::Int128(_) => DataType::Int128,
1128            Value::UInteger(_) => DataType::UInt,
1129            Value::UBigInt(_) => DataType::UInt64,
1130            Value::UInt128(_) => DataType::UInt128,
1131            Value::Text(_) => DataType::Text,
1132            Value::Enum(_) => DataType::Enum(Vec::new()),
1133            Value::Boolean(_) => DataType::Boolean,
1134            Value::Float32(_) => DataType::Float32,
1135            Value::Float(_) => DataType::Float,
1136            Value::Decimal(_) => DataType::Decimal {
1137                precision: None,
1138                scale: None,
1139            },
1140            Value::Blob(_) => DataType::Blob,
1141            Value::Date(_) => DataType::Date,
1142            Value::Time(_) => DataType::Time,
1143            Value::DateTime(_) => DataType::DateTime,
1144            Value::TimeWithTimeZone(_) => DataType::TimeWithTimeZone,
1145            Value::IntervalYearMonth(_) => DataType::IntervalYearMonth,
1146            Value::IntervalDaySecond(_) => DataType::IntervalDaySecond,
1147            Value::Null => DataType::Text,
1148        }
1149    }
1150
1151    pub fn is_compatible_with(&self, data_type: DataType) -> bool {
1152        match (self, data_type) {
1153            (Value::Integer(_), DataType::Int8)
1154            | (Value::Integer(_), DataType::Int16)
1155            | (Value::Integer(_), DataType::Int) => true,
1156            (Value::BigInt(_), DataType::Int64) => true,
1157            (Value::Int128(_), DataType::Int128) => true,
1158            (Value::UInteger(_), DataType::UInt8)
1159            | (Value::UInteger(_), DataType::UInt16)
1160            | (Value::UInteger(_), DataType::UInt) => true,
1161            (Value::UBigInt(_), DataType::UInt64) => true,
1162            (Value::UInt128(_), DataType::UInt128) => true,
1163            (Value::Text(_), DataType::Text)
1164            | (Value::Text(_), DataType::Char(_))
1165            | (Value::Text(_), DataType::VarChar(_)) => true,
1166            (Value::Blob(_), DataType::Binary(_)) | (Value::Blob(_), DataType::VarBinary(_)) => {
1167                true
1168            }
1169            (Value::Enum(value), DataType::Enum(values)) => values.contains(value),
1170            (Value::Boolean(_), DataType::Boolean) => true,
1171            (Value::Float32(_), DataType::Float32) => true,
1172            (Value::Float(_), DataType::Float) => true,
1173            (Value::Decimal(value), DataType::Decimal { precision, scale }) => {
1174                value.fits_precision_scale(precision, scale)
1175            }
1176            (Value::Blob(_), DataType::Blob) => true,
1177            (Value::Date(_), DataType::Date) => true,
1178            (Value::Time(_), DataType::Time) => true,
1179            (Value::DateTime(_), DataType::DateTime) => true,
1180            (Value::TimeWithTimeZone(_), DataType::TimeWithTimeZone) => true,
1181            (Value::IntervalYearMonth(_), DataType::IntervalYearMonth) => true,
1182            (Value::IntervalDaySecond(_), DataType::IntervalDaySecond) => true,
1183            (Value::Null, _) => true,
1184            _ => false,
1185        }
1186    }
1187
1188    pub fn as_integer(&self) -> Option<i32> {
1189        match self {
1190            Value::Integer(i) => Some(*i),
1191            _ => None,
1192        }
1193    }
1194
1195    pub fn as_text(&self) -> Option<String> {
1196        match self {
1197            Value::Text(s) => Some(s.clone()),
1198            Value::Enum(s) => Some(s.clone()),
1199            Value::Decimal(s) => Some(s.to_string()),
1200            Value::Date(s) => Some(s.to_string()),
1201            Value::Time(s) => Some(s.to_string()),
1202            Value::DateTime(s) => Some(s.to_string()),
1203            Value::TimeWithTimeZone(s) => Some(s.to_string()),
1204            Value::IntervalYearMonth(s) => Some(s.to_string()),
1205            Value::IntervalDaySecond(s) => Some(s.to_string()),
1206            _ => None,
1207        }
1208    }
1209
1210    pub fn as_boolean(&self) -> Option<bool> {
1211        match self {
1212            Value::Boolean(b) => Some(*b),
1213            _ => None,
1214        }
1215    }
1216
1217    pub fn as_float(&self) -> Option<f64> {
1218        match self {
1219            Value::Float32(f) => Some(*f as f64),
1220            Value::Float(f) => Some(*f),
1221            _ => None,
1222        }
1223    }
1224
1225    pub fn as_bigint(&self) -> Option<i64> {
1226        match self {
1227            Value::BigInt(i) => Some(*i),
1228            Value::Integer(i) => Some(*i as i64),
1229            Value::UInteger(i) => Some(*i as i64),
1230            _ => None,
1231        }
1232    }
1233
1234    pub fn as_int128(&self) -> Option<i128> {
1235        match self {
1236            Value::Int128(i) => Some(*i),
1237            Value::BigInt(i) => Some(*i as i128),
1238            Value::Integer(i) => Some(*i as i128),
1239            Value::UInteger(i) => Some(*i as i128),
1240            Value::UBigInt(i) => i128::try_from(*i).ok(),
1241            _ => None,
1242        }
1243    }
1244
1245    pub fn as_uint(&self) -> Option<u32> {
1246        match self {
1247            Value::UInteger(i) => Some(*i),
1248            Value::Integer(i) if *i >= 0 => Some(*i as u32),
1249            _ => None,
1250        }
1251    }
1252
1253    pub fn as_uint64(&self) -> Option<u64> {
1254        match self {
1255            Value::UBigInt(i) => Some(*i),
1256            Value::UInteger(i) => Some(*i as u64),
1257            Value::Integer(i) if *i >= 0 => Some(*i as u64),
1258            Value::BigInt(i) if *i >= 0 => Some(*i as u64),
1259            _ => None,
1260        }
1261    }
1262
1263    pub fn as_uint128(&self) -> Option<u128> {
1264        match self {
1265            Value::UInt128(i) => Some(*i),
1266            Value::UBigInt(i) => Some(*i as u128),
1267            Value::UInteger(i) => Some(*i as u128),
1268            Value::Integer(i) if *i >= 0 => Some(*i as u128),
1269            Value::BigInt(i) if *i >= 0 => Some(*i as u128),
1270            Value::Int128(i) if *i >= 0 => Some(*i as u128),
1271            _ => None,
1272        }
1273    }
1274
1275    pub fn as_blob(&self) -> Option<&[u8]> {
1276        match self {
1277            Value::Blob(bytes) => Some(bytes),
1278            _ => None,
1279        }
1280    }
1281
1282    pub fn is_null(&self) -> bool {
1283        matches!(self, Value::Null)
1284    }
1285
1286    fn is_integral_value(&self) -> bool {
1287        matches!(
1288            self,
1289            Value::Integer(_)
1290                | Value::BigInt(_)
1291                | Value::Int128(_)
1292                | Value::UInteger(_)
1293                | Value::UBigInt(_)
1294                | Value::UInt128(_)
1295        )
1296    }
1297
1298    pub fn is_float_like(&self) -> bool {
1299        matches!(self, Value::Float32(_) | Value::Float(_))
1300    }
1301}
1302
1303impl Eq for Value {}
1304
1305impl PartialOrd for Value {
1306    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1307        match (self, other) {
1308            (Value::Integer(a), Value::Integer(b)) => a.partial_cmp(b),
1309            (Value::BigInt(a), Value::BigInt(b)) => a.partial_cmp(b),
1310            (Value::Int128(a), Value::Int128(b)) => a.partial_cmp(b),
1311            (Value::UInteger(a), Value::UInteger(b)) => a.partial_cmp(b),
1312            (Value::UBigInt(a), Value::UBigInt(b)) => a.partial_cmp(b),
1313            (Value::UInt128(a), Value::UInt128(b)) => a.partial_cmp(b),
1314            (Value::Integer(a), Value::BigInt(b)) => (*a as i64).partial_cmp(b),
1315            (Value::BigInt(a), Value::Integer(b)) => a.partial_cmp(&(*b as i64)),
1316            (Value::Integer(a), Value::Int128(b)) => (*a as i128).partial_cmp(b),
1317            (Value::Int128(a), Value::Integer(b)) => a.partial_cmp(&(*b as i128)),
1318            (Value::BigInt(a), Value::Int128(b)) => (*a as i128).partial_cmp(b),
1319            (Value::Int128(a), Value::BigInt(b)) => a.partial_cmp(&(*b as i128)),
1320            (left, right) if left.is_integral_value() && right.is_integral_value() => {
1321                compare_integral_values(left, right)
1322            }
1323            (Value::Text(a), Value::Text(b)) => a.partial_cmp(b),
1324            (Value::Enum(a), Value::Enum(b)) => a.partial_cmp(b),
1325            (Value::Boolean(a), Value::Boolean(b)) => a.partial_cmp(b),
1326            (Value::Float32(a), Value::Float32(b)) => a.partial_cmp(b),
1327            (Value::Float(a), Value::Float(b)) => a.partial_cmp(b),
1328            (Value::Float32(a), Value::Float(b)) => (*a as f64).partial_cmp(b),
1329            (Value::Float(b), Value::Float32(a)) => b.partial_cmp(&(*a as f64)),
1330            (Value::Decimal(a), Value::Decimal(b)) => a.partial_cmp(b),
1331            (Value::Blob(a), Value::Blob(b)) => a.partial_cmp(b),
1332            (Value::Date(a), Value::Date(b)) => a.partial_cmp(b),
1333            (Value::Time(a), Value::Time(b)) => a.partial_cmp(b),
1334            (Value::DateTime(a), Value::DateTime(b)) => a.partial_cmp(b),
1335            (Value::TimeWithTimeZone(a), Value::TimeWithTimeZone(b)) => a.partial_cmp(b),
1336            (Value::IntervalYearMonth(a), Value::IntervalYearMonth(b)) => a.partial_cmp(b),
1337            (Value::IntervalDaySecond(a), Value::IntervalDaySecond(b)) => a.partial_cmp(b),
1338            (Value::Null, _) => Some(Ordering::Less),
1339            (_, Value::Null) => Some(Ordering::Greater),
1340            _ => None,
1341        }
1342    }
1343}
1344
1345fn compare_integral_values(left: &Value, right: &Value) -> Option<Ordering> {
1346    #[derive(Clone, Copy)]
1347    enum Integral {
1348        Signed(i128),
1349        Unsigned(u128),
1350    }
1351
1352    fn integral(value: &Value) -> Option<Integral> {
1353        match value {
1354            Value::Integer(value) => Some(Integral::Signed((*value).into())),
1355            Value::BigInt(value) => Some(Integral::Signed((*value).into())),
1356            Value::Int128(value) => Some(Integral::Signed(*value)),
1357            Value::UInteger(value) => Some(Integral::Unsigned((*value).into())),
1358            Value::UBigInt(value) => Some(Integral::Unsigned((*value).into())),
1359            Value::UInt128(value) => Some(Integral::Unsigned(*value)),
1360            _ => None,
1361        }
1362    }
1363
1364    match (integral(left)?, integral(right)?) {
1365        (Integral::Signed(left), Integral::Signed(right)) => left.partial_cmp(&right),
1366        (Integral::Unsigned(left), Integral::Unsigned(right)) => left.partial_cmp(&right),
1367        (Integral::Signed(left), Integral::Unsigned(right)) => {
1368            if left < 0 {
1369                Some(Ordering::Less)
1370            } else {
1371                (left as u128).partial_cmp(&right)
1372            }
1373        }
1374        (Integral::Unsigned(left), Integral::Signed(right)) => {
1375            if right < 0 {
1376                Some(Ordering::Greater)
1377            } else {
1378                left.partial_cmp(&(right as u128))
1379            }
1380        }
1381    }
1382}
1383
1384fn parse_time_components(input: &str, type_name: &str) -> Result<(u32, u32, u32)> {
1385    let parts = input.split(':').collect::<Vec<_>>();
1386    if parts.len() != 3
1387        || parts.iter().any(|part| part.len() != 2)
1388        || !parts
1389            .iter()
1390            .all(|part| part.chars().all(|ch| ch.is_ascii_digit()))
1391    {
1392        return Err(HematiteError::ParseError(format!(
1393            "Invalid {} value '{}'",
1394            type_name, input
1395        )));
1396    }
1397    let hour = parts[0].parse::<u32>().map_err(|_| {
1398        HematiteError::ParseError(format!("Invalid {} value '{}'", type_name, input))
1399    })?;
1400    let minute = parts[1].parse::<u32>().map_err(|_| {
1401        HematiteError::ParseError(format!("Invalid {} value '{}'", type_name, input))
1402    })?;
1403    let second = parts[2].parse::<u32>().map_err(|_| {
1404        HematiteError::ParseError(format!("Invalid {} value '{}'", type_name, input))
1405    })?;
1406    if hour > 23 || minute > 59 || second > 59 {
1407        return Err(HematiteError::ParseError(format!(
1408            "Invalid {} value '{}'",
1409            type_name, input
1410        )));
1411    }
1412    Ok((hour, minute, second))
1413}
1414
1415fn parse_timezone_offset(offset: &str, input: &str) -> Result<i16> {
1416    if offset.len() != 6
1417        || !matches!(offset.as_bytes()[0], b'+' | b'-')
1418        || offset.as_bytes()[3] != b':'
1419        || !offset[1..3].chars().all(|ch| ch.is_ascii_digit())
1420        || !offset[4..6].chars().all(|ch| ch.is_ascii_digit())
1421    {
1422        return Err(HematiteError::ParseError(format!(
1423            "Invalid TIME WITH TIME ZONE value '{}'",
1424            input
1425        )));
1426    }
1427
1428    let sign = if offset.as_bytes()[0] == b'-' { -1 } else { 1 };
1429    let hours = offset[1..3].parse::<i16>().map_err(|_| {
1430        HematiteError::ParseError(format!("Invalid TIME WITH TIME ZONE value '{}'", input))
1431    })?;
1432    let minutes = offset[4..6].parse::<i16>().map_err(|_| {
1433        HematiteError::ParseError(format!("Invalid TIME WITH TIME ZONE value '{}'", input))
1434    })?;
1435    if hours > 23 || minutes > 59 {
1436        return Err(HematiteError::ParseError(format!(
1437            "Invalid TIME WITH TIME ZONE value '{}'",
1438            input
1439        )));
1440    }
1441
1442    Ok(sign * (hours * 60 + minutes))
1443}
1444
1445fn validate_date_components(year: i32, month: u32, day: u32, input: &str) -> Result<()> {
1446    if !(1..=12).contains(&month) {
1447        return Err(HematiteError::ParseError(format!(
1448            "Invalid DATE value '{}'",
1449            input
1450        )));
1451    }
1452    let max_day = match month {
1453        1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
1454        4 | 6 | 9 | 11 => 30,
1455        2 if is_leap_year(year) => 29,
1456        2 => 28,
1457        _ => unreachable!(),
1458    };
1459    if day == 0 || day > max_day {
1460        return Err(HematiteError::ParseError(format!(
1461            "Invalid DATE value '{}'",
1462            input
1463        )));
1464    }
1465    Ok(())
1466}
1467
1468fn is_leap_year(year: i32) -> bool {
1469    (year % 4 == 0 && year % 100 != 0) || year % 400 == 0
1470}
1471
1472fn days_from_civil(year: i32, month: u32, day: u32) -> i32 {
1473    let year = year - if month <= 2 { 1 } else { 0 };
1474    let era = if year >= 0 { year } else { year - 399 } / 400;
1475    let yoe = year - era * 400;
1476    let month = month as i32;
1477    let day = day as i32;
1478    let doy = (153 * (month + if month > 2 { -3 } else { 9 }) + 2) / 5 + day - 1;
1479    let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
1480    era * 146097 + doe - 719468
1481}
1482
1483fn civil_from_days(days: i32) -> (i32, u32, u32) {
1484    let z = days + 719468;
1485    let era = if z >= 0 { z } else { z - 146096 } / 146097;
1486    let doe = z - era * 146097;
1487    let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
1488    let year = yoe + era * 400;
1489    let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
1490    let mp = (5 * doy + 2) / 153;
1491    let day = doy - (153 * mp + 2) / 5 + 1;
1492    let month = mp + if mp < 10 { 3 } else { -9 };
1493    let year = year + if month <= 2 { 1 } else { 0 };
1494    (year, month as u32, day as u32)
1495}