Skip to content

Commit 29160c6

Browse files
author
Wojciech Padlo
committed
Snowflake: accept COMMENT interspersed among CREATE FILE FORMAT options
Snowflake accepts file-format options in any order, including COMMENT before other options (e.g. COMMENT = 'c' COMPRESSION = 'GZIP'). The previous parser treated COMMENT as a stop keyword and then expected end-of-statement, so any option after COMMENT was a syntax error. Parse COMMENT in the same loop as the key-value options (matching CREATE TABLE / CREATE DATABASE), hoisting it into the dedicated comment field.
1 parent 7ab0956 commit 29160c6

2 files changed

Lines changed: 52 additions & 14 deletions

File tree

src/dialect/snowflake.rs

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2234,15 +2234,6 @@ fn parse_create_file_format(
22342234
format_type = Some(parser.parse_identifier()?);
22352235
}
22362236

2237-
let options = if like_source.is_some() {
2238-
KeyValueOptions {
2239-
options: vec![],
2240-
delimiter: KeyValueOptionsDelimiter::Space,
2241-
}
2242-
} else {
2243-
parser.parse_key_value_options(false, &[Keyword::COMMENT])?
2244-
};
2245-
22462237
// `LIKE` is mutually exclusive with `TYPE`/options per Snowflake's grammar.
22472238
if like_source.is_some() && !matches!(parser.peek_token().token, Token::EOF | Token::SemiColon) {
22482239
return parser.expected(
@@ -2251,11 +2242,36 @@ fn parse_create_file_format(
22512242
);
22522243
}
22532244

2254-
let comment = if parser.parse_keyword(Keyword::COMMENT) {
2255-
parser.expect_token(&Token::Eq)?;
2256-
Some(parser.parse_comment_value()?)
2257-
} else {
2258-
None
2245+
// `COMMENT` may appear anywhere among the options (Snowflake accepts any
2246+
// order, like CREATE TABLE / CREATE DATABASE), so it is parsed in the same
2247+
// loop rather than only as a trailing clause. It is hoisted into its own
2248+
// AST field; the remaining key-value options stay in `options`.
2249+
let mut comment = None;
2250+
let mut option_list: Vec<KeyValueOption> = Vec::new();
2251+
let mut delimiter = KeyValueOptionsDelimiter::Space;
2252+
if like_source.is_none() {
2253+
loop {
2254+
if parser.parse_keyword(Keyword::COMMENT) {
2255+
parser.expect_token(&Token::Eq)?;
2256+
comment = Some(parser.parse_comment_value()?);
2257+
continue;
2258+
}
2259+
if matches!(parser.peek_token().token, Token::EOF | Token::SemiColon) {
2260+
break;
2261+
}
2262+
let parsed = parser.parse_key_value_options(false, &[Keyword::COMMENT])?;
2263+
if parsed.options.is_empty() {
2264+
break;
2265+
}
2266+
if parsed.delimiter == KeyValueOptionsDelimiter::Comma {
2267+
delimiter = KeyValueOptionsDelimiter::Comma;
2268+
}
2269+
option_list.extend(parsed.options);
2270+
}
2271+
}
2272+
let options = KeyValueOptions {
2273+
options: option_list,
2274+
delimiter,
22592275
};
22602276

22612277
Ok(Statement::CreateFileFormat {

tests/sqlparser_snowflake.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6927,6 +6927,28 @@ fn test_create_file_format_temporary_with_comment() {
69276927
}
69286928
}
69296929

6930+
#[test]
6931+
fn test_create_file_format_comment_between_options() {
6932+
// Snowflake accepts options in any order, including COMMENT before other
6933+
// options. COMMENT is hoisted into its own field; the rest stay in options.
6934+
let sql = "CREATE FILE FORMAT f TYPE = CSV COMMENT = 'c' COMPRESSION = 'GZIP'";
6935+
let canonical = "CREATE FILE FORMAT f TYPE = CSV COMPRESSION='GZIP' COMMENT = 'c'";
6936+
match snowflake().one_statement_parses_to(sql, canonical) {
6937+
Statement::CreateFileFormat {
6938+
format_type,
6939+
options,
6940+
comment,
6941+
..
6942+
} => {
6943+
assert_eq!(Some(Ident::new("CSV")), format_type);
6944+
assert_eq!(1, options.options.len());
6945+
assert_eq!("COMPRESSION", options.options[0].option_name);
6946+
assert_eq!(Some("c".to_string()), comment);
6947+
}
6948+
_ => unreachable!(),
6949+
}
6950+
}
6951+
69306952
#[test]
69316953
fn test_create_file_format_temp_synonym() {
69326954
// TEMP and VOLATILE are synonyms of TEMPORARY for file formats; both

0 commit comments

Comments
 (0)