Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1422,6 +1422,7 @@ pub struct ViewColumnDef {
pub name: Ident,
pub data_type: Option<DataType>,
pub options: Option<Vec<ColumnOption>>,
pub options_comma_separated: bool,
}

impl fmt::Display for ViewColumnDef {
Expand All @@ -1431,7 +1432,11 @@ impl fmt::Display for ViewColumnDef {
write!(f, " {}", data_type)?;
}
if let Some(options) = self.options.as_ref() {
write!(f, " {}", display_comma_separated(options.as_slice()))?;
if self.options_comma_separated {
write!(f, " {}", display_comma_separated(options.as_slice()))?;
} else {
write!(f, " {}", display_separated(options.as_slice(), " "))?;
}
}
Ok(())
}
Expand Down
1 change: 1 addition & 0 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,7 @@ impl Spanned for ViewColumnDef {
name,
data_type: _, // todo, DataType
options,
options_comma_separated: _,
} = self;

union_spans(
Expand Down
27 changes: 16 additions & 11 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use alloc::{
};
use core::{
fmt::{self, Display},
ops::Not,
str::FromStr,
};
use helpers::attached_token::AttachedToken;
Expand Down Expand Up @@ -10569,17 +10570,7 @@ impl<'a> Parser<'a> {
/// Parses a column definition within a view.
fn parse_view_column(&mut self) -> Result<ViewColumnDef, ParserError> {
let name = self.parse_identifier()?;
let options = if (dialect_of!(self is BigQueryDialect | GenericDialect)
&& self.parse_keyword(Keyword::OPTIONS))
|| (dialect_of!(self is SnowflakeDialect | GenericDialect)
&& self.parse_keyword(Keyword::COMMENT))
{
self.prev_token();
self.parse_optional_column_option()?
.map(|option| vec![option])
} else {
None
};
let options = self.parse_view_column_options()?;
let data_type = if dialect_of!(self is ClickHouseDialect) {
Some(self.parse_data_type()?)
} else {
Expand All @@ -10589,9 +10580,23 @@ impl<'a> Parser<'a> {
name,
data_type,
options,
options_comma_separated: !dialect_of!(self is SnowflakeDialect),
})
}

fn parse_view_column_options(&mut self) -> Result<Option<Vec<ColumnOption>>, ParserError> {
let mut options = Vec::new();
loop {
let option = self.parse_optional_column_option()?;
if let Some(option) = option {
options.push(option);
} else {
break;
}
}
Ok(options.is_empty().not().then_some(options))
}

/// Parses a parenthesized comma-separated list of unqualified, possibly quoted identifiers.
/// For example: `(col1, "col 2", ...)`
pub fn parse_parenthesized_column_list(
Expand Down
2 changes: 2 additions & 0 deletions tests/sqlparser_bigquery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ fn parse_create_view_with_options() {
name: Ident::new("name"),
data_type: None,
options: None,
options_comma_separated: true,
},
ViewColumnDef {
name: Ident::new("age"),
Expand All @@ -360,6 +361,7 @@ fn parse_create_view_with_options() {
)
),
}])]),
options_comma_separated: true,
},
],
columns
Expand Down
6 changes: 4 additions & 2 deletions tests/sqlparser_clickhouse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -914,7 +914,8 @@ fn parse_create_view_with_fields_data_types() {
}]),
vec![]
)),
options: None
options: None,
options_comma_separated: true,
},
ViewColumnDef {
name: "f".into(),
Expand All @@ -926,7 +927,8 @@ fn parse_create_view_with_fields_data_types() {
}]),
vec![]
)),
options: None
options: None,
options_comma_separated: true,
},
]
);
Expand Down
98 changes: 55 additions & 43 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7960,52 +7960,64 @@ fn parse_create_view_with_options() {
#[test]
fn parse_create_view_with_columns() {
let sql = "CREATE VIEW v (has, cols) AS SELECT 1, 2";
// TODO: why does this fail for ClickHouseDialect? (#1449)
// match all_dialects().verified_stmt(sql) {
match all_dialects_except(|d| d.is::<ClickHouseDialect>()).verified_stmt(sql) {
Statement::CreateView {
or_alter,
name,
columns,
or_replace,
options,
query,
materialized,
cluster_by,
comment,
with_no_schema_binding: late_binding,
if_not_exists,
temporary,
to,
params,
} => {
assert_eq!(or_alter, false);
assert_eq!("v", name.to_string());
assert_eq!(
fn assert_stmt_as_expected(stmt: Statement, options_comma_separated: bool) {
match stmt {
Statement::CreateView {
or_alter,
name,
columns,
vec![Ident::new("has"), Ident::new("cols"),]
.into_iter()
.map(|name| ViewColumnDef {
name,
data_type: None,
options: None
})
.collect::<Vec<_>>()
);
assert_eq!(options, CreateTableOptions::None);
assert_eq!("SELECT 1, 2", query.to_string());
assert!(!materialized);
assert!(!or_replace);
assert_eq!(cluster_by, vec![]);
assert!(comment.is_none());
assert!(!late_binding);
assert!(!if_not_exists);
assert!(!temporary);
assert!(to.is_none());
assert!(params.is_none());
or_replace,
options,
query,
materialized,
cluster_by,
comment,
with_no_schema_binding: late_binding,
if_not_exists,
temporary,
to,
params,
} => {
assert_eq!(or_alter, false);
assert_eq!("v", name.to_string());
assert_eq!(
columns,
vec![Ident::new("has"), Ident::new("cols"),]
.into_iter()
.map(|name| ViewColumnDef {
name,
data_type: None,
options: None,
options_comma_separated,
})
.collect::<Vec<_>>()
);
assert_eq!(options, CreateTableOptions::None);
assert_eq!("SELECT 1, 2", query.to_string());
assert!(!materialized);
assert!(!or_replace);
assert_eq!(cluster_by, vec![]);
assert!(comment.is_none());
assert!(!late_binding);
assert!(!if_not_exists);
assert!(!temporary);
assert!(to.is_none());
assert!(params.is_none());
}
_ => unreachable!(),
}
_ => unreachable!(),
}
// TODO: why does this fail for ClickHouseDialect? (#1449)
// match all_dialects().verified_stmt(sql) {
assert_stmt_as_expected(
all_dialects_where(|d| !d.is::<ClickHouseDialect>() && !d.is::<SnowflakeDialect>())
.verified_stmt(sql),
true,
);
assert_stmt_as_expected(
all_dialects_where(|d| d.is::<SnowflakeDialect>()).verified_stmt(sql),
false,
);
}

#[test]
Expand Down
19 changes: 17 additions & 2 deletions tests/sqlparser_snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3124,7 +3124,7 @@ fn view_comment_option_should_be_after_column_list() {
"CREATE OR REPLACE VIEW v (a COMMENT 'a comment', b, c COMMENT 'c comment') COMMENT = 'Comment' AS SELECT a FROM t",
"CREATE OR REPLACE VIEW v (a COMMENT 'a comment', b, c COMMENT 'c comment') WITH (foo = bar) COMMENT = 'Comment' AS SELECT a FROM t",
] {
snowflake_and_generic()
snowflake()
.verified_stmt(sql);
}
}
Expand All @@ -3133,7 +3133,7 @@ fn view_comment_option_should_be_after_column_list() {
fn parse_view_column_descriptions() {
let sql = "CREATE OR REPLACE VIEW v (a COMMENT 'Comment', b) AS SELECT a, b FROM table1";

match snowflake_and_generic().verified_stmt(sql) {
match snowflake().verified_stmt(sql) {
Statement::CreateView { name, columns, .. } => {
assert_eq!(name.to_string(), "v");
assert_eq!(
Expand All @@ -3143,11 +3143,13 @@ fn parse_view_column_descriptions() {
name: Ident::new("a"),
data_type: None,
options: Some(vec![ColumnOption::Comment("Comment".to_string())]),
options_comma_separated: false,
},
ViewColumnDef {
name: Ident::new("b"),
data_type: None,
options: None,
options_comma_separated: false,
}
]
);
Expand Down Expand Up @@ -4082,3 +4084,16 @@ fn parse_connect_by_root_operator() {
"sql parser error: Expected an expression, found: FROM"
);
}

#[test]
fn test_snowflake_create_view_with_tag() {
let create_view_with_tag = r#"CREATE VIEW X (COL WITH TAG (pii='email')) AS SELECT * FROM Y"#;
snowflake().verified_stmt(create_view_with_tag);
}

#[test]
fn test_snowflake_create_view_with_tag_and_comment() {
let create_view_with_tag_and_comment =
r#"CREATE VIEW X (COL WITH TAG (pii='email') COMMENT 'foobar') AS SELECT * FROM Y"#;
snowflake().verified_stmt(create_view_with_tag_and_comment);
}