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#[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), KeyPart::parse, IndexOption::opt_index_option,
95 multispace0, 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#[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}