diff --git a/src/dialect/snowflake.rs b/src/dialect/snowflake.rs index 23fad2a398..ad4fd6bb99 100644 --- a/src/dialect/snowflake.rs +++ b/src/dialect/snowflake.rs @@ -1785,7 +1785,11 @@ fn parse_select_item_for_data_load( Ok(()) } Token::Word(w) => { - alias = Some(Ident::new(w.value)); + alias = Some(Ident { + value: w.value, + quote_style: w.quote_style, + span: next_token.span, + }); Ok(()) } _ => parser.expected("alias or file_col_num", next_token), @@ -1810,10 +1814,17 @@ fn parse_select_item_for_data_load( match parser.next_token().token { Token::Colon => { // parse element - element = Some(Ident::new(match parser.next_token().token { - Token::Word(w) => Ok(w.value), - _ => parser.expected_ref("file_col_num", parser.peek_token_ref()), - }?)); + element = Some(match parser.next_token() { + TokenWithSpan { + token: Token::Word(w), + span, + } => Ident { + value: w.value, + quote_style: w.quote_style, + span, + }, + token => return parser.expected("file_col_num", token), + }); } _ => { // element not present move back @@ -1835,10 +1846,17 @@ fn parse_select_item_for_data_load( // as if parser.parse_keyword(Keyword::AS) { - item_as = Some(match parser.next_token().token { - Token::Word(w) => Ok(Ident::new(w.value)), - _ => parser.expected_ref("column item alias", parser.peek_token_ref()), - }?); + item_as = Some(match parser.next_token() { + TokenWithSpan { + token: Token::Word(w), + span, + } => Ident { + value: w.value, + quote_style: w.quote_style, + span, + }, + token => return parser.expected("column item alias", token), + }); } Ok(StageLoadSelectItem { diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index f5dfa3f5d9..5d7fcb3aaf 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -2751,6 +2751,37 @@ fn test_copy_into_with_transformations() { snowflake().parse_sql_statements(sql1).unwrap(); } +#[test] +fn test_copy_into_staged_identifiers_preserve_quotes_and_spans() { + let sql = r#"COPY INTO t FROM (SELECT "src".$1:"field" AS "Mixed" FROM @stage AS "src")"#; + snowflake().verified_stmt(sql); + let Statement::CopyIntoSnowflake { + from_transformations: Some(items), + .. + } = sqlparser::parser::Parser::parse_sql(&SnowflakeDialect {}, sql) + .unwrap() + .remove(0) + else { + panic!("expected COPY INTO with transformations"); + }; + let StageLoadSelectItemKind::StageLoadSelectItem(item) = &items[0] else { + panic!("expected a staged field"); + }; + for (ident, source) in [ + (item.alias.as_ref().unwrap(), "\"src\""), + (item.element.as_ref().unwrap(), "\"field\""), + (item.item_as.as_ref().unwrap(), "\"Mixed\""), + ] { + assert_eq!(ident.quote_style, Some('"')); + assert_eq!(ident.span.start.line, 1); + assert_eq!( + usize::try_from(ident.span.start.column).unwrap(), + sql.find(source).unwrap() + 1 + ); + } + assert_eq!(items[0].to_string(), r#""src".$1:"field" AS "Mixed""#); +} + #[test] fn test_copy_into_with_nested_transformation_path() { let sql = concat!(