1use std::fmt;
12use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
13
14#[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#[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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
198#[repr(u8)]
199pub enum DataType {
200 Unknown = 0,
205 Integer = 1,
207 UnsignedInteger = 2,
209 Float = 3,
211 Text = 4,
213 Blob = 5,
215 Boolean = 6,
217 Timestamp = 7,
219 Duration = 8,
221 IpAddr = 9,
223 MacAddr = 10,
225 Vector = 11,
227 Nullable = 12,
229 Json = 13,
231 Uuid = 14,
233 NodeRef = 15,
235 EdgeRef = 16,
237 VectorRef = 17,
239 RowRef = 18,
241 Color = 19,
243 Email = 20,
245 Url = 21,
247 Phone = 22,
249 Semver = 23,
251 Cidr = 24,
253 Date = 25,
255 Time = 26,
257 Decimal = 27,
259 Enum = 28,
261 Array = 29,
263 TimestampMs = 30,
265 Ipv4 = 31,
267 Ipv6 = 32,
269 Subnet = 33,
271 Port = 34,
273 Latitude = 35,
275 Longitude = 36,
277 GeoPoint = 37,
279 Country2 = 38,
281 Country3 = 39,
283 Lang2 = 40,
285 Lang5 = 41,
287 Currency = 42,
289 ColorAlpha = 43,
291 BigInt = 44,
293 KeyRef = 45,
295 DocRef = 46,
297 TableRef = 47,
299 PageRef = 48,
301 Secret = 49,
303 Password = 50,
305 TextZstd = 51,
309 BlobZstd = 52,
312 AssetCode = 53,
314 Money = 54,
316 DecimalText = 55,
318}
319
320#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
332pub enum TypeCategory {
333 Numeric,
335 String,
338 Boolean,
340 DateTime,
343 TimeSpan,
345 Array,
349 Network,
351 Geo,
353 Domain,
358 Uuid,
361 Opaque,
365 Reference,
368 Vector,
370 Json,
372 Unknown,
375}
376
377impl DataType {
378 pub fn category(&self) -> TypeCategory {
382 match self {
383 DataType::Integer
384 | DataType::UnsignedInteger
385 | DataType::Float
386 | DataType::Decimal
387 | DataType::DecimalText
388 | DataType::BigInt
389 | DataType::Port
390 | DataType::Latitude
391 | DataType::Longitude => TypeCategory::Numeric,
392 DataType::Text | DataType::Blob => TypeCategory::String,
393 DataType::Boolean => TypeCategory::Boolean,
394 DataType::Timestamp | DataType::TimestampMs | DataType::Date | DataType::Time => {
395 TypeCategory::DateTime
396 }
397 DataType::Duration => TypeCategory::TimeSpan,
398 DataType::Array => TypeCategory::Array,
399 DataType::IpAddr
400 | DataType::Ipv4
401 | DataType::Ipv6
402 | DataType::Cidr
403 | DataType::Subnet
404 | DataType::MacAddr => TypeCategory::Network,
405 DataType::GeoPoint => TypeCategory::Geo,
406 DataType::Email
407 | DataType::Url
408 | DataType::Phone
409 | DataType::Semver
410 | DataType::Color
411 | DataType::ColorAlpha
412 | DataType::Country2
413 | DataType::Country3
414 | DataType::Lang2
415 | DataType::Lang5
416 | DataType::Currency
417 | DataType::AssetCode
418 | DataType::Money
419 | DataType::Enum => TypeCategory::Domain,
420 DataType::Uuid => TypeCategory::Uuid,
421 DataType::Secret | DataType::Password => TypeCategory::Opaque,
422 DataType::NodeRef
423 | DataType::EdgeRef
424 | DataType::VectorRef
425 | DataType::RowRef
426 | DataType::KeyRef
427 | DataType::DocRef
428 | DataType::TableRef
429 | DataType::PageRef => TypeCategory::Reference,
430 DataType::Vector => TypeCategory::Vector,
431 DataType::Json => TypeCategory::Json,
432 DataType::Nullable | DataType::Unknown => TypeCategory::Unknown,
433 DataType::TextZstd => TypeCategory::String,
437 DataType::BlobZstd => TypeCategory::String,
438 }
439 }
440
441 pub fn is_preferred(&self) -> bool {
455 matches!(
456 self,
457 DataType::Float
458 | DataType::Text
459 | DataType::TimestampMs
460 | DataType::IpAddr
461 | DataType::Boolean
462 | DataType::Uuid
463 )
464 }
465}
466
467impl DataType {
468 pub fn to_byte(&self) -> u8 {
470 *self as u8
471 }
472
473 pub fn from_byte(b: u8) -> Option<Self> {
475 match b {
476 1 => Some(DataType::Integer),
477 2 => Some(DataType::UnsignedInteger),
478 3 => Some(DataType::Float),
479 4 => Some(DataType::Text),
480 5 => Some(DataType::Blob),
481 6 => Some(DataType::Boolean),
482 7 => Some(DataType::Timestamp),
483 8 => Some(DataType::Duration),
484 9 => Some(DataType::IpAddr),
485 10 => Some(DataType::MacAddr),
486 11 => Some(DataType::Vector),
487 12 => Some(DataType::Nullable),
488 13 => Some(DataType::Json),
489 14 => Some(DataType::Uuid),
490 15 => Some(DataType::NodeRef),
491 16 => Some(DataType::EdgeRef),
492 17 => Some(DataType::VectorRef),
493 18 => Some(DataType::RowRef),
494 19 => Some(DataType::Color),
495 20 => Some(DataType::Email),
496 21 => Some(DataType::Url),
497 22 => Some(DataType::Phone),
498 23 => Some(DataType::Semver),
499 24 => Some(DataType::Cidr),
500 25 => Some(DataType::Date),
501 26 => Some(DataType::Time),
502 27 => Some(DataType::Decimal),
503 28 => Some(DataType::Enum),
504 29 => Some(DataType::Array),
505 30 => Some(DataType::TimestampMs),
506 31 => Some(DataType::Ipv4),
507 32 => Some(DataType::Ipv6),
508 33 => Some(DataType::Subnet),
509 34 => Some(DataType::Port),
510 35 => Some(DataType::Latitude),
511 36 => Some(DataType::Longitude),
512 37 => Some(DataType::GeoPoint),
513 38 => Some(DataType::Country2),
514 39 => Some(DataType::Country3),
515 40 => Some(DataType::Lang2),
516 41 => Some(DataType::Lang5),
517 42 => Some(DataType::Currency),
518 43 => Some(DataType::ColorAlpha),
519 44 => Some(DataType::BigInt),
520 45 => Some(DataType::KeyRef),
521 46 => Some(DataType::DocRef),
522 47 => Some(DataType::TableRef),
523 48 => Some(DataType::PageRef),
524 49 => Some(DataType::Secret),
525 50 => Some(DataType::Password),
526 51 => Some(DataType::TextZstd),
527 52 => Some(DataType::BlobZstd),
528 53 => Some(DataType::AssetCode),
529 54 => Some(DataType::Money),
530 55 => Some(DataType::DecimalText),
531 _ => None,
532 }
533 }
534
535 pub fn from_sql_name(name: &str) -> Option<Self> {
540 Self::from_sql_type_name(&SqlTypeName::parse_declared(name))
541 }
542
543 pub fn from_sql_type_name(sql_type: &SqlTypeName) -> Option<Self> {
545 let n = sql_type.base_name();
546 Some(match n.as_str() {
547 "BOOL" | "BOOLEAN" => DataType::Boolean,
548 "INT" | "INTEGER" | "INT4" | "SERIAL" => DataType::Integer,
549 "INT2" | "SMALLINT" => DataType::Integer,
550 "INT8" | "BIGINT" | "BIGINT_SIGNED" | "BIGSERIAL" => DataType::BigInt,
551 "UINT" | "UNSIGNED" | "UNSIGNED_INTEGER" | "UNSIGNED INTEGER" => {
552 DataType::UnsignedInteger
553 }
554 "FLOAT" | "DOUBLE" | "REAL" | "FLOAT8" => DataType::Float,
555 "TEXT" | "STRING" | "VARCHAR" | "CHAR" => DataType::Text,
556 "BLOB" | "BYTES" | "BYTEA" => DataType::Blob,
557 "TIMESTAMP" => DataType::Timestamp,
558 "TIMESTAMPTZ" | "TIMESTAMPMS" | "TIMESTAMP_MS" => DataType::TimestampMs,
559 "DURATION" | "INTERVAL" => DataType::Duration,
560 "DATE" => DataType::Date,
561 "TIME" => DataType::Time,
562 "DECIMAL" | "NUMERIC" => DataType::Decimal,
563 "JSON" | "JSONB" => DataType::Json,
564 "UUID" => DataType::Uuid,
565 "IPADDR" | "IP" | "INET" => DataType::IpAddr,
566 "IPV4" => DataType::Ipv4,
567 "IPV6" => DataType::Ipv6,
568 "MACADDR" => DataType::MacAddr,
569 "NODEREF" => DataType::NodeRef,
570 "EDGEREF" => DataType::EdgeRef,
571 "VECTORREF" => DataType::VectorRef,
572 "ROWREF" => DataType::RowRef,
573 "CIDR" => DataType::Cidr,
574 "SUBNET" => DataType::Subnet,
575 "PORT" => DataType::Port,
576 "COLOR" => DataType::Color,
577 "COLOR_ALPHA" | "COLORALPHA" => DataType::ColorAlpha,
578 "EMAIL" => DataType::Email,
579 "URL" => DataType::Url,
580 "PHONE" => DataType::Phone,
581 "SEMVER" => DataType::Semver,
582 "LATITUDE" => DataType::Latitude,
583 "LONGITUDE" => DataType::Longitude,
584 "GEOPOINT" | "GEO_POINT" => DataType::GeoPoint,
585 "COUNTRY2" => DataType::Country2,
586 "COUNTRY3" => DataType::Country3,
587 "LANG2" => DataType::Lang2,
588 "LANG5" => DataType::Lang5,
589 "CURRENCY" => DataType::Currency,
590 "ASSETCODE" | "ASSET_CODE" | "ASSET" => DataType::AssetCode,
591 "MONEY" => DataType::Money,
592 "DECIMAL_TEXT" | "DECIMALTEXT" => DataType::DecimalText,
593 "ENUM" => DataType::Enum,
594 "ARRAY" => DataType::Array,
595 "KEYREF" => DataType::KeyRef,
596 "DOCREF" => DataType::DocRef,
597 "TABLEREF" => DataType::TableRef,
598 "PAGEREF" => DataType::PageRef,
599 "SECRET" => DataType::Secret,
600 "PASSWORD" => DataType::Password,
601 "VECTOR" => DataType::Vector,
602 _ => return None,
603 })
604 }
605
606 pub fn fixed_size(&self) -> Option<usize> {
608 match self {
609 DataType::Integer => Some(8),
610 DataType::UnsignedInteger => Some(8),
611 DataType::Float => Some(8),
612 DataType::Boolean => Some(1),
613 DataType::Timestamp => Some(8),
614 DataType::Duration => Some(8),
615 DataType::MacAddr => Some(6),
616 DataType::Uuid => Some(16),
617 DataType::Text => None,
619 DataType::Blob => None,
620 DataType::IpAddr => None, DataType::Vector => None, DataType::Nullable => None,
623 DataType::Unknown => None,
624 DataType::Json => None,
625 DataType::NodeRef => None,
627 DataType::EdgeRef => None,
628 DataType::VectorRef => Some(8), DataType::RowRef => None, DataType::Color => Some(3), DataType::Email => None, DataType::Url => None, DataType::Phone => Some(8), DataType::Semver => Some(4), DataType::Cidr => Some(5), DataType::Date => Some(4), DataType::Time => Some(4), DataType::Decimal => Some(8), DataType::DecimalText => None, DataType::Enum => Some(1), DataType::Array => None, DataType::TimestampMs => Some(8), DataType::Ipv4 => Some(4), DataType::Ipv6 => Some(16), DataType::Subnet => Some(8), DataType::Port => Some(2), DataType::Latitude => Some(4), DataType::Longitude => Some(4), DataType::GeoPoint => Some(8), DataType::Country2 => Some(2), DataType::Country3 => Some(3), DataType::Lang2 => Some(2), DataType::Lang5 => Some(5), DataType::Currency => Some(3), DataType::ColorAlpha => Some(4), DataType::BigInt => Some(8), DataType::AssetCode => None, DataType::Money => None, DataType::KeyRef => None, DataType::DocRef => None, DataType::TableRef => None, DataType::PageRef => Some(4), DataType::Secret => None, DataType::Password => None, DataType::TextZstd => None, DataType::BlobZstd => None, }
668 }
669
670 pub fn is_indexable(&self) -> bool {
672 matches!(
673 self,
674 DataType::Integer
675 | DataType::UnsignedInteger
676 | DataType::Float
677 | DataType::Text
678 | DataType::Timestamp
679 | DataType::IpAddr
680 | DataType::Uuid
681 | DataType::NodeRef
682 | DataType::EdgeRef
683 | DataType::VectorRef
684 | DataType::RowRef
685 | DataType::Email
686 | DataType::Url
687 | DataType::Phone
688 | DataType::Semver
689 | DataType::Date
690 | DataType::Time
691 | DataType::Decimal
692 | DataType::DecimalText
693 | DataType::Enum
694 | DataType::TimestampMs
695 | DataType::Ipv4
696 | DataType::Ipv6
697 | DataType::Port
698 | DataType::Latitude
699 | DataType::Longitude
700 | DataType::GeoPoint
701 | DataType::Country2
702 | DataType::Country3
703 | DataType::Lang2
704 | DataType::Lang5
705 | DataType::Currency
706 | DataType::AssetCode
707 | DataType::BigInt
708 | DataType::KeyRef
709 | DataType::DocRef
710 | DataType::TableRef
711 | DataType::PageRef
712 )
713 }
714
715 pub fn is_orderable(&self) -> bool {
717 matches!(
718 self,
719 DataType::Integer
720 | DataType::UnsignedInteger
721 | DataType::Float
722 | DataType::Text
723 | DataType::Timestamp
724 | DataType::Duration
725 | DataType::Date
726 | DataType::Time
727 | DataType::Decimal
728 | DataType::DecimalText
729 | DataType::Semver
730 | DataType::TimestampMs
731 | DataType::Port
732 | DataType::Latitude
733 | DataType::Longitude
734 | DataType::BigInt
735 | DataType::AssetCode
736 )
737 }
738}
739
740impl fmt::Display for DataType {
741 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
742 match self {
743 DataType::Integer => write!(f, "INTEGER"),
744 DataType::UnsignedInteger => write!(f, "UNSIGNED INTEGER"),
745 DataType::Float => write!(f, "FLOAT"),
746 DataType::Text => write!(f, "TEXT"),
747 DataType::Blob => write!(f, "BLOB"),
748 DataType::Boolean => write!(f, "BOOLEAN"),
749 DataType::Timestamp => write!(f, "TIMESTAMP"),
750 DataType::Duration => write!(f, "DURATION"),
751 DataType::IpAddr => write!(f, "IPADDR"),
752 DataType::MacAddr => write!(f, "MACADDR"),
753 DataType::Vector => write!(f, "VECTOR"),
754 DataType::Nullable => write!(f, "NULLABLE"),
755 DataType::Unknown => write!(f, "UNKNOWN"),
756 DataType::Json => write!(f, "JSON"),
757 DataType::Uuid => write!(f, "UUID"),
758 DataType::NodeRef => write!(f, "NODEREF"),
759 DataType::EdgeRef => write!(f, "EDGEREF"),
760 DataType::VectorRef => write!(f, "VECTORREF"),
761 DataType::RowRef => write!(f, "ROWREF"),
762 DataType::Color => write!(f, "COLOR"),
763 DataType::Email => write!(f, "EMAIL"),
764 DataType::Url => write!(f, "URL"),
765 DataType::Phone => write!(f, "PHONE"),
766 DataType::Semver => write!(f, "SEMVER"),
767 DataType::Cidr => write!(f, "CIDR"),
768 DataType::Date => write!(f, "DATE"),
769 DataType::Time => write!(f, "TIME"),
770 DataType::Decimal => write!(f, "DECIMAL"),
771 DataType::Enum => write!(f, "ENUM"),
772 DataType::Array => write!(f, "ARRAY"),
773 DataType::TimestampMs => write!(f, "TIMESTAMP_MS"),
774 DataType::Ipv4 => write!(f, "IPV4"),
775 DataType::Ipv6 => write!(f, "IPV6"),
776 DataType::Subnet => write!(f, "SUBNET"),
777 DataType::Port => write!(f, "PORT"),
778 DataType::Latitude => write!(f, "LATITUDE"),
779 DataType::Longitude => write!(f, "LONGITUDE"),
780 DataType::GeoPoint => write!(f, "GEOPOINT"),
781 DataType::Country2 => write!(f, "COUNTRY2"),
782 DataType::Country3 => write!(f, "COUNTRY3"),
783 DataType::Lang2 => write!(f, "LANG2"),
784 DataType::Lang5 => write!(f, "LANG5"),
785 DataType::Currency => write!(f, "CURRENCY"),
786 DataType::AssetCode => write!(f, "ASSET_CODE"),
787 DataType::Money => write!(f, "MONEY"),
788 DataType::DecimalText => write!(f, "DECIMAL_TEXT"),
789 DataType::ColorAlpha => write!(f, "COLOR_ALPHA"),
790 DataType::BigInt => write!(f, "BIGINT"),
791 DataType::KeyRef => write!(f, "KEY_REF"),
792 DataType::DocRef => write!(f, "DOC_REF"),
793 DataType::TableRef => write!(f, "TABLE_REF"),
794 DataType::PageRef => write!(f, "PAGE_REF"),
795 DataType::Secret => write!(f, "SECRET"),
796 DataType::Password => write!(f, "PASSWORD"),
797 DataType::TextZstd => write!(f, "TEXT"), DataType::BlobZstd => write!(f, "BLOB"), }
800 }
801}
802
803#[derive(Debug, Clone, PartialEq)]
805pub enum Value {
806 Null,
808 Integer(i64),
810 UnsignedInteger(u64),
812 Float(f64),
814 Text(std::sync::Arc<str>),
816 Blob(Vec<u8>),
818 Boolean(bool),
820 Timestamp(i64),
822 Duration(i64),
824 IpAddr(IpAddr),
826 MacAddr([u8; 6]),
828 Vector(Vec<f32>),
830 Json(Vec<u8>),
832 Uuid([u8; 16]),
834 NodeRef(String),
836 EdgeRef(String),
838 VectorRef(String, u64),
840 RowRef(String, u64),
842 Color([u8; 3]),
844 Email(String),
846 Url(String),
848 Phone(u64),
850 Semver(u32),
852 Cidr(u32, u8),
854 Date(i32),
856 Time(u32),
858 Decimal(i64),
860 DecimalText(String),
862 EnumValue(u8),
864 Array(Vec<Value>),
866 TimestampMs(i64),
868 Ipv4(u32),
870 Ipv6([u8; 16]),
872 Subnet(u32, u32),
874 Port(u16),
876 Latitude(i32),
878 Longitude(i32),
880 GeoPoint(i32, i32),
882 Country2([u8; 2]),
884 Country3([u8; 3]),
886 Lang2([u8; 2]),
888 Lang5([u8; 5]),
890 Currency([u8; 3]),
892 AssetCode(String),
894 Money {
896 asset_code: String,
897 minor_units: i64,
898 scale: u8,
899 },
900 ColorAlpha([u8; 4]),
902 BigInt(i64),
904 KeyRef(String, String),
906 DocRef(String, u64),
908 TableRef(String),
910 PageRef(u32),
912 Secret(Vec<u8>),
914 Password(String),
916}
917
918impl Eq for Value {}
923
924impl std::hash::Hash for Value {
929 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
930 std::mem::discriminant(self).hash(state);
932 match self {
933 Value::Null => {}
934 Value::Integer(v) => v.hash(state),
935 Value::UnsignedInteger(v) => v.hash(state),
936 Value::Float(v) => v.to_bits().hash(state),
937 Value::Text(v) => v.hash(state),
938 Value::Blob(v) => v.hash(state),
939 Value::Boolean(v) => v.hash(state),
940 Value::Timestamp(v) => v.hash(state),
941 Value::Duration(v) => v.hash(state),
942 Value::IpAddr(v) => v.hash(state),
943 Value::MacAddr(v) => v.hash(state),
944 Value::Vector(v) => {
945 v.len().hash(state);
946 for f in v {
947 f.to_bits().hash(state);
948 }
949 }
950 Value::Json(v) => v.hash(state),
951 Value::Uuid(v) => v.hash(state),
952 Value::NodeRef(v) => v.hash(state),
953 Value::EdgeRef(v) => v.hash(state),
954 Value::VectorRef(c, id) => {
955 c.hash(state);
956 id.hash(state);
957 }
958 Value::RowRef(c, id) => {
959 c.hash(state);
960 id.hash(state);
961 }
962 Value::Color(v) => v.hash(state),
963 Value::Email(v) => v.hash(state),
964 Value::Url(v) => v.hash(state),
965 Value::Phone(v) => v.hash(state),
966 Value::Semver(v) => v.hash(state),
967 Value::Cidr(ip, prefix) => {
968 ip.hash(state);
969 prefix.hash(state);
970 }
971 Value::Date(v) => v.hash(state),
972 Value::Time(v) => v.hash(state),
973 Value::Decimal(v) => v.hash(state),
974 Value::DecimalText(v) => v.hash(state),
975 Value::EnumValue(v) => v.hash(state),
976 Value::Array(v) => {
977 v.len().hash(state);
978 for elem in v {
979 elem.hash(state);
980 }
981 }
982 Value::TimestampMs(v) => v.hash(state),
983 Value::Ipv4(v) => v.hash(state),
984 Value::Ipv6(v) => v.hash(state),
985 Value::Subnet(ip, mask) => {
986 ip.hash(state);
987 mask.hash(state);
988 }
989 Value::Port(v) => v.hash(state),
990 Value::Latitude(v) => v.hash(state),
991 Value::Longitude(v) => v.hash(state),
992 Value::GeoPoint(lat, lon) => {
993 lat.hash(state);
994 lon.hash(state);
995 }
996 Value::Country2(v) => v.hash(state),
997 Value::Country3(v) => v.hash(state),
998 Value::Lang2(v) => v.hash(state),
999 Value::Lang5(v) => v.hash(state),
1000 Value::Currency(v) => v.hash(state),
1001 Value::AssetCode(v) => v.hash(state),
1002 Value::Money {
1003 asset_code,
1004 minor_units,
1005 scale,
1006 } => {
1007 asset_code.hash(state);
1008 minor_units.hash(state);
1009 scale.hash(state);
1010 }
1011 Value::ColorAlpha(v) => v.hash(state),
1012 Value::BigInt(v) => v.hash(state),
1013 Value::KeyRef(c, k) => {
1014 c.hash(state);
1015 k.hash(state);
1016 }
1017 Value::DocRef(c, id) => {
1018 c.hash(state);
1019 id.hash(state);
1020 }
1021 Value::TableRef(v) => v.hash(state),
1022 Value::PageRef(v) => v.hash(state),
1023 Value::Secret(v) => v.hash(state),
1024 Value::Password(v) => v.hash(state),
1025 }
1026 }
1027}
1028
1029impl Value {
1030 #[inline]
1035 pub fn text(s: impl Into<std::sync::Arc<str>>) -> Self {
1036 Value::Text(s.into())
1037 }
1038
1039 pub fn data_type(&self) -> DataType {
1041 match self {
1042 Value::Null => DataType::Nullable,
1043 Value::Integer(_) => DataType::Integer,
1044 Value::UnsignedInteger(_) => DataType::UnsignedInteger,
1045 Value::Float(_) => DataType::Float,
1046 Value::Text(_) => DataType::Text,
1047 Value::Blob(_) => DataType::Blob,
1048 Value::Boolean(_) => DataType::Boolean,
1049 Value::Timestamp(_) => DataType::Timestamp,
1050 Value::Duration(_) => DataType::Duration,
1051 Value::IpAddr(_) => DataType::IpAddr,
1052 Value::MacAddr(_) => DataType::MacAddr,
1053 Value::Vector(_) => DataType::Vector,
1054 Value::Json(_) => DataType::Json,
1055 Value::Uuid(_) => DataType::Uuid,
1056 Value::NodeRef(_) => DataType::NodeRef,
1057 Value::EdgeRef(_) => DataType::EdgeRef,
1058 Value::VectorRef(_, _) => DataType::VectorRef,
1059 Value::RowRef(_, _) => DataType::RowRef,
1060 Value::Color(_) => DataType::Color,
1061 Value::Email(_) => DataType::Email,
1062 Value::Url(_) => DataType::Url,
1063 Value::Phone(_) => DataType::Phone,
1064 Value::Semver(_) => DataType::Semver,
1065 Value::Cidr(_, _) => DataType::Cidr,
1066 Value::Date(_) => DataType::Date,
1067 Value::Time(_) => DataType::Time,
1068 Value::Decimal(_) => DataType::Decimal,
1069 Value::DecimalText(_) => DataType::DecimalText,
1070 Value::EnumValue(_) => DataType::Enum,
1071 Value::Array(_) => DataType::Array,
1072 Value::TimestampMs(_) => DataType::TimestampMs,
1073 Value::Ipv4(_) => DataType::Ipv4,
1074 Value::Ipv6(_) => DataType::Ipv6,
1075 Value::Subnet(_, _) => DataType::Subnet,
1076 Value::Port(_) => DataType::Port,
1077 Value::Latitude(_) => DataType::Latitude,
1078 Value::Longitude(_) => DataType::Longitude,
1079 Value::GeoPoint(_, _) => DataType::GeoPoint,
1080 Value::Country2(_) => DataType::Country2,
1081 Value::Country3(_) => DataType::Country3,
1082 Value::Lang2(_) => DataType::Lang2,
1083 Value::Lang5(_) => DataType::Lang5,
1084 Value::Currency(_) => DataType::Currency,
1085 Value::AssetCode(_) => DataType::AssetCode,
1086 Value::Money { .. } => DataType::Money,
1087 Value::ColorAlpha(_) => DataType::ColorAlpha,
1088 Value::BigInt(_) => DataType::BigInt,
1089 Value::KeyRef(..) => DataType::KeyRef,
1090 Value::DocRef(..) => DataType::DocRef,
1091 Value::TableRef(..) => DataType::TableRef,
1092 Value::PageRef(..) => DataType::PageRef,
1093 Value::Secret(..) => DataType::Secret,
1094 Value::Password(..) => DataType::Password,
1095 }
1096 }
1097
1098 pub fn is_null(&self) -> bool {
1100 matches!(self, Value::Null)
1101 }
1102
1103 pub fn to_bytes(&self) -> Vec<u8> {
1109 let mut buf = Vec::new();
1110 super::value_codec::encode(self, &mut buf);
1111 buf
1112 }
1113
1114 pub fn from_bytes(data: &[u8]) -> Result<(Self, usize), ValueError> {
1118 super::value_codec::decode(data)
1119 }
1120
1121 pub fn from_bytes_with_limit(
1125 data: &[u8],
1126 limit_bytes: usize,
1127 ) -> Result<(Self, usize), ValueError> {
1128 super::value_codec::decode_with_limit(data, limit_bytes)
1129 }
1130
1131 pub fn from_bytes_with_budget(
1135 data: &[u8],
1136 remaining_bytes: &mut usize,
1137 ) -> Result<(Self, usize), ValueError> {
1138 super::value_codec::decode_with_budget(data, remaining_bytes)
1139 }
1140
1141 pub fn as_integer(&self) -> Option<i64> {
1143 match self {
1144 Value::Integer(v) => Some(*v),
1145 Value::UnsignedInteger(v) => {
1146 if *v <= i64::MAX as u64 {
1147 Some(*v as i64)
1148 } else {
1149 None
1150 }
1151 }
1152 Value::Timestamp(v) => Some(*v),
1153 Value::Duration(v) => Some(*v),
1154 _ => None,
1155 }
1156 }
1157
1158 pub fn as_float(&self) -> Option<f64> {
1160 match self {
1161 Value::Float(v) => Some(*v),
1162 Value::Integer(v) => Some(*v as f64),
1163 Value::UnsignedInteger(v) => Some(*v as f64),
1164 _ => None,
1165 }
1166 }
1167
1168 pub fn as_text(&self) -> Option<&str> {
1170 match self {
1171 Value::Text(s) => Some(s),
1172 _ => None,
1173 }
1174 }
1175
1176 pub fn as_boolean(&self) -> Option<bool> {
1178 match self {
1179 Value::Boolean(v) => Some(*v),
1180 _ => None,
1181 }
1182 }
1183
1184 pub fn as_ip_addr(&self) -> Option<IpAddr> {
1186 match self {
1187 Value::IpAddr(addr) => Some(*addr),
1188 _ => None,
1189 }
1190 }
1191
1192 pub fn as_vector(&self) -> Option<&[f32]> {
1194 match self {
1195 Value::Vector(v) => Some(v),
1196 _ => None,
1197 }
1198 }
1199
1200 pub fn display_string(&self) -> String {
1202 match self {
1203 Value::Color([r, g, b]) => format!("#{:02X}{:02X}{:02X}", r, g, b),
1204 Value::Email(s) => s.clone(),
1205 Value::Url(s) => s.clone(),
1206 Value::Phone(n) => format!("+{}", n),
1207 Value::Semver(packed) => format!(
1208 "{}.{}.{}",
1209 packed / 1_000_000,
1210 (packed / 1_000) % 1_000,
1211 packed % 1_000
1212 ),
1213 Value::Cidr(ip, prefix) => format!(
1214 "{}.{}.{}.{}/{}",
1215 (ip >> 24) & 0xFF,
1216 (ip >> 16) & 0xFF,
1217 (ip >> 8) & 0xFF,
1218 ip & 0xFF,
1219 prefix
1220 ),
1221 Value::Date(days) => format_civil_date(*days),
1222 Value::Time(ms) => {
1223 let total_secs = ms / 1000;
1224 format!(
1225 "{:02}:{:02}:{:02}",
1226 total_secs / 3600,
1227 (total_secs / 60) % 60,
1228 total_secs % 60
1229 )
1230 }
1231 Value::Decimal(v) => format_scaled_i64(*v, 4),
1232 Value::DecimalText(v) => v.clone(),
1233 Value::EnumValue(i) => format!("enum({})", i),
1234 Value::Array(elems) => {
1235 let items: Vec<String> = elems.iter().map(|e| e.display_string()).collect();
1236 format!("[{}]", items.join(", "))
1237 }
1238 Value::TimestampMs(ms) => {
1239 let secs = ms / 1000;
1240 let millis = (ms % 1000).unsigned_abs() as u32;
1241 let days = (secs / 86400) as i32;
1242 let day_secs = (secs % 86400) as u32;
1243 let h = day_secs / 3600;
1244 let m = (day_secs / 60) % 60;
1245 let s = day_secs % 60;
1246 format!(
1247 "{}T{:02}:{:02}:{:02}.{:03}Z",
1248 format_civil_date(days),
1249 h,
1250 m,
1251 s,
1252 millis
1253 )
1254 }
1255 Value::Ipv4(ip) => format!(
1256 "{}.{}.{}.{}",
1257 (ip >> 24) & 0xFF,
1258 (ip >> 16) & 0xFF,
1259 (ip >> 8) & 0xFF,
1260 ip & 0xFF
1261 ),
1262 Value::Ipv6(bytes) => {
1263 let addr = std::net::Ipv6Addr::from(*bytes);
1264 format!("{}", addr)
1265 }
1266 Value::Subnet(ip, mask) => {
1267 let ip_str = format!(
1268 "{}.{}.{}.{}",
1269 (ip >> 24) & 0xFF,
1270 (ip >> 16) & 0xFF,
1271 (ip >> 8) & 0xFF,
1272 ip & 0xFF
1273 );
1274 let prefix = mask.leading_ones();
1276 if prefix < 32 && (*mask << prefix) == 0 || prefix == 32 {
1277 format!("{}/{}", ip_str, prefix)
1278 } else {
1279 let mask_str = format!(
1280 "{}.{}.{}.{}",
1281 (mask >> 24) & 0xFF,
1282 (mask >> 16) & 0xFF,
1283 (mask >> 8) & 0xFF,
1284 mask & 0xFF
1285 );
1286 format!("{}/{}", ip_str, mask_str)
1287 }
1288 }
1289 Value::Port(p) => p.to_string(),
1290 Value::Latitude(micro) => format!("{:.6}", *micro as f64 / 1_000_000.0),
1291 Value::Longitude(micro) => format!("{:.6}", *micro as f64 / 1_000_000.0),
1292 Value::GeoPoint(lat, lon) => format!(
1293 "{:.6},{:.6}",
1294 *lat as f64 / 1_000_000.0,
1295 *lon as f64 / 1_000_000.0
1296 ),
1297 Value::Country2(c) => String::from_utf8_lossy(c).to_string(),
1298 Value::Country3(c) => String::from_utf8_lossy(c).to_string(),
1299 Value::Lang2(c) => String::from_utf8_lossy(c).to_string(),
1300 Value::Lang5(c) => String::from_utf8_lossy(c).to_string(),
1301 Value::Currency(c) => String::from_utf8_lossy(c).to_string(),
1302 Value::AssetCode(code) => code.clone(),
1303 Value::Money {
1304 asset_code,
1305 minor_units,
1306 scale,
1307 } => format!("{} {}", asset_code, format_scaled_i64(*minor_units, *scale)),
1308 Value::ColorAlpha([r, g, b, a]) => format!("#{:02X}{:02X}{:02X}{:02X}", r, g, b, a),
1309 Value::BigInt(v) => v.to_string(),
1310 Value::KeyRef(c, k) => format!("{}:{}", c, k),
1311 Value::DocRef(c, id) => format!("{}#{}", c, id),
1312 Value::TableRef(t) => t.clone(),
1313 Value::PageRef(p) => format!("page:{}", p),
1314 other => format!("{}", other),
1315 }
1316 }
1317
1318 pub fn plain_text(&self) -> String {
1322 match self {
1323 Value::Text(text) => text.to_string(),
1324 Value::Array(elems) => {
1325 let items: Vec<String> = elems.iter().map(Value::plain_text).collect();
1326 format!("[{}]", items.join(", "))
1327 }
1328 other => other.display_string(),
1329 }
1330 }
1331}
1332
1333pub const DECIMAL_SCALE: u8 = 4;
1342
1343pub fn decimal_to_f64(value: i64) -> f64 {
1350 value as f64 / 10_i64.pow(DECIMAL_SCALE as u32) as f64
1351}
1352
1353fn format_scaled_i64(value: i64, scale: u8) -> String {
1354 let negative = value < 0;
1355 let abs = (value as i128).abs();
1356 if scale == 0 {
1357 return if negative {
1358 format!("-{}", abs)
1359 } else {
1360 abs.to_string()
1361 };
1362 }
1363
1364 let divisor = 10_i128.pow(scale as u32);
1365 let whole = abs / divisor;
1366 let frac = abs % divisor;
1367 let sign = if negative { "-" } else { "" };
1368 format!("{}{}.{:0width$}", sign, whole, frac, width = scale as usize)
1369}
1370
1371impl fmt::Display for Value {
1372 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1373 match self {
1374 Value::Null => write!(f, "NULL"),
1375 Value::Integer(v) => write!(f, "{}", v),
1376 Value::UnsignedInteger(v) => write!(f, "{}", v),
1377 Value::Float(v) => write!(f, "{}", v),
1378 Value::Text(s) => write!(f, "'{}'", s),
1379 Value::Blob(b) => write!(f, "<blob {} bytes>", b.len()),
1380 Value::Boolean(v) => write!(f, "{}", v),
1381 Value::Timestamp(v) => write!(f, "ts:{}", v),
1382 Value::Duration(v) => write!(f, "{}ms", v),
1383 Value::IpAddr(addr) => write!(f, "{}", addr),
1384 Value::MacAddr(mac) => write!(
1385 f,
1386 "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
1387 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
1388 ),
1389 Value::Vector(v) => write!(f, "<vector dim={}>", v.len()),
1390 Value::Json(j) => write!(f, "<json {} bytes>", j.len()),
1391 Value::Uuid(u) => {
1392 write!(
1393 f,
1394 "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
1395 u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7],
1396 u[8], u[9], u[10], u[11], u[12], u[13], u[14], u[15]
1397 )
1398 }
1399 Value::NodeRef(id) => write!(f, "node:{}", id),
1400 Value::EdgeRef(id) => write!(f, "edge:{}", id),
1401 Value::VectorRef(coll, id) => write!(f, "vector:{}:{}", coll, id),
1402 Value::RowRef(table, id) => write!(f, "row:{}:{}", table, id),
1403 Value::Color([r, g, b]) => write!(f, "#{:02X}{:02X}{:02X}", r, g, b),
1404 Value::Email(s) => write!(f, "{}", s),
1405 Value::Url(s) => write!(f, "{}", s),
1406 Value::Phone(n) => write!(f, "+{}", n),
1407 Value::Semver(packed) => write!(
1408 f,
1409 "{}.{}.{}",
1410 packed / 1_000_000,
1411 (packed / 1_000) % 1_000,
1412 packed % 1_000
1413 ),
1414 Value::Cidr(ip, prefix) => write!(
1415 f,
1416 "{}.{}.{}.{}/{}",
1417 (ip >> 24) & 0xFF,
1418 (ip >> 16) & 0xFF,
1419 (ip >> 8) & 0xFF,
1420 ip & 0xFF,
1421 prefix
1422 ),
1423 Value::Date(days) => write!(f, "{}", format_civil_date(*days)),
1424 Value::Time(ms) => {
1425 let total_secs = ms / 1000;
1426 write!(
1427 f,
1428 "{:02}:{:02}:{:02}",
1429 total_secs / 3600,
1430 (total_secs / 60) % 60,
1431 total_secs % 60
1432 )
1433 }
1434 Value::Decimal(v) => write!(f, "{}", format_scaled_i64(*v, 4)),
1435 Value::DecimalText(v) => write!(f, "{v}"),
1436 Value::EnumValue(i) => write!(f, "enum({})", i),
1437 Value::Array(elems) => {
1438 write!(f, "[")?;
1439 for (i, elem) in elems.iter().enumerate() {
1440 if i > 0 {
1441 write!(f, ", ")?;
1442 }
1443 write!(f, "{}", elem)?;
1444 }
1445 write!(f, "]")
1446 }
1447 Value::TimestampMs(ms) => {
1448 let secs = ms / 1000;
1449 let millis = (ms % 1000).unsigned_abs() as u32;
1450 let days = (secs / 86400) as i32;
1451 let day_secs = (secs % 86400) as u32;
1452 let h = day_secs / 3600;
1453 let m = (day_secs / 60) % 60;
1454 let s = day_secs % 60;
1455 write!(
1456 f,
1457 "{}T{:02}:{:02}:{:02}.{:03}Z",
1458 format_civil_date(days),
1459 h,
1460 m,
1461 s,
1462 millis
1463 )
1464 }
1465 Value::Ipv4(ip) => write!(
1466 f,
1467 "{}.{}.{}.{}",
1468 (ip >> 24) & 0xFF,
1469 (ip >> 16) & 0xFF,
1470 (ip >> 8) & 0xFF,
1471 ip & 0xFF
1472 ),
1473 Value::Ipv6(bytes) => {
1474 let addr = std::net::Ipv6Addr::from(*bytes);
1475 write!(f, "{}", addr)
1476 }
1477 Value::Subnet(ip, mask) => {
1478 let prefix = mask.leading_ones();
1479 if prefix < 32 && (*mask << prefix) == 0 || prefix == 32 {
1480 write!(
1481 f,
1482 "{}.{}.{}.{}/{}",
1483 (ip >> 24) & 0xFF,
1484 (ip >> 16) & 0xFF,
1485 (ip >> 8) & 0xFF,
1486 ip & 0xFF,
1487 prefix
1488 )
1489 } else {
1490 write!(
1491 f,
1492 "{}.{}.{}.{}/{}.{}.{}.{}",
1493 (ip >> 24) & 0xFF,
1494 (ip >> 16) & 0xFF,
1495 (ip >> 8) & 0xFF,
1496 ip & 0xFF,
1497 (mask >> 24) & 0xFF,
1498 (mask >> 16) & 0xFF,
1499 (mask >> 8) & 0xFF,
1500 mask & 0xFF
1501 )
1502 }
1503 }
1504 Value::Port(p) => write!(f, "{}", p),
1505 Value::Latitude(micro) => write!(f, "{:.6}", *micro as f64 / 1_000_000.0),
1506 Value::Longitude(micro) => write!(f, "{:.6}", *micro as f64 / 1_000_000.0),
1507 Value::GeoPoint(lat, lon) => write!(
1508 f,
1509 "{:.6},{:.6}",
1510 *lat as f64 / 1_000_000.0,
1511 *lon as f64 / 1_000_000.0
1512 ),
1513 Value::Country2(c) => write!(f, "{}", String::from_utf8_lossy(c)),
1514 Value::Country3(c) => write!(f, "{}", String::from_utf8_lossy(c)),
1515 Value::Lang2(c) => write!(f, "{}", String::from_utf8_lossy(c)),
1516 Value::Lang5(c) => write!(f, "{}", String::from_utf8_lossy(c)),
1517 Value::Currency(c) => write!(f, "{}", String::from_utf8_lossy(c)),
1518 Value::AssetCode(code) => write!(f, "{}", code),
1519 Value::Money {
1520 asset_code,
1521 minor_units,
1522 scale,
1523 } => write!(
1524 f,
1525 "{} {}",
1526 asset_code,
1527 format_scaled_i64(*minor_units, *scale)
1528 ),
1529 Value::ColorAlpha([r, g, b, a]) => write!(f, "#{:02X}{:02X}{:02X}{:02X}", r, g, b, a),
1530 Value::BigInt(v) => write!(f, "{}", v),
1531 Value::KeyRef(c, k) => write!(f, "key_ref:{}:{}", c, k),
1532 Value::DocRef(c, id) => write!(f, "doc_ref:{}#{}", c, id),
1533 Value::TableRef(t) => write!(f, "table_ref:{}", t),
1534 Value::PageRef(p) => write!(f, "page_ref:{}", p),
1535 Value::Secret(_) => write!(f, "***"),
1536 Value::Password(_) => write!(f, "***"),
1537 }
1538 }
1539}
1540
1541#[derive(Debug, Clone, PartialEq)]
1543pub enum ValueError {
1544 EmptyData,
1546 InvalidType(u8),
1548 TruncatedData,
1550 InvalidUtf8,
1552 InvalidIpVersion(u8),
1554 VarintOverflow,
1556 ReadLimitExceeded {
1558 required_bytes: usize,
1559 limit_bytes: usize,
1560 },
1561 NestingLimitExceeded { maximum: u32 },
1563 TypeMismatch { expected: DataType, found: DataType },
1565}
1566
1567impl fmt::Display for ValueError {
1568 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1569 match self {
1570 ValueError::EmptyData => write!(f, "empty data"),
1571 ValueError::InvalidType(t) => write!(f, "invalid type byte: {}", t),
1572 ValueError::TruncatedData => write!(f, "truncated data"),
1573 ValueError::InvalidUtf8 => write!(f, "invalid UTF-8"),
1574 ValueError::InvalidIpVersion(v) => write!(f, "invalid IP version: {}", v),
1575 ValueError::VarintOverflow => write!(f, "varint overflow"),
1576 ValueError::ReadLimitExceeded {
1577 required_bytes,
1578 limit_bytes,
1579 } => write!(
1580 f,
1581 "read needs {required_bytes} bytes over read memory limit {limit_bytes}"
1582 ),
1583 ValueError::NestingLimitExceeded { maximum } => {
1584 write!(f, "value nesting depth exceeds {maximum}")
1585 }
1586 ValueError::TypeMismatch { expected, found } => {
1587 write!(f, "type mismatch: expected {}, found {}", expected, found)
1588 }
1589 }
1590 }
1591}
1592
1593impl std::error::Error for ValueError {}
1594
1595pub(super) fn write_varint(buf: &mut Vec<u8>, mut value: u64) {
1597 loop {
1598 let mut byte = (value & 0x7F) as u8;
1599 value >>= 7;
1600 if value != 0 {
1601 byte |= 0x80;
1602 }
1603 buf.push(byte);
1604 if value == 0 {
1605 break;
1606 }
1607 }
1608}
1609
1610pub(super) fn read_varint(data: &[u8]) -> Result<(u64, usize), ValueError> {
1612 let mut result: u64 = 0;
1613 let mut shift = 0;
1614 let mut offset = 0;
1615
1616 loop {
1617 if offset >= data.len() {
1618 return Err(ValueError::TruncatedData);
1619 }
1620 let byte = data[offset];
1621 offset += 1;
1622
1623 if shift >= 64 {
1624 return Err(ValueError::VarintOverflow);
1625 }
1626
1627 result |= ((byte & 0x7F) as u64) << shift;
1628 shift += 7;
1629
1630 if byte & 0x80 == 0 {
1631 break;
1632 }
1633 }
1634
1635 Ok((result, offset))
1636}
1637
1638fn format_civil_date(days: i32) -> String {
1640 let z = days as i64 + 719468;
1641 let era = (if z >= 0 { z } else { z - 146096 }) / 146097;
1642 let doe = (z - era * 146097) as u32;
1643 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
1644 let y = yoe as i64 + era * 400;
1645 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
1646 let mp = (5 * doy + 2) / 153;
1647 let d = doy - (153 * mp + 2) / 5 + 1;
1648 let m = if mp < 10 { mp + 3 } else { mp - 9 };
1649 let y = if m <= 2 { y + 1 } else { y };
1650 format!("{:04}-{:02}-{:02}", y, m, d)
1651}
1652
1653#[derive(Debug, Clone, PartialEq)]
1655pub struct Row {
1656 values: Vec<Value>,
1657}
1658
1659impl Row {
1660 pub fn new(values: Vec<Value>) -> Self {
1662 Self { values }
1663 }
1664
1665 pub fn get(&self, index: usize) -> Option<&Value> {
1667 self.values.get(index)
1668 }
1669
1670 pub fn len(&self) -> usize {
1672 self.values.len()
1673 }
1674
1675 pub fn is_empty(&self) -> bool {
1677 self.values.is_empty()
1678 }
1679
1680 pub fn iter(&self) -> impl Iterator<Item = &Value> {
1682 self.values.iter()
1683 }
1684
1685 pub fn values(&self) -> &[Value] {
1687 &self.values
1688 }
1689
1690 pub fn into_values(self) -> Vec<Value> {
1692 self.values
1693 }
1694
1695 pub fn to_bytes(&self) -> Vec<u8> {
1697 let mut buf = Vec::new();
1698
1699 write_varint(&mut buf, self.values.len() as u64);
1701
1702 for value in &self.values {
1704 let value_bytes = value.to_bytes();
1705 buf.extend_from_slice(&value_bytes);
1706 }
1707
1708 buf
1709 }
1710
1711 pub fn from_bytes(data: &[u8]) -> Result<(Self, usize), ValueError> {
1713 if data.is_empty() {
1714 return Err(ValueError::EmptyData);
1715 }
1716
1717 let (column_count, mut offset) = read_varint(data)?;
1718 let mut values = Vec::with_capacity(column_count as usize);
1719
1720 for _ in 0..column_count {
1721 let (value, size) = Value::from_bytes(&data[offset..])?;
1722 offset += size;
1723 values.push(value);
1724 }
1725
1726 Ok((Row { values }, offset))
1727 }
1728}
1729
1730impl From<Vec<Value>> for Row {
1731 fn from(values: Vec<Value>) -> Self {
1732 Row::new(values)
1733 }
1734}
1735
1736impl IntoIterator for Row {
1737 type Item = Value;
1738 type IntoIter = std::vec::IntoIter<Value>;
1739
1740 fn into_iter(self) -> Self::IntoIter {
1741 self.values.into_iter()
1742 }
1743}
1744
1745#[cfg(test)]
1746mod tests {
1747 use super::*;
1748 use proptest::prelude::*;
1749 use std::collections::hash_map::DefaultHasher;
1750 use std::hash::{Hash, Hasher};
1751 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
1752
1753 #[test]
1754 fn test_datatype_roundtrip() {
1755 let types = [
1756 DataType::Integer,
1757 DataType::UnsignedInteger,
1758 DataType::Float,
1759 DataType::Text,
1760 DataType::Blob,
1761 DataType::Boolean,
1762 DataType::Timestamp,
1763 DataType::Duration,
1764 DataType::IpAddr,
1765 DataType::MacAddr,
1766 DataType::Vector,
1767 DataType::Json,
1768 DataType::Uuid,
1769 DataType::Color,
1770 DataType::Email,
1771 DataType::Url,
1772 DataType::Phone,
1773 DataType::Semver,
1774 DataType::Cidr,
1775 DataType::Date,
1776 DataType::Time,
1777 DataType::Decimal,
1778 DataType::Enum,
1779 DataType::Array,
1780 DataType::TimestampMs,
1781 DataType::Ipv4,
1782 DataType::Ipv6,
1783 DataType::Subnet,
1784 DataType::Port,
1785 DataType::Latitude,
1786 DataType::Longitude,
1787 DataType::GeoPoint,
1788 DataType::Country2,
1789 DataType::Country3,
1790 DataType::Lang2,
1791 DataType::Lang5,
1792 DataType::Currency,
1793 DataType::ColorAlpha,
1794 DataType::BigInt,
1795 ];
1796
1797 for dt in types {
1798 let byte = dt.to_byte();
1799 let recovered = DataType::from_byte(byte).unwrap();
1800 assert_eq!(dt, recovered);
1801 }
1802 }
1803
1804 #[test]
1805 fn test_from_sql_name_uses_shared_alias_mapping() {
1806 assert_eq!(DataType::from_sql_name("INT8"), Some(DataType::BigInt));
1807 assert_eq!(DataType::from_sql_name("BIGINT"), Some(DataType::BigInt));
1808 assert_eq!(
1809 DataType::from_sql_name("TIMESTAMPTZ"),
1810 Some(DataType::TimestampMs)
1811 );
1812 assert_eq!(DataType::from_sql_name("ROWREF"), Some(DataType::RowRef));
1813 }
1814
1815 #[test]
1816 fn test_value_integer() {
1817 let value = Value::Integer(-12345);
1818 let bytes = value.to_bytes();
1819 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1820 assert_eq!(value, recovered);
1821 assert_eq!(size, bytes.len());
1822 }
1823
1824 #[test]
1825 fn test_value_text() {
1826 let value = Value::text("Hello, RedDB!".to_string());
1827 let bytes = value.to_bytes();
1828 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1829 assert_eq!(value, recovered);
1830 assert_eq!(size, bytes.len());
1831 }
1832
1833 #[test]
1834 fn plain_text_does_not_sql_quote_text() {
1835 assert_eq!(Value::text("alice").plain_text(), "alice");
1836 assert_eq!(
1837 Value::Array(vec![Value::text("alice"), Value::Integer(7)]).plain_text(),
1838 "[alice, 7]"
1839 );
1840 }
1841
1842 fn short_text() -> impl Strategy<Value = String> {
1843 "[a-zA-Z0-9_ ./:-]{0,16}".prop_map(|text| text)
1844 }
1845
1846 fn arb_value() -> impl Strategy<Value = Value> {
1847 let leaf = prop_oneof![
1848 Just(Value::Null),
1849 any::<i64>().prop_map(Value::Integer),
1850 any::<u64>().prop_map(Value::UnsignedInteger),
1851 any::<f64>().prop_map(Value::Float),
1852 short_text().prop_map(Value::text),
1853 proptest::collection::vec(any::<u8>(), 0..8).prop_map(Value::Blob),
1854 any::<bool>().prop_map(Value::Boolean),
1855 any::<i64>().prop_map(Value::Timestamp),
1856 any::<i64>().prop_map(Value::Duration),
1857 any::<u32>().prop_map(|ip| Value::IpAddr(IpAddr::V4(Ipv4Addr::from(ip)))),
1858 any::<[u8; 16]>().prop_map(|ip| Value::IpAddr(IpAddr::V6(Ipv6Addr::from(ip)))),
1859 any::<[u8; 6]>().prop_map(Value::MacAddr),
1860 proptest::collection::vec(any::<f32>(), 0..4).prop_map(Value::Vector),
1861 proptest::collection::vec(any::<u8>(), 0..8).prop_map(Value::Json),
1862 any::<[u8; 16]>().prop_map(Value::Uuid),
1863 short_text().prop_map(Value::NodeRef),
1864 short_text().prop_map(Value::EdgeRef),
1865 (short_text(), any::<u64>())
1866 .prop_map(|(collection, id)| Value::VectorRef(collection, id)),
1867 (short_text(), any::<u64>()).prop_map(|(table, id)| Value::RowRef(table, id)),
1868 any::<[u8; 3]>().prop_map(Value::Color),
1869 short_text().prop_map(Value::Email),
1870 short_text().prop_map(Value::Url),
1871 any::<u64>().prop_map(Value::Phone),
1872 any::<u32>().prop_map(Value::Semver),
1873 (any::<u32>(), 0u8..=32).prop_map(|(ip, prefix)| Value::Cidr(ip, prefix)),
1874 any::<i32>().prop_map(Value::Date),
1875 any::<u32>().prop_map(Value::Time),
1876 any::<i64>().prop_map(Value::Decimal),
1877 any::<u8>().prop_map(Value::EnumValue),
1878 any::<i64>().prop_map(Value::TimestampMs),
1879 any::<u32>().prop_map(Value::Ipv4),
1880 any::<[u8; 16]>().prop_map(Value::Ipv6),
1881 (any::<u32>(), any::<u32>()).prop_map(|(ip, mask)| Value::Subnet(ip, mask)),
1882 any::<u16>().prop_map(Value::Port),
1883 any::<i32>().prop_map(Value::Latitude),
1884 any::<i32>().prop_map(Value::Longitude),
1885 (any::<i32>(), any::<i32>()).prop_map(|(lat, lon)| Value::GeoPoint(lat, lon)),
1886 any::<[u8; 2]>().prop_map(Value::Country2),
1887 any::<[u8; 3]>().prop_map(Value::Country3),
1888 any::<[u8; 2]>().prop_map(Value::Lang2),
1889 any::<[u8; 5]>().prop_map(Value::Lang5),
1890 any::<[u8; 3]>().prop_map(Value::Currency),
1891 short_text().prop_map(Value::AssetCode),
1892 (short_text(), any::<i64>(), 0u8..=9).prop_map(|(asset_code, minor_units, scale)| {
1893 Value::Money {
1894 asset_code,
1895 minor_units,
1896 scale,
1897 }
1898 }),
1899 any::<[u8; 4]>().prop_map(Value::ColorAlpha),
1900 any::<i64>().prop_map(Value::BigInt),
1901 (short_text(), short_text())
1902 .prop_map(|(collection, key)| Value::KeyRef(collection, key)),
1903 (short_text(), any::<u64>()).prop_map(|(collection, id)| Value::DocRef(collection, id)),
1904 short_text().prop_map(Value::TableRef),
1905 any::<u32>().prop_map(Value::PageRef),
1906 proptest::collection::vec(any::<u8>(), 0..8).prop_map(Value::Secret),
1907 short_text().prop_map(Value::Password),
1908 ];
1909
1910 leaf.prop_recursive(2, 16, 4, |inner| {
1911 proptest::collection::vec(inner, 0..4).prop_map(Value::Array)
1912 })
1913 }
1914
1915 proptest! {
1916 #[test]
1917 fn plain_text_is_deterministic_for_every_value_variant(value in arb_value()) {
1918 let first = value.plain_text();
1919 prop_assert_eq!(first, value.plain_text());
1920 }
1921 }
1922
1923 #[test]
1924 fn test_value_ipaddr_v4() {
1925 let value = Value::IpAddr(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
1926 let bytes = value.to_bytes();
1927 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1928 assert_eq!(value, recovered);
1929 assert_eq!(size, bytes.len());
1930 }
1931
1932 #[test]
1933 fn test_value_ipaddr_v6() {
1934 let value = Value::IpAddr(IpAddr::V6(Ipv6Addr::LOCALHOST));
1935 let bytes = value.to_bytes();
1936 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1937 assert_eq!(value, recovered);
1938 assert_eq!(size, bytes.len());
1939 }
1940
1941 #[test]
1942 fn test_value_vector() {
1943 let value = Value::Vector(vec![1.0, 2.0, 3.0, 4.5]);
1944 let bytes = value.to_bytes();
1945 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1946 assert_eq!(value, recovered);
1947 assert_eq!(size, bytes.len());
1948 }
1949
1950 #[test]
1951 fn test_value_mac_addr() {
1952 let value = Value::MacAddr([0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
1953 let bytes = value.to_bytes();
1954 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1955 assert_eq!(value, recovered);
1956 assert_eq!(size, bytes.len());
1957 }
1958
1959 #[test]
1960 fn test_value_uuid() {
1961 let uuid = [
1962 0x55, 0x04, 0x43, 0x01, 0x8f, 0x3b, 0x4a, 0x12, 0x9c, 0x5d, 0x6e, 0x7f, 0x80, 0x91,
1963 0xa2, 0xb3,
1964 ];
1965 let value = Value::Uuid(uuid);
1966 let bytes = value.to_bytes();
1967 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1968 assert_eq!(value, recovered);
1969 assert_eq!(size, bytes.len());
1970 }
1971
1972 #[test]
1973 fn test_value_null() {
1974 let value = Value::Null;
1975 let bytes = value.to_bytes();
1976 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1977 assert_eq!(value, recovered);
1978 assert_eq!(size, bytes.len());
1979 }
1980
1981 #[test]
1982 fn test_value_blob() {
1983 let value = Value::Blob(vec![0x00, 0x01, 0x02, 0x03, 0xFF]);
1984 let bytes = value.to_bytes();
1985 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
1986 assert_eq!(value, recovered);
1987 assert_eq!(size, bytes.len());
1988 }
1989
1990 #[test]
1991 fn test_row_roundtrip() {
1992 let row = Row::new(vec![
1993 Value::Integer(42),
1994 Value::text("example.com".to_string()),
1995 Value::IpAddr(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))),
1996 Value::Boolean(true),
1997 Value::Null,
1998 ]);
1999
2000 let bytes = row.to_bytes();
2001 let (recovered, size) = Row::from_bytes(&bytes).unwrap();
2002
2003 assert_eq!(row, recovered);
2004 assert_eq!(size, bytes.len());
2005 }
2006
2007 #[test]
2008 fn test_varint_encoding() {
2009 let test_cases = [0u64, 1, 127, 128, 255, 256, 16383, 16384, u64::MAX];
2010
2011 for &value in &test_cases {
2012 let mut buf = Vec::new();
2013 write_varint(&mut buf, value);
2014 let (recovered, _) = read_varint(&buf).unwrap();
2015 assert_eq!(value, recovered, "Failed for value {}", value);
2016 }
2017 }
2018
2019 #[test]
2020 fn test_value_display() {
2021 assert_eq!(format!("{}", Value::Null), "NULL");
2022 assert_eq!(format!("{}", Value::Integer(42)), "42");
2023 assert_eq!(format!("{}", Value::Boolean(true)), "true");
2024 assert_eq!(format!("{}", Value::text("hello".to_string())), "'hello'");
2025 }
2026
2027 #[test]
2028 fn test_datatype_properties() {
2029 assert_eq!(DataType::Integer.fixed_size(), Some(8));
2030 assert_eq!(DataType::Text.fixed_size(), None);
2031 assert!(DataType::Integer.is_indexable());
2032 assert!(DataType::Text.is_indexable());
2033 assert!(!DataType::Blob.is_indexable());
2034 assert!(DataType::Integer.is_orderable());
2035 assert!(!DataType::Boolean.is_orderable());
2036 }
2037
2038 #[test]
2039 fn test_value_color_roundtrip() {
2040 let value = Value::Color([0xFF, 0x57, 0x33]);
2041 let bytes = value.to_bytes();
2042 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2043 assert_eq!(value, recovered);
2044 assert_eq!(size, bytes.len());
2045 assert_eq!(value.display_string(), "#FF5733");
2046 }
2047
2048 #[test]
2049 fn test_value_email_roundtrip() {
2050 let value = Value::Email("user@example.com".to_string());
2051 let bytes = value.to_bytes();
2052 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2053 assert_eq!(value, recovered);
2054 assert_eq!(size, bytes.len());
2055 }
2056
2057 #[test]
2058 fn test_value_url_roundtrip() {
2059 let value = Value::Url("https://example.com/path?q=1".to_string());
2060 let bytes = value.to_bytes();
2061 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2062 assert_eq!(value, recovered);
2063 assert_eq!(size, bytes.len());
2064 }
2065
2066 #[test]
2067 fn test_value_phone_roundtrip() {
2068 let value = Value::Phone(5511999887766);
2069 let bytes = value.to_bytes();
2070 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2071 assert_eq!(value, recovered);
2072 assert_eq!(size, bytes.len());
2073 assert_eq!(value.display_string(), "+5511999887766");
2074 }
2075
2076 #[test]
2077 fn test_value_semver_roundtrip() {
2078 let value = Value::Semver(1_000_000 + 23 * 1_000 + 456);
2079 let bytes = value.to_bytes();
2080 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2081 assert_eq!(value, recovered);
2082 assert_eq!(size, bytes.len());
2083 assert_eq!(value.display_string(), "1.23.456");
2084 }
2085
2086 #[test]
2087 fn test_value_cidr_roundtrip() {
2088 let ip: u32 = 10 << 24;
2090 let value = Value::Cidr(ip, 8);
2091 let bytes = value.to_bytes();
2092 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2093 assert_eq!(value, recovered);
2094 assert_eq!(size, bytes.len());
2095 assert_eq!(value.display_string(), "10.0.0.0/8");
2096 }
2097
2098 #[test]
2099 fn test_value_date_roundtrip() {
2100 let value = Value::Date(19738); let bytes = value.to_bytes();
2103 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2104 assert_eq!(value, recovered);
2105 assert_eq!(size, bytes.len());
2106 }
2107
2108 #[test]
2109 fn test_value_time_roundtrip() {
2110 let value = Value::Time(52_200_000);
2112 let bytes = value.to_bytes();
2113 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2114 assert_eq!(value, recovered);
2115 assert_eq!(size, bytes.len());
2116 assert_eq!(value.display_string(), "14:30:00");
2117 }
2118
2119 #[test]
2120 fn test_value_decimal_roundtrip() {
2121 let value = Value::Decimal(1_234_567);
2123 let bytes = value.to_bytes();
2124 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2125 assert_eq!(value, recovered);
2126 assert_eq!(size, bytes.len());
2127 assert_eq!(value.display_string(), "123.4567");
2128 }
2129
2130 #[test]
2131 fn test_value_enum_roundtrip() {
2132 let value = Value::EnumValue(3);
2133 let bytes = value.to_bytes();
2134 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2135 assert_eq!(value, recovered);
2136 assert_eq!(size, bytes.len());
2137 assert_eq!(value.display_string(), "enum(3)");
2138 }
2139
2140 #[test]
2141 fn test_value_array_roundtrip() {
2142 let value = Value::Array(vec![
2143 Value::Integer(1),
2144 Value::Integer(2),
2145 Value::Integer(3),
2146 ]);
2147 let bytes = value.to_bytes();
2148 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2149 assert_eq!(value, recovered);
2150 assert_eq!(size, bytes.len());
2151 }
2152
2153 #[test]
2154 fn test_value_array_nested_roundtrip() {
2155 let value = Value::Array(vec![
2156 Value::text("hello".to_string()),
2157 Value::text("world".to_string()),
2158 ]);
2159 let bytes = value.to_bytes();
2160 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2161 assert_eq!(value, recovered);
2162 assert_eq!(size, bytes.len());
2163 }
2164
2165 #[test]
2166 fn test_value_date_display_epoch() {
2167 let value = Value::Date(0);
2169 assert_eq!(value.display_string(), "1970-01-01");
2170 }
2171
2172 #[test]
2173 fn test_new_datatype_properties() {
2174 assert_eq!(DataType::Color.fixed_size(), Some(3));
2176 assert_eq!(DataType::Phone.fixed_size(), Some(8));
2177 assert_eq!(DataType::Semver.fixed_size(), Some(4));
2178 assert_eq!(DataType::Cidr.fixed_size(), Some(5));
2179 assert_eq!(DataType::Date.fixed_size(), Some(4));
2180 assert_eq!(DataType::Time.fixed_size(), Some(4));
2181 assert_eq!(DataType::Decimal.fixed_size(), Some(8));
2182 assert_eq!(DataType::Enum.fixed_size(), Some(1));
2183 assert_eq!(DataType::Email.fixed_size(), None);
2184 assert_eq!(DataType::Url.fixed_size(), None);
2185 assert_eq!(DataType::Array.fixed_size(), None);
2186
2187 assert!(DataType::Email.is_indexable());
2189 assert!(DataType::Date.is_indexable());
2190 assert!(DataType::Decimal.is_indexable());
2191 assert!(!DataType::Color.is_indexable());
2192 assert!(!DataType::Array.is_indexable());
2193
2194 assert!(DataType::Date.is_orderable());
2196 assert!(DataType::Time.is_orderable());
2197 assert!(DataType::Decimal.is_orderable());
2198 assert!(DataType::Semver.is_orderable());
2199 assert!(!DataType::Color.is_orderable());
2200 assert!(!DataType::Email.is_orderable());
2201 }
2202
2203 #[test]
2204 fn test_row_with_new_types() {
2205 let row = Row::new(vec![
2206 Value::Color([0xAA, 0xBB, 0xCC]),
2207 Value::Email("test@example.com".to_string()),
2208 Value::Phone(1234567890),
2209 Value::Semver(2_003_001), Value::Date(19738),
2211 Value::EnumValue(1),
2212 ]);
2213
2214 let bytes = row.to_bytes();
2215 let (recovered, size) = Row::from_bytes(&bytes).unwrap();
2216 assert_eq!(row, recovered);
2217 assert_eq!(size, bytes.len());
2218 }
2219
2220 #[test]
2223 fn test_value_timestamp_ms_roundtrip() {
2224 let value = Value::TimestampMs(1710510600123);
2225 let bytes = value.to_bytes();
2226 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2227 assert_eq!(value, recovered);
2228 assert_eq!(size, bytes.len());
2229 }
2230
2231 #[test]
2232 fn test_value_ipv4_roundtrip() {
2233 let ip = (192u32 << 24) | (168 << 16) | (1 << 8) | 1;
2234 let value = Value::Ipv4(ip);
2235 let bytes = value.to_bytes();
2236 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2237 assert_eq!(value, recovered);
2238 assert_eq!(size, bytes.len());
2239 assert_eq!(value.display_string(), "192.168.1.1");
2240 }
2241
2242 #[test]
2243 fn test_value_ipv6_roundtrip() {
2244 let mut octets = [0u8; 16];
2245 octets[15] = 1;
2246 let value = Value::Ipv6(octets);
2247 let bytes = value.to_bytes();
2248 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2249 assert_eq!(value, recovered);
2250 assert_eq!(size, bytes.len());
2251 assert_eq!(value.display_string(), "::1");
2252 }
2253
2254 #[test]
2255 fn test_value_subnet_roundtrip() {
2256 let ip = 10u32 << 24;
2257 let mask = !0u32 << 16; let value = Value::Subnet(ip, mask);
2259 let bytes = value.to_bytes();
2260 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2261 assert_eq!(value, recovered);
2262 assert_eq!(size, bytes.len());
2263 assert_eq!(value.display_string(), "10.0.0.0/16");
2264 }
2265
2266 #[test]
2267 fn test_value_port_roundtrip() {
2268 let value = Value::Port(8080);
2269 let bytes = value.to_bytes();
2270 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2271 assert_eq!(value, recovered);
2272 assert_eq!(size, bytes.len());
2273 assert_eq!(value.display_string(), "8080");
2274 }
2275
2276 #[test]
2277 fn test_value_latitude_roundtrip() {
2278 let value = Value::Latitude(-23550520);
2279 let bytes = value.to_bytes();
2280 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2281 assert_eq!(value, recovered);
2282 assert_eq!(size, bytes.len());
2283 assert_eq!(value.display_string(), "-23.550520");
2284 }
2285
2286 #[test]
2287 fn test_value_longitude_roundtrip() {
2288 let value = Value::Longitude(-46633308);
2289 let bytes = value.to_bytes();
2290 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2291 assert_eq!(value, recovered);
2292 assert_eq!(size, bytes.len());
2293 assert_eq!(value.display_string(), "-46.633308");
2294 }
2295
2296 #[test]
2297 fn test_value_geopoint_roundtrip() {
2298 let value = Value::GeoPoint(-23550520, -46633308);
2299 let bytes = value.to_bytes();
2300 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2301 assert_eq!(value, recovered);
2302 assert_eq!(size, bytes.len());
2303 }
2304
2305 #[test]
2306 fn test_value_country2_roundtrip() {
2307 let value = Value::Country2([b'B', b'R']);
2308 let bytes = value.to_bytes();
2309 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2310 assert_eq!(value, recovered);
2311 assert_eq!(size, bytes.len());
2312 assert_eq!(value.display_string(), "BR");
2313 }
2314
2315 #[test]
2316 fn test_value_country3_roundtrip() {
2317 let value = Value::Country3([b'B', b'R', b'A']);
2318 let bytes = value.to_bytes();
2319 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2320 assert_eq!(value, recovered);
2321 assert_eq!(size, bytes.len());
2322 assert_eq!(value.display_string(), "BRA");
2323 }
2324
2325 #[test]
2326 fn test_value_lang2_roundtrip() {
2327 let value = Value::Lang2([b'p', b't']);
2328 let bytes = value.to_bytes();
2329 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2330 assert_eq!(value, recovered);
2331 assert_eq!(size, bytes.len());
2332 assert_eq!(value.display_string(), "pt");
2333 }
2334
2335 #[test]
2336 fn test_value_lang5_roundtrip() {
2337 let value = Value::Lang5([b'p', b't', b'-', b'B', b'R']);
2338 let bytes = value.to_bytes();
2339 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2340 assert_eq!(value, recovered);
2341 assert_eq!(size, bytes.len());
2342 assert_eq!(value.display_string(), "pt-BR");
2343 }
2344
2345 #[test]
2346 fn test_value_currency_roundtrip() {
2347 let value = Value::Currency([b'U', b'S', b'D']);
2348 let bytes = value.to_bytes();
2349 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2350 assert_eq!(value, recovered);
2351 assert_eq!(size, bytes.len());
2352 assert_eq!(value.display_string(), "USD");
2353 }
2354
2355 #[test]
2356 fn test_value_color_alpha_roundtrip() {
2357 let value = Value::ColorAlpha([0xFF, 0x57, 0x33, 0x80]);
2358 let bytes = value.to_bytes();
2359 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2360 assert_eq!(value, recovered);
2361 assert_eq!(size, bytes.len());
2362 assert_eq!(value.display_string(), "#FF573380");
2363 }
2364
2365 #[test]
2366 fn test_value_bigint_roundtrip() {
2367 let value = Value::BigInt(i64::MAX);
2368 let bytes = value.to_bytes();
2369 let (recovered, size) = Value::from_bytes(&bytes).unwrap();
2370 assert_eq!(value, recovered);
2371 assert_eq!(size, bytes.len());
2372 }
2373
2374 #[test]
2375 fn test_new_datatype_roundtrip() {
2376 let types = [
2377 DataType::TimestampMs,
2378 DataType::Ipv4,
2379 DataType::Ipv6,
2380 DataType::Subnet,
2381 DataType::Port,
2382 DataType::Latitude,
2383 DataType::Longitude,
2384 DataType::GeoPoint,
2385 DataType::Country2,
2386 DataType::Country3,
2387 DataType::Lang2,
2388 DataType::Lang5,
2389 DataType::Currency,
2390 DataType::ColorAlpha,
2391 DataType::BigInt,
2392 ];
2393
2394 for dt in types {
2395 let byte = dt.to_byte();
2396 let recovered = DataType::from_byte(byte).unwrap();
2397 assert_eq!(dt, recovered);
2398 }
2399 }
2400
2401 #[test]
2402 fn test_rich_type_datatype_properties() {
2403 assert_eq!(DataType::TimestampMs.fixed_size(), Some(8));
2405 assert_eq!(DataType::Ipv4.fixed_size(), Some(4));
2406 assert_eq!(DataType::Ipv6.fixed_size(), Some(16));
2407 assert_eq!(DataType::Subnet.fixed_size(), Some(8));
2408 assert_eq!(DataType::Port.fixed_size(), Some(2));
2409 assert_eq!(DataType::Latitude.fixed_size(), Some(4));
2410 assert_eq!(DataType::Longitude.fixed_size(), Some(4));
2411 assert_eq!(DataType::GeoPoint.fixed_size(), Some(8));
2412 assert_eq!(DataType::Country2.fixed_size(), Some(2));
2413 assert_eq!(DataType::Country3.fixed_size(), Some(3));
2414 assert_eq!(DataType::Lang2.fixed_size(), Some(2));
2415 assert_eq!(DataType::Lang5.fixed_size(), Some(5));
2416 assert_eq!(DataType::Currency.fixed_size(), Some(3));
2417 assert_eq!(DataType::ColorAlpha.fixed_size(), Some(4));
2418 assert_eq!(DataType::BigInt.fixed_size(), Some(8));
2419
2420 assert!(DataType::TimestampMs.is_indexable());
2422 assert!(DataType::Ipv4.is_indexable());
2423 assert!(DataType::Ipv6.is_indexable());
2424 assert!(DataType::Port.is_indexable());
2425 assert!(DataType::Country2.is_indexable());
2426 assert!(DataType::Currency.is_indexable());
2427 assert!(DataType::BigInt.is_indexable());
2428 assert!(!DataType::Subnet.is_indexable());
2429 assert!(!DataType::ColorAlpha.is_indexable());
2430
2431 assert!(DataType::TimestampMs.is_orderable());
2433 assert!(DataType::Port.is_orderable());
2434 assert!(DataType::Latitude.is_orderable());
2435 assert!(DataType::Longitude.is_orderable());
2436 assert!(DataType::BigInt.is_orderable());
2437 assert!(!DataType::Country2.is_orderable());
2438 assert!(!DataType::Ipv4.is_orderable());
2439 }
2440
2441 #[test]
2442 fn test_row_with_all_new_types() {
2443 let row = Row::new(vec![
2444 Value::TimestampMs(1710510600123),
2445 Value::Ipv4((10u32 << 24) | 1),
2446 Value::Ipv6([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]),
2447 Value::Subnet(10u32 << 24, !0u32 << 16),
2448 Value::Port(443),
2449 Value::Latitude(-23550520),
2450 Value::Longitude(-46633308),
2451 Value::GeoPoint(-23550520, -46633308),
2452 Value::Country2([b'B', b'R']),
2453 Value::Country3([b'B', b'R', b'A']),
2454 Value::Lang2([b'p', b't']),
2455 Value::Lang5([b'p', b't', b'-', b'B', b'R']),
2456 Value::Currency([b'U', b'S', b'D']),
2457 Value::ColorAlpha([0xFF, 0x57, 0x33, 0x80]),
2458 Value::BigInt(i64::MAX),
2459 ]);
2460
2461 let bytes = row.to_bytes();
2462 let (recovered, size) = Row::from_bytes(&bytes).unwrap();
2463 assert_eq!(row, recovered);
2464 assert_eq!(size, bytes.len());
2465 }
2466
2467 #[test]
2468 fn sql_type_name_parses_modifiers_and_helpers() {
2469 let decimal = SqlTypeName::parse_declared("decimal(10, 2)");
2470 assert_eq!(decimal.base_name(), "DECIMAL");
2471 assert_eq!(decimal.decimal_precision(), Some(10));
2472 assert_eq!(decimal.to_string(), "DECIMAL(10,2)");
2473
2474 let enum_type = SqlTypeName::parse_declared("enum('red','blue')");
2475 assert_eq!(
2476 enum_type.enum_variants(),
2477 Some(vec!["red".to_string(), "blue".to_string()])
2478 );
2479
2480 let bad_enum = SqlTypeName::new("enum").with_modifiers(vec![TypeModifier::Number(1)]);
2481 assert_eq!(bad_enum.enum_variants(), None);
2482 assert_eq!(SqlTypeName::new("text").enum_variants(), None);
2483
2484 let array = SqlTypeName::parse_declared("array(varchar(12))");
2485 assert_eq!(array.array_element_type(), Some("VARCHAR(12)".to_string()));
2486 let array_ident =
2487 SqlTypeName::new("array").with_modifiers(vec![TypeModifier::Ident("int".to_string())]);
2488 assert_eq!(array_ident.array_element_type(), Some("INT".to_string()));
2489 assert_eq!(SqlTypeName::new("text").array_element_type(), None);
2490 assert_eq!(SqlTypeName::new("text").decimal_precision(), None);
2491
2492 assert_eq!(
2493 SqlTypeName::parse_declared("enum('a,b', array(int))").to_string(),
2494 "ENUM('a,b',ARRAY(INT))"
2495 );
2496 assert_eq!(SqlTypeName::parse_declared("").to_string(), "");
2497 }
2498
2499 #[test]
2500 fn datatype_category_preference_display_and_storage_traits_cover_all_variants() {
2501 let cases = [
2502 (
2503 DataType::Unknown,
2504 TypeCategory::Unknown,
2505 "UNKNOWN",
2506 None,
2507 false,
2508 false,
2509 ),
2510 (
2511 DataType::Integer,
2512 TypeCategory::Numeric,
2513 "INTEGER",
2514 Some(8),
2515 true,
2516 true,
2517 ),
2518 (
2519 DataType::UnsignedInteger,
2520 TypeCategory::Numeric,
2521 "UNSIGNED INTEGER",
2522 Some(8),
2523 true,
2524 true,
2525 ),
2526 (
2527 DataType::Float,
2528 TypeCategory::Numeric,
2529 "FLOAT",
2530 Some(8),
2531 true,
2532 true,
2533 ),
2534 (
2535 DataType::Text,
2536 TypeCategory::String,
2537 "TEXT",
2538 None,
2539 true,
2540 true,
2541 ),
2542 (
2543 DataType::Blob,
2544 TypeCategory::String,
2545 "BLOB",
2546 None,
2547 false,
2548 false,
2549 ),
2550 (
2551 DataType::Boolean,
2552 TypeCategory::Boolean,
2553 "BOOLEAN",
2554 Some(1),
2555 false,
2556 false,
2557 ),
2558 (
2559 DataType::Timestamp,
2560 TypeCategory::DateTime,
2561 "TIMESTAMP",
2562 Some(8),
2563 true,
2564 true,
2565 ),
2566 (
2567 DataType::Duration,
2568 TypeCategory::TimeSpan,
2569 "DURATION",
2570 Some(8),
2571 false,
2572 true,
2573 ),
2574 (
2575 DataType::IpAddr,
2576 TypeCategory::Network,
2577 "IPADDR",
2578 None,
2579 true,
2580 false,
2581 ),
2582 (
2583 DataType::MacAddr,
2584 TypeCategory::Network,
2585 "MACADDR",
2586 Some(6),
2587 false,
2588 false,
2589 ),
2590 (
2591 DataType::Vector,
2592 TypeCategory::Vector,
2593 "VECTOR",
2594 None,
2595 false,
2596 false,
2597 ),
2598 (
2599 DataType::Nullable,
2600 TypeCategory::Unknown,
2601 "NULLABLE",
2602 None,
2603 false,
2604 false,
2605 ),
2606 (
2607 DataType::Json,
2608 TypeCategory::Json,
2609 "JSON",
2610 None,
2611 false,
2612 false,
2613 ),
2614 (
2615 DataType::Uuid,
2616 TypeCategory::Uuid,
2617 "UUID",
2618 Some(16),
2619 true,
2620 false,
2621 ),
2622 (
2623 DataType::NodeRef,
2624 TypeCategory::Reference,
2625 "NODEREF",
2626 None,
2627 true,
2628 false,
2629 ),
2630 (
2631 DataType::EdgeRef,
2632 TypeCategory::Reference,
2633 "EDGEREF",
2634 None,
2635 true,
2636 false,
2637 ),
2638 (
2639 DataType::VectorRef,
2640 TypeCategory::Reference,
2641 "VECTORREF",
2642 Some(8),
2643 true,
2644 false,
2645 ),
2646 (
2647 DataType::RowRef,
2648 TypeCategory::Reference,
2649 "ROWREF",
2650 None,
2651 true,
2652 false,
2653 ),
2654 (
2655 DataType::Color,
2656 TypeCategory::Domain,
2657 "COLOR",
2658 Some(3),
2659 false,
2660 false,
2661 ),
2662 (
2663 DataType::Email,
2664 TypeCategory::Domain,
2665 "EMAIL",
2666 None,
2667 true,
2668 false,
2669 ),
2670 (
2671 DataType::Url,
2672 TypeCategory::Domain,
2673 "URL",
2674 None,
2675 true,
2676 false,
2677 ),
2678 (
2679 DataType::Phone,
2680 TypeCategory::Domain,
2681 "PHONE",
2682 Some(8),
2683 true,
2684 false,
2685 ),
2686 (
2687 DataType::Semver,
2688 TypeCategory::Domain,
2689 "SEMVER",
2690 Some(4),
2691 true,
2692 true,
2693 ),
2694 (
2695 DataType::Cidr,
2696 TypeCategory::Network,
2697 "CIDR",
2698 Some(5),
2699 false,
2700 false,
2701 ),
2702 (
2703 DataType::Date,
2704 TypeCategory::DateTime,
2705 "DATE",
2706 Some(4),
2707 true,
2708 true,
2709 ),
2710 (
2711 DataType::Time,
2712 TypeCategory::DateTime,
2713 "TIME",
2714 Some(4),
2715 true,
2716 true,
2717 ),
2718 (
2719 DataType::Decimal,
2720 TypeCategory::Numeric,
2721 "DECIMAL",
2722 Some(8),
2723 true,
2724 true,
2725 ),
2726 (
2727 DataType::Enum,
2728 TypeCategory::Domain,
2729 "ENUM",
2730 Some(1),
2731 true,
2732 false,
2733 ),
2734 (
2735 DataType::Array,
2736 TypeCategory::Array,
2737 "ARRAY",
2738 None,
2739 false,
2740 false,
2741 ),
2742 (
2743 DataType::TimestampMs,
2744 TypeCategory::DateTime,
2745 "TIMESTAMP_MS",
2746 Some(8),
2747 true,
2748 true,
2749 ),
2750 (
2751 DataType::Ipv4,
2752 TypeCategory::Network,
2753 "IPV4",
2754 Some(4),
2755 true,
2756 false,
2757 ),
2758 (
2759 DataType::Ipv6,
2760 TypeCategory::Network,
2761 "IPV6",
2762 Some(16),
2763 true,
2764 false,
2765 ),
2766 (
2767 DataType::Subnet,
2768 TypeCategory::Network,
2769 "SUBNET",
2770 Some(8),
2771 false,
2772 false,
2773 ),
2774 (
2775 DataType::Port,
2776 TypeCategory::Numeric,
2777 "PORT",
2778 Some(2),
2779 true,
2780 true,
2781 ),
2782 (
2783 DataType::Latitude,
2784 TypeCategory::Numeric,
2785 "LATITUDE",
2786 Some(4),
2787 true,
2788 true,
2789 ),
2790 (
2791 DataType::Longitude,
2792 TypeCategory::Numeric,
2793 "LONGITUDE",
2794 Some(4),
2795 true,
2796 true,
2797 ),
2798 (
2799 DataType::GeoPoint,
2800 TypeCategory::Geo,
2801 "GEOPOINT",
2802 Some(8),
2803 true,
2804 false,
2805 ),
2806 (
2807 DataType::Country2,
2808 TypeCategory::Domain,
2809 "COUNTRY2",
2810 Some(2),
2811 true,
2812 false,
2813 ),
2814 (
2815 DataType::Country3,
2816 TypeCategory::Domain,
2817 "COUNTRY3",
2818 Some(3),
2819 true,
2820 false,
2821 ),
2822 (
2823 DataType::Lang2,
2824 TypeCategory::Domain,
2825 "LANG2",
2826 Some(2),
2827 true,
2828 false,
2829 ),
2830 (
2831 DataType::Lang5,
2832 TypeCategory::Domain,
2833 "LANG5",
2834 Some(5),
2835 true,
2836 false,
2837 ),
2838 (
2839 DataType::Currency,
2840 TypeCategory::Domain,
2841 "CURRENCY",
2842 Some(3),
2843 true,
2844 false,
2845 ),
2846 (
2847 DataType::ColorAlpha,
2848 TypeCategory::Domain,
2849 "COLOR_ALPHA",
2850 Some(4),
2851 false,
2852 false,
2853 ),
2854 (
2855 DataType::BigInt,
2856 TypeCategory::Numeric,
2857 "BIGINT",
2858 Some(8),
2859 true,
2860 true,
2861 ),
2862 (
2863 DataType::KeyRef,
2864 TypeCategory::Reference,
2865 "KEY_REF",
2866 None,
2867 true,
2868 false,
2869 ),
2870 (
2871 DataType::DocRef,
2872 TypeCategory::Reference,
2873 "DOC_REF",
2874 None,
2875 true,
2876 false,
2877 ),
2878 (
2879 DataType::TableRef,
2880 TypeCategory::Reference,
2881 "TABLE_REF",
2882 None,
2883 true,
2884 false,
2885 ),
2886 (
2887 DataType::PageRef,
2888 TypeCategory::Reference,
2889 "PAGE_REF",
2890 Some(4),
2891 true,
2892 false,
2893 ),
2894 (
2895 DataType::Secret,
2896 TypeCategory::Opaque,
2897 "SECRET",
2898 None,
2899 false,
2900 false,
2901 ),
2902 (
2903 DataType::Password,
2904 TypeCategory::Opaque,
2905 "PASSWORD",
2906 None,
2907 false,
2908 false,
2909 ),
2910 (
2911 DataType::TextZstd,
2912 TypeCategory::String,
2913 "TEXT",
2914 None,
2915 false,
2916 false,
2917 ),
2918 (
2919 DataType::BlobZstd,
2920 TypeCategory::String,
2921 "BLOB",
2922 None,
2923 false,
2924 false,
2925 ),
2926 (
2927 DataType::AssetCode,
2928 TypeCategory::Domain,
2929 "ASSET_CODE",
2930 None,
2931 true,
2932 true,
2933 ),
2934 (
2935 DataType::Money,
2936 TypeCategory::Domain,
2937 "MONEY",
2938 None,
2939 false,
2940 false,
2941 ),
2942 (
2943 DataType::DecimalText,
2944 TypeCategory::Numeric,
2945 "DECIMAL_TEXT",
2946 None,
2947 true,
2948 true,
2949 ),
2950 ];
2951
2952 for (data_type, category, display, fixed, indexable, orderable) in cases {
2953 assert_eq!(data_type.category(), category, "{data_type:?}");
2954 assert_eq!(data_type.to_string(), display);
2955 assert_eq!(data_type.fixed_size(), fixed, "{data_type:?}");
2956 assert_eq!(data_type.is_indexable(), indexable, "{data_type:?}");
2957 assert_eq!(data_type.is_orderable(), orderable, "{data_type:?}");
2958 }
2959
2960 for preferred in [
2961 DataType::Float,
2962 DataType::Text,
2963 DataType::TimestampMs,
2964 DataType::IpAddr,
2965 DataType::Boolean,
2966 DataType::Uuid,
2967 ] {
2968 assert!(preferred.is_preferred(), "{preferred:?}");
2969 }
2970 assert!(!DataType::Integer.is_preferred());
2971 }
2972
2973 #[test]
2974 fn sql_aliases_cover_domain_reference_and_unknown_paths() {
2975 let aliases = [
2976 ("BOOL", DataType::Boolean),
2977 ("SMALLINT", DataType::Integer),
2978 ("BIGSERIAL", DataType::BigInt),
2979 ("UNSIGNED INTEGER", DataType::UnsignedInteger),
2980 ("DOUBLE", DataType::Float),
2981 ("VARCHAR(20)", DataType::Text),
2982 ("BYTEA", DataType::Blob),
2983 ("TIMESTAMP_MS", DataType::TimestampMs),
2984 ("INTERVAL", DataType::Duration),
2985 ("NUMERIC(10,2)", DataType::Decimal),
2986 ("JSONB", DataType::Json),
2987 ("INET", DataType::IpAddr),
2988 ("COLOR_ALPHA", DataType::ColorAlpha),
2989 ("GEO_POINT", DataType::GeoPoint),
2990 ("ASSET_CODE", DataType::AssetCode),
2991 ("DECIMAL_TEXT", DataType::DecimalText),
2992 ("KEYREF", DataType::KeyRef),
2993 ("DOCREF", DataType::DocRef),
2994 ("TABLEREF", DataType::TableRef),
2995 ("PAGEREF", DataType::PageRef),
2996 ("PASSWORD", DataType::Password),
2997 ("VECTOR", DataType::Vector),
2998 ];
2999
3000 for (alias, expected) in aliases {
3001 assert_eq!(DataType::from_sql_name(alias), Some(expected), "{alias}");
3002 }
3003 assert_eq!(DataType::from_sql_name("definitely_not_a_type"), None);
3004 assert_eq!(DataType::from_byte(0), None);
3005 assert_eq!(DataType::from_byte(56), None);
3006 }
3007
3008 #[test]
3009 fn value_accessors_hash_and_display_cover_remaining_variants() {
3010 let values = vec![
3011 Value::Null,
3012 Value::Integer(-1),
3013 Value::UnsignedInteger(2),
3014 Value::Float(3.5),
3015 Value::text("hello"),
3016 Value::Blob(vec![1, 2, 3]),
3017 Value::Boolean(true),
3018 Value::Timestamp(4),
3019 Value::Duration(5),
3020 Value::IpAddr(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
3021 Value::IpAddr(IpAddr::V6(Ipv6Addr::LOCALHOST)),
3022 Value::MacAddr([1, 2, 3, 4, 5, 6]),
3023 Value::Vector(vec![1.0, 2.0]),
3024 Value::Json(br#"{"ok":true}"#.to_vec()),
3025 Value::Uuid([7; 16]),
3026 Value::NodeRef("node".to_string()),
3027 Value::EdgeRef("edge".to_string()),
3028 Value::VectorRef("vectors".to_string(), 8),
3029 Value::RowRef("rows".to_string(), 9),
3030 Value::Color([0xAA, 0xBB, 0xCC]),
3031 Value::Email("a@example.com".to_string()),
3032 Value::Url("https://example.com".to_string()),
3033 Value::Phone(5511999),
3034 Value::Semver(1_002_003),
3035 Value::Cidr(10 << 24, 8),
3036 Value::Date(20_000),
3037 Value::Time(43_200_000),
3038 Value::Decimal(123_456),
3039 Value::DecimalText("3.14159265358979323846".to_string()),
3040 Value::EnumValue(3),
3041 Value::Array(vec![Value::Integer(1), Value::text("two")]),
3042 Value::TimestampMs(123_456),
3043 Value::Ipv4(0x7f000001),
3044 Value::Ipv6([1; 16]),
3045 Value::Subnet(10 << 24, 0xff00ff00),
3046 Value::Port(5432),
3047 Value::Latitude(-23_550_520),
3048 Value::Longitude(-46_633_308),
3049 Value::GeoPoint(-23_550_520, -46_633_308),
3050 Value::Country2(*b"BR"),
3051 Value::Country3(*b"BRA"),
3052 Value::Lang2(*b"pt"),
3053 Value::Lang5(*b"pt-BR"),
3054 Value::Currency(*b"USD"),
3055 Value::AssetCode("BTC".to_string()),
3056 Value::Money {
3057 asset_code: "USD".to_string(),
3058 minor_units: -1234,
3059 scale: 2,
3060 },
3061 Value::ColorAlpha([1, 2, 3, 4]),
3062 Value::BigInt(-10),
3063 Value::KeyRef("kv".to_string(), "key".to_string()),
3064 Value::DocRef("docs".to_string(), 42),
3065 Value::TableRef("users".to_string()),
3066 Value::PageRef(99),
3067 Value::Secret(vec![9, 8, 7]),
3068 Value::Password("$argon2id$v=19$hash".to_string()),
3069 ];
3070
3071 for value in &values {
3072 let mut hasher = DefaultHasher::new();
3073 value.hash(&mut hasher);
3074 let _ = hasher.finish();
3075 assert_eq!(value.data_type(), value.data_type());
3076 assert!(!value.display_string().is_empty());
3077 assert!(!value.plain_text().is_empty());
3078 }
3079
3080 assert!(Value::Null.is_null());
3081 assert_eq!(Value::Integer(-1).as_integer(), Some(-1));
3082 assert_eq!(
3083 Value::UnsignedInteger(i64::MAX as u64).as_integer(),
3084 Some(i64::MAX)
3085 );
3086 assert_eq!(
3087 Value::UnsignedInteger(i64::MAX as u64 + 1).as_integer(),
3088 None
3089 );
3090 assert_eq!(Value::Timestamp(1).as_integer(), Some(1));
3091 assert_eq!(Value::Duration(2).as_integer(), Some(2));
3092 assert_eq!(Value::Float(1.5).as_float(), Some(1.5));
3093 assert_eq!(Value::Integer(2).as_float(), Some(2.0));
3094 assert_eq!(Value::UnsignedInteger(3).as_float(), Some(3.0));
3095 assert_eq!(Value::text("x").as_text(), Some("x"));
3096 assert_eq!(Value::Boolean(true).as_boolean(), Some(true));
3097 assert_eq!(
3098 Value::IpAddr(IpAddr::V4(Ipv4Addr::LOCALHOST)).as_ip_addr(),
3099 Some(IpAddr::V4(Ipv4Addr::LOCALHOST))
3100 );
3101 assert_eq!(Value::Vector(vec![1.0]).as_vector(), Some(&[1.0][..]));
3102 assert_eq!(Value::Null.as_integer(), None);
3103 assert_eq!(Value::Null.as_float(), None);
3104 assert_eq!(Value::Null.as_text(), None);
3105 assert_eq!(Value::Null.as_boolean(), None);
3106 assert_eq!(Value::Null.as_ip_addr(), None);
3107 assert_eq!(Value::Null.as_vector(), None);
3108 }
3109
3110 #[test]
3111 fn row_accessors_iteration_and_error_paths() {
3112 let empty = Row::new(Vec::new());
3113 assert!(empty.is_empty());
3114 assert_eq!(empty.len(), 0);
3115 assert_eq!(empty.get(0), None);
3116
3117 let row = Row::from(vec![Value::Integer(1), Value::text("two")]);
3118 assert_eq!(row.len(), 2);
3119 assert_eq!(row.get(1), Some(&Value::text("two")));
3120 assert_eq!(row.values().len(), 2);
3121 assert_eq!(row.iter().count(), 2);
3122 assert_eq!(row.clone().into_values().len(), 2);
3123 assert_eq!(row.into_iter().count(), 2);
3124
3125 assert_eq!(Row::from_bytes(&[]).unwrap_err(), ValueError::EmptyData);
3126 assert_eq!(
3127 Row::from_bytes(&[0x80]).unwrap_err(),
3128 ValueError::TruncatedData
3129 );
3130 }
3131
3132 #[test]
3133 fn value_error_display_covers_every_variant() {
3134 let errors = [
3135 (ValueError::EmptyData, "empty data"),
3136 (ValueError::InvalidType(99), "invalid type byte: 99"),
3137 (ValueError::TruncatedData, "truncated data"),
3138 (ValueError::InvalidUtf8, "invalid UTF-8"),
3139 (ValueError::InvalidIpVersion(5), "invalid IP version: 5"),
3140 (ValueError::VarintOverflow, "varint overflow"),
3141 (
3142 ValueError::TypeMismatch {
3143 expected: DataType::Text,
3144 found: DataType::Integer,
3145 },
3146 "type mismatch: expected TEXT, found INTEGER",
3147 ),
3148 ];
3149
3150 for (error, message) in errors {
3151 assert_eq!(error.to_string(), message);
3152 }
3153 }
3154}