Skip to content

Commit 0561c97

Browse files
support COPY INTO in snowflake
Signed-off-by: Pawel Leszczynski <[email protected]>
1 parent 29dea5d commit 0561c97

File tree

5 files changed

+635
-7
lines changed

5 files changed

+635
-7
lines changed

src/ast/helpers/stmt_data_loading.rs

+27
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use core::fmt::Formatter;
2424
#[cfg(feature = "serde")]
2525
use serde::{Deserialize, Serialize};
2626

27+
use crate::ast::Ident;
2728
#[cfg(feature = "visitor")]
2829
use sqlparser_derive::{Visit, VisitMut};
2930

@@ -63,6 +64,16 @@ pub struct DataLoadingOption {
6364
pub value: String,
6465
}
6566

67+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
68+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
69+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
70+
pub struct StageLoadSelectItem {
71+
pub alias: Option<Ident>,
72+
pub file_col_num: i32,
73+
pub element: Option<Ident>,
74+
pub item_as: Option<Ident>,
75+
}
76+
6677
impl fmt::Display for StageParamsObject {
6778
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6879
let url = &self.url.as_ref();
@@ -121,3 +132,19 @@ impl fmt::Display for DataLoadingOption {
121132
Ok(())
122133
}
123134
}
135+
136+
impl fmt::Display for StageLoadSelectItem {
137+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
138+
if self.alias.is_some() {
139+
write!(f, "{}.", self.alias.as_ref().unwrap())?;
140+
}
141+
write!(f, "${}", self.file_col_num)?;
142+
if self.element.is_some() {
143+
write!(f, ":{}", self.element.as_ref().unwrap())?;
144+
}
145+
if self.item_as.is_some() {
146+
write!(f, " AS {}", self.item_as.as_ref().unwrap())?;
147+
}
148+
Ok(())
149+
}
150+
}

src/ast/mod.rs

+81-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ pub use self::value::{
4444
escape_quoted_string, DateTimeField, DollarQuotedString, TrimWhereField, Value,
4545
};
4646

47-
use crate::ast::helpers::stmt_data_loading::{DataLoadingOptions, StageParamsObject};
47+
use crate::ast::helpers::stmt_data_loading::{
48+
DataLoadingOptions, StageLoadSelectItem, StageParamsObject,
49+
};
4850
#[cfg(feature = "visitor")]
4951
pub use visitor::*;
5052

@@ -1187,6 +1189,26 @@ pub enum Statement {
11871189
/// VALUES a vector of values to be copied
11881190
values: Vec<Option<String>>,
11891191
},
1192+
/// ```sql
1193+
/// COPY INTO
1194+
/// ```
1195+
/// See <https://docs.snowflake.com/en/sql-reference/sql/copy-into-table>
1196+
/// Copy Into syntax available for Snowflake is different than the one implemented in
1197+
/// Postgres. Although they share common prefix, it is reasonable to implement them
1198+
/// in different enums. This can be refactored later once custom dialects
1199+
/// are allowed to have custom Statements.
1200+
CopyIntoSnowflake {
1201+
into: ObjectName,
1202+
from_stage: ObjectName,
1203+
from_stage_alias: Option<Ident>,
1204+
stage_params: StageParamsObject,
1205+
from_transformations: Option<Vec<StageLoadSelectItem>>,
1206+
files: Option<Vec<String>>,
1207+
pattern: Option<String>,
1208+
file_format: DataLoadingOptions,
1209+
copy_options: DataLoadingOptions,
1210+
validation_mode: Option<String>,
1211+
},
11901212
/// Close - closes the portal underlying an open cursor.
11911213
Close {
11921214
/// Cursor name
@@ -2795,6 +2817,64 @@ impl fmt::Display for Statement {
27952817
}
27962818
Ok(())
27972819
}
2820+
Statement::CopyIntoSnowflake {
2821+
into,
2822+
from_stage,
2823+
from_stage_alias,
2824+
stage_params,
2825+
from_transformations,
2826+
files,
2827+
pattern,
2828+
file_format,
2829+
copy_options,
2830+
validation_mode,
2831+
} => {
2832+
write!(f, "COPY INTO {}", into)?;
2833+
if from_transformations.is_none() {
2834+
// Standard data load
2835+
write!(f, " FROM {}{}", from_stage, stage_params)?;
2836+
if from_stage_alias.as_ref().is_some() {
2837+
write!(f, " AS {}", from_stage_alias.as_ref().unwrap())?;
2838+
}
2839+
} else {
2840+
// Data load with transformation
2841+
write!(
2842+
f,
2843+
" FROM (SELECT {} FROM {}{}",
2844+
display_separated(from_transformations.as_ref().unwrap(), ", "),
2845+
from_stage,
2846+
stage_params,
2847+
)?;
2848+
if from_stage_alias.as_ref().is_some() {
2849+
write!(f, " AS {}", from_stage_alias.as_ref().unwrap())?;
2850+
}
2851+
write!(f, ")")?;
2852+
}
2853+
if files.is_some() {
2854+
write!(
2855+
f,
2856+
" FILES = ('{}')",
2857+
display_separated(files.as_ref().unwrap(), "', '")
2858+
)?;
2859+
}
2860+
if pattern.is_some() {
2861+
write!(f, " PATTERN = '{}'", pattern.as_ref().unwrap())?;
2862+
}
2863+
if !file_format.options.is_empty() {
2864+
write!(f, " FILE_FORMAT=({})", file_format)?;
2865+
}
2866+
if !copy_options.options.is_empty() {
2867+
write!(f, " COPY_OPTIONS=({})", copy_options)?;
2868+
}
2869+
if validation_mode.is_some() {
2870+
write!(
2871+
f,
2872+
" VALIDATION_MODE = {}",
2873+
validation_mode.as_ref().unwrap()
2874+
)?;
2875+
}
2876+
Ok(())
2877+
}
27982878
}
27992879
}
28002880
}

0 commit comments

Comments
 (0)