mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-22 06:54:07 +00:00
Add support for view comments for Snowflake (#1287)
Co-authored-by: Joey Hain <joey@sigmacomputing.com>
This commit is contained in:
parent
c2d84f5683
commit
029a999645
6 changed files with 94 additions and 1 deletions
|
@ -1959,6 +1959,9 @@ pub enum Statement {
|
|||
query: Box<Query>,
|
||||
options: CreateTableOptions,
|
||||
cluster_by: Vec<Ident>,
|
||||
/// Snowflake: Views can have comments in Snowflake.
|
||||
/// <https://docs.snowflake.com/en/sql-reference/sql/create-view#syntax>
|
||||
comment: Option<String>,
|
||||
/// if true, has RedShift [`WITH NO SCHEMA BINDING`] clause <https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_VIEW.html>
|
||||
with_no_schema_binding: bool,
|
||||
/// if true, has SQLite `IF NOT EXISTS` clause <https://www.sqlite.org/lang_createview.html>
|
||||
|
@ -3323,6 +3326,7 @@ impl fmt::Display for Statement {
|
|||
materialized,
|
||||
options,
|
||||
cluster_by,
|
||||
comment,
|
||||
with_no_schema_binding,
|
||||
if_not_exists,
|
||||
temporary,
|
||||
|
@ -3336,6 +3340,13 @@ impl fmt::Display for Statement {
|
|||
temporary = if *temporary { "TEMPORARY " } else { "" },
|
||||
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }
|
||||
)?;
|
||||
if let Some(comment) = comment {
|
||||
write!(
|
||||
f,
|
||||
" COMMENT = '{}'",
|
||||
value::escape_single_quote_string(comment)
|
||||
)?;
|
||||
}
|
||||
if matches!(options, CreateTableOptions::With(_)) {
|
||||
write!(f, " {options}")?;
|
||||
}
|
||||
|
|
|
@ -4034,6 +4034,19 @@ impl<'a> Parser<'a> {
|
|||
};
|
||||
}
|
||||
|
||||
let comment = if dialect_of!(self is SnowflakeDialect | GenericDialect)
|
||||
&& self.parse_keyword(Keyword::COMMENT)
|
||||
{
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let next_token = self.next_token();
|
||||
match next_token.token {
|
||||
Token::SingleQuotedString(str) => Some(str),
|
||||
_ => self.expected("string literal", next_token)?,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
self.expect_keyword(Keyword::AS)?;
|
||||
let query = self.parse_boxed_query()?;
|
||||
// Optional `WITH [ CASCADED | LOCAL ] CHECK OPTION` is widely supported here.
|
||||
|
@ -4054,6 +4067,7 @@ impl<'a> Parser<'a> {
|
|||
or_replace,
|
||||
options,
|
||||
cluster_by,
|
||||
comment,
|
||||
with_no_schema_binding,
|
||||
if_not_exists,
|
||||
temporary,
|
||||
|
|
|
@ -309,6 +309,7 @@ fn parse_create_view_if_not_exists() {
|
|||
materialized,
|
||||
options,
|
||||
cluster_by,
|
||||
comment,
|
||||
with_no_schema_binding: late_binding,
|
||||
if_not_exists,
|
||||
temporary,
|
||||
|
@ -320,6 +321,7 @@ fn parse_create_view_if_not_exists() {
|
|||
assert!(!or_replace);
|
||||
assert_eq!(options, CreateTableOptions::None);
|
||||
assert_eq!(cluster_by, vec![]);
|
||||
assert!(comment.is_none());
|
||||
assert!(!late_binding);
|
||||
assert!(if_not_exists);
|
||||
assert!(!temporary);
|
||||
|
|
|
@ -6251,6 +6251,7 @@ fn parse_create_view() {
|
|||
materialized,
|
||||
options,
|
||||
cluster_by,
|
||||
comment,
|
||||
with_no_schema_binding: late_binding,
|
||||
if_not_exists,
|
||||
temporary,
|
||||
|
@ -6262,6 +6263,7 @@ fn parse_create_view() {
|
|||
assert!(!or_replace);
|
||||
assert_eq!(options, CreateTableOptions::None);
|
||||
assert_eq!(cluster_by, vec![]);
|
||||
assert!(comment.is_none());
|
||||
assert!(!late_binding);
|
||||
assert!(!if_not_exists);
|
||||
assert!(!temporary);
|
||||
|
@ -6305,6 +6307,7 @@ fn parse_create_view_with_columns() {
|
|||
query,
|
||||
materialized,
|
||||
cluster_by,
|
||||
comment,
|
||||
with_no_schema_binding: late_binding,
|
||||
if_not_exists,
|
||||
temporary,
|
||||
|
@ -6325,6 +6328,7 @@ fn parse_create_view_with_columns() {
|
|||
assert!(!materialized);
|
||||
assert!(!or_replace);
|
||||
assert_eq!(cluster_by, vec![]);
|
||||
assert!(comment.is_none());
|
||||
assert!(!late_binding);
|
||||
assert!(!if_not_exists);
|
||||
assert!(!temporary);
|
||||
|
@ -6345,6 +6349,7 @@ fn parse_create_view_temporary() {
|
|||
materialized,
|
||||
options,
|
||||
cluster_by,
|
||||
comment,
|
||||
with_no_schema_binding: late_binding,
|
||||
if_not_exists,
|
||||
temporary,
|
||||
|
@ -6356,6 +6361,7 @@ fn parse_create_view_temporary() {
|
|||
assert!(!or_replace);
|
||||
assert_eq!(options, CreateTableOptions::None);
|
||||
assert_eq!(cluster_by, vec![]);
|
||||
assert!(comment.is_none());
|
||||
assert!(!late_binding);
|
||||
assert!(!if_not_exists);
|
||||
assert!(temporary);
|
||||
|
@ -6376,6 +6382,7 @@ fn parse_create_or_replace_view() {
|
|||
query,
|
||||
materialized,
|
||||
cluster_by,
|
||||
comment,
|
||||
with_no_schema_binding: late_binding,
|
||||
if_not_exists,
|
||||
temporary,
|
||||
|
@ -6387,6 +6394,7 @@ fn parse_create_or_replace_view() {
|
|||
assert!(!materialized);
|
||||
assert!(or_replace);
|
||||
assert_eq!(cluster_by, vec![]);
|
||||
assert!(comment.is_none());
|
||||
assert!(!late_binding);
|
||||
assert!(!if_not_exists);
|
||||
assert!(!temporary);
|
||||
|
@ -6411,6 +6419,7 @@ fn parse_create_or_replace_materialized_view() {
|
|||
query,
|
||||
materialized,
|
||||
cluster_by,
|
||||
comment,
|
||||
with_no_schema_binding: late_binding,
|
||||
if_not_exists,
|
||||
temporary,
|
||||
|
@ -6422,6 +6431,7 @@ fn parse_create_or_replace_materialized_view() {
|
|||
assert!(materialized);
|
||||
assert!(or_replace);
|
||||
assert_eq!(cluster_by, vec![]);
|
||||
assert!(comment.is_none());
|
||||
assert!(!late_binding);
|
||||
assert!(!if_not_exists);
|
||||
assert!(!temporary);
|
||||
|
@ -6442,6 +6452,7 @@ fn parse_create_materialized_view() {
|
|||
materialized,
|
||||
options,
|
||||
cluster_by,
|
||||
comment,
|
||||
with_no_schema_binding: late_binding,
|
||||
if_not_exists,
|
||||
temporary,
|
||||
|
@ -6453,6 +6464,7 @@ fn parse_create_materialized_view() {
|
|||
assert_eq!(options, CreateTableOptions::None);
|
||||
assert!(!or_replace);
|
||||
assert_eq!(cluster_by, vec![]);
|
||||
assert!(comment.is_none());
|
||||
assert!(!late_binding);
|
||||
assert!(!if_not_exists);
|
||||
assert!(!temporary);
|
||||
|
@ -6473,6 +6485,7 @@ fn parse_create_materialized_view_with_cluster_by() {
|
|||
materialized,
|
||||
options,
|
||||
cluster_by,
|
||||
comment,
|
||||
with_no_schema_binding: late_binding,
|
||||
if_not_exists,
|
||||
temporary,
|
||||
|
@ -6484,6 +6497,7 @@ fn parse_create_materialized_view_with_cluster_by() {
|
|||
assert_eq!(options, CreateTableOptions::None);
|
||||
assert!(!or_replace);
|
||||
assert_eq!(cluster_by, vec![Ident::new("foo")]);
|
||||
assert!(comment.is_none());
|
||||
assert!(!late_binding);
|
||||
assert!(!if_not_exists);
|
||||
assert!(!temporary);
|
||||
|
|
|
@ -18,7 +18,7 @@ use sqlparser::ast::helpers::stmt_data_loading::{
|
|||
DataLoadingOption, DataLoadingOptionType, StageLoadSelectItem,
|
||||
};
|
||||
use sqlparser::ast::*;
|
||||
use sqlparser::dialect::{GenericDialect, SnowflakeDialect};
|
||||
use sqlparser::dialect::{Dialect, GenericDialect, SnowflakeDialect};
|
||||
use sqlparser::parser::{ParserError, ParserOptions};
|
||||
use sqlparser::tokenizer::*;
|
||||
use test_utils::*;
|
||||
|
@ -91,6 +91,56 @@ fn test_snowflake_single_line_tokenize() {
|
|||
assert_eq!(expected, tokens);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_sf_create_or_replace_view_with_comment_missing_equal() {
|
||||
assert!(snowflake_and_generic()
|
||||
.parse_sql_statements("CREATE OR REPLACE VIEW v COMMENT = 'hello, world' AS SELECT 1")
|
||||
.is_ok());
|
||||
|
||||
assert!(snowflake_and_generic()
|
||||
.parse_sql_statements("CREATE OR REPLACE VIEW v COMMENT 'hello, world' AS SELECT 1")
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_sf_create_or_replace_with_comment_for_snowflake() {
|
||||
let sql = "CREATE OR REPLACE VIEW v COMMENT = 'hello, world' AS SELECT 1";
|
||||
let dialect = test_utils::TestedDialects {
|
||||
dialects: vec![Box::new(SnowflakeDialect {}) as Box<dyn Dialect>],
|
||||
options: None,
|
||||
};
|
||||
|
||||
match dialect.verified_stmt(sql) {
|
||||
Statement::CreateView {
|
||||
name,
|
||||
columns,
|
||||
or_replace,
|
||||
options,
|
||||
query,
|
||||
materialized,
|
||||
cluster_by,
|
||||
comment,
|
||||
with_no_schema_binding: late_binding,
|
||||
if_not_exists,
|
||||
temporary,
|
||||
} => {
|
||||
assert_eq!("v", name.to_string());
|
||||
assert_eq!(columns, vec![]);
|
||||
assert_eq!(options, CreateTableOptions::None);
|
||||
assert_eq!("SELECT 1", query.to_string());
|
||||
assert!(!materialized);
|
||||
assert!(or_replace);
|
||||
assert_eq!(cluster_by, vec![]);
|
||||
assert!(comment.is_some());
|
||||
assert_eq!(comment.expect("expected comment"), "hello, world");
|
||||
assert!(!late_binding);
|
||||
assert!(!if_not_exists);
|
||||
assert!(!temporary);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sf_derived_table_in_parenthesis() {
|
||||
// Nesting a subquery in an extra set of parentheses is non-standard,
|
||||
|
|
|
@ -167,6 +167,7 @@ fn parse_create_view_temporary_if_not_exists() {
|
|||
materialized,
|
||||
options,
|
||||
cluster_by,
|
||||
comment,
|
||||
with_no_schema_binding: late_binding,
|
||||
if_not_exists,
|
||||
temporary,
|
||||
|
@ -178,6 +179,7 @@ fn parse_create_view_temporary_if_not_exists() {
|
|||
assert!(!or_replace);
|
||||
assert_eq!(options, CreateTableOptions::None);
|
||||
assert_eq!(cluster_by, vec![]);
|
||||
assert!(comment.is_none());
|
||||
assert!(!late_binding);
|
||||
assert!(if_not_exists);
|
||||
assert!(temporary);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue