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    /// Admit encoded input and conservative decoded allocations before parsing.
1122    /// The caller must separately reserve this allowance in its shared memory
1123    /// pool and retain that reservation for the resulting value's lifetime.
1124    pub fn from_bytes_with_limit(
1125        data: &[u8],
1126        limit_bytes: usize,
1127    ) -> Result<(Self, usize), ValueError> {
1128        super::value_codec::decode_with_limit(data, limit_bytes)
1129    }
1130
1131    /// Decode against a caller-owned, conservative allocation allowance. A
1132    /// successful decode subtracts its preflight usage; failures leave it intact.
1133    /// This local allowance does not reserve memory in the runtime's shared pool.
1134    pub fn from_bytes_with_budget(
1135        data: &[u8],
1136        remaining_bytes: &mut usize,
1137    ) -> Result<(Self, usize), ValueError> {
1138        super::value_codec::decode_with_budget(data, remaining_bytes)
1139    }
1140
1141    /// Try to convert to i64
1142    pub fn as_integer(&self) -> Option<i64> {
1143        match self {
1144            Value::Integer(v) => Some(*v),
1145            Value::UnsignedInteger(v) => {
1146                if *v <= i64::MAX as u64 {
1147                    Some(*v as i64)
1148                } else {
1149                    None
1150                }
1151            }
1152            Value::Timestamp(v) => Some(*v),
1153            Value::Duration(v) => Some(*v),
1154            _ => None,
1155        }
1156    }
1157
1158    /// Try to convert to f64
1159    pub fn as_float(&self) -> Option<f64> {
1160        match self {
1161            Value::Float(v) => Some(*v),
1162            Value::Integer(v) => Some(*v as f64),
1163            Value::UnsignedInteger(v) => Some(*v as f64),
1164            _ => None,
1165        }
1166    }
1167
1168    /// Try to convert to string reference
1169    pub fn as_text(&self) -> Option<&str> {
1170        match self {
1171            Value::Text(s) => Some(s),
1172            _ => None,
1173        }
1174    }
1175
1176    /// Try to convert to bool
1177    pub fn as_boolean(&self) -> Option<bool> {
1178        match self {
1179            Value::Boolean(v) => Some(*v),
1180            _ => None,
1181        }
1182    }
1183
1184    /// Try to convert to IpAddr
1185    pub fn as_ip_addr(&self) -> Option<IpAddr> {
1186        match self {
1187            Value::IpAddr(addr) => Some(*addr),
1188            _ => None,
1189        }
1190    }
1191
1192    /// Try to convert to vector reference
1193    pub fn as_vector(&self) -> Option<&[f32]> {
1194        match self {
1195            Value::Vector(v) => Some(v),
1196            _ => None,
1197        }
1198    }
1199
1200    /// Human-readable display string for rich types
1201    pub fn display_string(&self) -> String {
1202        match self {
1203            Value::Color([r, g, b]) => format!("#{:02X}{:02X}{:02X}", r, g, b),
1204            Value::Email(s) => s.clone(),
1205            Value::Url(s) => s.clone(),
1206            Value::Phone(n) => format!("+{}", n),
1207            Value::Semver(packed) => format!(
1208                "{}.{}.{}",
1209                packed / 1_000_000,
1210                (packed / 1_000) % 1_000,
1211                packed % 1_000
1212            ),
1213            Value::Cidr(ip, prefix) => format!(
1214                "{}.{}.{}.{}/{}",
1215                (ip >> 24) & 0xFF,
1216                (ip >> 16) & 0xFF,
1217                (ip >> 8) & 0xFF,
1218                ip & 0xFF,
1219                prefix
1220            ),
1221            Value::Date(days) => format_civil_date(*days),
1222            Value::Time(ms) => {
1223                let total_secs = ms / 1000;
1224                format!(
1225                    "{:02}:{:02}:{:02}",
1226                    total_secs / 3600,
1227                    (total_secs / 60) % 60,
1228                    total_secs % 60
1229                )
1230            }
1231            Value::Decimal(v) => format_scaled_i64(*v, 4),
1232            Value::DecimalText(v) => v.clone(),
1233            Value::EnumValue(i) => format!("enum({})", i),
1234            Value::Array(elems) => {
1235                let items: Vec<String> = elems.iter().map(|e| e.display_string()).collect();
1236                format!("[{}]", items.join(", "))
1237            }
1238            Value::TimestampMs(ms) => {
1239                let secs = ms / 1000;
1240                let millis = (ms % 1000).unsigned_abs() as u32;
1241                let days = (secs / 86400) as i32;
1242                let day_secs = (secs % 86400) as u32;
1243                let h = day_secs / 3600;
1244                let m = (day_secs / 60) % 60;
1245                let s = day_secs % 60;
1246                format!(
1247                    "{}T{:02}:{:02}:{:02}.{:03}Z",
1248                    format_civil_date(days),
1249                    h,
1250                    m,
1251                    s,
1252                    millis
1253                )
1254            }
1255            Value::Ipv4(ip) => format!(
1256                "{}.{}.{}.{}",
1257                (ip >> 24) & 0xFF,
1258                (ip >> 16) & 0xFF,
1259                (ip >> 8) & 0xFF,
1260                ip & 0xFF
1261            ),
1262            Value::Ipv6(bytes) => {
1263                let addr = std::net::Ipv6Addr::from(*bytes);
1264                format!("{}", addr)
1265            }
1266            Value::Subnet(ip, mask) => {
1267                let ip_str = format!(
1268                    "{}.{}.{}.{}",
1269                    (ip >> 24) & 0xFF,
1270                    (ip >> 16) & 0xFF,
1271                    (ip >> 8) & 0xFF,
1272                    ip & 0xFF
1273                );
1274                // Convert mask to prefix length if possible
1275                let prefix = mask.leading_ones();
1276                if prefix < 32 && (*mask << prefix) == 0 || prefix == 32 {
1277                    format!("{}/{}", ip_str, prefix)
1278                } else {
1279                    let mask_str = format!(
1280                        "{}.{}.{}.{}",
1281                        (mask >> 24) & 0xFF,
1282                        (mask >> 16) & 0xFF,
1283                        (mask >> 8) & 0xFF,
1284                        mask & 0xFF
1285                    );
1286                    format!("{}/{}", ip_str, mask_str)
1287                }
1288            }
1289            Value::Port(p) => p.to_string(),
1290            Value::Latitude(micro) => format!("{:.6}", *micro as f64 / 1_000_000.0),
1291            Value::Longitude(micro) => format!("{:.6}", *micro as f64 / 1_000_000.0),
1292            Value::GeoPoint(lat, lon) => format!(
1293                "{:.6},{:.6}",
1294                *lat as f64 / 1_000_000.0,
1295                *lon as f64 / 1_000_000.0
1296            ),
1297            Value::Country2(c) => String::from_utf8_lossy(c).to_string(),
1298            Value::Country3(c) => String::from_utf8_lossy(c).to_string(),
1299            Value::Lang2(c) => String::from_utf8_lossy(c).to_string(),
1300            Value::Lang5(c) => String::from_utf8_lossy(c).to_string(),
1301            Value::Currency(c) => String::from_utf8_lossy(c).to_string(),
1302            Value::AssetCode(code) => code.clone(),
1303            Value::Money {
1304                asset_code,
1305                minor_units,
1306                scale,
1307            } => format!("{} {}", asset_code, format_scaled_i64(*minor_units, *scale)),
1308            Value::ColorAlpha([r, g, b, a]) => format!("#{:02X}{:02X}{:02X}{:02X}", r, g, b, a),
1309            Value::BigInt(v) => v.to_string(),
1310            Value::KeyRef(c, k) => format!("{}:{}", c, k),
1311            Value::DocRef(c, id) => format!("{}#{}", c, id),
1312            Value::TableRef(t) => t.clone(),
1313            Value::PageRef(p) => format!("page:{}", p),
1314            other => format!("{}", other),
1315        }
1316    }
1317
1318    /// Plain text coercion for string-producing SQL operators.
1319    ///
1320    /// Unlike `Display`, this does not render `Text` as a SQL literal.
1321    pub fn plain_text(&self) -> String {
1322        match self {
1323            Value::Text(text) => text.to_string(),
1324            Value::Array(elems) => {
1325                let items: Vec<String> = elems.iter().map(Value::plain_text).collect();
1326                format!("[{}]", items.join(", "))
1327            }
1328            other => other.display_string(),
1329        }
1330    }
1331}
1332
1333/// The fixed scale of [`Value::Decimal`].
1334///
1335/// `Value::Decimal(i64)` stores `value * 10^DECIMAL_SCALE` and carries no scale
1336/// of its own. Four is not a free choice — it is already the scale the write
1337/// side parses at (`coerce::parse_decimal`), the render side formats at
1338/// (`display_string`, `Display`), and, load-bearingly, the scale that
1339/// comparison and ordering assume (`value_compare`). Reads must agree with all
1340/// of them.
1341pub const DECIMAL_SCALE: u8 = 4;
1342
1343/// Convert a [`Value::Decimal`] payload to its true numeric value.
1344///
1345/// The single authority for that conversion. It used to be open-coded at nine
1346/// call sites, four dividing by `10^4` and five casting raw, which made
1347/// `SUM`/`AVG` over a `DECIMAL` column differ by four orders of magnitude
1348/// depending on which aggregate route the planner picked (#2058).
1349pub fn decimal_to_f64(value: i64) -> f64 {
1350    value as f64 / 10_i64.pow(DECIMAL_SCALE as u32) as f64
1351}
1352
1353fn format_scaled_i64(value: i64, scale: u8) -> String {
1354    let negative = value < 0;
1355    let abs = (value as i128).abs();
1356    if scale == 0 {
1357        return if negative {
1358            format!("-{}", abs)
1359        } else {
1360            abs.to_string()
1361        };
1362    }
1363
1364    let divisor = 10_i128.pow(scale as u32);
1365    let whole = abs / divisor;
1366    let frac = abs % divisor;
1367    let sign = if negative { "-" } else { "" };
1368    format!("{}{}.{:0width$}", sign, whole, frac, width = scale as usize)
1369}
1370
1371impl fmt::Display for Value {
1372    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1373        match self {
1374            Value::Null => write!(f, "NULL"),
1375            Value::Integer(v) => write!(f, "{}", v),
1376            Value::UnsignedInteger(v) => write!(f, "{}", v),
1377            Value::Float(v) => write!(f, "{}", v),
1378            Value::Text(s) => write!(f, "'{}'", s),
1379            Value::Blob(b) => write!(f, "<blob {} bytes>", b.len()),
1380            Value::Boolean(v) => write!(f, "{}", v),
1381            Value::Timestamp(v) => write!(f, "ts:{}", v),
1382            Value::Duration(v) => write!(f, "{}ms", v),
1383            Value::IpAddr(addr) => write!(f, "{}", addr),
1384            Value::MacAddr(mac) => write!(
1385                f,
1386                "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
1387                mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
1388            ),
1389            Value::Vector(v) => write!(f, "<vector dim={}>", v.len()),
1390            Value::Json(j) => write!(f, "<json {} bytes>", j.len()),
1391            Value::Uuid(u) => {
1392                write!(
1393                    f,
1394                    "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
1395                    u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7],
1396                    u[8], u[9], u[10], u[11], u[12], u[13], u[14], u[15]
1397                )
1398            }
1399            Value::NodeRef(id) => write!(f, "node:{}", id),
1400            Value::EdgeRef(id) => write!(f, "edge:{}", id),
1401            Value::VectorRef(coll, id) => write!(f, "vector:{}:{}", coll, id),
1402            Value::RowRef(table, id) => write!(f, "row:{}:{}", table, id),
1403            Value::Color([r, g, b]) => write!(f, "#{:02X}{:02X}{:02X}", r, g, b),
1404            Value::Email(s) => write!(f, "{}", s),
1405            Value::Url(s) => write!(f, "{}", s),
1406            Value::Phone(n) => write!(f, "+{}", n),
1407            Value::Semver(packed) => write!(
1408                f,
1409                "{}.{}.{}",
1410                packed / 1_000_000,
1411                (packed / 1_000) % 1_000,
1412                packed % 1_000
1413            ),
1414            Value::Cidr(ip, prefix) => write!(
1415                f,
1416                "{}.{}.{}.{}/{}",
1417                (ip >> 24) & 0xFF,
1418                (ip >> 16) & 0xFF,
1419                (ip >> 8) & 0xFF,
1420                ip & 0xFF,
1421                prefix
1422            ),
1423            Value::Date(days) => write!(f, "{}", format_civil_date(*days)),
1424            Value::Time(ms) => {
1425                let total_secs = ms / 1000;
1426                write!(
1427                    f,
1428                    "{:02}:{:02}:{:02}",
1429                    total_secs / 3600,
1430                    (total_secs / 60) % 60,
1431                    total_secs % 60
1432                )
1433            }
1434            Value::Decimal(v) => write!(f, "{}", format_scaled_i64(*v, 4)),
1435            Value::DecimalText(v) => write!(f, "{v}"),
1436            Value::EnumValue(i) => write!(f, "enum({})", i),
1437            Value::Array(elems) => {
1438                write!(f, "[")?;
1439                for (i, elem) in elems.iter().enumerate() {
1440                    if i > 0 {
1441                        write!(f, ", ")?;
1442                    }
1443                    write!(f, "{}", elem)?;
1444                }
1445                write!(f, "]")
1446            }
1447            Value::TimestampMs(ms) => {
1448                let secs = ms / 1000;
1449                let millis = (ms % 1000).unsigned_abs() as u32;
1450                let days = (secs / 86400) as i32;
1451                let day_secs = (secs % 86400) as u32;
1452                let h = day_secs / 3600;
1453                let m = (day_secs / 60) % 60;
1454                let s = day_secs % 60;
1455                write!(
1456                    f,
1457                    "{}T{:02}:{:02}:{:02}.{:03}Z",
1458                    format_civil_date(days),
1459                    h,
1460                    m,
1461                    s,
1462                    millis
1463                )
1464            }
1465            Value::Ipv4(ip) => write!(
1466                f,
1467                "{}.{}.{}.{}",
1468                (ip >> 24) & 0xFF,
1469                (ip >> 16) & 0xFF,
1470                (ip >> 8) & 0xFF,
1471                ip & 0xFF
1472            ),
1473            Value::Ipv6(bytes) => {
1474                let addr = std::net::Ipv6Addr::from(*bytes);
1475                write!(f, "{}", addr)
1476            }
1477            Value::Subnet(ip, mask) => {
1478                let prefix = mask.leading_ones();
1479                if prefix < 32 && (*mask << prefix) == 0 || prefix == 32 {
1480                    write!(
1481                        f,
1482                        "{}.{}.{}.{}/{}",
1483                        (ip >> 24) & 0xFF,
1484                        (ip >> 16) & 0xFF,
1485                        (ip >> 8) & 0xFF,
1486                        ip & 0xFF,
1487                        prefix
1488                    )
1489                } else {
1490                    write!(
1491                        f,
1492                        "{}.{}.{}.{}/{}.{}.{}.{}",
1493                        (ip >> 24) & 0xFF,
1494                        (ip >> 16) & 0xFF,
1495                        (ip >> 8) & 0xFF,
1496                        ip & 0xFF,
1497                        (mask >> 24) & 0xFF,
1498                        (mask >> 16) & 0xFF,
1499                        (mask >> 8) & 0xFF,
1500                        mask & 0xFF
1501                    )
1502                }
1503            }
1504            Value::Port(p) => write!(f, "{}", p),
1505            Value::Latitude(micro) => write!(f, "{:.6}", *micro as f64 / 1_000_000.0),
1506            Value::Longitude(micro) => write!(f, "{:.6}", *micro as f64 / 1_000_000.0),
1507            Value::GeoPoint(lat, lon) => write!(
1508                f,
1509                "{:.6},{:.6}",
1510                *lat as f64 / 1_000_000.0,
1511                *lon as f64 / 1_000_000.0
1512            ),
1513            Value::Country2(c) => write!(f, "{}", String::from_utf8_lossy(c)),
1514            Value::Country3(c) => write!(f, "{}", String::from_utf8_lossy(c)),
1515            Value::Lang2(c) => write!(f, "{}", String::from_utf8_lossy(c)),
1516            Value::Lang5(c) => write!(f, "{}", String::from_utf8_lossy(c)),
1517            Value::Currency(c) => write!(f, "{}", String::from_utf8_lossy(c)),
1518            Value::AssetCode(code) => write!(f, "{}", code),
1519            Value::Money {
1520                asset_code,
1521                minor_units,
1522                scale,
1523            } => write!(
1524                f,
1525                "{} {}",
1526                asset_code,
1527                format_scaled_i64(*minor_units, *scale)
1528            ),
1529            Value::ColorAlpha([r, g, b, a]) => write!(f, "#{:02X}{:02X}{:02X}{:02X}", r, g, b, a),
1530            Value::BigInt(v) => write!(f, "{}", v),
1531            Value::KeyRef(c, k) => write!(f, "key_ref:{}:{}", c, k),
1532            Value::DocRef(c, id) => write!(f, "doc_ref:{}#{}", c, id),
1533            Value::TableRef(t) => write!(f, "table_ref:{}", t),
1534            Value::PageRef(p) => write!(f, "page_ref:{}", p),
1535            Value::Secret(_) => write!(f, "***"),
1536            Value::Password(_) => write!(f, "***"),
1537        }
1538    }
1539}
1540
1541/// Errors that can occur during value operations
1542#[derive(Debug, Clone, PartialEq)]
1543pub enum ValueError {
1544    /// No data to parse
1545    EmptyData,
1546    /// Invalid type byte
1547    InvalidType(u8),
1548    /// Data was truncated
1549    TruncatedData,
1550    /// Invalid UTF-8 in text
1551    InvalidUtf8,
1552    /// Invalid IP version byte
1553    InvalidIpVersion(u8),
1554    /// Varint overflow
1555    VarintOverflow,
1556    /// Input plus decoded allocations exceed the caller's read allowance.
1557    ReadLimitExceeded {
1558        required_bytes: usize,
1559        limit_bytes: usize,
1560    },
1561    /// Nested arrays exceed the same depth accepted by JSON input.
1562    NestingLimitExceeded { maximum: u32 },
1563    /// Type mismatch
1564    TypeMismatch { expected: DataType, found: DataType },
1565}
1566
1567impl fmt::Display for ValueError {
1568    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1569        match self {
1570            ValueError::EmptyData => write!(f, "empty data"),
1571            ValueError::InvalidType(t) => write!(f, "invalid type byte: {}", t),
1572            ValueError::TruncatedData => write!(f, "truncated data"),
1573            ValueError::InvalidUtf8 => write!(f, "invalid UTF-8"),
1574            ValueError::InvalidIpVersion(v) => write!(f, "invalid IP version: {}", v),
1575            ValueError::VarintOverflow => write!(f, "varint overflow"),
1576            ValueError::ReadLimitExceeded {
1577                required_bytes,
1578                limit_bytes,
1579            } => write!(
1580                f,
1581                "read needs {required_bytes} bytes over read memory limit {limit_bytes}"
1582            ),
1583            ValueError::NestingLimitExceeded { maximum } => {
1584                write!(f, "value nesting depth exceeds {maximum}")
1585            }
1586            ValueError::TypeMismatch { expected, found } => {
1587                write!(f, "type mismatch: expected {}, found {}", expected, found)
1588            }
1589        }
1590    }
1591}
1592
1593impl std::error::Error for ValueError {}
1594
1595/// Write a variable-length integer (LEB128 encoding)
1596pub(super) fn write_varint(buf: &mut Vec<u8>, mut value: u64) {
1597    loop {
1598        let mut byte = (value & 0x7F) as u8;
1599        value >>= 7;
1600        if value != 0 {
1601            byte |= 0x80;
1602        }
1603        buf.push(byte);
1604        if value == 0 {
1605            break;
1606        }
1607    }
1608}
1609
1610/// Read a variable-length integer (LEB128 encoding)
1611pub(super) fn read_varint(data: &[u8]) -> Result<(u64, usize), ValueError> {
1612    let mut result: u64 = 0;
1613    let mut shift = 0;
1614    let mut offset = 0;
1615
1616    loop {
1617        if offset >= data.len() {
1618            return Err(ValueError::TruncatedData);
1619        }
1620        let byte = data[offset];
1621        offset += 1;
1622
1623        if shift >= 64 {
1624            return Err(ValueError::VarintOverflow);
1625        }
1626
1627        result |= ((byte & 0x7F) as u64) << shift;
1628        shift += 7;
1629
1630        if byte & 0x80 == 0 {
1631            break;
1632        }
1633    }
1634
1635    Ok((result, offset))
1636}
1637
1638/// Convert days since Unix epoch back to YYYY-MM-DD (Howard Hinnant's civil_from_days)
1639fn format_civil_date(days: i32) -> String {
1640    let z = days as i64 + 719468;
1641    let era = (if z >= 0 { z } else { z - 146096 }) / 146097;
1642    let doe = (z - era * 146097) as u32;
1643    let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
1644    let y = yoe as i64 + era * 400;
1645    let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
1646    let mp = (5 * doy + 2) / 153;
1647    let d = doy - (153 * mp + 2) / 5 + 1;
1648    let m = if mp < 10 { mp + 3 } else { mp - 9 };
1649    let y = if m <= 2 { y + 1 } else { y };
1650    format!("{:04}-{:02}-{:02}", y, m, d)
1651}
1652
1653/// A row of values (tuple)
1654#[derive(Debug, Clone, PartialEq)]
1655pub struct Row {
1656    values: Vec<Value>,
1657}
1658
1659impl Row {
1660    /// Create a new row from values
1661    pub fn new(values: Vec<Value>) -> Self {
1662        Self { values }
1663    }
1664
1665    /// Get value at index
1666    pub fn get(&self, index: usize) -> Option<&Value> {
1667        self.values.get(index)
1668    }
1669
1670    /// Get number of columns
1671    pub fn len(&self) -> usize {
1672        self.values.len()
1673    }
1674
1675    /// Check if row is empty
1676    pub fn is_empty(&self) -> bool {
1677        self.values.is_empty()
1678    }
1679
1680    /// Iterate over values
1681    pub fn iter(&self) -> impl Iterator<Item = &Value> {
1682        self.values.iter()
1683    }
1684
1685    /// Get slice of all values
1686    pub fn values(&self) -> &[Value] {
1687        &self.values
1688    }
1689
1690    /// Convert to owned values
1691    pub fn into_values(self) -> Vec<Value> {
1692        self.values
1693    }
1694
1695    /// Serialize row to bytes
1696    pub fn to_bytes(&self) -> Vec<u8> {
1697        let mut buf = Vec::new();
1698
1699        // Number of columns
1700        write_varint(&mut buf, self.values.len() as u64);
1701
1702        // Each value
1703        for value in &self.values {
1704            let value_bytes = value.to_bytes();
1705            buf.extend_from_slice(&value_bytes);
1706        }
1707
1708        buf
1709    }
1710
1711    /// Deserialize row from bytes
1712    pub fn from_bytes(data: &[u8]) -> Result<(Self, usize), ValueError> {
1713        if data.is_empty() {
1714            return Err(ValueError::EmptyData);
1715        }
1716
1717        let (column_count, mut offset) = read_varint(data)?;
1718        let mut values = Vec::with_capacity(column_count as usize);
1719
1720        for _ in 0..column_count {
1721            let (value, size) = Value::from_bytes(&data[offset..])?;
1722            offset += size;
1723            values.push(value);
1724        }
1725
1726        Ok((Row { values }, offset))
1727    }
1728}
1729
1730impl From<Vec<Value>> for Row {
1731    fn from(values: Vec<Value>) -> Self {
1732        Row::new(values)
1733    }
1734}
1735
1736impl IntoIterator for Row {
1737    type Item = Value;
1738    type IntoIter = std::vec::IntoIter<Value>;
1739
1740    fn into_iter(self) -> Self::IntoIter {
1741        self.values.into_iter()
1742    }
1743}
1744
1745#[cfg(test)]
1746mod tests {
1747    use super::*;
1748    use proptest::prelude::*;
1749    use std::collections::hash_map::DefaultHasher;
1750    use std::hash::{Hash, Hasher};
1751    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
1752
1753    #[test]
1754    fn test_datatype_roundtrip() {
1755        let types = [
1756            DataType::Integer,
1757            DataType::UnsignedInteger,
1758            DataType::Float,
1759            DataType::Text,
1760            DataType::Blob,
1761            DataType::Boolean,
1762            DataType::Timestamp,
1763            DataType::Duration,
1764            DataType::IpAddr,
1765            DataType::MacAddr,
1766            DataType::Vector,
1767            DataType::Json,
1768            DataType::Uuid,
1769            DataType::Color,
1770            DataType::Email,
1771            DataType::Url,
1772            DataType::Phone,
1773            DataType::Semver,
1774            DataType::Cidr,
1775            DataType::Date,
1776            DataType::Time,
1777            DataType::Decimal,
1778            DataType::Enum,
1779            DataType::Array,
1780            DataType::TimestampMs,
1781            DataType::Ipv4,
1782            DataType::Ipv6,
1783            DataType::Subnet,
1784            DataType::Port,
1785            DataType::Latitude,
1786            DataType::Longitude,
1787            DataType::GeoPoint,
1788            DataType::Country2,
1789            DataType::Country3,
1790            DataType::Lang2,
1791            DataType::Lang5,
1792            DataType::Currency,
1793            DataType::ColorAlpha,
1794            DataType::BigInt,
1795        ];
1796
1797        for dt in types {
1798            let byte = dt.to_byte();
1799            let recovered = DataType::from_byte(byte).unwrap();
1800            assert_eq!(dt, recovered);
1801        }
1802    }
1803
1804    #[test]
1805    fn test_from_sql_name_uses_shared_alias_mapping() {
1806        assert_eq!(DataType::from_sql_name("INT8"), Some(DataType::BigInt));
1807        assert_eq!(DataType::from_sql_name("BIGINT"), Some(DataType::BigInt));
1808        assert_eq!(
1809            DataType::from_sql_name("TIMESTAMPTZ"),
1810            Some(DataType::TimestampMs)
1811        );
1812        assert_eq!(DataType::from_sql_name("ROWREF"), Some(DataType::RowRef));
1813    }
1814
1815    #[test]
1816    fn test_value_integer() {
1817        let value = Value::Integer(-12345);
1818        let bytes = value.to_bytes();
1819        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1820        assert_eq!(value, recovered);
1821        assert_eq!(size, bytes.len());
1822    }
1823
1824    #[test]
1825    fn test_value_text() {
1826        let value = Value::text("Hello, RedDB!".to_string());
1827        let bytes = value.to_bytes();
1828        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1829        assert_eq!(value, recovered);
1830        assert_eq!(size, bytes.len());
1831    }
1832
1833    #[test]
1834    fn plain_text_does_not_sql_quote_text() {
1835        assert_eq!(Value::text("alice").plain_text(), "alice");
1836        assert_eq!(
1837            Value::Array(vec![Value::text("alice"), Value::Integer(7)]).plain_text(),
1838            "[alice, 7]"
1839        );
1840    }
1841
1842    fn short_text() -> impl Strategy<Value = String> {
1843        "[a-zA-Z0-9_ ./:-]{0,16}".prop_map(|text| text)
1844    }
1845
1846    fn arb_value() -> impl Strategy<Value = Value> {
1847        let leaf = prop_oneof![
1848            Just(Value::Null),
1849            any::<i64>().prop_map(Value::Integer),
1850            any::<u64>().prop_map(Value::UnsignedInteger),
1851            any::<f64>().prop_map(Value::Float),
1852            short_text().prop_map(Value::text),
1853            proptest::collection::vec(any::<u8>(), 0..8).prop_map(Value::Blob),
1854            any::<bool>().prop_map(Value::Boolean),
1855            any::<i64>().prop_map(Value::Timestamp),
1856            any::<i64>().prop_map(Value::Duration),
1857            any::<u32>().prop_map(|ip| Value::IpAddr(IpAddr::V4(Ipv4Addr::from(ip)))),
1858            any::<[u8; 16]>().prop_map(|ip| Value::IpAddr(IpAddr::V6(Ipv6Addr::from(ip)))),
1859            any::<[u8; 6]>().prop_map(Value::MacAddr),
1860            proptest::collection::vec(any::<f32>(), 0..4).prop_map(Value::Vector),
1861            proptest::collection::vec(any::<u8>(), 0..8).prop_map(Value::Json),
1862            any::<[u8; 16]>().prop_map(Value::Uuid),
1863            short_text().prop_map(Value::NodeRef),
1864            short_text().prop_map(Value::EdgeRef),
1865            (short_text(), any::<u64>())
1866                .prop_map(|(collection, id)| Value::VectorRef(collection, id)),
1867            (short_text(), any::<u64>()).prop_map(|(table, id)| Value::RowRef(table, id)),
1868            any::<[u8; 3]>().prop_map(Value::Color),
1869            short_text().prop_map(Value::Email),
1870            short_text().prop_map(Value::Url),
1871            any::<u64>().prop_map(Value::Phone),
1872            any::<u32>().prop_map(Value::Semver),
1873            (any::<u32>(), 0u8..=32).prop_map(|(ip, prefix)| Value::Cidr(ip, prefix)),
1874            any::<i32>().prop_map(Value::Date),
1875            any::<u32>().prop_map(Value::Time),
1876            any::<i64>().prop_map(Value::Decimal),
1877            any::<u8>().prop_map(Value::EnumValue),
1878            any::<i64>().prop_map(Value::TimestampMs),
1879            any::<u32>().prop_map(Value::Ipv4),
1880            any::<[u8; 16]>().prop_map(Value::Ipv6),
1881            (any::<u32>(), any::<u32>()).prop_map(|(ip, mask)| Value::Subnet(ip, mask)),
1882            any::<u16>().prop_map(Value::Port),
1883            any::<i32>().prop_map(Value::Latitude),
1884            any::<i32>().prop_map(Value::Longitude),
1885            (any::<i32>(), any::<i32>()).prop_map(|(lat, lon)| Value::GeoPoint(lat, lon)),
1886            any::<[u8; 2]>().prop_map(Value::Country2),
1887            any::<[u8; 3]>().prop_map(Value::Country3),
1888            any::<[u8; 2]>().prop_map(Value::Lang2),
1889            any::<[u8; 5]>().prop_map(Value::Lang5),
1890            any::<[u8; 3]>().prop_map(Value::Currency),
1891            short_text().prop_map(Value::AssetCode),
1892            (short_text(), any::<i64>(), 0u8..=9).prop_map(|(asset_code, minor_units, scale)| {
1893                Value::Money {
1894                    asset_code,
1895                    minor_units,
1896                    scale,
1897                }
1898            }),
1899            any::<[u8; 4]>().prop_map(Value::ColorAlpha),
1900            any::<i64>().prop_map(Value::BigInt),
1901            (short_text(), short_text())
1902                .prop_map(|(collection, key)| Value::KeyRef(collection, key)),
1903            (short_text(), any::<u64>()).prop_map(|(collection, id)| Value::DocRef(collection, id)),
1904            short_text().prop_map(Value::TableRef),
1905            any::<u32>().prop_map(Value::PageRef),
1906            proptest::collection::vec(any::<u8>(), 0..8).prop_map(Value::Secret),
1907            short_text().prop_map(Value::Password),
1908        ];
1909
1910        leaf.prop_recursive(2, 16, 4, |inner| {
1911            proptest::collection::vec(inner, 0..4).prop_map(Value::Array)
1912        })
1913    }
1914
1915    proptest! {
1916        #[test]
1917        fn plain_text_is_deterministic_for_every_value_variant(value in arb_value()) {
1918            let first = value.plain_text();
1919            prop_assert_eq!(first, value.plain_text());
1920        }
1921    }
1922
1923    #[test]
1924    fn test_value_ipaddr_v4() {
1925        let value = Value::IpAddr(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
1926        let bytes = value.to_bytes();
1927        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1928        assert_eq!(value, recovered);
1929        assert_eq!(size, bytes.len());
1930    }
1931
1932    #[test]
1933    fn test_value_ipaddr_v6() {
1934        let value = Value::IpAddr(IpAddr::V6(Ipv6Addr::LOCALHOST));
1935        let bytes = value.to_bytes();
1936        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1937        assert_eq!(value, recovered);
1938        assert_eq!(size, bytes.len());
1939    }
1940
1941    #[test]
1942    fn test_value_vector() {
1943        let value = Value::Vector(vec![1.0, 2.0, 3.0, 4.5]);
1944        let bytes = value.to_bytes();
1945        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1946        assert_eq!(value, recovered);
1947        assert_eq!(size, bytes.len());
1948    }
1949
1950    #[test]
1951    fn test_value_mac_addr() {
1952        let value = Value::MacAddr([0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
1953        let bytes = value.to_bytes();
1954        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1955        assert_eq!(value, recovered);
1956        assert_eq!(size, bytes.len());
1957    }
1958
1959    #[test]
1960    fn test_value_uuid() {
1961        let uuid = [
1962            0x55, 0x04, 0x43, 0x01, 0x8f, 0x3b, 0x4a, 0x12, 0x9c, 0x5d, 0x6e, 0x7f, 0x80, 0x91,
1963            0xa2, 0xb3,
1964        ];
1965        let value = Value::Uuid(uuid);
1966        let bytes = value.to_bytes();
1967        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1968        assert_eq!(value, recovered);
1969        assert_eq!(size, bytes.len());
1970    }
1971
1972    #[test]
1973    fn test_value_null() {
1974        let value = Value::Null;
1975        let bytes = value.to_bytes();
1976        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1977        assert_eq!(value, recovered);
1978        assert_eq!(size, bytes.len());
1979    }
1980
1981    #[test]
1982    fn test_value_blob() {
1983        let value = Value::Blob(vec![0x00, 0x01, 0x02, 0x03, 0xFF]);
1984        let bytes = value.to_bytes();
1985        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1986        assert_eq!(value, recovered);
1987        assert_eq!(size, bytes.len());
1988    }
1989
1990    #[test]
1991    fn test_row_roundtrip() {
1992        let row = Row::new(vec![
1993            Value::Integer(42),
1994            Value::text("example.com".to_string()),
1995            Value::IpAddr(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))),
1996            Value::Boolean(true),
1997            Value::Null,
1998        ]);
1999
2000        let bytes = row.to_bytes();
2001        let (recovered, size) = Row::from_bytes(&bytes).unwrap();
2002
2003        assert_eq!(row, recovered);
2004        assert_eq!(size, bytes.len());
2005    }
2006
2007    #[test]
2008    fn test_varint_encoding() {
2009        let test_cases = [0u64, 1, 127, 128, 255, 256, 16383, 16384, u64::MAX];
2010
2011        for &value in &test_cases {
2012            let mut buf = Vec::new();
2013            write_varint(&mut buf, value);
2014            let (recovered, _) = read_varint(&buf).unwrap();
2015            assert_eq!(value, recovered, "Failed for value {}", value);
2016        }
2017    }
2018
2019    #[test]
2020    fn test_value_display() {
2021        assert_eq!(format!("{}", Value::Null), "NULL");
2022        assert_eq!(format!("{}", Value::Integer(42)), "42");
2023        assert_eq!(format!("{}", Value::Boolean(true)), "true");
2024        assert_eq!(format!("{}", Value::text("hello".to_string())), "'hello'");
2025    }
2026
2027    #[test]
2028    fn test_datatype_properties() {
2029        assert_eq!(DataType::Integer.fixed_size(), Some(8));
2030        assert_eq!(DataType::Text.fixed_size(), None);
2031        assert!(DataType::Integer.is_indexable());
2032        assert!(DataType::Text.is_indexable());
2033        assert!(!DataType::Blob.is_indexable());
2034        assert!(DataType::Integer.is_orderable());
2035        assert!(!DataType::Boolean.is_orderable());
2036    }
2037
2038    #[test]
2039    fn test_value_color_roundtrip() {
2040        let value = Value::Color([0xFF, 0x57, 0x33]);
2041        let bytes = value.to_bytes();
2042        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2043        assert_eq!(value, recovered);
2044        assert_eq!(size, bytes.len());
2045        assert_eq!(value.display_string(), "#FF5733");
2046    }
2047
2048    #[test]
2049    fn test_value_email_roundtrip() {
2050        let value = Value::Email("user@example.com".to_string());
2051        let bytes = value.to_bytes();
2052        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2053        assert_eq!(value, recovered);
2054        assert_eq!(size, bytes.len());
2055    }
2056
2057    #[test]
2058    fn test_value_url_roundtrip() {
2059        let value = Value::Url("https://example.com/path?q=1".to_string());
2060        let bytes = value.to_bytes();
2061        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2062        assert_eq!(value, recovered);
2063        assert_eq!(size, bytes.len());
2064    }
2065
2066    #[test]
2067    fn test_value_phone_roundtrip() {
2068        let value = Value::Phone(5511999887766);
2069        let bytes = value.to_bytes();
2070        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2071        assert_eq!(value, recovered);
2072        assert_eq!(size, bytes.len());
2073        assert_eq!(value.display_string(), "+5511999887766");
2074    }
2075
2076    #[test]
2077    fn test_value_semver_roundtrip() {
2078        let value = Value::Semver(1_000_000 + 23 * 1_000 + 456);
2079        let bytes = value.to_bytes();
2080        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2081        assert_eq!(value, recovered);
2082        assert_eq!(size, bytes.len());
2083        assert_eq!(value.display_string(), "1.23.456");
2084    }
2085
2086    #[test]
2087    fn test_value_cidr_roundtrip() {
2088        // 10.0.0.0/8
2089        let ip: u32 = 10 << 24;
2090        let value = Value::Cidr(ip, 8);
2091        let bytes = value.to_bytes();
2092        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2093        assert_eq!(value, recovered);
2094        assert_eq!(size, bytes.len());
2095        assert_eq!(value.display_string(), "10.0.0.0/8");
2096    }
2097
2098    #[test]
2099    fn test_value_date_roundtrip() {
2100        // 2024-01-15 -> some days since epoch
2101        let value = Value::Date(19738); // approximately 2024-01-15
2102        let bytes = value.to_bytes();
2103        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2104        assert_eq!(value, recovered);
2105        assert_eq!(size, bytes.len());
2106    }
2107
2108    #[test]
2109    fn test_value_time_roundtrip() {
2110        // 14:30:00 -> (14*3600 + 30*60) * 1000 = 52_200_000
2111        let value = Value::Time(52_200_000);
2112        let bytes = value.to_bytes();
2113        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2114        assert_eq!(value, recovered);
2115        assert_eq!(size, bytes.len());
2116        assert_eq!(value.display_string(), "14:30:00");
2117    }
2118
2119    #[test]
2120    fn test_value_decimal_roundtrip() {
2121        // 123.4567 * 10000 = 1234567
2122        let value = Value::Decimal(1_234_567);
2123        let bytes = value.to_bytes();
2124        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2125        assert_eq!(value, recovered);
2126        assert_eq!(size, bytes.len());
2127        assert_eq!(value.display_string(), "123.4567");
2128    }
2129
2130    #[test]
2131    fn test_value_enum_roundtrip() {
2132        let value = Value::EnumValue(3);
2133        let bytes = value.to_bytes();
2134        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2135        assert_eq!(value, recovered);
2136        assert_eq!(size, bytes.len());
2137        assert_eq!(value.display_string(), "enum(3)");
2138    }
2139
2140    #[test]
2141    fn test_value_array_roundtrip() {
2142        let value = Value::Array(vec![
2143            Value::Integer(1),
2144            Value::Integer(2),
2145            Value::Integer(3),
2146        ]);
2147        let bytes = value.to_bytes();
2148        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2149        assert_eq!(value, recovered);
2150        assert_eq!(size, bytes.len());
2151    }
2152
2153    #[test]
2154    fn test_value_array_nested_roundtrip() {
2155        let value = Value::Array(vec![
2156            Value::text("hello".to_string()),
2157            Value::text("world".to_string()),
2158        ]);
2159        let bytes = value.to_bytes();
2160        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2161        assert_eq!(value, recovered);
2162        assert_eq!(size, bytes.len());
2163    }
2164
2165    #[test]
2166    fn test_value_date_display_epoch() {
2167        // Day 0 = 1970-01-01
2168        let value = Value::Date(0);
2169        assert_eq!(value.display_string(), "1970-01-01");
2170    }
2171
2172    #[test]
2173    fn test_new_datatype_properties() {
2174        // Fixed sizes
2175        assert_eq!(DataType::Color.fixed_size(), Some(3));
2176        assert_eq!(DataType::Phone.fixed_size(), Some(8));
2177        assert_eq!(DataType::Semver.fixed_size(), Some(4));
2178        assert_eq!(DataType::Cidr.fixed_size(), Some(5));
2179        assert_eq!(DataType::Date.fixed_size(), Some(4));
2180        assert_eq!(DataType::Time.fixed_size(), Some(4));
2181        assert_eq!(DataType::Decimal.fixed_size(), Some(8));
2182        assert_eq!(DataType::Enum.fixed_size(), Some(1));
2183        assert_eq!(DataType::Email.fixed_size(), None);
2184        assert_eq!(DataType::Url.fixed_size(), None);
2185        assert_eq!(DataType::Array.fixed_size(), None);
2186
2187        // Indexable
2188        assert!(DataType::Email.is_indexable());
2189        assert!(DataType::Date.is_indexable());
2190        assert!(DataType::Decimal.is_indexable());
2191        assert!(!DataType::Color.is_indexable());
2192        assert!(!DataType::Array.is_indexable());
2193
2194        // Orderable
2195        assert!(DataType::Date.is_orderable());
2196        assert!(DataType::Time.is_orderable());
2197        assert!(DataType::Decimal.is_orderable());
2198        assert!(DataType::Semver.is_orderable());
2199        assert!(!DataType::Color.is_orderable());
2200        assert!(!DataType::Email.is_orderable());
2201    }
2202
2203    #[test]
2204    fn test_row_with_new_types() {
2205        let row = Row::new(vec![
2206            Value::Color([0xAA, 0xBB, 0xCC]),
2207            Value::Email("test@example.com".to_string()),
2208            Value::Phone(1234567890),
2209            Value::Semver(2_003_001), // 2.3.1
2210            Value::Date(19738),
2211            Value::EnumValue(1),
2212        ]);
2213
2214        let bytes = row.to_bytes();
2215        let (recovered, size) = Row::from_bytes(&bytes).unwrap();
2216        assert_eq!(row, recovered);
2217        assert_eq!(size, bytes.len());
2218    }
2219
2220    // --- New type serialization roundtrips ---
2221
2222    #[test]
2223    fn test_value_timestamp_ms_roundtrip() {
2224        let value = Value::TimestampMs(1710510600123);
2225        let bytes = value.to_bytes();
2226        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2227        assert_eq!(value, recovered);
2228        assert_eq!(size, bytes.len());
2229    }
2230
2231    #[test]
2232    fn test_value_ipv4_roundtrip() {
2233        let ip = (192u32 << 24) | (168 << 16) | (1 << 8) | 1;
2234        let value = Value::Ipv4(ip);
2235        let bytes = value.to_bytes();
2236        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2237        assert_eq!(value, recovered);
2238        assert_eq!(size, bytes.len());
2239        assert_eq!(value.display_string(), "192.168.1.1");
2240    }
2241
2242    #[test]
2243    fn test_value_ipv6_roundtrip() {
2244        let mut octets = [0u8; 16];
2245        octets[15] = 1;
2246        let value = Value::Ipv6(octets);
2247        let bytes = value.to_bytes();
2248        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2249        assert_eq!(value, recovered);
2250        assert_eq!(size, bytes.len());
2251        assert_eq!(value.display_string(), "::1");
2252    }
2253
2254    #[test]
2255    fn test_value_subnet_roundtrip() {
2256        let ip = 10u32 << 24;
2257        let mask = !0u32 << 16; // /16
2258        let value = Value::Subnet(ip, mask);
2259        let bytes = value.to_bytes();
2260        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2261        assert_eq!(value, recovered);
2262        assert_eq!(size, bytes.len());
2263        assert_eq!(value.display_string(), "10.0.0.0/16");
2264    }
2265
2266    #[test]
2267    fn test_value_port_roundtrip() {
2268        let value = Value::Port(8080);
2269        let bytes = value.to_bytes();
2270        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2271        assert_eq!(value, recovered);
2272        assert_eq!(size, bytes.len());
2273        assert_eq!(value.display_string(), "8080");
2274    }
2275
2276    #[test]
2277    fn test_value_latitude_roundtrip() {
2278        let value = Value::Latitude(-23550520);
2279        let bytes = value.to_bytes();
2280        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2281        assert_eq!(value, recovered);
2282        assert_eq!(size, bytes.len());
2283        assert_eq!(value.display_string(), "-23.550520");
2284    }
2285
2286    #[test]
2287    fn test_value_longitude_roundtrip() {
2288        let value = Value::Longitude(-46633308);
2289        let bytes = value.to_bytes();
2290        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2291        assert_eq!(value, recovered);
2292        assert_eq!(size, bytes.len());
2293        assert_eq!(value.display_string(), "-46.633308");
2294    }
2295
2296    #[test]
2297    fn test_value_geopoint_roundtrip() {
2298        let value = Value::GeoPoint(-23550520, -46633308);
2299        let bytes = value.to_bytes();
2300        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2301        assert_eq!(value, recovered);
2302        assert_eq!(size, bytes.len());
2303    }
2304
2305    #[test]
2306    fn test_value_country2_roundtrip() {
2307        let value = Value::Country2([b'B', b'R']);
2308        let bytes = value.to_bytes();
2309        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2310        assert_eq!(value, recovered);
2311        assert_eq!(size, bytes.len());
2312        assert_eq!(value.display_string(), "BR");
2313    }
2314
2315    #[test]
2316    fn test_value_country3_roundtrip() {
2317        let value = Value::Country3([b'B', b'R', b'A']);
2318        let bytes = value.to_bytes();
2319        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2320        assert_eq!(value, recovered);
2321        assert_eq!(size, bytes.len());
2322        assert_eq!(value.display_string(), "BRA");
2323    }
2324
2325    #[test]
2326    fn test_value_lang2_roundtrip() {
2327        let value = Value::Lang2([b'p', b't']);
2328        let bytes = value.to_bytes();
2329        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2330        assert_eq!(value, recovered);
2331        assert_eq!(size, bytes.len());
2332        assert_eq!(value.display_string(), "pt");
2333    }
2334
2335    #[test]
2336    fn test_value_lang5_roundtrip() {
2337        let value = Value::Lang5([b'p', b't', b'-', b'B', b'R']);
2338        let bytes = value.to_bytes();
2339        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2340        assert_eq!(value, recovered);
2341        assert_eq!(size, bytes.len());
2342        assert_eq!(value.display_string(), "pt-BR");
2343    }
2344
2345    #[test]
2346    fn test_value_currency_roundtrip() {
2347        let value = Value::Currency([b'U', b'S', b'D']);
2348        let bytes = value.to_bytes();
2349        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2350        assert_eq!(value, recovered);
2351        assert_eq!(size, bytes.len());
2352        assert_eq!(value.display_string(), "USD");
2353    }
2354
2355    #[test]
2356    fn test_value_color_alpha_roundtrip() {
2357        let value = Value::ColorAlpha([0xFF, 0x57, 0x33, 0x80]);
2358        let bytes = value.to_bytes();
2359        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2360        assert_eq!(value, recovered);
2361        assert_eq!(size, bytes.len());
2362        assert_eq!(value.display_string(), "#FF573380");
2363    }
2364
2365    #[test]
2366    fn test_value_bigint_roundtrip() {
2367        let value = Value::BigInt(i64::MAX);
2368        let bytes = value.to_bytes();
2369        let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2370        assert_eq!(value, recovered);
2371        assert_eq!(size, bytes.len());
2372    }
2373
2374    #[test]
2375    fn test_new_datatype_roundtrip() {
2376        let types = [
2377            DataType::TimestampMs,
2378            DataType::Ipv4,
2379            DataType::Ipv6,
2380            DataType::Subnet,
2381            DataType::Port,
2382            DataType::Latitude,
2383            DataType::Longitude,
2384            DataType::GeoPoint,
2385            DataType::Country2,
2386            DataType::Country3,
2387            DataType::Lang2,
2388            DataType::Lang5,
2389            DataType::Currency,
2390            DataType::ColorAlpha,
2391            DataType::BigInt,
2392        ];
2393
2394        for dt in types {
2395            let byte = dt.to_byte();
2396            let recovered = DataType::from_byte(byte).unwrap();
2397            assert_eq!(dt, recovered);
2398        }
2399    }
2400
2401    #[test]
2402    fn test_rich_type_datatype_properties() {
2403        // Fixed sizes
2404        assert_eq!(DataType::TimestampMs.fixed_size(), Some(8));
2405        assert_eq!(DataType::Ipv4.fixed_size(), Some(4));
2406        assert_eq!(DataType::Ipv6.fixed_size(), Some(16));
2407        assert_eq!(DataType::Subnet.fixed_size(), Some(8));
2408        assert_eq!(DataType::Port.fixed_size(), Some(2));
2409        assert_eq!(DataType::Latitude.fixed_size(), Some(4));
2410        assert_eq!(DataType::Longitude.fixed_size(), Some(4));
2411        assert_eq!(DataType::GeoPoint.fixed_size(), Some(8));
2412        assert_eq!(DataType::Country2.fixed_size(), Some(2));
2413        assert_eq!(DataType::Country3.fixed_size(), Some(3));
2414        assert_eq!(DataType::Lang2.fixed_size(), Some(2));
2415        assert_eq!(DataType::Lang5.fixed_size(), Some(5));
2416        assert_eq!(DataType::Currency.fixed_size(), Some(3));
2417        assert_eq!(DataType::ColorAlpha.fixed_size(), Some(4));
2418        assert_eq!(DataType::BigInt.fixed_size(), Some(8));
2419
2420        // Indexable
2421        assert!(DataType::TimestampMs.is_indexable());
2422        assert!(DataType::Ipv4.is_indexable());
2423        assert!(DataType::Ipv6.is_indexable());
2424        assert!(DataType::Port.is_indexable());
2425        assert!(DataType::Country2.is_indexable());
2426        assert!(DataType::Currency.is_indexable());
2427        assert!(DataType::BigInt.is_indexable());
2428        assert!(!DataType::Subnet.is_indexable());
2429        assert!(!DataType::ColorAlpha.is_indexable());
2430
2431        // Orderable
2432        assert!(DataType::TimestampMs.is_orderable());
2433        assert!(DataType::Port.is_orderable());
2434        assert!(DataType::Latitude.is_orderable());
2435        assert!(DataType::Longitude.is_orderable());
2436        assert!(DataType::BigInt.is_orderable());
2437        assert!(!DataType::Country2.is_orderable());
2438        assert!(!DataType::Ipv4.is_orderable());
2439    }
2440
2441    #[test]
2442    fn test_row_with_all_new_types() {
2443        let row = Row::new(vec![
2444            Value::TimestampMs(1710510600123),
2445            Value::Ipv4((10u32 << 24) | 1),
2446            Value::Ipv6([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]),
2447            Value::Subnet(10u32 << 24, !0u32 << 16),
2448            Value::Port(443),
2449            Value::Latitude(-23550520),
2450            Value::Longitude(-46633308),
2451            Value::GeoPoint(-23550520, -46633308),
2452            Value::Country2([b'B', b'R']),
2453            Value::Country3([b'B', b'R', b'A']),
2454            Value::Lang2([b'p', b't']),
2455            Value::Lang5([b'p', b't', b'-', b'B', b'R']),
2456            Value::Currency([b'U', b'S', b'D']),
2457            Value::ColorAlpha([0xFF, 0x57, 0x33, 0x80]),
2458            Value::BigInt(i64::MAX),
2459        ]);
2460
2461        let bytes = row.to_bytes();
2462        let (recovered, size) = Row::from_bytes(&bytes).unwrap();
2463        assert_eq!(row, recovered);
2464        assert_eq!(size, bytes.len());
2465    }
2466
2467    #[test]
2468    fn sql_type_name_parses_modifiers_and_helpers() {
2469        let decimal = SqlTypeName::parse_declared("decimal(10, 2)");
2470        assert_eq!(decimal.base_name(), "DECIMAL");
2471        assert_eq!(decimal.decimal_precision(), Some(10));
2472        assert_eq!(decimal.to_string(), "DECIMAL(10,2)");
2473
2474        let enum_type = SqlTypeName::parse_declared("enum('red','blue')");
2475        assert_eq!(
2476            enum_type.enum_variants(),
2477            Some(vec!["red".to_string(), "blue".to_string()])
2478        );
2479
2480        let bad_enum = SqlTypeName::new("enum").with_modifiers(vec![TypeModifier::Number(1)]);
2481        assert_eq!(bad_enum.enum_variants(), None);
2482        assert_eq!(SqlTypeName::new("text").enum_variants(), None);
2483
2484        let array = SqlTypeName::parse_declared("array(varchar(12))");
2485        assert_eq!(array.array_element_type(), Some("VARCHAR(12)".to_string()));
2486        let array_ident =
2487            SqlTypeName::new("array").with_modifiers(vec![TypeModifier::Ident("int".to_string())]);
2488        assert_eq!(array_ident.array_element_type(), Some("INT".to_string()));
2489        assert_eq!(SqlTypeName::new("text").array_element_type(), None);
2490        assert_eq!(SqlTypeName::new("text").decimal_precision(), None);
2491
2492        assert_eq!(
2493            SqlTypeName::parse_declared("enum('a,b', array(int))").to_string(),
2494            "ENUM('a,b',ARRAY(INT))"
2495        );
2496        assert_eq!(SqlTypeName::parse_declared("").to_string(), "");
2497    }
2498
2499    #[test]
2500    fn datatype_category_preference_display_and_storage_traits_cover_all_variants() {
2501        let cases = [
2502            (
2503                DataType::Unknown,
2504                TypeCategory::Unknown,
2505                "UNKNOWN",
2506                None,
2507                false,
2508                false,
2509            ),
2510            (
2511                DataType::Integer,
2512                TypeCategory::Numeric,
2513                "INTEGER",
2514                Some(8),
2515                true,
2516                true,
2517            ),
2518            (
2519                DataType::UnsignedInteger,
2520                TypeCategory::Numeric,
2521                "UNSIGNED INTEGER",
2522                Some(8),
2523                true,
2524                true,
2525            ),
2526            (
2527                DataType::Float,
2528                TypeCategory::Numeric,
2529                "FLOAT",
2530                Some(8),
2531                true,
2532                true,
2533            ),
2534            (
2535                DataType::Text,
2536                TypeCategory::String,
2537                "TEXT",
2538                None,
2539                true,
2540                true,
2541            ),
2542            (
2543                DataType::Blob,
2544                TypeCategory::String,
2545                "BLOB",
2546                None,
2547                false,
2548                false,
2549            ),
2550            (
2551                DataType::Boolean,
2552                TypeCategory::Boolean,
2553                "BOOLEAN",
2554                Some(1),
2555                false,
2556                false,
2557            ),
2558            (
2559                DataType::Timestamp,
2560                TypeCategory::DateTime,
2561                "TIMESTAMP",
2562                Some(8),
2563                true,
2564                true,
2565            ),
2566            (
2567                DataType::Duration,
2568                TypeCategory::TimeSpan,
2569                "DURATION",
2570                Some(8),
2571                false,
2572                true,
2573            ),
2574            (
2575                DataType::IpAddr,
2576                TypeCategory::Network,
2577                "IPADDR",
2578                None,
2579                true,
2580                false,
2581            ),
2582            (
2583                DataType::MacAddr,
2584                TypeCategory::Network,
2585                "MACADDR",
2586                Some(6),
2587                false,
2588                false,
2589            ),
2590            (
2591                DataType::Vector,
2592                TypeCategory::Vector,
2593                "VECTOR",
2594                None,
2595                false,
2596                false,
2597            ),
2598            (
2599                DataType::Nullable,
2600                TypeCategory::Unknown,
2601                "NULLABLE",
2602                None,
2603                false,
2604                false,
2605            ),
2606            (
2607                DataType::Json,
2608                TypeCategory::Json,
2609                "JSON",
2610                None,
2611                false,
2612                false,
2613            ),
2614            (
2615                DataType::Uuid,
2616                TypeCategory::Uuid,
2617                "UUID",
2618                Some(16),
2619                true,
2620                false,
2621            ),
2622            (
2623                DataType::NodeRef,
2624                TypeCategory::Reference,
2625                "NODEREF",
2626                None,
2627                true,
2628                false,
2629            ),
2630            (
2631                DataType::EdgeRef,
2632                TypeCategory::Reference,
2633                "EDGEREF",
2634                None,
2635                true,
2636                false,
2637            ),
2638            (
2639                DataType::VectorRef,
2640                TypeCategory::Reference,
2641                "VECTORREF",
2642                Some(8),
2643                true,
2644                false,
2645            ),
2646            (
2647                DataType::RowRef,
2648                TypeCategory::Reference,
2649                "ROWREF",
2650                None,
2651                true,
2652                false,
2653            ),
2654            (
2655                DataType::Color,
2656                TypeCategory::Domain,
2657                "COLOR",
2658                Some(3),
2659                false,
2660                false,
2661            ),
2662            (
2663                DataType::Email,
2664                TypeCategory::Domain,
2665                "EMAIL",
2666                None,
2667                true,
2668                false,
2669            ),
2670            (
2671                DataType::Url,
2672                TypeCategory::Domain,
2673                "URL",
2674                None,
2675                true,
2676                false,
2677            ),
2678            (
2679                DataType::Phone,
2680                TypeCategory::Domain,
2681                "PHONE",
2682                Some(8),
2683                true,
2684                false,
2685            ),
2686            (
2687                DataType::Semver,
2688                TypeCategory::Domain,
2689                "SEMVER",
2690                Some(4),
2691                true,
2692                true,
2693            ),
2694            (
2695                DataType::Cidr,
2696                TypeCategory::Network,
2697                "CIDR",
2698                Some(5),
2699                false,
2700                false,
2701            ),
2702            (
2703                DataType::Date,
2704                TypeCategory::DateTime,
2705                "DATE",
2706                Some(4),
2707                true,
2708                true,
2709            ),
2710            (
2711                DataType::Time,
2712                TypeCategory::DateTime,
2713                "TIME",
2714                Some(4),
2715                true,
2716                true,
2717            ),
2718            (
2719                DataType::Decimal,
2720                TypeCategory::Numeric,
2721                "DECIMAL",
2722                Some(8),
2723                true,
2724                true,
2725            ),
2726            (
2727                DataType::Enum,
2728                TypeCategory::Domain,
2729                "ENUM",
2730                Some(1),
2731                true,
2732                false,
2733            ),
2734            (
2735                DataType::Array,
2736                TypeCategory::Array,
2737                "ARRAY",
2738                None,
2739                false,
2740                false,
2741            ),
2742            (
2743                DataType::TimestampMs,
2744                TypeCategory::DateTime,
2745                "TIMESTAMP_MS",
2746                Some(8),
2747                true,
2748                true,
2749            ),
2750            (
2751                DataType::Ipv4,
2752                TypeCategory::Network,
2753                "IPV4",
2754                Some(4),
2755                true,
2756                false,
2757            ),
2758            (
2759                DataType::Ipv6,
2760                TypeCategory::Network,
2761                "IPV6",
2762                Some(16),
2763                true,
2764                false,
2765            ),
2766            (
2767                DataType::Subnet,
2768                TypeCategory::Network,
2769                "SUBNET",
2770                Some(8),
2771                false,
2772                false,
2773            ),
2774            (
2775                DataType::Port,
2776                TypeCategory::Numeric,
2777                "PORT",
2778                Some(2),
2779                true,
2780                true,
2781            ),
2782            (
2783                DataType::Latitude,
2784                TypeCategory::Numeric,
2785                "LATITUDE",
2786                Some(4),
2787                true,
2788                true,
2789            ),
2790            (
2791                DataType::Longitude,
2792                TypeCategory::Numeric,
2793                "LONGITUDE",
2794                Some(4),
2795                true,
2796                true,
2797            ),
2798            (
2799                DataType::GeoPoint,
2800                TypeCategory::Geo,
2801                "GEOPOINT",
2802                Some(8),
2803                true,
2804                false,
2805            ),
2806            (
2807                DataType::Country2,
2808                TypeCategory::Domain,
2809                "COUNTRY2",
2810                Some(2),
2811                true,
2812                false,
2813            ),
2814            (
2815                DataType::Country3,
2816                TypeCategory::Domain,
2817                "COUNTRY3",
2818                Some(3),
2819                true,
2820                false,
2821            ),
2822            (
2823                DataType::Lang2,
2824                TypeCategory::Domain,
2825                "LANG2",
2826                Some(2),
2827                true,
2828                false,
2829            ),
2830            (
2831                DataType::Lang5,
2832                TypeCategory::Domain,
2833                "LANG5",
2834                Some(5),
2835                true,
2836                false,
2837            ),
2838            (
2839                DataType::Currency,
2840                TypeCategory::Domain,
2841                "CURRENCY",
2842                Some(3),
2843                true,
2844                false,
2845            ),
2846            (
2847                DataType::ColorAlpha,
2848                TypeCategory::Domain,
2849                "COLOR_ALPHA",
2850                Some(4),
2851                false,
2852                false,
2853            ),
2854            (
2855                DataType::BigInt,
2856                TypeCategory::Numeric,
2857                "BIGINT",
2858                Some(8),
2859                true,
2860                true,
2861            ),
2862            (
2863                DataType::KeyRef,
2864                TypeCategory::Reference,
2865                "KEY_REF",
2866                None,
2867                true,
2868                false,
2869            ),
2870            (
2871                DataType::DocRef,
2872                TypeCategory::Reference,
2873                "DOC_REF",
2874                None,
2875                true,
2876                false,
2877            ),
2878            (
2879                DataType::TableRef,
2880                TypeCategory::Reference,
2881                "TABLE_REF",
2882                None,
2883                true,
2884                false,
2885            ),
2886            (
2887                DataType::PageRef,
2888                TypeCategory::Reference,
2889                "PAGE_REF",
2890                Some(4),
2891                true,
2892                false,
2893            ),
2894            (
2895                DataType::Secret,
2896                TypeCategory::Opaque,
2897                "SECRET",
2898                None,
2899                false,
2900                false,
2901            ),
2902            (
2903                DataType::Password,
2904                TypeCategory::Opaque,
2905                "PASSWORD",
2906                None,
2907                false,
2908                false,
2909            ),
2910            (
2911                DataType::TextZstd,
2912                TypeCategory::String,
2913                "TEXT",
2914                None,
2915                false,
2916                false,
2917            ),
2918            (
2919                DataType::BlobZstd,
2920                TypeCategory::String,
2921                "BLOB",
2922                None,
2923                false,
2924                false,
2925            ),
2926            (
2927                DataType::AssetCode,
2928                TypeCategory::Domain,
2929                "ASSET_CODE",
2930                None,
2931                true,
2932                true,
2933            ),
2934            (
2935                DataType::Money,
2936                TypeCategory::Domain,
2937                "MONEY",
2938                None,
2939                false,
2940                false,
2941            ),
2942            (
2943                DataType::DecimalText,
2944                TypeCategory::Numeric,
2945                "DECIMAL_TEXT",
2946                None,
2947                true,
2948                true,
2949            ),
2950        ];
2951
2952        for (data_type, category, display, fixed, indexable, orderable) in cases {
2953            assert_eq!(data_type.category(), category, "{data_type:?}");
2954            assert_eq!(data_type.to_string(), display);
2955            assert_eq!(data_type.fixed_size(), fixed, "{data_type:?}");
2956            assert_eq!(data_type.is_indexable(), indexable, "{data_type:?}");
2957            assert_eq!(data_type.is_orderable(), orderable, "{data_type:?}");
2958        }
2959
2960        for preferred in [
2961            DataType::Float,
2962            DataType::Text,
2963            DataType::TimestampMs,
2964            DataType::IpAddr,
2965            DataType::Boolean,
2966            DataType::Uuid,
2967        ] {
2968            assert!(preferred.is_preferred(), "{preferred:?}");
2969        }
2970        assert!(!DataType::Integer.is_preferred());
2971    }
2972
2973    #[test]
2974    fn sql_aliases_cover_domain_reference_and_unknown_paths() {
2975        let aliases = [
2976            ("BOOL", DataType::Boolean),
2977            ("SMALLINT", DataType::Integer),
2978            ("BIGSERIAL", DataType::BigInt),
2979            ("UNSIGNED INTEGER", DataType::UnsignedInteger),
2980            ("DOUBLE", DataType::Float),
2981            ("VARCHAR(20)", DataType::Text),
2982            ("BYTEA", DataType::Blob),
2983            ("TIMESTAMP_MS", DataType::TimestampMs),
2984            ("INTERVAL", DataType::Duration),
2985            ("NUMERIC(10,2)", DataType::Decimal),
2986            ("JSONB", DataType::Json),
2987            ("INET", DataType::IpAddr),
2988            ("COLOR_ALPHA", DataType::ColorAlpha),
2989            ("GEO_POINT", DataType::GeoPoint),
2990            ("ASSET_CODE", DataType::AssetCode),
2991            ("DECIMAL_TEXT", DataType::DecimalText),
2992            ("KEYREF", DataType::KeyRef),
2993            ("DOCREF", DataType::DocRef),
2994            ("TABLEREF", DataType::TableRef),
2995            ("PAGEREF", DataType::PageRef),
2996            ("PASSWORD", DataType::Password),
2997            ("VECTOR", DataType::Vector),
2998        ];
2999
3000        for (alias, expected) in aliases {
3001            assert_eq!(DataType::from_sql_name(alias), Some(expected), "{alias}");
3002        }
3003        assert_eq!(DataType::from_sql_name("definitely_not_a_type"), None);
3004        assert_eq!(DataType::from_byte(0), None);
3005        assert_eq!(DataType::from_byte(56), None);
3006    }
3007
3008    #[test]
3009    fn value_accessors_hash_and_display_cover_remaining_variants() {
3010        let values = vec![
3011            Value::Null,
3012            Value::Integer(-1),
3013            Value::UnsignedInteger(2),
3014            Value::Float(3.5),
3015            Value::text("hello"),
3016            Value::Blob(vec![1, 2, 3]),
3017            Value::Boolean(true),
3018            Value::Timestamp(4),
3019            Value::Duration(5),
3020            Value::IpAddr(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
3021            Value::IpAddr(IpAddr::V6(Ipv6Addr::LOCALHOST)),
3022            Value::MacAddr([1, 2, 3, 4, 5, 6]),
3023            Value::Vector(vec![1.0, 2.0]),
3024            Value::Json(br#"{"ok":true}"#.to_vec()),
3025            Value::Uuid([7; 16]),
3026            Value::NodeRef("node".to_string()),
3027            Value::EdgeRef("edge".to_string()),
3028            Value::VectorRef("vectors".to_string(), 8),
3029            Value::RowRef("rows".to_string(), 9),
3030            Value::Color([0xAA, 0xBB, 0xCC]),
3031            Value::Email("a@example.com".to_string()),
3032            Value::Url("https://example.com".to_string()),
3033            Value::Phone(5511999),
3034            Value::Semver(1_002_003),
3035            Value::Cidr(10 << 24, 8),
3036            Value::Date(20_000),
3037            Value::Time(43_200_000),
3038            Value::Decimal(123_456),
3039            Value::DecimalText("3.14159265358979323846".to_string()),
3040            Value::EnumValue(3),
3041            Value::Array(vec![Value::Integer(1), Value::text("two")]),
3042            Value::TimestampMs(123_456),
3043            Value::Ipv4(0x7f000001),
3044            Value::Ipv6([1; 16]),
3045            Value::Subnet(10 << 24, 0xff00ff00),
3046            Value::Port(5432),
3047            Value::Latitude(-23_550_520),
3048            Value::Longitude(-46_633_308),
3049            Value::GeoPoint(-23_550_520, -46_633_308),
3050            Value::Country2(*b"BR"),
3051            Value::Country3(*b"BRA"),
3052            Value::Lang2(*b"pt"),
3053            Value::Lang5(*b"pt-BR"),
3054            Value::Currency(*b"USD"),
3055            Value::AssetCode("BTC".to_string()),
3056            Value::Money {
3057                asset_code: "USD".to_string(),
3058                minor_units: -1234,
3059                scale: 2,
3060            },
3061            Value::ColorAlpha([1, 2, 3, 4]),
3062            Value::BigInt(-10),
3063            Value::KeyRef("kv".to_string(), "key".to_string()),
3064            Value::DocRef("docs".to_string(), 42),
3065            Value::TableRef("users".to_string()),
3066            Value::PageRef(99),
3067            Value::Secret(vec![9, 8, 7]),
3068            Value::Password("$argon2id$v=19$hash".to_string()),
3069        ];
3070
3071        for value in &values {
3072            let mut hasher = DefaultHasher::new();
3073            value.hash(&mut hasher);
3074            let _ = hasher.finish();
3075            assert_eq!(value.data_type(), value.data_type());
3076            assert!(!value.display_string().is_empty());
3077            assert!(!value.plain_text().is_empty());
3078        }
3079
3080        assert!(Value::Null.is_null());
3081        assert_eq!(Value::Integer(-1).as_integer(), Some(-1));
3082        assert_eq!(
3083            Value::UnsignedInteger(i64::MAX as u64).as_integer(),
3084            Some(i64::MAX)
3085        );
3086        assert_eq!(
3087            Value::UnsignedInteger(i64::MAX as u64 + 1).as_integer(),
3088            None
3089        );
3090        assert_eq!(Value::Timestamp(1).as_integer(), Some(1));
3091        assert_eq!(Value::Duration(2).as_integer(), Some(2));
3092        assert_eq!(Value::Float(1.5).as_float(), Some(1.5));
3093        assert_eq!(Value::Integer(2).as_float(), Some(2.0));
3094        assert_eq!(Value::UnsignedInteger(3).as_float(), Some(3.0));
3095        assert_eq!(Value::text("x").as_text(), Some("x"));
3096        assert_eq!(Value::Boolean(true).as_boolean(), Some(true));
3097        assert_eq!(
3098            Value::IpAddr(IpAddr::V4(Ipv4Addr::LOCALHOST)).as_ip_addr(),
3099            Some(IpAddr::V4(Ipv4Addr::LOCALHOST))
3100        );
3101        assert_eq!(Value::Vector(vec![1.0]).as_vector(), Some(&[1.0][..]));
3102        assert_eq!(Value::Null.as_integer(), None);
3103        assert_eq!(Value::Null.as_float(), None);
3104        assert_eq!(Value::Null.as_text(), None);
3105        assert_eq!(Value::Null.as_boolean(), None);
3106        assert_eq!(Value::Null.as_ip_addr(), None);
3107        assert_eq!(Value::Null.as_vector(), None);
3108    }
3109
3110    #[test]
3111    fn row_accessors_iteration_and_error_paths() {
3112        let empty = Row::new(Vec::new());
3113        assert!(empty.is_empty());
3114        assert_eq!(empty.len(), 0);
3115        assert_eq!(empty.get(0), None);
3116
3117        let row = Row::from(vec![Value::Integer(1), Value::text("two")]);
3118        assert_eq!(row.len(), 2);
3119        assert_eq!(row.get(1), Some(&Value::text("two")));
3120        assert_eq!(row.values().len(), 2);
3121        assert_eq!(row.iter().count(), 2);
3122        assert_eq!(row.clone().into_values().len(), 2);
3123        assert_eq!(row.into_iter().count(), 2);
3124
3125        assert_eq!(Row::from_bytes(&[]).unwrap_err(), ValueError::EmptyData);
3126        assert_eq!(
3127            Row::from_bytes(&[0x80]).unwrap_err(),
3128            ValueError::TruncatedData
3129        );
3130    }
3131
3132    #[test]
3133    fn value_error_display_covers_every_variant() {
3134        let errors = [
3135            (ValueError::EmptyData, "empty data"),
3136            (ValueError::InvalidType(99), "invalid type byte: 99"),
3137            (ValueError::TruncatedData, "truncated data"),
3138            (ValueError::InvalidUtf8, "invalid UTF-8"),
3139            (ValueError::InvalidIpVersion(5), "invalid IP version: 5"),
3140            (ValueError::VarintOverflow, "varint overflow"),
3141            (
3142                ValueError::TypeMismatch {
3143                    expected: DataType::Text,
3144                    found: DataType::Integer,
3145                },
3146                "type mismatch: expected TEXT, found INTEGER",
3147            ),
3148        ];
3149
3150        for (error, message) in errors {
3151            assert_eq!(error.to_string(), message);
3152        }
3153    }
3154}