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