Skip to content

Commit 86f9bbc

Browse files
Merge main into PR apache#2556
2 parents 840b340 + b1d1486 commit 86f9bbc

7 files changed

Lines changed: 100 additions & 6 deletions

File tree

‎src/ast/mod.rs‎

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,12 +1491,14 @@ pub enum AccessExpr {
14911491
Subscript(Subscript),
14921492
}
14931493

1494+
const fn is_number_expr(expr: &Expr) -> bool {
1495+
matches!(expr, Expr::Value(v) if matches!(v.value, Value::Number(_, _)))
1496+
}
1497+
14941498
impl fmt::Display for AccessExpr {
14951499
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14961500
match self {
1497-
AccessExpr::Dot(Expr::Value(value)) if matches!(value.value, Value::Number(_, _)) => {
1498-
write!(f, " . {value}")
1499-
}
1501+
AccessExpr::Dot(expr) if is_number_expr(expr) => write!(f, " . {expr}"),
15001502
AccessExpr::Dot(expr) => write!(f, ".{expr}"),
15011503
AccessExpr::Subscript(subscript) => write!(f, "[{subscript}]"),
15021504
}
@@ -1758,8 +1760,13 @@ impl fmt::Display for Expr {
17581760
Expr::CompoundIdentifier(s) => write!(f, "{}", display_separated(s, ".")),
17591761
Expr::CompoundFieldAccess { root, access_chain } => {
17601762
write!(f, "{root}")?;
1763+
let mut prev_is_number = is_number_expr(root);
17611764
for field in access_chain {
1762-
write!(f, "{field}")?;
1765+
match field {
1766+
AccessExpr::Dot(expr) if prev_is_number => write!(f, " . {expr}")?,
1767+
_ => write!(f, "{field}")?,
1768+
}
1769+
prev_is_number = matches!(field, AccessExpr::Dot(expr) if is_number_expr(expr));
17631770
}
17641771
Ok(())
17651772
}
@@ -2029,7 +2036,11 @@ impl fmt::Display for Expr {
20292036
format,
20302037
} => match kind {
20312038
CastKind::Cast => {
2032-
write!(f, "CAST({expr} AS {data_type}")?;
2039+
if matches!(data_type, DataType::Unspecified) {
2040+
write!(f, "CAST({expr} AS")?;
2041+
} else {
2042+
write!(f, "CAST({expr} AS {data_type}")?;
2043+
}
20332044
if let Some(format) = format {
20342045
write!(f, " FORMAT {format}")?;
20352046
}

‎src/dialect/mod.rs‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1969,6 +1969,18 @@ pub trait Dialect: Debug + Any {
19691969
fn supports_map_literal_with_angle_brackets(&self) -> bool {
19701970
false
19711971
}
1972+
1973+
/// Returns true if the dialect supports `CAST(expr AS)` with an empty type name.
1974+
///
1975+
/// Example:
1976+
/// ```sql
1977+
/// SELECT CAST(a AS)
1978+
/// ```
1979+
///
1980+
/// [SQLite](https://www.sqlite.org/lang_expr.html)
1981+
fn supports_cast_empty_data_type_to_unspecified(&self) -> bool {
1982+
false
1983+
}
19721984
}
19731985

19741986
/// Operators for which precedence must be defined.

‎src/dialect/sqlite.rs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,10 @@ impl Dialect for SQLiteDialect {
134134
true
135135
}
136136

137+
fn supports_cast_empty_data_type_to_unspecified(&self) -> bool {
138+
true
139+
}
140+
137141
fn supports_national_string_literal(&self) -> bool {
138142
false
139143
}

‎src/parser/mod.rs‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2925,7 +2925,13 @@ impl<'a> Parser<'a> {
29252925
self.expect_token(&Token::LParen)?;
29262926
let expr = self.parse_expr()?;
29272927
self.expect_keyword_is(Keyword::AS)?;
2928-
let data_type = self.parse_data_type()?;
2928+
let data_type = if self.dialect.supports_cast_empty_data_type_to_unspecified()
2929+
&& self.peek_token_ref().token == Token::RParen
2930+
{
2931+
DataType::Unspecified
2932+
} else {
2933+
self.parse_data_type()?
2934+
};
29292935
let format = self.parse_optional_cast_format()?;
29302936
self.expect_token(&Token::RParen)?;
29312937
Ok(Expr::Cast {
@@ -19360,6 +19366,13 @@ impl<'a> Parser<'a> {
1936019366
} if self.dialect.supports_eq_alias_assignment()
1936119367
&& matches!(left.as_ref(), Expr::Identifier(_)) =>
1936219368
{
19369+
if matches!(right.as_ref(), Expr::Identifier(v) if v.value.to_lowercase() == "from" && v.quote_style.is_none())
19370+
{
19371+
return parser_err!(
19372+
format!("Expected an expression, found: {right}"),
19373+
self.peek_token_ref().span.start
19374+
);
19375+
}
1936319376
let Expr::Identifier(alias) = *left else {
1936419377
return parser_err!(
1936519378
"BUG: expected identifier expression as alias",

‎tests/sqlparser_common.rs‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15271,6 +15271,24 @@ fn test_alias_equal_expr() {
1527115271
let expected = r#"SELECT (a * b) AS some_alias FROM some_table"#;
1527215272
let _ = dialects.one_statement_parses_to(sql, expected);
1527315273

15274+
let sql = r#"SELECT some_alias = "from" FROM some_table"#;
15275+
let expected = r#"SELECT "from" AS some_alias FROM some_table"#;
15276+
let _ = dialects.one_statement_parses_to(sql, expected);
15277+
15278+
assert_eq!(
15279+
dialects
15280+
.parse_sql_statements("SELECT a = FROM")
15281+
.unwrap_err(),
15282+
ParserError::ParserError("Expected an expression, found: FROM".to_string())
15283+
);
15284+
15285+
assert_eq!(
15286+
dialects
15287+
.parse_sql_statements("SELECT a = FROM some_table")
15288+
.unwrap_err(),
15289+
ParserError::ParserError("Expected an expression, found: FROM".to_string())
15290+
);
15291+
1527415292
let dialects = all_dialects_where(|d| !d.supports_eq_alias_assignment());
1527515293
let sql = r#"SELECT x = (a * b) FROM some_table"#;
1527615294
let expected = r#"SELECT x = (a * b) FROM some_table"#;
@@ -20223,6 +20241,25 @@ fn parse_alter_table_column_position() {
2022320241
}
2022420242
}
2022520243

20244+
#[test]
20245+
fn parse_compound_field_access_numeric_display() {
20246+
all_dialects().verified_stmt("SELECT 1 . i");
20247+
all_dialects().verified_stmt("SELECT (1 . i)");
20248+
all_dialects().verified_stmt("SELECT 1 . 2");
20249+
all_dialects().verified_stmt("SELECT (a . 1 . b)");
20250+
all_dialects().verified_stmt("SELECT 1 . i.j");
20251+
all_dialects().verified_stmt("SELECT 1 . 2 . 3");
20252+
all_dialects().verified_stmt("SELECT a.b.c");
20253+
20254+
let err = all_dialects_where(|d| !d.supports_numeric_prefix())
20255+
.parse_sql_statements("SELECT (1.i)")
20256+
.unwrap_err();
20257+
assert_eq!(
20258+
err,
20259+
ParserError::ParserError("Expected: ), found: i".to_string())
20260+
);
20261+
}
20262+
2022620263
#[test]
2022720264
fn parse_placeholder_disallows_quoted_ident() {
2022820265
let dialects = TestedDialects::new(vec![

‎tests/sqlparser_mssql.rs‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2942,6 +2942,11 @@ fn parse_bracket_quoted_function_argument_name() {
29422942
);
29432943
}
29442944

2945+
#[test]
2946+
fn parse_bracket_quoted_eq_alias_assignment() {
2947+
ms().one_statement_parses_to("SELECT a = [from] FROM t", "SELECT [from] AS a FROM t");
2948+
}
2949+
29452950
#[test]
29462951
fn parse_create_proc() {
29472952
ms().one_statement_parses_to(

‎tests/sqlparser_sqlite.rs‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -990,6 +990,18 @@ fn test_multiword_type_names_rejected_by_other_dialects() {
990990
.is_err());
991991
}
992992

993+
#[test]
994+
fn test_cast_empty_type() {
995+
// SQLite allows CAST(expr AS) with an empty type name (typetoken can be empty)
996+
// See https://www.sqlite.org/lang_expr.html
997+
sqlite().verified_stmt("SELECT CAST(a AS)");
998+
999+
// Rejected by dialects without the flag
1000+
assert!(TestedDialects::new(vec![Box::new(GenericDialect {})])
1001+
.parse_sql_statements("SELECT CAST(a AS)")
1002+
.is_err());
1003+
}
1004+
9931005
#[test]
9941006
fn parse_n_prefix_not_national_string() {
9951007
// In SQLite, `n'...'` is the identifier `n` followed by a string literal.

0 commit comments

Comments
 (0)