Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion src/ast/helpers/stmt_data_loading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use core::fmt::Formatter;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

use crate::ast::Ident;
use crate::ast::{Ident, ObjectName};
#[cfg(feature = "visitor")]
use sqlparser_derive::{Visit, VisitMut};

Expand Down Expand Up @@ -156,3 +156,22 @@ impl fmt::Display for StageLoadSelectItem {
Ok(())
}
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct FileStagingCommand {
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
pub stage: ObjectName,
pub pattern: Option<String>,
}

impl fmt::Display for FileStagingCommand {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.stage)?;
if let Some(pattern) = self.pattern.as_ref() {
write!(f, " PATTERN='{pattern}'")?;
}
Ok(())
}
}
10 changes: 9 additions & 1 deletion src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use alloc::{
string::{String, ToString},
vec::Vec,
};
use helpers::attached_token::AttachedToken;
use helpers::{attached_token::AttachedToken, stmt_data_loading::FileStagingCommand};

use core::ops::Deref;
use core::{
Expand Down Expand Up @@ -3420,6 +3420,12 @@ pub enum Statement {
///
/// See Mysql <https://dev.mysql.com/doc/refman/9.1/en/rename-table.html>
RenameTable(Vec<RenameTable>),
/// Snowflake `LIST`
/// See: <https://docs.snowflake.com/en/sql-reference/sql/list>
List(FileStagingCommand),
/// Snowflake `REMOVE`
/// See: <https://docs.snowflake.com/en/sql-reference/sql/remove>
Remove(FileStagingCommand),
}

impl fmt::Display for Statement {
Expand Down Expand Up @@ -4980,6 +4986,8 @@ impl fmt::Display for Statement {
Statement::RenameTable(rename_tables) => {
write!(f, "RENAME TABLE {}", display_comma_separated(rename_tables))
}
Statement::List(command) => write!(f, "LIST {command}"),
Statement::Remove(command) => write!(f, "REMOVE {command}"),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ impl Spanned for Statement {
Statement::LoadData { .. } => Span::empty(),
Statement::UNLISTEN { .. } => Span::empty(),
Statement::RenameTable { .. } => Span::empty(),
Statement::List(..) | Statement::Remove(..) => Span::empty(),
}
}
}
Expand Down
35 changes: 32 additions & 3 deletions src/dialect/snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
use crate::alloc::string::ToString;
use crate::ast::helpers::stmt_create_table::CreateTableBuilder;
use crate::ast::helpers::stmt_data_loading::{
DataLoadingOption, DataLoadingOptionType, DataLoadingOptions, StageLoadSelectItem,
StageParamsObject,
DataLoadingOption, DataLoadingOptionType, DataLoadingOptions, FileStagingCommand,
StageLoadSelectItem, StageParamsObject,
};
use crate::ast::{
ColumnOption, ColumnPolicy, ColumnPolicyProperty, Ident, IdentityParameters, IdentityProperty,
Expand Down Expand Up @@ -165,6 +165,15 @@ impl Dialect for SnowflakeDialect {
return Some(parse_copy_into(parser));
}

if let Some(kw) = parser.parse_one_of_keywords(&[
Keyword::LIST,
Keyword::LS,
Keyword::REMOVE,
Keyword::RM,
]) {
return Some(parse_file_staging_command(kw, parser));
}

None
}

Expand Down Expand Up @@ -240,6 +249,26 @@ impl Dialect for SnowflakeDialect {
}
}

fn parse_file_staging_command(kw: Keyword, parser: &mut Parser) -> Result<Statement, ParserError> {
let stage = parse_snowflake_stage_name(parser)?;
let pattern = if parser.parse_keyword(Keyword::PATTERN) {
parser.expect_token(&Token::Eq)?;
Some(parser.parse_literal_string()?)
} else {
None
};

match kw {
Keyword::LIST | Keyword::LS => Ok(Statement::List(FileStagingCommand { stage, pattern })),
Keyword::REMOVE | Keyword::RM => {
Ok(Statement::Remove(FileStagingCommand { stage, pattern }))
}
_ => Err(ParserError::ParserError(
"unexpected stage command, expecting LIST, LS, REMOVE or RM".to_string(),
)),
}
}

/// Parse snowflake create table statement.
/// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
pub fn parse_create_table(
Expand Down Expand Up @@ -501,7 +530,7 @@ pub fn parse_stage_name_identifier(parser: &mut Parser) -> Result<Ident, ParserE
Token::Tilde => ident.push('~'),
Token::Mod => ident.push('%'),
Token::Div => ident.push('/'),
Token::Word(w) => ident.push_str(&w.value),
Token::Word(w) => ident.push_str(&w.to_string()),
_ => return parser.expected("stage name identifier", parser.peek_token()),
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,7 @@ define_keywords!(
LIKE_REGEX,
LIMIT,
LINES,
LIST,
LISTEN,
LN,
LOAD,
Expand All @@ -467,6 +468,7 @@ define_keywords!(
LOWCARDINALITY,
LOWER,
LOW_PRIORITY,
LS,
MACRO,
MANAGEDLOCATION,
MAP,
Expand Down Expand Up @@ -648,6 +650,7 @@ define_keywords!(
RELAY,
RELEASE,
REMOTE,
REMOVE,
RENAME,
REORG,
REPAIR,
Expand All @@ -671,6 +674,7 @@ define_keywords!(
REVOKE,
RIGHT,
RLIKE,
RM,
ROLE,
ROLES,
ROLLBACK,
Expand Down
31 changes: 31 additions & 0 deletions tests/sqlparser_snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2983,3 +2983,34 @@ fn test_table_sample() {
snowflake_and_generic().verified_stmt("SELECT id FROM mytable TABLESAMPLE (10) REPEATABLE (1)");
snowflake_and_generic().verified_stmt("SELECT id FROM mytable TABLESAMPLE (10) SEED (1)");
}

#[test]
fn parse_ls_and_rm() {
snowflake().one_statement_parses_to("LS @~", "LIST @~");
snowflake().one_statement_parses_to("RM @~", "REMOVE @~");

let statement = snowflake()
.verified_stmt("LIST @SNOWFLAKE_KAFKA_CONNECTOR_externalDataLakeSnowflakeConnector_STAGE_call_tracker_stream/");
match statement {
Statement::List(command) => {
assert_eq!(command.stage, ObjectName(vec!["@SNOWFLAKE_KAFKA_CONNECTOR_externalDataLakeSnowflakeConnector_STAGE_call_tracker_stream/".into()]));
assert!(command.pattern.is_none());
}
_ => unreachable!(),
};

let statement =
snowflake().verified_stmt("REMOVE @my_csv_stage/analysis/ PATTERN='.*data_0.*'");
match statement {
Statement::Remove(command) => {
assert_eq!(
command.stage,
ObjectName(vec!["@my_csv_stage/analysis/".into()])
);
assert_eq!(command.pattern, Some(".*data_0.*".to_string()));
}
_ => unreachable!(),
};

snowflake().verified_stmt(r#"LIST @"STAGE_WITH_QUOTES""#);
}
Loading