taos_query/helpers/
describe.rs

1use std::{
2    fmt,
3    ops::{Deref, DerefMut},
4    str::FromStr,
5};
6
7use serde::{
8    de::{self, MapAccess, SeqAccess, Visitor},
9    Deserialize, Deserializer, Serialize,
10};
11
12use crate::common::Ty;
13
14/// Compress options for column, supported since TDengine 3.3.0.0 .
15///
16/// The `encode` field is the encoding method for the column, it can be one of the following values:
17/// - `disabled`
18/// - `delta-i`
19/// - `delta-d`
20/// - `simple8b`
21///
22/// The `compress` field is the compression method for the column, it can be one of the following values:
23/// - `none`
24/// - `lz4`
25/// - `gzip`
26/// - `zstd`
27///
28/// The `level` field is the compression level for the column, it can be one of the following values:
29/// - `low`
30/// - `medium`
31/// - `high`
32#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
33pub struct CompressOptions {
34    pub encode: String,
35    pub compress: String,
36    pub level: String,
37}
38
39impl CompressOptions {
40    pub fn new(
41        encode: impl Into<String>,
42        compress: impl Into<String>,
43        level: impl Into<String>,
44    ) -> Self {
45        Self {
46            encode: encode.into(),
47            compress: compress.into(),
48            level: level.into(),
49        }
50    }
51}
52
53impl fmt::Display for CompressOptions {
54    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
55        write!(
56            f,
57            "ENCODE '{}' COMPRESS '{}' LEVEL '{}'",
58            self.encode, self.compress, self.level
59        )
60    }
61}
62
63#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
64pub struct Described {
65    pub field: String,
66    #[serde(rename = "type")]
67    pub ty: Ty,
68    pub length: usize,
69    #[serde(default)]
70    pub note: Option<String>,
71    #[serde(flatten, default)]
72    pub compression: Option<CompressOptions>,
73}
74
75impl Described {
76    /// Represent the data type in sql.
77    ///
78    /// For example: "INT", "VARCHAR(100)".
79    pub fn sql_repr(&self) -> String {
80        let ty = self.ty;
81        match (self.is_primary_key(), ty.is_var_type(), &self.compression) {
82            (true, true, None) => format!("`{}` {}({}) PRIMARY KEY", self.field, ty, self.length),
83            (true, false, None) => format!("`{}` {} PRIMARY KEY", self.field, self.ty),
84            (true, true, Some(t)) => {
85                format!("`{}` {}({}) {} PRIMARY KEY", self.field, ty, self.length, t)
86            }
87            (true, false, Some(t)) => {
88                format!("`{}` {} {} PRIMARY KEY", self.field, ty, t)
89            }
90
91            (false, true, None) => format!("`{}` {}({})", self.field, ty, self.length),
92            (false, false, None) => format!("`{}` {}", self.field, self.ty),
93            (false, true, Some(t)) => {
94                format!("`{}` {}({}) {}", self.field, ty, self.length, t)
95            }
96            (false, false, Some(t)) => {
97                format!("`{}` {} {}", self.field, ty, t)
98            }
99        }
100    }
101
102    /// Create a new column description without primary-key/compression feature.
103    pub fn new(field: impl Into<String>, ty: Ty, length: impl Into<Option<usize>>) -> Self {
104        let field = field.into();
105        let length = length.into();
106        let length = length.unwrap_or_else(|| {
107            if ty.is_var_type() {
108                32
109            } else {
110                ty.fixed_length()
111            }
112        });
113        Self {
114            field,
115            ty,
116            length,
117            note: None,
118            compression: None,
119        }
120    }
121
122    /// Return true if the field is primary key.
123    pub fn is_primary_key(&self) -> bool {
124        self.note.as_deref() == Some("PRIMARY KEY")
125    }
126
127    /// Return true if the field is tag.
128    pub fn is_tag(&self) -> bool {
129        self.note.as_deref() == Some("TAG")
130    }
131}
132#[derive(Debug, Serialize, PartialEq, Eq, Clone)]
133#[serde(untagged)]
134pub enum ColumnMeta {
135    Column(Described),
136    Tag(Described),
137}
138
139impl Deref for ColumnMeta {
140    type Target = Described;
141
142    fn deref(&self) -> &Self::Target {
143        match self {
144            ColumnMeta::Column(v) => v,
145            ColumnMeta::Tag(v) => v,
146        }
147    }
148}
149
150impl DerefMut for ColumnMeta {
151    fn deref_mut(&mut self) -> &mut Self::Target {
152        match self {
153            ColumnMeta::Column(v) => v,
154            ColumnMeta::Tag(v) => v,
155        }
156    }
157}
158
159#[inline(always)]
160fn empty_as_none(s: &str) -> Option<String> {
161    if s.is_empty() {
162        None
163    } else {
164        Some(s.to_string())
165    }
166}
167unsafe impl Send for ColumnMeta {}
168impl<'de> Deserialize<'de> for ColumnMeta {
169    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
170    where
171        D: Deserializer<'de>,
172    {
173        enum Meta {
174            Field,
175            Type,
176            Length,
177            Note,
178            Encode,
179            Compress,
180            Level,
181        }
182
183        impl<'de> Deserialize<'de> for Meta {
184            fn deserialize<D>(deserializer: D) -> Result<Meta, D::Error>
185            where
186                D: Deserializer<'de>,
187            {
188                struct FieldVisitor;
189
190                impl<'de> Visitor<'de> for FieldVisitor {
191                    type Value = Meta;
192
193                    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
194                        formatter.write_str(
195                            "one of `field`, `type`, `length`, `note`, `encode`, `compress`, `level`",
196                        )
197                    }
198
199                    fn visit_str<E>(self, value: &str) -> Result<Meta, E>
200                    where
201                        E: de::Error,
202                    {
203                        match value.to_lowercase().as_str() {
204                            "field" => Ok(Meta::Field),
205                            "type" => Ok(Meta::Type),
206                            "length" => Ok(Meta::Length),
207                            "note" => Ok(Meta::Note),
208                            "encode" => Ok(Meta::Encode),
209                            "compress" => Ok(Meta::Compress),
210                            "level" => Ok(Meta::Level),
211                            _ => Err(de::Error::unknown_field(value, FIELDS)),
212                        }
213                    }
214                }
215
216                deserializer.deserialize_identifier(FieldVisitor)
217            }
218        }
219
220        struct MetaVisitor;
221
222        impl<'de> Visitor<'de> for MetaVisitor {
223            type Value = ColumnMeta;
224
225            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
226                formatter.write_str("struct ColumnMeta")
227            }
228
229            fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
230            where
231                V: SeqAccess<'de>,
232            {
233                let field = seq
234                    .next_element()?
235                    .ok_or_else(|| de::Error::invalid_length(0, &self))?;
236                let ty = seq
237                    .next_element()?
238                    .ok_or_else(|| de::Error::invalid_length(1, &self))
239                    .and_then(|s| Ty::from_str(s).map_err(de::Error::custom))?;
240                let length = seq
241                    .next_element()?
242                    .ok_or_else(|| de::Error::invalid_length(2, &self))?;
243                let note: Option<String> = seq
244                    .next_element::<Option<&str>>()?
245                    .and_then(|opt| opt)
246                    .and_then(empty_as_none);
247
248                // let is_primary_key = &note == "PRIMARY KEY";
249
250                let encode: Option<String> = seq
251                    .next_element::<Option<&str>>()?
252                    .and_then(|opt| opt)
253                    .and_then(empty_as_none);
254                let compress: Option<String> = seq
255                    .next_element::<Option<&str>>()?
256                    .and_then(|opt| opt)
257                    .and_then(empty_as_none);
258                let level: Option<String> = seq
259                    .next_element::<Option<&str>>()?
260                    .and_then(|opt| opt)
261                    .and_then(empty_as_none);
262
263                let compression = if let (Some(encode), Some(compress), Some(level)) =
264                    (encode, compress, level)
265                {
266                    Some(CompressOptions::new(encode, compress, level))
267                } else {
268                    None
269                };
270                let desc = Described {
271                    field,
272                    ty,
273                    length,
274                    note,
275                    compression,
276                };
277                if !desc.is_tag() {
278                    Ok(ColumnMeta::Column(desc))
279                } else {
280                    Ok(ColumnMeta::Tag(desc))
281                }
282            }
283
284            fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
285            where
286                V: MapAccess<'de>,
287            {
288                let mut field = None;
289                let mut ty = None;
290                let mut length = None;
291                let mut note = None;
292                let mut encode = None;
293                let mut compress = None;
294                let mut level = None;
295                while let Some(key) = map.next_key()? {
296                    match key {
297                        Meta::Field => {
298                            if field.is_some() {
299                                return Err(de::Error::duplicate_field("field"));
300                            }
301                            field = Some(map.next_value()?);
302                        }
303                        Meta::Type => {
304                            if ty.is_some() {
305                                return Err(de::Error::duplicate_field("type"));
306                            }
307                            let t: Ty = map.next_value()?;
308                            ty = Some(t);
309                        }
310                        Meta::Length => {
311                            if length.is_some() {
312                                return Err(de::Error::duplicate_field("length"));
313                            }
314                            length = Some(map.next_value()?);
315                        }
316                        Meta::Note => {
317                            if note.is_some() {
318                                return Err(de::Error::duplicate_field("note"));
319                            }
320                            note = map.next_value::<Option<&str>>()?.and_then(empty_as_none);
321                        }
322                        Meta::Encode => {
323                            if encode.is_some() {
324                                return Err(de::Error::duplicate_field("encode"));
325                            }
326                            encode = map.next_value::<Option<&str>>()?.and_then(empty_as_none);
327                        }
328                        Meta::Compress => {
329                            if compress.is_some() {
330                                return Err(de::Error::duplicate_field("compress"));
331                            }
332                            compress = map.next_value::<Option<&str>>()?.and_then(empty_as_none);
333                        }
334                        Meta::Level => {
335                            if level.is_some() {
336                                return Err(de::Error::duplicate_field("level"));
337                            }
338                            level = map.next_value::<Option<&str>>()?.and_then(empty_as_none);
339                        }
340                    }
341                }
342                let field = field.ok_or_else(|| de::Error::missing_field("field"))?;
343                let ty = ty.ok_or_else(|| de::Error::missing_field("type"))?;
344                let length = length.ok_or_else(|| de::Error::missing_field("length"))?;
345                let note = note.map(|s| s.to_string());
346                let compression = if let (Some(encode), Some(compress), Some(level)) =
347                    (encode, compress, level)
348                {
349                    Some(CompressOptions::new(encode, compress, level))
350                } else {
351                    None
352                };
353                let desc = Described {
354                    field,
355                    ty,
356                    length,
357                    note,
358                    compression,
359                };
360                if !desc.is_tag() {
361                    Ok(ColumnMeta::Column(desc))
362                } else {
363                    Ok(ColumnMeta::Tag(desc))
364                }
365            }
366        }
367
368        const FIELDS: &[&str] = &[
369            "field", "type", "length", "note", "encode", "compress", "level",
370        ];
371        deserializer.deserialize_struct("ColumnMeta", FIELDS, MetaVisitor)
372    }
373}
374impl ColumnMeta {
375    pub fn field(&self) -> &str {
376        match self {
377            ColumnMeta::Column(desc) | ColumnMeta::Tag(desc) => desc.field.as_str(),
378        }
379    }
380    pub fn ty(&self) -> Ty {
381        match self {
382            ColumnMeta::Column(desc) | ColumnMeta::Tag(desc) => desc.ty,
383        }
384    }
385    pub fn length(&self) -> usize {
386        match self {
387            ColumnMeta::Column(desc) | ColumnMeta::Tag(desc) => desc.length,
388        }
389    }
390    pub fn note(&self) -> &str {
391        match self {
392            ColumnMeta::Tag(_) => "TAG",
393            _ => "",
394        }
395    }
396    pub fn is_tag(&self) -> bool {
397        matches!(self, ColumnMeta::Tag(_))
398    }
399}
400
401#[test]
402fn serde_meta() {
403    // ordinary column
404    let meta = ColumnMeta::Column(Described {
405        field: "name".to_string(),
406        ty: Ty::BigInt,
407        length: 8,
408        note: None,
409        compression: None,
410    });
411
412    let sql = meta.deref().sql_repr();
413
414    assert_eq!(sql, "`name` BIGINT");
415
416    let a = serde_json::to_string(&meta).unwrap();
417
418    let d: ColumnMeta = serde_json::from_str(&a).unwrap();
419
420    assert_eq!(meta, d);
421
422    // primary key column
423    let meta = ColumnMeta::Column(Described {
424        field: "name".to_string(),
425        ty: Ty::BigInt,
426        length: 8,
427        note: Some("PRIMARY KEY".to_string()),
428        compression: None,
429    });
430    let sql = meta.deref().sql_repr();
431
432    assert_eq!(sql, "`name` BIGINT PRIMARY KEY");
433
434    let a = serde_json::to_string(&meta).unwrap();
435
436    let d: ColumnMeta = serde_json::from_str(&a).unwrap();
437
438    assert_eq!(meta, d);
439
440    // with compression
441    let meta = ColumnMeta::Column(Described {
442        field: "name".to_string(),
443        ty: Ty::BigInt,
444        length: 8,
445        note: None,
446        compression: Some(CompressOptions::new("delta-i", "lz4", "medium")),
447    });
448    let sql = meta.deref().sql_repr();
449
450    assert_eq!(
451        sql,
452        "`name` BIGINT ENCODE 'delta-i' COMPRESS 'lz4' LEVEL 'medium'"
453    );
454
455    let a = serde_json::to_string(&meta).unwrap();
456
457    let d: ColumnMeta = serde_json::from_str(&a).unwrap();
458
459    assert_eq!(meta, d);
460
461    // primary key with compression
462    let meta = ColumnMeta::Column(Described {
463        field: "name".to_string(),
464        ty: Ty::BigInt,
465        length: 8,
466        note: Some("PRIMARY KEY".to_string()),
467        compression: Some(CompressOptions::new("delta-i", "lz4", "medium")),
468    });
469    let sql = meta.deref().sql_repr();
470
471    assert_eq!(
472        sql,
473        "`name` BIGINT ENCODE 'delta-i' COMPRESS 'lz4' LEVEL 'medium' PRIMARY KEY"
474    );
475
476    let a = serde_json::to_string(&meta).unwrap();
477
478    let d: ColumnMeta = serde_json::from_str(&a).unwrap();
479
480    assert_eq!(meta, d);
481
482    // deserialize from sequence.
483    let a = r#"["name", "BIGINT", 8, null, null, null, null]"#;
484    let d: ColumnMeta = serde_json::from_str(a).unwrap();
485    assert_eq!(
486        d,
487        ColumnMeta::Column(Described {
488            field: "name".to_string(),
489            ty: Ty::BigInt,
490            length: 8,
491            note: None,
492            compression: None,
493        })
494    );
495}