Skip to main content

nom_sql/
create_table_options.rs

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    // TODO: make the create options accessible
13    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
39/// Helper to parse equals-separated create option pairs.
40/// Throws away the create option and value
41pub 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        // TODO(malte): imprecise hack, should not accept everything
93        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}