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