mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-09-27 07:59:11 +00:00
Add support of MATERIALIZED/ALIAS/EPHERMERAL default column options for ClickHouse (#1348)
This commit is contained in:
parent
b27abf00e2
commit
390d4d3554
4 changed files with 137 additions and 0 deletions
|
@ -923,6 +923,18 @@ pub enum ColumnOption {
|
||||||
NotNull,
|
NotNull,
|
||||||
/// `DEFAULT <restricted-expr>`
|
/// `DEFAULT <restricted-expr>`
|
||||||
Default(Expr),
|
Default(Expr),
|
||||||
|
|
||||||
|
/// ClickHouse supports `MATERIALIZE`, `EPHEMERAL` and `ALIAS` expr to generate default values.
|
||||||
|
/// Syntax: `b INT MATERIALIZE (a + 1)`
|
||||||
|
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/create/table#default_values)
|
||||||
|
|
||||||
|
/// `MATERIALIZE <expr>`
|
||||||
|
Materialized(Expr),
|
||||||
|
/// `EPHEMERAL [<expr>]`
|
||||||
|
Ephemeral(Option<Expr>),
|
||||||
|
/// `ALIAS <expr>`
|
||||||
|
Alias(Expr),
|
||||||
|
|
||||||
/// `{ PRIMARY KEY | UNIQUE } [<constraint_characteristics>]`
|
/// `{ PRIMARY KEY | UNIQUE } [<constraint_characteristics>]`
|
||||||
Unique {
|
Unique {
|
||||||
is_primary: bool,
|
is_primary: bool,
|
||||||
|
@ -978,6 +990,15 @@ impl fmt::Display for ColumnOption {
|
||||||
Null => write!(f, "NULL"),
|
Null => write!(f, "NULL"),
|
||||||
NotNull => write!(f, "NOT NULL"),
|
NotNull => write!(f, "NOT NULL"),
|
||||||
Default(expr) => write!(f, "DEFAULT {expr}"),
|
Default(expr) => write!(f, "DEFAULT {expr}"),
|
||||||
|
Materialized(expr) => write!(f, "MATERIALIZED {expr}"),
|
||||||
|
Ephemeral(expr) => {
|
||||||
|
if let Some(e) = expr {
|
||||||
|
write!(f, "EPHEMERAL {e}")
|
||||||
|
} else {
|
||||||
|
write!(f, "EPHEMERAL")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Alias(expr) => write!(f, "ALIAS {expr}"),
|
||||||
Unique {
|
Unique {
|
||||||
is_primary,
|
is_primary,
|
||||||
characteristics,
|
characteristics,
|
||||||
|
|
|
@ -77,6 +77,7 @@ define_keywords!(
|
||||||
AFTER,
|
AFTER,
|
||||||
AGAINST,
|
AGAINST,
|
||||||
AGGREGATION,
|
AGGREGATION,
|
||||||
|
ALIAS,
|
||||||
ALL,
|
ALL,
|
||||||
ALLOCATE,
|
ALLOCATE,
|
||||||
ALTER,
|
ALTER,
|
||||||
|
@ -267,6 +268,7 @@ define_keywords!(
|
||||||
ENFORCED,
|
ENFORCED,
|
||||||
ENGINE,
|
ENGINE,
|
||||||
ENUM,
|
ENUM,
|
||||||
|
EPHEMERAL,
|
||||||
EPOCH,
|
EPOCH,
|
||||||
EQUALS,
|
EQUALS,
|
||||||
ERROR,
|
ERROR,
|
||||||
|
|
|
@ -5748,6 +5748,24 @@ impl<'a> Parser<'a> {
|
||||||
Ok(Some(ColumnOption::Null))
|
Ok(Some(ColumnOption::Null))
|
||||||
} else if self.parse_keyword(Keyword::DEFAULT) {
|
} else if self.parse_keyword(Keyword::DEFAULT) {
|
||||||
Ok(Some(ColumnOption::Default(self.parse_expr()?)))
|
Ok(Some(ColumnOption::Default(self.parse_expr()?)))
|
||||||
|
} else if dialect_of!(self is ClickHouseDialect| GenericDialect)
|
||||||
|
&& self.parse_keyword(Keyword::MATERIALIZED)
|
||||||
|
{
|
||||||
|
Ok(Some(ColumnOption::Materialized(self.parse_expr()?)))
|
||||||
|
} else if dialect_of!(self is ClickHouseDialect| GenericDialect)
|
||||||
|
&& self.parse_keyword(Keyword::ALIAS)
|
||||||
|
{
|
||||||
|
Ok(Some(ColumnOption::Alias(self.parse_expr()?)))
|
||||||
|
} else if dialect_of!(self is ClickHouseDialect| GenericDialect)
|
||||||
|
&& self.parse_keyword(Keyword::EPHEMERAL)
|
||||||
|
{
|
||||||
|
// The expression is optional for the EPHEMERAL syntax, so we need to check
|
||||||
|
// if the column definition has remaining tokens before parsing the expression.
|
||||||
|
if matches!(self.peek_token().token, Token::Comma | Token::RParen) {
|
||||||
|
Ok(Some(ColumnOption::Ephemeral(None)))
|
||||||
|
} else {
|
||||||
|
Ok(Some(ColumnOption::Ephemeral(Some(self.parse_expr()?))))
|
||||||
|
}
|
||||||
} else if self.parse_keywords(&[Keyword::PRIMARY, Keyword::KEY]) {
|
} else if self.parse_keywords(&[Keyword::PRIMARY, Keyword::KEY]) {
|
||||||
let characteristics = self.parse_constraint_characteristics()?;
|
let characteristics = self.parse_constraint_characteristics()?;
|
||||||
Ok(Some(ColumnOption::Unique {
|
Ok(Some(ColumnOption::Unique {
|
||||||
|
|
|
@ -493,6 +493,102 @@ fn parse_create_table_with_primary_key() {
|
||||||
.expect_err("ORDER BY supports one expression with tuple");
|
.expect_err("ORDER BY supports one expression with tuple");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_create_table_with_variant_default_expressions() {
|
||||||
|
let sql = concat!(
|
||||||
|
"CREATE TABLE table (",
|
||||||
|
"a DATETIME MATERIALIZED now(),",
|
||||||
|
" b DATETIME EPHEMERAL now(),",
|
||||||
|
" c DATETIME EPHEMERAL,",
|
||||||
|
" d STRING ALIAS toString(c)",
|
||||||
|
") ENGINE=MergeTree"
|
||||||
|
);
|
||||||
|
match clickhouse_and_generic().verified_stmt(sql) {
|
||||||
|
Statement::CreateTable(CreateTable { columns, .. }) => {
|
||||||
|
assert_eq!(
|
||||||
|
columns,
|
||||||
|
vec![
|
||||||
|
ColumnDef {
|
||||||
|
name: Ident::new("a"),
|
||||||
|
data_type: DataType::Datetime(None),
|
||||||
|
collation: None,
|
||||||
|
options: vec![ColumnOptionDef {
|
||||||
|
name: None,
|
||||||
|
option: ColumnOption::Materialized(Expr::Function(Function {
|
||||||
|
name: ObjectName(vec![Ident::new("now")]),
|
||||||
|
args: FunctionArguments::List(FunctionArgumentList {
|
||||||
|
args: vec![],
|
||||||
|
duplicate_treatment: None,
|
||||||
|
clauses: vec![],
|
||||||
|
}),
|
||||||
|
parameters: FunctionArguments::None,
|
||||||
|
null_treatment: None,
|
||||||
|
filter: None,
|
||||||
|
over: None,
|
||||||
|
within_group: vec![],
|
||||||
|
}))
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
ColumnDef {
|
||||||
|
name: Ident::new("b"),
|
||||||
|
data_type: DataType::Datetime(None),
|
||||||
|
collation: None,
|
||||||
|
options: vec![ColumnOptionDef {
|
||||||
|
name: None,
|
||||||
|
option: ColumnOption::Ephemeral(Some(Expr::Function(Function {
|
||||||
|
name: ObjectName(vec![Ident::new("now")]),
|
||||||
|
args: FunctionArguments::List(FunctionArgumentList {
|
||||||
|
args: vec![],
|
||||||
|
duplicate_treatment: None,
|
||||||
|
clauses: vec![],
|
||||||
|
}),
|
||||||
|
parameters: FunctionArguments::None,
|
||||||
|
null_treatment: None,
|
||||||
|
filter: None,
|
||||||
|
over: None,
|
||||||
|
within_group: vec![],
|
||||||
|
})))
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
ColumnDef {
|
||||||
|
name: Ident::new("c"),
|
||||||
|
data_type: DataType::Datetime(None),
|
||||||
|
collation: None,
|
||||||
|
options: vec![ColumnOptionDef {
|
||||||
|
name: None,
|
||||||
|
option: ColumnOption::Ephemeral(None)
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
ColumnDef {
|
||||||
|
name: Ident::new("d"),
|
||||||
|
data_type: DataType::String(None),
|
||||||
|
collation: None,
|
||||||
|
options: vec![ColumnOptionDef {
|
||||||
|
name: None,
|
||||||
|
option: ColumnOption::Alias(Expr::Function(Function {
|
||||||
|
name: ObjectName(vec![Ident::new("toString")]),
|
||||||
|
args: FunctionArguments::List(FunctionArgumentList {
|
||||||
|
args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(
|
||||||
|
Identifier(Ident::new("c"))
|
||||||
|
))],
|
||||||
|
duplicate_treatment: None,
|
||||||
|
clauses: vec![],
|
||||||
|
}),
|
||||||
|
parameters: FunctionArguments::None,
|
||||||
|
null_treatment: None,
|
||||||
|
filter: None,
|
||||||
|
over: None,
|
||||||
|
within_group: vec![],
|
||||||
|
}))
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_create_view_with_fields_data_types() {
|
fn parse_create_view_with_fields_data_types() {
|
||||||
match clickhouse().verified_stmt(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"#) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue