1use 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}