senax_mysql_parser/
create_table_options.rs

1use nom::IResult;
2use nom::Parser;
3use nom::branch::alt;
4use nom::bytes::complete::{tag, tag_no_case};
5use nom::character::complete::{alphanumeric1, multispace0, multispace1};
6use nom::combinator::{map, opt};
7use nom::multi::separated_list0;
8use nom::sequence::{delimited, preceded};
9use serde::{Deserialize, Serialize};
10use std::fmt;
11
12use super::common::{integer_literal, string_literal, ws_sep_comma, ws_sep_equals};
13use crate::common::{Literal, sql_identifier};
14
15#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
16pub enum TableOption {
17    Comment(Literal),
18    Collation(String),
19    Engine(String),
20    Another,
21}
22
23impl fmt::Display for TableOption {
24    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
25        match self {
26            TableOption::Comment(comment) => write!(f, "COMMENT={}", comment.to_string()),
27            TableOption::Collation(collation) => write!(f, "COLLATE={}", collation),
28            TableOption::Engine(engine) => write!(f, "ENGINE={}", engine),
29            TableOption::Another => Ok(()),
30        }
31    }
32}
33
34pub fn table_options(i: &[u8]) -> IResult<&[u8], Vec<TableOption>> {
35    separated_list0(table_options_separator, create_option).parse(i)
36}
37
38fn table_options_separator(i: &[u8]) -> IResult<&[u8], ()> {
39    map(alt((multispace1, ws_sep_comma)), |_| ()).parse(i)
40}
41
42fn create_option(i: &[u8]) -> IResult<&[u8], TableOption> {
43    alt((
44        create_option_type,
45        create_option_pack_keys,
46        create_option_engine,
47        create_option_auto_increment,
48        create_option_default_charset,
49        create_option_collate,
50        create_option_quoted_collate,
51        create_option_comment,
52        create_option_max_rows,
53        create_option_avg_row_length,
54        create_option_row_format,
55        create_option_key_block_size,
56    ))
57    .parse(i)
58}
59
60/// Helper to parse equals-separated create option pairs.
61/// Throws away the create option and value
62pub fn create_option_equals_pair<'a, I, O1, O2, F, G>(
63    mut first: F,
64    mut second: G,
65) -> impl FnMut(I) -> IResult<I, TableOption>
66where
67    F: FnMut(I) -> IResult<I, O1>,
68    G: FnMut(I) -> IResult<I, O2>,
69    I: nom::Input + nom::Compare<&'a str>,
70    <I as nom::Input>::Item: nom::AsChar + Clone,
71{
72    move |i: I| {
73        let (i, _o1) = first(i)?;
74        let (i, _) = ws_sep_equals(i)?;
75        let (i, _o2) = second(i)?;
76        Ok((i, TableOption::Another))
77    }
78}
79
80fn create_option_type(i: &[u8]) -> IResult<&[u8], TableOption> {
81    create_option_equals_pair(tag_no_case("type"), alphanumeric1)(i)
82}
83
84pub fn pack_keys_literal(i: &[u8]) -> IResult<&[u8], &[u8]> {
85    alt((tag("0"), tag("1"))).parse(i)
86}
87
88fn create_option_pack_keys(i: &[u8]) -> IResult<&[u8], TableOption> {
89    create_option_equals_pair(tag_no_case("pack_keys"), pack_keys_literal)(i)
90}
91
92fn create_option_engine(i: &[u8]) -> IResult<&[u8], TableOption> {
93    map(
94        preceded(
95            delimited(multispace0, tag_no_case("engine"), ws_sep_equals),
96            sql_identifier,
97        ),
98        |s| TableOption::Engine(String::from_utf8_lossy(s).to_string()),
99    )
100    .parse(i)
101}
102
103fn create_option_auto_increment(i: &[u8]) -> IResult<&[u8], TableOption> {
104    create_option_equals_pair(tag_no_case("auto_increment"), integer_literal)(i)
105}
106
107pub fn charset_literal(i: &[u8]) -> IResult<&[u8], &[u8]> {
108    alt((
109        tag("utf8mb4"),
110        tag("utf8"),
111        tag("binary"),
112        tag("big5"),
113        tag("ucs2"),
114        tag("latin1"),
115    ))
116    .parse(i)
117}
118
119fn create_option_default_charset(i: &[u8]) -> IResult<&[u8], TableOption> {
120    create_option_equals_pair(tag_no_case("default charset"), charset_literal)(i)
121}
122
123fn create_option_collate(i: &[u8]) -> IResult<&[u8], TableOption> {
124    map(
125        preceded(
126            delimited(multispace0, tag_no_case("collate"), ws_sep_equals),
127            sql_identifier,
128        ),
129        |s| TableOption::Collation(String::from_utf8_lossy(s).to_string()),
130    )
131    .parse(i)
132}
133
134fn create_option_quoted_collate(i: &[u8]) -> IResult<&[u8], TableOption> {
135    map(
136        preceded(
137            delimited(multispace0, tag_no_case("collate"), ws_sep_equals),
138            string_literal,
139        ),
140        |s| TableOption::Collation(s.to_raw_string()),
141    )
142    .parse(i)
143}
144
145fn create_option_comment(i: &[u8]) -> IResult<&[u8], TableOption> {
146    map(
147        preceded(
148            delimited(multispace0, tag_no_case("comment"), ws_sep_equals),
149            string_literal,
150        ),
151        TableOption::Comment,
152    )
153    .parse(i)
154}
155
156fn create_option_max_rows(i: &[u8]) -> IResult<&[u8], TableOption> {
157    create_option_equals_pair(tag_no_case("max_rows"), integer_literal)(i)
158}
159
160fn create_option_avg_row_length(i: &[u8]) -> IResult<&[u8], TableOption> {
161    create_option_equals_pair(tag_no_case("avg_row_length"), integer_literal)(i)
162}
163
164fn create_option_row_format(i: &[u8]) -> IResult<&[u8], TableOption> {
165    let (remaining_input, (_, _, _, _, _)) = (
166        tag_no_case("row_format"),
167        multispace0,
168        opt(tag("=")),
169        multispace0,
170        alt((
171            tag_no_case("DEFAULT"),
172            tag_no_case("DYNAMIC"),
173            tag_no_case("FIXED"),
174            tag_no_case("COMPRESSED"),
175            tag_no_case("REDUNDANT"),
176            tag_no_case("COMPACT"),
177        )),
178    )
179        .parse(i)?;
180    Ok((remaining_input, TableOption::Another))
181}
182
183fn create_option_key_block_size(i: &[u8]) -> IResult<&[u8], TableOption> {
184    let (remaining_input, (_, _, _, _, _)) = (
185        tag_no_case("key_block_size"),
186        multispace0,
187        opt(tag("=")),
188        multispace0,
189        integer_literal,
190    )
191        .parse(i)?;
192    Ok((remaining_input, TableOption::Another))
193}