diff --git a/src/dialect/snowflake.rs b/src/dialect/snowflake.rs index 1ac21d007..fda5b7b97 100644 --- a/src/dialect/snowflake.rs +++ b/src/dialect/snowflake.rs @@ -1545,6 +1545,12 @@ 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. + 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 { diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index 790bf1515..32a55d500 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -2447,6 +2447,35 @@ fn test_copy_into_with_transformations() { snowflake().parse_sql_statements(sql1).unwrap(); } +#[test] +fn test_copy_into_with_cast_transformation() { + 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)"#, + ), + 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().verified_stmt(sql); + } +} + #[test] fn test_copy_into_file_format() { let sql = concat!(