mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-17 12:40:17 +00:00
Support identifiers beginning with digits in MySQL (#856)
This commit is contained in:
parent
6b2b3f1f6c
commit
3e92ad349f
3 changed files with 115 additions and 3 deletions
|
@ -18,8 +18,8 @@ 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.
|
||||
// We don't yet support identifiers beginning with numbers, as that
|
||||
// makes it hard to distinguish numeric literals.
|
||||
// Identifiers which begin with a digit are recognized while tokenizing numbers,
|
||||
// so they can be distinguished from exponent numeric literals.
|
||||
ch.is_alphabetic()
|
||||
|| ch == '_'
|
||||
|| ch == '$'
|
||||
|
|
|
@ -673,10 +673,10 @@ impl<'a> Tokenizer<'a> {
|
|||
return Ok(Some(Token::Period));
|
||||
}
|
||||
|
||||
let mut exponent_part = String::new();
|
||||
// Parse exponent as number
|
||||
if chars.peek() == Some(&'e') || chars.peek() == Some(&'E') {
|
||||
let mut char_clone = chars.peekable.clone();
|
||||
let mut exponent_part = String::new();
|
||||
exponent_part.push(char_clone.next().unwrap());
|
||||
|
||||
// Optional sign
|
||||
|
@ -703,6 +703,18 @@ impl<'a> Tokenizer<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
// mysql dialect supports identifiers that start with a numeric prefix,
|
||||
// as long as they aren't an exponent number.
|
||||
if dialect_of!(self is MySqlDialect) && exponent_part.is_empty() {
|
||||
let word =
|
||||
peeking_take_while(chars, |ch| self.dialect.is_identifier_part(ch));
|
||||
|
||||
if !word.is_empty() {
|
||||
s += word.as_str();
|
||||
return Ok(Some(Token::make_word(s.as_str(), None)));
|
||||
}
|
||||
}
|
||||
|
||||
let long = if chars.peek() == Some(&'L') {
|
||||
chars.next();
|
||||
true
|
||||
|
|
|
@ -849,6 +849,106 @@ fn parse_insert_with_on_duplicate_update() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_select_with_numeric_prefix_column_name() {
|
||||
let sql = "SELECT 123col_$@123abc FROM \"table\"";
|
||||
match mysql().verified_stmt(sql) {
|
||||
Statement::Query(q) => {
|
||||
assert_eq!(
|
||||
q.body,
|
||||
Box::new(SetExpr::Select(Box::new(Select {
|
||||
distinct: false,
|
||||
top: None,
|
||||
projection: vec![SelectItem::UnnamedExpr(Expr::Identifier(Ident::new(
|
||||
"123col_$@123abc"
|
||||
)))],
|
||||
into: None,
|
||||
from: vec![TableWithJoins {
|
||||
relation: TableFactor::Table {
|
||||
name: ObjectName(vec![Ident::with_quote('"', "table")]),
|
||||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
},
|
||||
joins: vec![]
|
||||
}],
|
||||
lateral_views: vec![],
|
||||
selection: None,
|
||||
group_by: vec![],
|
||||
cluster_by: vec![],
|
||||
distribute_by: vec![],
|
||||
sort_by: vec![],
|
||||
having: None,
|
||||
qualify: None,
|
||||
})))
|
||||
);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "bigdecimal"))]
|
||||
#[test]
|
||||
fn parse_select_with_concatenation_of_exp_number_and_numeric_prefix_column() {
|
||||
let sql = "SELECT 123e4, 123col_$@123abc FROM \"table\"";
|
||||
match mysql().verified_stmt(sql) {
|
||||
Statement::Query(q) => {
|
||||
assert_eq!(
|
||||
q.body,
|
||||
Box::new(SetExpr::Select(Box::new(Select {
|
||||
distinct: false,
|
||||
top: None,
|
||||
projection: vec![
|
||||
SelectItem::UnnamedExpr(Expr::Value(Value::Number(
|
||||
"123e4".to_string(),
|
||||
false
|
||||
))),
|
||||
SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("123col_$@123abc")))
|
||||
],
|
||||
into: None,
|
||||
from: vec![TableWithJoins {
|
||||
relation: TableFactor::Table {
|
||||
name: ObjectName(vec![Ident::with_quote('"', "table")]),
|
||||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
},
|
||||
joins: vec![]
|
||||
}],
|
||||
lateral_views: vec![],
|
||||
selection: None,
|
||||
group_by: vec![],
|
||||
cluster_by: vec![],
|
||||
distribute_by: vec![],
|
||||
sort_by: vec![],
|
||||
having: None,
|
||||
qualify: None,
|
||||
})))
|
||||
);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_insert_with_numeric_prefix_column_name() {
|
||||
let sql = "INSERT INTO s1.t1 (123col_$@length123) VALUES (67.654)";
|
||||
match mysql().verified_stmt(sql) {
|
||||
Statement::Insert {
|
||||
table_name,
|
||||
columns,
|
||||
..
|
||||
} => {
|
||||
assert_eq!(
|
||||
ObjectName(vec![Ident::new("s1"), Ident::new("t1")]),
|
||||
table_name
|
||||
);
|
||||
assert_eq!(vec![Ident::new("123col_$@length123")], columns);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_update_with_joins() {
|
||||
let sql = "UPDATE orders AS o JOIN customers AS c ON o.customer_id = c.id SET o.completed = true WHERE c.firstname = 'Peter'";
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue