clickhouse_client/value/ty/
mod.rs

1//! Data types
2
3#[cfg(test)]
4mod tests;
5
6use std::{collections::BTreeMap, str::FromStr};
7
8use crate::error::Error;
9
10/// Data type
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum Type {
13    /// u8
14    UInt8,
15    /// u16
16    UInt16,
17    /// u32
18    UInt32,
19    /// u64
20    UInt64,
21    /// u128
22    UInt128,
23    /// u256
24    UInt256,
25    /// i8
26    Int8,
27    /// i16
28    Int16,
29    /// i32
30    Int32,
31    /// i64
32    Int64,
33    /// i128
34    Int128,
35    /// i256
36    Int256,
37    /// f32
38    Float32,
39    /// f64
40    Float64,
41    /// Decimal(P,S) (precision ∈ [1:76], scale ∈ [0:P], range ( -1 * 10^(P - S), 1 * 10^(P - S) )
42    Decimal(u8, u8),
43    /// Decimal(P ∈ [1:9])
44    Decimal32(u8),
45    /// Decimal(P ∈ [19:18])
46    Decimal64(u8),
47    /// Decimal(P ∈ [19:38])
48    Decimal128(u8),
49    /// Decimal(P ∈ [39:76])
50    Decimal256(u8),
51    /// Boolean
52    Bool,
53    /// String
54    String,
55    /// Fixed string
56    FixedString(u8),
57    /// UUID (16 bytes)
58    UUID,
59    /// Date (number of days since 1970-01-01, 2 bytes)
60    Date,
61    /// Date32 (number of days since 1970-01-01, signed i32)
62    Date32,
63    /// DateTime (seconds since EPOCH, [1970-01-01 00:00:00, 2106-02-07 06:28:15])
64    DateTime,
65    /// Ticks since since epoch start (1970-01-01 00:00:00 UTC)
66    ///
67    /// Precision [0:9] defines the resolution, eg 3=ms, 6=us, 9=ns
68    DateTime64(u8),
69    /// Enum (256 values, i8)
70    ///
71    /// Keys and indices must be unique
72    Enum8(BTreeMap<String, i8>),
73    /// Enum (65536 values, i16)
74    ///
75    /// Keys and indices must be unique
76    Enum16(BTreeMap<String, i16>),
77    /// Array
78    ///
79    /// An array element can have any type
80    Array(Box<Type>),
81    /// Tuple
82    ///
83    /// Each element can have a different type
84    Tuple(Vec<Type>),
85    /// Map
86    ///
87    /// - key: String, Integer, LowCardinality, FixedString, UUID, Date, DateTime, Date32, Enum
88    /// - value: any type
89    Map(Box<Type>, Box<Type>),
90    /// Nested
91    ///
92    /// A nested structure is a table inside a cell.
93    ///
94    /// Each type can have a name (optional)
95    Nested(Vec<(String, Type)>),
96    /// Nullable u8
97    NullableUInt8,
98    /// Nullable u16
99    NullableUInt16,
100    /// Nullable u32
101    NullableUInt32,
102    /// Nullable u64
103    NullableUInt64,
104    /// Nullable u128
105    NullableUInt128,
106    /// Nullable u256
107    NullableUInt256,
108    /// Nullable i8
109    NullableInt8,
110    /// Nullable i16
111    NullableInt16,
112    /// Nullable i32
113    NullableInt32,
114    /// Nullable i64
115    NullableInt64,
116    /// Nullable i128
117    NullableInt128,
118    /// Nullable i256
119    NullableInt256,
120    /// Nullable f32
121    NullableFloat32,
122    /// Nullable f64
123    NullableFloat64,
124    /// Nullable decimal
125    NullableDecimal(u8, u8),
126    /// Nullable decimal32
127    NullableDecimal32(u8),
128    /// Nullable decimal64
129    NullableDecimal64(u8),
130    /// Nullable decimal128
131    NullableDecimal128(u8),
132    /// Nullable decimal256
133    NullableDecimal256(u8),
134    /// Nullable bool
135    NullableBool,
136    /// Nullable string
137    NullableString,
138    /// Nullable fixed string
139    NullableFixedString(u8),
140    /// Nullbale UUID
141    NullableUUID,
142    /// Nullable date
143    NullableDate,
144    /// Nullable date32
145    NullableDate32,
146    /// Nullable datetime
147    NullableDateTime,
148    /// Nullable datetime64
149    NullableDateTime64(u8),
150    /// Nullable Enum8
151    NullableEnum8(BTreeMap<String, i8>),
152    /// Nullable Enum16
153    NullableEnum16(BTreeMap<String, i16>),
154}
155
156impl std::fmt::Display for Type {
157    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158        let s: String = match self {
159            Type::UInt8 => "UInt8".into(),
160            Type::UInt16 => "UInt16".into(),
161            Type::UInt32 => "UInt32".into(),
162            Type::UInt64 => "UInt64".into(),
163            Type::UInt128 => "UInt128".into(),
164            Type::UInt256 => "UInt256".into(),
165            Type::Int8 => "Int8".into(),
166            Type::Int16 => "Int16".into(),
167            Type::Int32 => "Int32".into(),
168            Type::Int64 => "Int64".into(),
169            Type::Int128 => "Int128".into(),
170            Type::Int256 => "Int256".into(),
171            Type::Float32 => "Float32".into(),
172            Type::Float64 => "Float64".into(),
173            Type::Decimal(p, s) => format!("Decimal({p},{s})"),
174            Type::Decimal32(s) => format!("Decimal32({s})"),
175            Type::Decimal64(s) => format!("Decimal64({s})"),
176            Type::Decimal128(s) => format!("Decimal128({s})"),
177            Type::Decimal256(s) => format!("Decimal256({s})"),
178            Type::Bool => "Bool".into(),
179            Type::String => "String".into(),
180            Type::FixedString(n) => format!("FixedString({n})"),
181            Type::Date => "Date".into(),
182            Type::Date32 => "Date32".into(),
183            Type::DateTime => "DateTime".into(),
184            Type::DateTime64(p) => format!("DateTime64({p})"),
185            Type::UUID => "UUID".into(),
186            Type::Enum8(vars) => {
187                format!(
188                    "Enum8({})",
189                    vars.iter()
190                        .map(|(key, idx)| { format!("'{key}' = {idx}") })
191                        .collect::<Vec<_>>()
192                        .join(", ")
193                )
194            }
195            Type::Enum16(vars) => {
196                format!(
197                    "Enum16({})",
198                    vars.iter()
199                        .map(|(key, idx)| { format!("'{key}' = {idx}") })
200                        .collect::<Vec<_>>()
201                        .join(", ")
202                )
203            }
204            Type::Array(t) => format!("Array({t})"),
205            Type::Map(k, v) => format!("Map({k}, {v})"),
206            Type::Tuple(types) => {
207                format!(
208                    "Tuple({})",
209                    types
210                        .iter()
211                        .map(|ty| { format!("{ty}") })
212                        .collect::<Vec<_>>()
213                        .join(", ")
214                )
215            }
216            Type::Nested(fields) => {
217                format!(
218                    "Nested({})",
219                    fields
220                        .iter()
221                        .map(|(name, ty)| { format!("{} {}", name, ty) })
222                        .collect::<Vec<_>>()
223                        .join(", ")
224                )
225            }
226            Type::NullableUInt8 => "Nullable(UInt8)".into(),
227            Type::NullableUInt16 => "Nullable(UInt16)".into(),
228            Type::NullableUInt32 => "Nullable(UInt32)".into(),
229            Type::NullableUInt64 => "Nullable(UInt64)".into(),
230            Type::NullableUInt128 => "Nullable(UInt128)".into(),
231            Type::NullableUInt256 => "Nullable(UInt256)".into(),
232            Type::NullableInt8 => "Nullable(Int8)".into(),
233            Type::NullableInt16 => "Nullable(Int16)".into(),
234            Type::NullableInt32 => "Nullable(Int32)".into(),
235            Type::NullableInt64 => "Nullable(Int64)".into(),
236            Type::NullableInt128 => "Nullable(Int128)".into(),
237            Type::NullableInt256 => "Nullable(Int256)".into(),
238            Type::NullableFloat32 => "Nullable(Float32)".into(),
239            Type::NullableFloat64 => "Nullable(Float64)".into(),
240            Type::NullableDecimal(p, s) => format!("Nullable(Decimal({p},{s}))"),
241            Type::NullableDecimal32(s) => format!("Nullable(Decimal32({s}))"),
242            Type::NullableDecimal64(s) => format!("Nullable(Decimal64({s}))"),
243            Type::NullableDecimal128(s) => format!("Nullable(Decimal128({s}))"),
244            Type::NullableDecimal256(s) => format!("Nullable(Decimal256({s}))"),
245            Type::NullableBool => "Nullable(Bool)".into(),
246            Type::NullableString => "Nullable(String)".into(),
247            Type::NullableFixedString(n) => format!("Nullable(FixedString({n}))"),
248            Type::NullableDate => "Nullable(Date)".into(),
249            Type::NullableDate32 => "Nullable(Date32)".into(),
250            Type::NullableDateTime => "Nullable(DateTime)".into(),
251            Type::NullableDateTime64(p) => format!("Nullable(DateTime64({p}))"),
252            Type::NullableUUID => "Nullable(UUID)".into(),
253            Type::NullableEnum8(keys) => format!("Nullable({})", Type::Enum8(keys.clone())),
254            Type::NullableEnum16(keys) => format!("Nullable({})", Type::Enum16(keys.clone())),
255        };
256
257        write!(f, "{s}")
258    }
259}
260
261impl FromStr for Type {
262    type Err = Error;
263
264    fn from_str(s: &str) -> Result<Self, Self::Err> {
265        // base types
266        match s {
267            "UInt8" => return Ok(Type::UInt8),
268            "UInt16" => return Ok(Type::UInt16),
269            "UInt32" => return Ok(Type::UInt32),
270            "UInt64" => return Ok(Type::UInt64),
271            "UInt128" => return Ok(Type::UInt128),
272            "UInt256" => return Ok(Type::UInt256),
273            "Int8" => return Ok(Type::Int8),
274            "Int16" => return Ok(Type::Int16),
275            "Int32" => return Ok(Type::Int32),
276            "Int64" => return Ok(Type::Int64),
277            "Int128" => return Ok(Type::Int128),
278            "Int256" => return Ok(Type::Int256),
279            "Float32" => return Ok(Type::Float32),
280            "Float64" => return Ok(Type::Float64),
281            "Bool" => return Ok(Type::Bool),
282            "String" => return Ok(Type::String),
283            "UUID" => return Ok(Type::UUID),
284            "Date" => return Ok(Type::Date),
285            "Date32" => return Ok(Type::Date32),
286            "DateTime" => return Ok(Type::DateTime),
287            _ => {}
288        }
289
290        // > Decimal(P, S)
291        if let Some(s) = s.strip_prefix("Decimal(") {
292            if let Some(s) = s.strip_suffix(')') {
293                let parts = s.split(',').collect::<Vec<_>>();
294                if parts.len() != 2 {
295                    return Err(Error::new("invalid Decimal type"));
296                }
297                let p = parts[0].trim().parse::<u8>()?;
298                let s = parts[1].trim().parse::<u8>()?;
299                return Ok(Type::Decimal(p, s));
300            } else {
301                return Err(Error::new("invalid Decimal type"));
302            }
303        }
304
305        // > Decimal32(S)
306        if let Some(s) = s.strip_prefix("Decimal32(") {
307            if let Some(s) = s.strip_suffix(')') {
308                let s = s.trim().parse::<u8>()?;
309                return Ok(Type::Decimal32(s));
310            } else {
311                return Err(Error::new("invalid Decimal32 type"));
312            }
313        }
314
315        // > Decimal64(S)
316        if let Some(s) = s.strip_prefix("Decimal64(") {
317            if let Some(s) = s.strip_suffix(')') {
318                let s = s.trim().parse::<u8>()?;
319                return Ok(Type::Decimal64(s));
320            } else {
321                return Err(Error::new("invalid Decimal64 type"));
322            }
323        }
324
325        // > Decimal128(S)
326        if let Some(s) = s.strip_prefix("Decimal128(") {
327            if let Some(s) = s.strip_suffix(')') {
328                let s = s.trim().parse::<u8>()?;
329                return Ok(Type::Decimal128(s));
330            } else {
331                return Err(Error::new("invalid Decimal128 type"));
332            }
333        }
334
335        // > Decimal256(S)
336        if let Some(s) = s.strip_prefix("Decimal256(") {
337            if let Some(s) = s.strip_suffix(')') {
338                let s = s.trim().parse::<u8>()?;
339                return Ok(Type::Decimal256(s));
340            } else {
341                return Err(Error::new("invalid Decimal256 type"));
342            }
343        }
344
345        // > FixedString(N)
346        if let Some(s) = s.strip_prefix("FixedString(") {
347            if let Some(s) = s.strip_suffix(')') {
348                let n = s.trim().parse::<u8>()?;
349                return Ok(Type::FixedString(n));
350            } else {
351                return Err(Error::new("invalid FixedString type"));
352            }
353        }
354
355        // > DateTime64(P)
356        if let Some(s) = s.strip_prefix("DateTime64(") {
357            if let Some(s) = s.strip_suffix(')') {
358                let p = s.trim().parse::<u8>()?;
359                return Ok(Type::DateTime64(p));
360            } else {
361                return Err(Error::new("invalid DateTime64 type"));
362            }
363        }
364
365        // > Enum8(...)
366        if let Some(s) = s.strip_prefix("Enum8(") {
367            if let Some(s) = s.strip_suffix(')') {
368                let mut map = BTreeMap::new();
369                for variant in s.split(',') {
370                    let parts = variant.trim().split('=').collect::<Vec<_>>();
371                    if parts.len() != 2 {
372                        return Err(Error::new("invalid Enum8 type"));
373                    }
374                    let name = parts[0]
375                        .trim()
376                        .trim_start_matches('\'')
377                        .trim_end_matches('\'')
378                        .to_string();
379                    let index = parts[1].trim().parse::<i8>()?;
380                    map.insert(name, index);
381                }
382                return Ok(Type::Enum8(map));
383            } else {
384                return Err(Error::new("invalid DateTime64 type"));
385            }
386        }
387
388        // > Enum16(...)
389        if let Some(s) = s.strip_prefix("Enum16(") {
390            if let Some(s) = s.strip_suffix(')') {
391                let mut map = BTreeMap::new();
392                for variant in s.split(',') {
393                    let parts = variant.trim().split('=').collect::<Vec<_>>();
394                    if parts.len() != 2 {
395                        return Err(Error::new("invalid Enum8 type"));
396                    }
397                    let name = parts[0]
398                        .trim()
399                        .trim_start_matches('\'')
400                        .trim_end_matches('\'')
401                        .to_string();
402                    let index = parts[1].trim().parse::<i16>()?;
403                    map.insert(name, index);
404                }
405                return Ok(Type::Enum16(map));
406            } else {
407                return Err(Error::new("invalid DateTime64 type"));
408            }
409        }
410
411        // > Enum(...)
412        if let Some(s) = s.strip_prefix("Enum(") {
413            if let Some(s) = s.strip_suffix(')') {
414                let mut map = BTreeMap::new();
415                for variant in s.split(',') {
416                    let parts = variant.trim().split('=').collect::<Vec<_>>();
417                    if parts.len() != 2 {
418                        return Err(Error::new("invalid Enum8 type"));
419                    }
420                    let name = parts[0]
421                        .trim()
422                        .trim_start_matches('\'')
423                        .trim_end_matches('\'')
424                        .to_string();
425                    let index = parts[1].trim().parse::<i16>()?;
426                    map.insert(name, index);
427                }
428                return Ok(Type::Enum16(map));
429            } else {
430                return Err(Error::new("invalid DateTime64 type"));
431            }
432        }
433
434        // > Array
435        if let Some(s) = s.strip_prefix("Array(") {
436            if let Some(s) = s.strip_suffix(')') {
437                let ty = s.trim().parse::<Type>()?;
438                return Ok(Type::Array(Box::new(ty)));
439            } else {
440                return Err(Error::new("invalid Array type"));
441            }
442        }
443
444        // > Tuple
445        if let Some(s) = s.strip_prefix("Tuple(") {
446            if let Some(s) = s.strip_suffix(')') {
447                let mut types = vec![];
448                for ty_str in s.split(',') {
449                    let ty = ty_str.trim().parse::<Type>()?;
450                    types.push(ty);
451                }
452                return Ok(Type::Tuple(types));
453            } else {
454                return Err(Error::new("invalid Tuple type"));
455            }
456        }
457
458        // > Map
459        if let Some(s) = s.strip_prefix("Map(") {
460            if let Some(s) = s.strip_suffix(')') {
461                let parts = s.split(',').collect::<Vec<_>>();
462                if parts.len() != 2 {
463                    return Err(Error::new("invalid Map type"));
464                }
465                let key_ty = parts[0].trim().parse::<Type>()?;
466                let val_ty = parts[1].trim().parse::<Type>()?;
467                return Ok(Type::Map(Box::new(key_ty), Box::new(val_ty)));
468            } else {
469                return Err(Error::new("invalid Map type"));
470            }
471        }
472
473        // > Nested
474        if let Some(s) = s.strip_prefix("Nested(") {
475            if let Some(s) = s.strip_suffix(')') {
476                let mut fields = vec![];
477                for field_str in s.split(',').collect::<Vec<_>>() {
478                    let parts = field_str.trim().split(' ').collect::<Vec<_>>();
479                    if parts.len() != 2 {
480                        return Err(Error::new("invalid Nested type"));
481                    }
482                    let name = parts[0].trim();
483                    let ty = parts[1].trim().parse::<Type>()?;
484                    fields.push((name.to_string(), ty));
485                }
486
487                return Ok(Type::Nested(fields));
488            } else {
489                return Err(Error::new("invalid Nested type"));
490            }
491        }
492
493        // > Nullable(T)
494        if let Some(s) = s.strip_prefix("Nullable(") {
495            if let Some(s) = s.strip_suffix(')') {
496                let ty = s.parse::<Type>()?;
497                let ty = match ty {
498                    Type::UInt8 => Type::NullableUInt8,
499                    Type::UInt16 => Type::NullableUInt16,
500                    Type::UInt32 => Type::NullableUInt32,
501                    Type::UInt64 => Type::NullableUInt64,
502                    Type::UInt128 => Type::NullableUInt128,
503                    Type::UInt256 => Type::NullableUInt256,
504                    Type::Int8 => Type::NullableInt8,
505                    Type::Int16 => Type::NullableInt16,
506                    Type::Int32 => Type::NullableInt32,
507                    Type::Int64 => Type::NullableInt64,
508                    Type::Int128 => Type::NullableInt128,
509                    Type::Int256 => Type::NullableInt256,
510                    Type::Float32 => Type::NullableFloat32,
511                    Type::Float64 => Type::NullableFloat64,
512                    Type::Decimal(p, s) => Type::NullableDecimal(p, s),
513                    Type::Decimal32(s) => Type::NullableDecimal32(s),
514                    Type::Decimal64(s) => Type::NullableDecimal64(s),
515                    Type::Decimal128(s) => Type::NullableDecimal128(s),
516                    Type::Decimal256(s) => Type::NullableDecimal256(s),
517                    Type::Bool => Type::NullableBool,
518                    Type::String => Type::NullableString,
519                    Type::FixedString(n) => Type::NullableFixedString(n),
520                    Type::UUID => Type::NullableUUID,
521                    Type::Date => Type::NullableDate,
522                    Type::Date32 => Type::NullableDate32,
523                    Type::DateTime => Type::NullableDateTime,
524                    Type::DateTime64(p) => Type::NullableDateTime64(p),
525                    Type::Enum8(keys) => Type::NullableEnum8(keys),
526                    Type::Enum16(keys) => Type::NullableEnum16(keys),
527                    _ => return Err(Error::new("invalid Nullable type")),
528                };
529                return Ok(ty);
530            } else {
531                return Err(Error::new("invalid Nullable type"));
532            }
533        }
534
535        Err(Error::new(
536            format!("'{s}' is not a valid Clickhouse type").as_str(),
537        ))
538    }
539}