Skip to content

Commit bd750df

Browse files
authored
Support Databricks struct literal (#1542)
1 parent 4ab3ab9 commit bd750df

File tree

7 files changed

+71
-9
lines changed

7 files changed

+71
-9
lines changed

src/ast/mod.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -931,12 +931,14 @@ pub enum Expr {
931931
Rollup(Vec<Vec<Expr>>),
932932
/// ROW / TUPLE a single value, such as `SELECT (1, 2)`
933933
Tuple(Vec<Expr>),
934-
/// `BigQuery` specific `Struct` literal expression [1]
934+
/// `Struct` literal expression
935935
/// Syntax:
936936
/// ```sql
937937
/// STRUCT<[field_name] field_type, ...>( expr1 [, ... ])
938+
///
939+
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#struct_type)
940+
/// [Databricks](https://docs.databricks.com/en/sql/language-manual/functions/struct.html)
938941
/// ```
939-
/// [1]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#struct_type
940942
Struct {
941943
/// Struct values.
942944
values: Vec<Expr>,

src/dialect/bigquery.rs

+5
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,9 @@ impl Dialect for BigQueryDialect {
7272
fn require_interval_qualifier(&self) -> bool {
7373
true
7474
}
75+
76+
// See https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#constructing_a_struct
77+
fn supports_struct_literal(&self) -> bool {
78+
true
79+
}
7580
}

src/dialect/databricks.rs

+5
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,9 @@ impl Dialect for DatabricksDialect {
5959
fn require_interval_qualifier(&self) -> bool {
6060
true
6161
}
62+
63+
// See https://docs.databricks.com/en/sql/language-manual/functions/struct.html
64+
fn supports_struct_literal(&self) -> bool {
65+
true
66+
}
6267
}

src/dialect/generic.rs

+4
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,8 @@ impl Dialect for GenericDialect {
123123
fn supports_named_fn_args_with_assignment_operator(&self) -> bool {
124124
true
125125
}
126+
127+
fn supports_struct_literal(&self) -> bool {
128+
true
129+
}
126130
}

src/dialect/mod.rs

+10
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,16 @@ pub trait Dialect: Debug + Any {
375375
false
376376
}
377377

378+
/// Return true if the dialect supports the STRUCT literal
379+
///
380+
/// Example
381+
/// ```sql
382+
/// SELECT STRUCT(1 as one, 'foo' as foo, false)
383+
/// ```
384+
fn supports_struct_literal(&self) -> bool {
385+
false
386+
}
387+
378388
/// Dialect-specific infix parser override
379389
///
380390
/// This method is called to parse the next infix expression.

src/parser/mod.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -1123,9 +1123,8 @@ impl<'a> Parser<'a> {
11231123
Keyword::MATCH if dialect_of!(self is MySqlDialect | GenericDialect) => {
11241124
Ok(Some(self.parse_match_against()?))
11251125
}
1126-
Keyword::STRUCT if dialect_of!(self is BigQueryDialect | GenericDialect) => {
1127-
self.prev_token();
1128-
Ok(Some(self.parse_bigquery_struct_literal()?))
1126+
Keyword::STRUCT if self.dialect.supports_struct_literal() => {
1127+
Ok(Some(self.parse_struct_literal()?))
11291128
}
11301129
Keyword::PRIOR if matches!(self.state, ParserState::ConnectBy) => {
11311130
let expr = self.parse_subexpr(self.dialect.prec_value(Precedence::PlusMinus))?;
@@ -2383,15 +2382,16 @@ impl<'a> Parser<'a> {
23832382
}
23842383
}
23852384

2386-
/// Bigquery specific: Parse a struct literal
23872385
/// Syntax
23882386
/// ```sql
23892387
/// -- typed
23902388
/// STRUCT<[field_name] field_type, ...>( expr1 [, ... ])
23912389
/// -- typeless
23922390
/// STRUCT( expr1 [AS field_name] [, ... ])
23932391
/// ```
2394-
fn parse_bigquery_struct_literal(&mut self) -> Result<Expr, ParserError> {
2392+
fn parse_struct_literal(&mut self) -> Result<Expr, ParserError> {
2393+
// Parse the fields definition if exist `<[field_name] field_type, ...>`
2394+
self.prev_token();
23952395
let (fields, trailing_bracket) =
23962396
self.parse_struct_type_def(Self::parse_struct_field_def)?;
23972397
if trailing_bracket.0 {
@@ -2401,6 +2401,7 @@ impl<'a> Parser<'a> {
24012401
);
24022402
}
24032403

2404+
// Parse the struct values `(expr1 [, ... ])`
24042405
self.expect_token(&Token::LParen)?;
24052406
let values = self
24062407
.parse_comma_separated(|parser| parser.parse_struct_field_expr(!fields.is_empty()))?;
@@ -2409,13 +2410,13 @@ impl<'a> Parser<'a> {
24092410
Ok(Expr::Struct { values, fields })
24102411
}
24112412

2412-
/// Parse an expression value for a bigquery struct [1]
2413+
/// Parse an expression value for a struct literal
24132414
/// Syntax
24142415
/// ```sql
24152416
/// expr [AS name]
24162417
/// ```
24172418
///
2418-
/// Parameter typed_syntax is set to true if the expression
2419+
/// For biquery [1], Parameter typed_syntax is set to true if the expression
24192420
/// is to be parsed as a field expression declared using typed
24202421
/// struct syntax [2], and false if using typeless struct syntax [3].
24212422
///

tests/sqlparser_databricks.rs

+35
Original file line numberDiff line numberDiff line change
@@ -278,3 +278,38 @@ fn parse_use() {
278278
);
279279
}
280280
}
281+
282+
#[test]
283+
fn parse_databricks_struct_function() {
284+
assert_eq!(
285+
databricks_and_generic()
286+
.verified_only_select("SELECT STRUCT(1, 'foo')")
287+
.projection[0],
288+
SelectItem::UnnamedExpr(Expr::Struct {
289+
values: vec![
290+
Expr::Value(number("1")),
291+
Expr::Value(Value::SingleQuotedString("foo".to_string()))
292+
],
293+
fields: vec![]
294+
})
295+
);
296+
assert_eq!(
297+
databricks_and_generic()
298+
.verified_only_select("SELECT STRUCT(1 AS one, 'foo' AS foo, false)")
299+
.projection[0],
300+
SelectItem::UnnamedExpr(Expr::Struct {
301+
values: vec![
302+
Expr::Named {
303+
expr: Expr::Value(number("1")).into(),
304+
name: Ident::new("one")
305+
},
306+
Expr::Named {
307+
expr: Expr::Value(Value::SingleQuotedString("foo".to_string())).into(),
308+
name: Ident::new("foo")
309+
},
310+
Expr::Value(Value::Boolean(false))
311+
],
312+
fields: vec![]
313+
})
314+
);
315+
}

0 commit comments

Comments
 (0)