mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-09-27 07:59:11 +00:00
ClickHouse CREATE TABLE Fixes: add ORDER BY and fix clause ordering (#824)
* Fix ClickHouse (add ORDER BY) * Improve test case
This commit is contained in:
parent
1cf913e717
commit
d69b875367
4 changed files with 59 additions and 11 deletions
|
@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use sqlparser_derive::{Visit, VisitMut};
|
use sqlparser_derive::{Visit, VisitMut};
|
||||||
|
|
||||||
use crate::ast::{
|
use crate::ast::{
|
||||||
ColumnDef, FileFormat, HiveDistributionStyle, HiveFormat, ObjectName, OnCommit, Query,
|
ColumnDef, FileFormat, HiveDistributionStyle, HiveFormat, Ident, ObjectName, OnCommit, Query,
|
||||||
SqlOption, Statement, TableConstraint,
|
SqlOption, Statement, TableConstraint,
|
||||||
};
|
};
|
||||||
use crate::parser::ParserError;
|
use crate::parser::ParserError;
|
||||||
|
@ -69,6 +69,7 @@ pub struct CreateTableBuilder {
|
||||||
pub collation: Option<String>,
|
pub collation: Option<String>,
|
||||||
pub on_commit: Option<OnCommit>,
|
pub on_commit: Option<OnCommit>,
|
||||||
pub on_cluster: Option<String>,
|
pub on_cluster: Option<String>,
|
||||||
|
pub order_by: Option<Vec<Ident>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreateTableBuilder {
|
impl CreateTableBuilder {
|
||||||
|
@ -98,6 +99,7 @@ impl CreateTableBuilder {
|
||||||
collation: None,
|
collation: None,
|
||||||
on_commit: None,
|
on_commit: None,
|
||||||
on_cluster: None,
|
on_cluster: None,
|
||||||
|
order_by: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn or_replace(mut self, or_replace: bool) -> Self {
|
pub fn or_replace(mut self, or_replace: bool) -> Self {
|
||||||
|
@ -213,6 +215,11 @@ impl CreateTableBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn order_by(mut self, order_by: Option<Vec<Ident>>) -> Self {
|
||||||
|
self.order_by = order_by;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Statement {
|
pub fn build(self) -> Statement {
|
||||||
Statement::CreateTable {
|
Statement::CreateTable {
|
||||||
or_replace: self.or_replace,
|
or_replace: self.or_replace,
|
||||||
|
@ -239,6 +246,7 @@ impl CreateTableBuilder {
|
||||||
collation: self.collation,
|
collation: self.collation,
|
||||||
on_commit: self.on_commit,
|
on_commit: self.on_commit,
|
||||||
on_cluster: self.on_cluster,
|
on_cluster: self.on_cluster,
|
||||||
|
order_by: self.order_by,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -275,6 +283,7 @@ impl TryFrom<Statement> for CreateTableBuilder {
|
||||||
collation,
|
collation,
|
||||||
on_commit,
|
on_commit,
|
||||||
on_cluster,
|
on_cluster,
|
||||||
|
order_by,
|
||||||
} => Ok(Self {
|
} => Ok(Self {
|
||||||
or_replace,
|
or_replace,
|
||||||
temporary,
|
temporary,
|
||||||
|
@ -300,6 +309,7 @@ impl TryFrom<Statement> for CreateTableBuilder {
|
||||||
collation,
|
collation,
|
||||||
on_commit,
|
on_commit,
|
||||||
on_cluster,
|
on_cluster,
|
||||||
|
order_by,
|
||||||
}),
|
}),
|
||||||
_ => Err(ParserError::ParserError(format!(
|
_ => Err(ParserError::ParserError(format!(
|
||||||
"Expected create table statement, but received: {stmt}"
|
"Expected create table statement, but received: {stmt}"
|
||||||
|
|
|
@ -1254,9 +1254,13 @@ pub enum Statement {
|
||||||
default_charset: Option<String>,
|
default_charset: Option<String>,
|
||||||
collation: Option<String>,
|
collation: Option<String>,
|
||||||
on_commit: Option<OnCommit>,
|
on_commit: Option<OnCommit>,
|
||||||
/// Click house "ON CLUSTER" clause:
|
/// ClickHouse "ON CLUSTER" clause:
|
||||||
/// <https://clickhouse.com/docs/en/sql-reference/distributed-ddl/>
|
/// <https://clickhouse.com/docs/en/sql-reference/distributed-ddl/>
|
||||||
on_cluster: Option<String>,
|
on_cluster: Option<String>,
|
||||||
|
/// ClickHouse "ORDER BY " clause. Note that omitted ORDER BY is different
|
||||||
|
/// than empty (represented as ()), the latter meaning "no sorting".
|
||||||
|
/// <https://clickhouse.com/docs/en/sql-reference/statements/create/table/>
|
||||||
|
order_by: Option<Vec<Ident>>,
|
||||||
},
|
},
|
||||||
/// SQLite's `CREATE VIRTUAL TABLE .. USING <module_name> (<module_args>)`
|
/// SQLite's `CREATE VIRTUAL TABLE .. USING <module_name> (<module_args>)`
|
||||||
CreateVirtualTable {
|
CreateVirtualTable {
|
||||||
|
@ -2053,6 +2057,7 @@ impl fmt::Display for Statement {
|
||||||
collation,
|
collation,
|
||||||
on_commit,
|
on_commit,
|
||||||
on_cluster,
|
on_cluster,
|
||||||
|
order_by,
|
||||||
} => {
|
} => {
|
||||||
// We want to allow the following options
|
// We want to allow the following options
|
||||||
// Empty column list, allowed by PostgreSQL:
|
// Empty column list, allowed by PostgreSQL:
|
||||||
|
@ -2196,12 +2201,15 @@ impl fmt::Display for Statement {
|
||||||
if !with_options.is_empty() {
|
if !with_options.is_empty() {
|
||||||
write!(f, " WITH ({})", display_comma_separated(with_options))?;
|
write!(f, " WITH ({})", display_comma_separated(with_options))?;
|
||||||
}
|
}
|
||||||
if let Some(query) = query {
|
|
||||||
write!(f, " AS {query}")?;
|
|
||||||
}
|
|
||||||
if let Some(engine) = engine {
|
if let Some(engine) = engine {
|
||||||
write!(f, " 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 {
|
if let Some(default_charset) = default_charset {
|
||||||
write!(f, " DEFAULT CHARSET={default_charset}")?;
|
write!(f, " DEFAULT CHARSET={default_charset}")?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3340,12 +3340,6 @@ impl<'a> Parser<'a> {
|
||||||
// PostgreSQL supports `WITH ( options )`, before `AS`
|
// PostgreSQL supports `WITH ( options )`, before `AS`
|
||||||
let with_options = self.parse_options(Keyword::WITH)?;
|
let with_options = self.parse_options(Keyword::WITH)?;
|
||||||
let table_properties = self.parse_options(Keyword::TBLPROPERTIES)?;
|
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) {
|
let engine = if self.parse_keyword(Keyword::ENGINE) {
|
||||||
self.expect_token(&Token::Eq)?;
|
self.expect_token(&Token::Eq)?;
|
||||||
|
@ -3358,6 +3352,29 @@ impl<'a> Parser<'a> {
|
||||||
None
|
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]) {
|
let default_charset = if self.parse_keywords(&[Keyword::DEFAULT, Keyword::CHARSET]) {
|
||||||
self.expect_token(&Token::Eq)?;
|
self.expect_token(&Token::Eq)?;
|
||||||
let next_token = self.next_token();
|
let next_token = self.next_token();
|
||||||
|
@ -3414,6 +3431,7 @@ impl<'a> Parser<'a> {
|
||||||
.like(like)
|
.like(like)
|
||||||
.clone_clause(clone)
|
.clone_clause(clone)
|
||||||
.engine(engine)
|
.engine(engine)
|
||||||
|
.order_by(order_by)
|
||||||
.default_charset(default_charset)
|
.default_charset(default_charset)
|
||||||
.collation(collation)
|
.collation(collation)
|
||||||
.on_commit(on_commit)
|
.on_commit(on_commit)
|
||||||
|
|
|
@ -317,6 +317,18 @@ fn parse_similar_to() {
|
||||||
chk(true);
|
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 {
|
fn clickhouse() -> TestedDialects {
|
||||||
TestedDialects {
|
TestedDialects {
|
||||||
dialects: vec![Box::new(ClickHouseDialect {})],
|
dialects: vec![Box::new(ClickHouseDialect {})],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue