Add SECURE keyword for views in Snowflake (#2004)

This commit is contained in:
Denys Tsomenko 2025-08-26 22:20:13 +03:00 committed by GitHub
parent 779dcf91f6
commit cffff30961
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 49 additions and 3 deletions

View file

@ -3256,6 +3256,9 @@ pub enum Statement {
or_alter: bool, or_alter: bool,
or_replace: bool, or_replace: bool,
materialized: bool, materialized: bool,
/// Snowflake: SECURE view modifier
/// <https://docs.snowflake.com/en/sql-reference/sql/create-view#syntax>
secure: bool,
/// View name /// View name
name: ObjectName, name: ObjectName,
/// If `if_not_exists` is true, this flag is set to true if the view name comes before the `IF NOT EXISTS` clause. /// If `if_not_exists` is true, this flag is set to true if the view name comes before the `IF NOT EXISTS` clause.
@ -5118,6 +5121,7 @@ impl fmt::Display for Statement {
columns, columns,
query, query,
materialized, materialized,
secure,
options, options,
cluster_by, cluster_by,
comment, comment,
@ -5139,7 +5143,7 @@ impl fmt::Display for Statement {
} }
write!( write!(
f, f,
"{materialized}{temporary}VIEW {if_not_and_name}{to}", "{secure}{materialized}{temporary}VIEW {if_not_and_name}{to}",
if_not_and_name = if *if_not_exists { if_not_and_name = if *if_not_exists {
if *name_before_not_exists { if *name_before_not_exists {
format!("{name} IF NOT EXISTS") format!("{name} IF NOT EXISTS")
@ -5149,6 +5153,7 @@ impl fmt::Display for Statement {
} else { } else {
format!("{name}") format!("{name}")
}, },
secure = if *secure { "SECURE " } else { "" },
materialized = if *materialized { "MATERIALIZED " } else { "" }, materialized = if *materialized { "MATERIALIZED " } else { "" },
temporary = if *temporary { "TEMPORARY " } else { "" }, temporary = if *temporary { "TEMPORARY " } else { "" },
to = to to = to

View file

@ -395,6 +395,7 @@ impl Spanned for Statement {
or_alter: _, or_alter: _,
or_replace: _, or_replace: _,
materialized: _, materialized: _,
secure: _,
name, name,
columns, columns,
query, query,

View file

@ -833,6 +833,7 @@ define_keywords!(
SECONDARY_ENGINE_ATTRIBUTE, SECONDARY_ENGINE_ATTRIBUTE,
SECONDS, SECONDS,
SECRET, SECRET,
SECURE,
SECURITY, SECURITY,
SEED, SEED,
SELECT, SELECT,

View file

@ -4732,8 +4732,11 @@ impl<'a> Parser<'a> {
let create_view_params = self.parse_create_view_params()?; let create_view_params = self.parse_create_view_params()?;
if self.parse_keyword(Keyword::TABLE) { if self.parse_keyword(Keyword::TABLE) {
self.parse_create_table(or_replace, temporary, global, transient) self.parse_create_table(or_replace, temporary, global, transient)
} else if self.parse_keyword(Keyword::MATERIALIZED) || self.parse_keyword(Keyword::VIEW) { } else if self.peek_keyword(Keyword::MATERIALIZED)
self.prev_token(); || self.peek_keyword(Keyword::VIEW)
|| self.peek_keywords(&[Keyword::SECURE, Keyword::MATERIALIZED, Keyword::VIEW])
|| self.peek_keywords(&[Keyword::SECURE, Keyword::VIEW])
{
self.parse_create_view(or_alter, or_replace, temporary, create_view_params) self.parse_create_view(or_alter, or_replace, temporary, create_view_params)
} else if self.parse_keyword(Keyword::POLICY) { } else if self.parse_keyword(Keyword::POLICY) {
self.parse_create_policy() self.parse_create_policy()
@ -5853,6 +5856,7 @@ impl<'a> Parser<'a> {
temporary: bool, temporary: bool,
create_view_params: Option<CreateViewParams>, create_view_params: Option<CreateViewParams>,
) -> Result<Statement, ParserError> { ) -> Result<Statement, ParserError> {
let secure = self.parse_keyword(Keyword::SECURE);
let materialized = self.parse_keyword(Keyword::MATERIALIZED); let materialized = self.parse_keyword(Keyword::MATERIALIZED);
self.expect_keyword_is(Keyword::VIEW)?; self.expect_keyword_is(Keyword::VIEW)?;
let allow_unquoted_hyphen = dialect_of!(self is BigQueryDialect); let allow_unquoted_hyphen = dialect_of!(self is BigQueryDialect);
@ -5923,6 +5927,7 @@ impl<'a> Parser<'a> {
columns, columns,
query, query,
materialized, materialized,
secure,
or_replace, or_replace,
options, options,
cluster_by, cluster_by,

View file

@ -8078,6 +8078,7 @@ fn parse_create_view() {
to, to,
params, params,
name_before_not_exists: _, name_before_not_exists: _,
secure: _,
} => { } => {
assert_eq!(or_alter, false); assert_eq!(or_alter, false);
assert_eq!("myschema.myview", name.to_string()); assert_eq!("myschema.myview", name.to_string());
@ -8147,6 +8148,7 @@ fn parse_create_view_with_columns() {
to, to,
params, params,
name_before_not_exists: _, name_before_not_exists: _,
secure: _,
} => { } => {
assert_eq!(or_alter, false); assert_eq!(or_alter, false);
assert_eq!("v", name.to_string()); assert_eq!("v", name.to_string());
@ -8197,6 +8199,7 @@ fn parse_create_view_temporary() {
to, to,
params, params,
name_before_not_exists: _, name_before_not_exists: _,
secure: _,
} => { } => {
assert_eq!(or_alter, false); assert_eq!(or_alter, false);
assert_eq!("myschema.myview", name.to_string()); assert_eq!("myschema.myview", name.to_string());
@ -8237,6 +8240,7 @@ fn parse_create_or_replace_view() {
to, to,
params, params,
name_before_not_exists: _, name_before_not_exists: _,
secure: _,
} => { } => {
assert_eq!(or_alter, false); assert_eq!(or_alter, false);
assert_eq!("v", name.to_string()); assert_eq!("v", name.to_string());
@ -8281,6 +8285,7 @@ fn parse_create_or_replace_materialized_view() {
to, to,
params, params,
name_before_not_exists: _, name_before_not_exists: _,
secure: _,
} => { } => {
assert_eq!(or_alter, false); assert_eq!(or_alter, false);
assert_eq!("v", name.to_string()); assert_eq!("v", name.to_string());
@ -8321,6 +8326,7 @@ fn parse_create_materialized_view() {
to, to,
params, params,
name_before_not_exists: _, name_before_not_exists: _,
secure: _,
} => { } => {
assert_eq!(or_alter, false); assert_eq!(or_alter, false);
assert_eq!("myschema.myview", name.to_string()); assert_eq!("myschema.myview", name.to_string());
@ -8361,6 +8367,7 @@ fn parse_create_materialized_view_with_cluster_by() {
to, to,
params, params,
name_before_not_exists: _, name_before_not_exists: _,
secure: _,
} => { } => {
assert_eq!(or_alter, false); assert_eq!(or_alter, false);
assert_eq!("myschema.myview", name.to_string()); assert_eq!("myschema.myview", name.to_string());

View file

@ -44,6 +44,33 @@ fn test_snowflake_create_table() {
} }
} }
#[test]
fn parse_sf_create_secure_view_and_materialized_view() {
for sql in [
"CREATE SECURE VIEW v AS SELECT 1",
"CREATE SECURE MATERIALIZED VIEW v AS SELECT 1",
"CREATE OR REPLACE SECURE VIEW v AS SELECT 1",
"CREATE OR REPLACE SECURE MATERIALIZED VIEW v AS SELECT 1",
] {
match snowflake().verified_stmt(sql) {
Statement::CreateView {
secure,
materialized,
..
} => {
assert!(secure);
if sql.contains("MATERIALIZED") {
assert!(materialized);
} else {
assert!(!materialized);
}
}
_ => unreachable!(),
}
assert_eq!(snowflake().verified_stmt(sql).to_string(), sql);
}
}
#[test] #[test]
fn test_snowflake_create_or_replace_table() { fn test_snowflake_create_or_replace_table() {
let sql = "CREATE OR REPLACE TABLE my_table (a number)"; let sql = "CREATE OR REPLACE TABLE my_table (a number)";