1use nom::character::complete::{alphanumeric1, multispace0, multispace1};
2
3use common::{integer_literal, sql_identifier, string_literal, ws_sep_comma, ws_sep_equals};
4use nom::branch::alt;
5use nom::bytes::complete::{tag, tag_no_case};
6use nom::combinator::{map, opt};
7use nom::multi::separated_list;
8use nom::sequence::tuple;
9use nom::IResult;
10
11pub fn table_options(i: &[u8]) -> IResult<&[u8], ()> {
12 map(
14 separated_list(table_options_separator, create_option),
15 |_| (),
16 )(i)
17}
18
19fn table_options_separator(i: &[u8]) -> IResult<&[u8], ()> {
20 map(alt((multispace1, ws_sep_comma)), |_| ())(i)
21}
22
23fn create_option(i: &[u8]) -> IResult<&[u8], ()> {
24 alt((
25 create_option_type,
26 create_option_pack_keys,
27 create_option_engine,
28 create_option_auto_increment,
29 create_option_default_charset,
30 create_option_collate,
31 create_option_comment,
32 create_option_max_rows,
33 create_option_avg_row_length,
34 create_option_row_format,
35 create_option_key_block_size,
36 ))(i)
37}
38
39pub fn create_option_equals_pair<'a, I, O1, O2, F, G>(
42 first: F,
43 second: G,
44) -> impl Fn(I) -> IResult<I, ()>
45where
46 F: Fn(I) -> IResult<I, O1>,
47 G: Fn(I) -> IResult<I, O2>,
48 I: nom::InputTakeAtPosition + nom::InputTake + nom::Compare<&'a str>,
49 <I as nom::InputTakeAtPosition>::Item: nom::AsChar + Clone,
50{
51 move |i: I| {
52 let (i, _o1) = first(i)?;
53 let (i, _) = ws_sep_equals(i)?;
54 let (i, _o2) = second(i)?;
55 Ok((i, ()))
56 }
57}
58
59fn create_option_type(i: &[u8]) -> IResult<&[u8], ()> {
60 create_option_equals_pair(tag_no_case("type"), alphanumeric1)(i)
61}
62
63fn create_option_pack_keys(i: &[u8]) -> IResult<&[u8], ()> {
64 create_option_equals_pair(tag_no_case("pack_keys"), alt((tag("0"), tag("1"))))(i)
65}
66
67fn create_option_engine(i: &[u8]) -> IResult<&[u8], ()> {
68 create_option_equals_pair(tag_no_case("engine"), opt(alphanumeric1))(i)
69}
70
71fn create_option_auto_increment(i: &[u8]) -> IResult<&[u8], ()> {
72 create_option_equals_pair(tag_no_case("auto_increment"), integer_literal)(i)
73}
74
75fn create_option_default_charset(i: &[u8]) -> IResult<&[u8], ()> {
76 create_option_equals_pair(
77 tag_no_case("default charset"),
78 alt((
79 tag("utf8mb4"),
80 tag("utf8"),
81 tag("binary"),
82 tag("big5"),
83 tag("ucs2"),
84 tag("latin1"),
85 )),
86 )(i)
87}
88
89fn create_option_collate(i: &[u8]) -> IResult<&[u8], ()> {
90 create_option_equals_pair(
91 tag_no_case("collate"),
92 sql_identifier,
94 )(i)
95}
96
97fn create_option_comment(i: &[u8]) -> IResult<&[u8], ()> {
98 create_option_equals_pair(tag_no_case("comment"), string_literal)(i)
99}
100
101fn create_option_max_rows(i: &[u8]) -> IResult<&[u8], ()> {
102 create_option_equals_pair(tag_no_case("max_rows"), integer_literal)(i)
103}
104
105fn create_option_avg_row_length(i: &[u8]) -> IResult<&[u8], ()> {
106 create_option_equals_pair(tag_no_case("avg_row_length"), integer_literal)(i)
107}
108
109fn create_option_row_format(i: &[u8]) -> IResult<&[u8], ()> {
110 let (remaining_input, (_, _, _, _, _)) = tuple((
111 tag_no_case("row_format"),
112 multispace0,
113 opt(tag("=")),
114 multispace0,
115 alt((
116 tag_no_case("DEFAULT"),
117 tag_no_case("DYNAMIC"),
118 tag_no_case("FIXED"),
119 tag_no_case("COMPRESSED"),
120 tag_no_case("REDUNDANT"),
121 tag_no_case("COMPACT"),
122 )),
123 ))(i)?;
124 Ok((remaining_input, ()))
125}
126
127fn create_option_key_block_size(i: &[u8]) -> IResult<&[u8], ()> {
128 let (remaining_input, (_, _, _, _, _)) = tuple((
129 tag_no_case("key_block_size"),
130 multispace0,
131 opt(tag("=")),
132 multispace0,
133 integer_literal,
134 ))(i)?;
135 Ok((remaining_input, ()))
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 fn should_parse_all(qstring: &str) {
143 assert_eq!(Ok((&b""[..], ())), table_options(qstring.as_bytes()))
144 }
145
146 #[test]
147 fn create_table_option_list_empty() {
148 should_parse_all("");
149 }
150
151 #[test]
152 fn create_table_option_list() {
153 should_parse_all(
154 "ENGINE=InnoDB AUTO_INCREMENT=44782967 \
155 DEFAULT CHARSET=binary ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8",
156 );
157 }
158
159 #[test]
160 fn create_table_option_list_commaseparated() {
161 should_parse_all("AUTO_INCREMENT=1,ENGINE=,KEY_BLOCK_SIZE=8");
162 }
163}