sqlparser_mysql/base/
table.rs

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/// **Table Definition**
15#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
16pub struct Table {
17    /// Table name
18    pub name: String,
19    /// Optional table name alias
20    pub alias: Option<String>,
21    /// Optional schema/database name
22    pub schema: Option<String>,
23}
24
25impl Table {
26    // Parse list of table names.
27    // XXX(malte): add support for aliases
28    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    // Parse a reference to a named schema.table, with an optional alias
36    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    // Parse a reference to a named table, with an optional alias
52    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    /// table alias not allowed in DROP/TRUNCATE/RENAME TABLE statement
64    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    /// db_name.tb_name TO db_name.tb_name
79    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}