Skip to content

Commit d69b875

Browse files
authored
ClickHouse CREATE TABLE Fixes: add ORDER BY and fix clause ordering (apache#824)
* Fix ClickHouse (add ORDER BY) * Improve test case
1 parent 1cf913e commit d69b875

File tree

4 files changed

+59
-11
lines changed

4 files changed

+59
-11
lines changed

src/ast/helpers/stmt_create_table.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
88
use sqlparser_derive::{Visit, VisitMut};
99

1010
use crate::ast::{
11-
ColumnDef, FileFormat, HiveDistributionStyle, HiveFormat, ObjectName, OnCommit, Query,
11+
ColumnDef, FileFormat, HiveDistributionStyle, HiveFormat, Ident, ObjectName, OnCommit, Query,
1212
SqlOption, Statement, TableConstraint,
1313
};
1414
use crate::parser::ParserError;
@@ -69,6 +69,7 @@ pub struct CreateTableBuilder {
6969
pub collation: Option<String>,
7070
pub on_commit: Option<OnCommit>,
7171
pub on_cluster: Option<String>,
72+
pub order_by: Option<Vec<Ident>>,
7273
}
7374

7475
impl CreateTableBuilder {
@@ -98,6 +99,7 @@ impl CreateTableBuilder {
9899
collation: None,
99100
on_commit: None,
100101
on_cluster: None,
102+
order_by: None,
101103
}
102104
}
103105
pub fn or_replace(mut self, or_replace: bool) -> Self {
@@ -213,6 +215,11 @@ impl CreateTableBuilder {
213215
self
214216
}
215217

218+
pub fn order_by(mut self, order_by: Option<Vec<Ident>>) -> Self {
219+
self.order_by = order_by;
220+
self
221+
}
222+
216223
pub fn build(self) -> Statement {
217224
Statement::CreateTable {
218225
or_replace: self.or_replace,
@@ -239,6 +246,7 @@ impl CreateTableBuilder {
239246
collation: self.collation,
240247
on_commit: self.on_commit,
241248
on_cluster: self.on_cluster,
249+
order_by: self.order_by,
242250
}
243251
}
244252
}
@@ -275,6 +283,7 @@ impl TryFrom<Statement> for CreateTableBuilder {
275283
collation,
276284
on_commit,
277285
on_cluster,
286+
order_by,
278287
} => Ok(Self {
279288
or_replace,
280289
temporary,
@@ -300,6 +309,7 @@ impl TryFrom<Statement> for CreateTableBuilder {
300309
collation,
301310
on_commit,
302311
on_cluster,
312+
order_by,
303313
}),
304314
_ => Err(ParserError::ParserError(format!(
305315
"Expected create table statement, but received: {stmt}"

src/ast/mod.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,9 +1254,13 @@ pub enum Statement {
12541254
default_charset: Option<String>,
12551255
collation: Option<String>,
12561256
on_commit: Option<OnCommit>,
1257-
/// Click house "ON CLUSTER" clause:
1257+
/// ClickHouse "ON CLUSTER" clause:
12581258
/// <https://clickhouse.com/docs/en/sql-reference/distributed-ddl/>
12591259
on_cluster: Option<String>,
1260+
/// ClickHouse "ORDER BY " clause. Note that omitted ORDER BY is different
1261+
/// than empty (represented as ()), the latter meaning "no sorting".
1262+
/// <https://clickhouse.com/docs/en/sql-reference/statements/create/table/>
1263+
order_by: Option<Vec<Ident>>,
12601264
},
12611265
/// SQLite's `CREATE VIRTUAL TABLE .. USING <module_name> (<module_args>)`
12621266
CreateVirtualTable {
@@ -2053,6 +2057,7 @@ impl fmt::Display for Statement {
20532057
collation,
20542058
on_commit,
20552059
on_cluster,
2060+
order_by,
20562061
} => {
20572062
// We want to allow the following options
20582063
// Empty column list, allowed by PostgreSQL:
@@ -2196,12 +2201,15 @@ impl fmt::Display for Statement {
21962201
if !with_options.is_empty() {
21972202
write!(f, " WITH ({})", display_comma_separated(with_options))?;
21982203
}
2199-
if let Some(query) = query {
2200-
write!(f, " AS {query}")?;
2201-
}
22022204
if let Some(engine) = engine {
22032205
write!(f, " ENGINE={engine}")?;
22042206
}
2207+
if let Some(order_by) = order_by {
2208+
write!(f, " ORDER BY ({})", display_comma_separated(order_by))?;
2209+
}
2210+
if let Some(query) = query {
2211+
write!(f, " AS {query}")?;
2212+
}
22052213
if let Some(default_charset) = default_charset {
22062214
write!(f, " DEFAULT CHARSET={default_charset}")?;
22072215
}

src/parser.rs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3340,12 +3340,6 @@ impl<'a> Parser<'a> {
33403340
// PostgreSQL supports `WITH ( options )`, before `AS`
33413341
let with_options = self.parse_options(Keyword::WITH)?;
33423342
let table_properties = self.parse_options(Keyword::TBLPROPERTIES)?;
3343-
// Parse optional `AS ( query )`
3344-
let query = if self.parse_keyword(Keyword::AS) {
3345-
Some(Box::new(self.parse_query()?))
3346-
} else {
3347-
None
3348-
};
33493343

33503344
let engine = if self.parse_keyword(Keyword::ENGINE) {
33513345
self.expect_token(&Token::Eq)?;
@@ -3358,6 +3352,29 @@ impl<'a> Parser<'a> {
33583352
None
33593353
};
33603354

3355+
let order_by = if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) {
3356+
if self.consume_token(&Token::LParen) {
3357+
let columns = if self.peek_token() != Token::RParen {
3358+
self.parse_comma_separated(Parser::parse_identifier)?
3359+
} else {
3360+
vec![]
3361+
};
3362+
self.expect_token(&Token::RParen)?;
3363+
Some(columns)
3364+
} else {
3365+
Some(vec![self.parse_identifier()?])
3366+
}
3367+
} else {
3368+
None
3369+
};
3370+
3371+
// Parse optional `AS ( query )`
3372+
let query = if self.parse_keyword(Keyword::AS) {
3373+
Some(Box::new(self.parse_query()?))
3374+
} else {
3375+
None
3376+
};
3377+
33613378
let default_charset = if self.parse_keywords(&[Keyword::DEFAULT, Keyword::CHARSET]) {
33623379
self.expect_token(&Token::Eq)?;
33633380
let next_token = self.next_token();
@@ -3414,6 +3431,7 @@ impl<'a> Parser<'a> {
34143431
.like(like)
34153432
.clone_clause(clone)
34163433
.engine(engine)
3434+
.order_by(order_by)
34173435
.default_charset(default_charset)
34183436
.collation(collation)
34193437
.on_commit(on_commit)

tests/sqlparser_clickhouse.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,18 @@ fn parse_similar_to() {
317317
chk(true);
318318
}
319319

320+
#[test]
321+
fn parse_create_table() {
322+
clickhouse().verified_stmt(r#"CREATE TABLE "x" ("a" "int") ENGINE=MergeTree ORDER BY ("x")"#);
323+
clickhouse().one_statement_parses_to(
324+
r#"CREATE TABLE "x" ("a" "int") ENGINE=MergeTree ORDER BY "x""#,
325+
r#"CREATE TABLE "x" ("a" "int") ENGINE=MergeTree ORDER BY ("x")"#,
326+
);
327+
clickhouse().verified_stmt(
328+
r#"CREATE TABLE "x" ("a" "int") ENGINE=MergeTree ORDER BY ("x") AS SELECT * FROM "t" WHERE true"#,
329+
);
330+
}
331+
320332
fn clickhouse() -> TestedDialects {
321333
TestedDialects {
322334
dialects: vec![Box::new(ClickHouseDialect {})],

0 commit comments

Comments
 (0)