sqlparser/dialect/
mysql.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5// http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12
13#[cfg(not(feature = "std"))]
14use alloc::boxed::Box;
15
16use crate::{
17    ast::{BinaryOperator, Expr, LockTable, LockTableType, Statement},
18    dialect::Dialect,
19    keywords::Keyword,
20    parser::{Parser, ParserError},
21};
22
23/// A [`Dialect`] for [MySQL](https://www.mysql.com/)
24#[derive(Debug)]
25pub struct MySqlDialect {}
26
27impl Dialect for MySqlDialect {
28    fn is_identifier_start(&self, ch: char) -> bool {
29        // See https://dev.mysql.com/doc/refman/8.0/en/identifiers.html.
30        // Identifiers which begin with a digit are recognized while tokenizing numbers,
31        // so they can be distinguished from exponent numeric literals.
32        ch.is_alphabetic()
33            || ch == '_'
34            || ch == '$'
35            || ch == '@'
36            || ('\u{0080}'..='\u{ffff}').contains(&ch)
37    }
38
39    fn is_identifier_part(&self, ch: char) -> bool {
40        self.is_identifier_start(ch) || ch.is_ascii_digit()
41    }
42
43    fn is_delimited_identifier_start(&self, ch: char) -> bool {
44        ch == '`'
45    }
46
47    fn parse_infix(
48        &self,
49        parser: &mut crate::parser::Parser,
50        expr: &crate::ast::Expr,
51        _precedence: u8,
52    ) -> Option<Result<crate::ast::Expr, ParserError>> {
53        // Parse DIV as an operator
54        if parser.parse_keyword(Keyword::DIV) {
55            Some(Ok(Expr::BinaryOp {
56                left: Box::new(expr.clone()),
57                op: BinaryOperator::MyIntegerDivide,
58                right: Box::new(parser.parse_expr().unwrap()),
59            }))
60        } else {
61            None
62        }
63    }
64
65    fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
66        if parser.parse_keywords(&[Keyword::LOCK, Keyword::TABLES]) {
67            Some(parse_lock_tables(parser))
68        } else if parser.parse_keywords(&[Keyword::UNLOCK, Keyword::TABLES]) {
69            Some(parse_unlock_tables(parser))
70        } else {
71            None
72        }
73    }
74}
75
76/// `LOCK TABLES`
77/// <https://dev.mysql.com/doc/refman/8.0/en/lock-tables.html>
78fn parse_lock_tables(parser: &mut Parser) -> Result<Statement, ParserError> {
79    let tables = parser.parse_comma_separated(parse_lock_table)?;
80    Ok(Statement::LockTables { tables })
81}
82
83// tbl_name [[AS] alias] lock_type
84fn parse_lock_table(parser: &mut Parser) -> Result<LockTable, ParserError> {
85    let table = parser.parse_identifier(false)?;
86    let alias =
87        parser.parse_optional_alias(&[Keyword::READ, Keyword::WRITE, Keyword::LOW_PRIORITY])?;
88    let lock_type = parse_lock_tables_type(parser)?;
89
90    Ok(LockTable {
91        table,
92        alias,
93        lock_type,
94    })
95}
96
97// READ [LOCAL] | [LOW_PRIORITY] WRITE
98fn parse_lock_tables_type(parser: &mut Parser) -> Result<LockTableType, ParserError> {
99    if parser.parse_keyword(Keyword::READ) {
100        if parser.parse_keyword(Keyword::LOCAL) {
101            Ok(LockTableType::Read { local: true })
102        } else {
103            Ok(LockTableType::Read { local: false })
104        }
105    } else if parser.parse_keyword(Keyword::WRITE) {
106        Ok(LockTableType::Write {
107            low_priority: false,
108        })
109    } else if parser.parse_keywords(&[Keyword::LOW_PRIORITY, Keyword::WRITE]) {
110        Ok(LockTableType::Write { low_priority: true })
111    } else {
112        parser.expected("an lock type in LOCK TABLES", parser.peek_token())
113    }
114}
115
116/// UNLOCK TABLES
117/// <https://dev.mysql.com/doc/refman/8.0/en/lock-tables.html>
118fn parse_unlock_tables(_parser: &mut Parser) -> Result<Statement, ParserError> {
119    Ok(Statement::UnlockTables)
120}