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(), alias: Some("foo".into()),
236 table: None,
237 function: Some(Box::new(FunctionExpression::CountStar)),
238 };
239 let c2 = Column {
240 name: "".into(), alias: None,
242 table: None,
243 function: Some(Box::new(FunctionExpression::CountStar)),
244 };
245 let c3 = Column {
246 name: "".into(), 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}