Skip to content

Commit deaa6d8

Browse files
authored
Snowflake: support for object constants (#1223)
1 parent 2490034 commit deaa6d8

File tree

6 files changed

+81
-1
lines changed

6 files changed

+81
-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
@@ -46,4 +46,8 @@ impl Dialect for GenericDialect {
4646
fn supports_start_transaction_modifier(&self) -> bool {
4747
true
4848
}
49+
50+
fn supports_dictionary_syntax(&self) -> bool {
51+
true
52+
}
4953
}

src/dialect/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,11 @@ pub trait Dialect: Debug + Any {
170170
fn supports_named_fn_args_with_eq_operator(&self) -> bool {
171171
false
172172
}
173+
/// Returns true if the dialect supports defining structs or objects using a
174+
/// syntax like `{'x': 1, 'y': 2, 'z': 3}`.
175+
fn supports_dictionary_syntax(&self) -> bool {
176+
false
177+
}
173178
/// Returns true if the dialect has a CONVERT function which accepts a type first
174179
/// and an expression second, e.g. `CONVERT(varchar, 1)`
175180
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
@@ -59,6 +59,14 @@ impl Dialect for SnowflakeDialect {
5959
true
6060
}
6161

62+
// Snowflake uses this syntax for "object constants" (the values of which
63+
// are not actually required to be constants).
64+
//
65+
// https://docs.snowflake.com/en/sql-reference/data-types-semistructured#label-object-constant
66+
fn supports_dictionary_syntax(&self) -> bool {
67+
true
68+
}
69+
6270
fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
6371
if parser.parse_keyword(Keyword::CREATE) {
6472
// 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: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9408,3 +9408,59 @@ fn insert_into_with_parentheses() {
94089408
};
94099409
dialects.verified_stmt("INSERT INTO t1 (id, name) (SELECT t2.id, t2.name FROM t2)");
94109410
}
9411+
9412+
#[test]
9413+
fn test_dictionary_syntax() {
9414+
fn check(sql: &str, expect: Expr) {
9415+
assert_eq!(
9416+
all_dialects_where(|d| d.supports_dictionary_syntax()).verified_expr(sql),
9417+
expect
9418+
);
9419+
}
9420+
9421+
check(
9422+
"{'Alberta': 'Edmonton', 'Manitoba': 'Winnipeg'}",
9423+
Expr::Dictionary(vec![
9424+
DictionaryField {
9425+
key: Ident::with_quote('\'', "Alberta"),
9426+
value: Box::new(Expr::Value(Value::SingleQuotedString(
9427+
"Edmonton".to_owned(),
9428+
))),
9429+
},
9430+
DictionaryField {
9431+
key: Ident::with_quote('\'', "Manitoba"),
9432+
value: Box::new(Expr::Value(Value::SingleQuotedString(
9433+
"Winnipeg".to_owned(),
9434+
))),
9435+
},
9436+
]),
9437+
);
9438+
9439+
check(
9440+
"{'start': CAST('2023-04-01' AS TIMESTAMP), 'end': CAST('2023-04-05' AS TIMESTAMP)}",
9441+
Expr::Dictionary(vec![
9442+
DictionaryField {
9443+
key: Ident::with_quote('\'', "start"),
9444+
value: Box::new(Expr::Cast {
9445+
kind: CastKind::Cast,
9446+
expr: Box::new(Expr::Value(Value::SingleQuotedString(
9447+
"2023-04-01".to_owned(),
9448+
))),
9449+
data_type: DataType::Timestamp(None, TimezoneInfo::None),
9450+
format: None,
9451+
}),
9452+
},
9453+
DictionaryField {
9454+
key: Ident::with_quote('\'', "end"),
9455+
value: Box::new(Expr::Cast {
9456+
kind: CastKind::Cast,
9457+
expr: Box::new(Expr::Value(Value::SingleQuotedString(
9458+
"2023-04-05".to_owned(),
9459+
))),
9460+
data_type: DataType::Timestamp(None, TimezoneInfo::None),
9461+
format: None,
9462+
}),
9463+
},
9464+
]),
9465+
)
9466+
}

0 commit comments

Comments
 (0)