Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/dialect/snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1545,6 +1545,14 @@ fn parse_select_item_for_data_load(
}
}

// A trailing `::` means this is a cast expression (e.g.
// `$1:"col"::NUMBER(38,0)`), not a stage-load-select-item. Bail so
// `maybe_parse` rewinds and the caller falls through to
// `parse_select_item`, which handles the cast correctly.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// `$1:"col"::NUMBER(38,0)`), not a stage-load-select-item. Bail so
// `maybe_parse` rewinds and the caller falls through to
// `parse_select_item`, which handles the cast correctly.
// `$1:"col"::NUMBER(38,0)`), not a stage-load-select-item.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

if matches!(parser.peek_token_ref().token, Token::DoubleColon) {
return parser.expected("stage load select item", parser.peek_token());
}

// as
if parser.parse_keyword(Keyword::AS) {
item_as = Some(match parser.next_token().token {
Expand Down
43 changes: 43 additions & 0 deletions tests/sqlparser_snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2447,6 +2447,49 @@ fn test_copy_into_with_transformations() {
snowflake().parse_sql_statements(sql1).unwrap();
}

#[test]
fn test_copy_into_with_cast_transformation() {
// Snowflake `COPY INTO` transformation lists support casts like
// `$1:"col"::TYPE` and `$1::TYPE`. These are distinct from the bare
// `$<n>[.<col>][:<elem>]` stage-load-select-item shape, so the parser
// must fall through to the generic select-item parser to handle the
// `::` cast operator.
//
// Regression: the Snowflake-specific parser used to successfully
// consume `$1:"col"` as a stage-load-select-item, leaving `::TYPE`
// behind and causing "Expected: FROM, found: ::" once the COPY INTO
// body tried to expect `FROM`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Snowflake `COPY INTO` transformation lists support casts like
// `$1:"col"::TYPE` and `$1::TYPE`. These are distinct from the bare
// `$<n>[.<col>][:<elem>]` stage-load-select-item shape, so the parser
// must fall through to the generic select-item parser to handle the
// `::` cast operator.
//
// Regression: the Snowflake-specific parser used to successfully
// consume `$1:"col"` as a stage-load-select-item, leaving `::TYPE`
// behind and causing "Expected: FROM, found: ::" once the COPY INTO
// body tried to expect `FROM`.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

let variants = [
concat!(
"COPY INTO my_company.emp_basic (a) FROM ",
r#"(SELECT $1:"A"::NUMBER(38, 0) FROM @stg)"#,
),
concat!(
"COPY INTO my_company.emp_basic (a) FROM ",
"(SELECT $1::NUMBER(38, 0) FROM @stg)",
),
concat!(
"COPY INTO my_company.emp_basic (a) FROM ",
"(SELECT $1:SEQUENCE::NUMBER(38, 0) FROM @stg)",
),
concat!(
"COPY INTO my_company.emp_basic (a, b) FROM ",
r#"(SELECT $1:"A"::VARIANT, $1:"B"::TEXT FROM @stg)"#,
),
// Mix with an ordinary stage-load-select-item in the same list,
// so we don't over-correct and break the existing shape.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Mix with an ordinary stage-load-select-item in the same list,
// so we don't over-correct and break the existing shape.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

concat!(
"COPY INTO my_company.emp_basic (a, b) FROM ",
r#"(SELECT t.$1:plain AS plain, $1:"B"::TEXT FROM @stg AS t)"#,
),
];
for sql in variants {
snowflake().parse_sql_statements(sql).unwrap_or_else(|e| {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these should use verified_stmt?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

panic!("expected {sql:?} to parse, got {e}");
});
}
}

#[test]
fn test_copy_into_file_format() {
let sql = concat!(
Expand Down