1pub(crate) mod array;
2pub(crate) mod geo;
3pub(crate) mod low_cardinality;
4pub(crate) mod map;
5pub(crate) mod nullable;
6pub(crate) mod object;
7pub(crate) mod sized;
8pub(crate) mod string;
9pub(crate) mod tuple;
10
11use super::low_cardinality::LOW_CARDINALITY_VERSION;
12use super::*;
13use crate::io::ClickHouseBytesRead;
14
15pub(crate) trait ClickHouseNativeDeserializer {
17 fn deserialize_prefix_async<'a, R: ClickHouseRead>(
18 &'a self,
19 reader: &'a mut R,
20 state: &'a mut DeserializerState,
21 ) -> impl Future<Output = Result<()>> + Send + 'a;
22
23 #[allow(dead_code)] fn deserialize_prefix<R: ClickHouseBytesRead>(&self, reader: &mut R) -> Result<()>;
25}
26
27impl ClickHouseNativeDeserializer for Type {
28 fn deserialize_prefix_async<'a, R: ClickHouseRead>(
29 &'a self,
30 reader: &'a mut R,
31 state: &'a mut DeserializerState,
32 ) -> impl Future<Output = Result<()>> + Send + 'a {
33 use deserialize::*;
34 async move {
35 match self {
36 Type::Int8
37 | Type::Int16
38 | Type::Int32
39 | Type::Int64
40 | Type::Int128
41 | Type::Int256
42 | Type::UInt8
43 | Type::UInt16
44 | Type::UInt32
45 | Type::UInt64
46 | Type::UInt128
47 | Type::UInt256
48 | Type::Float32
49 | Type::Float64
50 | Type::Decimal32(_)
51 | Type::Decimal64(_)
52 | Type::Decimal128(_)
53 | Type::Decimal256(_)
54 | Type::Uuid
55 | Type::Date
56 | Type::Date32
57 | Type::DateTime(_)
58 | Type::DateTime64(_, _)
59 | Type::Ipv4
60 | Type::Ipv6
61 | Type::Enum8(_)
62 | Type::Enum16(_) => {
63 sized::SizedDeserializer::read_prefix(self, reader, state).await?;
64 }
65
66 Type::String
67 | Type::FixedSizedString(_)
68 | Type::Binary
69 | Type::FixedSizedBinary(_) => {
70 string::StringDeserializer::read_prefix(self, reader, state).await?;
71 }
72
73 Type::Array(_) => {
74 array::ArrayDeserializer::read_prefix(self, reader, state).await?;
75 }
76 Type::Tuple(_) => {
77 tuple::TupleDeserializer::read_prefix(self, reader, state).await?;
78 }
79 Type::Point => geo::PointDeserializer::read_prefix(self, reader, state).await?,
80 Type::Ring => geo::RingDeserializer::read_prefix(self, reader, state).await?,
81 Type::Polygon => geo::PolygonDeserializer::read_prefix(self, reader, state).await?,
82 Type::MultiPolygon => {
83 geo::MultiPolygonDeserializer::read_prefix(self, reader, state).await?;
84 }
85 Type::Nullable(_) => {
86 nullable::NullableDeserializer::read_prefix(self, reader, state).await?;
87 }
88 Type::Map(_, _) => map::MapDeserializer::read_prefix(self, reader, state).await?,
89 Type::LowCardinality(_) => {
90 low_cardinality::LowCardinalityDeserializer::read_prefix(self, reader, state)
91 .await?;
92 }
93 Type::Object => {
94 object::ObjectDeserializer::read_prefix(self, reader, state).await?;
95 }
96 }
97 Ok(())
98 }
99 .boxed()
100 }
101
102 #[allow(dead_code)] fn deserialize_prefix<R: ClickHouseBytesRead>(&self, reader: &mut R) -> Result<()> {
104 match self {
105 Type::Array(inner) | Type::Nullable(inner) => inner.deserialize_prefix(reader)?,
106 Type::Point => {
107 for _ in 0..2 {
108 Type::Float64.deserialize_prefix(reader)?;
109 }
110 }
111 Type::LowCardinality(_) => {
112 let version = reader.try_get_u64_le()?;
113 if version != LOW_CARDINALITY_VERSION {
114 return Err(Error::DeserializeError(format!(
115 "LowCardinality: invalid low cardinality version: {version}"
116 )));
117 }
118 }
119 Type::Map(key, value) => {
120 let nested = super::map::normalize_map_type(key, value);
121 nested.deserialize_prefix(reader)?;
122 }
123 Type::Tuple(inner) => {
124 for inner_type in inner {
125 inner_type.deserialize_prefix(reader)?;
126 }
127 }
128 Type::Object => {
129 let _ = reader.try_get_i8()?;
130 }
131 _ => {}
132 }
133 Ok(())
134 }
135}
136
137pub(crate) const DAYS_1900_TO_1970: i32 = 25_567;
143
144trait EnumValueType: FromStr + std::fmt::Debug {}
145impl EnumValueType for i8 {}
146impl EnumValueType for i16 {}
147
148macro_rules! parse_enum_options {
149 ($opt_str:expr, $num_type:ty) => {{
150 fn inner_parse(input: &str) -> Result<Vec<(String, $num_type)>> {
151 if !input.starts_with('(') || !input.ends_with(')') {
152 return Err(Error::TypeParseError(
153 "Enum arguments must be enclosed in parentheses".to_string(),
154 ));
155 }
156
157 let input = input[1..input.len() - 1].trim();
158 if input.is_empty() {
159 return Ok(Vec::new());
160 }
161
162 let mut options = Vec::new();
163 let mut name = String::new();
164 let mut value = String::new();
165 let mut state = EnumParseState::ExpectQuote;
166 let mut escaped = false;
167
168 for ch in input.chars() {
169 match state {
170 EnumParseState::ExpectQuote => {
171 if ch == '\'' {
172 state = EnumParseState::InName;
173 } else if !ch.is_whitespace() {
174 return Err(Error::TypeParseError(format!(
175 "Expected single quote at start of variant name, found '{}'",
176 ch
177 )));
178 }
179 }
180 EnumParseState::InName => {
181 if escaped {
182 name.push(ch);
183 escaped = false;
184 } else if ch == '\\' {
185 escaped = true;
186 } else if ch == '\'' {
187 state = EnumParseState::ExpectEqual;
188 } else {
189 name.push(ch);
190 }
191 }
192 EnumParseState::ExpectEqual => {
193 if ch == '=' {
194 state = EnumParseState::InValue;
195 } else if !ch.is_whitespace() {
196 return Err(Error::TypeParseError(format!(
197 "Expected '=' after variant name, found '{}'",
198 ch
199 )));
200 }
201 }
202 EnumParseState::InValue => {
203 if ch == ',' {
204 let parsed_value = value.parse::<$num_type>().map_err(|e| {
205 Error::TypeParseError(format!("Invalid enum value '{value}': {e}"))
206 })?;
207 options.push((name, parsed_value));
208 name = String::new();
209 value = String::new();
210 state = EnumParseState::ExpectQuote;
211 } else if !ch.is_whitespace() {
212 value.push(ch);
213 }
214 }
215 }
216 }
217
218 match state {
219 EnumParseState::InValue if !value.is_empty() => {
220 let parsed_value = value.parse::<$num_type>().map_err(|e| {
221 Error::TypeParseError(format!("Invalid enum value '{value}': {e}"))
222 })?;
223 options.push((name, parsed_value));
224 }
225 EnumParseState::ExpectQuote if !input.is_empty() => {
226 return Err(Error::TypeParseError(
227 "Expected enum variant, found end of input".to_string(),
228 ));
229 }
230 EnumParseState::InName | EnumParseState::ExpectEqual => {
231 return Err(Error::TypeParseError(
232 "Incomplete enum variant at end of input".to_string(),
233 ));
234 }
235 _ => {}
236 }
237
238 if input.ends_with(',') {
239 return Err(Error::TypeParseError("Trailing comma in enum variants".to_string()));
240 }
241
242 Ok(options)
243 }
244
245 fn assert_numeric_type<T: EnumValueType>() {}
246 assert_numeric_type::<$num_type>();
247 inner_parse($opt_str)
248 }};
249}
250
251#[derive(PartialEq)]
252enum EnumParseState {
253 ExpectQuote,
254 InName,
255 ExpectEqual,
256 InValue,
257}
258
259impl FromStr for Type {
260 type Err = Error;
261
262 #[expect(clippy::too_many_lines)]
263 fn from_str(s: &str) -> Result<Self> {
264 let (ident, following) = eat_identifier(s);
265
266 if ident.is_empty() {
267 return Err(Error::TypeParseError(format!("invalid empty identifier for type: '{s}'")));
268 }
269
270 let following = following.trim();
271 if !following.is_empty() {
272 return Ok(match ident {
273 "Decimal" => {
274 let (args, count) = parse_fixed_args::<2>(following)?;
275 if count != 2 {
276 return Err(Error::TypeParseError(format!(
277 "Decimal expects 2 args, got {count}: {args:?}"
278 )));
279 }
280 let p: usize = parse_precision(args[0])?;
281 let s: usize = parse_scale(args[1])?;
282 if s == 0
283 || (p <= 9 && s > 9)
284 || (p <= 18 && s > 18)
285 || (p <= 38 && s > 38)
286 || (p <= 76 && s > 76)
287 {
288 return Err(Error::TypeParseError(format!(
289 "Invalid scale {s} for precision {p}"
290 )));
291 }
292 if p <= 9 {
293 Type::Decimal32(s)
294 } else if p <= 18 {
295 Type::Decimal64(s)
296 } else if p <= 38 {
297 Type::Decimal128(s)
298 } else if p <= 76 {
299 Type::Decimal256(s)
300 } else {
301 return Err(Error::TypeParseError(
302 "bad decimal spec, cannot exceed 76 precision".to_string(),
303 ));
304 }
305 }
306 "Decimal32" => {
307 let (args, count) = parse_fixed_args::<1>(following)?;
308 if count != 1 {
309 return Err(Error::TypeParseError(format!(
310 "bad arg count for Decimal32, expected 1 and got {count}: {args:?}"
311 )));
312 }
313 let s: usize = parse_scale(args[0])?;
314 if s == 0 || s > 9 {
315 return Err(Error::TypeParseError(format!(
316 "Invalid scale {s} for Decimal32, must be 1..=9"
317 )));
318 }
319 Type::Decimal32(s)
320 }
321 "Decimal64" => {
322 let (args, count) = parse_fixed_args::<1>(following)?;
323 if count != 1 {
324 return Err(Error::TypeParseError(format!(
325 "bad arg count for Decimal64, expected 1 and got {count}: {args:?}"
326 )));
327 }
328 let s: usize = parse_scale(args[0])?;
329 if s == 0 || s > 18 {
330 return Err(Error::TypeParseError(format!(
331 "Invalid scale {s} for Decimal64, must be 1..=18"
332 )));
333 }
334 Type::Decimal64(s)
335 }
336 "Decimal128" => {
337 let (args, count) = parse_fixed_args::<1>(following)?;
338 if count != 1 {
339 return Err(Error::TypeParseError(format!(
340 "bad arg count for Decimal128, expected 1 and got {count}: {args:?}"
341 )));
342 }
343 let s: usize = parse_scale(args[0])?;
344 if s == 0 || s > 38 {
345 return Err(Error::TypeParseError(format!(
346 "Invalid scale {s} for Decimal128, must be 1..=38"
347 )));
348 }
349 Type::Decimal128(s)
350 }
351 "Decimal256" => {
352 let (args, count) = parse_fixed_args::<1>(following)?;
353 if count != 1 {
354 return Err(Error::TypeParseError(format!(
355 "bad arg count for Decimal256, expected 1 and got {count}: {args:?}"
356 )));
357 }
358 let s: usize = parse_scale(args[0])?;
359 if s == 0 || s > 76 {
360 return Err(Error::TypeParseError(format!(
361 "Invalid scale {s} for Decimal256, must be 1..=76"
362 )));
363 }
364 Type::Decimal256(s)
365 }
366 "FixedString" => {
367 let (args, count) = parse_fixed_args::<1>(following)?;
368 if count != 1 {
369 return Err(Error::TypeParseError(format!(
370 "bad arg count for FixedString, expected 1 and got {count}: {args:?}"
371 )));
372 }
373 let s: usize = parse_scale(args[0])?;
374 if s == 0 {
375 return Err(Error::TypeParseError(
376 "FixedString size must be greater than 0".to_string(),
377 ));
378 }
379 Type::FixedSizedString(s)
380 }
381 "DateTime" => {
382 let (args, count) = parse_fixed_args::<1>(following)?;
383 if count > 1 {
384 return Err(Error::TypeParseError(format!(
385 "DateTime expects 0 or 1 arg: {args:?}"
386 )));
387 }
388 if count == 0 {
389 Type::DateTime(chrono_tz::UTC)
390 } else {
391 let tz_str = args[0];
392 if !tz_str.starts_with('\'') || !tz_str.ends_with('\'') {
393 return Err(Error::TypeParseError(format!(
394 "DateTime timezone must be quoted: '{tz_str}'"
395 )));
396 }
397 let tz = tz_str[1..tz_str.len() - 1].parse().map_err(|e| {
398 Error::TypeParseError(format!(
399 "failed to parse timezone '{tz_str}': {e}"
400 ))
401 })?;
402 Type::DateTime(tz)
403 }
404 }
405 "DateTime64" => {
406 let (args, count) = parse_fixed_args::<2>(following)?;
407 if !(1..=2).contains(&count) {
408 return Err(Error::TypeParseError(format!(
409 "DateTime64 expects 1 or 2 args, got {count}: {args:?}"
410 )));
411 }
412 let precision = parse_precision(args[0])?;
413 let tz = if count == 2 {
414 let tz_str = args[1];
415 if !tz_str.starts_with('\'') || !tz_str.ends_with('\'') {
416 return Err(Error::TypeParseError(format!(
417 "DateTime64 timezone must be quoted: '{tz_str}'"
418 )));
419 }
420 tz_str[1..tz_str.len() - 1].parse().map_err(|e| {
421 Error::TypeParseError(format!(
422 "failed to parse timezone '{tz_str}': {e}"
423 ))
424 })?
425 } else {
426 chrono_tz::UTC
427 };
428 Type::DateTime64(precision, tz)
429 }
430 "Enum8" => Type::Enum8(parse_enum_options!(following, i8)?),
431 "Enum16" => Type::Enum16(parse_enum_options!(following, i16)?),
432 "LowCardinality" => {
433 let (args, count) = parse_fixed_args::<1>(following)?;
434 if count != 1 {
435 return Err(Error::TypeParseError(format!(
436 "LowCardinality expected 1 arg and got {count}: {args:?}"
437 )));
438 }
439 Type::LowCardinality(Box::new(Type::from_str(args[0])?))
440 }
441 "Array" => {
442 let (args, count) = parse_fixed_args::<1>(following)?;
443 if count != 1 {
444 return Err(Error::TypeParseError(format!(
445 "Array expected 1 arg and got {count}: {args:?}"
446 )));
447 }
448 Type::Array(Box::new(Type::from_str(args[0])?))
449 }
450 "Tuple" => {
451 let args = parse_variable_args(following)?;
452 let inner: Vec<Type> = args
453 .into_iter()
454 .map(strip_tuple_field_name)
456 .map(Type::from_str)
457 .collect::<Result<_, _>>()?;
458 Type::Tuple(inner)
459 }
460 "Nullable" => {
461 let (args, count) = parse_fixed_args::<1>(following)?;
462 if count != 1 {
463 return Err(Error::TypeParseError(format!(
464 "Nullable expects 1 arg: {args:?}"
465 )));
466 }
467 Type::Nullable(Box::new(Type::from_str(args[0])?))
468 }
469 "Map" => {
470 let (args, count) = parse_fixed_args::<2>(following)?;
471 if count != 2 {
472 return Err(Error::TypeParseError(format!(
473 "Map expects 2 args, got {count}: {args:?}"
474 )));
475 }
476 Type::Map(
477 Box::new(Type::from_str(args[0])?),
478 Box::new(Type::from_str(args[1])?),
479 )
480 }
481 "Nested" => {
483 return Err(Error::TypeParseError("unsupported Nested type".to_string()));
484 }
485 id => {
486 return Err(Error::TypeParseError(format!(
487 "invalid type with arguments: '{ident}' (ident = {id})"
488 )));
489 }
490 });
491 }
492 Ok(match ident {
493 "Int8" => Type::Int8,
494 "Int16" => Type::Int16,
495 "Int32" => Type::Int32,
496 "Int64" => Type::Int64,
497 "Int128" => Type::Int128,
498 "Int256" => Type::Int256,
499 "Bool" | "UInt8" => Type::UInt8,
500 "UInt16" => Type::UInt16,
501 "UInt32" => Type::UInt32,
502 "UInt64" => Type::UInt64,
503 "UInt128" => Type::UInt128,
504 "UInt256" => Type::UInt256,
505 "Float32" => Type::Float32,
506 "Float64" => Type::Float64,
507 "String" => Type::String,
508 "UUID" | "Uuid" | "uuid" => Type::Uuid,
509 "Date" => Type::Date,
510 "Date32" => Type::Date32,
511 "DateTime" => Type::DateTime(chrono_tz::UTC),
514 "IPv4" => Type::Ipv4,
515 "IPv6" => Type::Ipv6,
516 "Point" => Type::Point,
517 "Ring" => Type::Ring,
518 "Polygon" => Type::Polygon,
519 "MultiPolygon" => Type::MultiPolygon,
520 "Object" | "Json" | "OBJECT" | "JSON" => Type::Object,
521 _ => {
522 return Err(Error::TypeParseError(format!("invalid type name: '{ident}'")));
523 }
524 })
525 }
526}
527
528fn eat_identifier(input: &str) -> (&str, &str) {
530 for (i, c) in input.char_indices() {
531 if c.is_alphabetic() || c == '_' || c == '$' || (i > 0 && c.is_numeric()) {
532 continue;
533 }
534 return (&input[..i], &input[i..]);
535 }
536 (input, "")
537}
538
539fn strip_tuple_field_name(arg: &str) -> &str {
548 let arg = arg.trim();
549 if let Some(space_idx) = arg.find(' ') {
550 let before_space = &arg[..space_idx];
551 if before_space.contains('(') {
554 return arg;
555 }
556 let rest = arg[space_idx..].trim_start();
557 if rest.chars().next().is_some_and(char::is_alphabetic) {
558 return rest;
559 }
560 }
561 arg
562}
563
564fn parse_fixed_args<const N: usize>(input: &str) -> Result<([&str; N], usize)> {
566 let mut iter = parse_args_iter(input)?;
567 let mut out = [""; N];
568 let mut count = 0;
569
570 for (i, arg_result) in iter.by_ref().take(N).enumerate() {
572 out[i] = arg_result?;
573 count += 1;
574 }
575
576 if iter.next().is_some() {
578 return Err(Error::TypeParseError("too many arguments".to_string()));
579 }
580 Ok((out, count))
581}
582
583fn parse_variable_args(input: &str) -> Result<Vec<&str>> { parse_args_iter(input)?.collect() }
585
586fn parse_scale(from: &str) -> Result<usize> {
587 from.parse().map_err(|_| Error::TypeParseError("couldn't parse scale".to_string()))
588}
589
590fn parse_precision(from: &str) -> Result<usize> {
591 from.parse().map_err(|_| Error::TypeParseError("could not parse precision".to_string()))
592}
593
594fn parse_args_iter(input: &str) -> Result<impl Iterator<Item = Result<&str, Error>>> {
596 if !input.starts_with('(') || !input.ends_with(')') {
597 return Err(Error::TypeParseError("Malformed arguments to type".to_string()));
598 }
599 let input = input[1..input.len() - 1].trim();
600 if input.ends_with(',') {
601 return Err(Error::TypeParseError("Trailing comma in argument list".to_string()));
602 }
603
604 Ok(ArgsIterator { input, last_start: 0, in_parens: 0, in_quotes: false, done: false })
605}
606
607struct ArgsIterator<'a> {
608 input: &'a str,
609 last_start: usize,
610 in_parens: usize,
611 in_quotes: bool,
612 done: bool,
613}
614
615impl<'a> Iterator for ArgsIterator<'a> {
616 type Item = Result<&'a str, Error>;
617
618 #[allow(unused_assignments)]
619 fn next(&mut self) -> Option<Self::Item> {
620 if self.done {
621 return None;
622 }
623
624 let start = self.last_start;
625 let mut i = start;
626 let chars = self.input[start..].char_indices();
627 let mut escaped = false;
628
629 for (offset, c) in chars {
630 i = start + offset;
631 if self.in_quotes {
632 if c == '\\' {
633 escaped = true;
634 continue;
635 }
636 if c == '\'' && !escaped {
637 self.in_quotes = false;
638 }
639 escaped = false;
640 continue;
641 }
642 match c {
643 '\'' if !escaped => {
644 self.in_quotes = true;
645 }
646 '(' => self.in_parens += 1,
647 ')' => self.in_parens -= 1,
648 ',' if self.in_parens == 0 => {
649 let slice = self.input[self.last_start..i].trim();
650 if slice.is_empty() {
651 return Some(Err(Error::TypeParseError(
652 "Empty argument in list".to_string(),
653 )));
654 }
655 self.last_start = i + 1;
656 return Some(Ok(slice));
657 }
658 _ => {}
659 }
660 escaped = false;
661 }
662
663 if self.in_parens != 0 {
664 self.done = true;
665 return Some(Err(Error::TypeParseError("Mismatched parentheses".to_string())));
666 }
667 if self.last_start <= self.input.len() {
668 let slice = self.input[self.last_start..].trim();
669 if slice.is_empty() {
670 self.done = true;
671 return None; }
673 if slice == "," {
674 self.done = true;
675 return Some(Err(Error::TypeParseError(
676 "Trailing comma in argument list".to_string(),
677 )));
678 }
679 self.done = true;
680 return Some(Ok(slice));
681 }
682
683 self.done = true;
684 None
685 }
686}
687
688#[cfg(test)]
689mod tests {
690 use std::str::FromStr;
691
692 use super::*;
693 #[test]
695 fn test_eat_identifier() {
696 assert_eq!(eat_identifier("Int8"), ("Int8", ""));
697 assert_eq!(eat_identifier("Enum8('a'=1)"), ("Enum8", "('a'=1)"));
698 assert_eq!(eat_identifier("DateTime('UTC')"), ("DateTime", "('UTC')"));
699 assert_eq!(eat_identifier("Map(String,Int32)"), ("Map", "(String,Int32)"));
700 assert_eq!(eat_identifier(""), ("", ""));
701 assert_eq!(eat_identifier("Invalid Type"), ("Invalid", " Type"));
702 }
703
704 #[test]
706 fn test_parse_fixed_args() {
707 let (args, count) = parse_fixed_args::<2>("(UInt32, String)").unwrap();
708 assert_eq!(count, 2);
709 assert_eq!(args[..count], ["UInt32", "String"]);
710
711 let (args, count) = parse_fixed_args::<1>("(String)").unwrap();
712 assert_eq!(count, 1);
713 assert_eq!(args[..count], ["String"]);
714
715 let (args, count) = parse_fixed_args::<2>("(3, 'UTC')").unwrap();
716 assert_eq!(count, 2);
717 assert_eq!(args[..count], ["3", "'UTC'"]);
718
719 assert!(parse_fixed_args::<1>("(UInt32, String)").is_err()); assert!(parse_fixed_args::<1>("(()").is_err()); assert!(parse_fixed_args::<1>("(String,)").is_err()); }
723
724 #[test]
726 fn test_parse_variable_args() {
727 let args = parse_variable_args("(Int8, String, Float64)").unwrap();
728 assert_eq!(args, vec!["Int8", "String", "Float64"]);
729
730 let args = parse_variable_args("(3, 'UTC', 'extra')").unwrap();
731 assert_eq!(args, vec!["3", "'UTC'", "'extra'"]);
732
733 let args = parse_variable_args("(())").unwrap();
734 assert_eq!(args, vec!["()"]);
735
736 let args = parse_variable_args("()").unwrap();
737 assert_eq!(args, Vec::<&str>::new());
738
739 assert!(parse_variable_args("(()").is_err()); assert!(parse_variable_args("(String,)").is_err()); }
742
743 #[test]
745 fn test_from_str_primitives() {
746 assert_eq!(Type::from_str("Int8").unwrap(), Type::Int8);
747 assert_eq!(Type::from_str("UInt8").unwrap(), Type::UInt8);
748 assert_eq!(Type::from_str("Bool").unwrap(), Type::UInt8); assert_eq!(Type::from_str("Float64").unwrap(), Type::Float64);
750 assert_eq!(Type::from_str("String").unwrap(), Type::String);
751 assert_eq!(Type::from_str("UUID").unwrap(), Type::Uuid);
752 assert_eq!(Type::from_str("Date").unwrap(), Type::Date);
753 assert_eq!(Type::from_str("IPv4").unwrap(), Type::Ipv4);
754 assert_eq!(Type::from_str("IPv6").unwrap(), Type::Ipv6);
755 }
756
757 #[test]
759 fn test_from_str_decimals() {
760 assert_eq!(Type::from_str("Decimal32(2)").unwrap(), Type::Decimal32(2));
761 assert_eq!(Type::from_str("Decimal64(4)").unwrap(), Type::Decimal64(4));
762 assert_eq!(Type::from_str("Decimal128(6)").unwrap(), Type::Decimal128(6));
763 assert_eq!(Type::from_str("Decimal256(8)").unwrap(), Type::Decimal256(8));
764 assert_eq!(Type::from_str("Decimal(9, 2)").unwrap(), Type::Decimal32(2));
765 assert_eq!(Type::from_str("Decimal(18, 4)").unwrap(), Type::Decimal64(4));
766 assert_eq!(Type::from_str("Decimal(38, 6)").unwrap(), Type::Decimal128(6));
767 assert_eq!(Type::from_str("Decimal(76, 8)").unwrap(), Type::Decimal256(8));
768
769 assert!(Type::from_str("Decimal32(0)").is_err()); assert!(Type::from_str("Decimal(77, 8)").is_err()); assert!(Type::from_str("Decimal(9)").is_err()); }
773
774 #[test]
776 fn test_from_str_strings() {
777 assert_eq!(Type::from_str("String").unwrap(), Type::String);
778 assert_eq!(Type::from_str("FixedString(4)").unwrap(), Type::FixedSizedString(4));
779 assert!(Type::from_str("FixedString(0)").is_err()); assert!(Type::from_str("FixedString(a)").is_err()); }
782
783 #[test]
785 fn test_from_str_datetime() {
786 assert_eq!(Type::from_str("DateTime").unwrap(), Type::DateTime(chrono_tz::UTC));
787 assert_eq!(Type::from_str("DateTime('UTC')").unwrap(), Type::DateTime(chrono_tz::UTC));
788 assert_eq!(
789 Type::from_str("DateTime('America/New_York')").unwrap(),
790 Type::DateTime(chrono_tz::America::New_York)
791 );
792 assert!(Type::from_str("DateTime('UTC', 'extra')").is_err()); assert!(Type::from_str("DateTime(UTC)").is_err()); assert_eq!(Type::from_str("DateTime64(3)").unwrap(), Type::DateTime64(3, chrono_tz::UTC));
796 assert_eq!(
797 Type::from_str("DateTime64(6, 'UTC')").unwrap(),
798 Type::DateTime64(6, chrono_tz::UTC)
799 );
800 assert_eq!(
801 Type::from_str("DateTime64(3, 'America/New_York')").unwrap(),
802 Type::DateTime64(3, chrono_tz::America::New_York)
803 );
804 assert!(Type::from_str("DateTime64()").is_err()); assert!(Type::from_str("DateTime64(3, 'UTC', 'extra')").is_err()); assert!(Type::from_str("DateTime64(3, UTC)").is_err()); }
808
809 #[test]
811 fn test_from_str_enum8_explicit() {
812 let enum8 = Type::from_str("Enum8('active' = 1, 'inactive' = 2)").unwrap();
813 assert_eq!(enum8, Type::Enum8(vec![("active".into(), 1), ("inactive".into(), 2)]));
814
815 let single = Type::from_str("Enum8('test' = -1)").unwrap();
816 assert_eq!(single, Type::Enum8(vec![("test".into(), -1)]));
817
818 let negative = Type::from_str("Enum8('neg' = -128, 'zero' = 0)").unwrap();
819 assert_eq!(negative, Type::Enum8(vec![("neg".into(), -128), ("zero".into(), 0)]));
820 }
821
822 #[test]
824 fn test_from_str_enum8_empty() {
825 let empty = Type::from_str("Enum8()").unwrap();
826 assert_eq!(empty, Type::Enum8(vec![]));
827 }
828
829 #[test]
831 fn test_from_str_enum16_explicit() {
832 let enum16 = Type::from_str("Enum16('high' = 1000, 'low' = -1000)").unwrap();
833 assert_eq!(enum16, Type::Enum16(vec![("high".into(), 1000), ("low".into(), -1000)]));
834
835 let single = Type::from_str("Enum16('test' = 0)").unwrap();
836 assert_eq!(single, Type::Enum16(vec![("test".into(), 0)]));
837 }
838
839 #[test]
841 fn test_from_str_enum8_errors() {
842 assert!(Type::from_str("Enum8('a' = 1, 2)").is_err()); assert!(Type::from_str("Enum8('a' = x)").is_err()); assert!(Type::from_str("Enum8(a = 1)").is_err()); assert!(Type::from_str("Enum8('a' = 1, )").is_err()); assert!(Type::from_str("Enum8('a' = 1").is_err()); }
848
849 #[test]
851 fn test_from_str_enum16_errors() {
852 assert!(Type::from_str("Enum16('a' = 1, 2)").is_err()); assert!(Type::from_str("Enum16('a' = x)").is_err()); assert!(Type::from_str("Enum16(a = 1)").is_err()); assert!(Type::from_str("Enum16('a' = 1, )").is_err()); assert!(Type::from_str("Enum16('a' = 1").is_err()); }
858
859 #[test]
861 fn test_from_str_complex_types() {
862 assert_eq!(
863 Type::from_str("LowCardinality(String)").unwrap(),
864 Type::LowCardinality(Box::new(Type::String))
865 );
866 assert_eq!(Type::from_str("Array(Int32)").unwrap(), Type::Array(Box::new(Type::Int32)));
867 assert_eq!(
868 Type::from_str("Tuple(Int32, String)").unwrap(),
869 Type::Tuple(vec![Type::Int32, Type::String])
870 );
871 assert_eq!(
872 Type::from_str("Nullable(Int32)").unwrap(),
873 Type::Nullable(Box::new(Type::Int32))
874 );
875 assert_eq!(
876 Type::from_str("Map(String, Int32)").unwrap(),
877 Type::Map(Box::new(Type::String), Box::new(Type::Int32))
878 );
879 assert_eq!(Type::from_str("JSON").unwrap(), Type::Object);
880 assert_eq!(Type::from_str("Object").unwrap(), Type::Object);
881
882 assert!(Type::from_str("LowCardinality()").is_err()); assert!(Type::from_str("Array(Int32, String)").is_err()); assert!(Type::from_str("Map(String)").is_err()); }
886
887 #[test]
889 fn test_round_trip_type_strings() {
890 let special_types = vec![
891 (Type::Binary, Type::String),
892 (Type::FixedSizedBinary(8), Type::FixedSizedString(8)),
893 ];
894
895 let types = vec![
896 Type::Int8,
897 Type::UInt8,
898 Type::Float64,
899 Type::String,
900 Type::FixedSizedString(4),
901 Type::Uuid,
902 Type::Date,
903 Type::Date32,
904 Type::DateTime(Tz::UTC),
905 Type::DateTime64(3, Tz::America__New_York),
906 Type::Ipv4,
907 Type::Ipv6,
908 Type::Decimal32(2),
909 Type::Enum8(vec![("active".into(), 1), ("inactive".into(), 2)]),
910 Type::Enum16(vec![("high".into(), 1000)]),
911 Type::LowCardinality(Box::new(Type::String)),
912 Type::Array(Box::new(Type::Int32)),
913 Type::Tuple(vec![Type::Int32, Type::String]),
914 Type::Nullable(Box::new(Type::Int32)),
915 Type::Map(Box::new(Type::String), Box::new(Type::Int32)),
916 Type::Object,
917 ];
918
919 for ty in types {
920 let type_str = ty.to_string();
921 let parsed = Type::from_str(&type_str)
922 .unwrap_or_else(|e| panic!("Failed to parse '{type_str}' for type {ty:?}: {e}"));
923 assert_eq!(
924 parsed, ty,
925 "Round-trip failed for type {ty:?}: expected {ty}, got {parsed}"
926 );
927 }
928
929 for (ty, mapped_ty) in special_types {
930 let type_str = ty.to_string();
931 let parsed = Type::from_str(&type_str)
932 .unwrap_or_else(|e| panic!("Failed to parse '{type_str}' for type {ty:?}: {e}"));
933 assert_eq!(
934 parsed, mapped_ty,
935 "Round-trip failed for type {ty:?}: expected {mapped_ty}, got {parsed}"
936 );
937 }
938 }
939
940 #[test]
942 fn test_from_str_general_errors() {
943 assert!(Type::from_str("").is_err()); assert!(Type::from_str("InvalidType").is_err()); assert!(Type::from_str("Nested(String)").is_err()); assert!(Type::from_str("Int8(").is_err()); assert!(Type::from_str("Tuple(String,)").is_err()); }
949
950 #[test]
952 fn test_strip_tuple_field_name() {
953 assert_eq!(strip_tuple_field_name("String"), "String");
955 assert_eq!(strip_tuple_field_name("Int64"), "Int64");
956 assert_eq!(strip_tuple_field_name("Nullable(Int32)"), "Nullable(Int32)");
957
958 assert_eq!(strip_tuple_field_name("s String"), "String");
960 assert_eq!(strip_tuple_field_name("i Int64"), "Int64");
961 assert_eq!(strip_tuple_field_name("my_field Nullable(Int32)"), "Nullable(Int32)");
962 assert_eq!(
963 strip_tuple_field_name("status Enum8('active' = 1, 'inactive' = 2)"),
964 "Enum8('active' = 1, 'inactive' = 2)"
965 );
966
967 assert_eq!(strip_tuple_field_name(" s String "), "String"); assert_eq!(strip_tuple_field_name("field123 UInt32"), "UInt32"); assert_eq!(strip_tuple_field_name("Map(String, Int32)"), "Map(String, Int32)");
973 assert_eq!(strip_tuple_field_name("Array(Nullable(String))"), "Array(Nullable(String))");
974 assert_eq!(strip_tuple_field_name("Tuple(String, Int32)"), "Tuple(String, Int32)");
975
976 assert_eq!(strip_tuple_field_name("my_map Map(String, Int32)"), "Map(String, Int32)");
978 }
979
980 #[test]
982 fn test_from_str_named_tuple() {
983 assert_eq!(
985 Type::from_str("Tuple(s String, i Int64)").unwrap(),
986 Type::Tuple(vec![Type::String, Type::Int64])
987 );
988
989 assert_eq!(
991 Type::from_str("Tuple(name String, value Nullable(Int32))").unwrap(),
992 Type::Tuple(vec![Type::String, Type::Nullable(Box::new(Type::Int32))])
993 );
994
995 assert_eq!(
997 Type::from_str("Tuple(arr Array(String), map Map(String, Int32))").unwrap(),
998 Type::Tuple(vec![
999 Type::Array(Box::new(Type::String)),
1000 Type::Map(Box::new(Type::String), Box::new(Type::Int32))
1001 ])
1002 );
1003
1004 assert_eq!(
1006 Type::from_str("Tuple(status Enum8('active' = 1, 'inactive' = 2), count Int64)")
1007 .unwrap(),
1008 Type::Tuple(vec![
1009 Type::Enum8(vec![("active".into(), 1), ("inactive".into(), 2)]),
1010 Type::Int64
1011 ])
1012 );
1013
1014 assert_eq!(
1017 Type::from_str("Tuple(String, i Int64)").unwrap(),
1018 Type::Tuple(vec![Type::String, Type::Int64])
1019 );
1020
1021 assert_eq!(
1024 Type::from_str("Tuple(Map(String, Int32), Int32)").unwrap(),
1025 Type::Tuple(vec![
1026 Type::Map(Box::new(Type::String), Box::new(Type::Int32)),
1027 Type::Int32
1028 ])
1029 );
1030 assert_eq!(
1031 Type::from_str("Tuple(Array(Nullable(String)), Map(String, Int64))").unwrap(),
1032 Type::Tuple(vec![
1033 Type::Array(Box::new(Type::Nullable(Box::new(Type::String)))),
1034 Type::Map(Box::new(Type::String), Box::new(Type::Int64))
1035 ])
1036 );
1037 }
1038}