Skip to content

Commit 10e8499

Browse files
committed
Redshift: UNLOAD
1 parent 9b6f6de commit 10e8499

File tree

4 files changed

+411
-18
lines changed

4 files changed

+411
-18
lines changed

src/ast/mod.rs

Lines changed: 167 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4294,15 +4294,24 @@ pub enum Statement {
42944294
/// ```
42954295
/// Note: this is a MySQL-specific statement. See <https://dev.mysql.com/doc/refman/8.0/en/lock-tables.html>
42964296
UnlockTables,
4297+
/// Unloads the result of a query to file
4298+
///
4299+
/// [Athena](https://docs.aws.amazon.com/athena/latest/ug/unload.html):
42974300
/// ```sql
42984301
/// UNLOAD(statement) TO <destination> [ WITH options ]
42994302
/// ```
4300-
/// See Redshift <https://docs.aws.amazon.com/redshift/latest/dg/r_UNLOAD.html> and
4301-
// Athena <https://docs.aws.amazon.com/athena/latest/ug/unload.html>
4303+
///
4304+
/// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_UNLOAD.html):
4305+
/// ```sql
4306+
/// UNLOAD('statement') TO <destination> [ OPTIONS ]
4307+
/// ```
43024308
Unload {
4303-
query: Box<Query>,
4309+
query: Option<Box<Query>>,
4310+
query_text: Option<String>,
43044311
to: Ident,
4312+
auth: Option<IamRoleKind>,
43054313
with: Vec<SqlOption>,
4314+
options: Vec<CopyLegacyOption>,
43064315
},
43074316
/// ```sql
43084317
/// OPTIMIZE TABLE [db.]name [ON CLUSTER cluster] [PARTITION partition | PARTITION ID 'partition_id'] [FINAL] [DEDUPLICATE [BY expression]]
@@ -6282,13 +6291,31 @@ impl fmt::Display for Statement {
62826291
Statement::UnlockTables => {
62836292
write!(f, "UNLOCK TABLES")
62846293
}
6285-
Statement::Unload { query, to, with } => {
6286-
write!(f, "UNLOAD({query}) TO {to}")?;
6287-
6294+
Statement::Unload {
6295+
query,
6296+
query_text,
6297+
to,
6298+
auth,
6299+
with,
6300+
options,
6301+
} => {
6302+
write!(f, "UNLOAD(")?;
6303+
if let Some(query) = query {
6304+
write!(f, "{query}")?;
6305+
}
6306+
if let Some(query_text) = query_text {
6307+
write!(f, "'{query_text}'")?;
6308+
}
6309+
write!(f, ") TO {to}")?;
6310+
if let Some(auth) = auth {
6311+
write!(f, " IAM_ROLE {auth}")?;
6312+
}
62886313
if !with.is_empty() {
62896314
write!(f, " WITH ({})", display_comma_separated(with))?;
62906315
}
6291-
6316+
if !options.is_empty() {
6317+
write!(f, " {}", display_separated(options, " "))?;
6318+
}
62926319
Ok(())
62936320
}
62946321
Statement::OptimizeTable {
@@ -8797,10 +8824,18 @@ pub enum CopyLegacyOption {
87978824
AcceptAnyDate,
87988825
/// ACCEPTINVCHARS
87998826
AcceptInvChars(Option<String>),
8827+
/// ADDQUOTES
8828+
AddQuotes,
8829+
/// ALLOWOVERWRITE
8830+
AllowOverwrite,
88008831
/// BINARY
88018832
Binary,
88028833
/// BLANKSASNULL
88038834
BlankAsNull,
8835+
/// BZIP2
8836+
Bzip2,
8837+
/// CLEANPATH
8838+
CleanPath,
88048839
/// CSV ...
88058840
Csv(Vec<CopyLegacyCsvOption>),
88068841
/// DATEFORMAT \[ AS \] {'dateformat_string' | 'auto' }
@@ -8809,16 +8844,46 @@ pub enum CopyLegacyOption {
88098844
Delimiter(char),
88108845
/// EMPTYASNULL
88118846
EmptyAsNull,
8847+
/// ENCRYPTED \[ AUTO \]
8848+
Encrypted { auto: bool },
8849+
/// ESCAPE
8850+
Escape,
8851+
/// EXTENSION 'extension-name'
8852+
Extension(String),
8853+
/// FIXEDWIDTH \[ AS \] 'fixedwidth-spec'
8854+
FixedWidth(String),
8855+
/// GZIP
8856+
Gzip,
8857+
/// HEADER
8858+
Header,
88128859
/// IAM_ROLE { DEFAULT | 'arn:aws:iam::123456789:role/role1' }
88138860
IamRole(IamRoleKind),
88148861
/// IGNOREHEADER \[ AS \] number_rows
88158862
IgnoreHeader(u64),
8863+
/// JSON
8864+
Json,
8865+
/// MANIFEST \[ VERBOSE \]
8866+
Manifest { verbose: bool },
8867+
/// MAXFILESIZE \[ AS \] max-size \[ MB | GB \]
8868+
MaxFileSize(FileSize),
88168869
/// NULL \[ AS \] 'null_string'
88178870
Null(String),
8871+
/// PARALLEL
8872+
Parallel(Option<bool>),
8873+
/// PARQUET
8874+
Parquet,
8875+
/// PARTITION BY ( column_name [, ... ] ) \[ INCLUDE \]
8876+
PartitionBy(PartitionBy),
8877+
/// REGION \[ AS \] 'aws-region' }
8878+
Region(String),
8879+
/// ROWGROUPSIZE \[ AS \] size \[ MB | GB \]
8880+
RowGroupSize(FileSize),
88188881
/// TIMEFORMAT \[ AS \] {'timeformat_string' | 'auto' | 'epochsecs' | 'epochmillisecs' }
88198882
TimeFormat(Option<String>),
88208883
/// TRUNCATECOLUMNS
88218884
TruncateColumns,
8885+
/// ZSTD
8886+
Zstd,
88228887
}
88238888

88248889
impl fmt::Display for CopyLegacyOption {
@@ -8833,8 +8898,12 @@ impl fmt::Display for CopyLegacyOption {
88338898
}
88348899
Ok(())
88358900
}
8901+
AddQuotes => write!(f, "ADDQUOTES"),
8902+
AllowOverwrite => write!(f, "ALLOWOVERWRITE"),
88368903
Binary => write!(f, "BINARY"),
88378904
BlankAsNull => write!(f, "BLANKSASNULL"),
8905+
Bzip2 => write!(f, "BZIP2"),
8906+
CleanPath => write!(f, "CLEANPATH"),
88388907
Csv(opts) => {
88398908
write!(f, "CSV")?;
88408909
if !opts.is_empty() {
@@ -8851,9 +8920,37 @@ impl fmt::Display for CopyLegacyOption {
88518920
}
88528921
Delimiter(char) => write!(f, "DELIMITER '{char}'"),
88538922
EmptyAsNull => write!(f, "EMPTYASNULL"),
8923+
Encrypted { auto } => write!(f, "ENCRYPTED{}", if *auto { " AUTO" } else { "" }),
8924+
Escape => write!(f, "ESCAPE"),
8925+
Extension(ext) => write!(f, "EXTENSION '{}'", value::escape_single_quote_string(ext)),
8926+
FixedWidth(spec) => write!(
8927+
f,
8928+
"FIXEDWIDTH '{}'",
8929+
value::escape_single_quote_string(spec)
8930+
),
8931+
Gzip => write!(f, "GZIP"),
8932+
Header => write!(f, "HEADER"),
88548933
IamRole(role) => write!(f, "IAM_ROLE {role}"),
88558934
IgnoreHeader(num_rows) => write!(f, "IGNOREHEADER {num_rows}"),
8935+
Json => write!(f, "JSON"),
8936+
Manifest { verbose } => write!(f, "MANIFEST{}", if *verbose { " VERBOSE" } else { "" }),
8937+
MaxFileSize(file_size) => write!(f, "MAXFILESIZE {file_size}"),
88568938
Null(string) => write!(f, "NULL '{}'", value::escape_single_quote_string(string)),
8939+
Parallel(enabled) => {
8940+
write!(
8941+
f,
8942+
"PARALLEL{}",
8943+
match enabled {
8944+
Some(true) => " TRUE",
8945+
Some(false) => " FALSE",
8946+
_ => "",
8947+
}
8948+
)
8949+
}
8950+
Parquet => write!(f, "PARQUET"),
8951+
PartitionBy(p) => write!(f, "{p}"),
8952+
Region(region) => write!(f, "REGION '{}'", value::escape_single_quote_string(region)),
8953+
RowGroupSize(file_size) => write!(f, "ROWGROUPSIZE {file_size}"),
88578954
TimeFormat(fmt) => {
88588955
write!(f, "TIMEFORMAT")?;
88598956
if let Some(fmt) = fmt {
@@ -8862,10 +8959,73 @@ impl fmt::Display for CopyLegacyOption {
88628959
Ok(())
88638960
}
88648961
TruncateColumns => write!(f, "TRUNCATECOLUMNS"),
8962+
Zstd => write!(f, "ZSTD"),
8963+
}
8964+
}
8965+
}
8966+
8967+
/// ```sql
8968+
/// SIZE \[ MB | GB \]
8969+
/// ```
8970+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
8971+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
8972+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
8973+
pub struct FileSize {
8974+
pub size: Value,
8975+
pub unit: Option<FileSizeUnit>,
8976+
}
8977+
8978+
impl fmt::Display for FileSize {
8979+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
8980+
write!(f, "{}", self.size)?;
8981+
if let Some(unit) = &self.unit {
8982+
write!(f, " {unit}")?;
8983+
}
8984+
Ok(())
8985+
}
8986+
}
8987+
8988+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
8989+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
8990+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
8991+
pub enum FileSizeUnit {
8992+
MB,
8993+
GB,
8994+
}
8995+
8996+
impl fmt::Display for FileSizeUnit {
8997+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
8998+
match self {
8999+
FileSizeUnit::MB => write!(f, "MB"),
9000+
FileSizeUnit::GB => write!(f, "GB"),
88659001
}
88669002
}
88679003
}
88689004

9005+
/// Specifies the partition keys for the unload operation
9006+
///
9007+
/// ```sql
9008+
/// PARTITION BY ( column_name [, ... ] ) [ INCLUDE ]
9009+
/// ```
9010+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
9011+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
9012+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
9013+
pub struct PartitionBy {
9014+
pub columns: Vec<Ident>,
9015+
pub include: bool,
9016+
}
9017+
9018+
impl fmt::Display for PartitionBy {
9019+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
9020+
write!(
9021+
f,
9022+
"PARTITION BY ({}){}",
9023+
display_comma_separated(&self.columns),
9024+
if self.include { " INCLUDE" } else { "" }
9025+
)
9026+
}
9027+
}
9028+
88699029
/// An `IAM_ROLE` option in the AWS ecosystem
88709030
///
88719031
/// [Redshift COPY](https://docs.aws.amazon.com/redshift/latest/dg/copy-parameters-authorization.html#copy-iam-role)

src/keywords.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ define_keywords!(
8282
ACCOUNT,
8383
ACTION,
8484
ADD,
85+
ADDQUOTES,
8586
ADMIN,
8687
AFTER,
8788
AGAINST,
@@ -92,6 +93,7 @@ define_keywords!(
9293
ALIAS,
9394
ALL,
9495
ALLOCATE,
96+
ALLOWOVERWRITE,
9597
ALTER,
9698
ALWAYS,
9799
ANALYZE,
@@ -159,6 +161,7 @@ define_keywords!(
159161
BYPASSRLS,
160162
BYTEA,
161163
BYTES,
164+
BZIP2,
162165
CACHE,
163166
CALL,
164167
CALLED,
@@ -190,6 +193,7 @@ define_keywords!(
190193
CHECK,
191194
CHECKSUM,
192195
CIRCLE,
196+
CLEANPATH,
193197
CLEAR,
194198
CLOB,
195199
CLONE,
@@ -322,6 +326,7 @@ define_keywords!(
322326
ENABLE,
323327
ENABLE_SCHEMA_EVOLUTION,
324328
ENCODING,
329+
ENCRYPTED,
325330
ENCRYPTION,
326331
END,
327332
END_EXEC = "END-EXEC",
@@ -380,6 +385,7 @@ define_keywords!(
380385
FIRST,
381386
FIRST_VALUE,
382387
FIXEDSTRING,
388+
FIXEDWIDTH,
383389
FLATTEN,
384390
FLOAT,
385391
FLOAT32,
@@ -411,6 +417,7 @@ define_keywords!(
411417
FUNCTIONS,
412418
FUSION,
413419
FUTURE,
420+
GB,
414421
GENERAL,
415422
GENERATE,
416423
GENERATED,
@@ -426,6 +433,7 @@ define_keywords!(
426433
GROUP,
427434
GROUPING,
428435
GROUPS,
436+
GZIP,
429437
HASH,
430438
HAVING,
431439
HEADER,
@@ -550,6 +558,7 @@ define_keywords!(
550558
MANAGE,
551559
MANAGED,
552560
MANAGEDLOCATION,
561+
MANIFEST,
553562
MAP,
554563
MASKING,
555564
MATCH,
@@ -560,9 +569,11 @@ define_keywords!(
560569
MATERIALIZE,
561570
MATERIALIZED,
562571
MAX,
572+
MAXFILESIZE,
563573
MAXVALUE,
564574
MAX_DATA_EXTENSION_TIME_IN_DAYS,
565575
MAX_ROWS,
576+
MB,
566577
MEASURES,
567578
MEDIUMBLOB,
568579
MEDIUMINT,
@@ -761,6 +772,7 @@ define_keywords!(
761772
REFRESH_MODE,
762773
REGCLASS,
763774
REGEXP,
775+
REGION,
764776
REGR_AVGX,
765777
REGR_AVGY,
766778
REGR_COUNT,
@@ -813,6 +825,7 @@ define_keywords!(
813825
ROLLUP,
814826
ROOT,
815827
ROW,
828+
ROWGROUPSIZE,
816829
ROWID,
817830
ROWS,
818831
ROW_FORMAT,
@@ -1062,7 +1075,8 @@ define_keywords!(
10621075
YEAR,
10631076
YEARS,
10641077
ZONE,
1065-
ZORDER
1078+
ZORDER,
1079+
ZSTD
10661080
);
10671081

10681082
/// These keywords can't be used as a table alias, so that `FROM table_name alias`

0 commit comments

Comments
 (0)