| // Licensed to the Apache Software Foundation (ASF) under one | |
| // or more contributor license agreements. See the NOTICE file | |
| // distributed with this work for additional information | |
| // regarding copyright ownership. The ASF licenses this file | |
| // to you under the Apache License, Version 2.0 (the | |
| // "License"); you may not use this file except in compliance | |
| // with the License. You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, | |
| // software distributed under the License is distributed on an | |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| // KIND, either express or implied. See the License for the | |
| // specific language governing permissions and limitations | |
| // under the License. | |
| #[cfg(not(feature = "std"))] | |
| use alloc::boxed::Box; | |
| use crate::{ | |
| ast::{BinaryOperator, Expr, LockTable, LockTableType, Statement}, | |
| dialect::Dialect, | |
| keywords::Keyword, | |
| parser::{Parser, ParserError}, | |
| }; | |
| use super::keywords; | |
| const RESERVED_FOR_TABLE_ALIAS_MYSQL: &[Keyword] = &[ | |
| Keyword::USE, | |
| Keyword::IGNORE, | |
| Keyword::FORCE, | |
| Keyword::STRAIGHT_JOIN, | |
| ]; | |
| /// A [`Dialect`] for [MySQL](https://www.mysql.com/) | |
| #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] | |
| #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | |
| pub struct MySqlDialect {} | |
| impl Dialect for MySqlDialect { | |
| fn is_identifier_start(&self, ch: char) -> bool { | |
| // See https://dev.mysql.com/doc/refman/8.0/en/identifiers.html. | |
| // Identifiers which begin with a digit are recognized while tokenizing numbers, | |
| // so they can be distinguished from exponent numeric literals. | |
| // MySQL also implements non ascii utf-8 charecters | |
| ch.is_alphabetic() | |
| || ch == '_' | |
| || ch == '$' | |
| || ch == '@' | |
| || ('\u{0080}'..='\u{ffff}').contains(&ch) | |
| || !ch.is_ascii() | |
| } | |
| fn is_identifier_part(&self, ch: char) -> bool { | |
| self.is_identifier_start(ch) || ch.is_ascii_digit() || | |
| // MySQL implements Unicode characters in identifiers. | |
| !ch.is_ascii() | |
| } | |
| fn is_delimited_identifier_start(&self, ch: char) -> bool { | |
| ch == '`' | |
| } | |
| fn identifier_quote_style(&self, _identifier: &str) -> Option<char> { | |
| Some('`') | |
| } | |
| // See https://dev.mysql.com/doc/refman/8.0/en/string-literals.html#character-escape-sequences | |
| fn supports_string_literal_backslash_escape(&self) -> bool { | |
| true | |
| } | |
| /// see <https://dev.mysql.com/doc/refman/8.4/en/string-functions.html#function_concat> | |
| fn supports_string_literal_concatenation(&self) -> bool { | |
| true | |
| } | |
| fn ignores_wildcard_escapes(&self) -> bool { | |
| true | |
| } | |
| fn supports_numeric_prefix(&self) -> bool { | |
| true | |
| } | |
| fn supports_bitwise_shift_operators(&self) -> bool { | |
| true | |
| } | |
| /// see <https://dev.mysql.com/doc/refman/8.4/en/comments.html> | |
| fn supports_multiline_comment_hints(&self) -> bool { | |
| true | |
| } | |
| fn parse_infix( | |
| &self, | |
| parser: &mut crate::parser::Parser, | |
| expr: &crate::ast::Expr, | |
| precedence: u8, | |
| ) -> Option<Result<crate::ast::Expr, ParserError>> { | |
| // Parse DIV as an operator | |
| if parser.parse_keyword(Keyword::DIV) { | |
| let left = Box::new(expr.clone()); | |
| let right = Box::new(match parser.parse_subexpr(precedence) { | |
| Ok(expr) => expr, | |
| Err(e) => return Some(Err(e)), | |
| }); | |
| Some(Ok(Expr::BinaryOp { | |
| left, | |
| op: BinaryOperator::MyIntegerDivide, | |
| right, | |
| })) | |
| } else { | |
| None | |
| } | |
| } | |
| fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> { | |
| if parser.parse_keywords(&[Keyword::LOCK, Keyword::TABLES]) { | |
| Some(parse_lock_tables(parser)) | |
| } else if parser.parse_keywords(&[Keyword::UNLOCK, Keyword::TABLES]) { | |
| Some(parse_unlock_tables(parser)) | |
| } else { | |
| None | |
| } | |
| } | |
| fn require_interval_qualifier(&self) -> bool { | |
| true | |
| } | |
| fn supports_limit_comma(&self) -> bool { | |
| true | |
| } | |
| /// See: <https://dev.mysql.com/doc/refman/8.4/en/create-table-select.html> | |
| fn supports_create_table_select(&self) -> bool { | |
| true | |
| } | |
| /// See: <https://dev.mysql.com/doc/refman/8.4/en/insert.html> | |
| fn supports_insert_set(&self) -> bool { | |
| true | |
| } | |
| fn supports_user_host_grantee(&self) -> bool { | |
| true | |
| } | |
| fn is_table_factor_alias(&self, explicit: bool, kw: &Keyword, _parser: &mut Parser) -> bool { | |
| explicit | |
| || (!keywords::RESERVED_FOR_TABLE_ALIAS.contains(kw) | |
| && !RESERVED_FOR_TABLE_ALIAS_MYSQL.contains(kw)) | |
| } | |
| fn supports_table_hints(&self) -> bool { | |
| true | |
| } | |
| fn requires_single_line_comment_whitespace(&self) -> bool { | |
| true | |
| } | |
| fn supports_match_against(&self) -> bool { | |
| true | |
| } | |
| fn supports_select_modifiers(&self) -> bool { | |
| true | |
| } | |
| fn supports_set_names(&self) -> bool { | |
| true | |
| } | |
| fn supports_comma_separated_set_assignments(&self) -> bool { | |
| true | |
| } | |
| /// See: <https://dev.mysql.com/doc/refman/8.4/en/alter-table.html> | |
| fn supports_alter_column_position(&self) -> bool { | |
| true | |
| } | |
| /// See: <https://dev.mysql.com/doc/refman/8.4/en/update.html> | |
| fn supports_update_order_by(&self) -> bool { | |
| true | |
| } | |
| fn supports_data_type_signed_suffix(&self) -> bool { | |
| true | |
| } | |
| fn supports_cross_join_constraint(&self) -> bool { | |
| true | |
| } | |
| /// See: <https://dev.mysql.com/doc/refman/8.4/en/expressions.html> | |
| fn supports_double_ampersand_operator(&self) -> bool { | |
| true | |
| } | |
| /// Deprecated functionality by MySQL but still supported | |
| /// See: <https://dev.mysql.com/doc/refman/8.4/en/cast-functions.html#operator_binary> | |
| fn supports_binary_kw_as_cast(&self) -> bool { | |
| true | |
| } | |
| /// See: <https://dev.mysql.com/doc/refman/8.4/en/partitioning-selection.html> | |
| fn supports_table_partitions(&self) -> bool { | |
| true | |
| } | |
| fn supports_comment_optimizer_hint(&self) -> bool { | |
| true | |
| } | |
| /// See: <https://dev.mysql.com/doc/refman/8.4/en/create-table.html> | |
| fn supports_constraint_keyword_without_name(&self) -> bool { | |
| true | |
| } | |
| /// See: <https://dev.mysql.com/doc/refman/8.4/en/create-table.html> | |
| fn supports_key_column_option(&self) -> bool { | |
| true | |
| } | |
| /// See: <https://dev.mysql.com/doc/refman/9.7/en/group-by-modifiers.html> | |
| fn supports_group_by_with_modifier(&self) -> bool { | |
| true | |
| } | |
| fn supports_left_associative_joins_without_parens(&self) -> bool { | |
| false | |
| } | |
| } | |
| /// `LOCK TABLES` | |
| /// <https://dev.mysql.com/doc/refman/8.0/en/lock-tables.html> | |
| fn parse_lock_tables(parser: &mut Parser) -> Result<Statement, ParserError> { | |
| let tables = parser.parse_comma_separated(parse_lock_table)?; | |
| Ok(Statement::LockTables { tables }) | |
| } | |
| // tbl_name [[AS] alias] lock_type | |
| fn parse_lock_table(parser: &mut Parser) -> Result<LockTable, ParserError> { | |
| let table = parser.parse_identifier()?; | |
| let alias = | |
| parser.parse_optional_alias(&[Keyword::READ, Keyword::WRITE, Keyword::LOW_PRIORITY])?; | |
| let lock_type = parse_lock_tables_type(parser)?; | |
| Ok(LockTable { | |
| table, | |
| alias, | |
| lock_type, | |
| }) | |
| } | |
| // READ [LOCAL] | [LOW_PRIORITY] WRITE | |
| fn parse_lock_tables_type(parser: &mut Parser) -> Result<LockTableType, ParserError> { | |
| if parser.parse_keyword(Keyword::READ) { | |
| if parser.parse_keyword(Keyword::LOCAL) { | |
| Ok(LockTableType::Read { local: true }) | |
| } else { | |
| Ok(LockTableType::Read { local: false }) | |
| } | |
| } else if parser.parse_keyword(Keyword::WRITE) { | |
| Ok(LockTableType::Write { | |
| low_priority: false, | |
| }) | |
| } else if parser.parse_keywords(&[Keyword::LOW_PRIORITY, Keyword::WRITE]) { | |
| Ok(LockTableType::Write { low_priority: true }) | |
| } else { | |
| parser.expected_ref("an lock type in LOCK TABLES", parser.peek_token_ref()) | |
| } | |
| } | |
| /// UNLOCK TABLES | |
| /// <https://dev.mysql.com/doc/refman/8.0/en/lock-tables.html> | |
| fn parse_unlock_tables(_parser: &mut Parser) -> Result<Statement, ParserError> { | |
| Ok(Statement::UnlockTables) | |
| } |