Skip to content

Commit 6f60343

Browse files
committed
SGA-11419 Added snowflake ability for if not exists after create view, also added ability to write view name before if not exists in snowflake as it is implemented, replaced dialect of with trait functions
1 parent 23f40cd commit 6f60343

9 files changed

Lines changed: 102 additions & 7 deletions

File tree

src/ast/mod.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3263,6 +3263,8 @@ pub enum Statement {
32633263
materialized: bool,
32643264
/// View name
32653265
name: ObjectName,
3266+
// Name IF NOT EXIST instead of IF NOT EXIST name
3267+
name_before_not_exists: bool,
32663268
columns: Vec<ViewColumnDef>,
32673269
query: Box<Query>,
32683270
options: CreateTableOptions,
@@ -4962,6 +4964,7 @@ impl fmt::Display for Statement {
49624964
temporary,
49634965
to,
49644966
params,
4967+
name_before_not_exists,
49654968
} => {
49664969
write!(
49674970
f,
@@ -4974,11 +4977,18 @@ impl fmt::Display for Statement {
49744977
}
49754978
write!(
49764979
f,
4977-
"{materialized}{temporary}VIEW {if_not_exists}{name}{to}",
4980+
"{materialized}{temporary}VIEW {if_not_and_name}{to}",
4981+
if_not_and_name = if *if_not_exists {
4982+
if *name_before_not_exists {
4983+
format!("{name} IF NOT EXISTS")
4984+
} else {
4985+
format!("IF NOT EXISTS {name}")
4986+
}
4987+
} else {
4988+
format!("{name}")
4989+
},
49784990
materialized = if *materialized { "MATERIALIZED " } else { "" },
4979-
name = name,
49804991
temporary = if *temporary { "TEMPORARY " } else { "" },
4981-
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
49824992
to = to
49834993
.as_ref()
49844994
.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/dialect/bigquery.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ impl Dialect for BigQueryDialect {
108108
true
109109
}
110110

111+
// See https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#:~:text=CREATE%20%5B%20OR%20REPLACE%20%5D%20VIEW%20%5B%20IF%20NOT%20EXISTS%20%5D
112+
fn create_view_if_not_exists_supported(&self) -> bool {
113+
true
114+
}
115+
111116
fn require_interval_qualifier(&self) -> bool {
112117
true
113118
}

src/dialect/generic.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,14 @@ impl Dialect for GenericDialect {
8888
true
8989
}
9090

91+
fn create_view_if_not_exists_supported(&self) -> bool {
92+
true
93+
}
94+
95+
fn create_view_name_before_if_not_exists_supported(&self) -> bool {
96+
true
97+
}
98+
9199
fn support_map_literal_syntax(&self) -> bool {
92100
true
93101
}

src/dialect/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,18 @@ pub trait Dialect: Debug + Any {
247247
false
248248
}
249249

250+
/// Does the dialect support sql statements such as:
251+
/// CREATE VIEW IF NOT EXISTS view_name AS SELECT * FROM table_name
252+
fn create_view_if_not_exists_supported(&self) -> bool {
253+
false
254+
}
255+
256+
/// Does the dialect support view_name before IF NOT EXISTS in CREATE VIEW:
257+
/// CREATE VIEW IF NOT EXISTS view_name AS SELECT * FROM table_name
258+
fn create_view_name_before_if_not_exists_supported(&self) -> bool {
259+
false
260+
}
261+
250262
/// Returns true if the dialect supports referencing another named window
251263
/// within a window clause declaration.
252264
///

src/dialect/snowflake.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,16 @@ impl Dialect for SnowflakeDialect {
283283
true
284284
}
285285

286+
// See https://docs.snowflake.com/en/sql-reference/sql/create-view
287+
fn create_view_if_not_exists_supported(&self) -> bool {
288+
true
289+
}
290+
291+
// Snowflake allows table name before if not exists in CREATE VIEW
292+
fn create_view_name_before_if_not_exists_supported(&self) -> bool {
293+
true
294+
}
295+
286296
fn supports_left_associative_joins_without_parens(&self) -> bool {
287297
false
288298
}

src/dialect/sqlite.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ impl Dialect for SQLiteDialect {
5757
true
5858
}
5959

60+
// See https://www.sqlite.org/lang_createview.html
61+
fn create_view_if_not_exists_supported(&self) -> bool {
62+
true
63+
}
64+
6065
fn supports_start_transaction_modifier(&self) -> bool {
6166
true
6267
}

src/parser/mod.rs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5704,12 +5704,30 @@ impl<'a> Parser<'a> {
57045704
) -> Result<Statement, ParserError> {
57055705
let materialized = self.parse_keyword(Keyword::MATERIALIZED);
57065706
self.expect_keyword_is(Keyword::VIEW)?;
5707-
let if_not_exists = dialect_of!(self is BigQueryDialect|SQLiteDialect|GenericDialect)
5708-
&& self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
5707+
let allow_unquoted_hyphen = dialect_of!(self is BigQueryDialect);
5708+
let mut if_not_exists = false;
5709+
let name: ObjectName;
5710+
let mut name_before_not_exists = false;
5711+
if self.peek_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]) {
5712+
// Possible syntax -> ... IF NOT EXISTS <name>
5713+
if self.dialect.create_view_if_not_exists_supported() {
5714+
if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
5715+
}
5716+
name = self.parse_object_name(allow_unquoted_hyphen)?;
5717+
} else {
5718+
// Possible syntax -> ... <name> IF NOT EXISTS
5719+
name = self.parse_object_name(allow_unquoted_hyphen)?;
5720+
if self
5721+
.dialect
5722+
.create_view_name_before_if_not_exists_supported()
5723+
&& self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS])
5724+
{
5725+
if_not_exists = true;
5726+
name_before_not_exists = true;
5727+
}
5728+
}
57095729
// Many dialects support `OR ALTER` right after `CREATE`, but we don't (yet).
57105730
// ANSI SQL and Postgres support RECURSIVE here, but we don't support it either.
5711-
let allow_unquoted_hyphen = dialect_of!(self is BigQueryDialect);
5712-
let name = self.parse_object_name(allow_unquoted_hyphen)?;
57135731
let columns = self.parse_view_columns()?;
57145732
let mut options = CreateTableOptions::None;
57155733
let with_options = self.parse_options(Keyword::WITH)?;
@@ -5776,6 +5794,7 @@ impl<'a> Parser<'a> {
57765794
temporary,
57775795
to,
57785796
params: create_view_params,
5797+
name_before_not_exists,
57795798
})
57805799
}
57815800

tests/sqlparser_common.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7985,6 +7985,7 @@ fn parse_create_view() {
79857985
temporary,
79867986
to,
79877987
params,
7988+
name_before_not_exists: _,
79887989
} => {
79897990
assert_eq!(or_alter, false);
79907991
assert_eq!("myschema.myview", name.to_string());
@@ -8053,6 +8054,7 @@ fn parse_create_view_with_columns() {
80538054
temporary,
80548055
to,
80558056
params,
8057+
name_before_not_exists: _,
80568058
} => {
80578059
assert_eq!(or_alter, false);
80588060
assert_eq!("v", name.to_string());
@@ -8102,6 +8104,7 @@ fn parse_create_view_temporary() {
81028104
temporary,
81038105
to,
81048106
params,
8107+
name_before_not_exists: _,
81058108
} => {
81068109
assert_eq!(or_alter, false);
81078110
assert_eq!("myschema.myview", name.to_string());
@@ -8141,6 +8144,7 @@ fn parse_create_or_replace_view() {
81418144
temporary,
81428145
to,
81438146
params,
8147+
name_before_not_exists: _,
81448148
} => {
81458149
assert_eq!(or_alter, false);
81468150
assert_eq!("v", name.to_string());
@@ -8184,6 +8188,7 @@ fn parse_create_or_replace_materialized_view() {
81848188
temporary,
81858189
to,
81868190
params,
8191+
name_before_not_exists: _,
81878192
} => {
81888193
assert_eq!(or_alter, false);
81898194
assert_eq!("v", name.to_string());
@@ -8223,6 +8228,7 @@ fn parse_create_materialized_view() {
82238228
temporary,
82248229
to,
82258230
params,
8231+
name_before_not_exists: _,
82268232
} => {
82278233
assert_eq!(or_alter, false);
82288234
assert_eq!("myschema.myview", name.to_string());
@@ -8262,6 +8268,7 @@ fn parse_create_materialized_view_with_cluster_by() {
82628268
temporary,
82638269
to,
82648270
params,
8271+
name_before_not_exists: _,
82658272
} => {
82668273
assert_eq!(or_alter, false);
82678274
assert_eq!("myschema.myview", name.to_string());
@@ -16183,3 +16190,21 @@ fn test_identifier_unicode_start() {
1618316190
]);
1618416191
let _ = dialects.verified_stmt(sql);
1618516192
}
16193+
16194+
#[test]
16195+
fn parse_create_view_if_not_exists() {
16196+
let sql = "CREATE VIEW IF NOT EXISTS v AS SELECT 1";
16197+
let dialects = TestedDialects::new(vec![
16198+
Box::new(SnowflakeDialect {}),
16199+
Box::new(GenericDialect {}),
16200+
Box::new(SQLiteDialect {}),
16201+
Box::new(BigQueryDialect {}),
16202+
]);
16203+
let _ = dialects.verified_stmt(sql);
16204+
let sql = "CREATE VIEW v IF NOT EXISTS AS SELECT 1";
16205+
let dialects = TestedDialects::new(vec![
16206+
Box::new(SnowflakeDialect {}),
16207+
Box::new(GenericDialect {}),
16208+
]);
16209+
let _ = dialects.verified_stmt(sql);
16210+
}

0 commit comments

Comments
 (0)