Skip to main content

reddb_types/
types.rs

1//! RedDB Type System
2//!
3//! Defines the core data types supported by RedDB, including:
4//! - Primitive types (Integer, Float, Text, Blob, Boolean)
5//! - Network types (IpAddr, MacAddr)
6//! - Temporal types (Timestamp, Duration)
7//! - Vector types (for similarity search)
8//!
9//! All types support efficient binary serialization for storage.
10
11use std::fmt;
12use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
13
14/// Structured SQL type name used by the parser/analyzer boundary.
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct SqlTypeName {
17    pub name: String,
18    pub modifiers: Vec<TypeModifier>,
19}
20
21impl SqlTypeName {
22    pub fn new(name: impl Into<String>) -> Self {
23        Self {
24            name: name.into(),
25            modifiers: Vec::new(),
26        }
27    }
28
29    pub fn simple(name: impl Into<String>) -> Self {
30        Self::new(name)
31    }
32
33    pub fn with_modifiers(mut self, modifiers: Vec<TypeModifier>) -> Self {
34        self.modifiers = modifiers;
35        self
36    }
37
38    pub fn base_name(&self) -> String {
39        self.name.to_ascii_uppercase()
40    }
41
42    pub fn parse_declared(input: &str) -> Self {
43        parse_sql_type_name(input).unwrap_or_else(|| Self::simple(input.trim()))
44    }
45
46    pub fn enum_variants(&self) -> Option<Vec<String>> {
47        if self.base_name() != "ENUM" {
48            return None;
49        }
50        let mut variants = Vec::new();
51        for modifier in &self.modifiers {
52            if let TypeModifier::StringLiteral(value) = modifier {
53                variants.push(value.clone());
54            } else {
55                return None;
56            }
57        }
58        Some(variants)
59    }
60
61    pub fn array_element_type(&self) -> Option<String> {
62        if self.base_name() != "ARRAY" {
63            return None;
64        }
65        self.modifiers.iter().find_map(|modifier| match modifier {
66            TypeModifier::Type(inner) => Some(inner.to_string()),
67            TypeModifier::Ident(name) => Some(name.to_ascii_uppercase()),
68            _ => None,
69        })
70    }
71
72    pub fn decimal_precision(&self) -> Option<u8> {
73        match self.base_name().as_str() {
74            "DECIMAL" | "NUMERIC" => self.modifiers.iter().find_map(|modifier| match modifier {
75                TypeModifier::Number(value) => u8::try_from(*value).ok(),
76                _ => None,
77            }),
78            _ => None,
79        }
80    }
81}
82
83impl fmt::Display for SqlTypeName {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        write!(f, "{}", self.base_name())?;
86        if !self.modifiers.is_empty() {
87            write!(f, "(")?;
88            for (idx, modifier) in self.modifiers.iter().enumerate() {
89                if idx > 0 {
90                    write!(f, ",")?;
91                }
92                write!(f, "{modifier}")?;
93            }
94            write!(f, ")")?;
95        }
96        Ok(())
97    }
98}
99
100/// Type modifiers used by SQL types like `DECIMAL(10)` or `ARRAY(TEXT)`.
101#[derive(Debug, Clone, PartialEq, Eq)]
102pub enum TypeModifier {
103    Number(u32),
104    Ident(String),
105    StringLiteral(String),
106    Type(Box<SqlTypeName>),
107}
108
109impl fmt::Display for TypeModifier {
110    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111        match self {
112            Self::Number(value) => write!(f, "{value}"),
113            Self::Ident(value) => write!(f, "{}", value.to_ascii_uppercase()),
114            Self::StringLiteral(value) => write!(f, "'{value}'"),
115            Self::Type(value) => write!(f, "{value}"),
116        }
117    }
118}
119
120fn parse_sql_type_name(input: &str) -> Option<SqlTypeName> {
121    let input = input.trim();
122    if input.is_empty() {
123        return None;
124    }
125
126    let open = input.find('(');
127    let close = input.rfind(')');
128    match (open, close) {
129        (Some(open), Some(close)) if close > open => {
130            let name = input[..open].trim();
131            let inner = &input[open + 1..close];
132            let modifiers = split_type_modifiers(inner)
133                .into_iter()
134                .map(parse_type_modifier)
135                .collect::<Option<Vec<_>>>()?;
136            Some(SqlTypeName::new(name).with_modifiers(modifiers))
137        }
138        _ => Some(SqlTypeName::new(input)),
139    }
140}
141
142fn parse_type_modifier(input: String) -> Option<TypeModifier> {
143    let value = input.trim();
144    if value.is_empty() {
145        return None;
146    }
147    if value.starts_with('\'') && value.ends_with('\'') && value.len() >= 2 {
148        return Some(TypeModifier::StringLiteral(
149            value[1..value.len() - 1].to_string(),
150        ));
151    }
152    if let Ok(number) = value.parse::<u32>() {
153        return Some(TypeModifier::Number(number));
154    }
155    if value.contains('(') {
156        return parse_sql_type_name(value).map(|inner| TypeModifier::Type(Box::new(inner)));
157    }
158    Some(TypeModifier::Ident(value.to_string()))
159}
160
161fn split_type_modifiers(input: &str) -> Vec<String> {
162    let mut parts = Vec::new();
163    let mut current = String::new();
164    let mut depth = 0usize;
165    let mut in_string = false;
166
167    for ch in input.chars() {
168        match ch {
169            '\'' => {
170                in_string = !in_string;
171                current.push(ch);
172            }
173            '(' if !in_string => {
174                depth += 1;
175                current.push(ch);
176            }
177            ')' if !in_string => {
178                depth = depth.saturating_sub(1);
179                current.push(ch);
180            }
181            ',' if !in_string && depth == 0 => {
182                parts.push(current.trim().to_string());
183                current.clear();
184            }
185            _ => current.push(ch),
186        }
187    }
188
189    if !current.trim().is_empty() {
190        parts.push(current.trim().to_string());
191    }
192
193    parts
194}
195
196/// Type identifier for column definitions
197#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
198#[repr(u8)]
199pub enum DataType {
200    /// Polymorphic / "any" placeholder. Used by function-catalog
201    /// signatures (e.g. `JSON_SET(json, path, value)` where `value`
202    /// accepts any scalar) so the resolver can skip concrete type
203    /// checking for that argument. Never appears in stored schemas.
204    Unknown = 0,
205    /// Signed 64-bit integer
206    Integer = 1,
207    /// Unsigned 64-bit integer
208    UnsignedInteger = 2,
209    /// 64-bit floating point
210    Float = 3,
211    /// Variable-length UTF-8 text
212    Text = 4,
213    /// Variable-length binary data
214    Blob = 5,
215    /// Boolean (true/false)
216    Boolean = 6,
217    /// Unix timestamp (seconds since epoch)
218    Timestamp = 7,
219    /// Duration in milliseconds
220    Duration = 8,
221    /// IPv4 or IPv6 address
222    IpAddr = 9,
223    /// MAC address (6 bytes)
224    MacAddr = 10,
225    /// Fixed-dimension float vector (for similarity search)
226    Vector = 11,
227    /// Nullable wrapper (stores inner type in high nibble)
228    Nullable = 12,
229    /// JSON-like structured data
230    Json = 13,
231    /// UUID (16 bytes)
232    Uuid = 14,
233    /// Reference to a graph node (for unified queries)
234    NodeRef = 15,
235    /// Reference to a graph edge
236    EdgeRef = 16,
237    /// Reference to a vector in vector store
238    VectorRef = 17,
239    /// Reference to a table row (table_id, row_id)
240    RowRef = 18,
241    /// RGB color (3 bytes)
242    Color = 19,
243    /// Email address (validated, stored lowercase)
244    Email = 20,
245    /// URL (validated)
246    Url = 21,
247    /// Phone number (stored as u64 digits)
248    Phone = 22,
249    /// Semantic version (packed u32: major*1M + minor*1K + patch)
250    Semver = 23,
251    /// CIDR notation (IPv4 u32 + prefix u8 = 5 bytes)
252    Cidr = 24,
253    /// Date only (i32 days since Unix epoch, no time)
254    Date = 25,
255    /// Time only (u32 milliseconds since midnight)
256    Time = 26,
257    /// Fixed-point decimal (i64 with configurable precision)
258    Decimal = 27,
259    /// Enumerated type (u8 index into variant list)
260    Enum = 28,
261    /// Array of values (homogeneous)
262    Array = 29,
263    /// Timestamp with millisecond precision (i64 ms since epoch)
264    TimestampMs = 30,
265    /// IPv4 address (u32)
266    Ipv4 = 31,
267    /// IPv6 address ([u8; 16])
268    Ipv6 = 32,
269    /// Network subnet (ip u32 + mask u32)
270    Subnet = 33,
271    /// TCP/UDP port number (u16)
272    Port = 34,
273    /// Geographic latitude (i32 microdegrees)
274    Latitude = 35,
275    /// Geographic longitude (i32 microdegrees)
276    Longitude = 36,
277    /// Geographic point (lat i32 + lon i32)
278    GeoPoint = 37,
279    /// ISO 3166-1 alpha-2 country code ([u8; 2])
280    Country2 = 38,
281    /// ISO 3166-1 alpha-3 country code ([u8; 3])
282    Country3 = 39,
283    /// ISO 639-1 language code ([u8; 2])
284    Lang2 = 40,
285    /// IETF language tag, e.g. "pt-BR" ([u8; 5])
286    Lang5 = 41,
287    /// ISO 4217 currency code ([u8; 3])
288    Currency = 42,
289    /// RGBA color with alpha ([u8; 4])
290    ColorAlpha = 43,
291    /// Signed 64-bit integer (alias for large numbers)
292    BigInt = 44,
293    /// Reference to a KV pair (collection, key string)
294    KeyRef = 45,
295    /// Reference to a document (collection, entity_id)
296    DocRef = 46,
297    /// Reference to a table/collection by name
298    TableRef = 47,
299    /// Reference to a physical storage page
300    PageRef = 48,
301    /// Encrypted secret (AES-256-GCM ciphertext, keyed by vault AES key)
302    Secret = 49,
303    /// Argon2id password hash
304    Password = 50,
305    /// C3 TOAST: zstd-compressed UTF-8 text (> TOAST_THRESHOLD bytes).
306    /// In-memory representation is always `Value::Text` — compression is
307    /// transparent to all callers above the serialization layer.
308    TextZstd = 51,
309    /// C3 TOAST: zstd-compressed binary blob (> TOAST_THRESHOLD bytes).
310    /// In-memory representation is always `Value::Blob`.
311    BlobZstd = 52,
312    /// General asset code (fiat or crypto), validated and normalized uppercase text
313    AssetCode = 53,
314    /// Monetary amount represented as minor units + scale + asset code
315    Money = 54,
316}
317
318/// Type categories used by the Fase 3 coercion resolver. Mirrors
319/// PostgreSQL's `pg_type.h` `typcategory` values. Types in the same
320/// category are candidates for implicit coercion; the preferred flag
321/// breaks ties when multiple candidates match an operator / function
322/// signature.
323///
324/// See the Fase 3 algorithm described in the roadmap file
325/// (`/home/cyber/.claude/plans/squishy-mixing-honey.md`, Parte 4)
326/// for the full candidate-selection heuristic. This enum is the
327/// decoupler that makes that algorithm tractable — without
328/// categories you'd need O(n²) pairwise coercion rules.
329#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
330pub enum TypeCategory {
331    /// Integer, float, decimal — widening promotes int → float → numeric.
332    Numeric,
333    /// Text, blob, enum labels — stringy families that freely coerce
334    /// to text via display.
335    String,
336    /// Boolean only.
337    Boolean,
338    /// Date, time, timestamp — temporal values that share arithmetic
339    /// (`date + interval`, `timestamp - timestamp`, etc.).
340    DateTime,
341    /// Duration / interval.
342    TimeSpan,
343    /// Array of any element type. The element's own category is
344    /// tracked on `DataType::Array` itself; this top-level marker
345    /// distinguishes array-typed operands from scalar ones.
346    Array,
347    /// Network addresses (IPv4, IPv6, CIDR, subnet, MAC).
348    Network,
349    /// Geographic / spatial types (lat / lon / geopoint).
350    Geo,
351    /// Domain-typed values (email, URL, phone, semver, color, locale codes)
352    /// — these are all conceptually strings with extra validation, but
353    /// they get their own category so the coercion resolver can prefer
354    /// their native equality over `text` equality.
355    Domain,
356    /// UUID — distinct category because neither string nor numeric
357    /// coercion makes sense.
358    Uuid,
359    /// Encrypted / protected values (Secret, Password). Callers must
360    /// opt in to any coercion; the resolver treats this category as
361    /// opaque.
362    Opaque,
363    /// Reference to another entity / page / table — opaque to the
364    /// expression layer.
365    Reference,
366    /// Vector of floats.
367    Vector,
368    /// Embedded JSON blob.
369    Json,
370    /// Null / unknown — used as the sentinel for untyped literals
371    /// before the analyzer resolves a concrete type.
372    Unknown,
373}
374
375impl DataType {
376    /// Return the coercion category this type belongs to. Used by
377    /// the Fase 3 operator / function resolver to group candidate
378    /// types when picking an overload.
379    pub fn category(&self) -> TypeCategory {
380        match self {
381            DataType::Integer
382            | DataType::UnsignedInteger
383            | DataType::Float
384            | DataType::Decimal
385            | DataType::BigInt
386            | DataType::Port
387            | DataType::Latitude
388            | DataType::Longitude => TypeCategory::Numeric,
389            DataType::Text | DataType::Blob => TypeCategory::String,
390            DataType::Boolean => TypeCategory::Boolean,
391            DataType::Timestamp | DataType::TimestampMs | DataType::Date | DataType::Time => {
392                TypeCategory::DateTime
393            }
394            DataType::Duration => TypeCategory::TimeSpan,
395            DataType::Array => TypeCategory::Array,
396            DataType::IpAddr
397            | DataType::Ipv4
398            | DataType::Ipv6
399            | DataType::Cidr
400            | DataType::Subnet
401            | DataType::MacAddr => TypeCategory::Network,
402            DataType::GeoPoint => TypeCategory::Geo,
403            DataType::Email
404            | DataType::Url
405            | DataType::Phone
406            | DataType::Semver
407            | DataType::Color
408            | DataType::ColorAlpha
409            | DataType::Country2
410            | DataType::Country3
411            | DataType::Lang2
412            | DataType::Lang5
413            | DataType::Currency
414            | DataType::AssetCode
415            | DataType::Money
416            | DataType::Enum => TypeCategory::Domain,
417            DataType::Uuid => TypeCategory::Uuid,
418            DataType::Secret | DataType::Password => TypeCategory::Opaque,
419            DataType::NodeRef
420            | DataType::EdgeRef
421            | DataType::VectorRef
422            | DataType::RowRef
423            | DataType::KeyRef
424            | DataType::DocRef
425            | DataType::TableRef
426            | DataType::PageRef => TypeCategory::Reference,
427            DataType::Vector => TypeCategory::Vector,
428            DataType::Json => TypeCategory::Json,
429            DataType::Nullable | DataType::Unknown => TypeCategory::Unknown,
430            // C3 TOAST compressed variants — same category as their uncompressed originals.
431            // Callers see Value::Text / Value::Blob after decompression; the DataType is an
432            // on-disk-only detail.
433            DataType::TextZstd => TypeCategory::String,
434            DataType::BlobZstd => TypeCategory::String,
435        }
436    }
437
438    /// Is this type the *preferred* representative of its category?
439    /// When the Fase 3 resolver has two equally-good candidates after
440    /// exact-match counting, it picks the preferred one. Preferences
441    /// are:
442    ///
443    /// - Numeric: `Float` (highest precision, captures all integer
444    ///   values lossily for arithmetic purposes)
445    /// - String: `Text`
446    /// - DateTime: `TimestampMs` (milli precision beats second)
447    /// - Network: `IpAddr` (superset of Ipv4/Ipv6)
448    /// - Domain / Reference / Opaque / others: no preferred member
449    ///   (categories where every type is equally "native" so a tie
450    ///   means the user must be explicit).
451    pub fn is_preferred(&self) -> bool {
452        matches!(
453            self,
454            DataType::Float
455                | DataType::Text
456                | DataType::TimestampMs
457                | DataType::IpAddr
458                | DataType::Boolean
459                | DataType::Uuid
460        )
461    }
462}
463
464impl DataType {
465    /// Serialize type to byte
466    pub fn to_byte(&self) -> u8 {
467        *self as u8
468    }
469
470    /// Deserialize type from byte
471    pub fn from_byte(b: u8) -> Option<Self> {
472        match b {
473            1 => Some(DataType::Integer),
474            2 => Some(DataType::UnsignedInteger),
475            3 => Some(DataType::Float),
476            4 => Some(DataType::Text),
477            5 => Some(DataType::Blob),
478            6 => Some(DataType::Boolean),
479            7 => Some(DataType::Timestamp),
480            8 => Some(DataType::Duration),
481            9 => Some(DataType::IpAddr),
482            10 => Some(DataType::MacAddr),
483            11 => Some(DataType::Vector),
484            12 => Some(DataType::Nullable),
485            13 => Some(DataType::Json),
486            14 => Some(DataType::Uuid),
487            15 => Some(DataType::NodeRef),
488            16 => Some(DataType::EdgeRef),
489            17 => Some(DataType::VectorRef),
490            18 => Some(DataType::RowRef),
491            19 => Some(DataType::Color),
492            20 => Some(DataType::Email),
493            21 => Some(DataType::Url),
494            22 => Some(DataType::Phone),
495            23 => Some(DataType::Semver),
496            24 => Some(DataType::Cidr),
497            25 => Some(DataType::Date),
498            26 => Some(DataType::Time),
499            27 => Some(DataType::Decimal),
500            28 => Some(DataType::Enum),
501            29 => Some(DataType::Array),
502            30 => Some(DataType::TimestampMs),
503            31 => Some(DataType::Ipv4),
504            32 => Some(DataType::Ipv6),
505            33 => Some(DataType::Subnet),
506            34 => Some(DataType::Port),
507            35 => Some(DataType::Latitude),
508            36 => Some(DataType::Longitude),
509            37 => Some(DataType::GeoPoint),
510            38 => Some(DataType::Country2),
511            39 => Some(DataType::Country3),
512            40 => Some(DataType::Lang2),
513            41 => Some(DataType::Lang5),
514            42 => Some(DataType::Currency),
515            43 => Some(DataType::ColorAlpha),
516            44 => Some(DataType::BigInt),
517            45 => Some(DataType::KeyRef),
518            46 => Some(DataType::DocRef),
519            47 => Some(DataType::TableRef),
520            48 => Some(DataType::PageRef),
521            49 => Some(DataType::Secret),
522            50 => Some(DataType::Password),
523            51 => Some(DataType::TextZstd),
524            52 => Some(DataType::BlobZstd),
525            53 => Some(DataType::AssetCode),
526            54 => Some(DataType::Money),
527            _ => None,
528        }
529    }
530
531    /// Resolve a DataType from an SQL-level type name (case-insensitive).
532    /// Accepts short SQL aliases (INT → Integer, STRING → Text) plus the
533    /// canonical reddb names rendered by `Display`. Returns `None` for
534    /// unknown / unsupported names so callers can surface a parse error.
535    pub fn from_sql_name(name: &str) -> Option<Self> {
536        Self::from_sql_type_name(&SqlTypeName::parse_declared(name))
537    }
538
539    /// Resolve a DataType from a parsed SQL type name.
540    pub fn from_sql_type_name(sql_type: &SqlTypeName) -> Option<Self> {
541        let n = sql_type.base_name();
542        Some(match n.as_str() {
543            "BOOL" | "BOOLEAN" => DataType::Boolean,
544            "INT" | "INTEGER" | "INT4" | "SERIAL" => DataType::Integer,
545            "INT2" | "SMALLINT" => DataType::Integer,
546            "INT8" | "BIGINT" | "BIGINT_SIGNED" | "BIGSERIAL" => DataType::BigInt,
547            "UINT" | "UNSIGNED" | "UNSIGNED_INTEGER" | "UNSIGNED INTEGER" => {
548                DataType::UnsignedInteger
549            }
550            "FLOAT" | "DOUBLE" | "REAL" | "FLOAT8" => DataType::Float,
551            "TEXT" | "STRING" | "VARCHAR" | "CHAR" => DataType::Text,
552            "BLOB" | "BYTES" | "BYTEA" => DataType::Blob,
553            "TIMESTAMP" => DataType::Timestamp,
554            "TIMESTAMPTZ" | "TIMESTAMPMS" | "TIMESTAMP_MS" => DataType::TimestampMs,
555            "DURATION" | "INTERVAL" => DataType::Duration,
556            "DATE" => DataType::Date,
557            "TIME" => DataType::Time,
558            "DECIMAL" | "NUMERIC" => DataType::Decimal,
559            "JSON" | "JSONB" => DataType::Json,
560            "UUID" => DataType::Uuid,
561            "IPADDR" | "IP" | "INET" => DataType::IpAddr,
562            "IPV4" => DataType::Ipv4,
563            "IPV6" => DataType::Ipv6,
564            "MACADDR" => DataType::MacAddr,
565            "NODEREF" => DataType::NodeRef,
566            "EDGEREF" => DataType::EdgeRef,
567            "VECTORREF" => DataType::VectorRef,
568            "ROWREF" => DataType::RowRef,
569            "CIDR" => DataType::Cidr,
570            "SUBNET" => DataType::Subnet,
571            "PORT" => DataType::Port,
572            "COLOR" => DataType::Color,
573            "COLOR_ALPHA" | "COLORALPHA" => DataType::ColorAlpha,
574            "EMAIL" => DataType::Email,
575            "URL" => DataType::Url,
576            "PHONE" => DataType::Phone,
577            "SEMVER" => DataType::Semver,
578            "LATITUDE" => DataType::Latitude,
579            "LONGITUDE" => DataType::Longitude,
580            "GEOPOINT" | "GEO_POINT" => DataType::GeoPoint,
581            "COUNTRY2" => DataType::Country2,
582            "COUNTRY3" => DataType::Country3,
583            "LANG2" => DataType::Lang2,
584            "LANG5" => DataType::Lang5,
585            "CURRENCY" => DataType::Currency,
586            "ASSETCODE" | "ASSET_CODE" | "ASSET" => DataType::AssetCode,
587            "MONEY" => DataType::Money,
588            "ENUM" => DataType::Enum,
589            "ARRAY" => DataType::Array,
590            "KEYREF" => DataType::KeyRef,
591            "DOCREF" => DataType::DocRef,
592            "TABLEREF" => DataType::TableRef,
593            "PAGEREF" => DataType::PageRef,
594            "SECRET" => DataType::Secret,
595            "PASSWORD" => DataType::Password,
596            "VECTOR" => DataType::Vector,
597            _ => return None,
598        })
599    }
600
601    /// Returns the fixed size in bytes if known, None for variable-length types
602    pub fn fixed_size(&self) -> Option<usize> {
603        match self {
604            DataType::Integer => Some(8),
605            DataType::UnsignedInteger => Some(8),
606            DataType::Float => Some(8),
607            DataType::Boolean => Some(1),
608            DataType::Timestamp => Some(8),
609            DataType::Duration => Some(8),
610            DataType::MacAddr => Some(6),
611            DataType::Uuid => Some(16),
612            // Variable-length types
613            DataType::Text => None,
614            DataType::Blob => None,
615            DataType::IpAddr => None, // 4 or 16 bytes
616            DataType::Vector => None, // depends on dimensions
617            DataType::Nullable => None,
618            DataType::Unknown => None,
619            DataType::Json => None,
620            // Cross-references (variable-length IDs)
621            DataType::NodeRef => None,
622            DataType::EdgeRef => None,
623            DataType::VectorRef => Some(8),   // u64 vector ID
624            DataType::RowRef => None,         // table_id (varint) + row_id (u64)
625            DataType::Color => Some(3),       // RGB
626            DataType::Email => None,          // variable-length string
627            DataType::Url => None,            // variable-length string
628            DataType::Phone => Some(8),       // u64
629            DataType::Semver => Some(4),      // u32
630            DataType::Cidr => Some(5),        // u32 + u8
631            DataType::Date => Some(4),        // i32
632            DataType::Time => Some(4),        // u32
633            DataType::Decimal => Some(8),     // i64
634            DataType::Enum => Some(1),        // u8
635            DataType::Array => None,          // variable-length
636            DataType::TimestampMs => Some(8), // i64
637            DataType::Ipv4 => Some(4),        // u32
638            DataType::Ipv6 => Some(16),       // [u8; 16]
639            DataType::Subnet => Some(8),      // u32 + u32
640            DataType::Port => Some(2),        // u16
641            DataType::Latitude => Some(4),    // i32
642            DataType::Longitude => Some(4),   // i32
643            DataType::GeoPoint => Some(8),    // i32 + i32
644            DataType::Country2 => Some(2),    // [u8; 2]
645            DataType::Country3 => Some(3),    // [u8; 3]
646            DataType::Lang2 => Some(2),       // [u8; 2]
647            DataType::Lang5 => Some(5),       // [u8; 5]
648            DataType::Currency => Some(3),    // [u8; 3]
649            DataType::ColorAlpha => Some(4),  // [u8; 4]
650            DataType::BigInt => Some(8),      // i64
651            DataType::AssetCode => None,      // variable-length normalized code
652            DataType::Money => None,          // variable-length asset + scale + i64
653            DataType::KeyRef => None,         // variable-length (collection + key)
654            DataType::DocRef => None,         // variable-length (collection + u64)
655            DataType::TableRef => None,       // variable-length (table name)
656            DataType::PageRef => Some(4),     // u32
657            DataType::Secret => None,         // variable-length ciphertext
658            DataType::Password => None,       // variable-length hash string
659            DataType::TextZstd => None,       // variable-length compressed text
660            DataType::BlobZstd => None,       // variable-length compressed blob
661        }
662    }
663
664    /// Check if this type supports indexing
665    pub fn is_indexable(&self) -> bool {
666        matches!(
667            self,
668            DataType::Integer
669                | DataType::UnsignedInteger
670                | DataType::Float
671                | DataType::Text
672                | DataType::Timestamp
673                | DataType::IpAddr
674                | DataType::Uuid
675                | DataType::NodeRef
676                | DataType::EdgeRef
677                | DataType::VectorRef
678                | DataType::RowRef
679                | DataType::Email
680                | DataType::Url
681                | DataType::Phone
682                | DataType::Semver
683                | DataType::Date
684                | DataType::Time
685                | DataType::Decimal
686                | DataType::Enum
687                | DataType::TimestampMs
688                | DataType::Ipv4
689                | DataType::Ipv6
690                | DataType::Port
691                | DataType::Latitude
692                | DataType::Longitude
693                | DataType::GeoPoint
694                | DataType::Country2
695                | DataType::Country3
696                | DataType::Lang2
697                | DataType::Lang5
698                | DataType::Currency
699                | DataType::AssetCode
700                | DataType::BigInt
701                | DataType::KeyRef
702                | DataType::DocRef
703                | DataType::TableRef
704                | DataType::PageRef
705        )
706    }
707
708    /// Check if this type supports ordering
709    pub fn is_orderable(&self) -> bool {
710        matches!(
711            self,
712            DataType::Integer
713                | DataType::UnsignedInteger
714                | DataType::Float
715                | DataType::Text
716                | DataType::Timestamp
717                | DataType::Duration
718                | DataType::Date
719                | DataType::Time
720                | DataType::Decimal
721                | DataType::Semver
722                | DataType::TimestampMs
723                | DataType::Port
724                | DataType::Latitude
725                | DataType::Longitude
726                | DataType::BigInt
727                | DataType::AssetCode
728        )
729    }
730}
731
732impl fmt::Display for DataType {
733    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
734        match self {
735            DataType::Integer => write!(f, "INTEGER"),
736            DataType::UnsignedInteger => write!(f, "UNSIGNED INTEGER"),
737            DataType::Float => write!(f, "FLOAT"),
738            DataType::Text => write!(f, "TEXT"),
739            DataType::Blob => write!(f, "BLOB"),
740            DataType::Boolean => write!(f, "BOOLEAN"),
741            DataType::Timestamp => write!(f, "TIMESTAMP"),
742            DataType::Duration => write!(f, "DURATION"),
743            DataType::IpAddr => write!(f, "IPADDR"),
744            DataType::MacAddr => write!(f, "MACADDR"),
745            DataType::Vector => write!(f, "VECTOR"),
746            DataType::Nullable => write!(f, "NULLABLE"),
747            DataType::Unknown => write!(f, "UNKNOWN"),
748            DataType::Json => write!(f, "JSON"),
749            DataType::Uuid => write!(f, "UUID"),
750            DataType::NodeRef => write!(f, "NODEREF"),
751            DataType::EdgeRef => write!(f, "EDGEREF"),
752            DataType::VectorRef => write!(f, "VECTORREF"),
753            DataType::RowRef => write!(f, "ROWREF"),
754            DataType::Color => write!(f, "COLOR"),
755            DataType::Email => write!(f, "EMAIL"),
756            DataType::Url => write!(f, "URL"),
757            DataType::Phone => write!(f, "PHONE"),
758            DataType::Semver => write!(f, "SEMVER"),
759            DataType::Cidr => write!(f, "CIDR"),
760            DataType::Date => write!(f, "DATE"),
761            DataType::Time => write!(f, "TIME"),
762            DataType::Decimal => write!(f, "DECIMAL"),
763            DataType::Enum => write!(f, "ENUM"),
764            DataType::Array => write!(f, "ARRAY"),
765            DataType::TimestampMs => write!(f, "TIMESTAMP_MS"),
766            DataType::Ipv4 => write!(f, "IPV4"),
767            DataType::Ipv6 => write!(f, "IPV6"),
768            DataType::Subnet => write!(f, "SUBNET"),
769            DataType::Port => write!(f, "PORT"),
770            DataType::Latitude => write!(f, "LATITUDE"),
771            DataType::Longitude => write!(f, "LONGITUDE"),
772            DataType::GeoPoint => write!(f, "GEOPOINT"),
773            DataType::Country2 => write!(f, "COUNTRY2"),
774            DataType::Country3 => write!(f, "COUNTRY3"),
775            DataType::Lang2 => write!(f, "LANG2"),
776            DataType::Lang5 => write!(f, "LANG5"),
777            DataType::Currency => write!(f, "CURRENCY"),
778            DataType::AssetCode => write!(f, "ASSET_CODE"),
779            DataType::Money => write!(f, "MONEY"),
780            DataType::ColorAlpha => write!(f, "COLOR_ALPHA"),
781            DataType::BigInt => write!(f, "BIGINT"),
782            DataType::KeyRef => write!(f, "KEY_REF"),
783            DataType::DocRef => write!(f, "DOC_REF"),
784            DataType::TableRef => write!(f, "TABLE_REF"),
785            DataType::PageRef => write!(f, "PAGE_REF"),
786            DataType::Secret => write!(f, "SECRET"),
787            DataType::Password => write!(f, "PASSWORD"),
788            DataType::TextZstd => write!(f, "TEXT"), // presents as TEXT externally
789            DataType::BlobZstd => write!(f, "BLOB"), // presents as BLOB externally
790        }
791    }
792}
793
794/// A typed value that can be stored in RedDB
795#[derive(Debug, Clone, PartialEq)]
796pub enum Value {
797    /// Null value
798    Null,
799    /// Signed 64-bit integer
800    Integer(i64),
801    /// Unsigned 64-bit integer
802    UnsignedInteger(u64),
803    /// 64-bit floating point
804    Float(f64),
805    /// UTF-8 text
806    Text(std::sync::Arc<str>),
807    /// Binary data
808    Blob(Vec<u8>),
809    /// Boolean
810    Boolean(bool),
811    /// Unix timestamp (seconds)
812    Timestamp(i64),
813    /// Duration in milliseconds
814    Duration(i64),
815    /// IP address (v4 or v6)
816    IpAddr(IpAddr),
817    /// MAC address
818    MacAddr([u8; 6]),
819    /// Float vector for similarity search
820    Vector(Vec<f32>),
821    /// JSON-like structured data (stored as bytes)
822    Json(Vec<u8>),
823    /// UUID
824    Uuid([u8; 16]),
825    /// Graph node reference (node ID string)
826    NodeRef(String),
827    /// Graph edge reference (edge ID string)
828    EdgeRef(String),
829    /// Vector store reference (collection, vector ID)
830    VectorRef(String, u64),
831    /// Table row reference (table name, row ID)
832    RowRef(String, u64),
833    /// RGB color
834    Color([u8; 3]),
835    /// Email (stored lowercase, validated)
836    Email(String),
837    /// URL (validated)
838    Url(String),
839    /// Phone as digits (e.g., +5511999 -> 5511999u64)
840    Phone(u64),
841    /// Semantic version packed as u32
842    Semver(u32),
843    /// CIDR (ip as u32, prefix as u8)
844    Cidr(u32, u8),
845    /// Date as days since Unix epoch
846    Date(i32),
847    /// Time as milliseconds since midnight
848    Time(u32),
849    /// Fixed-point decimal (value * 10^precision)
850    Decimal(i64),
851    /// Enum variant index
852    EnumValue(u8),
853    /// Homogeneous array
854    Array(Vec<Value>),
855    /// Timestamp in milliseconds since epoch
856    TimestampMs(i64),
857    /// IPv4 as u32
858    Ipv4(u32),
859    /// IPv6 as 16 bytes
860    Ipv6([u8; 16]),
861    /// Subnet: ip(u32) + mask(u32)
862    Subnet(u32, u32),
863    /// Port number
864    Port(u16),
865    /// Latitude in microdegrees (degrees * 1_000_000)
866    Latitude(i32),
867    /// Longitude in microdegrees
868    Longitude(i32),
869    /// GeoPoint (lat, lon) in microdegrees
870    GeoPoint(i32, i32),
871    /// ISO country code 2-letter
872    Country2([u8; 2]),
873    /// ISO country code 3-letter
874    Country3([u8; 3]),
875    /// Language code 2-letter
876    Lang2([u8; 2]),
877    /// Language tag 5-char (e.g., "pt-BR")
878    Lang5([u8; 5]),
879    /// Currency code 3-letter
880    Currency([u8; 3]),
881    /// General asset code, normalized uppercase text
882    AssetCode(String),
883    /// Monetary amount stored as integer minor units plus explicit scale and asset code
884    Money {
885        asset_code: String,
886        minor_units: i64,
887        scale: u8,
888    },
889    /// RGBA color with alpha
890    ColorAlpha([u8; 4]),
891    /// Big integer (same as Integer but with distinct type for schema clarity)
892    BigInt(i64),
893    /// Reference to a KV pair (collection, key)
894    KeyRef(String, String),
895    /// Reference to a document (collection, entity_id)
896    DocRef(String, u64),
897    /// Reference to a table/collection by name
898    TableRef(String),
899    /// Reference to a physical storage page (page_id)
900    PageRef(u32),
901    /// Encrypted secret (AES-256-GCM ciphertext bytes: nonce + ciphertext + tag)
902    Secret(Vec<u8>),
903    /// Argon2id password hash string
904    Password(String),
905}
906
907/// Manual `Eq` impl: consistent with the manual `Hash` impl below.
908/// Float NaN is treated as equal to itself (bit-level equality) so that
909/// `Value` can be used in `HashSet<Value>` for IN-list optimizations.
910/// This diverges from IEEE 754 but is safe for SQL query evaluation.
911impl Eq for Value {}
912
913/// Manual `Hash` impl for `Value`.
914/// - Floats: use `f64.to_bits()` so the invariant `a == b → hash(a) == hash(b)` holds.
915/// - Arrays / nested values: hash each element recursively.
916/// - All other variants: delegate to the byte-level representation.
917impl std::hash::Hash for Value {
918    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
919        // Discriminant first — ensures different variants never collide
920        std::mem::discriminant(self).hash(state);
921        match self {
922            Value::Null => {}
923            Value::Integer(v) => v.hash(state),
924            Value::UnsignedInteger(v) => v.hash(state),
925            Value::Float(v) => v.to_bits().hash(state),
926            Value::Text(v) => v.hash(state),
927            Value::Blob(v) => v.hash(state),
928            Value::Boolean(v) => v.hash(state),
929            Value::Timestamp(v) => v.hash(state),
930            Value::Duration(v) => v.hash(state),
931            Value::IpAddr(v) => v.hash(state),
932            Value::MacAddr(v) => v.hash(state),
933            Value::Vector(v) => {
934                v.len().hash(state);
935                for f in v {
936                    f.to_bits().hash(state);
937                }
938            }
939            Value::Json(v) => v.hash(state),
940            Value::Uuid(v) => v.hash(state),
941            Value::NodeRef(v) => v.hash(state),
942            Value::EdgeRef(v) => v.hash(state),
943            Value::VectorRef(c, id) => {
944                c.hash(state);
945                id.hash(state);
946            }
947            Value::RowRef(c, id) => {
948                c.hash(state);
949                id.hash(state);
950            }
951            Value::Color(v) => v.hash(state),
952            Value::Email(v) => v.hash(state),
953            Value::Url(v) => v.hash(state),
954            Value::Phone(v) => v.hash(state),
955            Value::Semver(v) => v.hash(state),
956            Value::Cidr(ip, prefix) => {
957                ip.hash(state);
958                prefix.hash(state);
959            }
960            Value::Date(v) => v.hash(state),
961            Value::Time(v) => v.hash(state),
962            Value::Decimal(v) => v.hash(state),
963            Value::EnumValue(v) => v.hash(state),
964            Value::Array(v) => {
965                v.len().hash(state);
966                for elem in v {
967                    elem.hash(state);
968                }
969            }
970            Value::TimestampMs(v) => v.hash(state),
971            Value::Ipv4(v) => v.hash(state),
972            Value::Ipv6(v) => v.hash(state),
973            Value::Subnet(ip, mask) => {
974                ip.hash(state);
975                mask.hash(state);
976            }
977            Value::Port(v) => v.hash(state),
978            Value::Latitude(v) => v.hash(state),
979            Value::Longitude(v) => v.hash(state),
980            Value::GeoPoint(lat, lon) => {
981                lat.hash(state);
982                lon.hash(state);
983            }
984            Value::Country2(v) => v.hash(state),
985            Value::Country3(v) => v.hash(state),
986            Value::Lang2(v) => v.hash(state),
987            Value::Lang5(v) => v.hash(state),
988            Value::Currency(v) => v.hash(state),
989            Value::AssetCode(v) => v.hash(state),
990            Value::Money {
991                asset_code,
992                minor_units,
993                scale,
994            } => {
995                asset_code.hash(state);
996                minor_units.hash(state);
997                scale.hash(state);
998            }
999            Value::ColorAlpha(v) => v.hash(state),
1000            Value::BigInt(v) => v.hash(state),
1001            Value::KeyRef(c, k) => {
1002                c.hash(state);
1003                k.hash(state);
1004            }
1005            Value::DocRef(c, id) => {
1006                c.hash(state);
1007                id.hash(state);
1008            }
1009            Value::TableRef(v) => v.hash(state),
1010            Value::PageRef(v) => v.hash(state),
1011            Value::Secret(v) => v.hash(state),
1012            Value::Password(v) => v.hash(state),
1013        }
1014    }
1015}
1016
1017impl Value {
1018    /// Construct a `Value::Text` from anything that can produce an
1019    /// `Arc<str>` — `String`, `&str`, or an existing `Arc<str>`.
1020    /// The drop-in migration helper for the old
1021    /// `Value::Text(String)` constructor style.
1022    #[inline]
1023    pub fn text(s: impl Into<std::sync::Arc<str>>) -> Self {
1024        Value::Text(s.into())
1025    }
1026
1027    /// Get the data type of this value
1028    pub fn data_type(&self) -> DataType {
1029        match self {
1030            Value::Null => DataType::Nullable,
1031            Value::Integer(_) => DataType::Integer,
1032            Value::UnsignedInteger(_) => DataType::UnsignedInteger,
1033            Value::Float(_) => DataType::Float,
1034            Value::Text(_) => DataType::Text,
1035            Value::Blob(_) => DataType::Blob,
1036            Value::Boolean(_) => DataType::Boolean,
1037            Value::Timestamp(_) => DataType::Timestamp,
1038            Value::Duration(_) => DataType::Duration,
1039            Value::IpAddr(_) => DataType::IpAddr,
1040            Value::MacAddr(_) => DataType::MacAddr,
1041            Value::Vector(_) => DataType::Vector,
1042            Value::Json(_) => DataType::Json,
1043            Value::Uuid(_) => DataType::Uuid,
1044            Value::NodeRef(_) => DataType::NodeRef,
1045            Value::EdgeRef(_) => DataType::EdgeRef,
1046            Value::VectorRef(_, _) => DataType::VectorRef,
1047            Value::RowRef(_, _) => DataType::RowRef,
1048            Value::Color(_) => DataType::Color,
1049            Value::Email(_) => DataType::Email,
1050            Value::Url(_) => DataType::Url,
1051            Value::Phone(_) => DataType::Phone,
1052            Value::Semver(_) => DataType::Semver,
1053            Value::Cidr(_, _) => DataType::Cidr,
1054            Value::Date(_) => DataType::Date,
1055            Value::Time(_) => DataType::Time,
1056            Value::Decimal(_) => DataType::Decimal,
1057            Value::EnumValue(_) => DataType::Enum,
1058            Value::Array(_) => DataType::Array,
1059            Value::TimestampMs(_) => DataType::TimestampMs,
1060            Value::Ipv4(_) => DataType::Ipv4,
1061            Value::Ipv6(_) => DataType::Ipv6,
1062            Value::Subnet(_, _) => DataType::Subnet,
1063            Value::Port(_) => DataType::Port,
1064            Value::Latitude(_) => DataType::Latitude,
1065            Value::Longitude(_) => DataType::Longitude,
1066            Value::GeoPoint(_, _) => DataType::GeoPoint,
1067            Value::Country2(_) => DataType::Country2,
1068            Value::Country3(_) => DataType::Country3,
1069            Value::Lang2(_) => DataType::Lang2,
1070            Value::Lang5(_) => DataType::Lang5,
1071            Value::Currency(_) => DataType::Currency,
1072            Value::AssetCode(_) => DataType::AssetCode,
1073            Value::Money { .. } => DataType::Money,
1074            Value::ColorAlpha(_) => DataType::ColorAlpha,
1075            Value::BigInt(_) => DataType::BigInt,
1076            Value::KeyRef(..) => DataType::KeyRef,
1077            Value::DocRef(..) => DataType::DocRef,
1078            Value::TableRef(..) => DataType::TableRef,
1079            Value::PageRef(..) => DataType::PageRef,
1080            Value::Secret(..) => DataType::Secret,
1081            Value::Password(..) => DataType::Password,
1082        }
1083    }
1084
1085    /// Check if value is null
1086    pub fn is_null(&self) -> bool {
1087        matches!(self, Value::Null)
1088    }
1089
1090    /// Serialize value to bytes.
1091    ///
1092    /// Delegates to [`super::value_codec::encode`] — the registry
1093    /// module owns every variant's byte layout. See `value_codec`
1094    /// for the full format spec.
1095    pub fn to_bytes(&self) -> Vec<u8> {
1096        let mut buf = Vec::new();
1097        super::value_codec::encode(self, &mut buf);
1098        buf
1099    }
1100
1101    /// Deserialize value from bytes.
1102    ///
1103    /// Delegates to [`super::value_codec::decode`].
1104    pub fn from_bytes(data: &[u8]) -> Result<(Self, usize), ValueError> {
1105        super::value_codec::decode(data)
1106    }
1107
1108    /// Try to convert to i64
1109    pub fn as_integer(&self) -> Option<i64> {
1110        match self {
1111            Value::Integer(v) => Some(*v),
1112            Value::UnsignedInteger(v) => {
1113                if *v <= i64::MAX as u64 {
1114                    Some(*v as i64)
1115                } else {
1116                    None
1117                }
1118            }
1119            Value::Timestamp(v) => Some(*v),
1120            Value::Duration(v) => Some(*v),
1121            _ => None,
1122        }
1123    }
1124
1125    /// Try to convert to f64
1126    pub fn as_float(&self) -> Option<f64> {
1127        match self {
1128            Value::Float(v) => Some(*v),
1129            Value::Integer(v) => Some(*v as f64),
1130            Value::UnsignedInteger(v) => Some(*v as f64),
1131            _ => None,
1132        }
1133    }
1134
1135    /// Try to convert to string reference
1136    pub fn as_text(&self) -> Option<&str> {
1137        match self {
1138            Value::Text(s) => Some(s),
1139            _ => None,
1140        }
1141    }
1142
1143    /// Try to convert to bool
1144    pub fn as_boolean(&self) -> Option<bool> {
1145        match self {
1146            Value::Boolean(v) => Some(*v),
1147            _ => None,
1148        }
1149    }
1150
1151    /// Try to convert to IpAddr
1152    pub fn as_ip_addr(&self) -> Option<IpAddr> {
1153        match self {
1154            Value::IpAddr(addr) => Some(*addr),
1155            _ => None,
1156        }
1157    }
1158
1159    /// Try to convert to vector reference
1160    pub fn as_vector(&self) -> Option<&[f32]> {
1161        match self {
1162            Value::Vector(v) => Some(v),
1163            _ => None,
1164        }
1165    }
1166
1167    /// Human-readable display string for rich types
1168    pub fn display_string(&self) -> String {
1169        match self {
1170            Value::Color([r, g, b]) => format!("#{:02X}{:02X}{:02X}", r, g, b),
1171            Value::Email(s) => s.clone(),
1172            Value::Url(s) => s.clone(),
1173            Value::Phone(n) => format!("+{}", n),
1174            Value::Semver(packed) => format!(
1175                "{}.{}.{}",
1176                packed / 1_000_000,
1177                (packed / 1_000) % 1_000,
1178                packed % 1_000
1179            ),
1180            Value::Cidr(ip, prefix) => format!(
1181                "{}.{}.{}.{}/{}",
1182                (ip >> 24) & 0xFF,
1183                (ip >> 16) & 0xFF,
1184                (ip >> 8) & 0xFF,
1185                ip & 0xFF,
1186                prefix
1187            ),
1188            Value::Date(days) => format_civil_date(*days),
1189            Value::Time(ms) => {
1190                let total_secs = ms / 1000;
1191                format!(
1192                    "{:02}:{:02}:{:02}",
1193                    total_secs / 3600,
1194                    (total_secs / 60) % 60,
1195                    total_secs % 60
1196                )
1197            }
1198            Value::Decimal(v) => format_scaled_i64(*v, 4),
1199            Value::EnumValue(i) => format!("enum({})", i),
1200            Value::Array(elems) => {
1201                let items: Vec<String> = elems.iter().map(|e| e.display_string()).collect();
1202                format!("[{}]", items.join(", "))
1203            }
1204            Value::TimestampMs(ms) => {
1205                let secs = ms / 1000;
1206                let millis = (ms % 1000).unsigned_abs() as u32;
1207                let days = (secs / 86400) as i32;
1208                let day_secs = (secs % 86400) as u32;
1209                let h = day_secs / 3600;
1210                let m = (day_secs / 60) % 60;
1211                let s = day_secs % 60;
1212                format!(
1213                    "{}T{:02}:{:02}:{:02}.{:03}Z",
1214                    format_civil_date(days),
1215                    h,
1216                    m,
1217                    s,
1218                    millis
1219                )
1220            }
1221            Value::Ipv4(ip) => format!(
1222                "{}.{}.{}.{}",
1223                (ip >> 24) & 0xFF,
1224                (ip >> 16) & 0xFF,
1225                (ip >> 8) & 0xFF,
1226                ip & 0xFF
1227            ),
1228            Value::Ipv6(bytes) => {
1229                let addr = std::net::Ipv6Addr::from(*bytes);
1230                format!("{}", addr)
1231            }
1232            Value::Subnet(ip, mask) => {
1233                let ip_str = format!(
1234                    "{}.{}.{}.{}",
1235                    (ip >> 24) & 0xFF,
1236                    (ip >> 16) & 0xFF,
1237                    (ip >> 8) & 0xFF,
1238                    ip & 0xFF
1239                );
1240                // Convert mask to prefix length if possible
1241                let prefix = mask.leading_ones();
1242                if prefix < 32 && (*mask << prefix) == 0 || prefix == 32 {
1243                    format!("{}/{}", ip_str, prefix)
1244                } else {
1245                    let mask_str = format!(
1246                        "{}.{}.{}.{}",
1247                        (mask >> 24) & 0xFF,
1248                        (mask >> 16) & 0xFF,
1249                        (mask >> 8) & 0xFF,
1250                        mask & 0xFF
1251                    );
1252                    format!("{}/{}", ip_str, mask_str)
1253                }
1254            }
1255            Value::Port(p) => p.to_string(),
1256            Value::Latitude(micro) => format!("{:.6}", *micro as f64 / 1_000_000.0),
1257            Value::Longitude(micro) => format!("{:.6}", *micro as f64 / 1_000_000.0),
1258            Value::GeoPoint(lat, lon) => format!(
1259                "{:.6},{:.6}",
1260                *lat as f64 / 1_000_000.0,
1261                *lon as f64 / 1_000_000.0
1262            ),
1263            Value::Country2(c) => String::from_utf8_lossy(c).to_string(),
1264            Value::Country3(c) => String::from_utf8_lossy(c).to_string(),
1265            Value::Lang2(c) => String::from_utf8_lossy(c).to_string(),
1266            Value::Lang5(c) => String::from_utf8_lossy(c).to_string(),
1267            Value::Currency(c) => String::from_utf8_lossy(c).to_string(),
1268            Value::AssetCode(code) => code.clone(),
1269            Value::Money {
1270                asset_code,
1271                minor_units,
1272                scale,
1273            } => format!("{} {}", asset_code, format_scaled_i64(*minor_units, *scale)),
1274            Value::ColorAlpha([r, g, b, a]) => format!("#{:02X}{:02X}{:02X}{:02X}", r, g, b, a),
1275            Value::BigInt(v) => v.to_string(),
1276            Value::KeyRef(c, k) => format!("{}:{}", c, k),
1277            Value::DocRef(c, id) => format!("{}#{}", c, id),
1278            Value::TableRef(t) => t.clone(),
1279            Value::PageRef(p) => format!("page:{}", p),
1280            other => format!("{}", other),
1281        }
1282    }
1283
1284    /// Plain text coercion for string-producing SQL operators.
1285    ///
1286    /// Unlike `Display`, this does not render `Text` as a SQL literal.
1287    pub fn plain_text(&self) -> String {
1288        match self {
1289            Value::Text(text) => text.to_string(),
1290            Value::Array(elems) => {
1291                let items: Vec<String> = elems.iter().map(Value::plain_text).collect();
1292                format!("[{}]", items.join(", "))
1293            }
1294            other => other.display_string(),
1295        }
1296    }
1297}
1298
1299fn format_scaled_i64(value: i64, scale: u8) -> String {
1300    let negative = value < 0;
1301    let abs = (value as i128).abs();
1302    if scale == 0 {
1303        return if negative {
1304            format!("-{}", abs)
1305        } else {
1306            abs.to_string()
1307        };
1308    }
1309
1310    let divisor = 10_i128.pow(scale as u32);
1311    let whole = abs / divisor;
1312    let frac = abs % divisor;
1313    let sign = if negative { "-" } else { "" };
1314    format!("{}{}.{:0width$}", sign, whole, frac, width = scale as usize)
1315}
1316
1317impl fmt::Display for Value {
1318    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1319        match self {
1320            Value::Null => write!(f, "NULL"),
1321            Value::Integer(v) => write!(f, "{}", v),
1322            Value::UnsignedInteger(v) => write!(f, "{}", v),
1323            Value::Float(v) => write!(f, "{}", v),
1324            Value::Text(s) => write!(f, "'{}'", s),
1325            Value::Blob(b) => write!(f, "<blob {} bytes>", b.len()),
1326            Value::Boolean(v) => write!(f, "{}", v),
1327            Value::Timestamp(v) => write!(f, "ts:{}", v),
1328            Value::Duration(v) => write!(f, "{}ms", v),
1329            Value::IpAddr(addr) => write!(f, "{}", addr),
1330            Value::MacAddr(mac) => write!(
1331                f,
1332                "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
1333                mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
1334            ),
1335            Value::Vector(v) => write!(f, "<vector dim={}>", v.len()),
1336            Value::Json(j) => write!(f, "<json {} bytes>", j.len()),
1337            Value::Uuid(u) => {
1338                write!(
1339                    f,
1340                    "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
1341                    u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7],
1342                    u[8], u[9], u[10], u[11], u[12], u[13], u[14], u[15]
1343                )
1344            }
1345            Value::NodeRef(id) => write!(f, "node:{}", id),
1346            Value::EdgeRef(id) => write!(f, "edge:{}", id),
1347            Value::VectorRef(coll, id) => write!(f, "vector:{}:{}", coll, id),
1348            Value::RowRef(table, id) => write!(f, "row:{}:{}", table, id),
1349            Value::Color([r, g, b]) => write!(f, "#{:02X}{:02X}{:02X}", r, g, b),
1350            Value::Email(s) => write!(f, "{}", s),
1351            Value::Url(s) => write!(f, "{}", s),
1352            Value::Phone(n) => write!(f, "+{}", n),
1353            Value::Semver(packed) => write!(
1354                f,
1355                "{}.{}.{}",
1356                packed / 1_000_000,
1357                (packed / 1_000) % 1_000,
1358                packed % 1_000
1359            ),
1360            Value::Cidr(ip, prefix) => write!(
1361                f,
1362                "{}.{}.{}.{}/{}",
1363                (ip >> 24) & 0xFF,
1364                (ip >> 16) & 0xFF,
1365                (ip >> 8) & 0xFF,
1366                ip & 0xFF,
1367                prefix
1368            ),
1369            Value::Date(days) => write!(f, "{}", format_civil_date(*days)),
1370            Value::Time(ms) => {
1371                let total_secs = ms / 1000;
1372                write!(
1373                    f,
1374                    "{:02}:{:02}:{:02}",
1375                    total_secs / 3600,
1376                    (total_secs / 60) % 60,
1377                    total_secs % 60
1378                )
1379            }
1380            Value::Decimal(v) => write!(f, "{}", format_scaled_i64(*v, 4)),
1381            Value::EnumValue(i) => write!(f, "enum({})", i),
1382            Value::Array(elems) => {
1383                write!(f, "[")?;
1384                for (i, elem) in elems.iter().enumerate() {
1385                    if i > 0 {
1386                        write!(f, ", ")?;
1387                    }
1388                    write!(f, "{}", elem)?;
1389                }
1390                write!(f, "]")
1391            }
1392            Value::TimestampMs(ms) => {
1393                let secs = ms / 1000;
1394                let millis = (ms % 1000).unsigned_abs() as u32;
1395                let days = (secs / 86400) as i32;
1396                let day_secs = (secs % 86400) as u32;
1397                let h = day_secs / 3600;
1398                let m = (day_secs / 60) % 60;
1399                let s = day_secs % 60;
1400                write!(
1401                    f,
1402                    "{}T{:02}:{:02}:{:02}.{:03}Z",
1403                    format_civil_date(days),
1404                    h,
1405                    m,
1406                    s,
1407                    millis
1408                )
1409            }
1410            Value::Ipv4(ip) => write!(
1411                f,
1412                "{}.{}.{}.{}",
1413                (ip >> 24) & 0xFF,
1414                (ip >> 16) & 0xFF,
1415                (ip >> 8) & 0xFF,
1416                ip & 0xFF
1417            ),
1418            Value::Ipv6(bytes) => {
1419                let addr = std::net::Ipv6Addr::from(*bytes);
1420                write!(f, "{}", addr)
1421            }
1422            Value::Subnet(ip, mask) => {
1423                let prefix = mask.leading_ones();
1424                if prefix < 32 && (*mask << prefix) == 0 || prefix == 32 {
1425                    write!(
1426                        f,
1427                        "{}.{}.{}.{}/{}",
1428                        (ip >> 24) & 0xFF,
1429                        (ip >> 16) & 0xFF,
1430                        (ip >> 8) & 0xFF,
1431                        ip & 0xFF,
1432                        prefix
1433                    )
1434                } else {
1435                    write!(
1436                        f,
1437                        "{}.{}.{}.{}/{}.{}.{}.{}",
1438                        (ip >> 24) & 0xFF,
1439                        (ip >> 16) & 0xFF,
1440                        (ip >> 8) & 0xFF,
1441                        ip & 0xFF,
1442                        (mask >> 24) & 0xFF,
1443                        (mask >> 16) & 0xFF,
1444                        (mask >> 8) & 0xFF,
1445                        mask & 0xFF
1446                    )
1447                }
1448            }
1449            Value::Port(p) => write!(f, "{}", p),
1450            Value::Latitude(micro) => write!(f, "{:.6}", *micro as f64 / 1_000_000.0),
1451            Value::Longitude(micro) => write!(f, "{:.6}", *micro as f64 / 1_000_000.0),
1452            Value::GeoPoint(lat, lon) => write!(
1453                f,
1454                "{:.6},{:.6}",
1455                *lat as f64 / 1_000_000.0,
1456                *lon as f64 / 1_000_000.0
1457            ),
1458            Value::Country2(c) => write!(f, "{}", String::from_utf8_lossy(c)),
1459            Value::Country3(c) => write!(f, "{}", String::from_utf8_lossy(c)),
1460            Value::Lang2(c) => write!(f, "{}", String::from_utf8_lossy(c)),
1461            Value::Lang5(c) => write!(f, "{}", String::from_utf8_lossy(c)),
1462            Value::Currency(c) => write!(f, "{}", String::from_utf8_lossy(c)),
1463            Value::AssetCode(code) => write!(f, "{}", code),
1464            Value::Money {
1465                asset_code,
1466                minor_units,
1467                scale,
1468            } => write!(
1469                f,
1470                "{} {}",
1471                asset_code,
1472                format_scaled_i64(*minor_units, *scale)
1473            ),
1474            Value::ColorAlpha([r, g, b, a]) => write!(f, "#{:02X}{:02X}{:02X}{:02X}", r, g, b, a),
1475            Value::BigInt(v) => write!(f, "{}", v),
1476            Value::KeyRef(c, k) => write!(f, "key_ref:{}:{}", c, k),
1477            Value::DocRef(c, id) => write!(f, "doc_ref:{}#{}", c, id),
1478            Value::TableRef(t) => write!(f, "table_ref:{}", t),
1479            Value::PageRef(p) => write!(f, "page_ref:{}", p),
1480            Value::Secret(b) => write!(f, "<secret {} bytes>", b.len()),
1481            Value::Password(_) => write!(f, "***"),
1482        }
1483    }
1484}
1485
1486/// Errors that can occur during value operations
1487#[derive(Debug, Clone, PartialEq)]
1488pub enum ValueError {
1489    /// No data to parse
1490    EmptyData,
1491    /// Invalid type byte
1492    InvalidType(u8),
1493    /// Data was truncated
1494    TruncatedData,
1495    /// Invalid UTF-8 in text
1496    InvalidUtf8,
1497    /// Invalid IP version byte
1498    InvalidIpVersion(u8),
1499    /// Varint overflow
1500    VarintOverflow,
1501    /// Type mismatch
1502    TypeMismatch { expected: DataType, found: DataType },
1503}
1504
1505impl fmt::Display for ValueError {
1506    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1507        match self {
1508            ValueError::EmptyData => write!(f, "empty data"),
1509            ValueError::InvalidType(t) => write!(f, "invalid type byte: {}", t),
1510            ValueError::TruncatedData => write!(f, "truncated data"),
1511            ValueError::InvalidUtf8 => write!(f, "invalid UTF-8"),
1512            ValueError::InvalidIpVersion(v) => write!(f, "invalid IP version: {}", v),
1513            ValueError::VarintOverflow => write!(f, "varint overflow"),
1514            ValueError::TypeMismatch { expected, found } => {
1515                write!(f, "type mismatch: expected {}, found {}", expected, found)
1516            }
1517        }
1518    }
1519}
1520
1521impl std::error::Error for ValueError {}
1522
1523/// Write a variable-length integer (LEB128 encoding)
1524pub(super) fn write_varint(buf: &mut Vec<u8>, mut value: u64) {
1525    loop {
1526        let mut byte = (value & 0x7F) as u8;
1527        value >>= 7;
1528        if value != 0 {
1529            byte |= 0x80;
1530        }
1531        buf.push(byte);
1532        if value == 0 {
1533            break;
1534        }
1535    }
1536}
1537
1538/// Read a variable-length integer (LEB128 encoding)
1539pub(super) fn read_varint(data: &[u8]) -> Result<(u64, usize), ValueError> {
1540    let mut result: u64 = 0;
1541    let mut shift = 0;
1542    let mut offset = 0;
1543
1544    loop {
1545        if offset >= data.len() {
1546            return Err(ValueError::TruncatedData);
1547        }
1548        let byte = data[offset];
1549        offset += 1;
1550
1551        if shift >= 64 {
1552            return Err(ValueError::VarintOverflow);
1553        }
1554
1555        result |= ((byte & 0x7F) as u64) << shift;
1556        shift += 7;
1557
1558        if byte & 0x80 == 0 {
1559            break;
1560        }
1561    }
1562
1563    Ok((result, offset))
1564}
1565
1566/// Convert days since Unix epoch back to YYYY-MM-DD (Howard Hinnant's civil_from_days)
1567fn format_civil_date(days: i32) -> String {
1568    let z = days as i64 + 719468;
1569    let era = (if z >= 0 { z } else { z - 146096 }) / 146097;
1570    let doe = (z - era * 146097) as u32;
1571    let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
1572    let y = yoe as i64 + era * 400;
1573    let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
1574    let mp = (5 * doy + 2) / 153;
1575    let d = doy - (153 * mp + 2) / 5 + 1;
1576    let m = if mp < 10 { mp + 3 } else { mp - 9 };
1577    let y = if m <= 2 { y + 1 } else { y };
1578    format!("{:04}-{:02}-{:02}", y, m, d)
1579}
1580
1581/// A row of values (tuple)
1582#[derive(Debug, Clone, PartialEq)]
1583pub struct Row {
1584    values: Vec<Value>,
1585}
1586
1587impl Row {
1588    /// Create a new row from values
1589    pub fn new(values: Vec<Value>) -> Self {
1590        Self { values }
1591    }
1592
1593    /// Get value at index
1594    pub fn get(&self, index: usize) -> Option<&Value> {
1595        self.values.get(index)
1596    }
1597
1598    /// Get number of columns
1599    pub fn len(&self) -> usize {
1600        self.values.len()
1601    }
1602
1603    /// Check if row is empty
1604    pub fn is_empty(&self) -> bool {
1605        self.values.is_empty()
1606    }
1607
1608    /// Iterate over values
1609    pub fn iter(&self) -> impl Iterator<Item = &Value> {
1610        self.values.iter()
1611    }
1612
1613    /// Get slice of all values
1614    pub fn values(&self) -> &[Value] {
1615        &self.values
1616    }
1617
1618    /// Convert to owned values
1619    pub fn into_values(self) -> Vec<Value> {
1620        self.values
1621    }
1622
1623    /// Serialize row to bytes
1624    pub fn to_bytes(&self) -> Vec<u8> {
1625        let mut buf = Vec::new();
1626
1627        // Number of columns
1628        write_varint(&mut buf, self.values.len() as u64);
1629
1630        // Each value
1631        for value in &self.values {
1632            let value_bytes = value.to_bytes();
1633            buf.extend_from_slice(&value_bytes);
1634        }
1635
1636        buf
1637    }
1638
1639    /// Deserialize row from bytes
1640    pub fn from_bytes(data: &[u8]) -> Result<(Self, usize), ValueError> {
1641        if data.is_empty() {
1642            return Err(ValueError::EmptyData);
1643        }
1644
1645        let (column_count, mut offset) = read_varint(data)?;
1646        let mut values = Vec::with_capacity(column_count as usize);
1647
1648        for _ in 0..column_count {
1649            let (value, size) = Value::from_bytes(&data[offset..])?;
1650            offset += size;
1651            values.push(value);
1652        }
1653
1654        Ok((Row { values }, offset))
1655    }
1656}
1657
1658impl From<Vec<Value>> for Row {
1659    fn from(values: Vec<Value>) -> Self {
1660        Row::new(values)
1661    }
1662}
1663
1664impl IntoIterator for Row {
1665    type Item = Value;
1666    type IntoIter = std::vec::IntoIter<Value>;
1667
1668    fn into_iter(self) -> Self::IntoIter {
1669        self.values.into_iter()
1670    }
1671}
1672
1673#[cfg(test)]
1674mod tests {
1675    use super::*;
1676    use proptest::prelude::*;
1677    use std::collections::hash_map::DefaultHasher;
1678    use std::hash::{Hash, Hasher};
1679    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
1680
1681    #[test]
1682    fn test_datatype_roundtrip() {
1683        let types = [
1684            DataType::Integer,
1685            DataType::UnsignedInteger,
1686            DataType::Float,
1687            DataType::Text,
1688            DataType::Blob,
1689            DataType::Boolean,
1690            DataType::Timestamp,
1691            DataType::Duration,
1692            DataType::IpAddr,
1693            DataType::MacAddr,
1694            DataType::Vector,
1695            DataType::Json,
1696            DataType::Uuid,
1697            DataType::Color,
1698            DataType::Email,
1699            DataType::Url,
1700            DataType::Phone,
1701            DataType::Semver,
1702            DataType::Cidr,
1703            DataType::Date,
1704            DataType::Time,
1705            DataType::Decimal,
1706            DataType::Enum,
1707            DataType::Array,
1708            DataType::TimestampMs,
1709            DataType::Ipv4,
1710            DataType::Ipv6,
1711            DataType::Subnet,
1712            DataType::Port,
1713            DataType::Latitude,
1714            DataType::Longitude,
1715            DataType::GeoPoint,
1716            DataType::Country2,
1717            DataType::Country3,
1718            DataType::Lang2,
1719            DataType::Lang5,
1720            DataType::Currency,
1721            DataType::ColorAlpha,
1722            DataType::BigInt,
1723        ];
1724
1725        for dt in types {
1726            let byte = dt.to_byte();
1727            let recovered = DataType::from_byte(byte).unwrap();
1728            assert_eq!(dt, recovered);
1729        }
1730    }
1731
1732    #[test]
1733    fn test_from_sql_name_uses_shared_alias_mapping() {
1734        assert_eq!(DataType::from_sql_name("INT8"), Some(DataType::BigInt));
1735        assert_eq!(DataType::from_sql_name("BIGINT"), Some(DataType::BigInt));
1736        assert_eq!(
1737            DataType::from_sql_name("TIMESTAMPTZ"),
1738            Some(DataType::TimestampMs)
1739        );
1740        assert_eq!(DataType::from_sql_name("ROWREF"), Some(DataType::RowRef));
1741    }
1742
1743    #[test]
1744    fn test_value_integer() {
1745        let value = Value::Integer(-12345);
1746        let bytes = value.to_bytes();
1747        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1748        assert_eq!(value, recovered);
1749        assert_eq!(size, bytes.len());
1750    }
1751
1752    #[test]
1753    fn test_value_text() {
1754        let value = Value::text("Hello, RedDB!".to_string());
1755        let bytes = value.to_bytes();
1756        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1757        assert_eq!(value, recovered);
1758        assert_eq!(size, bytes.len());
1759    }
1760
1761    #[test]
1762    fn plain_text_does_not_sql_quote_text() {
1763        assert_eq!(Value::text("alice").plain_text(), "alice");
1764        assert_eq!(
1765            Value::Array(vec![Value::text("alice"), Value::Integer(7)]).plain_text(),
1766            "[alice, 7]"
1767        );
1768    }
1769
1770    fn short_text() -> impl Strategy<Value = String> {
1771        "[a-zA-Z0-9_ ./:-]{0,16}".prop_map(|text| text)
1772    }
1773
1774    fn arb_value() -> impl Strategy<Value = Value> {
1775        let leaf = prop_oneof![
1776            Just(Value::Null),
1777            any::<i64>().prop_map(Value::Integer),
1778            any::<u64>().prop_map(Value::UnsignedInteger),
1779            any::<f64>().prop_map(Value::Float),
1780            short_text().prop_map(Value::text),
1781            proptest::collection::vec(any::<u8>(), 0..8).prop_map(Value::Blob),
1782            any::<bool>().prop_map(Value::Boolean),
1783            any::<i64>().prop_map(Value::Timestamp),
1784            any::<i64>().prop_map(Value::Duration),
1785            any::<u32>().prop_map(|ip| Value::IpAddr(IpAddr::V4(Ipv4Addr::from(ip)))),
1786            any::<[u8; 16]>().prop_map(|ip| Value::IpAddr(IpAddr::V6(Ipv6Addr::from(ip)))),
1787            any::<[u8; 6]>().prop_map(Value::MacAddr),
1788            proptest::collection::vec(any::<f32>(), 0..4).prop_map(Value::Vector),
1789            proptest::collection::vec(any::<u8>(), 0..8).prop_map(Value::Json),
1790            any::<[u8; 16]>().prop_map(Value::Uuid),
1791            short_text().prop_map(Value::NodeRef),
1792            short_text().prop_map(Value::EdgeRef),
1793            (short_text(), any::<u64>())
1794                .prop_map(|(collection, id)| Value::VectorRef(collection, id)),
1795            (short_text(), any::<u64>()).prop_map(|(table, id)| Value::RowRef(table, id)),
1796            any::<[u8; 3]>().prop_map(Value::Color),
1797            short_text().prop_map(Value::Email),
1798            short_text().prop_map(Value::Url),
1799            any::<u64>().prop_map(Value::Phone),
1800            any::<u32>().prop_map(Value::Semver),
1801            (any::<u32>(), 0u8..=32).prop_map(|(ip, prefix)| Value::Cidr(ip, prefix)),
1802            any::<i32>().prop_map(Value::Date),
1803            any::<u32>().prop_map(Value::Time),
1804            any::<i64>().prop_map(Value::Decimal),
1805            any::<u8>().prop_map(Value::EnumValue),
1806            any::<i64>().prop_map(Value::TimestampMs),
1807            any::<u32>().prop_map(Value::Ipv4),
1808            any::<[u8; 16]>().prop_map(Value::Ipv6),
1809            (any::<u32>(), any::<u32>()).prop_map(|(ip, mask)| Value::Subnet(ip, mask)),
1810            any::<u16>().prop_map(Value::Port),
1811            any::<i32>().prop_map(Value::Latitude),
1812            any::<i32>().prop_map(Value::Longitude),
1813            (any::<i32>(), any::<i32>()).prop_map(|(lat, lon)| Value::GeoPoint(lat, lon)),
1814            any::<[u8; 2]>().prop_map(Value::Country2),
1815            any::<[u8; 3]>().prop_map(Value::Country3),
1816            any::<[u8; 2]>().prop_map(Value::Lang2),
1817            any::<[u8; 5]>().prop_map(Value::Lang5),
1818            any::<[u8; 3]>().prop_map(Value::Currency),
1819            short_text().prop_map(Value::AssetCode),
1820            (short_text(), any::<i64>(), 0u8..=9).prop_map(|(asset_code, minor_units, scale)| {
1821                Value::Money {
1822                    asset_code,
1823                    minor_units,
1824                    scale,
1825                }
1826            }),
1827            any::<[u8; 4]>().prop_map(Value::ColorAlpha),
1828            any::<i64>().prop_map(Value::BigInt),
1829            (short_text(), short_text())
1830                .prop_map(|(collection, key)| Value::KeyRef(collection, key)),
1831            (short_text(), any::<u64>()).prop_map(|(collection, id)| Value::DocRef(collection, id)),
1832            short_text().prop_map(Value::TableRef),
1833            any::<u32>().prop_map(Value::PageRef),
1834            proptest::collection::vec(any::<u8>(), 0..8).prop_map(Value::Secret),
1835            short_text().prop_map(Value::Password),
1836        ];
1837
1838        leaf.prop_recursive(2, 16, 4, |inner| {
1839            proptest::collection::vec(inner, 0..4).prop_map(Value::Array)
1840        })
1841    }
1842
1843    proptest! {
1844        #[test]
1845        fn plain_text_is_deterministic_for_every_value_variant(value in arb_value()) {
1846            let first = value.plain_text();
1847            prop_assert_eq!(first, value.plain_text());
1848        }
1849    }
1850
1851    #[test]
1852    fn test_value_ipaddr_v4() {
1853        let value = Value::IpAddr(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
1854        let bytes = value.to_bytes();
1855        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1856        assert_eq!(value, recovered);
1857        assert_eq!(size, bytes.len());
1858    }
1859
1860    #[test]
1861    fn test_value_ipaddr_v6() {
1862        let value = Value::IpAddr(IpAddr::V6(Ipv6Addr::LOCALHOST));
1863        let bytes = value.to_bytes();
1864        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1865        assert_eq!(value, recovered);
1866        assert_eq!(size, bytes.len());
1867    }
1868
1869    #[test]
1870    fn test_value_vector() {
1871        let value = Value::Vector(vec![1.0, 2.0, 3.0, 4.5]);
1872        let bytes = value.to_bytes();
1873        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1874        assert_eq!(value, recovered);
1875        assert_eq!(size, bytes.len());
1876    }
1877
1878    #[test]
1879    fn test_value_mac_addr() {
1880        let value = Value::MacAddr([0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
1881        let bytes = value.to_bytes();
1882        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1883        assert_eq!(value, recovered);
1884        assert_eq!(size, bytes.len());
1885    }
1886
1887    #[test]
1888    fn test_value_uuid() {
1889        let uuid = [
1890            0x55, 0x04, 0x43, 0x01, 0x8f, 0x3b, 0x4a, 0x12, 0x9c, 0x5d, 0x6e, 0x7f, 0x80, 0x91,
1891            0xa2, 0xb3,
1892        ];
1893        let value = Value::Uuid(uuid);
1894        let bytes = value.to_bytes();
1895        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1896        assert_eq!(value, recovered);
1897        assert_eq!(size, bytes.len());
1898    }
1899
1900    #[test]
1901    fn test_value_null() {
1902        let value = Value::Null;
1903        let bytes = value.to_bytes();
1904        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1905        assert_eq!(value, recovered);
1906        assert_eq!(size, bytes.len());
1907    }
1908
1909    #[test]
1910    fn test_value_blob() {
1911        let value = Value::Blob(vec![0x00, 0x01, 0x02, 0x03, 0xFF]);
1912        let bytes = value.to_bytes();
1913        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1914        assert_eq!(value, recovered);
1915        assert_eq!(size, bytes.len());
1916    }
1917
1918    #[test]
1919    fn test_row_roundtrip() {
1920        let row = Row::new(vec![
1921            Value::Integer(42),
1922            Value::text("example.com".to_string()),
1923            Value::IpAddr(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))),
1924            Value::Boolean(true),
1925            Value::Null,
1926        ]);
1927
1928        let bytes = row.to_bytes();
1929        let (recovered, size) = Row::from_bytes(&bytes).unwrap();
1930
1931        assert_eq!(row, recovered);
1932        assert_eq!(size, bytes.len());
1933    }
1934
1935    #[test]
1936    fn test_varint_encoding() {
1937        let test_cases = [0u64, 1, 127, 128, 255, 256, 16383, 16384, u64::MAX];
1938
1939        for &value in &test_cases {
1940            let mut buf = Vec::new();
1941            write_varint(&mut buf, value);
1942            let (recovered, _) = read_varint(&buf).unwrap();
1943            assert_eq!(value, recovered, "Failed for value {}", value);
1944        }
1945    }
1946
1947    #[test]
1948    fn test_value_display() {
1949        assert_eq!(format!("{}", Value::Null), "NULL");
1950        assert_eq!(format!("{}", Value::Integer(42)), "42");
1951        assert_eq!(format!("{}", Value::Boolean(true)), "true");
1952        assert_eq!(format!("{}", Value::text("hello".to_string())), "'hello'");
1953    }
1954
1955    #[test]
1956    fn test_datatype_properties() {
1957        assert_eq!(DataType::Integer.fixed_size(), Some(8));
1958        assert_eq!(DataType::Text.fixed_size(), None);
1959        assert!(DataType::Integer.is_indexable());
1960        assert!(DataType::Text.is_indexable());
1961        assert!(!DataType::Blob.is_indexable());
1962        assert!(DataType::Integer.is_orderable());
1963        assert!(!DataType::Boolean.is_orderable());
1964    }
1965
1966    #[test]
1967    fn test_value_color_roundtrip() {
1968        let value = Value::Color([0xFF, 0x57, 0x33]);
1969        let bytes = value.to_bytes();
1970        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1971        assert_eq!(value, recovered);
1972        assert_eq!(size, bytes.len());
1973        assert_eq!(value.display_string(), "#FF5733");
1974    }
1975
1976    #[test]
1977    fn test_value_email_roundtrip() {
1978        let value = Value::Email("user@example.com".to_string());
1979        let bytes = value.to_bytes();
1980        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1981        assert_eq!(value, recovered);
1982        assert_eq!(size, bytes.len());
1983    }
1984
1985    #[test]
1986    fn test_value_url_roundtrip() {
1987        let value = Value::Url("https://example.com/path?q=1".to_string());
1988        let bytes = value.to_bytes();
1989        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1990        assert_eq!(value, recovered);
1991        assert_eq!(size, bytes.len());
1992    }
1993
1994    #[test]
1995    fn test_value_phone_roundtrip() {
1996        let value = Value::Phone(5511999887766);
1997        let bytes = value.to_bytes();
1998        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1999        assert_eq!(value, recovered);
2000        assert_eq!(size, bytes.len());
2001        assert_eq!(value.display_string(), "+5511999887766");
2002    }
2003
2004    #[test]
2005    fn test_value_semver_roundtrip() {
2006        let value = Value::Semver(1_000_000 + 23 * 1_000 + 456);
2007        let bytes = value.to_bytes();
2008        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2009        assert_eq!(value, recovered);
2010        assert_eq!(size, bytes.len());
2011        assert_eq!(value.display_string(), "1.23.456");
2012    }
2013
2014    #[test]
2015    fn test_value_cidr_roundtrip() {
2016        // 10.0.0.0/8
2017        let ip: u32 = 10 << 24;
2018        let value = Value::Cidr(ip, 8);
2019        let bytes = value.to_bytes();
2020        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2021        assert_eq!(value, recovered);
2022        assert_eq!(size, bytes.len());
2023        assert_eq!(value.display_string(), "10.0.0.0/8");
2024    }
2025
2026    #[test]
2027    fn test_value_date_roundtrip() {
2028        // 2024-01-15 -> some days since epoch
2029        let value = Value::Date(19738); // approximately 2024-01-15
2030        let bytes = value.to_bytes();
2031        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2032        assert_eq!(value, recovered);
2033        assert_eq!(size, bytes.len());
2034    }
2035
2036    #[test]
2037    fn test_value_time_roundtrip() {
2038        // 14:30:00 -> (14*3600 + 30*60) * 1000 = 52_200_000
2039        let value = Value::Time(52_200_000);
2040        let bytes = value.to_bytes();
2041        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2042        assert_eq!(value, recovered);
2043        assert_eq!(size, bytes.len());
2044        assert_eq!(value.display_string(), "14:30:00");
2045    }
2046
2047    #[test]
2048    fn test_value_decimal_roundtrip() {
2049        // 123.4567 * 10000 = 1234567
2050        let value = Value::Decimal(1_234_567);
2051        let bytes = value.to_bytes();
2052        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2053        assert_eq!(value, recovered);
2054        assert_eq!(size, bytes.len());
2055        assert_eq!(value.display_string(), "123.4567");
2056    }
2057
2058    #[test]
2059    fn test_value_enum_roundtrip() {
2060        let value = Value::EnumValue(3);
2061        let bytes = value.to_bytes();
2062        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2063        assert_eq!(value, recovered);
2064        assert_eq!(size, bytes.len());
2065        assert_eq!(value.display_string(), "enum(3)");
2066    }
2067
2068    #[test]
2069    fn test_value_array_roundtrip() {
2070        let value = Value::Array(vec![
2071            Value::Integer(1),
2072            Value::Integer(2),
2073            Value::Integer(3),
2074        ]);
2075        let bytes = value.to_bytes();
2076        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2077        assert_eq!(value, recovered);
2078        assert_eq!(size, bytes.len());
2079    }
2080
2081    #[test]
2082    fn test_value_array_nested_roundtrip() {
2083        let value = Value::Array(vec![
2084            Value::text("hello".to_string()),
2085            Value::text("world".to_string()),
2086        ]);
2087        let bytes = value.to_bytes();
2088        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2089        assert_eq!(value, recovered);
2090        assert_eq!(size, bytes.len());
2091    }
2092
2093    #[test]
2094    fn test_value_date_display_epoch() {
2095        // Day 0 = 1970-01-01
2096        let value = Value::Date(0);
2097        assert_eq!(value.display_string(), "1970-01-01");
2098    }
2099
2100    #[test]
2101    fn test_new_datatype_properties() {
2102        // Fixed sizes
2103        assert_eq!(DataType::Color.fixed_size(), Some(3));
2104        assert_eq!(DataType::Phone.fixed_size(), Some(8));
2105        assert_eq!(DataType::Semver.fixed_size(), Some(4));
2106        assert_eq!(DataType::Cidr.fixed_size(), Some(5));
2107        assert_eq!(DataType::Date.fixed_size(), Some(4));
2108        assert_eq!(DataType::Time.fixed_size(), Some(4));
2109        assert_eq!(DataType::Decimal.fixed_size(), Some(8));
2110        assert_eq!(DataType::Enum.fixed_size(), Some(1));
2111        assert_eq!(DataType::Email.fixed_size(), None);
2112        assert_eq!(DataType::Url.fixed_size(), None);
2113        assert_eq!(DataType::Array.fixed_size(), None);
2114
2115        // Indexable
2116        assert!(DataType::Email.is_indexable());
2117        assert!(DataType::Date.is_indexable());
2118        assert!(DataType::Decimal.is_indexable());
2119        assert!(!DataType::Color.is_indexable());
2120        assert!(!DataType::Array.is_indexable());
2121
2122        // Orderable
2123        assert!(DataType::Date.is_orderable());
2124        assert!(DataType::Time.is_orderable());
2125        assert!(DataType::Decimal.is_orderable());
2126        assert!(DataType::Semver.is_orderable());
2127        assert!(!DataType::Color.is_orderable());
2128        assert!(!DataType::Email.is_orderable());
2129    }
2130
2131    #[test]
2132    fn test_row_with_new_types() {
2133        let row = Row::new(vec![
2134            Value::Color([0xAA, 0xBB, 0xCC]),
2135            Value::Email("test@example.com".to_string()),
2136            Value::Phone(1234567890),
2137            Value::Semver(2_003_001), // 2.3.1
2138            Value::Date(19738),
2139            Value::EnumValue(1),
2140        ]);
2141
2142        let bytes = row.to_bytes();
2143        let (recovered, size) = Row::from_bytes(&bytes).unwrap();
2144        assert_eq!(row, recovered);
2145        assert_eq!(size, bytes.len());
2146    }
2147
2148    // --- New type serialization roundtrips ---
2149
2150    #[test]
2151    fn test_value_timestamp_ms_roundtrip() {
2152        let value = Value::TimestampMs(1710510600123);
2153        let bytes = value.to_bytes();
2154        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2155        assert_eq!(value, recovered);
2156        assert_eq!(size, bytes.len());
2157    }
2158
2159    #[test]
2160    fn test_value_ipv4_roundtrip() {
2161        let ip = (192u32 << 24) | (168 << 16) | (1 << 8) | 1;
2162        let value = Value::Ipv4(ip);
2163        let bytes = value.to_bytes();
2164        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2165        assert_eq!(value, recovered);
2166        assert_eq!(size, bytes.len());
2167        assert_eq!(value.display_string(), "192.168.1.1");
2168    }
2169
2170    #[test]
2171    fn test_value_ipv6_roundtrip() {
2172        let mut octets = [0u8; 16];
2173        octets[15] = 1;
2174        let value = Value::Ipv6(octets);
2175        let bytes = value.to_bytes();
2176        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2177        assert_eq!(value, recovered);
2178        assert_eq!(size, bytes.len());
2179        assert_eq!(value.display_string(), "::1");
2180    }
2181
2182    #[test]
2183    fn test_value_subnet_roundtrip() {
2184        let ip = 10u32 << 24;
2185        let mask = !0u32 << 16; // /16
2186        let value = Value::Subnet(ip, mask);
2187        let bytes = value.to_bytes();
2188        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2189        assert_eq!(value, recovered);
2190        assert_eq!(size, bytes.len());
2191        assert_eq!(value.display_string(), "10.0.0.0/16");
2192    }
2193
2194    #[test]
2195    fn test_value_port_roundtrip() {
2196        let value = Value::Port(8080);
2197        let bytes = value.to_bytes();
2198        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2199        assert_eq!(value, recovered);
2200        assert_eq!(size, bytes.len());
2201        assert_eq!(value.display_string(), "8080");
2202    }
2203
2204    #[test]
2205    fn test_value_latitude_roundtrip() {
2206        let value = Value::Latitude(-23550520);
2207        let bytes = value.to_bytes();
2208        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2209        assert_eq!(value, recovered);
2210        assert_eq!(size, bytes.len());
2211        assert_eq!(value.display_string(), "-23.550520");
2212    }
2213
2214    #[test]
2215    fn test_value_longitude_roundtrip() {
2216        let value = Value::Longitude(-46633308);
2217        let bytes = value.to_bytes();
2218        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2219        assert_eq!(value, recovered);
2220        assert_eq!(size, bytes.len());
2221        assert_eq!(value.display_string(), "-46.633308");
2222    }
2223
2224    #[test]
2225    fn test_value_geopoint_roundtrip() {
2226        let value = Value::GeoPoint(-23550520, -46633308);
2227        let bytes = value.to_bytes();
2228        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2229        assert_eq!(value, recovered);
2230        assert_eq!(size, bytes.len());
2231    }
2232
2233    #[test]
2234    fn test_value_country2_roundtrip() {
2235        let value = Value::Country2([b'B', b'R']);
2236        let bytes = value.to_bytes();
2237        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2238        assert_eq!(value, recovered);
2239        assert_eq!(size, bytes.len());
2240        assert_eq!(value.display_string(), "BR");
2241    }
2242
2243    #[test]
2244    fn test_value_country3_roundtrip() {
2245        let value = Value::Country3([b'B', b'R', b'A']);
2246        let bytes = value.to_bytes();
2247        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2248        assert_eq!(value, recovered);
2249        assert_eq!(size, bytes.len());
2250        assert_eq!(value.display_string(), "BRA");
2251    }
2252
2253    #[test]
2254    fn test_value_lang2_roundtrip() {
2255        let value = Value::Lang2([b'p', b't']);
2256        let bytes = value.to_bytes();
2257        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2258        assert_eq!(value, recovered);
2259        assert_eq!(size, bytes.len());
2260        assert_eq!(value.display_string(), "pt");
2261    }
2262
2263    #[test]
2264    fn test_value_lang5_roundtrip() {
2265        let value = Value::Lang5([b'p', b't', b'-', b'B', b'R']);
2266        let bytes = value.to_bytes();
2267        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2268        assert_eq!(value, recovered);
2269        assert_eq!(size, bytes.len());
2270        assert_eq!(value.display_string(), "pt-BR");
2271    }
2272
2273    #[test]
2274    fn test_value_currency_roundtrip() {
2275        let value = Value::Currency([b'U', b'S', b'D']);
2276        let bytes = value.to_bytes();
2277        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2278        assert_eq!(value, recovered);
2279        assert_eq!(size, bytes.len());
2280        assert_eq!(value.display_string(), "USD");
2281    }
2282
2283    #[test]
2284    fn test_value_color_alpha_roundtrip() {
2285        let value = Value::ColorAlpha([0xFF, 0x57, 0x33, 0x80]);
2286        let bytes = value.to_bytes();
2287        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2288        assert_eq!(value, recovered);
2289        assert_eq!(size, bytes.len());
2290        assert_eq!(value.display_string(), "#FF573380");
2291    }
2292
2293    #[test]
2294    fn test_value_bigint_roundtrip() {
2295        let value = Value::BigInt(i64::MAX);
2296        let bytes = value.to_bytes();
2297        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2298        assert_eq!(value, recovered);
2299        assert_eq!(size, bytes.len());
2300    }
2301
2302    #[test]
2303    fn test_new_datatype_roundtrip() {
2304        let types = [
2305            DataType::TimestampMs,
2306            DataType::Ipv4,
2307            DataType::Ipv6,
2308            DataType::Subnet,
2309            DataType::Port,
2310            DataType::Latitude,
2311            DataType::Longitude,
2312            DataType::GeoPoint,
2313            DataType::Country2,
2314            DataType::Country3,
2315            DataType::Lang2,
2316            DataType::Lang5,
2317            DataType::Currency,
2318            DataType::ColorAlpha,
2319            DataType::BigInt,
2320        ];
2321
2322        for dt in types {
2323            let byte = dt.to_byte();
2324            let recovered = DataType::from_byte(byte).unwrap();
2325            assert_eq!(dt, recovered);
2326        }
2327    }
2328
2329    #[test]
2330    fn test_rich_type_datatype_properties() {
2331        // Fixed sizes
2332        assert_eq!(DataType::TimestampMs.fixed_size(), Some(8));
2333        assert_eq!(DataType::Ipv4.fixed_size(), Some(4));
2334        assert_eq!(DataType::Ipv6.fixed_size(), Some(16));
2335        assert_eq!(DataType::Subnet.fixed_size(), Some(8));
2336        assert_eq!(DataType::Port.fixed_size(), Some(2));
2337        assert_eq!(DataType::Latitude.fixed_size(), Some(4));
2338        assert_eq!(DataType::Longitude.fixed_size(), Some(4));
2339        assert_eq!(DataType::GeoPoint.fixed_size(), Some(8));
2340        assert_eq!(DataType::Country2.fixed_size(), Some(2));
2341        assert_eq!(DataType::Country3.fixed_size(), Some(3));
2342        assert_eq!(DataType::Lang2.fixed_size(), Some(2));
2343        assert_eq!(DataType::Lang5.fixed_size(), Some(5));
2344        assert_eq!(DataType::Currency.fixed_size(), Some(3));
2345        assert_eq!(DataType::ColorAlpha.fixed_size(), Some(4));
2346        assert_eq!(DataType::BigInt.fixed_size(), Some(8));
2347
2348        // Indexable
2349        assert!(DataType::TimestampMs.is_indexable());
2350        assert!(DataType::Ipv4.is_indexable());
2351        assert!(DataType::Ipv6.is_indexable());
2352        assert!(DataType::Port.is_indexable());
2353        assert!(DataType::Country2.is_indexable());
2354        assert!(DataType::Currency.is_indexable());
2355        assert!(DataType::BigInt.is_indexable());
2356        assert!(!DataType::Subnet.is_indexable());
2357        assert!(!DataType::ColorAlpha.is_indexable());
2358
2359        // Orderable
2360        assert!(DataType::TimestampMs.is_orderable());
2361        assert!(DataType::Port.is_orderable());
2362        assert!(DataType::Latitude.is_orderable());
2363        assert!(DataType::Longitude.is_orderable());
2364        assert!(DataType::BigInt.is_orderable());
2365        assert!(!DataType::Country2.is_orderable());
2366        assert!(!DataType::Ipv4.is_orderable());
2367    }
2368
2369    #[test]
2370    fn test_row_with_all_new_types() {
2371        let row = Row::new(vec![
2372            Value::TimestampMs(1710510600123),
2373            Value::Ipv4((10u32 << 24) | 1),
2374            Value::Ipv6([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]),
2375            Value::Subnet(10u32 << 24, !0u32 << 16),
2376            Value::Port(443),
2377            Value::Latitude(-23550520),
2378            Value::Longitude(-46633308),
2379            Value::GeoPoint(-23550520, -46633308),
2380            Value::Country2([b'B', b'R']),
2381            Value::Country3([b'B', b'R', b'A']),
2382            Value::Lang2([b'p', b't']),
2383            Value::Lang5([b'p', b't', b'-', b'B', b'R']),
2384            Value::Currency([b'U', b'S', b'D']),
2385            Value::ColorAlpha([0xFF, 0x57, 0x33, 0x80]),
2386            Value::BigInt(i64::MAX),
2387        ]);
2388
2389        let bytes = row.to_bytes();
2390        let (recovered, size) = Row::from_bytes(&bytes).unwrap();
2391        assert_eq!(row, recovered);
2392        assert_eq!(size, bytes.len());
2393    }
2394
2395    #[test]
2396    fn sql_type_name_parses_modifiers_and_helpers() {
2397        let decimal = SqlTypeName::parse_declared("decimal(10, 2)");
2398        assert_eq!(decimal.base_name(), "DECIMAL");
2399        assert_eq!(decimal.decimal_precision(), Some(10));
2400        assert_eq!(decimal.to_string(), "DECIMAL(10,2)");
2401
2402        let enum_type = SqlTypeName::parse_declared("enum('red','blue')");
2403        assert_eq!(
2404            enum_type.enum_variants(),
2405            Some(vec!["red".to_string(), "blue".to_string()])
2406        );
2407
2408        let bad_enum = SqlTypeName::new("enum").with_modifiers(vec![TypeModifier::Number(1)]);
2409        assert_eq!(bad_enum.enum_variants(), None);
2410        assert_eq!(SqlTypeName::new("text").enum_variants(), None);
2411
2412        let array = SqlTypeName::parse_declared("array(varchar(12))");
2413        assert_eq!(array.array_element_type(), Some("VARCHAR(12)".to_string()));
2414        let array_ident =
2415            SqlTypeName::new("array").with_modifiers(vec![TypeModifier::Ident("int".to_string())]);
2416        assert_eq!(array_ident.array_element_type(), Some("INT".to_string()));
2417        assert_eq!(SqlTypeName::new("text").array_element_type(), None);
2418        assert_eq!(SqlTypeName::new("text").decimal_precision(), None);
2419
2420        assert_eq!(
2421            SqlTypeName::parse_declared("enum('a,b', array(int))").to_string(),
2422            "ENUM('a,b',ARRAY(INT))"
2423        );
2424        assert_eq!(SqlTypeName::parse_declared("").to_string(), "");
2425    }
2426
2427    #[test]
2428    fn datatype_category_preference_display_and_storage_traits_cover_all_variants() {
2429        let cases = [
2430            (
2431                DataType::Unknown,
2432                TypeCategory::Unknown,
2433                "UNKNOWN",
2434                None,
2435                false,
2436                false,
2437            ),
2438            (
2439                DataType::Integer,
2440                TypeCategory::Numeric,
2441                "INTEGER",
2442                Some(8),
2443                true,
2444                true,
2445            ),
2446            (
2447                DataType::UnsignedInteger,
2448                TypeCategory::Numeric,
2449                "UNSIGNED INTEGER",
2450                Some(8),
2451                true,
2452                true,
2453            ),
2454            (
2455                DataType::Float,
2456                TypeCategory::Numeric,
2457                "FLOAT",
2458                Some(8),
2459                true,
2460                true,
2461            ),
2462            (
2463                DataType::Text,
2464                TypeCategory::String,
2465                "TEXT",
2466                None,
2467                true,
2468                true,
2469            ),
2470            (
2471                DataType::Blob,
2472                TypeCategory::String,
2473                "BLOB",
2474                None,
2475                false,
2476                false,
2477            ),
2478            (
2479                DataType::Boolean,
2480                TypeCategory::Boolean,
2481                "BOOLEAN",
2482                Some(1),
2483                false,
2484                false,
2485            ),
2486            (
2487                DataType::Timestamp,
2488                TypeCategory::DateTime,
2489                "TIMESTAMP",
2490                Some(8),
2491                true,
2492                true,
2493            ),
2494            (
2495                DataType::Duration,
2496                TypeCategory::TimeSpan,
2497                "DURATION",
2498                Some(8),
2499                false,
2500                true,
2501            ),
2502            (
2503                DataType::IpAddr,
2504                TypeCategory::Network,
2505                "IPADDR",
2506                None,
2507                true,
2508                false,
2509            ),
2510            (
2511                DataType::MacAddr,
2512                TypeCategory::Network,
2513                "MACADDR",
2514                Some(6),
2515                false,
2516                false,
2517            ),
2518            (
2519                DataType::Vector,
2520                TypeCategory::Vector,
2521                "VECTOR",
2522                None,
2523                false,
2524                false,
2525            ),
2526            (
2527                DataType::Nullable,
2528                TypeCategory::Unknown,
2529                "NULLABLE",
2530                None,
2531                false,
2532                false,
2533            ),
2534            (
2535                DataType::Json,
2536                TypeCategory::Json,
2537                "JSON",
2538                None,
2539                false,
2540                false,
2541            ),
2542            (
2543                DataType::Uuid,
2544                TypeCategory::Uuid,
2545                "UUID",
2546                Some(16),
2547                true,
2548                false,
2549            ),
2550            (
2551                DataType::NodeRef,
2552                TypeCategory::Reference,
2553                "NODEREF",
2554                None,
2555                true,
2556                false,
2557            ),
2558            (
2559                DataType::EdgeRef,
2560                TypeCategory::Reference,
2561                "EDGEREF",
2562                None,
2563                true,
2564                false,
2565            ),
2566            (
2567                DataType::VectorRef,
2568                TypeCategory::Reference,
2569                "VECTORREF",
2570                Some(8),
2571                true,
2572                false,
2573            ),
2574            (
2575                DataType::RowRef,
2576                TypeCategory::Reference,
2577                "ROWREF",
2578                None,
2579                true,
2580                false,
2581            ),
2582            (
2583                DataType::Color,
2584                TypeCategory::Domain,
2585                "COLOR",
2586                Some(3),
2587                false,
2588                false,
2589            ),
2590            (
2591                DataType::Email,
2592                TypeCategory::Domain,
2593                "EMAIL",
2594                None,
2595                true,
2596                false,
2597            ),
2598            (
2599                DataType::Url,
2600                TypeCategory::Domain,
2601                "URL",
2602                None,
2603                true,
2604                false,
2605            ),
2606            (
2607                DataType::Phone,
2608                TypeCategory::Domain,
2609                "PHONE",
2610                Some(8),
2611                true,
2612                false,
2613            ),
2614            (
2615                DataType::Semver,
2616                TypeCategory::Domain,
2617                "SEMVER",
2618                Some(4),
2619                true,
2620                true,
2621            ),
2622            (
2623                DataType::Cidr,
2624                TypeCategory::Network,
2625                "CIDR",
2626                Some(5),
2627                false,
2628                false,
2629            ),
2630            (
2631                DataType::Date,
2632                TypeCategory::DateTime,
2633                "DATE",
2634                Some(4),
2635                true,
2636                true,
2637            ),
2638            (
2639                DataType::Time,
2640                TypeCategory::DateTime,
2641                "TIME",
2642                Some(4),
2643                true,
2644                true,
2645            ),
2646            (
2647                DataType::Decimal,
2648                TypeCategory::Numeric,
2649                "DECIMAL",
2650                Some(8),
2651                true,
2652                true,
2653            ),
2654            (
2655                DataType::Enum,
2656                TypeCategory::Domain,
2657                "ENUM",
2658                Some(1),
2659                true,
2660                false,
2661            ),
2662            (
2663                DataType::Array,
2664                TypeCategory::Array,
2665                "ARRAY",
2666                None,
2667                false,
2668                false,
2669            ),
2670            (
2671                DataType::TimestampMs,
2672                TypeCategory::DateTime,
2673                "TIMESTAMP_MS",
2674                Some(8),
2675                true,
2676                true,
2677            ),
2678            (
2679                DataType::Ipv4,
2680                TypeCategory::Network,
2681                "IPV4",
2682                Some(4),
2683                true,
2684                false,
2685            ),
2686            (
2687                DataType::Ipv6,
2688                TypeCategory::Network,
2689                "IPV6",
2690                Some(16),
2691                true,
2692                false,
2693            ),
2694            (
2695                DataType::Subnet,
2696                TypeCategory::Network,
2697                "SUBNET",
2698                Some(8),
2699                false,
2700                false,
2701            ),
2702            (
2703                DataType::Port,
2704                TypeCategory::Numeric,
2705                "PORT",
2706                Some(2),
2707                true,
2708                true,
2709            ),
2710            (
2711                DataType::Latitude,
2712                TypeCategory::Numeric,
2713                "LATITUDE",
2714                Some(4),
2715                true,
2716                true,
2717            ),
2718            (
2719                DataType::Longitude,
2720                TypeCategory::Numeric,
2721                "LONGITUDE",
2722                Some(4),
2723                true,
2724                true,
2725            ),
2726            (
2727                DataType::GeoPoint,
2728                TypeCategory::Geo,
2729                "GEOPOINT",
2730                Some(8),
2731                true,
2732                false,
2733            ),
2734            (
2735                DataType::Country2,
2736                TypeCategory::Domain,
2737                "COUNTRY2",
2738                Some(2),
2739                true,
2740                false,
2741            ),
2742            (
2743                DataType::Country3,
2744                TypeCategory::Domain,
2745                "COUNTRY3",
2746                Some(3),
2747                true,
2748                false,
2749            ),
2750            (
2751                DataType::Lang2,
2752                TypeCategory::Domain,
2753                "LANG2",
2754                Some(2),
2755                true,
2756                false,
2757            ),
2758            (
2759                DataType::Lang5,
2760                TypeCategory::Domain,
2761                "LANG5",
2762                Some(5),
2763                true,
2764                false,
2765            ),
2766            (
2767                DataType::Currency,
2768                TypeCategory::Domain,
2769                "CURRENCY",
2770                Some(3),
2771                true,
2772                false,
2773            ),
2774            (
2775                DataType::ColorAlpha,
2776                TypeCategory::Domain,
2777                "COLOR_ALPHA",
2778                Some(4),
2779                false,
2780                false,
2781            ),
2782            (
2783                DataType::BigInt,
2784                TypeCategory::Numeric,
2785                "BIGINT",
2786                Some(8),
2787                true,
2788                true,
2789            ),
2790            (
2791                DataType::KeyRef,
2792                TypeCategory::Reference,
2793                "KEY_REF",
2794                None,
2795                true,
2796                false,
2797            ),
2798            (
2799                DataType::DocRef,
2800                TypeCategory::Reference,
2801                "DOC_REF",
2802                None,
2803                true,
2804                false,
2805            ),
2806            (
2807                DataType::TableRef,
2808                TypeCategory::Reference,
2809                "TABLE_REF",
2810                None,
2811                true,
2812                false,
2813            ),
2814            (
2815                DataType::PageRef,
2816                TypeCategory::Reference,
2817                "PAGE_REF",
2818                Some(4),
2819                true,
2820                false,
2821            ),
2822            (
2823                DataType::Secret,
2824                TypeCategory::Opaque,
2825                "SECRET",
2826                None,
2827                false,
2828                false,
2829            ),
2830            (
2831                DataType::Password,
2832                TypeCategory::Opaque,
2833                "PASSWORD",
2834                None,
2835                false,
2836                false,
2837            ),
2838            (
2839                DataType::TextZstd,
2840                TypeCategory::String,
2841                "TEXT",
2842                None,
2843                false,
2844                false,
2845            ),
2846            (
2847                DataType::BlobZstd,
2848                TypeCategory::String,
2849                "BLOB",
2850                None,
2851                false,
2852                false,
2853            ),
2854            (
2855                DataType::AssetCode,
2856                TypeCategory::Domain,
2857                "ASSET_CODE",
2858                None,
2859                true,
2860                true,
2861            ),
2862            (
2863                DataType::Money,
2864                TypeCategory::Domain,
2865                "MONEY",
2866                None,
2867                false,
2868                false,
2869            ),
2870        ];
2871
2872        for (data_type, category, display, fixed, indexable, orderable) in cases {
2873            assert_eq!(data_type.category(), category, "{data_type:?}");
2874            assert_eq!(data_type.to_string(), display);
2875            assert_eq!(data_type.fixed_size(), fixed, "{data_type:?}");
2876            assert_eq!(data_type.is_indexable(), indexable, "{data_type:?}");
2877            assert_eq!(data_type.is_orderable(), orderable, "{data_type:?}");
2878        }
2879
2880        for preferred in [
2881            DataType::Float,
2882            DataType::Text,
2883            DataType::TimestampMs,
2884            DataType::IpAddr,
2885            DataType::Boolean,
2886            DataType::Uuid,
2887        ] {
2888            assert!(preferred.is_preferred(), "{preferred:?}");
2889        }
2890        assert!(!DataType::Integer.is_preferred());
2891    }
2892
2893    #[test]
2894    fn sql_aliases_cover_domain_reference_and_unknown_paths() {
2895        let aliases = [
2896            ("BOOL", DataType::Boolean),
2897            ("SMALLINT", DataType::Integer),
2898            ("BIGSERIAL", DataType::BigInt),
2899            ("UNSIGNED INTEGER", DataType::UnsignedInteger),
2900            ("DOUBLE", DataType::Float),
2901            ("VARCHAR(20)", DataType::Text),
2902            ("BYTEA", DataType::Blob),
2903            ("TIMESTAMP_MS", DataType::TimestampMs),
2904            ("INTERVAL", DataType::Duration),
2905            ("NUMERIC(10,2)", DataType::Decimal),
2906            ("JSONB", DataType::Json),
2907            ("INET", DataType::IpAddr),
2908            ("COLOR_ALPHA", DataType::ColorAlpha),
2909            ("GEO_POINT", DataType::GeoPoint),
2910            ("ASSET_CODE", DataType::AssetCode),
2911            ("KEYREF", DataType::KeyRef),
2912            ("DOCREF", DataType::DocRef),
2913            ("TABLEREF", DataType::TableRef),
2914            ("PAGEREF", DataType::PageRef),
2915            ("PASSWORD", DataType::Password),
2916            ("VECTOR", DataType::Vector),
2917        ];
2918
2919        for (alias, expected) in aliases {
2920            assert_eq!(DataType::from_sql_name(alias), Some(expected), "{alias}");
2921        }
2922        assert_eq!(DataType::from_sql_name("definitely_not_a_type"), None);
2923        assert_eq!(DataType::from_byte(0), None);
2924        assert_eq!(DataType::from_byte(55), None);
2925    }
2926
2927    #[test]
2928    fn value_accessors_hash_and_display_cover_remaining_variants() {
2929        let values = vec![
2930            Value::Null,
2931            Value::Integer(-1),
2932            Value::UnsignedInteger(2),
2933            Value::Float(3.5),
2934            Value::text("hello"),
2935            Value::Blob(vec![1, 2, 3]),
2936            Value::Boolean(true),
2937            Value::Timestamp(4),
2938            Value::Duration(5),
2939            Value::IpAddr(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
2940            Value::IpAddr(IpAddr::V6(Ipv6Addr::LOCALHOST)),
2941            Value::MacAddr([1, 2, 3, 4, 5, 6]),
2942            Value::Vector(vec![1.0, 2.0]),
2943            Value::Json(br#"{"ok":true}"#.to_vec()),
2944            Value::Uuid([7; 16]),
2945            Value::NodeRef("node".to_string()),
2946            Value::EdgeRef("edge".to_string()),
2947            Value::VectorRef("vectors".to_string(), 8),
2948            Value::RowRef("rows".to_string(), 9),
2949            Value::Color([0xAA, 0xBB, 0xCC]),
2950            Value::Email("a@example.com".to_string()),
2951            Value::Url("https://example.com".to_string()),
2952            Value::Phone(5511999),
2953            Value::Semver(1_002_003),
2954            Value::Cidr(10 << 24, 8),
2955            Value::Date(20_000),
2956            Value::Time(43_200_000),
2957            Value::Decimal(123_456),
2958            Value::EnumValue(3),
2959            Value::Array(vec![Value::Integer(1), Value::text("two")]),
2960            Value::TimestampMs(123_456),
2961            Value::Ipv4(0x7f000001),
2962            Value::Ipv6([1; 16]),
2963            Value::Subnet(10 << 24, 0xff00ff00),
2964            Value::Port(5432),
2965            Value::Latitude(-23_550_520),
2966            Value::Longitude(-46_633_308),
2967            Value::GeoPoint(-23_550_520, -46_633_308),
2968            Value::Country2(*b"BR"),
2969            Value::Country3(*b"BRA"),
2970            Value::Lang2(*b"pt"),
2971            Value::Lang5(*b"pt-BR"),
2972            Value::Currency(*b"USD"),
2973            Value::AssetCode("BTC".to_string()),
2974            Value::Money {
2975                asset_code: "USD".to_string(),
2976                minor_units: -1234,
2977                scale: 2,
2978            },
2979            Value::ColorAlpha([1, 2, 3, 4]),
2980            Value::BigInt(-10),
2981            Value::KeyRef("kv".to_string(), "key".to_string()),
2982            Value::DocRef("docs".to_string(), 42),
2983            Value::TableRef("users".to_string()),
2984            Value::PageRef(99),
2985            Value::Secret(vec![9, 8, 7]),
2986            Value::Password("$argon2id$v=19$hash".to_string()),
2987        ];
2988
2989        for value in &values {
2990            let mut hasher = DefaultHasher::new();
2991            value.hash(&mut hasher);
2992            let _ = hasher.finish();
2993            assert_eq!(value.data_type(), value.data_type());
2994            assert!(!value.display_string().is_empty());
2995            assert!(!value.plain_text().is_empty());
2996        }
2997
2998        assert!(Value::Null.is_null());
2999        assert_eq!(Value::Integer(-1).as_integer(), Some(-1));
3000        assert_eq!(
3001            Value::UnsignedInteger(i64::MAX as u64).as_integer(),
3002            Some(i64::MAX)
3003        );
3004        assert_eq!(
3005            Value::UnsignedInteger(i64::MAX as u64 + 1).as_integer(),
3006            None
3007        );
3008        assert_eq!(Value::Timestamp(1).as_integer(), Some(1));
3009        assert_eq!(Value::Duration(2).as_integer(), Some(2));
3010        assert_eq!(Value::Float(1.5).as_float(), Some(1.5));
3011        assert_eq!(Value::Integer(2).as_float(), Some(2.0));
3012        assert_eq!(Value::UnsignedInteger(3).as_float(), Some(3.0));
3013        assert_eq!(Value::text("x").as_text(), Some("x"));
3014        assert_eq!(Value::Boolean(true).as_boolean(), Some(true));
3015        assert_eq!(
3016            Value::IpAddr(IpAddr::V4(Ipv4Addr::LOCALHOST)).as_ip_addr(),
3017            Some(IpAddr::V4(Ipv4Addr::LOCALHOST))
3018        );
3019        assert_eq!(Value::Vector(vec![1.0]).as_vector(), Some(&[1.0][..]));
3020        assert_eq!(Value::Null.as_integer(), None);
3021        assert_eq!(Value::Null.as_float(), None);
3022        assert_eq!(Value::Null.as_text(), None);
3023        assert_eq!(Value::Null.as_boolean(), None);
3024        assert_eq!(Value::Null.as_ip_addr(), None);
3025        assert_eq!(Value::Null.as_vector(), None);
3026    }
3027
3028    #[test]
3029    fn row_accessors_iteration_and_error_paths() {
3030        let empty = Row::new(Vec::new());
3031        assert!(empty.is_empty());
3032        assert_eq!(empty.len(), 0);
3033        assert_eq!(empty.get(0), None);
3034
3035        let row = Row::from(vec![Value::Integer(1), Value::text("two")]);
3036        assert_eq!(row.len(), 2);
3037        assert_eq!(row.get(1), Some(&Value::text("two")));
3038        assert_eq!(row.values().len(), 2);
3039        assert_eq!(row.iter().count(), 2);
3040        assert_eq!(row.clone().into_values().len(), 2);
3041        assert_eq!(row.into_iter().count(), 2);
3042
3043        assert_eq!(Row::from_bytes(&[]).unwrap_err(), ValueError::EmptyData);
3044        assert_eq!(
3045            Row::from_bytes(&[0x80]).unwrap_err(),
3046            ValueError::TruncatedData
3047        );
3048    }
3049
3050    #[test]
3051    fn value_error_display_covers_every_variant() {
3052        let errors = [
3053            (ValueError::EmptyData, "empty data"),
3054            (ValueError::InvalidType(99), "invalid type byte: 99"),
3055            (ValueError::TruncatedData, "truncated data"),
3056            (ValueError::InvalidUtf8, "invalid UTF-8"),
3057            (ValueError::InvalidIpVersion(5), "invalid IP version: 5"),
3058            (ValueError::VarintOverflow, "varint overflow"),
3059            (
3060                ValueError::TypeMismatch {
3061                    expected: DataType::Text,
3062                    found: DataType::Integer,
3063                },
3064                "type mismatch: expected TEXT, found INTEGER",
3065            ),
3066        ];
3067
3068        for (error, message) in errors {
3069            assert_eq!(error.to_string(), message);
3070        }
3071    }
3072}