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
58pub 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}