@@ -2498,24 +2498,30 @@ pub enum Statement {
24982498 values : Vec < Option < String > > ,
24992499 } ,
25002500 /// ```sql
2501- /// COPY INTO
2501+ /// COPY INTO <table> | <location>
25022502 /// ```
2503- /// See <https://docs.snowflake.com/en/sql-reference/sql/copy-into-table>
2503+ /// See:
2504+ /// <https://docs.snowflake.com/en/sql-reference/sql/copy-into-table>
2505+ /// <https://docs.snowflake.com/en/sql-reference/sql/copy-into-location>
2506+ ///
25042507 /// Copy Into syntax available for Snowflake is different than the one implemented in
25052508 /// Postgres. Although they share common prefix, it is reasonable to implement them
25062509 /// in different enums. This can be refactored later once custom dialects
25072510 /// are allowed to have custom Statements.
25082511 CopyIntoSnowflake {
2512+ kind : CopyIntoSnowflakeKind ,
25092513 into : ObjectName ,
2510- from_stage : ObjectName ,
2511- from_stage_alias : Option < Ident > ,
2514+ from_obj : Option < ObjectName > ,
2515+ from_obj_alias : Option < Ident > ,
25122516 stage_params : StageParamsObject ,
25132517 from_transformations : Option < Vec < StageLoadSelectItem > > ,
2518+ from_query : Option < Box < Query > > ,
25142519 files : Option < Vec < String > > ,
25152520 pattern : Option < String > ,
25162521 file_format : DataLoadingOptions ,
25172522 copy_options : DataLoadingOptions ,
25182523 validation_mode : Option < String > ,
2524+ partition : Option < Box < Expr > > ,
25192525 } ,
25202526 /// ```sql
25212527 /// CLOSE
@@ -5048,60 +5054,69 @@ impl fmt::Display for Statement {
50485054 Ok ( ( ) )
50495055 }
50505056 Statement :: CopyIntoSnowflake {
5057+ kind,
50515058 into,
5052- from_stage ,
5053- from_stage_alias ,
5059+ from_obj ,
5060+ from_obj_alias ,
50545061 stage_params,
50555062 from_transformations,
5063+ from_query,
50565064 files,
50575065 pattern,
50585066 file_format,
50595067 copy_options,
50605068 validation_mode,
5069+ partition,
50615070 } => {
50625071 write ! ( f, "COPY INTO {}" , into) ?;
5063- if from_transformations. is_none ( ) {
5064- // Standard data load
5065- write ! ( f, " FROM {}{}" , from_stage, stage_params) ?;
5066- if from_stage_alias. as_ref ( ) . is_some ( ) {
5067- write ! ( f, " AS {}" , from_stage_alias. as_ref( ) . unwrap( ) ) ?;
5068- }
5069- } else {
5072+ if let Some ( from_transformations) = from_transformations {
50705073 // Data load with transformation
5071- write ! (
5072- f,
5073- " FROM (SELECT {} FROM {}{}" ,
5074- display_separated( from_transformations. as_ref( ) . unwrap( ) , ", " ) ,
5075- from_stage,
5076- stage_params,
5077- ) ?;
5078- if from_stage_alias. as_ref ( ) . is_some ( ) {
5079- write ! ( f, " AS {}" , from_stage_alias. as_ref( ) . unwrap( ) ) ?;
5074+ if let Some ( from_stage) = from_obj {
5075+ write ! (
5076+ f,
5077+ " FROM (SELECT {} FROM {}{}" ,
5078+ display_separated( from_transformations, ", " ) ,
5079+ from_stage,
5080+ stage_params
5081+ ) ?;
5082+ }
5083+ if let Some ( from_obj_alias) = from_obj_alias {
5084+ write ! ( f, " AS {}" , from_obj_alias) ?;
50805085 }
50815086 write ! ( f, ")" ) ?;
5087+ } else if let Some ( from_obj) = from_obj {
5088+ // Standard data load
5089+ write ! ( f, " FROM {}{}" , from_obj, stage_params) ?;
5090+ if let Some ( from_obj_alias) = from_obj_alias {
5091+ write ! ( f, " AS {from_obj_alias}" ) ?;
5092+ }
5093+ } else if let Some ( from_query) = from_query {
5094+ // Data unload from query
5095+ write ! ( f, " FROM ({from_query})" ) ?;
50825096 }
5083- if files . is_some ( ) {
5084- write ! (
5085- f ,
5086- " FILES = ('{}')" ,
5087- display_separated ( files . as_ref ( ) . unwrap ( ) , "', '" )
5088- ) ?;
5097+
5098+ if let Some ( files ) = files {
5099+ write ! ( f , " FILES = ('{}')" , display_separated ( files , "', '" ) ) ? ;
5100+ }
5101+ if let Some ( pattern ) = pattern {
5102+ write ! ( f , " PATTERN = '{}'" , pattern ) ?;
50895103 }
5090- if pattern . is_some ( ) {
5091- write ! ( f, " PATTERN = '{}'" , pattern . as_ref ( ) . unwrap ( ) ) ?;
5104+ if let Some ( partition ) = partition {
5105+ write ! ( f, " PARTITION BY {partition}" ) ?;
50925106 }
50935107 if !file_format. options . is_empty ( ) {
50945108 write ! ( f, " FILE_FORMAT=({})" , file_format) ?;
50955109 }
50965110 if !copy_options. options . is_empty ( ) {
5097- write ! ( f, " COPY_OPTIONS=({})" , copy_options) ?;
5111+ match kind {
5112+ CopyIntoSnowflakeKind :: Table => {
5113+ write ! ( f, " COPY_OPTIONS=({})" , copy_options) ?
5114+ }
5115+ CopyIntoSnowflakeKind :: Location => write ! ( f, " {copy_options}" ) ?,
5116+ }
50985117 }
5099- if validation_mode. is_some ( ) {
5100- write ! (
5101- f,
5102- " VALIDATION_MODE = {}" ,
5103- validation_mode. as_ref( ) . unwrap( )
5104- ) ?;
5118+ if let Some ( validation_mode) = validation_mode {
5119+ write ! ( f, " VALIDATION_MODE = {}" , validation_mode) ?;
51055120 }
51065121 Ok ( ( ) )
51075122 }
@@ -8543,6 +8558,19 @@ impl Display for StorageSerializationPolicy {
85438558 }
85448559}
85458560
8561+ /// Variants of the Snowflake `COPY INTO` statement
8562+ #[ derive( Debug , Copy , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
8563+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
8564+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
8565+ pub enum CopyIntoSnowflakeKind {
8566+ /// Loads data from files to a table
8567+ /// See: <https://docs.snowflake.com/en/sql-reference/sql/copy-into-table>
8568+ Table ,
8569+ /// Unloads data from a table or query to external files
8570+ /// See: <https://docs.snowflake.com/en/sql-reference/sql/copy-into-location>
8571+ Location ,
8572+ }
8573+
85468574#[ cfg( test) ]
85478575mod tests {
85488576 use super :: * ;
0 commit comments