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