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