Skip to content

Commit 9127370

Browse files
authored
Snowflake: Support CREATE VIEW myview IF NOT EXISTS (#1961)
1 parent 15d8bfe commit 9127370

File tree

4 files changed

+56
-6
lines changed

4 files changed

+56
-6
lines changed

src/ast/mod.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3255,6 +3255,17 @@ pub enum Statement {
32553255
materialized: bool,
32563256
/// View name
32573257
name: ObjectName,
3258+
/// If `if_not_exists` is true, this flag is set to true if the view name comes before the `IF NOT EXISTS` clause.
3259+
/// Example:
3260+
/// ```sql
3261+
/// CREATE VIEW myview IF NOT EXISTS AS SELECT 1`
3262+
/// ```
3263+
/// Otherwise, the flag is set to false if the view name comes after the clause
3264+
/// Example:
3265+
/// ```sql
3266+
/// CREATE VIEW IF NOT EXISTS myview AS SELECT 1`
3267+
/// ```
3268+
name_before_not_exists: bool,
32583269
columns: Vec<ViewColumnDef>,
32593270
query: Box<Query>,
32603271
options: CreateTableOptions,
@@ -4994,6 +5005,7 @@ impl fmt::Display for Statement {
49945005
temporary,
49955006
to,
49965007
params,
5008+
name_before_not_exists,
49975009
} => {
49985010
write!(
49995011
f,
@@ -5006,11 +5018,18 @@ impl fmt::Display for Statement {
50065018
}
50075019
write!(
50085020
f,
5009-
"{materialized}{temporary}VIEW {if_not_exists}{name}{to}",
5021+
"{materialized}{temporary}VIEW {if_not_and_name}{to}",
5022+
if_not_and_name = if *if_not_exists {
5023+
if *name_before_not_exists {
5024+
format!("{name} IF NOT EXISTS")
5025+
} else {
5026+
format!("IF NOT EXISTS {name}")
5027+
}
5028+
} else {
5029+
format!("{name}")
5030+
},
50105031
materialized = if *materialized { "MATERIALIZED " } else { "" },
5011-
name = name,
50125032
temporary = if *temporary { "TEMPORARY " } else { "" },
5013-
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
50145033
to = to
50155034
.as_ref()
50165035
.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
@@ -5818,12 +5818,17 @@ impl<'a> Parser<'a> {
58185818
) -> Result<Statement, ParserError> {
58195819
let materialized = self.parse_keyword(Keyword::MATERIALIZED);
58205820
self.expect_keyword_is(Keyword::VIEW)?;
5821-
let if_not_exists = dialect_of!(self is BigQueryDialect|SQLiteDialect|GenericDialect)
5821+
let allow_unquoted_hyphen = dialect_of!(self is BigQueryDialect);
5822+
// Tries to parse IF NOT EXISTS either before name or after name
5823+
// Name before IF NOT EXISTS is supported by snowflake but undocumented
5824+
let if_not_exists_first =
5825+
self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
5826+
let name = self.parse_object_name(allow_unquoted_hyphen)?;
5827+
let name_before_not_exists = !if_not_exists_first
58225828
&& self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
5829+
let if_not_exists = if_not_exists_first || name_before_not_exists;
58235830
// Many dialects support `OR ALTER` right after `CREATE`, but we don't (yet).
58245831
// ANSI SQL and Postgres support RECURSIVE here, but we don't support it either.
5825-
let allow_unquoted_hyphen = dialect_of!(self is BigQueryDialect);
5826-
let name = self.parse_object_name(allow_unquoted_hyphen)?;
58275832
let columns = self.parse_view_columns()?;
58285833
let mut options = CreateTableOptions::None;
58295834
let with_options = self.parse_options(Keyword::WITH)?;
@@ -5890,6 +5895,7 @@ impl<'a> Parser<'a> {
58905895
temporary,
58915896
to,
58925897
params: create_view_params,
5898+
name_before_not_exists,
58935899
})
58945900
}
58955901

tests/sqlparser_common.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8058,6 +8058,7 @@ fn parse_create_view() {
80588058
temporary,
80598059
to,
80608060
params,
8061+
name_before_not_exists: _,
80618062
} => {
80628063
assert_eq!(or_alter, false);
80638064
assert_eq!("myschema.myview", name.to_string());
@@ -8126,6 +8127,7 @@ fn parse_create_view_with_columns() {
81268127
temporary,
81278128
to,
81288129
params,
8130+
name_before_not_exists: _,
81298131
} => {
81308132
assert_eq!(or_alter, false);
81318133
assert_eq!("v", name.to_string());
@@ -8175,6 +8177,7 @@ fn parse_create_view_temporary() {
81758177
temporary,
81768178
to,
81778179
params,
8180+
name_before_not_exists: _,
81788181
} => {
81798182
assert_eq!(or_alter, false);
81808183
assert_eq!("myschema.myview", name.to_string());
@@ -8214,6 +8217,7 @@ fn parse_create_or_replace_view() {
82148217
temporary,
82158218
to,
82168219
params,
8220+
name_before_not_exists: _,
82178221
} => {
82188222
assert_eq!(or_alter, false);
82198223
assert_eq!("v", name.to_string());
@@ -8257,6 +8261,7 @@ fn parse_create_or_replace_materialized_view() {
82578261
temporary,
82588262
to,
82598263
params,
8264+
name_before_not_exists: _,
82608265
} => {
82618266
assert_eq!(or_alter, false);
82628267
assert_eq!("v", name.to_string());
@@ -8296,6 +8301,7 @@ fn parse_create_materialized_view() {
82968301
temporary,
82978302
to,
82988303
params,
8304+
name_before_not_exists: _,
82998305
} => {
83008306
assert_eq!(or_alter, false);
83018307
assert_eq!("myschema.myview", name.to_string());
@@ -8335,6 +8341,7 @@ fn parse_create_materialized_view_with_cluster_by() {
83358341
temporary,
83368342
to,
83378343
params,
8344+
name_before_not_exists: _,
83388345
} => {
83398346
assert_eq!(or_alter, false);
83408347
assert_eq!("myschema.myview", name.to_string());
@@ -16427,3 +16434,20 @@ fn parse_drop_stream() {
1642716434
}
1642816435
verified_stmt("DROP STREAM IF EXISTS s1");
1642916436
}
16437+
16438+
#[test]
16439+
fn parse_create_view_if_not_exists() {
16440+
// Name after IF NOT EXISTS
16441+
let sql: &'static str = "CREATE VIEW IF NOT EXISTS v AS SELECT 1";
16442+
let _ = all_dialects().verified_stmt(sql);
16443+
// Name before IF NOT EXISTS
16444+
let sql = "CREATE VIEW v IF NOT EXISTS AS SELECT 1";
16445+
let _ = all_dialects().verified_stmt(sql);
16446+
// Name missing from query
16447+
let sql = "CREATE VIEW IF NOT EXISTS AS SELECT 1";
16448+
let res = all_dialects().parse_sql_statements(sql);
16449+
assert_eq!(
16450+
ParserError::ParserError("Expected: AS, found: SELECT".to_string()),
16451+
res.unwrap_err()
16452+
);
16453+
}

0 commit comments

Comments
 (0)