1use std::fmt;
2use std::str;
3
4use nom::bytes::complete::{tag, tag_no_case};
5use nom::character::complete::{multispace0, multispace1};
6use nom::combinator::{map, opt};
7use nom::multi::many0;
8use nom::sequence::{pair, terminated, tuple};
9use nom::IResult;
10
11use base::error::ParseSQLError;
12use base::{CommonParser, DisplayUtil};
13
14#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
16pub struct Table {
17 pub name: String,
19 pub alias: Option<String>,
21 pub schema: Option<String>,
23}
24
25impl Table {
26 pub fn table_list(i: &str) -> IResult<&str, Vec<Table>, ParseSQLError<&str>> {
29 many0(terminated(
30 Table::schema_table_reference,
31 opt(CommonParser::ws_sep_comma),
32 ))(i)
33 }
34
35 pub fn schema_table_reference(i: &str) -> IResult<&str, Table, ParseSQLError<&str>> {
37 map(
38 tuple((
39 opt(pair(CommonParser::sql_identifier, tag("."))),
40 CommonParser::sql_identifier,
41 opt(CommonParser::as_alias),
42 )),
43 |tup| Table {
44 name: String::from(tup.1),
45 alias: tup.2.map(String::from),
46 schema: tup.0.map(|(schema, _)| String::from(schema)),
47 },
48 )(i)
49 }
50
51 pub fn table_reference(i: &str) -> IResult<&str, Table, ParseSQLError<&str>> {
53 map(
54 pair(CommonParser::sql_identifier, opt(CommonParser::as_alias)),
55 |tup| Table {
56 name: String::from(tup.0),
57 alias: tup.1.map(String::from),
58 schema: None,
59 },
60 )(i)
61 }
62
63 pub fn without_alias(i: &str) -> IResult<&str, Table, ParseSQLError<&str>> {
65 map(
66 tuple((
67 opt(pair(CommonParser::sql_identifier, tag("."))),
68 CommonParser::sql_identifier,
69 )),
70 |tup| Table {
71 name: String::from(tup.1),
72 alias: None,
73 schema: tup.0.map(|(schema, _)| String::from(schema)),
74 },
75 )(i)
76 }
77
78 pub fn schema_table_reference_to_schema_table_reference(
80 i: &str,
81 ) -> IResult<&str, (Table, Table), ParseSQLError<&str>> {
82 map(
83 tuple((
84 Self::schema_table_reference,
85 multispace0,
86 tag_no_case("TO"),
87 multispace1,
88 Self::schema_table_reference,
89 )),
90 |(from, _, _, _, to)| (from, to),
91 )(i)
92 }
93}
94
95impl fmt::Display for Table {
96 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
97 if let Some(ref schema) = self.schema {
98 write!(f, "{}.", DisplayUtil::escape_if_keyword(schema))?;
99 }
100 write!(f, "{}", DisplayUtil::escape_if_keyword(&self.name))?;
101 if let Some(ref alias) = self.alias {
102 write!(f, " AS {}", DisplayUtil::escape_if_keyword(alias))?;
103 }
104 Ok(())
105 }
106}
107
108impl<'a> From<&'a str> for Table {
109 fn from(t: &str) -> Table {
110 Table {
111 name: String::from(t),
112 alias: None,
113 schema: None,
114 }
115 }
116}
117
118impl<'a> From<(&'a str, &'a str)> for Table {
119 fn from(t: (&str, &str)) -> Table {
120 Table {
121 name: String::from(t.1),
122 alias: None,
123 schema: Some(String::from(t.0)),
124 }
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use base::Table;
131
132 #[test]
133 fn parse_trigger() {
134 let str1 = "tbl_name";
135 let res1 = Table::table_reference(str1);
136 let exp1 = Table {
137 name: "tbl_name".to_string(),
138 alias: None,
139 schema: None,
140 };
141 assert!(res1.is_ok());
142 assert_eq!(res1.unwrap().1, exp1);
143
144 let str2 = "foo.tbl_name";
145 let res2 = Table::schema_table_reference(str2);
146 let exp2 = Table {
147 name: "tbl_name".to_string(),
148 alias: None,
149 schema: Some("foo".to_string()),
150 };
151 assert!(res2.is_ok());
152 assert_eq!(res2.unwrap().1, exp2);
153
154 let str3 = "foo.tbl_name as bar";
155 let res3 = Table::schema_table_reference(str3);
156 let exp3 = Table {
157 name: "tbl_name".to_string(),
158 alias: Some("bar".to_string()),
159 schema: Some("foo".to_string()),
160 };
161 assert!(res3.is_ok());
162 assert_eq!(res3.unwrap().1, exp3);
163 }
164
165 #[test]
166 fn from_str() {
167 let trigger1: Table = "tbl_name".into();
168 let exp1 = Table {
169 name: "tbl_name".to_string(),
170 alias: None,
171 schema: None,
172 };
173 assert_eq!(trigger1, exp1);
174 }
175
176 #[test]
177 fn from_tuple_str() {
178 let table2: Table = ("foo", "tbl_name").into();
179 let exp2 = Table {
180 name: "tbl_name".to_string(),
181 alias: None,
182 schema: Some("foo".to_string()),
183 };
184 assert_eq!(table2, exp2);
185 }
186}