1use core::fmt;
2use std::fmt::Formatter;
3
4use nom::branch::alt;
5use nom::bytes::complete::{tag, tag_no_case};
6use nom::character::complete::{multispace0, multispace1};
7use nom::combinator::{map, opt};
8use nom::multi::many1;
9use nom::sequence::{terminated, tuple};
10use nom::IResult;
11
12use base::error::ParseSQLError;
13use base::{CommonParser, DefaultOrZeroOrOne};
14
15#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
25pub struct AlterDatabaseStatement {
26 pub db_name: String,
28 pub alter_options: Vec<AlterDatabaseOption>,
29}
30
31impl AlterDatabaseStatement {
32 pub fn parse(i: &str) -> IResult<&str, AlterDatabaseStatement, ParseSQLError<&str>> {
33 map(
34 tuple((
35 tag_no_case("ALTER"),
36 multispace0,
37 alt((tag_no_case("DATABASE"), tag_no_case("SCHEMA"))),
38 multispace1,
39 map(CommonParser::sql_identifier, String::from),
40 multispace1,
41 many1(terminated(AlterDatabaseOption::parse, multispace0)),
42 )),
43 |x| AlterDatabaseStatement {
44 db_name: x.4,
45 alter_options: x.6,
46 },
47 )(i)
48 }
49}
50
51impl fmt::Display for AlterDatabaseStatement {
52 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
53 write!(f, "ALTER DATABASE")?;
54 let database = self.db_name.clone();
55 write!(f, " {}", database)?;
56 for alter_option in self.alter_options.iter() {
57 write!(f, " {}", alter_option)?;
58 }
59 Ok(())
60 }
61}
62
63#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
70pub enum AlterDatabaseOption {
71 CharacterSet(String),
72 Collate(String),
73 Encryption(bool),
74 ReadOnly(DefaultOrZeroOrOne),
75}
76
77impl AlterDatabaseOption {
78 fn parse(i: &str) -> IResult<&str, AlterDatabaseOption, ParseSQLError<&str>> {
79 let character = map(
81 tuple((
82 opt(tag_no_case("DEFAULT")),
83 multispace1,
84 tuple((
85 tag_no_case("CHARACTER"),
86 multispace1,
87 tag_no_case("SET"),
88 multispace0,
89 opt(tag("=")),
90 multispace0,
91 )),
92 map(CommonParser::sql_identifier, String::from),
93 multispace0,
94 )),
95 |(_, _, _, charset_name, _)| AlterDatabaseOption::CharacterSet(charset_name),
96 );
97
98 let collate = map(
100 tuple((
101 opt(tag_no_case("DEFAULT")),
102 multispace1,
103 map(
104 tuple((
105 tag_no_case("COLLATE"),
106 multispace0,
107 opt(tag("=")),
108 multispace0,
109 CommonParser::sql_identifier,
110 multispace0,
111 )),
112 |(_, _, _, _, collation_name, _)| String::from(collation_name),
113 ),
114 multispace0,
115 )),
116 |(_, _, collation_name, _)| AlterDatabaseOption::Collate(collation_name),
117 );
118
119 let encryption = map(
121 tuple((
122 opt(tag_no_case("DEFAULT")),
123 multispace1,
124 tag_no_case("ENCRYPTION"),
125 multispace1,
126 opt(tag("=")),
127 multispace0,
128 alt((map(tag("'Y'"), |_| true), map(tag("'N'"), |_| false))),
129 multispace0,
130 )),
131 |x| AlterDatabaseOption::Encryption(x.6),
132 );
133
134 let read_only = alt((
136 map(
137 tuple((
138 opt(tag_no_case("READ")),
139 multispace1,
140 tag_no_case("ONLY"),
141 multispace1,
142 DefaultOrZeroOrOne::parse,
143 )),
144 |(_, _, _, _, value)| AlterDatabaseOption::ReadOnly(value),
145 ),
146 map(
147 tuple((
148 opt(tag_no_case("READ")),
149 multispace1,
150 tag_no_case("ONLY"),
151 multispace0,
152 tag("="),
153 multispace0,
154 DefaultOrZeroOrOne::parse,
155 )),
156 |(_, _, _, _, _, _, value)| AlterDatabaseOption::ReadOnly(value),
157 ),
158 ));
159
160 alt((character, collate, encryption, read_only))(i)
161 }
162}
163
164impl fmt::Display for AlterDatabaseOption {
165 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
166 match self {
167 AlterDatabaseOption::CharacterSet(str) => write!(f, " CHARACTER SET {}", str)?,
168 AlterDatabaseOption::Collate(str) => write!(f, " COLLATE {}", str)?,
169 AlterDatabaseOption::Encryption(bl) => {
170 if *bl {
171 write!(f, " ENCRYPTION 'Y'",)?
172 } else {
173 write!(f, " ENCRYPTION 'N'",)?
174 }
175 }
176 AlterDatabaseOption::ReadOnly(val) => write!(f, " READ ONLY {}", val)?,
177 }
178 Ok(())
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use base::DefaultOrZeroOrOne;
185 use dds::alter_database::{AlterDatabaseOption, AlterDatabaseStatement};
186
187 #[test]
188 fn test_alter_database() {
189 let sqls = ["ALTER DATABASE test_db DEFAULT CHARACTER SET = utf8mb4 \
190 DEFAULT COLLATE utf8mb4_unicode_ci DEFAULT ENCRYPTION = 'Y' READ ONLY = 1;"];
191 let exp_statements = [AlterDatabaseStatement {
192 db_name: "test_db".to_string(),
193 alter_options: vec![
194 AlterDatabaseOption::CharacterSet("utf8mb4".to_string()),
195 AlterDatabaseOption::Collate("utf8mb4_unicode_ci".to_string()),
196 AlterDatabaseOption::Encryption(true),
197 AlterDatabaseOption::ReadOnly(DefaultOrZeroOrOne::One),
198 ],
199 }];
200 for i in 0..sqls.len() {
201 let res = AlterDatabaseStatement::parse(sqls[i]);
202 assert!(res.is_ok());
203 assert_eq!(res.unwrap().1, exp_statements[i]);
204 }
205 }
206}