Skip to content

Commit f80782a

Browse files
etgarperetsayman-sigma
authored andcommitted
Snowflake: Support CREATE VIEW myview IF NOT EXISTS (apache#1961)
1 parent 5011169 commit f80782a

4 files changed

Lines changed: 56 additions & 6 deletions

File tree

src/ast/mod.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3284,6 +3284,17 @@ pub enum Statement {
32843284
materialized: bool,
32853285
/// View name
32863286
name: ObjectName,
3287+
/// If `if_not_exists` is true, this flag is set to true if the view name comes before the `IF NOT EXISTS` clause.
3288+
/// Example:
3289+
/// ```sql
3290+
/// CREATE VIEW myview IF NOT EXISTS AS SELECT 1`
3291+
/// ```
3292+
/// Otherwise, the flag is set to false if the view name comes after the clause
3293+
/// Example:
3294+
/// ```sql
3295+
/// CREATE VIEW IF NOT EXISTS myview AS SELECT 1`
3296+
/// ```
3297+
name_before_not_exists: bool,
32873298
columns: Vec<ViewColumnDef>,
32883299
query: Box<Query>,
32893300
options: CreateTableOptions,
@@ -5023,6 +5034,7 @@ impl fmt::Display for Statement {
50235034
temporary,
50245035
to,
50255036
params,
5037+
name_before_not_exists,
50265038
} => {
50275039
write!(
50285040
f,
@@ -5035,11 +5047,18 @@ impl fmt::Display for Statement {
50355047
}
50365048
write!(
50375049
f,
5038-
"{materialized}{temporary}VIEW {if_not_exists}{name}{to}",
5050+
"{materialized}{temporary}VIEW {if_not_and_name}{to}",
5051+
if_not_and_name = if *if_not_exists {
5052+
if *name_before_not_exists {
5053+
format!("{name} IF NOT EXISTS")
5054+
} else {
5055+
format!("IF NOT EXISTS {name}")
5056+
}
5057+
} else {
5058+
format!("{name}")
5059+
},
50395060
materialized = if *materialized { "MATERIALIZED " } else { "" },
5040-
name = name,
50415061
temporary = if *temporary { "TEMPORARY " } else { "" },
5042-
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
50435062
to = to
50445063
.as_ref()
50455064
.map(|to| format!(" TO {to}"))

src/ast/spans.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ impl Spanned for Statement {
400400
if_not_exists: _,
401401
temporary: _,
402402
to,
403+
name_before_not_exists: _,
403404
params: _,
404405
} => union_spans(
405406
core::iter::once(name.span())

src/parser/mod.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5849,12 +5849,17 @@ impl<'a> Parser<'a> {
58495849
) -> Result<Statement, ParserError> {
58505850
let materialized = self.parse_keyword(Keyword::MATERIALIZED);
58515851
self.expect_keyword_is(Keyword::VIEW)?;
5852-
let if_not_exists = dialect_of!(self is BigQueryDialect|SQLiteDialect|GenericDialect)
5852+
let allow_unquoted_hyphen = dialect_of!(self is BigQueryDialect);
5853+
// Tries to parse IF NOT EXISTS either before name or after name
5854+
// Name before IF NOT EXISTS is supported by snowflake but undocumented
5855+
let if_not_exists_first =
5856+
self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
5857+
let name = self.parse_object_name(allow_unquoted_hyphen)?;
5858+
let name_before_not_exists = !if_not_exists_first
58535859
&& self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
5860+
let if_not_exists = if_not_exists_first || name_before_not_exists;
58545861
// Many dialects support `OR ALTER` right after `CREATE`, but we don't (yet).
58555862
// ANSI SQL and Postgres support RECURSIVE here, but we don't support it either.
5856-
let allow_unquoted_hyphen = dialect_of!(self is BigQueryDialect);
5857-
let name = self.parse_object_name(allow_unquoted_hyphen)?;
58585863
let columns = self.parse_view_columns()?;
58595864
let mut options = CreateTableOptions::None;
58605865
let with_options = self.parse_options(Keyword::WITH)?;
@@ -5921,6 +5926,7 @@ impl<'a> Parser<'a> {
59215926
temporary,
59225927
to,
59235928
params: create_view_params,
5929+
name_before_not_exists,
59245930
})
59255931
}
59265932

tests/sqlparser_common.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8059,6 +8059,7 @@ fn parse_create_view() {
80598059
temporary,
80608060
to,
80618061
params,
8062+
name_before_not_exists: _,
80628063
} => {
80638064
assert_eq!(or_alter, false);
80648065
assert_eq!("myschema.myview", name.to_string());
@@ -8127,6 +8128,7 @@ fn parse_create_view_with_columns() {
81278128
temporary,
81288129
to,
81298130
params,
8131+
name_before_not_exists: _,
81308132
} => {
81318133
assert_eq!(or_alter, false);
81328134
assert_eq!("v", name.to_string());
@@ -8176,6 +8178,7 @@ fn parse_create_view_temporary() {
81768178
temporary,
81778179
to,
81788180
params,
8181+
name_before_not_exists: _,
81798182
} => {
81808183
assert_eq!(or_alter, false);
81818184
assert_eq!("myschema.myview", name.to_string());
@@ -8215,6 +8218,7 @@ fn parse_create_or_replace_view() {
82158218
temporary,
82168219
to,
82178220
params,
8221+
name_before_not_exists: _,
82188222
} => {
82198223
assert_eq!(or_alter, false);
82208224
assert_eq!("v", name.to_string());
@@ -8258,6 +8262,7 @@ fn parse_create_or_replace_materialized_view() {
82588262
temporary,
82598263
to,
82608264
params,
8265+
name_before_not_exists: _,
82618266
} => {
82628267
assert_eq!(or_alter, false);
82638268
assert_eq!("v", name.to_string());
@@ -8297,6 +8302,7 @@ fn parse_create_materialized_view() {
82978302
temporary,
82988303
to,
82998304
params,
8305+
name_before_not_exists: _,
83008306
} => {
83018307
assert_eq!(or_alter, false);
83028308
assert_eq!("myschema.myview", name.to_string());
@@ -8336,6 +8342,7 @@ fn parse_create_materialized_view_with_cluster_by() {
83368342
temporary,
83378343
to,
83388344
params,
8345+
name_before_not_exists: _,
83398346
} => {
83408347
assert_eq!(or_alter, false);
83418348
assert_eq!("myschema.myview", name.to_string());
@@ -16453,3 +16460,20 @@ fn parse_drop_stream() {
1645316460
}
1645416461
verified_stmt("DROP STREAM IF EXISTS s1");
1645516462
}
16463+
16464+
#[test]
16465+
fn parse_create_view_if_not_exists() {
16466+
// Name after IF NOT EXISTS
16467+
let sql: &'static str = "CREATE VIEW IF NOT EXISTS v AS SELECT 1";
16468+
let _ = all_dialects().verified_stmt(sql);
16469+
// Name before IF NOT EXISTS
16470+
let sql = "CREATE VIEW v IF NOT EXISTS AS SELECT 1";
16471+
let _ = all_dialects().verified_stmt(sql);
16472+
// Name missing from query
16473+
let sql = "CREATE VIEW IF NOT EXISTS AS SELECT 1";
16474+
let res = all_dialects().parse_sql_statements(sql);
16475+
assert_eq!(
16476+
ParserError::ParserError("Expected: AS, found: SELECT".to_string()),
16477+
res.unwrap_err()
16478+
);
16479+
}

0 commit comments

Comments
 (0)