Skip to content

Commit a3134dc

Browse files
committed
snowflake: support for object constants
1 parent 2f03fad commit a3134dc

File tree

6 files changed

+79
-1
lines changed

6 files changed

+79
-1
lines changed

src/dialect/duckdb.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,11 @@ impl Dialect for DuckDbDialect {
3737
fn supports_named_fn_args_with_eq_operator(&self) -> bool {
3838
true
3939
}
40+
41+
// DuckDB uses this syntax for `STRUCT`s.
42+
//
43+
// https://duckdb.org/docs/sql/data_types/struct.html#creating-structs
44+
fn supports_dictionary_syntax(&self) -> bool {
45+
true
46+
}
4047
}

src/dialect/generic.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,8 @@ impl Dialect for GenericDialect {
4242
fn supports_start_transaction_modifier(&self) -> bool {
4343
true
4444
}
45+
46+
fn supports_dictionary_syntax(&self) -> bool {
47+
true
48+
}
4549
}

src/dialect/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,11 @@ pub trait Dialect: Debug + Any {
147147
fn supports_named_fn_args_with_eq_operator(&self) -> bool {
148148
false
149149
}
150+
/// Returns true if the dialect supports defining structs or objects using a
151+
/// syntax like `{'x': 1, 'y': 2, 'z': 3}`.
152+
fn supports_dictionary_syntax(&self) -> bool {
153+
false
154+
}
150155
/// Returns true if the dialect has a CONVERT function which accepts a type first
151156
/// and an expression second, e.g. `CONVERT(varchar, 1)`
152157
fn convert_type_before_value(&self) -> bool {

src/dialect/snowflake.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ impl Dialect for SnowflakeDialect {
5050
true
5151
}
5252

53+
// Snowflake uses this syntax for "object constants" (the values of which
54+
// are not actually required to be constants).
55+
//
56+
// https://docs.snowflake.com/en/sql-reference/data-types-semistructured#label-object-constant
57+
fn supports_dictionary_syntax(&self) -> bool {
58+
true
59+
}
60+
5361
fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
5462
if parser.parse_keyword(Keyword::CREATE) {
5563
// possibly CREATE STAGE

src/parser/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1192,7 +1192,7 @@ impl<'a> Parser<'a> {
11921192
self.prev_token();
11931193
Ok(Expr::Value(self.parse_value()?))
11941194
}
1195-
Token::LBrace if dialect_of!(self is DuckDbDialect | GenericDialect) => {
1195+
Token::LBrace if self.dialect.supports_dictionary_syntax() => {
11961196
self.prev_token();
11971197
self.parse_duckdb_struct_literal()
11981198
}

tests/sqlparser_common.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8685,3 +8685,57 @@ fn parse_map_access_expr() {
86858685
let _ = dialects.verified_expr(sql);
86868686
}
86878687
}
8688+
8689+
#[test]
8690+
fn test_dictionary_syntax() {
8691+
fn check(sql: &str, expect: Expr) {
8692+
assert_eq!(
8693+
all_dialects_where(|d| d.supports_dictionary_syntax()).verified_expr(sql),
8694+
expect
8695+
);
8696+
}
8697+
8698+
check(
8699+
"{'Alberta': 'Edmonton', 'Manitoba': 'Winnipeg'}",
8700+
Expr::Dictionary(vec![
8701+
DictionaryField {
8702+
key: Ident::with_quote('\'', "Alberta"),
8703+
value: Box::new(Expr::Value(Value::SingleQuotedString(
8704+
"Edmonton".to_owned(),
8705+
))),
8706+
},
8707+
DictionaryField {
8708+
key: Ident::with_quote('\'', "Manitoba"),
8709+
value: Box::new(Expr::Value(Value::SingleQuotedString(
8710+
"Winnipeg".to_owned(),
8711+
))),
8712+
},
8713+
]),
8714+
);
8715+
8716+
check(
8717+
"{'start': CAST('2023-04-01' AS TIMESTAMP), 'end': CAST('2023-04-05' AS TIMESTAMP)}",
8718+
Expr::Dictionary(vec![
8719+
DictionaryField {
8720+
key: Ident::with_quote('\'', "start"),
8721+
value: Box::new(Expr::Cast {
8722+
expr: Box::new(Expr::Value(Value::SingleQuotedString(
8723+
"2023-04-01".to_owned(),
8724+
))),
8725+
data_type: DataType::Timestamp(None, TimezoneInfo::None),
8726+
format: None,
8727+
}),
8728+
},
8729+
DictionaryField {
8730+
key: Ident::with_quote('\'', "end"),
8731+
value: Box::new(Expr::Cast {
8732+
expr: Box::new(Expr::Value(Value::SingleQuotedString(
8733+
"2023-04-05".to_owned(),
8734+
))),
8735+
data_type: DataType::Timestamp(None, TimezoneInfo::None),
8736+
format: None,
8737+
}),
8738+
},
8739+
]),
8740+
)
8741+
}

0 commit comments

Comments
 (0)