clickhouse_sql_parser/
column.rs

1// vim: set expandtab ts=4 sw=4:
2
3use std::fmt; 
4use crate::keywords::{escape_if_keyword};
5use crate::{
6    SqlType,
7    create::{
8        CodecList,
9        ColumnTTL,
10    },
11};
12
13#[derive(Clone, Debug, Eq, Hash, PartialEq)]
14pub struct Column {
15    pub name: String,
16    pub alias: Option<String>,
17    pub table: Option<String>,
18}
19
20impl fmt::Display for Column {
21    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
22        if let Some(ref table) = self.table {
23            write!(
24                f,
25                "{}.{}",
26                escape_if_keyword(table),
27                escape_if_keyword(&self.name)
28            )?;
29        } else {
30            write!(f, "{}", escape_if_keyword(&self.name))?;
31        }
32        if let Some(ref alias) = self.alias {
33            write!(f, " AS {}", escape_if_keyword(alias))?;
34        }
35        Ok(())
36    }
37}
38
39impl<'a> From<&'a str> for Column {
40    fn from(c: &str) -> Column {
41        match c.find(".") {
42            None => Column {
43                name: String::from(c),
44                alias: None,
45                table: None,
46            },
47            Some(i) => Column {
48                name: String::from(&c[i + 1..]),
49                alias: None,
50                table: Some(String::from(&c[0..i])),
51            },
52        }
53    }
54}
55
56#[derive(Clone, Debug, Eq, Hash, PartialEq)]
57pub enum ColumnOption {
58    DefaultValue(String),
59    Materialized(String),
60}
61
62impl fmt::Display for ColumnOption {
63    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64        match *self {
65            ColumnOption::DefaultValue(ref literal) => {
66                write!(f, "DEFAULT {}", literal.to_string())
67            }
68            ColumnOption::Materialized(ref literal) => {
69                write!(f, "MATERIALIZED {}", literal.to_string())
70            }
71        }
72    }
73}
74
75#[derive(Clone, Debug, Eq, Hash, PartialEq)]
76pub struct ColumnSpecification {
77    pub column: Column,
78    pub sql_type: SqlType,
79    pub codec: Option<CodecList>,
80    pub ttl: Option<ColumnTTL>,
81    pub nullable: bool,
82    pub option: Option<ColumnOption>,
83    pub comment: Option<String>,
84    pub lowcardinality: bool,
85}
86
87impl fmt::Display for ColumnSpecification {
88    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
89        write!(f, "`{}` ", escape_if_keyword(&self.column.name))?;
90        match (self.lowcardinality, self.nullable) {
91            (false,false) => write!(f, "{}", self.sql_type),
92            (true,false) => write!(f, "LowCardinality({})", self.sql_type),
93            (false,true) => write!(f, "Nullable({})", self.sql_type),
94            (true,true) => write!(f, "LowCardinality(Nullable({}))", self.sql_type),
95        }?;
96        if let Some(ref opt) = self.option {
97            write!(f, " {}", opt)?;
98        }
99        if let Some(ref comment) = self.comment {
100            write!(f, " COMMENT '{}'", comment)?;
101        }
102        if let Some(ref codec) = self.codec {
103            write!(f, " CODEC({})",
104                codec.0
105                    .iter()
106                    .map(|c| format!("{}", c)) 
107                    .collect::<Vec<String>>()
108                    .join(", ")
109            )?;
110        }
111        if let Some(ref ttl) = self.ttl {
112            write!(f, " {}", ttl)?;
113        }
114        Ok(())
115    }
116}
117
118impl ColumnSpecification {
119    pub fn new(column: Column, sql_type: SqlType) -> ColumnSpecification {
120        ColumnSpecification {
121            column,
122            sql_type,
123            codec: None,
124            ttl: None,
125            nullable: false,
126            option: None,
127            comment: None,
128            lowcardinality: false,
129        }
130    }
131}
132
133#[cfg(test)]
134mod test {
135    use super::*;
136    use crate::{
137        TypeSize16,
138    };
139
140    #[test]
141    fn t_column_display() {
142        let cs = ColumnSpecification::new(
143            "time_local".into(),
144            SqlType::DateTime(None)
145        );
146
147        let exp = "`time_local` DateTime";
148        assert_eq!(exp, format!("{}", cs).as_str());
149    }
150
151    #[test]
152    fn t_column_display_enum() {
153        let cs = ColumnSpecification::new(
154            "device".into(),
155            SqlType::Enum(Some(TypeSize16::B8), vec![("desktop".into(), 1), ("mobile".into(),2)]),
156        );
157
158        let exp = "`device` Enum8('desktop' = 1, 'mobile' = 2)";
159        assert_eq!(exp, format!("{}", cs).as_str());
160    }
161
162}