Implement some MySQL specific syntax and extend the UPDATE statement (#375)

* * implement the ON DUPLICATE KEY syntax for MySQL in an INSERT statement

* * add MySQL to the cli example
* remove the dialect check for the ON DUPLICATE KEY insert to support
  custom dialects and unwrap some missing results

* * use the Assignment DataType for the ON DUPLICATE KEY UPDATE

* * add support for table aliases in an UPDATE statement
* add support for JOINS in an UPDATE statement (for MySQL)

* * implement the MySQL ALTER TABLE CHANGE COLUMN syntax

* * fix the formatting of the else * rename the parse_identifiers_strict
  to parse_identifiers_non_keywords

* Parse SUBSTRING calls that are separated with a comma instead of
keywords

* Fix the linting errors

Co-authored-by: Piotr <piotr.morawski@nc-vision.com>
Co-authored-by: Piotr Morawski <contact@peter-morawski.de>
This commit is contained in:
Andrew Lamb 2021-12-10 14:46:11 -05:00 committed by GitHub
parent 40d67aab87
commit 82eaae1522
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 489 additions and 18 deletions

View file

@ -60,6 +60,13 @@ pub enum AlterTableOperation {
},
/// `RENAME TO <table_name>`
RenameTable { table_name: ObjectName },
// CHANGE [ COLUMN ] <old_name> <new_name> <data_type> [ <options> ]
ChangeColumn {
old_name: Ident,
new_name: Ident,
data_type: DataType,
options: Vec<ColumnOption>,
},
}
impl fmt::Display for AlterTableOperation {
@ -119,6 +126,19 @@ impl fmt::Display for AlterTableOperation {
AlterTableOperation::RenameTable { table_name } => {
write!(f, "RENAME TO {}", table_name)
}
AlterTableOperation::ChangeColumn {
old_name,
new_name,
data_type,
options,
} => {
write!(f, "CHANGE COLUMN {} {} {}", old_name, new_name, data_type)?;
if options.is_empty() {
Ok(())
} else {
write!(f, " {}", display_separated(options, " "))
}
}
}
}
}

View file

@ -650,6 +650,7 @@ pub enum Statement {
after_columns: Vec<Ident>,
/// whether the insert has the table keyword (Hive)
table: bool,
on: Option<OnInsert>,
},
// TODO: Support ROW FORMAT
Directory {
@ -670,7 +671,7 @@ pub enum Statement {
/// UPDATE
Update {
/// TABLE
table_name: ObjectName,
table: TableWithJoins,
/// Column assignments
assignments: Vec<Assignment>,
/// WHERE
@ -990,6 +991,7 @@ impl fmt::Display for Statement {
after_columns,
source,
table,
on,
} => {
if let Some(action) = or {
write!(f, "INSERT OR {} INTO {} ", action, table_name)?;
@ -1013,7 +1015,13 @@ impl fmt::Display for Statement {
if !after_columns.is_empty() {
write!(f, "({}) ", display_comma_separated(after_columns))?;
}
write!(f, "{}", source)
write!(f, "{}", source)?;
if let Some(on) = on {
write!(f, "{}", on)
} else {
Ok(())
}
}
Statement::Copy {
@ -1042,11 +1050,11 @@ impl fmt::Display for Statement {
write!(f, "\n\\.")
}
Statement::Update {
table_name,
table,
assignments,
selection,
} => {
write!(f, "UPDATE {}", table_name)?;
write!(f, "UPDATE {}", table)?;
if !assignments.is_empty() {
write!(f, " SET {}", display_comma_separated(assignments))?;
}
@ -1452,6 +1460,26 @@ impl fmt::Display for Statement {
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[non_exhaustive]
pub enum OnInsert {
/// ON DUPLICATE KEY UPDATE (MySQL when the key already exists, then execute an update instead)
DuplicateKeyUpdate(Vec<Assignment>),
}
impl fmt::Display for OnInsert {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::DuplicateKeyUpdate(expr) => write!(
f,
" ON DUPLICATE KEY UPDATE {}",
display_comma_separated(expr)
),
}
}
}
/// Privileges granted in a GRANT statement or revoked in a REVOKE statement.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@ -1587,13 +1615,13 @@ impl fmt::Display for GrantObjects {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Assignment {
pub id: Ident,
pub id: Vec<Ident>,
pub value: Expr,
}
impl fmt::Display for Assignment {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} = {}", self.id, self.value)
write!(f, "{} = {}", display_separated(&self.id, "."), self.value)
}
}

View file

@ -114,6 +114,7 @@ define_keywords!(
CEIL,
CEILING,
CHAIN,
CHANGE,
CHAR,
CHARACTER,
CHARACTER_LENGTH,
@ -181,6 +182,7 @@ define_keywords!(
DISTRIBUTE,
DOUBLE,
DROP,
DUPLICATE,
DYNAMIC,
EACH,
ELEMENT,
@ -542,6 +544,7 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[Keyword] = &[
Keyword::DISTRIBUTE,
// for MSSQL-specific OUTER APPLY (seems reserved in most dialects)
Keyword::OUTER,
Keyword::SET,
];
/// Can't be used as a column alias, so that `SELECT <expr> alias`

View file

@ -702,11 +702,12 @@ impl<'a> Parser<'a> {
self.expect_token(&Token::LParen)?;
let expr = self.parse_expr()?;
let mut from_expr = None;
let mut to_expr = None;
if self.parse_keyword(Keyword::FROM) {
if self.parse_keyword(Keyword::FROM) || self.consume_token(&Token::Comma) {
from_expr = Some(self.parse_expr()?);
}
if self.parse_keyword(Keyword::FOR) {
let mut to_expr = None;
if self.parse_keyword(Keyword::FOR) || self.consume_token(&Token::Comma) {
to_expr = Some(self.parse_expr()?);
}
self.expect_token(&Token::RParen)?;
@ -1958,6 +1959,22 @@ impl<'a> Parser<'a> {
old_partitions: before,
new_partitions: renames,
}
} else if self.parse_keyword(Keyword::CHANGE) {
let _ = self.parse_keyword(Keyword::COLUMN);
let old_name = self.parse_identifier()?;
let new_name = self.parse_identifier()?;
let data_type = self.parse_data_type()?;
let mut options = vec![];
while let Some(option) = self.parse_optional_column_option()? {
options.push(option);
}
AlterTableOperation::ChangeColumn {
old_name,
new_name,
data_type,
options,
}
} else {
return self.expected(
"ADD, RENAME, PARTITION or DROP after ALTER TABLE",
@ -2234,16 +2251,41 @@ impl<'a> Parser<'a> {
Ok(ObjectName(idents))
}
/// Parse identifiers strictly i.e. don't parse keywords
pub fn parse_identifiers_non_keywords(&mut self) -> Result<Vec<Ident>, ParserError> {
let mut idents = vec![];
loop {
match self.peek_token() {
Token::Word(w) => {
if w.keyword != Keyword::NoKeyword {
break;
}
idents.push(w.to_ident());
}
Token::EOF | Token::Eq => break,
_ => {}
}
self.next_token();
}
Ok(idents)
}
/// Parse identifiers
pub fn parse_identifiers(&mut self) -> Result<Vec<Ident>, ParserError> {
let mut idents = vec![];
loop {
match self.next_token() {
Token::Word(w) => idents.push(w.to_ident()),
Token::Word(w) => {
idents.push(w.to_ident());
}
Token::EOF => break,
_ => {}
}
}
Ok(idents)
}
@ -2386,6 +2428,7 @@ impl<'a> Parser<'a> {
})
} else {
let insert = self.parse_insert()?;
Ok(Query {
with,
body: SetExpr::Insert(insert),
@ -3145,6 +3188,17 @@ impl<'a> Parser<'a> {
let after_columns = self.parse_parenthesized_column_list(Optional)?;
let source = Box::new(self.parse_query()?);
let on = if self.parse_keyword(Keyword::ON) {
self.expect_keyword(Keyword::DUPLICATE)?;
self.expect_keyword(Keyword::KEY)?;
self.expect_keyword(Keyword::UPDATE)?;
let l = self.parse_comma_separated(Parser::parse_assignment)?;
Some(OnInsert::DuplicateKeyUpdate(l))
} else {
None
};
Ok(Statement::Insert {
or,
table_name,
@ -3154,12 +3208,13 @@ impl<'a> Parser<'a> {
after_columns,
source,
table,
on,
})
}
}
pub fn parse_update(&mut self) -> Result<Statement, ParserError> {
let table_name = self.parse_object_name()?;
let table = self.parse_table_and_joins()?;
self.expect_keyword(Keyword::SET)?;
let assignments = self.parse_comma_separated(Parser::parse_assignment)?;
let selection = if self.parse_keyword(Keyword::WHERE) {
@ -3168,7 +3223,7 @@ impl<'a> Parser<'a> {
None
};
Ok(Statement::Update {
table_name,
table,
assignments,
selection,
})
@ -3176,7 +3231,7 @@ impl<'a> Parser<'a> {
/// Parse a `var = expr` assignment, used in an UPDATE statement
pub fn parse_assignment(&mut self) -> Result<Assignment, ParserError> {
let id = self.parse_identifier()?;
let id = self.parse_identifiers_non_keywords()?;
self.expect_token(&Token::Eq)?;
let value = self.parse_expr()?;
Ok(Assignment { id, value })