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