mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-07-07 17:04:59 +00:00
Snowflake: support multiple column options in CREATE VIEW
(#1891)
Some checks failed
license / Release Audit Tool (RAT) (push) Has been cancelled
Rust / codestyle (push) Has been cancelled
Rust / lint (push) Has been cancelled
Rust / benchmark-lint (push) Has been cancelled
Rust / compile (push) Has been cancelled
Rust / docs (push) Has been cancelled
Rust / compile-no-std (push) Has been cancelled
Rust / test (beta) (push) Has been cancelled
Rust / test (nightly) (push) Has been cancelled
Rust / test (stable) (push) Has been cancelled
Some checks failed
license / Release Audit Tool (RAT) (push) Has been cancelled
Rust / codestyle (push) Has been cancelled
Rust / lint (push) Has been cancelled
Rust / benchmark-lint (push) Has been cancelled
Rust / compile (push) Has been cancelled
Rust / docs (push) Has been cancelled
Rust / compile-no-std (push) Has been cancelled
Rust / test (beta) (push) Has been cancelled
Rust / test (nightly) (push) Has been cancelled
Rust / test (stable) (push) Has been cancelled
This commit is contained in:
parent
b2ab0061c1
commit
1bbc05cdff
10 changed files with 98 additions and 40 deletions
|
@ -1426,7 +1426,24 @@ impl fmt::Display for ColumnDef {
|
||||||
pub struct ViewColumnDef {
|
pub struct ViewColumnDef {
|
||||||
pub name: Ident,
|
pub name: Ident,
|
||||||
pub data_type: Option<DataType>,
|
pub data_type: Option<DataType>,
|
||||||
pub options: Option<Vec<ColumnOption>>,
|
pub options: Option<ColumnOptions>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||||
|
pub enum ColumnOptions {
|
||||||
|
CommaSeparated(Vec<ColumnOption>),
|
||||||
|
SpaceSeparated(Vec<ColumnOption>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColumnOptions {
|
||||||
|
pub fn as_slice(&self) -> &[ColumnOption] {
|
||||||
|
match self {
|
||||||
|
ColumnOptions::CommaSeparated(options) => options.as_slice(),
|
||||||
|
ColumnOptions::SpaceSeparated(options) => options.as_slice(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ViewColumnDef {
|
impl fmt::Display for ViewColumnDef {
|
||||||
|
@ -1436,7 +1453,14 @@ impl fmt::Display for ViewColumnDef {
|
||||||
write!(f, " {}", data_type)?;
|
write!(f, " {}", data_type)?;
|
||||||
}
|
}
|
||||||
if let Some(options) = self.options.as_ref() {
|
if let Some(options) = self.options.as_ref() {
|
||||||
write!(f, " {}", display_comma_separated(options.as_slice()))?;
|
match options {
|
||||||
|
ColumnOptions::CommaSeparated(column_options) => {
|
||||||
|
write!(f, " {}", display_comma_separated(column_options.as_slice()))?;
|
||||||
|
}
|
||||||
|
ColumnOptions::SpaceSeparated(column_options) => {
|
||||||
|
write!(f, " {}", display_separated(column_options.as_slice(), " "))?
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,13 +61,14 @@ pub use self::ddl::{
|
||||||
AlterColumnOperation, AlterConnectorOwner, AlterIndexOperation, AlterPolicyOperation,
|
AlterColumnOperation, AlterConnectorOwner, AlterIndexOperation, AlterPolicyOperation,
|
||||||
AlterTableAlgorithm, AlterTableLock, AlterTableOperation, AlterType, AlterTypeAddValue,
|
AlterTableAlgorithm, AlterTableLock, AlterTableOperation, AlterType, AlterTypeAddValue,
|
||||||
AlterTypeAddValuePosition, AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue,
|
AlterTypeAddValuePosition, AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue,
|
||||||
ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnPolicy, ColumnPolicyProperty,
|
ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy,
|
||||||
ConstraintCharacteristics, CreateConnector, CreateDomain, CreateFunction, Deduplicate,
|
ColumnPolicyProperty, ConstraintCharacteristics, CreateConnector, CreateDomain, CreateFunction,
|
||||||
DeferrableInitial, DropBehavior, GeneratedAs, GeneratedExpressionMode, IdentityParameters,
|
Deduplicate, DeferrableInitial, DropBehavior, GeneratedAs, GeneratedExpressionMode,
|
||||||
IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder,
|
IdentityParameters, IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind,
|
||||||
IndexOption, IndexType, KeyOrIndexDisplay, NullsDistinctOption, Owner, Partition,
|
IdentityPropertyOrder, IndexOption, IndexType, KeyOrIndexDisplay, NullsDistinctOption, Owner,
|
||||||
ProcedureParam, ReferentialAction, ReplicaIdentity, TableConstraint, TagsColumnOption,
|
Partition, ProcedureParam, ReferentialAction, ReplicaIdentity, TableConstraint,
|
||||||
UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation, ViewColumnDef,
|
TagsColumnOption, UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation,
|
||||||
|
ViewColumnDef,
|
||||||
};
|
};
|
||||||
pub use self::dml::{CreateIndex, CreateTable, Delete, IndexColumn, Insert};
|
pub use self::dml::{CreateIndex, CreateTable, Delete, IndexColumn, Insert};
|
||||||
pub use self::operator::{BinaryOperator, UnaryOperator};
|
pub use self::operator::{BinaryOperator, UnaryOperator};
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
// specific language governing permissions and limitations
|
// specific language governing permissions and limitations
|
||||||
// under the License.
|
// under the License.
|
||||||
|
|
||||||
use crate::ast::query::SelectItemQualifiedWildcardKind;
|
use crate::ast::{query::SelectItemQualifiedWildcardKind, ColumnOptions};
|
||||||
use core::iter;
|
use core::iter;
|
||||||
|
|
||||||
use crate::tokenizer::Span;
|
use crate::tokenizer::Span;
|
||||||
|
@ -991,10 +991,13 @@ impl Spanned for ViewColumnDef {
|
||||||
options,
|
options,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
union_spans(
|
name.span.union_opt(&options.as_ref().map(|o| o.span()))
|
||||||
core::iter::once(name.span)
|
}
|
||||||
.chain(options.iter().flat_map(|i| i.iter().map(|k| k.span()))),
|
}
|
||||||
)
|
|
||||||
|
impl Spanned for ColumnOptions {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
union_spans(self.as_slice().iter().map(|i| i.span()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1055,7 +1058,9 @@ impl Spanned for CreateTableOptions {
|
||||||
match self {
|
match self {
|
||||||
CreateTableOptions::None => Span::empty(),
|
CreateTableOptions::None => Span::empty(),
|
||||||
CreateTableOptions::With(vec) => union_spans(vec.iter().map(|i| i.span())),
|
CreateTableOptions::With(vec) => union_spans(vec.iter().map(|i| i.span())),
|
||||||
CreateTableOptions::Options(vec) => union_spans(vec.iter().map(|i| i.span())),
|
CreateTableOptions::Options(vec) => {
|
||||||
|
union_spans(vec.as_slice().iter().map(|i| i.span()))
|
||||||
|
}
|
||||||
CreateTableOptions::Plain(vec) => union_spans(vec.iter().map(|i| i.span())),
|
CreateTableOptions::Plain(vec) => union_spans(vec.iter().map(|i| i.span())),
|
||||||
CreateTableOptions::TableProperties(vec) => union_spans(vec.iter().map(|i| i.span())),
|
CreateTableOptions::TableProperties(vec) => union_spans(vec.iter().map(|i| i.span())),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1028,6 +1028,10 @@ pub trait Dialect: Debug + Any {
|
||||||
fn supports_set_names(&self) -> bool {
|
fn supports_set_names(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn supports_space_separated_column_options(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This represents the operators for which precedence must be defined
|
/// This represents the operators for which precedence must be defined
|
||||||
|
|
|
@ -356,6 +356,10 @@ impl Dialect for SnowflakeDialect {
|
||||||
fn get_reserved_keywords_for_select_item_operator(&self) -> &[Keyword] {
|
fn get_reserved_keywords_for_select_item_operator(&self) -> &[Keyword] {
|
||||||
&RESERVED_KEYWORDS_FOR_SELECT_ITEM_OPERATOR
|
&RESERVED_KEYWORDS_FOR_SELECT_ITEM_OPERATOR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn supports_space_separated_column_options(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_file_staging_command(kw: Keyword, parser: &mut Parser) -> Result<Statement, ParserError> {
|
fn parse_file_staging_command(kw: Keyword, parser: &mut Parser) -> Result<Statement, ParserError> {
|
||||||
|
|
|
@ -10579,17 +10579,7 @@ impl<'a> Parser<'a> {
|
||||||
/// Parses a column definition within a view.
|
/// Parses a column definition within a view.
|
||||||
fn parse_view_column(&mut self) -> Result<ViewColumnDef, ParserError> {
|
fn parse_view_column(&mut self) -> Result<ViewColumnDef, ParserError> {
|
||||||
let name = self.parse_identifier()?;
|
let name = self.parse_identifier()?;
|
||||||
let options = if (dialect_of!(self is BigQueryDialect | GenericDialect)
|
let options = self.parse_view_column_options()?;
|
||||||
&& 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 data_type = if dialect_of!(self is ClickHouseDialect) {
|
let data_type = if dialect_of!(self is ClickHouseDialect) {
|
||||||
Some(self.parse_data_type()?)
|
Some(self.parse_data_type()?)
|
||||||
} else {
|
} else {
|
||||||
|
@ -10602,6 +10592,25 @@ impl<'a> Parser<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_view_column_options(&mut self) -> Result<Option<ColumnOptions>, ParserError> {
|
||||||
|
let mut options = Vec::new();
|
||||||
|
loop {
|
||||||
|
let option = self.parse_optional_column_option()?;
|
||||||
|
if let Some(option) = option {
|
||||||
|
options.push(option);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if options.is_empty() {
|
||||||
|
Ok(None)
|
||||||
|
} else if self.dialect.supports_space_separated_column_options() {
|
||||||
|
Ok(Some(ColumnOptions::SpaceSeparated(options)))
|
||||||
|
} else {
|
||||||
|
Ok(Some(ColumnOptions::CommaSeparated(options)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parses a parenthesized comma-separated list of unqualified, possibly quoted identifiers.
|
/// Parses a parenthesized comma-separated list of unqualified, possibly quoted identifiers.
|
||||||
/// For example: `(col1, "col 2", ...)`
|
/// For example: `(col1, "col 2", ...)`
|
||||||
pub fn parse_parenthesized_column_list(
|
pub fn parse_parenthesized_column_list(
|
||||||
|
|
|
@ -355,14 +355,16 @@ fn parse_create_view_with_options() {
|
||||||
ViewColumnDef {
|
ViewColumnDef {
|
||||||
name: Ident::new("age"),
|
name: Ident::new("age"),
|
||||||
data_type: None,
|
data_type: None,
|
||||||
options: Some(vec![ColumnOption::Options(vec![SqlOption::KeyValue {
|
options: Some(ColumnOptions::CommaSeparated(vec![ColumnOption::Options(
|
||||||
key: Ident::new("description"),
|
vec![SqlOption::KeyValue {
|
||||||
value: Expr::Value(
|
key: Ident::new("description"),
|
||||||
Value::DoubleQuotedString("field age".to_string()).with_span(
|
value: Expr::Value(
|
||||||
Span::new(Location::new(1, 42), Location::new(1, 52))
|
Value::DoubleQuotedString("field age".to_string()).with_span(
|
||||||
)
|
Span::new(Location::new(1, 42), Location::new(1, 52))
|
||||||
),
|
)
|
||||||
}])]),
|
),
|
||||||
|
}]
|
||||||
|
)])),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
columns
|
columns
|
||||||
|
|
|
@ -914,7 +914,7 @@ fn parse_create_view_with_fields_data_types() {
|
||||||
}]),
|
}]),
|
||||||
vec![]
|
vec![]
|
||||||
)),
|
)),
|
||||||
options: None
|
options: None,
|
||||||
},
|
},
|
||||||
ViewColumnDef {
|
ViewColumnDef {
|
||||||
name: "f".into(),
|
name: "f".into(),
|
||||||
|
@ -926,7 +926,7 @@ fn parse_create_view_with_fields_data_types() {
|
||||||
}]),
|
}]),
|
||||||
vec![]
|
vec![]
|
||||||
)),
|
)),
|
||||||
options: None
|
options: None,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
|
@ -7990,7 +7990,7 @@ fn parse_create_view_with_columns() {
|
||||||
.map(|name| ViewColumnDef {
|
.map(|name| ViewColumnDef {
|
||||||
name,
|
name,
|
||||||
data_type: None,
|
data_type: None,
|
||||||
options: None
|
options: None,
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
);
|
);
|
||||||
|
|
|
@ -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') 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",
|
"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);
|
.verified_stmt(sql);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3133,7 +3133,7 @@ fn view_comment_option_should_be_after_column_list() {
|
||||||
fn parse_view_column_descriptions() {
|
fn parse_view_column_descriptions() {
|
||||||
let sql = "CREATE OR REPLACE VIEW v (a COMMENT 'Comment', b) AS SELECT a, b FROM table1";
|
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, .. } => {
|
Statement::CreateView { name, columns, .. } => {
|
||||||
assert_eq!(name.to_string(), "v");
|
assert_eq!(name.to_string(), "v");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -3142,7 +3142,9 @@ fn parse_view_column_descriptions() {
|
||||||
ViewColumnDef {
|
ViewColumnDef {
|
||||||
name: Ident::new("a"),
|
name: Ident::new("a"),
|
||||||
data_type: None,
|
data_type: None,
|
||||||
options: Some(vec![ColumnOption::Comment("Comment".to_string())]),
|
options: Some(ColumnOptions::SpaceSeparated(vec![ColumnOption::Comment(
|
||||||
|
"Comment".to_string()
|
||||||
|
)])),
|
||||||
},
|
},
|
||||||
ViewColumnDef {
|
ViewColumnDef {
|
||||||
name: Ident::new("b"),
|
name: Ident::new("b"),
|
||||||
|
@ -4165,3 +4167,10 @@ fn test_snowflake_fetch_clause_syntax() {
|
||||||
canonical,
|
canonical,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_snowflake_create_view_with_multiple_column_options() {
|
||||||
|
let create_view_with_tag =
|
||||||
|
r#"CREATE VIEW X (COL WITH TAG (pii='email') COMMENT 'foobar') AS SELECT * FROM Y"#;
|
||||||
|
snowflake().verified_stmt(create_view_with_tag);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue