Skip to content

Commit e9ab4d6

Browse files
authored
Fix BigQuery hyphenated ObjectName with numbers (#1598)
1 parent 8fcdf48 commit e9ab4d6

File tree

3 files changed

+61
-8
lines changed

3 files changed

+61
-8
lines changed

src/parser/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -8755,7 +8755,9 @@ impl<'a> Parser<'a> {
87558755
}
87568756
Token::Number(s, false) if s.chars().all(|c| c.is_ascii_digit()) => {
87578757
ident.value.push_str(&s);
8758-
true
8758+
// If next token is period, then it is part of an ObjectName and we don't expect whitespace
8759+
// after the number.
8760+
!matches!(self.peek_token().token, Token::Period)
87598761
}
87608762
_ => {
87618763
return self

src/tokenizer.rs

+38-7
Original file line numberDiff line numberDiff line change
@@ -1144,15 +1144,29 @@ impl<'a> Tokenizer<'a> {
11441144

11451145
// match one period
11461146
if let Some('.') = chars.peek() {
1147-
s.push('.');
1148-
chars.next();
1147+
// Check if this actually is a float point number
1148+
let mut char_clone = chars.peekable.clone();
1149+
char_clone.next();
1150+
// Next char should be a digit, otherwise, it is not a float point number
1151+
if char_clone
1152+
.peek()
1153+
.map(|c| c.is_ascii_digit())
1154+
.unwrap_or(false)
1155+
{
1156+
s.push('.');
1157+
chars.next();
1158+
} else if !s.is_empty() {
1159+
// Number might be part of period separated construct. Keep the period for next token
1160+
// e.g. a-12.b
1161+
return Ok(Some(Token::Number(s, false)));
1162+
} else {
1163+
// No number -> Token::Period
1164+
chars.next();
1165+
return Ok(Some(Token::Period));
1166+
}
11491167
}
1150-
s += &peeking_take_while(chars, |ch| ch.is_ascii_digit());
11511168

1152-
// No number -> Token::Period
1153-
if s == "." {
1154-
return Ok(Some(Token::Period));
1155-
}
1169+
s += &peeking_take_while(chars, |ch| ch.is_ascii_digit());
11561170

11571171
let mut exponent_part = String::new();
11581172
// Parse exponent as number
@@ -2185,6 +2199,23 @@ mod tests {
21852199
compare(expected, tokens);
21862200
}
21872201

2202+
#[test]
2203+
fn tokenize_select_float_hyphenated_identifier() {
2204+
let sql = String::from("SELECT a-12.b");
2205+
let dialect = GenericDialect {};
2206+
let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2207+
let expected = vec![
2208+
Token::make_keyword("SELECT"),
2209+
Token::Whitespace(Whitespace::Space),
2210+
Token::make_word("a", None),
2211+
Token::Minus,
2212+
Token::Number(String::from("12"), false),
2213+
Token::Period,
2214+
Token::make_word("b", None),
2215+
];
2216+
compare(expected, tokens);
2217+
}
2218+
21882219
#[test]
21892220
fn tokenize_clickhouse_double_equal() {
21902221
let sql = String::from("SELECT foo=='1'");

tests/sqlparser_bigquery.rs

+20
Original file line numberDiff line numberDiff line change
@@ -1504,6 +1504,26 @@ fn parse_hyphenated_table_identifiers() {
15041504
"SELECT * FROM foo-bar AS f JOIN baz-qux AS b ON f.id = b.id",
15051505
);
15061506

1507+
assert_eq!(
1508+
bigquery()
1509+
.verified_only_select_with_canonical(
1510+
"select * from foo-123.bar",
1511+
"SELECT * FROM foo-123.bar"
1512+
)
1513+
.from[0]
1514+
.relation,
1515+
TableFactor::Table {
1516+
name: ObjectName(vec![Ident::new("foo-123"), Ident::new("bar")]),
1517+
alias: None,
1518+
args: None,
1519+
with_hints: vec![],
1520+
version: None,
1521+
partitions: vec![],
1522+
with_ordinality: false,
1523+
json_path: None,
1524+
}
1525+
);
1526+
15071527
assert_eq!(
15081528
bigquery()
15091529
.verified_only_select_with_canonical(

0 commit comments

Comments
 (0)