sqlparser/dialect/
mysql.rs1#[cfg(not(feature = "std"))]
19use alloc::boxed::Box;
20
21use crate::{
22 ast::{BinaryOperator, Expr, LockTable, LockTableType, Statement},
23 dialect::Dialect,
24 keywords::Keyword,
25 parser::{Parser, ParserError},
26};
27
28use super::keywords;
29
30const RESERVED_FOR_TABLE_ALIAS_MYSQL: &[Keyword] = &[Keyword::USE, Keyword::IGNORE, Keyword::FORCE];
31
32#[derive(Debug)]
34pub struct MySqlDialect {}
35
36impl Dialect for MySqlDialect {
37 fn is_identifier_start(&self, ch: char) -> bool {
38 ch.is_alphabetic()
42 || ch == '_'
43 || ch == '$'
44 || ch == '@'
45 || ('\u{0080}'..='\u{ffff}').contains(&ch)
46 }
47
48 fn is_identifier_part(&self, ch: char) -> bool {
49 self.is_identifier_start(ch) || ch.is_ascii_digit()
50 }
51
52 fn is_delimited_identifier_start(&self, ch: char) -> bool {
53 ch == '`'
54 }
55
56 fn identifier_quote_style(&self, _identifier: &str) -> Option<char> {
57 Some('`')
58 }
59
60 fn supports_string_literal_backslash_escape(&self) -> bool {
62 true
63 }
64
65 fn supports_numeric_prefix(&self) -> bool {
66 true
67 }
68
69 fn parse_infix(
70 &self,
71 parser: &mut crate::parser::Parser,
72 expr: &crate::ast::Expr,
73 _precedence: u8,
74 ) -> Option<Result<crate::ast::Expr, ParserError>> {
75 if parser.parse_keyword(Keyword::DIV) {
77 Some(Ok(Expr::BinaryOp {
78 left: Box::new(expr.clone()),
79 op: BinaryOperator::MyIntegerDivide,
80 right: Box::new(parser.parse_expr().unwrap()),
81 }))
82 } else {
83 None
84 }
85 }
86
87 fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
88 if parser.parse_keywords(&[Keyword::LOCK, Keyword::TABLES]) {
89 Some(parse_lock_tables(parser))
90 } else if parser.parse_keywords(&[Keyword::UNLOCK, Keyword::TABLES]) {
91 Some(parse_unlock_tables(parser))
92 } else {
93 None
94 }
95 }
96
97 fn require_interval_qualifier(&self) -> bool {
98 true
99 }
100
101 fn supports_limit_comma(&self) -> bool {
102 true
103 }
104
105 fn supports_create_table_select(&self) -> bool {
107 true
108 }
109
110 fn supports_insert_set(&self) -> bool {
112 true
113 }
114
115 fn supports_user_host_grantee(&self) -> bool {
116 true
117 }
118
119 fn is_table_factor_alias(&self, explicit: bool, kw: &Keyword, _parser: &mut Parser) -> bool {
120 explicit
121 || (!keywords::RESERVED_FOR_TABLE_ALIAS.contains(kw)
122 && !RESERVED_FOR_TABLE_ALIAS_MYSQL.contains(kw))
123 }
124
125 fn supports_table_hints(&self) -> bool {
126 true
127 }
128
129 fn requires_single_line_comment_whitespace(&self) -> bool {
130 true
131 }
132
133 fn supports_match_against(&self) -> bool {
134 true
135 }
136}
137
138fn parse_lock_tables(parser: &mut Parser) -> Result<Statement, ParserError> {
141 let tables = parser.parse_comma_separated(parse_lock_table)?;
142 Ok(Statement::LockTables { tables })
143}
144
145fn parse_lock_table(parser: &mut Parser) -> Result<LockTable, ParserError> {
147 let table = parser.parse_identifier()?;
148 let alias =
149 parser.parse_optional_alias(&[Keyword::READ, Keyword::WRITE, Keyword::LOW_PRIORITY])?;
150 let lock_type = parse_lock_tables_type(parser)?;
151
152 Ok(LockTable {
153 table,
154 alias,
155 lock_type,
156 })
157}
158
159fn parse_lock_tables_type(parser: &mut Parser) -> Result<LockTableType, ParserError> {
161 if parser.parse_keyword(Keyword::READ) {
162 if parser.parse_keyword(Keyword::LOCAL) {
163 Ok(LockTableType::Read { local: true })
164 } else {
165 Ok(LockTableType::Read { local: false })
166 }
167 } else if parser.parse_keyword(Keyword::WRITE) {
168 Ok(LockTableType::Write {
169 low_priority: false,
170 })
171 } else if parser.parse_keywords(&[Keyword::LOW_PRIORITY, Keyword::WRITE]) {
172 Ok(LockTableType::Write { low_priority: true })
173 } else {
174 parser.expected("an lock type in LOCK TABLES", parser.peek_token())
175 }
176}
177
178fn parse_unlock_tables(_parser: &mut Parser) -> Result<Statement, ParserError> {
181 Ok(Statement::UnlockTables)
182}