diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index de514550b..9c30999ab 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -815,7 +815,7 @@ impl fmt::Display for ColumnDef { /// /// Syntax /// ```markdown -/// [OPTIONS(option, ...)] +/// [data_type][OPTIONS(option, ...)] /// /// option: = /// ``` @@ -824,18 +824,23 @@ impl fmt::Display for ColumnDef { /// ```sql /// name /// age OPTIONS(description = "age column", tag = "prod") +/// created_at DateTime64 /// ``` #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct ViewColumnDef { pub name: Ident, + pub data_type: Option, pub options: Option>, } impl fmt::Display for ViewColumnDef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.name)?; + if let Some(data_type) = self.data_type.as_ref() { + write!(f, " {}", data_type)?; + } if let Some(options) = self.options.as_ref() { write!( f, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index f88aefd10..d65987126 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -7122,7 +7122,16 @@ impl<'a> Parser<'a> { } else { None }; - Ok(ViewColumnDef { name, options }) + let data_type = if dialect_of!(self is ClickHouseDialect) { + Some(self.parse_data_type()?) + } else { + None + }; + Ok(ViewColumnDef { + name, + data_type, + options, + }) } /// Parse a parenthesized comma-separated list of unqualified, possibly quoted identifiers diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index 179755e0c..6b765a359 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -261,10 +261,12 @@ fn parse_create_view_with_options() { vec![ ViewColumnDef { name: Ident::new("name"), + data_type: None, options: None, }, ViewColumnDef { name: Ident::new("age"), + data_type: None, options: Some(vec![SqlOption { name: Ident::new("description"), value: Expr::Value(Value::DoubleQuotedString("field age".to_string())), diff --git a/tests/sqlparser_clickhouse.rs b/tests/sqlparser_clickhouse.rs index 7150a9489..a693936bc 100644 --- a/tests/sqlparser_clickhouse.rs +++ b/tests/sqlparser_clickhouse.rs @@ -220,6 +220,47 @@ fn parse_create_table() { ); } +#[test] +fn parse_create_view_with_fields_data_types() { + match clickhouse().verified_stmt(r#"CREATE VIEW v (i "int", f "String") AS SELECT * FROM t"#) { + Statement::CreateView { name, columns, .. } => { + assert_eq!(name, ObjectName(vec!["v".into()])); + assert_eq!( + columns, + vec![ + ViewColumnDef { + name: "i".into(), + data_type: Some(DataType::Custom( + ObjectName(vec![Ident { + value: "int".into(), + quote_style: Some('"') + }]), + vec![] + )), + options: None + }, + ViewColumnDef { + name: "f".into(), + data_type: Some(DataType::Custom( + ObjectName(vec![Ident { + value: "String".into(), + quote_style: Some('"') + }]), + vec![] + )), + options: None + }, + ] + ); + } + _ => unreachable!(), + } + + clickhouse() + .parse_sql_statements(r#"CREATE VIEW v (i, f) AS SELECT * FROM t"#) + .expect_err("CREATE VIEW with fields and without data types should be invalid"); +} + #[test] fn parse_double_equal() { clickhouse().one_statement_parses_to( diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index f8b7d0265..e8fc67e51 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -6316,6 +6316,7 @@ fn parse_create_view_with_columns() { .into_iter() .map(|name| ViewColumnDef { name, + data_type: None, options: None }) .collect::>()