Skip to content

Commit 26f2680

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 5ec953b commit 26f2680

File tree

9 files changed

+102
-7
lines changed

9 files changed

+102
-7
lines changed

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,
@@ -4987,6 +4989,7 @@ impl fmt::Display for Statement {
49874989
temporary,
49884990
to,
49894991
params,
4992+
name_before_not_exists,
49904993
} => {
49914994
write!(
49924995
f,
@@ -4999,11 +5002,18 @@ impl fmt::Display for Statement {
49995002
}
50005003
write!(
50015004
f,
5002-
"{materialized}{temporary}VIEW {if_not_exists}{name}{to}",
5005+
"{materialized}{temporary}VIEW {if_not_and_name}{to}",
5006+
if_not_and_name = if *if_not_exists {
5007+
if *name_before_not_exists {
5008+
format!("{name} IF NOT EXISTS")
5009+
} else {
5010+
format!("IF NOT EXISTS {name}")
5011+
}
5012+
} else {
5013+
format!("{name}")
5014+
},
50035015
materialized = if *materialized { "MATERIALIZED " } else { "" },
5004-
name = name,
50055016
temporary = if *temporary { "TEMPORARY " } else { "" },
5006-
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
50075017
to = to
50085018
.as_ref()
50095019
.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
@@ -116,6 +116,11 @@ impl Dialect for BigQueryDialect {
116116
true
117117
}
118118

119+
// 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
120+
fn create_view_if_not_exists_supported(&self) -> bool {
121+
true
122+
}
123+
119124
fn require_interval_qualifier(&self) -> bool {
120125
true
121126
}

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
@@ -285,6 +285,16 @@ impl Dialect for SnowflakeDialect {
285285
true
286286
}
287287

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

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
@@ -5768,12 +5768,30 @@ impl<'a> Parser<'a> {
57685768
) -> Result<Statement, ParserError> {
57695769
let materialized = self.parse_keyword(Keyword::MATERIALIZED);
57705770
self.expect_keyword_is(Keyword::VIEW)?;
5771-
let if_not_exists = dialect_of!(self is BigQueryDialect|SQLiteDialect|GenericDialect)
5772-
&& self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
5771+
let allow_unquoted_hyphen = dialect_of!(self is BigQueryDialect);
5772+
let mut if_not_exists = false;
5773+
let name: ObjectName;
5774+
let mut name_before_not_exists = false;
5775+
if self.peek_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]) {
5776+
// Possible syntax -> ... IF NOT EXISTS <name>
5777+
if self.dialect.create_view_if_not_exists_supported() {
5778+
if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
5779+
}
5780+
name = self.parse_object_name(allow_unquoted_hyphen)?;
5781+
} else {
5782+
// Possible syntax -> ... <name> IF NOT EXISTS
5783+
name = self.parse_object_name(allow_unquoted_hyphen)?;
5784+
if self
5785+
.dialect
5786+
.create_view_name_before_if_not_exists_supported()
5787+
&& self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS])
5788+
{
5789+
if_not_exists = true;
5790+
name_before_not_exists = true;
5791+
}
5792+
}
57735793
// Many dialects support `OR ALTER` right after `CREATE`, but we don't (yet).
57745794
// ANSI SQL and Postgres support RECURSIVE here, but we don't support it either.
5775-
let allow_unquoted_hyphen = dialect_of!(self is BigQueryDialect);
5776-
let name = self.parse_object_name(allow_unquoted_hyphen)?;
57775795
let columns = self.parse_view_columns()?;
57785796
let mut options = CreateTableOptions::None;
57795797
let with_options = self.parse_options(Keyword::WITH)?;
@@ -5840,6 +5858,7 @@ impl<'a> Parser<'a> {
58405858
temporary,
58415859
to,
58425860
params: create_view_params,
5861+
name_before_not_exists,
58435862
})
58445863
}
58455864

tests/sqlparser_common.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8040,6 +8040,7 @@ fn parse_create_view() {
80408040
temporary,
80418041
to,
80428042
params,
8043+
name_before_not_exists: _,
80438044
} => {
80448045
assert_eq!(or_alter, false);
80458046
assert_eq!("myschema.myview", name.to_string());
@@ -8108,6 +8109,7 @@ fn parse_create_view_with_columns() {
81088109
temporary,
81098110
to,
81108111
params,
8112+
name_before_not_exists: _,
81118113
} => {
81128114
assert_eq!(or_alter, false);
81138115
assert_eq!("v", name.to_string());
@@ -8157,6 +8159,7 @@ fn parse_create_view_temporary() {
81578159
temporary,
81588160
to,
81598161
params,
8162+
name_before_not_exists: _,
81608163
} => {
81618164
assert_eq!(or_alter, false);
81628165
assert_eq!("myschema.myview", name.to_string());
@@ -8196,6 +8199,7 @@ fn parse_create_or_replace_view() {
81968199
temporary,
81978200
to,
81988201
params,
8202+
name_before_not_exists: _,
81998203
} => {
82008204
assert_eq!(or_alter, false);
82018205
assert_eq!("v", name.to_string());
@@ -8239,6 +8243,7 @@ fn parse_create_or_replace_materialized_view() {
82398243
temporary,
82408244
to,
82418245
params,
8246+
name_before_not_exists: _,
82428247
} => {
82438248
assert_eq!(or_alter, false);
82448249
assert_eq!("v", name.to_string());
@@ -8278,6 +8283,7 @@ fn parse_create_materialized_view() {
82788283
temporary,
82798284
to,
82808285
params,
8286+
name_before_not_exists: _,
82818287
} => {
82828288
assert_eq!(or_alter, false);
82838289
assert_eq!("myschema.myview", name.to_string());
@@ -8317,6 +8323,7 @@ fn parse_create_materialized_view_with_cluster_by() {
83178323
temporary,
83188324
to,
83198325
params,
8326+
name_before_not_exists: _,
83208327
} => {
83218328
assert_eq!(or_alter, false);
83228329
assert_eq!("myschema.myview", name.to_string());
@@ -16377,3 +16384,21 @@ fn parse_drop_stream() {
1637716384
}
1637816385
verified_stmt("DROP STREAM IF EXISTS s1");
1637916386
}
16387+
16388+
#[test]
16389+
fn parse_create_view_if_not_exists() {
16390+
let sql = "CREATE VIEW IF NOT EXISTS v AS SELECT 1";
16391+
let dialects = TestedDialects::new(vec![
16392+
Box::new(SnowflakeDialect {}),
16393+
Box::new(GenericDialect {}),
16394+
Box::new(SQLiteDialect {}),
16395+
Box::new(BigQueryDialect {}),
16396+
]);
16397+
let _ = dialects.verified_stmt(sql);
16398+
let sql = "CREATE VIEW v IF NOT EXISTS AS SELECT 1";
16399+
let dialects = TestedDialects::new(vec![
16400+
Box::new(SnowflakeDialect {}),
16401+
Box::new(GenericDialect {}),
16402+
]);
16403+
let _ = dialects.verified_stmt(sql);
16404+
}

0 commit comments

Comments
 (0)