From 5f2637740f99222997444898d3b3ea1859f4e214 Mon Sep 17 00:00:00 2001 From: Mosha Pasumansky Date: Thu, 29 Oct 2026 14:06:44 -0700 Subject: [PATCH 1/2] Databricks, Hive: parse `1L` as a long literal Both dialects read `1L` as an identifier, since the numeric-prefix rule (tables named `59901_user_login`) ran before the long suffix. A new dialect method, supports_long_suffix_before_numeric_prefix, lets an `L` that ends the number win; `1La` is still an identifier. MySQL, which has no long literal, keeps reading `1L` as an identifier. Co-authored-by: Claude Opus 5.5 --- src/dialect/databricks.rs | 5 +++++ src/dialect/hive.rs | 5 +++++ src/dialect/mod.rs | 7 +++++++ src/tokenizer.rs | 14 ++++++++++++++ tests/sqlparser_common.rs | 25 +++++++++++++++++++++++++ 5 files changed, 56 insertions(+) diff --git a/src/dialect/databricks.rs b/src/dialect/databricks.rs index d660cec9eb..79fe7bc29b 100644 --- a/src/dialect/databricks.rs +++ b/src/dialect/databricks.rs @@ -72,6 +72,11 @@ impl Dialect for DatabricksDialect { true } + /// See + fn supports_long_suffix_before_numeric_prefix(&self) -> bool { + true + } + fn supports_filter_during_aggregation(&self) -> bool { true } diff --git a/src/dialect/hive.rs b/src/dialect/hive.rs index e80bc75d2b..6b8889aea7 100644 --- a/src/dialect/hive.rs +++ b/src/dialect/hive.rs @@ -78,6 +78,11 @@ impl Dialect for HiveDialect { true } + /// See + fn supports_long_suffix_before_numeric_prefix(&self) -> bool { + true + } + fn require_interval_qualifier(&self) -> bool { true } diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index 20fe597d54..cf7ef43cdd 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -597,6 +597,13 @@ pub trait Dialect: Debug + Any { false } + /// Returns true if an `L` ending a number, as in `1L`, makes it a long + /// literal even when [`Dialect::supports_numeric_prefix`] would otherwise + /// read `1L` as an identifier. `1La` is still an identifier. + fn supports_long_suffix_before_numeric_prefix(&self) -> bool { + false + } + /// Returns true if a period directly after an identifier starts a tuple /// element access such as `t.1`, instead of the number `.1`. fn supports_tuple_element_access(&self) -> bool { diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 02a54f08ff..4abe4165a8 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -1686,6 +1686,20 @@ impl<'a> Tokenizer<'a> { } } + if self.dialect.supports_long_suffix_before_numeric_prefix() + && chars.peek() == Some(&'L') + { + let mut ahead = chars.peekable.clone(); + ahead.next(); + if !ahead + .peek() + .is_some_and(|ch| self.dialect.is_identifier_part(*ch)) + { + chars.next(); + return Ok(Some(Token::Number(s, true))); + } + } + // If the dialect supports identifiers that start with a numeric prefix, // we need to check if the value is in fact an identifier and must thus // be tokenized as a word. diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 379e37a660..996404acfd 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -22744,3 +22744,28 @@ fn display_create_procedure_parameter_list_propagates_write_errors() { let mut writer = RejectingWriter(" ("); assert_eq!(write!(writer, "{statement}"), Err(fmt::Error)); } + +#[test] +fn parse_long_suffix_before_numeric_prefix() { + let dialects = all_dialects_where(|d| d.supports_long_suffix_before_numeric_prefix()); + assert_eq!( + dialects.verified_only_select("SELECT 1L").projection, + vec![SelectItem::UnnamedExpr(Expr::value(Value::Number( + "1".parse().unwrap(), + true + )))] + ); + assert_eq!( + dialects.verified_only_select("SELECT 1La").projection, + vec![SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("1La")))] + ); + + assert_eq!( + all_dialects_where(|d| { + d.supports_numeric_prefix() && !d.supports_long_suffix_before_numeric_prefix() + }) + .verified_only_select("SELECT 1L") + .projection, + vec![SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("1L")))] + ); +} From ccf327d2c0e1b86cce146b299b62ebdc1c96fbe8 Mon Sep 17 00:00:00 2001 From: Mosha Pasumansky Date: Thu, 29 Oct 2026 14:32:55 -0700 Subject: [PATCH 2/2] Add a fuzz seed for the long literal Co-authored-by: Claude Opus 5.5 --- fuzz/fuzz_seeds/databricks_2771_long_literal.sql | 1 + 1 file changed, 1 insertion(+) create mode 100644 fuzz/fuzz_seeds/databricks_2771_long_literal.sql diff --git a/fuzz/fuzz_seeds/databricks_2771_long_literal.sql b/fuzz/fuzz_seeds/databricks_2771_long_literal.sql new file mode 100644 index 0000000000..d3c60f2764 --- /dev/null +++ b/fuzz/fuzz_seeds/databricks_2771_long_literal.sql @@ -0,0 +1 @@ +SELECT 1L, 1La