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