From 07daab104b334e5a3a7966f660354f3d72b35810 Mon Sep 17 00:00:00 2001 From: "aleksei.p" Date: Tue, 28 May 2024 11:54:07 +0200 Subject: [PATCH 1/2] [1285] ClickHouse: create view with fields and data types --- src/ast/ddl.rs | 7 ++++++- src/parser/mod.rs | 12 +++++++++++- tests/sqlparser_bigquery.rs | 2 ++ tests/sqlparser_clickhouse.rs | 34 ++++++++++++++++++++++++++++++++++ tests/sqlparser_common.rs | 1 + 5 files changed, 54 insertions(+), 2 deletions(-) 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..6bcdfff57 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -7122,7 +7122,17 @@ impl<'a> Parser<'a> { } else { None }; - Ok(ViewColumnDef { name, options }) + let data_type = if dialect_of!(self is ClickHouseDialect) { + let (data_type, _) = self.parse_data_type_helper()?; + Some(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..fbbe47154 100644 --- a/tests/sqlparser_clickhouse.rs +++ b/tests/sqlparser_clickhouse.rs @@ -220,6 +220,40 @@ fn parse_create_table() { ); } +#[test] +fn parse_create_view_with_fields_data_types() { + match clickhouse().one_statement_parses_to( + r#"CREATE VIEW v (i "int", f String) AS SELECT * FROM t"#, + 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::String(None)), + options: None + }, + ] + ); + } + _ => unreachable!(), + } +} + #[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::>() From f3ab53a23c658a0f4b867775342105169d371797 Mon Sep 17 00:00:00 2001 From: "aleksei.p" Date: Tue, 28 May 2024 12:41:52 +0200 Subject: [PATCH 2/2] update --- src/parser/mod.rs | 3 +-- tests/sqlparser_clickhouse.rs | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 6bcdfff57..d65987126 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -7123,8 +7123,7 @@ impl<'a> Parser<'a> { None }; let data_type = if dialect_of!(self is ClickHouseDialect) { - let (data_type, _) = self.parse_data_type_helper()?; - Some(data_type) + Some(self.parse_data_type()?) } else { None }; diff --git a/tests/sqlparser_clickhouse.rs b/tests/sqlparser_clickhouse.rs index fbbe47154..a693936bc 100644 --- a/tests/sqlparser_clickhouse.rs +++ b/tests/sqlparser_clickhouse.rs @@ -222,10 +222,7 @@ fn parse_create_table() { #[test] fn parse_create_view_with_fields_data_types() { - match clickhouse().one_statement_parses_to( - r#"CREATE VIEW v (i "int", f String) AS SELECT * FROM t"#, - r#"CREATE VIEW v (i "int", f STRING) AS SELECT * FROM t"#, - ) { + 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!( @@ -244,7 +241,13 @@ fn parse_create_view_with_fields_data_types() { }, ViewColumnDef { name: "f".into(), - data_type: Some(DataType::String(None)), + data_type: Some(DataType::Custom( + ObjectName(vec![Ident { + value: "String".into(), + quote_style: Some('"') + }]), + vec![] + )), options: None }, ] @@ -252,6 +255,10 @@ fn parse_create_view_with_fields_data_types() { } _ => 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]