From d69b875367385cfc1d18296f390674cfc3729e4a Mon Sep 17 00:00:00 2001 From: Ankur Goyal Date: Mon, 6 Mar 2023 06:55:55 -0800 Subject: [PATCH] ClickHouse CREATE TABLE Fixes: add ORDER BY and fix clause ordering (#824) * Fix ClickHouse (add ORDER BY) * Improve test case --- src/ast/helpers/stmt_create_table.rs | 12 ++++++++++- src/ast/mod.rs | 16 +++++++++++---- src/parser.rs | 30 ++++++++++++++++++++++------ tests/sqlparser_clickhouse.rs | 12 +++++++++++ 4 files changed, 59 insertions(+), 11 deletions(-) diff --git a/src/ast/helpers/stmt_create_table.rs b/src/ast/helpers/stmt_create_table.rs index 60307d4ef..cb1ad45e7 100644 --- a/src/ast/helpers/stmt_create_table.rs +++ b/src/ast/helpers/stmt_create_table.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use sqlparser_derive::{Visit, VisitMut}; use crate::ast::{ - ColumnDef, FileFormat, HiveDistributionStyle, HiveFormat, ObjectName, OnCommit, Query, + ColumnDef, FileFormat, HiveDistributionStyle, HiveFormat, Ident, ObjectName, OnCommit, Query, SqlOption, Statement, TableConstraint, }; use crate::parser::ParserError; @@ -69,6 +69,7 @@ pub struct CreateTableBuilder { pub collation: Option, pub on_commit: Option, pub on_cluster: Option, + pub order_by: Option>, } impl CreateTableBuilder { @@ -98,6 +99,7 @@ impl CreateTableBuilder { collation: None, on_commit: None, on_cluster: None, + order_by: None, } } pub fn or_replace(mut self, or_replace: bool) -> Self { @@ -213,6 +215,11 @@ impl CreateTableBuilder { self } + pub fn order_by(mut self, order_by: Option>) -> Self { + self.order_by = order_by; + self + } + pub fn build(self) -> Statement { Statement::CreateTable { or_replace: self.or_replace, @@ -239,6 +246,7 @@ impl CreateTableBuilder { collation: self.collation, on_commit: self.on_commit, on_cluster: self.on_cluster, + order_by: self.order_by, } } } @@ -275,6 +283,7 @@ impl TryFrom for CreateTableBuilder { collation, on_commit, on_cluster, + order_by, } => Ok(Self { or_replace, temporary, @@ -300,6 +309,7 @@ impl TryFrom for CreateTableBuilder { collation, on_commit, on_cluster, + order_by, }), _ => Err(ParserError::ParserError(format!( "Expected create table statement, but received: {stmt}" diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 497b7ac32..0d2303cad 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1254,9 +1254,13 @@ pub enum Statement { default_charset: Option, collation: Option, on_commit: Option, - /// Click house "ON CLUSTER" clause: + /// ClickHouse "ON CLUSTER" clause: /// on_cluster: Option, + /// ClickHouse "ORDER BY " clause. Note that omitted ORDER BY is different + /// than empty (represented as ()), the latter meaning "no sorting". + /// + order_by: Option>, }, /// SQLite's `CREATE VIRTUAL TABLE .. USING ()` CreateVirtualTable { @@ -2053,6 +2057,7 @@ impl fmt::Display for Statement { collation, on_commit, on_cluster, + order_by, } => { // We want to allow the following options // Empty column list, allowed by PostgreSQL: @@ -2196,12 +2201,15 @@ impl fmt::Display for Statement { if !with_options.is_empty() { write!(f, " WITH ({})", display_comma_separated(with_options))?; } - if let Some(query) = query { - write!(f, " AS {query}")?; - } if let Some(engine) = engine { write!(f, " ENGINE={engine}")?; } + if let Some(order_by) = order_by { + write!(f, " ORDER BY ({})", display_comma_separated(order_by))?; + } + if let Some(query) = query { + write!(f, " AS {query}")?; + } if let Some(default_charset) = default_charset { write!(f, " DEFAULT CHARSET={default_charset}")?; } diff --git a/src/parser.rs b/src/parser.rs index 526cf58d4..f2009d31a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3340,12 +3340,6 @@ impl<'a> Parser<'a> { // PostgreSQL supports `WITH ( options )`, before `AS` let with_options = self.parse_options(Keyword::WITH)?; let table_properties = self.parse_options(Keyword::TBLPROPERTIES)?; - // Parse optional `AS ( query )` - let query = if self.parse_keyword(Keyword::AS) { - Some(Box::new(self.parse_query()?)) - } else { - None - }; let engine = if self.parse_keyword(Keyword::ENGINE) { self.expect_token(&Token::Eq)?; @@ -3358,6 +3352,29 @@ impl<'a> Parser<'a> { None }; + let order_by = if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) { + if self.consume_token(&Token::LParen) { + let columns = if self.peek_token() != Token::RParen { + self.parse_comma_separated(Parser::parse_identifier)? + } else { + vec![] + }; + self.expect_token(&Token::RParen)?; + Some(columns) + } else { + Some(vec![self.parse_identifier()?]) + } + } else { + None + }; + + // Parse optional `AS ( query )` + let query = if self.parse_keyword(Keyword::AS) { + Some(Box::new(self.parse_query()?)) + } else { + None + }; + let default_charset = if self.parse_keywords(&[Keyword::DEFAULT, Keyword::CHARSET]) { self.expect_token(&Token::Eq)?; let next_token = self.next_token(); @@ -3414,6 +3431,7 @@ impl<'a> Parser<'a> { .like(like) .clone_clause(clone) .engine(engine) + .order_by(order_by) .default_charset(default_charset) .collation(collation) .on_commit(on_commit) diff --git a/tests/sqlparser_clickhouse.rs b/tests/sqlparser_clickhouse.rs index 10e0d9ea5..dfea7f18c 100644 --- a/tests/sqlparser_clickhouse.rs +++ b/tests/sqlparser_clickhouse.rs @@ -317,6 +317,18 @@ fn parse_similar_to() { chk(true); } +#[test] +fn parse_create_table() { + clickhouse().verified_stmt(r#"CREATE TABLE "x" ("a" "int") ENGINE=MergeTree ORDER BY ("x")"#); + clickhouse().one_statement_parses_to( + r#"CREATE TABLE "x" ("a" "int") ENGINE=MergeTree ORDER BY "x""#, + r#"CREATE TABLE "x" ("a" "int") ENGINE=MergeTree ORDER BY ("x")"#, + ); + clickhouse().verified_stmt( + r#"CREATE TABLE "x" ("a" "int") ENGINE=MergeTree ORDER BY ("x") AS SELECT * FROM "t" WHERE true"#, + ); +} + fn clickhouse() -> TestedDialects { TestedDialects { dialects: vec![Box::new(ClickHouseDialect {})],