mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-24 07:54:06 +00:00
Redshift: UNLOAD
This commit is contained in:
parent
376f47e3d1
commit
3b2bb4530e
4 changed files with 411 additions and 18 deletions
174
src/ast/mod.rs
174
src/ast/mod.rs
|
@ -4291,15 +4291,24 @@ pub enum Statement {
|
||||||
/// ```
|
/// ```
|
||||||
/// Note: this is a MySQL-specific statement. See <https://dev.mysql.com/doc/refman/8.0/en/lock-tables.html>
|
/// Note: this is a MySQL-specific statement. See <https://dev.mysql.com/doc/refman/8.0/en/lock-tables.html>
|
||||||
UnlockTables,
|
UnlockTables,
|
||||||
|
/// Unloads the result of a query to file
|
||||||
|
///
|
||||||
|
/// [Athena](https://docs.aws.amazon.com/athena/latest/ug/unload.html):
|
||||||
/// ```sql
|
/// ```sql
|
||||||
/// UNLOAD(statement) TO <destination> [ WITH options ]
|
/// UNLOAD(statement) TO <destination> [ WITH options ]
|
||||||
/// ```
|
/// ```
|
||||||
/// See Redshift <https://docs.aws.amazon.com/redshift/latest/dg/r_UNLOAD.html> and
|
///
|
||||||
// Athena <https://docs.aws.amazon.com/athena/latest/ug/unload.html>
|
/// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_UNLOAD.html):
|
||||||
|
/// ```sql
|
||||||
|
/// UNLOAD('statement') TO <destination> [ OPTIONS ]
|
||||||
|
/// ```
|
||||||
Unload {
|
Unload {
|
||||||
query: Box<Query>,
|
query: Option<Box<Query>>,
|
||||||
|
query_text: Option<String>,
|
||||||
to: Ident,
|
to: Ident,
|
||||||
|
auth: Option<IamRoleKind>,
|
||||||
with: Vec<SqlOption>,
|
with: Vec<SqlOption>,
|
||||||
|
options: Vec<CopyLegacyOption>,
|
||||||
},
|
},
|
||||||
/// ```sql
|
/// ```sql
|
||||||
/// OPTIMIZE TABLE [db.]name [ON CLUSTER cluster] [PARTITION partition | PARTITION ID 'partition_id'] [FINAL] [DEDUPLICATE [BY expression]]
|
/// 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 {
|
||||||
Statement::UnlockTables => {
|
Statement::UnlockTables => {
|
||||||
write!(f, "UNLOCK TABLES")
|
write!(f, "UNLOCK TABLES")
|
||||||
}
|
}
|
||||||
Statement::Unload { query, to, with } => {
|
Statement::Unload {
|
||||||
write!(f, "UNLOAD({query}) TO {to}")?;
|
query,
|
||||||
|
query_text,
|
||||||
|
to,
|
||||||
|
auth,
|
||||||
|
with,
|
||||||
|
options,
|
||||||
|
} => {
|
||||||
|
write!(f, "UNLOAD(")?;
|
||||||
|
if let Some(query) = query {
|
||||||
|
write!(f, "{query}")?;
|
||||||
|
}
|
||||||
|
if let Some(query_text) = query_text {
|
||||||
|
write!(f, "'{query_text}'")?;
|
||||||
|
}
|
||||||
|
write!(f, ") TO {to}")?;
|
||||||
|
if let Some(auth) = auth {
|
||||||
|
write!(f, " IAM_ROLE {auth}")?;
|
||||||
|
}
|
||||||
if !with.is_empty() {
|
if !with.is_empty() {
|
||||||
write!(f, " WITH ({})", display_comma_separated(with))?;
|
write!(f, " WITH ({})", display_comma_separated(with))?;
|
||||||
}
|
}
|
||||||
|
if !options.is_empty() {
|
||||||
|
write!(f, " {}", display_separated(options, " "))?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Statement::OptimizeTable {
|
Statement::OptimizeTable {
|
||||||
|
@ -8784,10 +8811,18 @@ pub enum CopyLegacyOption {
|
||||||
AcceptAnyDate,
|
AcceptAnyDate,
|
||||||
/// ACCEPTINVCHARS
|
/// ACCEPTINVCHARS
|
||||||
AcceptInvChars(Option<String>),
|
AcceptInvChars(Option<String>),
|
||||||
|
/// ADDQUOTES
|
||||||
|
AddQuotes,
|
||||||
|
/// ALLOWOVERWRITE
|
||||||
|
AllowOverwrite,
|
||||||
/// BINARY
|
/// BINARY
|
||||||
Binary,
|
Binary,
|
||||||
/// BLANKSASNULL
|
/// BLANKSASNULL
|
||||||
BlankAsNull,
|
BlankAsNull,
|
||||||
|
/// BZIP2
|
||||||
|
Bzip2,
|
||||||
|
/// CLEANPATH
|
||||||
|
CleanPath,
|
||||||
/// CSV ...
|
/// CSV ...
|
||||||
Csv(Vec<CopyLegacyCsvOption>),
|
Csv(Vec<CopyLegacyCsvOption>),
|
||||||
/// DATEFORMAT \[ AS \] {'dateformat_string' | 'auto' }
|
/// DATEFORMAT \[ AS \] {'dateformat_string' | 'auto' }
|
||||||
|
@ -8796,16 +8831,46 @@ pub enum CopyLegacyOption {
|
||||||
Delimiter(char),
|
Delimiter(char),
|
||||||
/// EMPTYASNULL
|
/// EMPTYASNULL
|
||||||
EmptyAsNull,
|
EmptyAsNull,
|
||||||
|
/// ENCRYPTED \[ AUTO \]
|
||||||
|
Encrypted { auto: bool },
|
||||||
|
/// ESCAPE
|
||||||
|
Escape,
|
||||||
|
/// EXTENSION 'extension-name'
|
||||||
|
Extension(String),
|
||||||
|
/// FIXEDWIDTH \[ AS \] 'fixedwidth-spec'
|
||||||
|
FixedWidth(String),
|
||||||
|
/// GZIP
|
||||||
|
Gzip,
|
||||||
|
/// HEADER
|
||||||
|
Header,
|
||||||
/// IAM_ROLE { DEFAULT | 'arn:aws:iam::123456789:role/role1' }
|
/// IAM_ROLE { DEFAULT | 'arn:aws:iam::123456789:role/role1' }
|
||||||
IamRole(IamRoleKind),
|
IamRole(IamRoleKind),
|
||||||
/// IGNOREHEADER \[ AS \] number_rows
|
/// IGNOREHEADER \[ AS \] number_rows
|
||||||
IgnoreHeader(u64),
|
IgnoreHeader(u64),
|
||||||
|
/// JSON
|
||||||
|
Json,
|
||||||
|
/// MANIFEST \[ VERBOSE \]
|
||||||
|
Manifest { verbose: bool },
|
||||||
|
/// MAXFILESIZE \[ AS \] max-size \[ MB | GB \]
|
||||||
|
MaxFileSize(FileSize),
|
||||||
/// NULL \[ AS \] 'null_string'
|
/// NULL \[ AS \] 'null_string'
|
||||||
Null(String),
|
Null(String),
|
||||||
|
/// PARALLEL
|
||||||
|
Parallel(Option<bool>),
|
||||||
|
/// PARQUET
|
||||||
|
Parquet,
|
||||||
|
/// PARTITION BY ( column_name [, ... ] ) \[ INCLUDE \]
|
||||||
|
PartitionBy(PartitionBy),
|
||||||
|
/// REGION \[ AS \] 'aws-region' }
|
||||||
|
Region(String),
|
||||||
|
/// ROWGROUPSIZE \[ AS \] size \[ MB | GB \]
|
||||||
|
RowGroupSize(FileSize),
|
||||||
/// TIMEFORMAT \[ AS \] {'timeformat_string' | 'auto' | 'epochsecs' | 'epochmillisecs' }
|
/// TIMEFORMAT \[ AS \] {'timeformat_string' | 'auto' | 'epochsecs' | 'epochmillisecs' }
|
||||||
TimeFormat(Option<String>),
|
TimeFormat(Option<String>),
|
||||||
/// TRUNCATECOLUMNS
|
/// TRUNCATECOLUMNS
|
||||||
TruncateColumns,
|
TruncateColumns,
|
||||||
|
/// ZSTD
|
||||||
|
Zstd,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for CopyLegacyOption {
|
impl fmt::Display for CopyLegacyOption {
|
||||||
|
@ -8820,8 +8885,12 @@ impl fmt::Display for CopyLegacyOption {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
AddQuotes => write!(f, "ADDQUOTES"),
|
||||||
|
AllowOverwrite => write!(f, "ALLOWOVERWRITE"),
|
||||||
Binary => write!(f, "BINARY"),
|
Binary => write!(f, "BINARY"),
|
||||||
BlankAsNull => write!(f, "BLANKSASNULL"),
|
BlankAsNull => write!(f, "BLANKSASNULL"),
|
||||||
|
Bzip2 => write!(f, "BZIP2"),
|
||||||
|
CleanPath => write!(f, "CLEANPATH"),
|
||||||
Csv(opts) => {
|
Csv(opts) => {
|
||||||
write!(f, "CSV")?;
|
write!(f, "CSV")?;
|
||||||
if !opts.is_empty() {
|
if !opts.is_empty() {
|
||||||
|
@ -8838,9 +8907,37 @@ impl fmt::Display for CopyLegacyOption {
|
||||||
}
|
}
|
||||||
Delimiter(char) => write!(f, "DELIMITER '{char}'"),
|
Delimiter(char) => write!(f, "DELIMITER '{char}'"),
|
||||||
EmptyAsNull => write!(f, "EMPTYASNULL"),
|
EmptyAsNull => write!(f, "EMPTYASNULL"),
|
||||||
|
Encrypted { auto } => write!(f, "ENCRYPTED{}", if *auto { " AUTO" } else { "" }),
|
||||||
|
Escape => write!(f, "ESCAPE"),
|
||||||
|
Extension(ext) => write!(f, "EXTENSION '{}'", value::escape_single_quote_string(ext)),
|
||||||
|
FixedWidth(spec) => write!(
|
||||||
|
f,
|
||||||
|
"FIXEDWIDTH '{}'",
|
||||||
|
value::escape_single_quote_string(spec)
|
||||||
|
),
|
||||||
|
Gzip => write!(f, "GZIP"),
|
||||||
|
Header => write!(f, "HEADER"),
|
||||||
IamRole(role) => write!(f, "IAM_ROLE {role}"),
|
IamRole(role) => write!(f, "IAM_ROLE {role}"),
|
||||||
IgnoreHeader(num_rows) => write!(f, "IGNOREHEADER {num_rows}"),
|
IgnoreHeader(num_rows) => write!(f, "IGNOREHEADER {num_rows}"),
|
||||||
|
Json => write!(f, "JSON"),
|
||||||
|
Manifest { verbose } => write!(f, "MANIFEST{}", if *verbose { " VERBOSE" } else { "" }),
|
||||||
|
MaxFileSize(file_size) => write!(f, "MAXFILESIZE {file_size}"),
|
||||||
Null(string) => write!(f, "NULL '{}'", value::escape_single_quote_string(string)),
|
Null(string) => write!(f, "NULL '{}'", value::escape_single_quote_string(string)),
|
||||||
|
Parallel(enabled) => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"PARALLEL{}",
|
||||||
|
match enabled {
|
||||||
|
Some(true) => " TRUE",
|
||||||
|
Some(false) => " FALSE",
|
||||||
|
_ => "",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Parquet => write!(f, "PARQUET"),
|
||||||
|
PartitionBy(p) => write!(f, "{p}"),
|
||||||
|
Region(region) => write!(f, "REGION '{}'", value::escape_single_quote_string(region)),
|
||||||
|
RowGroupSize(file_size) => write!(f, "ROWGROUPSIZE {file_size}"),
|
||||||
TimeFormat(fmt) => {
|
TimeFormat(fmt) => {
|
||||||
write!(f, "TIMEFORMAT")?;
|
write!(f, "TIMEFORMAT")?;
|
||||||
if let Some(fmt) = fmt {
|
if let Some(fmt) = fmt {
|
||||||
|
@ -8849,10 +8946,73 @@ impl fmt::Display for CopyLegacyOption {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
TruncateColumns => write!(f, "TRUNCATECOLUMNS"),
|
TruncateColumns => write!(f, "TRUNCATECOLUMNS"),
|
||||||
|
Zstd => write!(f, "ZSTD"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ```sql
|
||||||
|
/// SIZE \[ MB | GB \]
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||||
|
pub struct FileSize {
|
||||||
|
pub size: Value,
|
||||||
|
pub unit: Option<FileSizeUnit>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for FileSize {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.size)?;
|
||||||
|
if let Some(unit) = &self.unit {
|
||||||
|
write!(f, " {unit}")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||||
|
pub enum FileSizeUnit {
|
||||||
|
MB,
|
||||||
|
GB,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for FileSizeUnit {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
FileSizeUnit::MB => write!(f, "MB"),
|
||||||
|
FileSizeUnit::GB => write!(f, "GB"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Specifies the partition keys for the unload operation
|
||||||
|
///
|
||||||
|
/// ```sql
|
||||||
|
/// PARTITION BY ( column_name [, ... ] ) [ INCLUDE ]
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||||
|
pub struct PartitionBy {
|
||||||
|
pub columns: Vec<Ident>,
|
||||||
|
pub include: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for PartitionBy {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"PARTITION BY ({}){}",
|
||||||
|
display_comma_separated(&self.columns),
|
||||||
|
if self.include { " INCLUDE" } else { "" }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An `IAM_ROLE` option in the AWS ecosystem
|
/// An `IAM_ROLE` option in the AWS ecosystem
|
||||||
///
|
///
|
||||||
/// [Redshift COPY](https://docs.aws.amazon.com/redshift/latest/dg/copy-parameters-authorization.html#copy-iam-role)
|
/// [Redshift COPY](https://docs.aws.amazon.com/redshift/latest/dg/copy-parameters-authorization.html#copy-iam-role)
|
||||||
|
|
|
@ -82,6 +82,7 @@ define_keywords!(
|
||||||
ACCOUNT,
|
ACCOUNT,
|
||||||
ACTION,
|
ACTION,
|
||||||
ADD,
|
ADD,
|
||||||
|
ADDQUOTES,
|
||||||
ADMIN,
|
ADMIN,
|
||||||
AFTER,
|
AFTER,
|
||||||
AGAINST,
|
AGAINST,
|
||||||
|
@ -92,6 +93,7 @@ define_keywords!(
|
||||||
ALIAS,
|
ALIAS,
|
||||||
ALL,
|
ALL,
|
||||||
ALLOCATE,
|
ALLOCATE,
|
||||||
|
ALLOWOVERWRITE,
|
||||||
ALTER,
|
ALTER,
|
||||||
ALWAYS,
|
ALWAYS,
|
||||||
ANALYZE,
|
ANALYZE,
|
||||||
|
@ -159,6 +161,7 @@ define_keywords!(
|
||||||
BYPASSRLS,
|
BYPASSRLS,
|
||||||
BYTEA,
|
BYTEA,
|
||||||
BYTES,
|
BYTES,
|
||||||
|
BZIP2,
|
||||||
CACHE,
|
CACHE,
|
||||||
CALL,
|
CALL,
|
||||||
CALLED,
|
CALLED,
|
||||||
|
@ -190,6 +193,7 @@ define_keywords!(
|
||||||
CHECK,
|
CHECK,
|
||||||
CHECKSUM,
|
CHECKSUM,
|
||||||
CIRCLE,
|
CIRCLE,
|
||||||
|
CLEANPATH,
|
||||||
CLEAR,
|
CLEAR,
|
||||||
CLOB,
|
CLOB,
|
||||||
CLONE,
|
CLONE,
|
||||||
|
@ -322,6 +326,7 @@ define_keywords!(
|
||||||
ENABLE,
|
ENABLE,
|
||||||
ENABLE_SCHEMA_EVOLUTION,
|
ENABLE_SCHEMA_EVOLUTION,
|
||||||
ENCODING,
|
ENCODING,
|
||||||
|
ENCRYPTED,
|
||||||
ENCRYPTION,
|
ENCRYPTION,
|
||||||
END,
|
END,
|
||||||
END_EXEC = "END-EXEC",
|
END_EXEC = "END-EXEC",
|
||||||
|
@ -380,6 +385,7 @@ define_keywords!(
|
||||||
FIRST,
|
FIRST,
|
||||||
FIRST_VALUE,
|
FIRST_VALUE,
|
||||||
FIXEDSTRING,
|
FIXEDSTRING,
|
||||||
|
FIXEDWIDTH,
|
||||||
FLATTEN,
|
FLATTEN,
|
||||||
FLOAT,
|
FLOAT,
|
||||||
FLOAT32,
|
FLOAT32,
|
||||||
|
@ -411,6 +417,7 @@ define_keywords!(
|
||||||
FUNCTIONS,
|
FUNCTIONS,
|
||||||
FUSION,
|
FUSION,
|
||||||
FUTURE,
|
FUTURE,
|
||||||
|
GB,
|
||||||
GENERAL,
|
GENERAL,
|
||||||
GENERATE,
|
GENERATE,
|
||||||
GENERATED,
|
GENERATED,
|
||||||
|
@ -426,6 +433,7 @@ define_keywords!(
|
||||||
GROUP,
|
GROUP,
|
||||||
GROUPING,
|
GROUPING,
|
||||||
GROUPS,
|
GROUPS,
|
||||||
|
GZIP,
|
||||||
HASH,
|
HASH,
|
||||||
HAVING,
|
HAVING,
|
||||||
HEADER,
|
HEADER,
|
||||||
|
@ -550,6 +558,7 @@ define_keywords!(
|
||||||
MANAGE,
|
MANAGE,
|
||||||
MANAGED,
|
MANAGED,
|
||||||
MANAGEDLOCATION,
|
MANAGEDLOCATION,
|
||||||
|
MANIFEST,
|
||||||
MAP,
|
MAP,
|
||||||
MASKING,
|
MASKING,
|
||||||
MATCH,
|
MATCH,
|
||||||
|
@ -560,9 +569,11 @@ define_keywords!(
|
||||||
MATERIALIZE,
|
MATERIALIZE,
|
||||||
MATERIALIZED,
|
MATERIALIZED,
|
||||||
MAX,
|
MAX,
|
||||||
|
MAXFILESIZE,
|
||||||
MAXVALUE,
|
MAXVALUE,
|
||||||
MAX_DATA_EXTENSION_TIME_IN_DAYS,
|
MAX_DATA_EXTENSION_TIME_IN_DAYS,
|
||||||
MAX_ROWS,
|
MAX_ROWS,
|
||||||
|
MB,
|
||||||
MEASURES,
|
MEASURES,
|
||||||
MEDIUMBLOB,
|
MEDIUMBLOB,
|
||||||
MEDIUMINT,
|
MEDIUMINT,
|
||||||
|
@ -761,6 +772,7 @@ define_keywords!(
|
||||||
REFRESH_MODE,
|
REFRESH_MODE,
|
||||||
REGCLASS,
|
REGCLASS,
|
||||||
REGEXP,
|
REGEXP,
|
||||||
|
REGION,
|
||||||
REGR_AVGX,
|
REGR_AVGX,
|
||||||
REGR_AVGY,
|
REGR_AVGY,
|
||||||
REGR_COUNT,
|
REGR_COUNT,
|
||||||
|
@ -813,6 +825,7 @@ define_keywords!(
|
||||||
ROLLUP,
|
ROLLUP,
|
||||||
ROOT,
|
ROOT,
|
||||||
ROW,
|
ROW,
|
||||||
|
ROWGROUPSIZE,
|
||||||
ROWID,
|
ROWID,
|
||||||
ROWS,
|
ROWS,
|
||||||
ROW_FORMAT,
|
ROW_FORMAT,
|
||||||
|
@ -1061,7 +1074,8 @@ define_keywords!(
|
||||||
YEAR,
|
YEAR,
|
||||||
YEARS,
|
YEARS,
|
||||||
ZONE,
|
ZONE,
|
||||||
ZORDER
|
ZORDER,
|
||||||
|
ZSTD
|
||||||
);
|
);
|
||||||
|
|
||||||
/// These keywords can't be used as a table alias, so that `FROM table_name alias`
|
/// These keywords can't be used as a table alias, so that `FROM table_name alias`
|
||||||
|
|
|
@ -630,7 +630,10 @@ impl<'a> Parser<'a> {
|
||||||
Keyword::NOTIFY if self.dialect.supports_listen_notify() => self.parse_notify(),
|
Keyword::NOTIFY if self.dialect.supports_listen_notify() => self.parse_notify(),
|
||||||
// `PRAGMA` is sqlite specific https://www.sqlite.org/pragma.html
|
// `PRAGMA` is sqlite specific https://www.sqlite.org/pragma.html
|
||||||
Keyword::PRAGMA => self.parse_pragma(),
|
Keyword::PRAGMA => self.parse_pragma(),
|
||||||
Keyword::UNLOAD => self.parse_unload(),
|
Keyword::UNLOAD => {
|
||||||
|
self.prev_token();
|
||||||
|
self.parse_unload()
|
||||||
|
}
|
||||||
Keyword::RENAME => self.parse_rename(),
|
Keyword::RENAME => self.parse_rename(),
|
||||||
// `INSTALL` is duckdb specific https://duckdb.org/docs/extensions/overview
|
// `INSTALL` is duckdb specific https://duckdb.org/docs/extensions/overview
|
||||||
Keyword::INSTALL if dialect_of!(self is DuckDbDialect | GenericDialect) => {
|
Keyword::INSTALL if dialect_of!(self is DuckDbDialect | GenericDialect) => {
|
||||||
|
@ -9610,17 +9613,36 @@ impl<'a> Parser<'a> {
|
||||||
let ret = match self.parse_one_of_keywords(&[
|
let ret = match self.parse_one_of_keywords(&[
|
||||||
Keyword::ACCEPTANYDATE,
|
Keyword::ACCEPTANYDATE,
|
||||||
Keyword::ACCEPTINVCHARS,
|
Keyword::ACCEPTINVCHARS,
|
||||||
|
Keyword::ADDQUOTES,
|
||||||
|
Keyword::ALLOWOVERWRITE,
|
||||||
Keyword::BINARY,
|
Keyword::BINARY,
|
||||||
Keyword::BLANKSASNULL,
|
Keyword::BLANKSASNULL,
|
||||||
|
Keyword::BZIP2,
|
||||||
|
Keyword::CLEANPATH,
|
||||||
Keyword::CSV,
|
Keyword::CSV,
|
||||||
Keyword::DATEFORMAT,
|
Keyword::DATEFORMAT,
|
||||||
Keyword::DELIMITER,
|
Keyword::DELIMITER,
|
||||||
Keyword::EMPTYASNULL,
|
Keyword::EMPTYASNULL,
|
||||||
|
Keyword::ENCRYPTED,
|
||||||
|
Keyword::ESCAPE,
|
||||||
|
Keyword::EXTENSION,
|
||||||
|
Keyword::FIXEDWIDTH,
|
||||||
|
Keyword::GZIP,
|
||||||
|
Keyword::HEADER,
|
||||||
Keyword::IAM_ROLE,
|
Keyword::IAM_ROLE,
|
||||||
Keyword::IGNOREHEADER,
|
Keyword::IGNOREHEADER,
|
||||||
|
Keyword::JSON,
|
||||||
|
Keyword::MANIFEST,
|
||||||
|
Keyword::MAXFILESIZE,
|
||||||
Keyword::NULL,
|
Keyword::NULL,
|
||||||
|
Keyword::PARALLEL,
|
||||||
|
Keyword::PARQUET,
|
||||||
|
Keyword::PARTITION,
|
||||||
|
Keyword::REGION,
|
||||||
|
Keyword::ROWGROUPSIZE,
|
||||||
Keyword::TIMEFORMAT,
|
Keyword::TIMEFORMAT,
|
||||||
Keyword::TRUNCATECOLUMNS,
|
Keyword::TRUNCATECOLUMNS,
|
||||||
|
Keyword::ZSTD,
|
||||||
]) {
|
]) {
|
||||||
Some(Keyword::ACCEPTANYDATE) => CopyLegacyOption::AcceptAnyDate,
|
Some(Keyword::ACCEPTANYDATE) => CopyLegacyOption::AcceptAnyDate,
|
||||||
Some(Keyword::ACCEPTINVCHARS) => {
|
Some(Keyword::ACCEPTINVCHARS) => {
|
||||||
|
@ -9632,8 +9654,12 @@ impl<'a> Parser<'a> {
|
||||||
};
|
};
|
||||||
CopyLegacyOption::AcceptInvChars(ch)
|
CopyLegacyOption::AcceptInvChars(ch)
|
||||||
}
|
}
|
||||||
|
Some(Keyword::ADDQUOTES) => CopyLegacyOption::AddQuotes,
|
||||||
|
Some(Keyword::ALLOWOVERWRITE) => CopyLegacyOption::AllowOverwrite,
|
||||||
Some(Keyword::BINARY) => CopyLegacyOption::Binary,
|
Some(Keyword::BINARY) => CopyLegacyOption::Binary,
|
||||||
Some(Keyword::BLANKSASNULL) => CopyLegacyOption::BlankAsNull,
|
Some(Keyword::BLANKSASNULL) => CopyLegacyOption::BlankAsNull,
|
||||||
|
Some(Keyword::BZIP2) => CopyLegacyOption::Bzip2,
|
||||||
|
Some(Keyword::CLEANPATH) => CopyLegacyOption::CleanPath,
|
||||||
Some(Keyword::CSV) => CopyLegacyOption::Csv({
|
Some(Keyword::CSV) => CopyLegacyOption::Csv({
|
||||||
let mut opts = vec![];
|
let mut opts = vec![];
|
||||||
while let Some(opt) =
|
while let Some(opt) =
|
||||||
|
@ -9657,16 +9683,81 @@ impl<'a> Parser<'a> {
|
||||||
CopyLegacyOption::Delimiter(self.parse_literal_char()?)
|
CopyLegacyOption::Delimiter(self.parse_literal_char()?)
|
||||||
}
|
}
|
||||||
Some(Keyword::EMPTYASNULL) => CopyLegacyOption::EmptyAsNull,
|
Some(Keyword::EMPTYASNULL) => CopyLegacyOption::EmptyAsNull,
|
||||||
|
Some(Keyword::ENCRYPTED) => {
|
||||||
|
let auto = self.parse_keyword(Keyword::AUTO);
|
||||||
|
CopyLegacyOption::Encrypted { auto }
|
||||||
|
}
|
||||||
|
Some(Keyword::ESCAPE) => CopyLegacyOption::Escape,
|
||||||
|
Some(Keyword::EXTENSION) => {
|
||||||
|
let ext = self.parse_literal_string()?;
|
||||||
|
CopyLegacyOption::Extension(ext)
|
||||||
|
}
|
||||||
|
Some(Keyword::FIXEDWIDTH) => {
|
||||||
|
let spec = self.parse_literal_string()?;
|
||||||
|
CopyLegacyOption::FixedWidth(spec)
|
||||||
|
}
|
||||||
|
Some(Keyword::GZIP) => CopyLegacyOption::Gzip,
|
||||||
|
Some(Keyword::HEADER) => CopyLegacyOption::Header,
|
||||||
Some(Keyword::IAM_ROLE) => CopyLegacyOption::IamRole(self.parse_iam_role_kind()?),
|
Some(Keyword::IAM_ROLE) => CopyLegacyOption::IamRole(self.parse_iam_role_kind()?),
|
||||||
Some(Keyword::IGNOREHEADER) => {
|
Some(Keyword::IGNOREHEADER) => {
|
||||||
let _ = self.parse_keyword(Keyword::AS);
|
let _ = self.parse_keyword(Keyword::AS);
|
||||||
let num_rows = self.parse_literal_uint()?;
|
let num_rows = self.parse_literal_uint()?;
|
||||||
CopyLegacyOption::IgnoreHeader(num_rows)
|
CopyLegacyOption::IgnoreHeader(num_rows)
|
||||||
}
|
}
|
||||||
|
Some(Keyword::JSON) => CopyLegacyOption::Json,
|
||||||
|
Some(Keyword::MANIFEST) => {
|
||||||
|
let verbose = self.parse_keyword(Keyword::VERBOSE);
|
||||||
|
CopyLegacyOption::Manifest { verbose }
|
||||||
|
}
|
||||||
|
Some(Keyword::MAXFILESIZE) => {
|
||||||
|
let _ = self.parse_keyword(Keyword::AS);
|
||||||
|
let size = self.parse_number_value()?.value;
|
||||||
|
let unit = match self.parse_one_of_keywords(&[Keyword::MB, Keyword::GB]) {
|
||||||
|
Some(Keyword::MB) => Some(FileSizeUnit::MB),
|
||||||
|
Some(Keyword::GB) => Some(FileSizeUnit::GB),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
CopyLegacyOption::MaxFileSize(FileSize { size, unit })
|
||||||
|
}
|
||||||
Some(Keyword::NULL) => {
|
Some(Keyword::NULL) => {
|
||||||
let _ = self.parse_keyword(Keyword::AS);
|
let _ = self.parse_keyword(Keyword::AS);
|
||||||
CopyLegacyOption::Null(self.parse_literal_string()?)
|
CopyLegacyOption::Null(self.parse_literal_string()?)
|
||||||
}
|
}
|
||||||
|
Some(Keyword::PARALLEL) => {
|
||||||
|
let enabled = match self.parse_one_of_keywords(&[
|
||||||
|
Keyword::TRUE,
|
||||||
|
Keyword::FALSE,
|
||||||
|
Keyword::ON,
|
||||||
|
Keyword::OFF,
|
||||||
|
]) {
|
||||||
|
Some(Keyword::TRUE) | Some(Keyword::ON) => Some(true),
|
||||||
|
Some(Keyword::FALSE) | Some(Keyword::OFF) => Some(false),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
CopyLegacyOption::Parallel(enabled)
|
||||||
|
}
|
||||||
|
Some(Keyword::PARQUET) => CopyLegacyOption::Parquet,
|
||||||
|
Some(Keyword::PARTITION) => {
|
||||||
|
self.expect_keyword(Keyword::BY)?;
|
||||||
|
let columns = self.parse_parenthesized_column_list(IsOptional::Mandatory, false)?;
|
||||||
|
let include = self.parse_keyword(Keyword::INCLUDE);
|
||||||
|
CopyLegacyOption::PartitionBy(PartitionBy { columns, include })
|
||||||
|
}
|
||||||
|
Some(Keyword::REGION) => {
|
||||||
|
let _ = self.parse_keyword(Keyword::AS);
|
||||||
|
let region = self.parse_literal_string()?;
|
||||||
|
CopyLegacyOption::Region(region)
|
||||||
|
}
|
||||||
|
Some(Keyword::ROWGROUPSIZE) => {
|
||||||
|
let _ = self.parse_keyword(Keyword::AS);
|
||||||
|
let size = self.parse_number_value()?.value;
|
||||||
|
let unit = match self.parse_one_of_keywords(&[Keyword::MB, Keyword::GB]) {
|
||||||
|
Some(Keyword::MB) => Some(FileSizeUnit::MB),
|
||||||
|
Some(Keyword::GB) => Some(FileSizeUnit::GB),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
CopyLegacyOption::RowGroupSize(FileSize { size, unit })
|
||||||
|
}
|
||||||
Some(Keyword::TIMEFORMAT) => {
|
Some(Keyword::TIMEFORMAT) => {
|
||||||
let _ = self.parse_keyword(Keyword::AS);
|
let _ = self.parse_keyword(Keyword::AS);
|
||||||
let fmt = if matches!(self.peek_token().token, Token::SingleQuotedString(_)) {
|
let fmt = if matches!(self.peek_token().token, Token::SingleQuotedString(_)) {
|
||||||
|
@ -9677,6 +9768,7 @@ impl<'a> Parser<'a> {
|
||||||
CopyLegacyOption::TimeFormat(fmt)
|
CopyLegacyOption::TimeFormat(fmt)
|
||||||
}
|
}
|
||||||
Some(Keyword::TRUNCATECOLUMNS) => CopyLegacyOption::TruncateColumns,
|
Some(Keyword::TRUNCATECOLUMNS) => CopyLegacyOption::TruncateColumns,
|
||||||
|
Some(Keyword::ZSTD) => CopyLegacyOption::Zstd,
|
||||||
_ => self.expected("option", self.peek_token())?,
|
_ => self.expected("option", self.peek_token())?,
|
||||||
};
|
};
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
|
@ -16477,19 +16569,35 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_unload(&mut self) -> Result<Statement, ParserError> {
|
pub fn parse_unload(&mut self) -> Result<Statement, ParserError> {
|
||||||
|
self.expect_keyword(Keyword::UNLOAD)?;
|
||||||
self.expect_token(&Token::LParen)?;
|
self.expect_token(&Token::LParen)?;
|
||||||
let query = self.parse_query()?;
|
let (query, query_text) = if matches!(self.peek_token().token, Token::SingleQuotedString(_))
|
||||||
|
{
|
||||||
|
(None, Some(self.parse_literal_string()?))
|
||||||
|
} else {
|
||||||
|
(Some(self.parse_query()?), None)
|
||||||
|
};
|
||||||
self.expect_token(&Token::RParen)?;
|
self.expect_token(&Token::RParen)?;
|
||||||
|
|
||||||
self.expect_keyword_is(Keyword::TO)?;
|
self.expect_keyword_is(Keyword::TO)?;
|
||||||
let to = self.parse_identifier()?;
|
let to = self.parse_identifier()?;
|
||||||
|
let auth = if self.parse_keyword(Keyword::IAM_ROLE) {
|
||||||
let with_options = self.parse_options(Keyword::WITH)?;
|
Some(self.parse_iam_role_kind()?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let with = self.parse_options(Keyword::WITH)?;
|
||||||
|
let mut options = vec![];
|
||||||
|
while let Some(opt) = self.maybe_parse(|parser| parser.parse_copy_legacy_option())? {
|
||||||
|
options.push(opt);
|
||||||
|
}
|
||||||
Ok(Statement::Unload {
|
Ok(Statement::Unload {
|
||||||
query,
|
query,
|
||||||
|
query_text,
|
||||||
to,
|
to,
|
||||||
with: with_options,
|
auth,
|
||||||
|
with,
|
||||||
|
options,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11887,7 +11887,7 @@ fn parse_unload() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unload,
|
unload,
|
||||||
Statement::Unload {
|
Statement::Unload {
|
||||||
query: Box::new(Query {
|
query: Some(Box::new(Query {
|
||||||
body: Box::new(SetExpr::Select(Box::new(Select {
|
body: Box::new(SetExpr::Select(Box::new(Select {
|
||||||
select_token: AttachedToken::empty(),
|
select_token: AttachedToken::empty(),
|
||||||
distinct: None,
|
distinct: None,
|
||||||
|
@ -11924,7 +11924,7 @@ fn parse_unload() {
|
||||||
settings: None,
|
settings: None,
|
||||||
format_clause: None,
|
format_clause: None,
|
||||||
pipe_operators: vec![],
|
pipe_operators: vec![],
|
||||||
}),
|
})),
|
||||||
to: Ident {
|
to: Ident {
|
||||||
value: "s3://...".to_string(),
|
value: "s3://...".to_string(),
|
||||||
quote_style: Some('\''),
|
quote_style: Some('\''),
|
||||||
|
@ -11939,9 +11939,120 @@ fn parse_unload() {
|
||||||
value: Expr::Value(
|
value: Expr::Value(
|
||||||
(Value::SingleQuotedString("AVRO".to_string())).with_empty_span()
|
(Value::SingleQuotedString("AVRO".to_string())).with_empty_span()
|
||||||
)
|
)
|
||||||
}]
|
}],
|
||||||
|
query_text: None,
|
||||||
|
auth: None,
|
||||||
|
options: vec![],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
one_statement_parses_to(
|
||||||
|
concat!(
|
||||||
|
"UNLOAD('SELECT 1') ",
|
||||||
|
"TO 's3://...' ",
|
||||||
|
"IAM_ROLE 'arn:aws:iam::123456789:role/role1' ",
|
||||||
|
"FORMAT AS CSV ",
|
||||||
|
"FORMAT AS PARQUET ",
|
||||||
|
"FORMAT AS JSON ",
|
||||||
|
"MAXFILESIZE AS 10 MB ",
|
||||||
|
"ROWGROUPSIZE AS 10 MB ",
|
||||||
|
"PARALLEL ON ",
|
||||||
|
"PARALLEL OFF ",
|
||||||
|
"REGION AS 'us-east-1'"
|
||||||
|
),
|
||||||
|
concat!(
|
||||||
|
"UNLOAD('SELECT 1') ",
|
||||||
|
"TO 's3://...' ",
|
||||||
|
"IAM_ROLE 'arn:aws:iam::123456789:role/role1' ",
|
||||||
|
"CSV ",
|
||||||
|
"PARQUET ",
|
||||||
|
"JSON ",
|
||||||
|
"MAXFILESIZE 10 MB ",
|
||||||
|
"ROWGROUPSIZE 10 MB ",
|
||||||
|
"PARALLEL TRUE ",
|
||||||
|
"PARALLEL FALSE ",
|
||||||
|
"REGION 'us-east-1'"
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
verified_stmt(concat!(
|
||||||
|
"UNLOAD('SELECT 1') ",
|
||||||
|
"TO 's3://...' ",
|
||||||
|
"IAM_ROLE 'arn:aws:iam::123456789:role/role1' ",
|
||||||
|
"PARTITION BY (c1, c2, c3)",
|
||||||
|
));
|
||||||
|
verified_stmt(concat!(
|
||||||
|
"UNLOAD('SELECT 1') ",
|
||||||
|
"TO 's3://...' ",
|
||||||
|
"IAM_ROLE 'arn:aws:iam::123456789:role/role1' ",
|
||||||
|
"PARTITION BY (c1, c2, c3) INCLUDE",
|
||||||
|
));
|
||||||
|
|
||||||
|
verified_stmt(concat!(
|
||||||
|
"UNLOAD('SELECT 1') ",
|
||||||
|
"TO 's3://...' ",
|
||||||
|
"IAM_ROLE 'arn:aws:iam::123456789:role/role1' ",
|
||||||
|
"PARTITION BY (c1, c2, c3) INCLUDE ",
|
||||||
|
"MANIFEST"
|
||||||
|
));
|
||||||
|
verified_stmt(concat!(
|
||||||
|
"UNLOAD('SELECT 1') ",
|
||||||
|
"TO 's3://...' ",
|
||||||
|
"IAM_ROLE 'arn:aws:iam::123456789:role/role1' ",
|
||||||
|
"PARTITION BY (c1, c2, c3) INCLUDE ",
|
||||||
|
"MANIFEST VERBOSE"
|
||||||
|
));
|
||||||
|
|
||||||
|
verified_stmt(concat!(
|
||||||
|
"UNLOAD('SELECT 1') ",
|
||||||
|
"TO 's3://...' ",
|
||||||
|
"IAM_ROLE 'arn:aws:iam::123456789:role/role1' ",
|
||||||
|
"PARTITION BY (c1, c2, c3) INCLUDE ",
|
||||||
|
"MANIFEST VERBOSE ",
|
||||||
|
"HEADER ",
|
||||||
|
"FIXEDWIDTH 'col1:1,col2:2' ",
|
||||||
|
"ENCRYPTED"
|
||||||
|
));
|
||||||
|
verified_stmt(concat!(
|
||||||
|
"UNLOAD('SELECT 1') ",
|
||||||
|
"TO 's3://...' ",
|
||||||
|
"IAM_ROLE 'arn:aws:iam::123456789:role/role1' ",
|
||||||
|
"PARTITION BY (c1, c2, c3) INCLUDE ",
|
||||||
|
"MANIFEST VERBOSE ",
|
||||||
|
"HEADER ",
|
||||||
|
"FIXEDWIDTH 'col1:1,col2:2' ",
|
||||||
|
"ENCRYPTED AUTO"
|
||||||
|
));
|
||||||
|
|
||||||
|
verified_stmt(concat!(
|
||||||
|
"UNLOAD('SELECT 1') ",
|
||||||
|
"TO 's3://...' ",
|
||||||
|
"IAM_ROLE 'arn:aws:iam::123456789:role/role1' ",
|
||||||
|
"PARTITION BY (c1, c2, c3) INCLUDE ",
|
||||||
|
"MANIFEST VERBOSE ",
|
||||||
|
"HEADER ",
|
||||||
|
"FIXEDWIDTH 'col1:1,col2:2' ",
|
||||||
|
"ENCRYPTED AUTO ",
|
||||||
|
"BZIP2 ",
|
||||||
|
"GZIP ",
|
||||||
|
"ZSTD ",
|
||||||
|
"ADDQUOTES ",
|
||||||
|
"NULL 'nil' ",
|
||||||
|
"ESCAPE ",
|
||||||
|
"ALLOWOVERWRITE ",
|
||||||
|
"CLEANPATH ",
|
||||||
|
"PARALLEL ",
|
||||||
|
"PARALLEL TRUE ",
|
||||||
|
"PARALLEL FALSE ",
|
||||||
|
"MAXFILESIZE 10 ",
|
||||||
|
"MAXFILESIZE 10 MB ",
|
||||||
|
"MAXFILESIZE 10 GB ",
|
||||||
|
"ROWGROUPSIZE 10 ",
|
||||||
|
"ROWGROUPSIZE 10 MB ",
|
||||||
|
"ROWGROUPSIZE 10 GB ",
|
||||||
|
"REGION 'us-east-1' ",
|
||||||
|
"EXTENSION 'ext1'"
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -16978,7 +17089,7 @@ fn test_parse_semantic_view_table_factor() {
|
||||||
|
|
||||||
for sql in invalid_sqls {
|
for sql in invalid_sqls {
|
||||||
let result = dialects.parse_sql_statements(sql);
|
let result = dialects.parse_sql_statements(sql);
|
||||||
assert!(result.is_err(), "Expected error for invalid SQL: {}", sql);
|
assert!(result.is_err(), "Expected error for invalid SQL: {sql}");
|
||||||
}
|
}
|
||||||
|
|
||||||
let ast_sql = r#"SELECT * FROM SEMANTIC_VIEW(
|
let ast_sql = r#"SELECT * FROM SEMANTIC_VIEW(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue