Skip to content

Commit 3b2bb45

Browse files
committed
Redshift: UNLOAD
1 parent 376f47e commit 3b2bb45

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
@@ -4291,15 +4291,24 @@ pub enum Statement {
42914291
/// ```
42924292
/// Note: this is a MySQL-specific statement. See <https://dev.mysql.com/doc/refman/8.0/en/lock-tables.html>
42934293
UnlockTables,
4294+
/// Unloads the result of a query to file
4295+
///
4296+
/// [Athena](https://docs.aws.amazon.com/athena/latest/ug/unload.html):
42944297
/// ```sql
42954298
/// UNLOAD(statement) TO <destination> [ WITH options ]
42964299
/// ```
4297-
/// See Redshift <https://docs.aws.amazon.com/redshift/latest/dg/r_UNLOAD.html> and
4298-
// Athena <https://docs.aws.amazon.com/athena/latest/ug/unload.html>
4300+
///
4301+
/// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_UNLOAD.html):
4302+
/// ```sql
4303+
/// UNLOAD('statement') TO <destination> [ OPTIONS ]
4304+
/// ```
42994305
Unload {
4300-
query: Box<Query>,
4306+
query: Option<Box<Query>>,
4307+
query_text: Option<String>,
43014308
to: Ident,
4309+
auth: Option<IamRoleKind>,
43024310
with: Vec<SqlOption>,
4311+
options: Vec<CopyLegacyOption>,
43034312
},
43044313
/// ```sql
43054314
/// OPTIMIZE TABLE [db.]name [ON CLUSTER cluster] [PARTITION partition | PARTITION ID 'partition_id'] [FINAL] [DEDUPLICATE [BY expression]]
@@ -6277,13 +6286,31 @@ impl fmt::Display for Statement {
62776286
Statement::UnlockTables => {
62786287
write!(f, "UNLOCK TABLES")
62796288
}
6280-
Statement::Unload { query, to, with } => {
6281-
write!(f, "UNLOAD({query}) TO {to}")?;
6282-
6289+
Statement::Unload {
6290+
query,
6291+
query_text,
6292+
to,
6293+
auth,
6294+
with,
6295+
options,
6296+
} => {
6297+
write!(f, "UNLOAD(")?;
6298+
if let Some(query) = query {
6299+
write!(f, "{query}")?;
6300+
}
6301+
if let Some(query_text) = query_text {
6302+
write!(f, "'{query_text}'")?;
6303+
}
6304+
write!(f, ") TO {to}")?;
6305+
if let Some(auth) = auth {
6306+
write!(f, " IAM_ROLE {auth}")?;
6307+
}
62836308
if !with.is_empty() {
62846309
write!(f, " WITH ({})", display_comma_separated(with))?;
62856310
}
6286-
6311+
if !options.is_empty() {
6312+
write!(f, " {}", display_separated(options, " "))?;
6313+
}
62876314
Ok(())
62886315
}
62896316
Statement::OptimizeTable {
@@ -8784,10 +8811,18 @@ pub enum CopyLegacyOption {
87848811
AcceptAnyDate,
87858812
/// ACCEPTINVCHARS
87868813
AcceptInvChars(Option<String>),
8814+
/// ADDQUOTES
8815+
AddQuotes,
8816+
/// ALLOWOVERWRITE
8817+
AllowOverwrite,
87878818
/// BINARY
87888819
Binary,
87898820
/// BLANKSASNULL
87908821
BlankAsNull,
8822+
/// BZIP2
8823+
Bzip2,
8824+
/// CLEANPATH
8825+
CleanPath,
87918826
/// CSV ...
87928827
Csv(Vec<CopyLegacyCsvOption>),
87938828
/// DATEFORMAT \[ AS \] {'dateformat_string' | 'auto' }
@@ -8796,16 +8831,46 @@ pub enum CopyLegacyOption {
87968831
Delimiter(char),
87978832
/// EMPTYASNULL
87988833
EmptyAsNull,
8834+
/// ENCRYPTED \[ AUTO \]
8835+
Encrypted { auto: bool },
8836+
/// ESCAPE
8837+
Escape,
8838+
/// EXTENSION 'extension-name'
8839+
Extension(String),
8840+
/// FIXEDWIDTH \[ AS \] 'fixedwidth-spec'
8841+
FixedWidth(String),
8842+
/// GZIP
8843+
Gzip,
8844+
/// HEADER
8845+
Header,
87998846
/// IAM_ROLE { DEFAULT | 'arn:aws:iam::123456789:role/role1' }
88008847
IamRole(IamRoleKind),
88018848
/// IGNOREHEADER \[ AS \] number_rows
88028849
IgnoreHeader(u64),
8850+
/// JSON
8851+
Json,
8852+
/// MANIFEST \[ VERBOSE \]
8853+
Manifest { verbose: bool },
8854+
/// MAXFILESIZE \[ AS \] max-size \[ MB | GB \]
8855+
MaxFileSize(FileSize),
88038856
/// NULL \[ AS \] 'null_string'
88048857
Null(String),
8858+
/// PARALLEL
8859+
Parallel(Option<bool>),
8860+
/// PARQUET
8861+
Parquet,
8862+
/// PARTITION BY ( column_name [, ... ] ) \[ INCLUDE \]
8863+
PartitionBy(PartitionBy),
8864+
/// REGION \[ AS \] 'aws-region' }
8865+
Region(String),
8866+
/// ROWGROUPSIZE \[ AS \] size \[ MB | GB \]
8867+
RowGroupSize(FileSize),
88058868
/// TIMEFORMAT \[ AS \] {'timeformat_string' | 'auto' | 'epochsecs' | 'epochmillisecs' }
88068869
TimeFormat(Option<String>),
88078870
/// TRUNCATECOLUMNS
88088871
TruncateColumns,
8872+
/// ZSTD
8873+
Zstd,
88098874
}
88108875

88118876
impl fmt::Display for CopyLegacyOption {
@@ -8820,8 +8885,12 @@ impl fmt::Display for CopyLegacyOption {
88208885
}
88218886
Ok(())
88228887
}
8888+
AddQuotes => write!(f, "ADDQUOTES"),
8889+
AllowOverwrite => write!(f, "ALLOWOVERWRITE"),
88238890
Binary => write!(f, "BINARY"),
88248891
BlankAsNull => write!(f, "BLANKSASNULL"),
8892+
Bzip2 => write!(f, "BZIP2"),
8893+
CleanPath => write!(f, "CLEANPATH"),
88258894
Csv(opts) => {
88268895
write!(f, "CSV")?;
88278896
if !opts.is_empty() {
@@ -8838,9 +8907,37 @@ impl fmt::Display for CopyLegacyOption {
88388907
}
88398908
Delimiter(char) => write!(f, "DELIMITER '{char}'"),
88408909
EmptyAsNull => write!(f, "EMPTYASNULL"),
8910+
Encrypted { auto } => write!(f, "ENCRYPTED{}", if *auto { " AUTO" } else { "" }),
8911+
Escape => write!(f, "ESCAPE"),
8912+
Extension(ext) => write!(f, "EXTENSION '{}'", value::escape_single_quote_string(ext)),
8913+
FixedWidth(spec) => write!(
8914+
f,
8915+
"FIXEDWIDTH '{}'",
8916+
value::escape_single_quote_string(spec)
8917+
),
8918+
Gzip => write!(f, "GZIP"),
8919+
Header => write!(f, "HEADER"),
88418920
IamRole(role) => write!(f, "IAM_ROLE {role}"),
88428921
IgnoreHeader(num_rows) => write!(f, "IGNOREHEADER {num_rows}"),
8922+
Json => write!(f, "JSON"),
8923+
Manifest { verbose } => write!(f, "MANIFEST{}", if *verbose { " VERBOSE" } else { "" }),
8924+
MaxFileSize(file_size) => write!(f, "MAXFILESIZE {file_size}"),
88438925
Null(string) => write!(f, "NULL '{}'", value::escape_single_quote_string(string)),
8926+
Parallel(enabled) => {
8927+
write!(
8928+
f,
8929+
"PARALLEL{}",
8930+
match enabled {
8931+
Some(true) => " TRUE",
8932+
Some(false) => " FALSE",
8933+
_ => "",
8934+
}
8935+
)
8936+
}
8937+
Parquet => write!(f, "PARQUET"),
8938+
PartitionBy(p) => write!(f, "{p}"),
8939+
Region(region) => write!(f, "REGION '{}'", value::escape_single_quote_string(region)),
8940+
RowGroupSize(file_size) => write!(f, "ROWGROUPSIZE {file_size}"),
88448941
TimeFormat(fmt) => {
88458942
write!(f, "TIMEFORMAT")?;
88468943
if let Some(fmt) = fmt {
@@ -8849,10 +8946,73 @@ impl fmt::Display for CopyLegacyOption {
88498946
Ok(())
88508947
}
88518948
TruncateColumns => write!(f, "TRUNCATECOLUMNS"),
8949+
Zstd => write!(f, "ZSTD"),
8950+
}
8951+
}
8952+
}
8953+
8954+
/// ```sql
8955+
/// SIZE \[ MB | GB \]
8956+
/// ```
8957+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
8958+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
8959+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
8960+
pub struct FileSize {
8961+
pub size: Value,
8962+
pub unit: Option<FileSizeUnit>,
8963+
}
8964+
8965+
impl fmt::Display for FileSize {
8966+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
8967+
write!(f, "{}", self.size)?;
8968+
if let Some(unit) = &self.unit {
8969+
write!(f, " {unit}")?;
8970+
}
8971+
Ok(())
8972+
}
8973+
}
8974+
8975+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
8976+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
8977+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
8978+
pub enum FileSizeUnit {
8979+
MB,
8980+
GB,
8981+
}
8982+
8983+
impl fmt::Display for FileSizeUnit {
8984+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
8985+
match self {
8986+
FileSizeUnit::MB => write!(f, "MB"),
8987+
FileSizeUnit::GB => write!(f, "GB"),
88528988
}
88538989
}
88548990
}
88558991

8992+
/// Specifies the partition keys for the unload operation
8993+
///
8994+
/// ```sql
8995+
/// PARTITION BY ( column_name [, ... ] ) [ INCLUDE ]
8996+
/// ```
8997+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
8998+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
8999+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
9000+
pub struct PartitionBy {
9001+
pub columns: Vec<Ident>,
9002+
pub include: bool,
9003+
}
9004+
9005+
impl fmt::Display for PartitionBy {
9006+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
9007+
write!(
9008+
f,
9009+
"PARTITION BY ({}){}",
9010+
display_comma_separated(&self.columns),
9011+
if self.include { " INCLUDE" } else { "" }
9012+
)
9013+
}
9014+
}
9015+
88569016
/// An `IAM_ROLE` option in the AWS ecosystem
88579017
///
88589018
/// [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,
@@ -1061,7 +1074,8 @@ define_keywords!(
10611074
YEAR,
10621075
YEARS,
10631076
ZONE,
1064-
ZORDER
1077+
ZORDER,
1078+
ZSTD
10651079
);
10661080

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

0 commit comments

Comments
 (0)