Skip to main content

qusql_parse/
flush.rs

1use alloc::vec::Vec;
2
3use crate::{
4    Identifier, QualifiedName, Span, Spanned,
5    keywords::Keyword,
6    lexer::Token,
7    parser::{ParseError, Parser},
8    qualified_name::parse_qualified_name_unreserved,
9};
10
11#[derive(Debug, Clone)]
12pub enum FlushOption<'a> {
13    BinaryLogs(Span),
14    EngineLogs(Span),
15    ErrorLogs(Span),
16    GeneralLogs(Span),
17    Logs(Span),
18    Privileges(Span),
19    OptimizerCosts(Span),
20    RelayLogs(Span),
21    RelayLogsForChannel {
22        span: Span,
23        channel: Identifier<'a>,
24    },
25    SlowLogs(Span),
26    Status(Span),
27    UserResources(Span),
28    Table {
29        span: Span,
30        tables: Vec<QualifiedName<'a>>,
31        with_read_lock: Option<Span>,
32        for_export: Option<Span>,
33    },
34}
35
36impl Spanned for FlushOption<'_> {
37    fn span(&self) -> Span {
38        match self {
39            FlushOption::BinaryLogs(span)
40            | FlushOption::EngineLogs(span)
41            | FlushOption::ErrorLogs(span)
42            | FlushOption::GeneralLogs(span)
43            | FlushOption::Logs(span)
44            | FlushOption::Privileges(span)
45            | FlushOption::OptimizerCosts(span)
46            | FlushOption::RelayLogs(span)
47            | FlushOption::SlowLogs(span)
48            | FlushOption::Status(span)
49            | FlushOption::UserResources(span) => span.clone(),
50            FlushOption::RelayLogsForChannel { span, channel } => span.join_span(channel),
51            FlushOption::Table {
52                span,
53                tables,
54                with_read_lock,
55                for_export,
56            } => span
57                .join_span(tables)
58                .join_span(with_read_lock)
59                .join_span(for_export),
60        }
61    }
62}
63
64/// Parse mariadb FLUSH statement.
65/// ```
66/// # use qusql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, Statement, Issues};
67/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
68/// let sql = "FLUSH TABLES t1, t2 WITH READ LOCK;";
69/// let mut issues = Issues::new(sql);
70/// let mut stmts = parse_statements(sql, &mut issues, &options);
71/// # assert!(issues.is_ok(), "{}", issues);
72/// match stmts.pop() {
73///   Some(Statement::Flush(f)) => f,
74/// _ => panic!("We should get a flush statement"),
75/// };
76/// ```
77#[derive(Debug, Clone)]
78pub struct Flush<'a> {
79    pub flush_span: Span,
80    pub no_write_to_binlog: Option<Span>,
81    pub local: Option<Span>,
82    pub options: Vec<FlushOption<'a>>,
83}
84
85impl Spanned for Flush<'_> {
86    fn span(&self) -> Span {
87        self.flush_span
88            .join_span(&self.no_write_to_binlog)
89            .join_span(&self.local)
90            .join_span(&self.options)
91    }
92}
93
94pub(crate) fn parse_flush<'a>(parser: &mut Parser<'a, '_>) -> Result<Flush<'a>, ParseError> {
95    let flush_span = parser.consume_keyword(Keyword::FLUSH)?;
96
97    let no_write_to_binlog = parser.skip_keyword(Keyword::NO_WRITE_TO_BINLOG);
98    let local = parser.skip_keyword(Keyword::LOCAL);
99    let mut options = Vec::new();
100    loop {
101        match &parser.token {
102            Token::Ident(_, Keyword::BINARY) => {
103                let span = parser.consume_keywords(&[Keyword::BINARY, Keyword::LOGS])?;
104                options.push(FlushOption::BinaryLogs(span));
105            }
106            Token::Ident(_, Keyword::ENGINE) => {
107                let span = parser.consume_keywords(&[Keyword::ENGINE, Keyword::LOGS])?;
108                options.push(FlushOption::EngineLogs(span));
109            }
110            Token::Ident(_, Keyword::ERROR) => {
111                let span = parser.consume_keywords(&[Keyword::ERROR, Keyword::LOGS])?;
112                options.push(FlushOption::ErrorLogs(span));
113            }
114            Token::Ident(_, Keyword::GENERAL) => {
115                let span = parser.consume_keywords(&[Keyword::GENERAL, Keyword::LOGS])?;
116                options.push(FlushOption::GeneralLogs(span));
117            }
118            Token::Ident(_, Keyword::LOGS) => {
119                let span = parser.consume_keyword(Keyword::LOGS)?;
120                options.push(FlushOption::Logs(span));
121            }
122            Token::Ident(_, Keyword::PRIVILEGES) => {
123                let span = parser.consume_keyword(Keyword::PRIVILEGES)?;
124                options.push(FlushOption::Privileges(span));
125            }
126            Token::Ident(_, Keyword::OPTIMIZER_COSTS) => {
127                let span = parser.consume_keyword(Keyword::OPTIMIZER_COSTS)?;
128                options.push(FlushOption::OptimizerCosts(span));
129            }
130            Token::Ident(_, Keyword::RELAY) => {
131                let span = parser.consume_keywords(&[Keyword::RELAY, Keyword::LOGS])?;
132                if let Some(for_span) = parser.skip_keyword(Keyword::FOR) {
133                    let span = span
134                        .join_span(&for_span)
135                        .join_span(&parser.consume_keyword(Keyword::CHANNEL)?);
136                    options.push(FlushOption::RelayLogsForChannel {
137                        span,
138                        channel: parser.consume_plain_identifier_unreserved()?,
139                    });
140                } else {
141                    options.push(FlushOption::RelayLogs(span));
142                };
143            }
144            Token::Ident(_, Keyword::SLOW) => {
145                let span = parser.consume_keywords(&[Keyword::SLOW, Keyword::LOGS])?;
146                options.push(FlushOption::SlowLogs(span));
147            }
148            Token::Ident(_, Keyword::STATUS) => {
149                let span = parser.consume_keyword(Keyword::STATUS)?;
150                options.push(FlushOption::Status(span));
151            }
152            Token::Ident(_, Keyword::USER_RESOURCES) => {
153                let span = parser.consume_keyword(Keyword::USER_RESOURCES)?;
154                options.push(FlushOption::UserResources(span));
155            }
156            Token::Ident(_, Keyword::TABLE | Keyword::TABLES) => {
157                let span = parser.consume_keyword(Keyword::TABLES)?;
158                let mut tables = Vec::new();
159
160                if let Token::Ident(_, kw) = parser.token
161                    && !kw.restricted(parser.reserved())
162                {
163                    loop {
164                        tables.push(parse_qualified_name_unreserved(parser)?);
165                        if parser.skip_token(Token::Comma).is_none() {
166                            break;
167                        }
168                    }
169                }
170
171                let with_read_lock = if let Some(with_span) = parser.skip_keyword(Keyword::WITH) {
172                    Some(
173                        with_span
174                            .join_span(&parser.consume_keywords(&[Keyword::READ, Keyword::LOCK])?),
175                    )
176                } else {
177                    None
178                };
179                let for_export = if let Some(for_span) = parser.skip_keyword(Keyword::FOR) {
180                    Some(for_span.join_span(&parser.consume_keyword(Keyword::EXPORT)?))
181                } else {
182                    None
183                };
184
185                options.push(FlushOption::Table {
186                    span,
187                    tables,
188                    with_read_lock,
189                    for_export,
190                });
191            }
192            _ => break,
193        }
194    }
195
196    Ok(Flush {
197        flush_span,
198        no_write_to_binlog,
199        local,
200        options,
201    })
202}