stoolap/core/
value.rs

1// Copyright 2025 Stoolap Contributors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Value type for Stoolap - runtime values with type information
16//!
17//! This module provides a unified Value enum that represents SQL values
18//! with full type information and conversion capabilities.
19
20use std::cmp::Ordering;
21use std::fmt;
22use std::hash::{Hash, Hasher};
23use std::sync::Arc;
24
25use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc};
26
27use super::error::{Error, Result};
28use super::types::DataType;
29use crate::common::{CompactArc, SmartString};
30
31/// Timestamp formats supported for parsing
32/// Order matters - more specific formats first
33const TIMESTAMP_FORMATS: &[&str] = &[
34    "%Y-%m-%dT%H:%M:%S%.f%:z", // RFC3339 with fractional seconds
35    "%Y-%m-%dT%H:%M:%S%:z",    // RFC3339
36    "%Y-%m-%dT%H:%M:%SZ",      // RFC3339 UTC
37    "%Y-%m-%dT%H:%M:%S",       // ISO without timezone
38    "%Y-%m-%d %H:%M:%S%.f",    // SQL-style with fractional seconds
39    "%Y-%m-%d %H:%M:%S",       // SQL-style
40    "%Y-%m-%d",                // Date only
41    "%Y/%m/%d %H:%M:%S",       // Alternative with slashes
42    "%Y/%m/%d",                // Alternative date only
43    "%m/%d/%Y",                // US format
44    "%d/%m/%Y",                // European format
45];
46
47const TIME_FORMATS: &[&str] = &[
48    "%H:%M:%S%.f", // High precision
49    "%H:%M:%S",    // Standard
50    "%H:%M",       // Hours and minutes only
51];
52
53/// A runtime value with type information
54///
55/// Each variant carries its data directly, avoiding the need for interface
56/// indirection or separate value references.
57///
58/// ## Memory Layout (16 bytes)
59///
60/// Value is exactly 16 bytes due to niche optimization:
61/// - Text(SmartString): 16 bytes with niches in tag byte (values 17-255 unused)
62/// - Json(Arc<String>): 8 bytes thin pointer with null niche
63/// - Rust stores Value's discriminant in these niche values
64///
65/// Note: Text uses SmartString for inline storage of strings up to 15 bytes.
66/// Longer strings use Arc<String> for O(1) clone and sharing.
67#[derive(Debug, Clone)]
68pub enum Value {
69    /// NULL value with optional type hint
70    Null(DataType),
71
72    /// 64-bit signed integer
73    Integer(i64),
74
75    /// 64-bit floating point
76    Float(f64),
77
78    /// UTF-8 text string (SmartString: inline ≤15 bytes, Arc for larger)
79    Text(SmartString),
80
81    /// Boolean value
82    Boolean(bool),
83
84    /// Timestamp (UTC)
85    Timestamp(DateTime<Utc>),
86
87    /// JSON document (CompactArc<str> for cheap cloning, 16-byte fat pointer)
88    Json(CompactArc<str>),
89}
90
91/// Static NULL value for zero-cost reuse
92pub const NULL_VALUE: Value = Value::Null(DataType::Null);
93
94impl Value {
95    // =========================================================================
96    // Constructors
97    // =========================================================================
98
99    /// Create a NULL value with a type hint
100    #[inline]
101    pub fn null(data_type: DataType) -> Self {
102        Value::Null(data_type)
103    }
104
105    /// Create a NULL value with unknown type
106    #[inline(always)]
107    pub fn null_unknown() -> Self {
108        Value::Null(DataType::Null)
109    }
110
111    /// Create an integer value
112    pub fn integer(value: i64) -> Self {
113        Value::Integer(value)
114    }
115
116    /// Create a float value
117    pub fn float(value: f64) -> Self {
118        Value::Float(value)
119    }
120
121    /// Create a text value
122    ///
123    /// Uses SmartString::from_string_shared() for heap strings to enable
124    /// O(1) clone via Arc<str>. This allows string sharing between
125    /// Arena, Index, and VersionStore.
126    pub fn text(value: impl Into<String>) -> Self {
127        Value::Text(SmartString::from_string_shared(value.into()))
128    }
129
130    /// Create a text value from Arc<str> (zero-copy for heap strings)
131    ///
132    /// Preserves the Arc reference for O(1) clone and sharing.
133    pub fn text_arc(value: Arc<str>) -> Self {
134        Value::Text(SmartString::from(value))
135    }
136
137    /// Create a boolean value
138    pub fn boolean(value: bool) -> Self {
139        Value::Boolean(value)
140    }
141
142    /// Create a timestamp value
143    pub fn timestamp(value: DateTime<Utc>) -> Self {
144        Value::Timestamp(value)
145    }
146
147    /// Create a JSON value
148    pub fn json(value: impl Into<String>) -> Self {
149        Value::Json(CompactArc::from(value.into()))
150    }
151
152    /// Create a JSON value from CompactArc<str> (zero-copy)
153    pub fn json_arc(value: CompactArc<str>) -> Self {
154        Value::Json(value)
155    }
156
157    // =========================================================================
158    // Type accessors
159    // =========================================================================
160
161    /// Returns the data type of this value
162    pub fn data_type(&self) -> DataType {
163        match self {
164            Value::Null(dt) => *dt,
165            Value::Integer(_) => DataType::Integer,
166            Value::Float(_) => DataType::Float,
167            Value::Text(_) => DataType::Text,
168            Value::Boolean(_) => DataType::Boolean,
169            Value::Timestamp(_) => DataType::Timestamp,
170            Value::Json(_) => DataType::Json,
171        }
172    }
173
174    /// Returns true if this value is NULL
175    #[inline(always)]
176    pub fn is_null(&self) -> bool {
177        matches!(self, Value::Null(_))
178    }
179
180    // =========================================================================
181    // Value extractors
182    // =========================================================================
183
184    /// Extract as i64, with type coercion
185    ///
186    /// Returns None if:
187    /// - Value is NULL
188    /// - Conversion is not possible
189    pub fn as_int64(&self) -> Option<i64> {
190        match self {
191            Value::Null(_) => None,
192            Value::Integer(v) => Some(*v),
193            Value::Float(v) => Some(*v as i64),
194            Value::Text(s) => s
195                .parse::<i64>()
196                .ok()
197                .or_else(|| s.parse::<f64>().ok().map(|f| f as i64)),
198            Value::Boolean(b) => Some(if *b { 1 } else { 0 }),
199            Value::Timestamp(t) => Some(t.timestamp_nanos_opt().unwrap_or(0)),
200            Value::Json(_) => None,
201        }
202    }
203
204    /// Extract as f64, with type coercion
205    pub fn as_float64(&self) -> Option<f64> {
206        match self {
207            Value::Null(_) => None,
208            Value::Integer(v) => Some(*v as f64),
209            Value::Float(v) => Some(*v),
210            Value::Text(s) => s.parse::<f64>().ok(),
211            Value::Boolean(b) => Some(if *b { 1.0 } else { 0.0 }),
212            Value::Timestamp(_) => None,
213            Value::Json(_) => None,
214        }
215    }
216
217    /// Extract as boolean, with type coercion
218    pub fn as_boolean(&self) -> Option<bool> {
219        match self {
220            Value::Null(_) => None,
221            Value::Integer(v) => Some(*v != 0),
222            Value::Float(v) => Some(*v != 0.0),
223            Value::Text(s) => {
224                // OPTIMIZATION: Use eq_ignore_ascii_case to avoid allocation
225                let s_ref: &str = s.as_ref();
226                if s_ref.eq_ignore_ascii_case("true")
227                    || s_ref.eq_ignore_ascii_case("t")
228                    || s_ref.eq_ignore_ascii_case("yes")
229                    || s_ref.eq_ignore_ascii_case("y")
230                    || s_ref == "1"
231                {
232                    Some(true)
233                } else if s_ref.eq_ignore_ascii_case("false")
234                    || s_ref.eq_ignore_ascii_case("f")
235                    || s_ref.eq_ignore_ascii_case("no")
236                    || s_ref.eq_ignore_ascii_case("n")
237                    || s_ref == "0"
238                    || s_ref.is_empty()
239                {
240                    Some(false)
241                } else {
242                    s_ref.parse::<f64>().ok().map(|f| f != 0.0)
243                }
244            }
245            Value::Boolean(b) => Some(*b),
246            Value::Timestamp(_) => None,
247            Value::Json(_) => None,
248        }
249    }
250
251    /// Extract as String, with type coercion
252    pub fn as_string(&self) -> Option<String> {
253        match self {
254            Value::Null(_) => None,
255            Value::Integer(v) => Some(v.to_string()),
256            Value::Float(v) => Some(format_float(*v)),
257            Value::Text(s) => Some(s.to_string()),
258            Value::Boolean(b) => Some(if *b { "true" } else { "false" }.to_string()),
259            Value::Timestamp(t) => Some(t.to_rfc3339()),
260            Value::Json(s) => Some(s.to_string()),
261        }
262    }
263
264    /// Extract as string reference (avoids clone for Text/Json)
265    pub fn as_str(&self) -> Option<&str> {
266        match self {
267            Value::Text(s) => Some(s.as_str()),
268            Value::Json(s) => Some(s.as_ref()),
269            _ => None,
270        }
271    }
272
273    /// Extract as CompactArc<str> (creates CompactArc for Text, cheap clone for Json)
274    pub fn as_arc_str(&self) -> Option<CompactArc<str>> {
275        match self {
276            Value::Text(s) => Some(CompactArc::from(s.as_str())),
277            Value::Json(s) => Some(s.clone()),
278            _ => None,
279        }
280    }
281
282    /// Extract as DateTime<Utc>
283    pub fn as_timestamp(&self) -> Option<DateTime<Utc>> {
284        match self {
285            Value::Null(_) => None,
286            Value::Timestamp(t) => Some(*t),
287            Value::Text(s) => parse_timestamp(s).ok(),
288            Value::Integer(nanos) => {
289                // Interpret as nanoseconds since Unix epoch
290                DateTime::from_timestamp(*nanos / 1_000_000_000, (*nanos % 1_000_000_000) as u32)
291            }
292            _ => None,
293        }
294    }
295
296    /// Extract as JSON string
297    pub fn as_json(&self) -> Option<&str> {
298        match self {
299            Value::Null(_) => Some("{}"),
300            Value::Json(s) => Some(s),
301            _ => None,
302        }
303    }
304
305    // =========================================================================
306    // Comparison
307    // =========================================================================
308
309    /// Compare two values for ordering
310    ///
311    /// Returns:
312    /// - Ok(Ordering::Less) if self < other
313    /// - Ok(Ordering::Equal) if self == other
314    /// - Ok(Ordering::Greater) if self > other
315    /// - Err if comparison is not possible
316    pub fn compare(&self, other: &Value) -> Result<Ordering> {
317        // Handle NULL comparisons
318        if self.is_null() || other.is_null() {
319            if self.is_null() && other.is_null() {
320                return Ok(Ordering::Equal);
321            }
322            return Err(Error::NullComparison);
323        }
324
325        // Same type comparison (most efficient path)
326        if self.data_type() == other.data_type() {
327            return self.compare_same_type(other);
328        }
329
330        // Cross-type numeric comparison (integer vs float)
331        if self.data_type().is_numeric() && other.data_type().is_numeric() {
332            let v1 = self.as_float64().unwrap();
333            let v2 = other.as_float64().unwrap();
334            return Ok(compare_floats(v1, v2));
335        }
336
337        // Fall back to string comparison for mixed types
338        let s1 = self.as_string().unwrap_or_default();
339        let s2 = other.as_string().unwrap_or_default();
340        Ok(s1.cmp(&s2))
341    }
342
343    /// Compare values of the same type
344    fn compare_same_type(&self, other: &Value) -> Result<Ordering> {
345        match (self, other) {
346            (Value::Integer(a), Value::Integer(b)) => Ok(a.cmp(b)),
347            (Value::Float(a), Value::Float(b)) => Ok(compare_floats(*a, *b)),
348            (Value::Text(a), Value::Text(b)) => Ok(a.cmp(b)),
349            (Value::Boolean(a), Value::Boolean(b)) => Ok(a.cmp(b)),
350            (Value::Timestamp(a), Value::Timestamp(b)) => Ok(a.cmp(b)),
351            (Value::Json(a), Value::Json(b)) => {
352                // JSON can only test equality, not ordering
353                if a == b {
354                    Ok(Ordering::Equal)
355                } else {
356                    Err(Error::IncomparableTypes)
357                }
358            }
359            _ => Err(Error::IncomparableTypes),
360        }
361    }
362
363    // =========================================================================
364    // Construction from typed values
365    // =========================================================================
366
367    /// Create a Value from a typed value with explicit data type
368    pub fn from_typed(value: Option<&dyn std::any::Any>, data_type: DataType) -> Self {
369        match value {
370            None => Value::Null(data_type),
371            Some(v) => {
372                // Try to downcast based on expected type
373                match data_type {
374                    DataType::Integer => {
375                        if let Some(&i) = v.downcast_ref::<i64>() {
376                            Value::Integer(i)
377                        } else if let Some(&i) = v.downcast_ref::<i32>() {
378                            Value::Integer(i as i64)
379                        } else if let Some(s) = v.downcast_ref::<String>() {
380                            s.parse::<i64>()
381                                .map(Value::Integer)
382                                .unwrap_or(Value::Null(data_type))
383                        } else {
384                            Value::Null(data_type)
385                        }
386                    }
387                    DataType::Float => {
388                        if let Some(&f) = v.downcast_ref::<f64>() {
389                            Value::Float(f)
390                        } else if let Some(&i) = v.downcast_ref::<i64>() {
391                            Value::Float(i as f64)
392                        } else if let Some(s) = v.downcast_ref::<String>() {
393                            s.parse::<f64>()
394                                .map(Value::Float)
395                                .unwrap_or(Value::Null(data_type))
396                        } else {
397                            Value::Null(data_type)
398                        }
399                    }
400                    DataType::Text => {
401                        if let Some(s) = v.downcast_ref::<String>() {
402                            Value::Text(SmartString::new(s))
403                        } else if let Some(&s) = v.downcast_ref::<&str>() {
404                            Value::Text(SmartString::from(s))
405                        } else {
406                            Value::Null(data_type)
407                        }
408                    }
409                    DataType::Boolean => {
410                        if let Some(&b) = v.downcast_ref::<bool>() {
411                            Value::Boolean(b)
412                        } else if let Some(&i) = v.downcast_ref::<i64>() {
413                            Value::Boolean(i != 0)
414                        } else {
415                            Value::Null(data_type)
416                        }
417                    }
418                    DataType::Timestamp => {
419                        if let Some(&t) = v.downcast_ref::<DateTime<Utc>>() {
420                            Value::Timestamp(t)
421                        } else if let Some(s) = v.downcast_ref::<String>() {
422                            parse_timestamp(s)
423                                .map(Value::Timestamp)
424                                .unwrap_or(Value::Null(data_type))
425                        } else {
426                            Value::Null(data_type)
427                        }
428                    }
429                    DataType::Json => {
430                        if let Some(s) = v.downcast_ref::<String>() {
431                            // Validate JSON
432                            if serde_json::from_str::<serde_json::Value>(s).is_ok() {
433                                Value::Json(CompactArc::from(s.as_str()))
434                            } else {
435                                Value::Null(data_type)
436                            }
437                        } else {
438                            Value::Null(data_type)
439                        }
440                    }
441                    DataType::Null => Value::Null(DataType::Null),
442                }
443            }
444        }
445    }
446
447    // =========================================================================
448    // Type coercion
449    // =========================================================================
450
451    /// Coerce this value to the target data type
452    ///
453    /// Type coercion rules:
454    /// - Integer column receiving Float → converts to Integer
455    /// - Float column receiving Integer → converts to Float
456    /// - Text column receiving any type → converts to Text
457    /// - Timestamp column receiving String → parses timestamp
458    /// - JSON column receiving valid JSON string → stores as JSON
459    /// - Boolean column receiving Integer/String → converts to Boolean
460    ///
461    /// Returns the coerced value, or NULL if coercion fails.
462    pub fn coerce_to_type(&self, target_type: DataType) -> Value {
463        // NULL stays NULL (with target type hint)
464        if self.is_null() {
465            return Value::Null(target_type);
466        }
467
468        // Same type - no conversion needed
469        if self.data_type() == target_type {
470            return self.clone();
471        }
472
473        match target_type {
474            DataType::Integer => {
475                // Convert to INTEGER
476                match self {
477                    Value::Integer(v) => Value::Integer(*v),
478                    Value::Float(v) => Value::Integer(*v as i64),
479                    Value::Text(s) => s
480                        .parse::<i64>()
481                        .map(Value::Integer)
482                        .unwrap_or(Value::Null(target_type)),
483                    Value::Boolean(b) => Value::Integer(if *b { 1 } else { 0 }),
484                    _ => Value::Null(target_type),
485                }
486            }
487            DataType::Float => {
488                // Convert to FLOAT
489                match self {
490                    Value::Float(v) => Value::Float(*v),
491                    Value::Integer(v) => Value::Float(*v as f64),
492                    Value::Text(s) => s
493                        .parse::<f64>()
494                        .map(Value::Float)
495                        .unwrap_or(Value::Null(target_type)),
496                    Value::Boolean(b) => Value::Float(if *b { 1.0 } else { 0.0 }),
497                    _ => Value::Null(target_type),
498                }
499            }
500            DataType::Text => {
501                // Convert to TEXT - everything can become text
502                match self {
503                    Value::Text(s) => Value::Text(s.clone()),
504                    Value::Integer(v) => Value::Text(SmartString::from_string(v.to_string())),
505                    Value::Float(v) => Value::Text(SmartString::from_string(format_float(*v))),
506                    Value::Boolean(b) => {
507                        Value::Text(SmartString::new(if *b { "true" } else { "false" }))
508                    }
509                    Value::Timestamp(t) => Value::Text(SmartString::from_string(t.to_rfc3339())),
510                    Value::Json(s) => Value::Text(SmartString::new(s.as_ref())),
511                    Value::Null(_) => Value::Null(target_type),
512                }
513            }
514            DataType::Boolean => {
515                // Convert to BOOLEAN
516                match self {
517                    Value::Boolean(b) => Value::Boolean(*b),
518                    Value::Integer(v) => Value::Boolean(*v != 0),
519                    Value::Float(v) => Value::Boolean(*v != 0.0),
520                    Value::Text(s) => {
521                        // OPTIMIZATION: Use eq_ignore_ascii_case to avoid allocation
522                        let s_ref: &str = s.as_ref();
523                        if s_ref.eq_ignore_ascii_case("true")
524                            || s_ref.eq_ignore_ascii_case("t")
525                            || s_ref.eq_ignore_ascii_case("yes")
526                            || s_ref.eq_ignore_ascii_case("y")
527                            || s_ref == "1"
528                        {
529                            Value::Boolean(true)
530                        } else if s_ref.eq_ignore_ascii_case("false")
531                            || s_ref.eq_ignore_ascii_case("f")
532                            || s_ref.eq_ignore_ascii_case("no")
533                            || s_ref.eq_ignore_ascii_case("n")
534                            || s_ref == "0"
535                        {
536                            Value::Boolean(false)
537                        } else {
538                            Value::Null(target_type)
539                        }
540                    }
541                    _ => Value::Null(target_type),
542                }
543            }
544            DataType::Timestamp => {
545                // Convert to TIMESTAMP
546                match self {
547                    Value::Timestamp(t) => Value::Timestamp(*t),
548                    Value::Text(s) => parse_timestamp(s)
549                        .map(Value::Timestamp)
550                        .unwrap_or(Value::Null(target_type)),
551                    Value::Integer(nanos) => {
552                        // Interpret as nanoseconds since Unix epoch
553                        DateTime::from_timestamp(
554                            *nanos / 1_000_000_000,
555                            (*nanos % 1_000_000_000) as u32,
556                        )
557                        .map(Value::Timestamp)
558                        .unwrap_or(Value::Null(target_type))
559                    }
560                    _ => Value::Null(target_type),
561                }
562            }
563            DataType::Json => {
564                // Convert to JSON
565                match self {
566                    Value::Json(s) => Value::Json(s.clone()),
567                    Value::Text(s) => {
568                        // Validate JSON
569                        if serde_json::from_str::<serde_json::Value>(s.as_str()).is_ok() {
570                            Value::Json(CompactArc::from(s.as_str()))
571                        } else {
572                            Value::Null(target_type)
573                        }
574                    }
575                    // Convert other types to JSON representation
576                    Value::Integer(v) => Value::Json(CompactArc::from(v.to_string())),
577                    Value::Float(v) => Value::Json(CompactArc::from(format_float(*v))),
578                    Value::Boolean(b) => {
579                        Value::Json(CompactArc::from(if *b { "true" } else { "false" }))
580                    }
581                    _ => Value::Null(target_type),
582                }
583            }
584            DataType::Null => Value::Null(DataType::Null),
585        }
586    }
587
588    /// Coerce value to target type, consuming self
589    /// OPTIMIZATION: Avoids clone when types already match
590    #[inline]
591    pub fn into_coerce_to_type(self, target_type: DataType) -> Value {
592        // NULL stays NULL (with target type hint)
593        if self.is_null() {
594            return Value::Null(target_type);
595        }
596
597        // Same type - no conversion needed, return self directly
598        if self.data_type() == target_type {
599            return self;
600        }
601
602        match target_type {
603            DataType::Integer => match &self {
604                Value::Integer(v) => Value::Integer(*v),
605                Value::Float(v) => Value::Integer(*v as i64),
606                Value::Text(s) => s
607                    .parse::<i64>()
608                    .map(Value::Integer)
609                    .unwrap_or(Value::Null(target_type)),
610                Value::Boolean(b) => Value::Integer(if *b { 1 } else { 0 }),
611                _ => Value::Null(target_type),
612            },
613            DataType::Float => match &self {
614                Value::Float(v) => Value::Float(*v),
615                Value::Integer(v) => Value::Float(*v as f64),
616                Value::Text(s) => s
617                    .parse::<f64>()
618                    .map(Value::Float)
619                    .unwrap_or(Value::Null(target_type)),
620                Value::Boolean(b) => Value::Float(if *b { 1.0 } else { 0.0 }),
621                _ => Value::Null(target_type),
622            },
623            DataType::Text => match self {
624                Value::Text(s) => Value::Text(s),
625                Value::Integer(v) => Value::Text(SmartString::from_string(v.to_string())),
626                Value::Float(v) => Value::Text(SmartString::from_string(format_float(v))),
627                Value::Boolean(b) => {
628                    Value::Text(SmartString::new(if b { "true" } else { "false" }))
629                }
630                Value::Timestamp(t) => Value::Text(SmartString::from_string(t.to_rfc3339())),
631                Value::Json(s) => Value::Text(SmartString::new(s.as_ref())),
632                Value::Null(_) => Value::Null(target_type),
633            },
634            DataType::Boolean => match &self {
635                Value::Boolean(b) => Value::Boolean(*b),
636                Value::Integer(v) => Value::Boolean(*v != 0),
637                Value::Float(v) => Value::Boolean(*v != 0.0),
638                Value::Text(s) => {
639                    // OPTIMIZATION: Use eq_ignore_ascii_case to avoid allocation
640                    let s_ref: &str = s.as_ref();
641                    if s_ref.eq_ignore_ascii_case("true")
642                        || s_ref.eq_ignore_ascii_case("t")
643                        || s_ref.eq_ignore_ascii_case("yes")
644                        || s_ref.eq_ignore_ascii_case("y")
645                        || s_ref == "1"
646                    {
647                        Value::Boolean(true)
648                    } else if s_ref.eq_ignore_ascii_case("false")
649                        || s_ref.eq_ignore_ascii_case("f")
650                        || s_ref.eq_ignore_ascii_case("no")
651                        || s_ref.eq_ignore_ascii_case("n")
652                        || s_ref == "0"
653                    {
654                        Value::Boolean(false)
655                    } else {
656                        Value::Null(target_type)
657                    }
658                }
659                _ => Value::Null(target_type),
660            },
661            DataType::Timestamp => match self {
662                Value::Timestamp(t) => Value::Timestamp(t),
663                Value::Text(s) => parse_timestamp(&s)
664                    .map(Value::Timestamp)
665                    .unwrap_or(Value::Null(target_type)),
666                Value::Integer(nanos) => {
667                    DateTime::from_timestamp(nanos / 1_000_000_000, (nanos % 1_000_000_000) as u32)
668                        .map(Value::Timestamp)
669                        .unwrap_or(Value::Null(target_type))
670                }
671                _ => Value::Null(target_type),
672            },
673            DataType::Json => match self {
674                Value::Json(s) => Value::Json(s),
675                Value::Text(s) => {
676                    if serde_json::from_str::<serde_json::Value>(s.as_str()).is_ok() {
677                        Value::Json(CompactArc::from(s.as_str()))
678                    } else {
679                        Value::Null(target_type)
680                    }
681                }
682                Value::Integer(v) => Value::Json(CompactArc::from(v.to_string())),
683                Value::Float(v) => Value::Json(CompactArc::from(format_float(v))),
684                Value::Boolean(b) => {
685                    Value::Json(CompactArc::from(if b { "true" } else { "false" }))
686                }
687                _ => Value::Null(target_type),
688            },
689            DataType::Null => Value::Null(DataType::Null),
690        }
691    }
692}
693
694// =========================================================================
695// Trait implementations
696// =========================================================================
697
698impl Default for Value {
699    fn default() -> Self {
700        Value::Null(DataType::Null)
701    }
702}
703
704impl fmt::Display for Value {
705    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
706        match self {
707            Value::Null(_) => write!(f, "NULL"),
708            Value::Integer(v) => write!(f, "{}", v),
709            Value::Float(v) => write!(f, "{}", format_float(*v)),
710            Value::Text(s) => write!(f, "{}", s),
711            Value::Boolean(b) => write!(f, "{}", if *b { "true" } else { "false" }),
712            Value::Timestamp(t) => write!(f, "{}", t.to_rfc3339()),
713            Value::Json(s) => write!(f, "{}", s),
714        }
715    }
716}
717
718impl PartialEq for Value {
719    #[inline]
720    fn eq(&self, other: &Self) -> bool {
721        // Single match handles NULL and all type comparisons without redundant is_null() calls
722        match (self, other) {
723            // NULL handling: NULL == NULL (SQL equality semantics for grouping)
724            (Value::Null(_), Value::Null(_)) => true,
725            // NULL != any non-NULL value
726            (Value::Null(_), _) | (_, Value::Null(_)) => false,
727            // Same type comparisons
728            (Value::Integer(a), Value::Integer(b)) => a == b,
729            (Value::Float(a), Value::Float(b)) => {
730                // Handle NaN: NaN != NaN in IEEE 754, but we consider them equal
731                if a.is_nan() && b.is_nan() {
732                    true
733                } else {
734                    a == b
735                }
736            }
737            // Cross-type numeric comparison: Integer vs Float
738            // This is critical for queries like WHERE id = 5.0 or WHERE price = 100
739            (Value::Integer(i), Value::Float(f)) | (Value::Float(f), Value::Integer(i)) => {
740                *f == (*i as f64)
741            }
742            (Value::Text(a), Value::Text(b)) => a == b,
743            (Value::Boolean(a), Value::Boolean(b)) => a == b,
744            (Value::Timestamp(a), Value::Timestamp(b)) => a == b,
745            (Value::Json(a), Value::Json(b)) => a == b,
746            _ => false,
747        }
748    }
749}
750
751impl Eq for Value {}
752
753/// Maximum i64 value that can be safely hashed as i64 without f64 conversion.
754/// Integers in the range [I64_SAFE_MIN, I64_SAFE_MAX] have unique f64 representations,
755/// while integers outside this range may round to the same f64 value.
756/// This is 2^53 - 1 = 9007199254740991.
757const I64_SAFE_MAX: i64 = (1_i64 << 53) - 1;
758const I64_SAFE_MIN: i64 = -I64_SAFE_MAX;
759
760/// WyHash-style 128-bit multiply mixing function.
761/// Provides excellent avalanche properties - small input changes produce
762/// completely different outputs. This pre-mixes values before the hasher
763/// sees them, fixing collision problems with simple hashers like FxHash.
764#[inline(always)]
765fn wymix(a: u64, b: u64) -> u64 {
766    let r = (a as u128).wrapping_mul(b as u128);
767    (r as u64) ^ ((r >> 64) as u64)
768}
769
770// WyHash prime constants for mixing
771const WY_P1: u64 = 0xa0761d6478bd642f;
772const WY_P2: u64 = 0xe7037ed1a0b428db;
773
774impl Hash for Value {
775    #[inline(always)]
776    fn hash<H: Hasher>(&self, state: &mut H) {
777        // Pre-mix strategy: Instead of writing raw values that may have poor
778        // distribution (causing collisions in simple hashers like FxHash),
779        // we pre-mix everything using WyHash-style 128-bit multiply mixing.
780        // This gives ANY hasher well-distributed inputs.
781        //
782        // Constraint: Integer(5) == Float(5.0) must have equal hashes.
783        // We handle this by using the same mixing for whole-number floats.
784        match self {
785            Value::Null(_) => {
786                // All NULLs hash the same
787                state.write_u64(0);
788            }
789            Value::Integer(v) => {
790                // Pre-mix integer with discriminant
791                match *v {
792                    I64_SAFE_MIN..=I64_SAFE_MAX => {
793                        state.write_u64(wymix(1 ^ (*v as u64), WY_P1));
794                    }
795                    _ => {
796                        // Large integer: use f64 bits for consistency with Float
797                        state.write_u64(wymix(1 ^ (*v as f64).to_bits(), WY_P1));
798                    }
799                }
800            }
801            Value::Float(v) => {
802                if v.is_nan() {
803                    // All NaNs are equal, so they must hash the same
804                    state.write_u64(wymix(6 ^ f64::NAN.to_bits(), WY_P1));
805                } else if v.fract() == 0.0 {
806                    // Whole number float - must hash same as equivalent Integer
807                    match *v as i64 {
808                        i @ I64_SAFE_MIN..=I64_SAFE_MAX => {
809                            state.write_u64(wymix(1 ^ (i as u64), WY_P1));
810                        }
811                        _ => {
812                            state.write_u64(wymix(1 ^ v.to_bits(), WY_P1));
813                        }
814                    }
815                } else {
816                    // Fractional float - use discriminant 6 (can't equal any Integer)
817                    state.write_u64(wymix(6 ^ v.to_bits(), WY_P1));
818                }
819            }
820            Value::Text(s) => {
821                // Pre-hash string with WyHash-style mixing, write single u64
822                let bytes = s.as_bytes();
823                let len = bytes.len();
824                let mut h = wymix(2 ^ (len as u64), WY_P1);
825
826                // Process 8 bytes at a time
827                let chunks = len / 8;
828                let ptr = bytes.as_ptr();
829                for i in 0..chunks {
830                    // SAFETY: We iterate i from 0..chunks where chunks = len/8.
831                    // So i*8 is always < len, and we read 8 bytes which is valid
832                    // since (i+1)*8 <= chunks*8 <= len. read_unaligned handles alignment.
833                    let chunk = unsafe { (ptr.add(i * 8) as *const u64).read_unaligned() };
834                    h = wymix(h ^ chunk, WY_P2);
835                }
836
837                // Handle tail bytes (0-7)
838                let tail_start = chunks * 8;
839                if tail_start < len {
840                    let mut tail = 0u64;
841                    for (j, &b) in bytes[tail_start..].iter().enumerate() {
842                        tail |= (b as u64) << (j * 8);
843                    }
844                    h = wymix(h ^ tail, WY_P1);
845                }
846
847                state.write_u64(h);
848            }
849            Value::Boolean(b) => {
850                // Pre-mixed boolean
851                state.write_u64(wymix(if *b { 5 } else { 4 }, WY_P1));
852            }
853            Value::Timestamp(t) => {
854                // Pre-mix timestamp nanos
855                let nanos = t.timestamp_nanos_opt().unwrap_or(i64::MAX);
856                state.write_u64(wymix(3 ^ (nanos as u64), WY_P1));
857            }
858            Value::Json(s) => {
859                // Pre-hash JSON with WyHash-style mixing
860                let bytes = s.as_bytes();
861                let len = bytes.len();
862                let mut h = wymix(7 ^ (len as u64), WY_P1);
863
864                let chunks = len / 8;
865                let ptr = bytes.as_ptr();
866                for i in 0..chunks {
867                    // SAFETY: We iterate i from 0..chunks where chunks = len/8.
868                    // So i*8 is always < len, and we read 8 bytes which is valid
869                    // since (i+1)*8 <= chunks*8 <= len. read_unaligned handles alignment.
870                    let chunk = unsafe { (ptr.add(i * 8) as *const u64).read_unaligned() };
871                    h = wymix(h ^ chunk, WY_P2);
872                }
873
874                let tail_start = chunks * 8;
875                if tail_start < len {
876                    let mut tail = 0u64;
877                    for (j, &b) in bytes[tail_start..].iter().enumerate() {
878                        tail |= (b as u64) << (j * 8);
879                    }
880                    h = wymix(h ^ tail, WY_P1);
881                }
882
883                state.write_u64(h);
884            }
885        }
886    }
887}
888
889// Note: PartialOrd intentionally differs from Ord for SQL semantics
890// - PartialOrd: SQL comparison (NULL returns None, cross-type numeric comparison)
891// - Ord: BTreeMap ordering (NULLs first, type discriminant ordering)
892#[allow(clippy::non_canonical_partial_ord_impl)]
893impl PartialOrd for Value {
894    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
895        // Use the original compare method for semantic correctness in SQL operations
896        // This preserves NULL comparison semantics (returning None for NULL comparisons)
897        // and proper cross-type numeric comparison (Integer vs Float)
898        self.compare(other).ok()
899    }
900}
901
902/// Total ordering implementation for Value
903///
904/// This is required for using Value as a key in BTreeMap/BTreeSet.
905/// The ordering is defined as follows:
906/// 1. NULLs are always ordered first (smallest)
907/// 2. Numeric types (Integer, Float) are compared by numeric value (consistent with PartialEq)
908/// 3. Other different data types are ordered by their type discriminant
909/// 4. Same data types use their natural ordering
910///
911/// IMPORTANT: This ordering MUST be consistent with PartialEq. Since Integer(5) == Float(5.0)
912/// per PartialEq, we must ensure Integer(5).cmp(&Float(5.0)) == Ordering::Equal.
913/// Violating this contract causes BTreeMap corruption.
914///
915/// Note: This differs from SQL NULL semantics where NULL comparisons
916/// return UNKNOWN. This ordering is only for internal index structure.
917impl Ord for Value {
918    fn cmp(&self, other: &Self) -> Ordering {
919        // Handle NULL comparisons - NULLs are ordered first
920        match (self.is_null(), other.is_null()) {
921            (true, true) => return Ordering::Equal,
922            (true, false) => return Ordering::Less,
923            (false, true) => return Ordering::Greater,
924            (false, false) => {} // Continue to value comparison
925        }
926
927        // Cross-type numeric comparison: Integer vs Float
928        // This MUST be consistent with PartialEq where Integer(5) == Float(5.0)
929        match (self, other) {
930            (Value::Integer(i), Value::Float(f)) => {
931                let i_as_f64 = *i as f64;
932                // Handle NaN: NaN is ordered last
933                if f.is_nan() {
934                    return Ordering::Less; // Any number < NaN
935                }
936                return i_as_f64.partial_cmp(f).unwrap_or(Ordering::Equal);
937            }
938            (Value::Float(f), Value::Integer(i)) => {
939                let i_as_f64 = *i as f64;
940                // Handle NaN: NaN is ordered last
941                if f.is_nan() {
942                    return Ordering::Greater; // NaN > any number
943                }
944                return f.partial_cmp(&i_as_f64).unwrap_or(Ordering::Equal);
945            }
946            _ => {} // Continue to same-type comparison
947        }
948
949        // Helper function to get type discriminant for ordering
950        fn type_discriminant(v: &Value) -> u8 {
951            match v {
952                Value::Null(_) => 0,
953                Value::Boolean(_) => 1,
954                // Integer and Float share the same discriminant for ordering purposes
955                // This ensures they sort together by numeric value
956                Value::Integer(_) | Value::Float(_) => 2,
957                Value::Text(_) => 3,
958                Value::Timestamp(_) => 4,
959                Value::Json(_) => 5,
960            }
961        }
962
963        let self_disc = type_discriminant(self);
964        let other_disc = type_discriminant(other);
965
966        // Different types: order by type discriminant
967        if self_disc != other_disc {
968            return self_disc.cmp(&other_disc);
969        }
970
971        // Same type comparison
972        match (self, other) {
973            (Value::Integer(a), Value::Integer(b)) => a.cmp(b),
974            (Value::Float(a), Value::Float(b)) => {
975                // Handle NaN: NaN is ordered last
976                match (a.is_nan(), b.is_nan()) {
977                    (true, true) => Ordering::Equal,
978                    (true, false) => Ordering::Greater,
979                    (false, true) => Ordering::Less,
980                    (false, false) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
981                }
982            }
983            (Value::Text(a), Value::Text(b)) => a.cmp(b),
984            (Value::Boolean(a), Value::Boolean(b)) => a.cmp(b),
985            (Value::Timestamp(a), Value::Timestamp(b)) => a.cmp(b),
986            (Value::Json(a), Value::Json(b)) => a.cmp(b), // Lexicographic for JSON
987            _ => Ordering::Equal,                         // Should not reach here
988        }
989    }
990}
991
992// =========================================================================
993// From implementations for convenient construction
994// =========================================================================
995
996impl From<i64> for Value {
997    fn from(v: i64) -> Self {
998        Value::Integer(v)
999    }
1000}
1001
1002impl From<i32> for Value {
1003    fn from(v: i32) -> Self {
1004        Value::Integer(v as i64)
1005    }
1006}
1007
1008impl From<i16> for Value {
1009    fn from(v: i16) -> Self {
1010        Value::Integer(v as i64)
1011    }
1012}
1013
1014impl From<i8> for Value {
1015    fn from(v: i8) -> Self {
1016        Value::Integer(v as i64)
1017    }
1018}
1019
1020impl From<u32> for Value {
1021    fn from(v: u32) -> Self {
1022        Value::Integer(v as i64)
1023    }
1024}
1025
1026impl From<u16> for Value {
1027    fn from(v: u16) -> Self {
1028        Value::Integer(v as i64)
1029    }
1030}
1031
1032impl From<u8> for Value {
1033    fn from(v: u8) -> Self {
1034        Value::Integer(v as i64)
1035    }
1036}
1037
1038impl From<f64> for Value {
1039    fn from(v: f64) -> Self {
1040        Value::Float(v)
1041    }
1042}
1043
1044impl From<f32> for Value {
1045    fn from(v: f32) -> Self {
1046        Value::Float(v as f64)
1047    }
1048}
1049
1050impl From<String> for Value {
1051    fn from(v: String) -> Self {
1052        Value::Text(SmartString::from_string(v))
1053    }
1054}
1055
1056impl From<&str> for Value {
1057    fn from(v: &str) -> Self {
1058        Value::Text(SmartString::from(v))
1059    }
1060}
1061
1062impl From<Arc<str>> for Value {
1063    fn from(v: Arc<str>) -> Self {
1064        Value::Text(SmartString::from(v.as_ref()))
1065    }
1066}
1067
1068impl From<bool> for Value {
1069    fn from(v: bool) -> Self {
1070        Value::Boolean(v)
1071    }
1072}
1073
1074impl From<DateTime<Utc>> for Value {
1075    fn from(v: DateTime<Utc>) -> Self {
1076        Value::Timestamp(v)
1077    }
1078}
1079
1080impl<T: Into<Value>> From<Option<T>> for Value {
1081    fn from(v: Option<T>) -> Self {
1082        match v {
1083            Some(val) => val.into(),
1084            None => Value::Null(DataType::Null),
1085        }
1086    }
1087}
1088
1089// =========================================================================
1090// Helper functions
1091// =========================================================================
1092
1093/// Parse a timestamp string with multiple format support
1094pub fn parse_timestamp(s: &str) -> Result<DateTime<Utc>> {
1095    let s = s.trim();
1096
1097    // Try each timestamp format
1098    for format in TIMESTAMP_FORMATS {
1099        if let Ok(dt) = DateTime::parse_from_str(s, format) {
1100            return Ok(dt.with_timezone(&Utc));
1101        }
1102        // Try parsing as naive datetime and assume UTC
1103        if let Ok(ndt) = NaiveDateTime::parse_from_str(s, format) {
1104            return Ok(Utc.from_utc_datetime(&ndt));
1105        }
1106    }
1107
1108    // Try date-only formats
1109    if let Ok(date) = NaiveDate::parse_from_str(s, "%Y-%m-%d") {
1110        let datetime = date.and_hms_opt(0, 0, 0).unwrap();
1111        return Ok(Utc.from_utc_datetime(&datetime));
1112    }
1113
1114    // Try time-only formats (use today's date)
1115    for format in TIME_FORMATS {
1116        if let Ok(time) = NaiveTime::parse_from_str(s, format) {
1117            let today = Utc::now().date_naive();
1118            let datetime = today.and_time(time);
1119            return Ok(Utc.from_utc_datetime(&datetime));
1120        }
1121    }
1122
1123    Err(Error::parse(format!("invalid timestamp format: {}", s)))
1124}
1125
1126/// Format a float value consistently
1127fn format_float(v: f64) -> String {
1128    // Handle special cases
1129    if v.is_nan() {
1130        return "NaN".to_string();
1131    }
1132    if v.is_infinite() {
1133        return if v.is_sign_positive() {
1134            "Infinity"
1135        } else {
1136            "-Infinity"
1137        }
1138        .to_string();
1139    }
1140
1141    let abs_v = v.abs();
1142
1143    // Use scientific notation for very large or very small numbers
1144    if abs_v != 0.0 && !(1e-4..1e15).contains(&abs_v) {
1145        // Use scientific notation with up to 15 significant digits
1146        let s = format!("{:e}", v);
1147        // Clean up trailing zeros in mantissa
1148        if let Some(e_pos) = s.find('e') {
1149            let (mantissa, exp) = s.split_at(e_pos);
1150            let clean_mantissa = if mantissa.contains('.') {
1151                mantissa
1152                    .trim_end_matches('0')
1153                    .trim_end_matches('.')
1154                    .to_string()
1155            } else {
1156                mantissa.to_string()
1157            };
1158            return format!("{}{}", clean_mantissa, exp);
1159        }
1160        return s;
1161    }
1162
1163    if v.fract() == 0.0 {
1164        // Integer-like float, format without decimal
1165        format!("{:.0}", v)
1166    } else {
1167        // Use standard representation for normal range
1168        let s = format!("{:?}", v);
1169        // Remove trailing zeros after decimal point
1170        if s.contains('.') && !s.contains('e') && !s.contains('E') {
1171            s.trim_end_matches('0').trim_end_matches('.').to_string()
1172        } else {
1173            s
1174        }
1175    }
1176}
1177
1178/// Compare two floats with proper NaN handling
1179fn compare_floats(a: f64, b: f64) -> Ordering {
1180    // Handle NaN: treat as greater than all other values for consistency
1181    match (a.is_nan(), b.is_nan()) {
1182        (true, true) => Ordering::Equal,
1183        (true, false) => Ordering::Greater,
1184        (false, true) => Ordering::Less,
1185        (false, false) => a.partial_cmp(&b).unwrap_or(Ordering::Equal),
1186    }
1187}
1188
1189#[cfg(test)]
1190mod tests {
1191    use super::*;
1192    use chrono::{Datelike, Timelike};
1193
1194    // =========================================================================
1195    // Size verification tests
1196    // =========================================================================
1197
1198    #[test]
1199    fn test_value_size() {
1200        use std::mem::size_of;
1201
1202        // Value must be exactly 16 bytes for memory efficiency
1203        assert_eq!(
1204            size_of::<Value>(),
1205            16,
1206            "Value should be 16 bytes, got {}",
1207            size_of::<Value>()
1208        );
1209
1210        // Option<Value> should also be 16 bytes due to niche optimization
1211        assert_eq!(
1212            size_of::<Option<Value>>(),
1213            16,
1214            "Option<Value> should be 16 bytes (niche optimization), got {}",
1215            size_of::<Option<Value>>()
1216        );
1217    }
1218
1219    // =========================================================================
1220    // Constructor tests
1221    // =========================================================================
1222
1223    #[test]
1224    fn test_constructors() {
1225        assert!(Value::null(DataType::Integer).is_null());
1226        assert_eq!(Value::integer(42).as_int64(), Some(42));
1227        assert_eq!(Value::float(3.5).as_float64(), Some(3.5));
1228        assert_eq!(Value::text("hello").as_str(), Some("hello"));
1229        assert_eq!(Value::boolean(true).as_boolean(), Some(true));
1230        assert!(Value::json(r#"{"key": "value"}"#).as_json().is_some());
1231    }
1232
1233    #[test]
1234    fn test_from_implementations() {
1235        let v: Value = 42i64.into();
1236        assert_eq!(v.as_int64(), Some(42));
1237
1238        let v: Value = 3.5f64.into();
1239        assert_eq!(v.as_float64(), Some(3.5));
1240
1241        let v: Value = "hello".into();
1242        assert_eq!(v.as_str(), Some("hello"));
1243
1244        let v: Value = true.into();
1245        assert_eq!(v.as_boolean(), Some(true));
1246
1247        let v: Value = Option::<i64>::None.into();
1248        assert!(v.is_null());
1249
1250        let v: Value = Some(42i64).into();
1251        assert_eq!(v.as_int64(), Some(42));
1252    }
1253
1254    // =========================================================================
1255    // Type accessor tests
1256    // =========================================================================
1257
1258    #[test]
1259    fn test_data_type() {
1260        assert_eq!(
1261            Value::null(DataType::Integer).data_type(),
1262            DataType::Integer
1263        );
1264        assert_eq!(Value::integer(42).data_type(), DataType::Integer);
1265        assert_eq!(Value::float(3.5).data_type(), DataType::Float);
1266        assert_eq!(Value::text("hello").data_type(), DataType::Text);
1267        assert_eq!(Value::boolean(true).data_type(), DataType::Boolean);
1268        assert_eq!(
1269            Value::Timestamp(Utc::now()).data_type(),
1270            DataType::Timestamp
1271        );
1272        assert_eq!(Value::json("{}").data_type(), DataType::Json);
1273    }
1274
1275    // =========================================================================
1276    // AsXxx conversion tests
1277    // =========================================================================
1278
1279    #[test]
1280    fn test_as_int64() {
1281        // Direct integer
1282        assert_eq!(Value::integer(42).as_int64(), Some(42));
1283
1284        // Float to integer (truncates)
1285        assert_eq!(Value::float(3.7).as_int64(), Some(3));
1286        assert_eq!(Value::float(-3.7).as_int64(), Some(-3));
1287
1288        // String to integer
1289        assert_eq!(Value::text("42").as_int64(), Some(42));
1290        assert_eq!(Value::text("-42").as_int64(), Some(-42));
1291        assert_eq!(Value::text("3.7").as_int64(), Some(3)); // Parse as float, convert
1292
1293        // Boolean to integer
1294        assert_eq!(Value::boolean(true).as_int64(), Some(1));
1295        assert_eq!(Value::boolean(false).as_int64(), Some(0));
1296
1297        // NULL returns None
1298        assert_eq!(Value::null(DataType::Integer).as_int64(), None);
1299
1300        // Invalid string
1301        assert_eq!(Value::text("not a number").as_int64(), None);
1302    }
1303
1304    #[test]
1305    fn test_as_float64() {
1306        // Direct float
1307        assert_eq!(Value::float(3.5).as_float64(), Some(3.5));
1308
1309        // Integer to float
1310        assert_eq!(Value::integer(42).as_float64(), Some(42.0));
1311
1312        // String to float
1313        assert_eq!(Value::text("3.5").as_float64(), Some(3.5));
1314
1315        // Boolean to float
1316        assert_eq!(Value::boolean(true).as_float64(), Some(1.0));
1317        assert_eq!(Value::boolean(false).as_float64(), Some(0.0));
1318
1319        // NULL returns None
1320        assert_eq!(Value::null(DataType::Float).as_float64(), None);
1321    }
1322
1323    #[test]
1324    fn test_as_boolean() {
1325        // Direct boolean
1326        assert_eq!(Value::boolean(true).as_boolean(), Some(true));
1327        assert_eq!(Value::boolean(false).as_boolean(), Some(false));
1328
1329        // Integer to boolean
1330        assert_eq!(Value::integer(1).as_boolean(), Some(true));
1331        assert_eq!(Value::integer(0).as_boolean(), Some(false));
1332        assert_eq!(Value::integer(-1).as_boolean(), Some(true));
1333
1334        // Float to boolean
1335        assert_eq!(Value::float(1.0).as_boolean(), Some(true));
1336        assert_eq!(Value::float(0.0).as_boolean(), Some(false));
1337
1338        // String to boolean (various string values)
1339        assert_eq!(Value::text("true").as_boolean(), Some(true));
1340        assert_eq!(Value::text("TRUE").as_boolean(), Some(true));
1341        assert_eq!(Value::text("t").as_boolean(), Some(true));
1342        assert_eq!(Value::text("yes").as_boolean(), Some(true));
1343        assert_eq!(Value::text("y").as_boolean(), Some(true));
1344        assert_eq!(Value::text("1").as_boolean(), Some(true));
1345        assert_eq!(Value::text("false").as_boolean(), Some(false));
1346        assert_eq!(Value::text("FALSE").as_boolean(), Some(false));
1347        assert_eq!(Value::text("f").as_boolean(), Some(false));
1348        assert_eq!(Value::text("no").as_boolean(), Some(false));
1349        assert_eq!(Value::text("n").as_boolean(), Some(false));
1350        assert_eq!(Value::text("0").as_boolean(), Some(false));
1351        assert_eq!(Value::text("").as_boolean(), Some(false));
1352
1353        // Numeric strings
1354        assert_eq!(Value::text("42").as_boolean(), Some(true));
1355        assert_eq!(Value::text("0.0").as_boolean(), Some(false));
1356    }
1357
1358    #[test]
1359    fn test_as_string() {
1360        // Direct string
1361        assert_eq!(Value::text("hello").as_string(), Some("hello".to_string()));
1362
1363        // Integer to string
1364        assert_eq!(Value::integer(42).as_string(), Some("42".to_string()));
1365
1366        // Float to string
1367        assert_eq!(Value::float(3.5).as_string(), Some("3.5".to_string()));
1368
1369        // Boolean to string
1370        assert_eq!(Value::boolean(true).as_string(), Some("true".to_string()));
1371        assert_eq!(Value::boolean(false).as_string(), Some("false".to_string()));
1372
1373        // NULL returns None
1374        assert_eq!(Value::null(DataType::Text).as_string(), None);
1375    }
1376
1377    // =========================================================================
1378    // Equality tests
1379    // =========================================================================
1380
1381    #[test]
1382    fn test_equality() {
1383        // Same type equality
1384        assert_eq!(Value::integer(42), Value::integer(42));
1385        assert_ne!(Value::integer(42), Value::integer(43));
1386
1387        assert_eq!(Value::float(3.5), Value::float(3.5));
1388        assert_ne!(Value::float(3.5), Value::float(3.15));
1389
1390        assert_eq!(Value::text("hello"), Value::text("hello"));
1391        assert_ne!(Value::text("hello"), Value::text("world"));
1392
1393        assert_eq!(Value::boolean(true), Value::boolean(true));
1394        assert_ne!(Value::boolean(true), Value::boolean(false));
1395
1396        // NULL equality
1397        assert_eq!(Value::null(DataType::Integer), Value::null(DataType::Float));
1398        assert_ne!(Value::null(DataType::Integer), Value::integer(0));
1399
1400        // Cross-type numeric comparison: Integer and Float with same value ARE equal
1401        // This is important for queries like WHERE id = 5.0 or WHERE price = 100
1402        assert_eq!(Value::integer(1), Value::float(1.0));
1403        assert_eq!(Value::integer(5), Value::float(5.0));
1404        assert_ne!(Value::integer(1), Value::float(1.5)); // Different values are not equal
1405
1406        // Different non-numeric types are not equal
1407        assert_ne!(Value::text("1"), Value::integer(1));
1408    }
1409
1410    #[test]
1411    fn test_float_nan_equality() {
1412        // NaN handling: NaN == NaN in our implementation (for consistency)
1413        let nan = Value::float(f64::NAN);
1414        assert_eq!(nan, nan.clone());
1415    }
1416
1417    // =========================================================================
1418    // Comparison tests
1419    // =========================================================================
1420
1421    #[test]
1422    fn test_compare_integers() {
1423        assert_eq!(
1424            Value::integer(1).compare(&Value::integer(2)).unwrap(),
1425            Ordering::Less
1426        );
1427        assert_eq!(
1428            Value::integer(2).compare(&Value::integer(2)).unwrap(),
1429            Ordering::Equal
1430        );
1431        assert_eq!(
1432            Value::integer(3).compare(&Value::integer(2)).unwrap(),
1433            Ordering::Greater
1434        );
1435    }
1436
1437    #[test]
1438    fn test_compare_floats() {
1439        assert_eq!(
1440            Value::float(1.0).compare(&Value::float(2.0)).unwrap(),
1441            Ordering::Less
1442        );
1443        assert_eq!(
1444            Value::float(2.0).compare(&Value::float(2.0)).unwrap(),
1445            Ordering::Equal
1446        );
1447        assert_eq!(
1448            Value::float(3.0).compare(&Value::float(2.0)).unwrap(),
1449            Ordering::Greater
1450        );
1451    }
1452
1453    #[test]
1454    fn test_compare_cross_type_numeric() {
1455        // Integer vs Float comparison
1456        assert_eq!(
1457            Value::integer(1).compare(&Value::float(2.0)).unwrap(),
1458            Ordering::Less
1459        );
1460        assert_eq!(
1461            Value::integer(2).compare(&Value::float(2.0)).unwrap(),
1462            Ordering::Equal
1463        );
1464        assert_eq!(
1465            Value::float(3.0).compare(&Value::integer(2)).unwrap(),
1466            Ordering::Greater
1467        );
1468    }
1469
1470    #[test]
1471    fn test_compare_strings() {
1472        assert_eq!(
1473            Value::text("a").compare(&Value::text("b")).unwrap(),
1474            Ordering::Less
1475        );
1476        assert_eq!(
1477            Value::text("b").compare(&Value::text("b")).unwrap(),
1478            Ordering::Equal
1479        );
1480        assert_eq!(
1481            Value::text("c").compare(&Value::text("b")).unwrap(),
1482            Ordering::Greater
1483        );
1484    }
1485
1486    #[test]
1487    fn test_compare_null() {
1488        // NULL comparisons
1489        assert_eq!(
1490            Value::null(DataType::Integer)
1491                .compare(&Value::null(DataType::Float))
1492                .unwrap(),
1493            Ordering::Equal
1494        );
1495
1496        // NULL vs non-NULL should error
1497        assert!(Value::null(DataType::Integer)
1498            .compare(&Value::integer(0))
1499            .is_err());
1500        assert!(Value::integer(0)
1501            .compare(&Value::null(DataType::Integer))
1502            .is_err());
1503    }
1504
1505    #[test]
1506    fn test_compare_json_error() {
1507        // JSON comparison only allows equality
1508        let j1 = Value::json(r#"{"a": 1}"#);
1509        let j2 = Value::json(r#"{"b": 2}"#);
1510        assert!(j1.compare(&j2).is_err());
1511
1512        // Same JSON values are equal
1513        let j3 = Value::json(r#"{"a": 1}"#);
1514        assert_eq!(j1.compare(&j3).unwrap(), Ordering::Equal);
1515    }
1516
1517    // =========================================================================
1518    // Timestamp parsing tests
1519    // =========================================================================
1520
1521    #[test]
1522    fn test_parse_timestamp() {
1523        // RFC3339
1524        let ts = parse_timestamp("2024-01-15T10:30:00Z").unwrap();
1525        assert_eq!(ts.year(), 2024);
1526        assert_eq!(ts.month(), 1);
1527        assert_eq!(ts.day(), 15);
1528        assert_eq!(ts.hour(), 10);
1529        assert_eq!(ts.minute(), 30);
1530
1531        // SQL format
1532        let ts = parse_timestamp("2024-01-15 10:30:00").unwrap();
1533        assert_eq!(ts.year(), 2024);
1534
1535        // Date only
1536        let ts = parse_timestamp("2024-01-15").unwrap();
1537        assert_eq!(ts.year(), 2024);
1538        assert_eq!(ts.hour(), 0);
1539
1540        // Invalid format
1541        assert!(parse_timestamp("not a date").is_err());
1542    }
1543
1544    // =========================================================================
1545    // Display tests
1546    // =========================================================================
1547
1548    #[test]
1549    fn test_display() {
1550        assert_eq!(Value::null(DataType::Integer).to_string(), "NULL");
1551        assert_eq!(Value::integer(42).to_string(), "42");
1552        assert_eq!(Value::float(3.5).to_string(), "3.5");
1553        assert_eq!(Value::text("hello").to_string(), "hello");
1554        assert_eq!(Value::boolean(true).to_string(), "true");
1555        assert_eq!(Value::boolean(false).to_string(), "false");
1556    }
1557
1558    // =========================================================================
1559    // Hash tests
1560    // =========================================================================
1561
1562    #[test]
1563    fn test_hash() {
1564        use rustc_hash::FxHashSet;
1565
1566        let mut set = FxHashSet::default();
1567        set.insert(Value::integer(42));
1568        set.insert(Value::integer(42)); // Duplicate
1569        set.insert(Value::integer(43));
1570
1571        assert_eq!(set.len(), 2);
1572        assert!(set.contains(&Value::integer(42)));
1573        assert!(set.contains(&Value::integer(43)));
1574    }
1575
1576    #[test]
1577    fn test_hash_integer_float_consistency() {
1578        use std::hash::{DefaultHasher, Hash, Hasher};
1579
1580        fn hash_value(v: &Value) -> u64 {
1581            let mut hasher = DefaultHasher::new();
1582            v.hash(&mut hasher);
1583            hasher.finish()
1584        }
1585
1586        // Basic case: Integer(5) and Float(5.0) must hash the same
1587        assert_eq!(
1588            hash_value(&Value::integer(5)),
1589            hash_value(&Value::float(5.0))
1590        );
1591        assert_eq!(
1592            hash_value(&Value::integer(-100)),
1593            hash_value(&Value::float(-100.0))
1594        );
1595        assert_eq!(
1596            hash_value(&Value::integer(0)),
1597            hash_value(&Value::float(0.0))
1598        );
1599
1600        // Fractional floats should NOT hash the same as any integer
1601        assert_ne!(
1602            hash_value(&Value::float(5.5)),
1603            hash_value(&Value::integer(5))
1604        );
1605        assert_ne!(
1606            hash_value(&Value::float(5.5)),
1607            hash_value(&Value::integer(6))
1608        );
1609
1610        // Large integers within safe range
1611        let safe_max = (1_i64 << 53) - 1; // 9007199254740991
1612        assert_eq!(
1613            hash_value(&Value::integer(safe_max)),
1614            hash_value(&Value::float(safe_max as f64))
1615        );
1616        assert_eq!(
1617            hash_value(&Value::integer(-safe_max)),
1618            hash_value(&Value::float(-safe_max as f64))
1619        );
1620
1621        // Boundary case: 2^53 (outside safe range, uses f64.to_bits())
1622        let boundary = 1_i64 << 53; // 9007199254740992
1623        assert_eq!(
1624            hash_value(&Value::integer(boundary)),
1625            hash_value(&Value::float(boundary as f64))
1626        );
1627
1628        // Large integers that round to same f64 should hash same as that f64
1629        // 2^53 + 1 rounds to 2^53 in f64
1630        let large = boundary + 1; // 9007199254740993
1631        let large_as_f64 = large as f64; // rounds to 9007199254740992.0
1632        assert_eq!(
1633            hash_value(&Value::integer(large)),
1634            hash_value(&Value::float(large_as_f64))
1635        );
1636    }
1637
1638    #[test]
1639    fn test_hash_in_hashmap() {
1640        use rustc_hash::FxHashMap;
1641
1642        // Test that Integer and Float can be used as equivalent keys
1643        let mut map = FxHashMap::default();
1644        map.insert(Value::integer(42), "int");
1645
1646        // Looking up with Float(42.0) should find the Integer(42) entry
1647        assert_eq!(map.get(&Value::float(42.0)), Some(&"int"));
1648
1649        // Inserting Float(42.0) should overwrite Integer(42)
1650        map.insert(Value::float(42.0), "float");
1651        assert_eq!(map.len(), 1);
1652        assert_eq!(map.get(&Value::integer(42)), Some(&"float"));
1653    }
1654
1655    #[test]
1656    fn test_hash_nan_consistency() {
1657        use std::hash::{DefaultHasher, Hash, Hasher};
1658
1659        fn hash_value(v: &Value) -> u64 {
1660            let mut hasher = DefaultHasher::new();
1661            v.hash(&mut hasher);
1662            hasher.finish()
1663        }
1664
1665        // All NaN values must hash the same (they're equal in PartialEq)
1666        let nan1 = Value::float(f64::NAN);
1667        // Use a different NaN representation (quiet vs signaling doesn't matter for hash equality)
1668        let nan2 = Value::float(f64::from_bits(0x7ff8000000000001)); // Another NaN bit pattern
1669        let nan3 = Value::float(f64::INFINITY - f64::INFINITY);
1670
1671        assert_eq!(hash_value(&nan1), hash_value(&nan2));
1672        assert_eq!(hash_value(&nan2), hash_value(&nan3));
1673
1674        // Verify they're equal in PartialEq
1675        assert_eq!(nan1, nan2);
1676        assert_eq!(nan2, nan3);
1677    }
1678}