sqlparser_mysql/dds/
create_index.rs

1use nom::branch::alt;
2use nom::bytes::complete::tag_no_case;
3use nom::character::complete::{multispace0, multispace1};
4use nom::combinator::{map, opt};
5use nom::sequence::{terminated, tuple};
6use nom::IResult;
7use std::fmt::{write, Display, Formatter};
8
9use base::algorithm_type::AlgorithmType;
10use base::error::ParseSQLError;
11use base::index_option::IndexOption;
12use base::index_type::IndexType;
13use base::lock_type::LockType;
14use base::table::Table;
15use base::{CommonParser, KeyPart};
16
17/// parse `CREATE [UNIQUE | FULLTEXT | SPATIAL] INDEX index_name
18///     [index_type]
19///     ON tbl_name (key_part,...)
20///     [index_option]
21///     [algorithm_option | lock_option] ...`
22///
23/// `key_part: {col_name [(length)] | (expr)} [ASC | DESC]`
24///
25/// `index_option: {
26///     KEY_BLOCK_SIZE [=] value
27///   | index_type
28///   | WITH PARSER parser_name
29///   | COMMENT 'string'
30///   | {VISIBLE | INVISIBLE}
31///   | ENGINE_ATTRIBUTE [=] 'string'
32///   | SECONDARY_ENGINE_ATTRIBUTE [=] 'string'
33/// }`
34///
35/// `index_type:
36///     USING {BTREE | HASH}`
37///
38/// `algorithm_option:
39///     ALGORITHM [=] {DEFAULT | INPLACE | COPY}`
40///
41/// `lock_option:
42///     LOCK [=] {DEFAULT | NONE | SHARED | EXCLUSIVE}`
43#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
44pub struct CreateIndexStatement {
45    pub opt_index: Option<Index>,
46    pub index_name: String,
47    pub index_type: Option<IndexType>,
48    pub table: Table,
49    pub key_part: Vec<KeyPart>,
50    pub index_option: Option<Vec<IndexOption>>,
51    pub algorithm_option: Option<AlgorithmType>,
52    pub lock_option: Option<LockType>,
53}
54
55impl Display for CreateIndexStatement {
56    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
57        write!(f, "CREATE");
58        if let Some(opt_index) = &self.opt_index {
59            write!(f, " {}", opt_index);
60        }
61        write!(f, " INDEX {}", self.index_name);
62        if let Some(index_type) = &self.index_type {
63            write!(f, " {}", index_type);
64        }
65        write!(f, " ON {}", self.table);
66        write!(f, " {}", KeyPart::format_list(&self.key_part));
67        if let Some(index_option) = &self.index_option {
68            write!(f, " {}", IndexOption::format_list(index_option));
69        }
70        if let Some(algorithm_option) = &self.algorithm_option {
71            write!(f, " {}", algorithm_option);
72        }
73        if let Some(lock_option) = &self.lock_option {
74            write!(f, " {}", lock_option);
75        }
76        Ok(())
77    }
78}
79
80impl CreateIndexStatement {
81    pub fn parse(i: &str) -> IResult<&str, CreateIndexStatement, ParseSQLError<&str>> {
82        map(
83            tuple((
84                tuple((tag_no_case("CREATE"), multispace1)),
85                opt(terminated(Index::parse, multispace1)),
86                tuple((tag_no_case("INDEX"), multispace1)),
87                map(tuple((CommonParser::sql_identifier, multispace1)), |x| {
88                    String::from(x.0)
89                }),
90                opt(terminated(IndexType::parse, multispace1)),
91                terminated(tag_no_case("ON"), multispace1),
92                terminated(Table::without_alias, multispace1), // tbl_name
93                KeyPart::parse,                                // (key_part,...)
94                IndexOption::opt_index_option,
95                multispace0, // [index_option]
96                opt(terminated(AlgorithmType::parse, multispace0)),
97                opt(terminated(LockType::parse, multispace0)),
98                CommonParser::statement_terminator,
99            )),
100            |(
101                _,
102                opt_index,
103                _,
104                index_name,
105                index_type,
106                _,
107                table,
108                key_part,
109                index_option,
110                _,
111                algorithm_option,
112                lock_option,
113                _,
114            )| CreateIndexStatement {
115                opt_index,
116                index_name,
117                index_type,
118                table,
119                key_part,
120                index_option,
121                algorithm_option,
122                lock_option,
123            },
124        )(i)
125    }
126}
127
128/// `[UNIQUE | FULLTEXT | SPATIAL]`
129#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
130pub enum Index {
131    Unique,
132    Fulltext,
133    Spatial,
134}
135
136impl Display for Index {
137    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
138        match *self {
139            Index::Unique => write!(f, "UNIQUE"),
140            Index::Fulltext => write!(f, "FULLTEXT"),
141            Index::Spatial => write!(f, "SPATIAL"),
142        }
143    }
144}
145
146impl Index {
147    fn parse(i: &str) -> IResult<&str, Index, ParseSQLError<&str>> {
148        alt((
149            map(tag_no_case("UNIQUE"), |_| Index::Unique),
150            map(tag_no_case("FULLTEXT"), |_| Index::Fulltext),
151            map(tag_no_case("SPATIAL"), |_| Index::Spatial),
152        ))(i)
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use base::{KeyPart, KeyPartType};
159    use dds::create_index::CreateIndexStatement;
160
161    #[test]
162    fn parse_create_index() {
163        let sqls = [
164            "create index idx_1 on tbl_foo (age);",
165            "create index idx_2 on tbl_bar (name, age);",
166        ];
167        let exp_statements = [
168            CreateIndexStatement {
169                opt_index: None,
170                index_name: "idx_1".to_string(),
171                index_type: None,
172                table: "tbl_foo".into(),
173                key_part: vec![KeyPart {
174                    r#type: KeyPartType::ColumnNameWithLength {
175                        col_name: "age".to_string(),
176                        length: None,
177                    },
178                    order: None,
179                }],
180                index_option: None,
181                algorithm_option: None,
182                lock_option: None,
183            },
184            CreateIndexStatement {
185                opt_index: None,
186                index_name: "idx_2".to_string(),
187                index_type: None,
188                table: "tbl_bar".into(),
189                key_part: vec![
190                    KeyPart {
191                        r#type: KeyPartType::ColumnNameWithLength {
192                            col_name: "name".to_string(),
193                            length: None,
194                        },
195                        order: None,
196                    },
197                    KeyPart {
198                        r#type: KeyPartType::ColumnNameWithLength {
199                            col_name: "age".to_string(),
200                            length: None,
201                        },
202                        order: None,
203                    },
204                ],
205                index_option: None,
206                algorithm_option: None,
207                lock_option: None,
208            },
209        ];
210
211        for i in 0..sqls.len() {
212            let res = CreateIndexStatement::parse(sqls[i]);
213            assert!(res.is_ok());
214            assert_eq!(res.unwrap().1, exp_statements[i]);
215        }
216    }
217}