Skip to content

Commit e06524c

Browse files
committed
Snowflake: support named file format lifecycle syntax
1 parent e7f3b0b commit e06524c

8 files changed

Lines changed: 274 additions & 5 deletions

File tree

‎src/ast/helpers/stmt_data_loading.rs‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,26 @@ pub enum AlterStageOperation {
8585
},
8686
}
8787

88+
/// An operation supported by Snowflake's `ALTER FILE FORMAT` statement.
89+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
90+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
91+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
92+
pub enum AlterFileFormatOperation {
93+
/// Rename a named file format.
94+
RenameTo {
95+
/// New file format name.
96+
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
97+
new_name: ObjectName,
98+
},
99+
/// Set one or more format-specific properties or the comment.
100+
Set {
101+
/// Format-specific options.
102+
options: KeyValueOptions,
103+
/// Optional comment replacement.
104+
comment: Option<String>,
105+
},
106+
}
107+
88108
/// This enum enables support for both standard SQL select item expressions
89109
/// and Snowflake-specific ones for data loading.
90110
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]

‎src/ast/mod.rs‎

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ use alloc::{
2626
};
2727
use helpers::{
2828
attached_token::AttachedToken,
29-
stmt_data_loading::{AlterStageOperation, FileStagingCommand, StageLoadSelectItemKind},
29+
stmt_data_loading::{
30+
AlterFileFormatOperation, AlterStageOperation, FileStagingCommand, StageLoadSelectItemKind,
31+
},
3032
};
3133

3234
use core::cmp::Ordering;
@@ -4244,6 +4246,15 @@ pub enum Statement {
42444246
show_options: ShowStatementOptions,
42454247
},
42464248
/// ```sql
4249+
/// SHOW FILE FORMATS [ LIKE '<pattern>' ] [ IN { ACCOUNT | DATABASE | SCHEMA } ]
4250+
/// ```
4251+
/// Snowflake-specific statement.
4252+
/// <https://docs.snowflake.com/en/sql-reference/sql/show-file-formats>
4253+
ShowFileFormats {
4254+
/// Additional options for filtering and scoping the file format listing.
4255+
show_options: ShowStatementOptions,
4256+
},
4257+
/// ```sql
42474258
/// SHOW VIEWS
42484259
/// ```
42494260
ShowViews {
@@ -4547,6 +4558,18 @@ pub enum Statement {
45474558
operation: AlterStageOperation,
45484559
},
45494560
/// ```sql
4561+
/// ALTER FILE FORMAT
4562+
/// ```
4563+
/// See <https://docs.snowflake.com/en/sql-reference/sql/alter-file-format>
4564+
AlterFileFormat {
4565+
/// `IF EXISTS` flag.
4566+
if_exists: bool,
4567+
/// File format name.
4568+
name: ObjectName,
4569+
/// File format alteration to perform.
4570+
operation: AlterFileFormatOperation,
4571+
},
4572+
/// ```sql
45504573
/// CREATE FILE FORMAT
45514574
/// ```
45524575
/// See <https://docs.snowflake.com/en/sql-reference/sql/create-file-format>
@@ -4685,6 +4708,18 @@ pub enum Statement {
46854708
stage_name: ObjectName,
46864709
},
46874710
/// ```sql
4711+
/// DESC[RIBE] FILE FORMAT <name>
4712+
/// ```
4713+
/// Snowflake-specific statement.
4714+
/// <https://docs.snowflake.com/en/sql-reference/sql/desc-file-format>
4715+
DescribeFileFormat {
4716+
/// `DESC | DESCRIBE` spelling used by the input statement.
4717+
describe_alias: DescribeAlias,
4718+
/// File format name.
4719+
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
4720+
name: ObjectName,
4721+
},
4722+
/// ```sql
46884723
/// [EXPLAIN | DESC | DESCRIBE] <statement>
46894724
/// ```
46904725
Explain {
@@ -5185,6 +5220,10 @@ impl fmt::Display for Statement {
51855220
describe_alias,
51865221
stage_name,
51875222
} => write!(f, "{describe_alias} STAGE {stage_name}"),
5223+
Statement::DescribeFileFormat {
5224+
describe_alias,
5225+
name,
5226+
} => write!(f, "{describe_alias} FILE FORMAT {name}"),
51885227
Statement::Explain {
51895228
describe_alias,
51905229
verbose,
@@ -5999,6 +6038,9 @@ impl fmt::Display for Statement {
59996038
Statement::ShowStages { show_options } => {
60006039
write!(f, "SHOW STAGES{show_options}")
60016040
}
6041+
Statement::ShowFileFormats { show_options } => {
6042+
write!(f, "SHOW FILE FORMATS{show_options}")
6043+
}
60026044
Statement::ShowViews {
60036045
terse,
60046046
materialized,
@@ -6356,6 +6398,32 @@ impl fmt::Display for Statement {
63566398
}
63576399
}
63586400
}
6401+
Statement::AlterFileFormat {
6402+
if_exists,
6403+
name,
6404+
operation,
6405+
} => {
6406+
write!(
6407+
f,
6408+
"ALTER FILE FORMAT {if_exists}{name}",
6409+
if_exists = if *if_exists { "IF EXISTS " } else { "" },
6410+
)?;
6411+
match operation {
6412+
AlterFileFormatOperation::RenameTo { new_name } => {
6413+
write!(f, " RENAME TO {new_name}")
6414+
}
6415+
AlterFileFormatOperation::Set { options, comment } => {
6416+
write!(f, " SET")?;
6417+
if !options.options.is_empty() {
6418+
write!(f, " {options}")?;
6419+
}
6420+
if let Some(comment) = comment {
6421+
write!(f, " COMMENT='{comment}'")?;
6422+
}
6423+
Ok(())
6424+
}
6425+
}
6426+
}
63596427
Statement::CreateFileFormat {
63606428
or_replace,
63616429
temporary,
@@ -8686,6 +8754,8 @@ pub enum ObjectType {
86868754
Sequence,
86878755
/// A stage.
86888756
Stage,
8757+
/// A named file format.
8758+
FileFormat,
86898759
/// A type definition.
86908760
Type,
86918761
/// A user.
@@ -8707,6 +8777,7 @@ impl fmt::Display for ObjectType {
87078777
ObjectType::Role => "ROLE",
87088778
ObjectType::Sequence => "SEQUENCE",
87098779
ObjectType::Stage => "STAGE",
8780+
ObjectType::FileFormat => "FILE FORMAT",
87108781
ObjectType::Type => "TYPE",
87118782
ObjectType::User => "USER",
87128783
ObjectType::Stream => "STREAM",

‎src/ast/spans.rs‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ impl Spanned for Values {
285285
/// - [Statement::ShowColumns]
286286
/// - [Statement::ShowTables]
287287
/// - [Statement::ShowStages]
288+
/// - [Statement::ShowFileFormats]
288289
/// - [Statement::ShowCollation]
289290
/// - [Statement::StartTransaction]
290291
/// - [Statement::Comment]
@@ -300,6 +301,7 @@ impl Spanned for Values {
300301
/// - [Statement::CreateMacro]
301302
/// - [Statement::CreateStage]
302303
/// - [Statement::AlterStage]
304+
/// - [Statement::AlterFileFormat]
303305
/// - [Statement::Assert]
304306
/// - [Statement::Grant]
305307
/// - [Statement::Revoke]
@@ -309,6 +311,7 @@ impl Spanned for Values {
309311
/// - [Statement::Kill]
310312
/// - [Statement::ExplainTable]
311313
/// - [Statement::DescribeStage]
314+
/// - [Statement::DescribeFileFormat]
312315
/// - [Statement::Explain]
313316
/// - [Statement::Savepoint]
314317
/// - [Statement::ReleaseSavepoint]
@@ -447,6 +450,7 @@ impl Spanned for Statement {
447450
Statement::ShowColumns { .. } => Span::empty(),
448451
Statement::ShowTables { .. } => Span::empty(),
449452
Statement::ShowStages { .. } => Span::empty(),
453+
Statement::ShowFileFormats { .. } => Span::empty(),
450454
Statement::ShowCollation { .. } => Span::empty(),
451455
Statement::ShowCharset { .. } => Span::empty(),
452456
Statement::Use(u) => u.span(),
@@ -465,6 +469,7 @@ impl Spanned for Statement {
465469
Statement::CreateMacro { .. } => Span::empty(),
466470
Statement::CreateStage { .. } => Span::empty(),
467471
Statement::AlterStage { .. } => Span::empty(),
472+
Statement::AlterFileFormat { .. } => Span::empty(),
468473
Statement::CreateFileFormat { .. } => Span::empty(),
469474
Statement::Assert { .. } => Span::empty(),
470475
Statement::Grant { .. } => Span::empty(),
@@ -476,6 +481,7 @@ impl Spanned for Statement {
476481
Statement::Kill { .. } => Span::empty(),
477482
Statement::ExplainTable { .. } => Span::empty(),
478483
Statement::DescribeStage { .. } => Span::empty(),
484+
Statement::DescribeFileFormat { .. } => Span::empty(),
479485
Statement::Explain { .. } => Span::empty(),
480486
Statement::Savepoint { .. } => Span::empty(),
481487
Statement::ReleaseSavepoint { .. } => Span::empty(),

‎src/dialect/mod.rs‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,6 +1257,12 @@ pub trait Dialect: Debug + Any {
12571257
false
12581258
}
12591259

1260+
/// Returns true if this dialect supports Snowflake-style named file format
1261+
/// lifecycle commands (`ALTER`, `DROP`, `SHOW`, and `DESCRIBE`).
1262+
fn supports_file_format_commands(&self) -> bool {
1263+
false
1264+
}
1265+
12601266
/// Returns true if this dialect supports the `COMMENT` statement
12611267
fn supports_comment_on(&self) -> bool {
12621268
false

‎src/dialect/snowflake.rs‎

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ use crate::ast::helpers::key_value_options::{
2424
use crate::ast::helpers::stmt_create_database::CreateDatabaseBuilder;
2525
use crate::ast::helpers::stmt_create_table::CreateTableBuilder;
2626
use crate::ast::helpers::stmt_data_loading::{
27-
AlterStageOperation, FileStagingCommand, StageLoadSelectItem, StageLoadSelectItemKind,
28-
StageParamsObject,
27+
AlterFileFormatOperation, AlterStageOperation, FileStagingCommand, StageLoadSelectItem,
28+
StageLoadSelectItemKind, StageParamsObject,
2929
};
3030
use crate::ast::{
3131
AlterTable, AlterTableOperation, AlterTableType, CatalogSyncNamespaceMode, CloudProviderParams,
@@ -303,6 +303,10 @@ impl Dialect for SnowflakeDialect {
303303
return Some(parse_alter_stage(parser));
304304
}
305305

306+
if parser.parse_keywords(&[Keyword::ALTER, Keyword::FILE, Keyword::FORMAT]) {
307+
return Some(parse_alter_file_format(parser));
308+
}
309+
306310
if parser.parse_keyword(Keyword::CREATE) {
307311
// possibly CREATE STAGE
308312
//[ OR REPLACE ]
@@ -492,6 +496,10 @@ impl Dialect for SnowflakeDialect {
492496
true
493497
}
494498

499+
fn supports_file_format_commands(&self) -> bool {
500+
true
501+
}
502+
495503
fn supports_left_associative_joins_without_parens(&self) -> bool {
496504
false
497505
}
@@ -1459,6 +1467,37 @@ fn parse_alter_stage(parser: &mut Parser) -> Result<Statement, ParserError> {
14591467
})
14601468
}
14611469

1470+
fn parse_alter_file_format(parser: &mut Parser) -> Result<Statement, ParserError> {
1471+
let if_exists = parser.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
1472+
let name = parser.parse_object_name(false)?;
1473+
let operation = if parser.parse_keyword(Keyword::RENAME) {
1474+
parser.expect_keyword(Keyword::TO)?;
1475+
AlterFileFormatOperation::RenameTo {
1476+
new_name: parser.parse_object_name(false)?,
1477+
}
1478+
} else if parser.parse_keyword(Keyword::SET) {
1479+
let options = parser.parse_key_value_options(false, &[Keyword::COMMENT])?;
1480+
let comment = if parser.parse_keyword(Keyword::COMMENT) {
1481+
parser.expect_token(&Token::Eq)?;
1482+
Some(parser.parse_comment_value()?)
1483+
} else {
1484+
None
1485+
};
1486+
if options.options.is_empty() && comment.is_none() {
1487+
return parser.expected_ref("a file format property", parser.peek_token_ref());
1488+
}
1489+
AlterFileFormatOperation::Set { options, comment }
1490+
} else {
1491+
return parser.expected_ref("RENAME TO or SET", parser.peek_token_ref());
1492+
};
1493+
1494+
Ok(Statement::AlterFileFormat {
1495+
if_exists,
1496+
name,
1497+
operation,
1498+
})
1499+
}
1500+
14621501
pub fn parse_create_file_format(
14631502
or_replace: bool,
14641503
temporary: bool,

‎src/keywords.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ define_keywords!(
448448
FORCE_QUOTE,
449449
FOREIGN,
450450
FORMAT,
451+
FORMATS,
451452
FORMATTED,
452453
FORWARD,
453454
FRAME_ROW,

‎src/parser/mod.rs‎

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7394,6 +7394,10 @@ impl<'a> Parser<'a> {
73947394
ObjectType::Sequence
73957395
} else if self.parse_keyword(Keyword::STAGE) {
73967396
ObjectType::Stage
7397+
} else if self.dialect.supports_file_format_commands()
7398+
&& self.parse_keywords(&[Keyword::FILE, Keyword::FORMAT])
7399+
{
7400+
ObjectType::FileFormat
73977401
} else if self.parse_keyword(Keyword::TYPE) {
73987402
ObjectType::Type
73997403
} else if self.parse_keyword(Keyword::USER) {
@@ -7427,7 +7431,7 @@ impl<'a> Parser<'a> {
74277431
};
74287432
} else {
74297433
return self.expected_ref(
7430-
"COLLATION, CONNECTOR, DATABASE, EXTENSION, FUNCTION, INDEX, OPERATOR, POLICY, PROCEDURE, ROLE, SCHEMA, SECRET, SEQUENCE, STAGE, TABLE, TRIGGER, TYPE, VIEW, MATERIALIZED VIEW or USER after DROP",
7434+
"COLLATION, CONNECTOR, DATABASE, EXTENSION, FILE FORMAT, FUNCTION, INDEX, OPERATOR, POLICY, PROCEDURE, ROLE, SCHEMA, SECRET, SEQUENCE, STAGE, TABLE, TRIGGER, TYPE, VIEW, MATERIALIZED VIEW or USER after DROP",
74317435
self.peek_token_ref(),
74327436
);
74337437
};
@@ -14030,6 +14034,16 @@ impl<'a> Parser<'a> {
1403014034
});
1403114035
}
1403214036

14037+
if describe_alias != DescribeAlias::Explain
14038+
&& self.dialect.supports_file_format_commands()
14039+
&& self.parse_keywords(&[Keyword::FILE, Keyword::FORMAT])
14040+
{
14041+
return Ok(Statement::DescribeFileFormat {
14042+
describe_alias,
14043+
name: self.parse_object_name(false)?,
14044+
});
14045+
}
14046+
1403314047
match self.maybe_parse(|parser| parser.parse_statement())? {
1403414048
Some(Statement::Explain { .. }) | Some(Statement::ExplainTable { .. }) => Err(
1403514049
ParserError::ParserError("Explain must be root of the plan".to_string()),
@@ -15540,6 +15554,18 @@ impl<'a> Parser<'a> {
1554015554
Ok(Statement::ShowStages {
1554115555
show_options: self.parse_show_stmt_options()?,
1554215556
})
15557+
} else if self.dialect.supports_file_format_commands()
15558+
&& self.parse_keywords(&[Keyword::FILE, Keyword::FORMATS])
15559+
{
15560+
if terse || extended || full || session || global || external {
15561+
Err(ParserError::ParserError(
15562+
"SHOW FILE FORMATS does not support SHOW modifiers".to_string(),
15563+
))
15564+
} else {
15565+
Ok(Statement::ShowFileFormats {
15566+
show_options: self.parse_show_stmt_options()?,
15567+
})
15568+
}
1554315569
} else if self.parse_keywords(&[Keyword::MATERIALIZED, Keyword::VIEWS]) {
1554415570
Ok(self.parse_show_views(terse, true)?)
1554515571
} else if self.parse_keyword(Keyword::VIEWS) {

0 commit comments

Comments
 (0)