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> =
453 args.into_iter().map(Type::from_str).collect::<Result<_, _>>()?;
454 Type::Tuple(inner)
455 }
456 "Nullable" => {
457 let (args, count) = parse_fixed_args::<1>(following)?;
458 if count != 1 {
459 return Err(Error::TypeParseError(format!(
460 "Nullable expects 1 arg: {args:?}"
461 )));
462 }
463 Type::Nullable(Box::new(Type::from_str(args[0])?))
464 }
465 "Map" => {
466 let (args, count) = parse_fixed_args::<2>(following)?;
467 if count != 2 {
468 return Err(Error::TypeParseError(format!(
469 "Map expects 2 args, got {count}: {args:?}"
470 )));
471 }
472 Type::Map(
473 Box::new(Type::from_str(args[0])?),
474 Box::new(Type::from_str(args[1])?),
475 )
476 }
477 "Nested" => {
479 return Err(Error::TypeParseError("unsupported Nested type".to_string()));
480 }
481 id => {
482 return Err(Error::TypeParseError(format!(
483 "invalid type with arguments: '{ident}' (ident = {id})"
484 )));
485 }
486 });
487 }
488 Ok(match ident {
489 "Int8" => Type::Int8,
490 "Int16" => Type::Int16,
491 "Int32" => Type::Int32,
492 "Int64" => Type::Int64,
493 "Int128" => Type::Int128,
494 "Int256" => Type::Int256,
495 "Bool" | "UInt8" => Type::UInt8,
496 "UInt16" => Type::UInt16,
497 "UInt32" => Type::UInt32,
498 "UInt64" => Type::UInt64,
499 "UInt128" => Type::UInt128,
500 "UInt256" => Type::UInt256,
501 "Float32" => Type::Float32,
502 "Float64" => Type::Float64,
503 "String" => Type::String,
504 "UUID" | "Uuid" | "uuid" => Type::Uuid,
505 "Date" => Type::Date,
506 "Date32" => Type::Date32,
507 "DateTime" => Type::DateTime(chrono_tz::UTC),
510 "IPv4" => Type::Ipv4,
511 "IPv6" => Type::Ipv6,
512 "Point" => Type::Point,
513 "Ring" => Type::Ring,
514 "Polygon" => Type::Polygon,
515 "MultiPolygon" => Type::MultiPolygon,
516 "Object" | "Json" | "OBJECT" | "JSON" => Type::Object,
517 _ => {
518 return Err(Error::TypeParseError(format!("invalid type name: '{ident}'")));
519 }
520 })
521 }
522}
523
524fn eat_identifier(input: &str) -> (&str, &str) {
526 for (i, c) in input.char_indices() {
527 if c.is_alphabetic() || c == '_' || c == '$' || (i > 0 && c.is_numeric()) {
528 continue;
529 }
530 return (&input[..i], &input[i..]);
531 }
532 (input, "")
533}
534
535fn parse_fixed_args<const N: usize>(input: &str) -> Result<([&str; N], usize)> {
537 let mut iter = parse_args_iter(input)?;
538 let mut out = [""; N];
539 let mut count = 0;
540
541 for (i, arg_result) in iter.by_ref().take(N).enumerate() {
543 out[i] = arg_result?;
544 count += 1;
545 }
546
547 if iter.next().is_some() {
549 return Err(Error::TypeParseError("too many arguments".to_string()));
550 }
551 Ok((out, count))
552}
553
554fn parse_variable_args(input: &str) -> Result<Vec<&str>> { parse_args_iter(input)?.collect() }
556
557fn parse_scale(from: &str) -> Result<usize> {
558 from.parse().map_err(|_| Error::TypeParseError("couldn't parse scale".to_string()))
559}
560
561fn parse_precision(from: &str) -> Result<usize> {
562 from.parse().map_err(|_| Error::TypeParseError("could not parse precision".to_string()))
563}
564
565fn parse_args_iter(input: &str) -> Result<impl Iterator<Item = Result<&str, Error>>> {
567 if !input.starts_with('(') || !input.ends_with(')') {
568 return Err(Error::TypeParseError("Malformed arguments to type".to_string()));
569 }
570 let input = input[1..input.len() - 1].trim();
571 if input.ends_with(',') {
572 return Err(Error::TypeParseError("Trailing comma in argument list".to_string()));
573 }
574
575 Ok(ArgsIterator { input, last_start: 0, in_parens: 0, in_quotes: false, done: false })
576}
577
578struct ArgsIterator<'a> {
579 input: &'a str,
580 last_start: usize,
581 in_parens: usize,
582 in_quotes: bool,
583 done: bool,
584}
585
586impl<'a> Iterator for ArgsIterator<'a> {
587 type Item = Result<&'a str, Error>;
588
589 #[allow(unused_assignments)]
590 fn next(&mut self) -> Option<Self::Item> {
591 if self.done {
592 return None;
593 }
594
595 let start = self.last_start;
596 let mut i = start;
597 let chars = self.input[start..].char_indices();
598 let mut escaped = false;
599
600 for (offset, c) in chars {
601 i = start + offset;
602 if self.in_quotes {
603 if c == '\\' {
604 escaped = true;
605 continue;
606 }
607 if c == '\'' && !escaped {
608 self.in_quotes = false;
609 }
610 escaped = false;
611 continue;
612 }
613 match c {
614 '\'' if !escaped => {
615 self.in_quotes = true;
616 }
617 '(' => self.in_parens += 1,
618 ')' => self.in_parens -= 1,
619 ',' if self.in_parens == 0 => {
620 let slice = self.input[self.last_start..i].trim();
621 if slice.is_empty() {
622 return Some(Err(Error::TypeParseError(
623 "Empty argument in list".to_string(),
624 )));
625 }
626 self.last_start = i + 1;
627 return Some(Ok(slice));
628 }
629 _ => {}
630 }
631 escaped = false;
632 }
633
634 if self.in_parens != 0 {
635 self.done = true;
636 return Some(Err(Error::TypeParseError("Mismatched parentheses".to_string())));
637 }
638 if self.last_start <= self.input.len() {
639 let slice = self.input[self.last_start..].trim();
640 if slice.is_empty() {
641 self.done = true;
642 return None; }
644 if slice == "," {
645 self.done = true;
646 return Some(Err(Error::TypeParseError(
647 "Trailing comma in argument list".to_string(),
648 )));
649 }
650 self.done = true;
651 return Some(Ok(slice));
652 }
653
654 self.done = true;
655 None
656 }
657}
658
659#[cfg(test)]
660mod tests {
661 use std::str::FromStr;
662
663 use super::*;
664 #[test]
666 fn test_eat_identifier() {
667 assert_eq!(eat_identifier("Int8"), ("Int8", ""));
668 assert_eq!(eat_identifier("Enum8('a'=1)"), ("Enum8", "('a'=1)"));
669 assert_eq!(eat_identifier("DateTime('UTC')"), ("DateTime", "('UTC')"));
670 assert_eq!(eat_identifier("Map(String,Int32)"), ("Map", "(String,Int32)"));
671 assert_eq!(eat_identifier(""), ("", ""));
672 assert_eq!(eat_identifier("Invalid Type"), ("Invalid", " Type"));
673 }
674
675 #[test]
677 fn test_parse_fixed_args() {
678 let (args, count) = parse_fixed_args::<2>("(UInt32, String)").unwrap();
679 assert_eq!(count, 2);
680 assert_eq!(args[..count], ["UInt32", "String"]);
681
682 let (args, count) = parse_fixed_args::<1>("(String)").unwrap();
683 assert_eq!(count, 1);
684 assert_eq!(args[..count], ["String"]);
685
686 let (args, count) = parse_fixed_args::<2>("(3, 'UTC')").unwrap();
687 assert_eq!(count, 2);
688 assert_eq!(args[..count], ["3", "'UTC'"]);
689
690 assert!(parse_fixed_args::<1>("(UInt32, String)").is_err()); assert!(parse_fixed_args::<1>("(()").is_err()); assert!(parse_fixed_args::<1>("(String,)").is_err()); }
694
695 #[test]
697 fn test_parse_variable_args() {
698 let args = parse_variable_args("(Int8, String, Float64)").unwrap();
699 assert_eq!(args, vec!["Int8", "String", "Float64"]);
700
701 let args = parse_variable_args("(3, 'UTC', 'extra')").unwrap();
702 assert_eq!(args, vec!["3", "'UTC'", "'extra'"]);
703
704 let args = parse_variable_args("(())").unwrap();
705 assert_eq!(args, vec!["()"]);
706
707 let args = parse_variable_args("()").unwrap();
708 assert_eq!(args, Vec::<&str>::new());
709
710 assert!(parse_variable_args("(()").is_err()); assert!(parse_variable_args("(String,)").is_err()); }
713
714 #[test]
716 fn test_from_str_primitives() {
717 assert_eq!(Type::from_str("Int8").unwrap(), Type::Int8);
718 assert_eq!(Type::from_str("UInt8").unwrap(), Type::UInt8);
719 assert_eq!(Type::from_str("Bool").unwrap(), Type::UInt8); assert_eq!(Type::from_str("Float64").unwrap(), Type::Float64);
721 assert_eq!(Type::from_str("String").unwrap(), Type::String);
722 assert_eq!(Type::from_str("UUID").unwrap(), Type::Uuid);
723 assert_eq!(Type::from_str("Date").unwrap(), Type::Date);
724 assert_eq!(Type::from_str("IPv4").unwrap(), Type::Ipv4);
725 assert_eq!(Type::from_str("IPv6").unwrap(), Type::Ipv6);
726 }
727
728 #[test]
730 fn test_from_str_decimals() {
731 assert_eq!(Type::from_str("Decimal32(2)").unwrap(), Type::Decimal32(2));
732 assert_eq!(Type::from_str("Decimal64(4)").unwrap(), Type::Decimal64(4));
733 assert_eq!(Type::from_str("Decimal128(6)").unwrap(), Type::Decimal128(6));
734 assert_eq!(Type::from_str("Decimal256(8)").unwrap(), Type::Decimal256(8));
735 assert_eq!(Type::from_str("Decimal(9, 2)").unwrap(), Type::Decimal32(2));
736 assert_eq!(Type::from_str("Decimal(18, 4)").unwrap(), Type::Decimal64(4));
737 assert_eq!(Type::from_str("Decimal(38, 6)").unwrap(), Type::Decimal128(6));
738 assert_eq!(Type::from_str("Decimal(76, 8)").unwrap(), Type::Decimal256(8));
739
740 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()); }
744
745 #[test]
747 fn test_from_str_strings() {
748 assert_eq!(Type::from_str("String").unwrap(), Type::String);
749 assert_eq!(Type::from_str("FixedString(4)").unwrap(), Type::FixedSizedString(4));
750 assert!(Type::from_str("FixedString(0)").is_err()); assert!(Type::from_str("FixedString(a)").is_err()); }
753
754 #[test]
756 fn test_from_str_datetime() {
757 assert_eq!(Type::from_str("DateTime").unwrap(), Type::DateTime(chrono_tz::UTC));
758 assert_eq!(Type::from_str("DateTime('UTC')").unwrap(), Type::DateTime(chrono_tz::UTC));
759 assert_eq!(
760 Type::from_str("DateTime('America/New_York')").unwrap(),
761 Type::DateTime(chrono_tz::America::New_York)
762 );
763 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));
767 assert_eq!(
768 Type::from_str("DateTime64(6, 'UTC')").unwrap(),
769 Type::DateTime64(6, chrono_tz::UTC)
770 );
771 assert_eq!(
772 Type::from_str("DateTime64(3, 'America/New_York')").unwrap(),
773 Type::DateTime64(3, chrono_tz::America::New_York)
774 );
775 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()); }
779
780 #[test]
782 fn test_from_str_enum8_explicit() {
783 let enum8 = Type::from_str("Enum8('active' = 1, 'inactive' = 2)").unwrap();
784 assert_eq!(enum8, Type::Enum8(vec![("active".into(), 1), ("inactive".into(), 2)]));
785
786 let single = Type::from_str("Enum8('test' = -1)").unwrap();
787 assert_eq!(single, Type::Enum8(vec![("test".into(), -1)]));
788
789 let negative = Type::from_str("Enum8('neg' = -128, 'zero' = 0)").unwrap();
790 assert_eq!(negative, Type::Enum8(vec![("neg".into(), -128), ("zero".into(), 0)]));
791 }
792
793 #[test]
795 fn test_from_str_enum8_empty() {
796 let empty = Type::from_str("Enum8()").unwrap();
797 assert_eq!(empty, Type::Enum8(vec![]));
798 }
799
800 #[test]
802 fn test_from_str_enum16_explicit() {
803 let enum16 = Type::from_str("Enum16('high' = 1000, 'low' = -1000)").unwrap();
804 assert_eq!(enum16, Type::Enum16(vec![("high".into(), 1000), ("low".into(), -1000)]));
805
806 let single = Type::from_str("Enum16('test' = 0)").unwrap();
807 assert_eq!(single, Type::Enum16(vec![("test".into(), 0)]));
808 }
809
810 #[test]
812 fn test_from_str_enum8_errors() {
813 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()); }
819
820 #[test]
822 fn test_from_str_enum16_errors() {
823 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()); }
829
830 #[test]
832 fn test_from_str_complex_types() {
833 assert_eq!(
834 Type::from_str("LowCardinality(String)").unwrap(),
835 Type::LowCardinality(Box::new(Type::String))
836 );
837 assert_eq!(Type::from_str("Array(Int32)").unwrap(), Type::Array(Box::new(Type::Int32)));
838 assert_eq!(
839 Type::from_str("Tuple(Int32, String)").unwrap(),
840 Type::Tuple(vec![Type::Int32, Type::String])
841 );
842 assert_eq!(
843 Type::from_str("Nullable(Int32)").unwrap(),
844 Type::Nullable(Box::new(Type::Int32))
845 );
846 assert_eq!(
847 Type::from_str("Map(String, Int32)").unwrap(),
848 Type::Map(Box::new(Type::String), Box::new(Type::Int32))
849 );
850 assert_eq!(Type::from_str("JSON").unwrap(), Type::Object);
851 assert_eq!(Type::from_str("Object").unwrap(), Type::Object);
852
853 assert!(Type::from_str("LowCardinality()").is_err()); assert!(Type::from_str("Array(Int32, String)").is_err()); assert!(Type::from_str("Map(String)").is_err()); }
857
858 #[test]
860 fn test_round_trip_type_strings() {
861 let special_types = vec![
862 (Type::Binary, Type::String),
863 (Type::FixedSizedBinary(8), Type::FixedSizedString(8)),
864 ];
865
866 let types = vec![
867 Type::Int8,
868 Type::UInt8,
869 Type::Float64,
870 Type::String,
871 Type::FixedSizedString(4),
872 Type::Uuid,
873 Type::Date,
874 Type::Date32,
875 Type::DateTime(Tz::UTC),
876 Type::DateTime64(3, Tz::America__New_York),
877 Type::Ipv4,
878 Type::Ipv6,
879 Type::Decimal32(2),
880 Type::Enum8(vec![("active".into(), 1), ("inactive".into(), 2)]),
881 Type::Enum16(vec![("high".into(), 1000)]),
882 Type::LowCardinality(Box::new(Type::String)),
883 Type::Array(Box::new(Type::Int32)),
884 Type::Tuple(vec![Type::Int32, Type::String]),
885 Type::Nullable(Box::new(Type::Int32)),
886 Type::Map(Box::new(Type::String), Box::new(Type::Int32)),
887 Type::Object,
888 ];
889
890 for ty in types {
891 let type_str = ty.to_string();
892 let parsed = Type::from_str(&type_str)
893 .unwrap_or_else(|e| panic!("Failed to parse '{type_str}' for type {ty:?}: {e}"));
894 assert_eq!(
895 parsed, ty,
896 "Round-trip failed for type {ty:?}: expected {ty}, got {parsed}"
897 );
898 }
899
900 for (ty, mapped_ty) in special_types {
901 let type_str = ty.to_string();
902 let parsed = Type::from_str(&type_str)
903 .unwrap_or_else(|e| panic!("Failed to parse '{type_str}' for type {ty:?}: {e}"));
904 assert_eq!(
905 parsed, mapped_ty,
906 "Round-trip failed for type {ty:?}: expected {mapped_ty}, got {parsed}"
907 );
908 }
909 }
910
911 #[test]
913 fn test_from_str_general_errors() {
914 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()); }
920}