sqlparser_mysql/dds/
alter_database.rs

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/// parse `ALTER {DATABASE | SCHEMA} [db_name]
16///     alter_option ...`
17///
18/// `alter_option: {
19///     [DEFAULT] CHARACTER SET [=] charset_name
20///   | [DEFAULT] COLLATE [=] collation_name
21///   | [DEFAULT] ENCRYPTION [=] {'Y' | 'N'}
22///   | READ ONLY [=] {DEFAULT | 0 | 1}
23/// }`
24#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
25pub struct AlterDatabaseStatement {
26    // we parse SQL, db_name is needed
27    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/// `alter_option: {
64///     [DEFAULT] CHARACTER SET [=] charset_name
65///   | [DEFAULT] COLLATE [=] collation_name
66///   | [DEFAULT] ENCRYPTION [=] {'Y' | 'N'}
67///   | READ ONLY [=] {DEFAULT | 0 | 1}
68/// }`
69#[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        // [DEFAULT] CHARACTER SET [=] charset_name
80        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        // [DEFAULT] COLLATE [=] collation_name
99        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        // [DEFAULT] ENCRYPTION [=] {'Y' | 'N'}
120        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        // READ ONLY [=] {DEFAULT | 0 | 1}
135        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}