senax_mysql_parser/
create_table_options.rs

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