nom_sql/
column.rs

1use std::cmp::Ordering;
2use std::fmt::{self, Display};
3use std::str;
4
5use case::CaseWhenExpression;
6use common::{Literal, SqlType};
7use keywords::escape_if_keyword;
8
9#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
10pub enum FunctionExpression {
11    Avg(FunctionArguments, bool),
12    Count(FunctionArguments, bool),
13    CountStar,
14    Sum(FunctionArguments, bool),
15    Max(FunctionArguments),
16    Min(FunctionArguments),
17    GroupConcat(FunctionArguments, String),
18}
19
20impl Display for FunctionExpression {
21    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
22        match *self {
23            FunctionExpression::Avg(ref col, d) if d => write!(f, "avg(distinct {})", col),
24            FunctionExpression::Count(ref col, d) if d => write!(f, "count(distinct {})", col),
25            FunctionExpression::Sum(ref col, d) if d => write!(f, "sum(distinct {})", col),
26
27            FunctionExpression::Avg(ref col, _) => write!(f, "avg({})", col),
28            FunctionExpression::Count(ref col, _) => write!(f, "count({})", col),
29            FunctionExpression::CountStar => write!(f, "count(*)"),
30            FunctionExpression::Sum(ref col, _) => write!(f, "sum({})", col),
31            FunctionExpression::Max(ref col) => write!(f, "max({})", col),
32            FunctionExpression::Min(ref col) => write!(f, "min({})", col),
33            FunctionExpression::GroupConcat(ref col, ref s) => {
34                write!(f, "group_concat({}, {})", col, s)
35            }
36        }
37    }
38}
39
40#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
41pub enum FunctionArguments {
42    Column(Column),
43    Conditional(CaseWhenExpression),
44}
45
46impl Display for FunctionArguments {
47    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
48        match *self {
49            FunctionArguments::Column(ref col) => write!(f, "{}", col)?,
50            FunctionArguments::Conditional(ref e) => {
51                write!(f, "{}", e)?;
52            }
53        }
54        Ok(())
55    }
56}
57
58#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
59pub struct Column {
60    pub name: String,
61    pub alias: Option<String>,
62    pub table: Option<String>,
63    pub function: Option<Box<FunctionExpression>>,
64}
65
66impl fmt::Display for Column {
67    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68        if let Some(ref table) = self.table {
69            write!(
70                f,
71                "{}.{}",
72                escape_if_keyword(table),
73                escape_if_keyword(&self.name)
74            )?;
75        } else if let Some(ref function) = self.function {
76            write!(f, "{}", *function)?;
77        } else {
78            write!(f, "{}", escape_if_keyword(&self.name))?;
79        }
80        if let Some(ref alias) = self.alias {
81            write!(f, " AS {}", escape_if_keyword(alias))?;
82        }
83        Ok(())
84    }
85}
86
87impl<'a> From<&'a str> for Column {
88    fn from(c: &str) -> Column {
89        match c.find(".") {
90            None => Column {
91                name: String::from(c),
92                alias: None,
93                table: None,
94                function: None,
95            },
96            Some(i) => Column {
97                name: String::from(&c[i + 1..]),
98                alias: None,
99                table: Some(String::from(&c[0..i])),
100                function: None,
101            },
102        }
103    }
104}
105
106impl Ord for Column {
107    fn cmp(&self, other: &Column) -> Ordering {
108        if self.table.is_some() && other.table.is_some() {
109            match self.table.cmp(&other.table) {
110                Ordering::Equal => self.name.cmp(&other.name),
111                x => x,
112            }
113        } else {
114            self.name.cmp(&other.name)
115        }
116    }
117}
118
119impl PartialOrd for Column {
120    fn partial_cmp(&self, other: &Column) -> Option<Ordering> {
121        if self.table.is_some() && other.table.is_some() {
122            match self.table.cmp(&other.table) {
123                Ordering::Equal => Some(self.name.cmp(&other.name)),
124                x => Some(x),
125            }
126        } else if self.table.is_none() && other.table.is_none() {
127            Some(self.name.cmp(&other.name))
128        } else {
129            None
130        }
131    }
132}
133
134#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
135pub enum ColumnConstraint {
136    NotNull,
137    CharacterSet(String),
138    Collation(String),
139    DefaultValue(Literal),
140    AutoIncrement,
141    PrimaryKey,
142    Unique,
143}
144
145impl fmt::Display for ColumnConstraint {
146    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
147        match *self {
148            ColumnConstraint::NotNull => write!(f, "NOT NULL"),
149            ColumnConstraint::CharacterSet(ref charset) => write!(f, "CHARACTER SET {}", charset),
150            ColumnConstraint::Collation(ref collation) => write!(f, "COLLATE {}", collation),
151            ColumnConstraint::DefaultValue(ref literal) => {
152                write!(f, "DEFAULT {}", literal.to_string())
153            }
154            ColumnConstraint::AutoIncrement => write!(f, "AUTO_INCREMENT"),
155            ColumnConstraint::PrimaryKey => write!(f, "PRIMARY KEY"),
156            ColumnConstraint::Unique => write!(f, "UNIQUE"),
157        }
158    }
159}
160
161#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
162pub struct ColumnSpecification {
163    pub column: Column,
164    pub sql_type: SqlType,
165    pub constraints: Vec<ColumnConstraint>,
166    pub comment: Option<String>,
167}
168
169impl fmt::Display for ColumnSpecification {
170    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
171        write!(
172            f,
173            "{} {}",
174            escape_if_keyword(&self.column.name),
175            self.sql_type
176        )?;
177        for constraint in self.constraints.iter() {
178            write!(f, " {}", constraint)?;
179        }
180        if let Some(ref comment) = self.comment {
181            write!(f, " COMMENT '{}'", comment)?;
182        }
183        Ok(())
184    }
185}
186
187impl ColumnSpecification {
188    pub fn new(column: Column, sql_type: SqlType) -> ColumnSpecification {
189        ColumnSpecification {
190            column,
191            sql_type,
192            constraints: vec![],
193            comment: None,
194        }
195    }
196
197    pub fn with_constraints(
198        column: Column,
199        sql_type: SqlType,
200        constraints: Vec<ColumnConstraint>,
201    ) -> ColumnSpecification {
202        ColumnSpecification {
203            column,
204            sql_type,
205            constraints,
206            comment: None,
207        }
208    }
209}
210
211#[cfg(test)]
212mod tests {
213    use super::*;
214
215    #[test]
216    fn column_from_str() {
217        let s = "table.col";
218        let c = Column::from(s);
219
220        assert_eq!(
221            c,
222            Column {
223                name: String::from("col"),
224                alias: None,
225                table: Some(String::from("table")),
226                function: None,
227            }
228        );
229    }
230
231    #[test]
232    fn print_function_column() {
233        let c1 = Column {
234            name: "".into(), // must be present, but will be ignored
235            alias: Some("foo".into()),
236            table: None,
237            function: Some(Box::new(FunctionExpression::CountStar)),
238        };
239        let c2 = Column {
240            name: "".into(), // must be present, but will be ignored
241            alias: None,
242            table: None,
243            function: Some(Box::new(FunctionExpression::CountStar)),
244        };
245        let c3 = Column {
246            name: "".into(), // must be present, but will be ignored
247            alias: None,
248            table: None,
249            function: Some(Box::new(FunctionExpression::Sum(
250                FunctionArguments::Column(Column::from("mytab.foo")),
251                false,
252            ))),
253        };
254
255        assert_eq!(format!("{}", c1), "count(*) AS foo");
256        assert_eq!(format!("{}", c2), "count(*)");
257        assert_eq!(format!("{}", c3), "sum(mytab.foo)");
258    }
259}