diff --git a/src/ast/mod.rs b/src/ast/mod.rs index f4e2825dd..176d36545 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -8210,6 +8210,8 @@ pub enum CopyLegacyOption { Bzip2, /// CLEANPATH CleanPath, + /// COMPUPDATE [ PRESET | { ON | TRUE } | { OFF | FALSE } ] + CompUpdate { preset: bool, enabled: Option }, /// CSV ... Csv(Vec), /// DATEFORMAT \[ AS \] {'dateformat_string' | 'auto' } @@ -8250,8 +8252,12 @@ pub enum CopyLegacyOption { PartitionBy(UnloadPartitionBy), /// REGION \[ AS \] 'aws-region' } Region(String), + /// REMOVEQUOTES + RemoveQuotes, /// ROWGROUPSIZE \[ AS \] size \[ MB | GB \] RowGroupSize(FileSize), + /// STATUPDATE [ { ON | TRUE } | { OFF | FALSE } ] + StatUpdate(Option), /// TIMEFORMAT \[ AS \] {'timeformat_string' | 'auto' | 'epochsecs' | 'epochmillisecs' } TimeFormat(Option), /// TRUNCATECOLUMNS @@ -8278,6 +8284,22 @@ impl fmt::Display for CopyLegacyOption { BlankAsNull => write!(f, "BLANKSASNULL"), Bzip2 => write!(f, "BZIP2"), CleanPath => write!(f, "CLEANPATH"), + CompUpdate { preset, enabled } => { + write!(f, "COMPUPDATE")?; + if *preset { + write!(f, " PRESET")?; + } else if let Some(enabled) = enabled { + write!( + f, + "{}", + match enabled { + true => " TRUE", + false => " FALSE", + } + )?; + } + Ok(()) + } Csv(opts) => { write!(f, "CSV")?; if !opts.is_empty() { @@ -8324,7 +8346,19 @@ impl fmt::Display for CopyLegacyOption { Parquet => write!(f, "PARQUET"), PartitionBy(p) => write!(f, "{p}"), Region(region) => write!(f, "REGION '{}'", value::escape_single_quote_string(region)), + RemoveQuotes => write!(f, "REMOVEQUOTES"), RowGroupSize(file_size) => write!(f, "ROWGROUPSIZE {file_size}"), + StatUpdate(enabled) => { + write!( + f, + "STATUPDATE{}", + match enabled { + Some(true) => " TRUE", + Some(false) => " FALSE", + _ => "", + } + ) + } TimeFormat(fmt) => { write!(f, "TIMEFORMAT")?; if let Some(fmt) = fmt { diff --git a/src/keywords.rs b/src/keywords.rs index 35bf616d9..319c57827 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -215,6 +215,7 @@ define_keywords!( COMMITTED, COMPATIBLE, COMPRESSION, + COMPUPDATE, COMPUTE, CONCURRENTLY, CONDITION, @@ -749,6 +750,7 @@ define_keywords!( PRECISION, PREPARE, PRESERVE, + PRESET, PREWHERE, PRIMARY, PRINT, @@ -801,6 +803,7 @@ define_keywords!( RELEASES, REMOTE, REMOVE, + REMOVEQUOTES, RENAME, REORG, REPAIR, @@ -915,6 +918,7 @@ define_keywords!( STATS_AUTO_RECALC, STATS_PERSISTENT, STATS_SAMPLE_PAGES, + STATUPDATE, STATUS, STDDEV_POP, STDDEV_SAMP, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index e1dd8c0ec..b7d69f308 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -9741,6 +9741,7 @@ impl<'a> Parser<'a> { Keyword::BLANKSASNULL, Keyword::BZIP2, Keyword::CLEANPATH, + Keyword::COMPUPDATE, Keyword::CSV, Keyword::DATEFORMAT, Keyword::DELIMITER, @@ -9761,7 +9762,9 @@ impl<'a> Parser<'a> { Keyword::PARQUET, Keyword::PARTITION, Keyword::REGION, + Keyword::REMOVEQUOTES, Keyword::ROWGROUPSIZE, + Keyword::STATUPDATE, Keyword::TIMEFORMAT, Keyword::TRUNCATECOLUMNS, Keyword::ZSTD, @@ -9782,6 +9785,20 @@ impl<'a> Parser<'a> { Some(Keyword::BLANKSASNULL) => CopyLegacyOption::BlankAsNull, Some(Keyword::BZIP2) => CopyLegacyOption::Bzip2, Some(Keyword::CLEANPATH) => CopyLegacyOption::CleanPath, + Some(Keyword::COMPUPDATE) => { + let preset = self.parse_keyword(Keyword::PRESET); + 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::CompUpdate { preset, enabled } + } Some(Keyword::CSV) => CopyLegacyOption::Csv({ let mut opts = vec![]; while let Some(opt) = @@ -9870,11 +9887,25 @@ impl<'a> Parser<'a> { let region = self.parse_literal_string()?; CopyLegacyOption::Region(region) } + Some(Keyword::REMOVEQUOTES) => CopyLegacyOption::RemoveQuotes, Some(Keyword::ROWGROUPSIZE) => { let _ = self.parse_keyword(Keyword::AS); let file_size = self.parse_file_size()?; CopyLegacyOption::RowGroupSize(file_size) } + Some(Keyword::STATUPDATE) => { + 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::StatUpdate(enabled) + } Some(Keyword::TIMEFORMAT) => { let _ = self.parse_keyword(Keyword::AS); let fmt = if matches!(self.peek_token().token, Token::SingleQuotedString(_)) { diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 659b37ca6..99b7ac3fa 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -17126,7 +17126,19 @@ fn parse_copy_options() { "IAM_ROLE DEFAULT ", "IGNOREHEADER AS 1 ", "TIMEFORMAT AS 'auto' ", - "TRUNCATECOLUMNS", + "TRUNCATECOLUMNS ", + "REMOVEQUOTES ", + "COMPUPDATE ", + "COMPUPDATE PRESET ", + "COMPUPDATE ON ", + "COMPUPDATE OFF ", + "COMPUPDATE TRUE ", + "COMPUPDATE FALSE ", + "STATUPDATE ", + "STATUPDATE ON ", + "STATUPDATE OFF ", + "STATUPDATE TRUE ", + "STATUPDATE FALSE", ), concat!( "COPY dst (c1, c2, c3) FROM 's3://redshift-downloads/tickit/category_pipe.txt' ", @@ -17139,7 +17151,19 @@ fn parse_copy_options() { "IAM_ROLE DEFAULT ", "IGNOREHEADER 1 ", "TIMEFORMAT 'auto' ", - "TRUNCATECOLUMNS", + "TRUNCATECOLUMNS ", + "REMOVEQUOTES ", + "COMPUPDATE ", + "COMPUPDATE PRESET ", + "COMPUPDATE TRUE ", + "COMPUPDATE FALSE ", + "COMPUPDATE TRUE ", + "COMPUPDATE FALSE ", + "STATUPDATE ", + "STATUPDATE TRUE ", + "STATUPDATE FALSE ", + "STATUPDATE TRUE ", + "STATUPDATE FALSE", ), ); one_statement_parses_to(