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>,
|
query: Box<Query>,
|
||||||
options: CreateTableOptions,
|
options: CreateTableOptions,
|
||||||
cluster_by: Vec<Ident>,
|
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>
|
/// 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,
|
with_no_schema_binding: bool,
|
||||||
/// if true, has SQLite `IF NOT EXISTS` clause <https://www.sqlite.org/lang_createview.html>
|
/// 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,
|
materialized,
|
||||||
options,
|
options,
|
||||||
cluster_by,
|
cluster_by,
|
||||||
|
comment,
|
||||||
with_no_schema_binding,
|
with_no_schema_binding,
|
||||||
if_not_exists,
|
if_not_exists,
|
||||||
temporary,
|
temporary,
|
||||||
|
@ -3336,6 +3340,13 @@ impl fmt::Display for Statement {
|
||||||
temporary = if *temporary { "TEMPORARY " } else { "" },
|
temporary = if *temporary { "TEMPORARY " } else { "" },
|
||||||
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } 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(_)) {
|
if matches!(options, CreateTableOptions::With(_)) {
|
||||||
write!(f, " {options}")?;
|
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)?;
|
self.expect_keyword(Keyword::AS)?;
|
||||||
let query = self.parse_boxed_query()?;
|
let query = self.parse_boxed_query()?;
|
||||||
// Optional `WITH [ CASCADED | LOCAL ] CHECK OPTION` is widely supported here.
|
// Optional `WITH [ CASCADED | LOCAL ] CHECK OPTION` is widely supported here.
|
||||||
|
@ -4054,6 +4067,7 @@ impl<'a> Parser<'a> {
|
||||||
or_replace,
|
or_replace,
|
||||||
options,
|
options,
|
||||||
cluster_by,
|
cluster_by,
|
||||||
|
comment,
|
||||||
with_no_schema_binding,
|
with_no_schema_binding,
|
||||||
if_not_exists,
|
if_not_exists,
|
||||||
temporary,
|
temporary,
|
||||||
|
|
|
@ -309,6 +309,7 @@ fn parse_create_view_if_not_exists() {
|
||||||
materialized,
|
materialized,
|
||||||
options,
|
options,
|
||||||
cluster_by,
|
cluster_by,
|
||||||
|
comment,
|
||||||
with_no_schema_binding: late_binding,
|
with_no_schema_binding: late_binding,
|
||||||
if_not_exists,
|
if_not_exists,
|
||||||
temporary,
|
temporary,
|
||||||
|
@ -320,6 +321,7 @@ fn parse_create_view_if_not_exists() {
|
||||||
assert!(!or_replace);
|
assert!(!or_replace);
|
||||||
assert_eq!(options, CreateTableOptions::None);
|
assert_eq!(options, CreateTableOptions::None);
|
||||||
assert_eq!(cluster_by, vec![]);
|
assert_eq!(cluster_by, vec![]);
|
||||||
|
assert!(comment.is_none());
|
||||||
assert!(!late_binding);
|
assert!(!late_binding);
|
||||||
assert!(if_not_exists);
|
assert!(if_not_exists);
|
||||||
assert!(!temporary);
|
assert!(!temporary);
|
||||||
|
|
|
@ -6251,6 +6251,7 @@ fn parse_create_view() {
|
||||||
materialized,
|
materialized,
|
||||||
options,
|
options,
|
||||||
cluster_by,
|
cluster_by,
|
||||||
|
comment,
|
||||||
with_no_schema_binding: late_binding,
|
with_no_schema_binding: late_binding,
|
||||||
if_not_exists,
|
if_not_exists,
|
||||||
temporary,
|
temporary,
|
||||||
|
@ -6262,6 +6263,7 @@ fn parse_create_view() {
|
||||||
assert!(!or_replace);
|
assert!(!or_replace);
|
||||||
assert_eq!(options, CreateTableOptions::None);
|
assert_eq!(options, CreateTableOptions::None);
|
||||||
assert_eq!(cluster_by, vec![]);
|
assert_eq!(cluster_by, vec![]);
|
||||||
|
assert!(comment.is_none());
|
||||||
assert!(!late_binding);
|
assert!(!late_binding);
|
||||||
assert!(!if_not_exists);
|
assert!(!if_not_exists);
|
||||||
assert!(!temporary);
|
assert!(!temporary);
|
||||||
|
@ -6305,6 +6307,7 @@ fn parse_create_view_with_columns() {
|
||||||
query,
|
query,
|
||||||
materialized,
|
materialized,
|
||||||
cluster_by,
|
cluster_by,
|
||||||
|
comment,
|
||||||
with_no_schema_binding: late_binding,
|
with_no_schema_binding: late_binding,
|
||||||
if_not_exists,
|
if_not_exists,
|
||||||
temporary,
|
temporary,
|
||||||
|
@ -6325,6 +6328,7 @@ fn parse_create_view_with_columns() {
|
||||||
assert!(!materialized);
|
assert!(!materialized);
|
||||||
assert!(!or_replace);
|
assert!(!or_replace);
|
||||||
assert_eq!(cluster_by, vec![]);
|
assert_eq!(cluster_by, vec![]);
|
||||||
|
assert!(comment.is_none());
|
||||||
assert!(!late_binding);
|
assert!(!late_binding);
|
||||||
assert!(!if_not_exists);
|
assert!(!if_not_exists);
|
||||||
assert!(!temporary);
|
assert!(!temporary);
|
||||||
|
@ -6345,6 +6349,7 @@ fn parse_create_view_temporary() {
|
||||||
materialized,
|
materialized,
|
||||||
options,
|
options,
|
||||||
cluster_by,
|
cluster_by,
|
||||||
|
comment,
|
||||||
with_no_schema_binding: late_binding,
|
with_no_schema_binding: late_binding,
|
||||||
if_not_exists,
|
if_not_exists,
|
||||||
temporary,
|
temporary,
|
||||||
|
@ -6356,6 +6361,7 @@ fn parse_create_view_temporary() {
|
||||||
assert!(!or_replace);
|
assert!(!or_replace);
|
||||||
assert_eq!(options, CreateTableOptions::None);
|
assert_eq!(options, CreateTableOptions::None);
|
||||||
assert_eq!(cluster_by, vec![]);
|
assert_eq!(cluster_by, vec![]);
|
||||||
|
assert!(comment.is_none());
|
||||||
assert!(!late_binding);
|
assert!(!late_binding);
|
||||||
assert!(!if_not_exists);
|
assert!(!if_not_exists);
|
||||||
assert!(temporary);
|
assert!(temporary);
|
||||||
|
@ -6376,6 +6382,7 @@ fn parse_create_or_replace_view() {
|
||||||
query,
|
query,
|
||||||
materialized,
|
materialized,
|
||||||
cluster_by,
|
cluster_by,
|
||||||
|
comment,
|
||||||
with_no_schema_binding: late_binding,
|
with_no_schema_binding: late_binding,
|
||||||
if_not_exists,
|
if_not_exists,
|
||||||
temporary,
|
temporary,
|
||||||
|
@ -6387,6 +6394,7 @@ fn parse_create_or_replace_view() {
|
||||||
assert!(!materialized);
|
assert!(!materialized);
|
||||||
assert!(or_replace);
|
assert!(or_replace);
|
||||||
assert_eq!(cluster_by, vec![]);
|
assert_eq!(cluster_by, vec![]);
|
||||||
|
assert!(comment.is_none());
|
||||||
assert!(!late_binding);
|
assert!(!late_binding);
|
||||||
assert!(!if_not_exists);
|
assert!(!if_not_exists);
|
||||||
assert!(!temporary);
|
assert!(!temporary);
|
||||||
|
@ -6411,6 +6419,7 @@ fn parse_create_or_replace_materialized_view() {
|
||||||
query,
|
query,
|
||||||
materialized,
|
materialized,
|
||||||
cluster_by,
|
cluster_by,
|
||||||
|
comment,
|
||||||
with_no_schema_binding: late_binding,
|
with_no_schema_binding: late_binding,
|
||||||
if_not_exists,
|
if_not_exists,
|
||||||
temporary,
|
temporary,
|
||||||
|
@ -6422,6 +6431,7 @@ fn parse_create_or_replace_materialized_view() {
|
||||||
assert!(materialized);
|
assert!(materialized);
|
||||||
assert!(or_replace);
|
assert!(or_replace);
|
||||||
assert_eq!(cluster_by, vec![]);
|
assert_eq!(cluster_by, vec![]);
|
||||||
|
assert!(comment.is_none());
|
||||||
assert!(!late_binding);
|
assert!(!late_binding);
|
||||||
assert!(!if_not_exists);
|
assert!(!if_not_exists);
|
||||||
assert!(!temporary);
|
assert!(!temporary);
|
||||||
|
@ -6442,6 +6452,7 @@ fn parse_create_materialized_view() {
|
||||||
materialized,
|
materialized,
|
||||||
options,
|
options,
|
||||||
cluster_by,
|
cluster_by,
|
||||||
|
comment,
|
||||||
with_no_schema_binding: late_binding,
|
with_no_schema_binding: late_binding,
|
||||||
if_not_exists,
|
if_not_exists,
|
||||||
temporary,
|
temporary,
|
||||||
|
@ -6453,6 +6464,7 @@ fn parse_create_materialized_view() {
|
||||||
assert_eq!(options, CreateTableOptions::None);
|
assert_eq!(options, CreateTableOptions::None);
|
||||||
assert!(!or_replace);
|
assert!(!or_replace);
|
||||||
assert_eq!(cluster_by, vec![]);
|
assert_eq!(cluster_by, vec![]);
|
||||||
|
assert!(comment.is_none());
|
||||||
assert!(!late_binding);
|
assert!(!late_binding);
|
||||||
assert!(!if_not_exists);
|
assert!(!if_not_exists);
|
||||||
assert!(!temporary);
|
assert!(!temporary);
|
||||||
|
@ -6473,6 +6485,7 @@ fn parse_create_materialized_view_with_cluster_by() {
|
||||||
materialized,
|
materialized,
|
||||||
options,
|
options,
|
||||||
cluster_by,
|
cluster_by,
|
||||||
|
comment,
|
||||||
with_no_schema_binding: late_binding,
|
with_no_schema_binding: late_binding,
|
||||||
if_not_exists,
|
if_not_exists,
|
||||||
temporary,
|
temporary,
|
||||||
|
@ -6484,6 +6497,7 @@ fn parse_create_materialized_view_with_cluster_by() {
|
||||||
assert_eq!(options, CreateTableOptions::None);
|
assert_eq!(options, CreateTableOptions::None);
|
||||||
assert!(!or_replace);
|
assert!(!or_replace);
|
||||||
assert_eq!(cluster_by, vec![Ident::new("foo")]);
|
assert_eq!(cluster_by, vec![Ident::new("foo")]);
|
||||||
|
assert!(comment.is_none());
|
||||||
assert!(!late_binding);
|
assert!(!late_binding);
|
||||||
assert!(!if_not_exists);
|
assert!(!if_not_exists);
|
||||||
assert!(!temporary);
|
assert!(!temporary);
|
||||||
|
|
|
@ -18,7 +18,7 @@ use sqlparser::ast::helpers::stmt_data_loading::{
|
||||||
DataLoadingOption, DataLoadingOptionType, StageLoadSelectItem,
|
DataLoadingOption, DataLoadingOptionType, StageLoadSelectItem,
|
||||||
};
|
};
|
||||||
use sqlparser::ast::*;
|
use sqlparser::ast::*;
|
||||||
use sqlparser::dialect::{GenericDialect, SnowflakeDialect};
|
use sqlparser::dialect::{Dialect, GenericDialect, SnowflakeDialect};
|
||||||
use sqlparser::parser::{ParserError, ParserOptions};
|
use sqlparser::parser::{ParserError, ParserOptions};
|
||||||
use sqlparser::tokenizer::*;
|
use sqlparser::tokenizer::*;
|
||||||
use test_utils::*;
|
use test_utils::*;
|
||||||
|
@ -91,6 +91,56 @@ fn test_snowflake_single_line_tokenize() {
|
||||||
assert_eq!(expected, tokens);
|
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]
|
#[test]
|
||||||
fn test_sf_derived_table_in_parenthesis() {
|
fn test_sf_derived_table_in_parenthesis() {
|
||||||
// Nesting a subquery in an extra set of parentheses is non-standard,
|
// 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,
|
materialized,
|
||||||
options,
|
options,
|
||||||
cluster_by,
|
cluster_by,
|
||||||
|
comment,
|
||||||
with_no_schema_binding: late_binding,
|
with_no_schema_binding: late_binding,
|
||||||
if_not_exists,
|
if_not_exists,
|
||||||
temporary,
|
temporary,
|
||||||
|
@ -178,6 +179,7 @@ fn parse_create_view_temporary_if_not_exists() {
|
||||||
assert!(!or_replace);
|
assert!(!or_replace);
|
||||||
assert_eq!(options, CreateTableOptions::None);
|
assert_eq!(options, CreateTableOptions::None);
|
||||||
assert_eq!(cluster_by, vec![]);
|
assert_eq!(cluster_by, vec![]);
|
||||||
|
assert!(comment.is_none());
|
||||||
assert!(!late_binding);
|
assert!(!late_binding);
|
||||||
assert!(if_not_exists);
|
assert!(if_not_exists);
|
||||||
assert!(temporary);
|
assert!(temporary);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue