Add support for generated columns skipping 'GENERATED ALWAYS' keywords (#1058)

This commit is contained in:
Thomas Kluyver 2023-12-19 20:42:25 +00:00 committed by GitHub
parent d0fce121ef
commit da2296e6d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 1 deletions

View file

@ -600,6 +600,8 @@ pub enum ColumnOption {
sequence_options: Option<Vec<SequenceOptions>>,
generation_expr: Option<Expr>,
generation_expr_mode: Option<GeneratedExpressionMode>,
/// false if 'GENERATED ALWAYS' is skipped (option starts with AS)
generated_keyword: bool,
},
}
@ -641,6 +643,7 @@ impl fmt::Display for ColumnOption {
sequence_options,
generation_expr,
generation_expr_mode,
generated_keyword,
} => {
if let Some(expr) = generation_expr {
let modifier = match generation_expr_mode {
@ -648,7 +651,11 @@ impl fmt::Display for ColumnOption {
Some(GeneratedExpressionMode::Virtual) => " VIRTUAL",
Some(GeneratedExpressionMode::Stored) => " STORED",
};
if *generated_keyword {
write!(f, "GENERATED ALWAYS AS ({expr}){modifier}")?;
} else {
write!(f, "AS ({expr}){modifier}")?;
}
Ok(())
} else {
// Like Postgres - generated from sequence

View file

@ -4288,6 +4288,10 @@ impl<'a> Parser<'a> {
Ok(Some(ColumnOption::OnUpdate(expr)))
} else if self.parse_keyword(Keyword::GENERATED) {
self.parse_optional_column_option_generated()
} else if self.parse_keyword(Keyword::AS)
&& dialect_of!(self is MySqlDialect | SQLiteDialect | DuckDbDialect | GenericDialect)
{
self.parse_optional_column_option_as()
} else {
Ok(None)
}
@ -4306,6 +4310,7 @@ impl<'a> Parser<'a> {
sequence_options: Some(sequence_options),
generation_expr: None,
generation_expr_mode: None,
generated_keyword: true,
}))
} else if self.parse_keywords(&[
Keyword::BY,
@ -4323,6 +4328,7 @@ impl<'a> Parser<'a> {
sequence_options: Some(sequence_options),
generation_expr: None,
generation_expr_mode: None,
generated_keyword: true,
}))
} else if self.parse_keywords(&[Keyword::ALWAYS, Keyword::AS]) {
if self.expect_token(&Token::LParen).is_ok() {
@ -4347,6 +4353,7 @@ impl<'a> Parser<'a> {
sequence_options: None,
generation_expr: Some(expr),
generation_expr_mode: expr_mode,
generated_keyword: true,
}))
} else {
Ok(None)
@ -4356,6 +4363,32 @@ impl<'a> Parser<'a> {
}
}
fn parse_optional_column_option_as(&mut self) -> Result<Option<ColumnOption>, ParserError> {
// Some DBs allow 'AS (expr)', shorthand for GENERATED ALWAYS AS
self.expect_token(&Token::LParen)?;
let expr = self.parse_expr()?;
self.expect_token(&Token::RParen)?;
let (gen_as, expr_mode) = if self.parse_keywords(&[Keyword::STORED]) {
(
GeneratedAs::ExpStored,
Some(GeneratedExpressionMode::Stored),
)
} else if self.parse_keywords(&[Keyword::VIRTUAL]) {
(GeneratedAs::Always, Some(GeneratedExpressionMode::Virtual))
} else {
(GeneratedAs::Always, None)
};
Ok(Some(ColumnOption::Generated {
generated_as: gen_as,
sequence_options: None,
generation_expr: Some(expr),
generation_expr_mode: expr_mode,
generated_keyword: false,
}))
}
pub fn parse_referential_action(&mut self) -> Result<ReferentialAction, ParserError> {
if self.parse_keyword(Keyword::RESTRICT) {
Ok(ReferentialAction::Restrict)

View file

@ -517,6 +517,10 @@ fn parse_create_table_gencol() {
let sql_stored = "CREATE TABLE t1 (a INT, b INT GENERATED ALWAYS AS (a * 2) STORED)";
mysql_and_generic().verified_stmt(sql_stored);
mysql_and_generic().verified_stmt("CREATE TABLE t1 (a INT, b INT AS (a * 2))");
mysql_and_generic().verified_stmt("CREATE TABLE t1 (a INT, b INT AS (a * 2) VIRTUAL)");
mysql_and_generic().verified_stmt("CREATE TABLE t1 (a INT, b INT AS (a * 2) STORED)");
}
#[test]

View file

@ -215,6 +215,10 @@ fn parse_create_table_gencol() {
let sql_stored = "CREATE TABLE t1 (a INT, b INT GENERATED ALWAYS AS (a * 2) STORED)";
sqlite_and_generic().verified_stmt(sql_stored);
sqlite_and_generic().verified_stmt("CREATE TABLE t1 (a INT, b INT AS (a * 2))");
sqlite_and_generic().verified_stmt("CREATE TABLE t1 (a INT, b INT AS (a * 2) VIRTUAL)");
sqlite_and_generic().verified_stmt("CREATE TABLE t1 (a INT, b INT AS (a * 2) STORED)");
}
#[test]