Skip to content

Commit 82eaae1

Browse files
alambPiotr0xA537FD
authored
Implement some MySQL specific syntax and extend the UPDATE statement (apache#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 <[email protected]> Co-authored-by: Piotr Morawski <[email protected]>
1 parent 40d67aa commit 82eaae1

File tree

7 files changed

+489
-18
lines changed

7 files changed

+489
-18
lines changed

examples/cli.rs

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ $ cargo run --feature json_example --example cli FILENAME.sql [--dialectname]
4040
"--ansi" => Box::new(AnsiDialect {}),
4141
"--postgres" => Box::new(PostgreSqlDialect {}),
4242
"--ms" => Box::new(MsSqlDialect {}),
43+
"--mysql" => Box::new(MySqlDialect {}),
4344
"--snowflake" => Box::new(SnowflakeDialect {}),
4445
"--hive" => Box::new(HiveDialect {}),
4546
"--generic" | "" => Box::new(GenericDialect {}),

src/ast/ddl.rs

+20
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ pub enum AlterTableOperation {
6060
},
6161
/// `RENAME TO <table_name>`
6262
RenameTable { table_name: ObjectName },
63+
// CHANGE [ COLUMN ] <old_name> <new_name> <data_type> [ <options> ]
64+
ChangeColumn {
65+
old_name: Ident,
66+
new_name: Ident,
67+
data_type: DataType,
68+
options: Vec<ColumnOption>,
69+
},
6370
}
6471

6572
impl fmt::Display for AlterTableOperation {
@@ -119,6 +126,19 @@ impl fmt::Display for AlterTableOperation {
119126
AlterTableOperation::RenameTable { table_name } => {
120127
write!(f, "RENAME TO {}", table_name)
121128
}
129+
AlterTableOperation::ChangeColumn {
130+
old_name,
131+
new_name,
132+
data_type,
133+
options,
134+
} => {
135+
write!(f, "CHANGE COLUMN {} {} {}", old_name, new_name, data_type)?;
136+
if options.is_empty() {
137+
Ok(())
138+
} else {
139+
write!(f, " {}", display_separated(options, " "))
140+
}
141+
}
122142
}
123143
}
124144
}

src/ast/mod.rs

+34-6
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,7 @@ pub enum Statement {
650650
after_columns: Vec<Ident>,
651651
/// whether the insert has the table keyword (Hive)
652652
table: bool,
653+
on: Option<OnInsert>,
653654
},
654655
// TODO: Support ROW FORMAT
655656
Directory {
@@ -670,7 +671,7 @@ pub enum Statement {
670671
/// UPDATE
671672
Update {
672673
/// TABLE
673-
table_name: ObjectName,
674+
table: TableWithJoins,
674675
/// Column assignments
675676
assignments: Vec<Assignment>,
676677
/// WHERE
@@ -990,6 +991,7 @@ impl fmt::Display for Statement {
990991
after_columns,
991992
source,
992993
table,
994+
on,
993995
} => {
994996
if let Some(action) = or {
995997
write!(f, "INSERT OR {} INTO {} ", action, table_name)?;
@@ -1013,7 +1015,13 @@ impl fmt::Display for Statement {
10131015
if !after_columns.is_empty() {
10141016
write!(f, "({}) ", display_comma_separated(after_columns))?;
10151017
}
1016-
write!(f, "{}", source)
1018+
write!(f, "{}", source)?;
1019+
1020+
if let Some(on) = on {
1021+
write!(f, "{}", on)
1022+
} else {
1023+
Ok(())
1024+
}
10171025
}
10181026

10191027
Statement::Copy {
@@ -1042,11 +1050,11 @@ impl fmt::Display for Statement {
10421050
write!(f, "\n\\.")
10431051
}
10441052
Statement::Update {
1045-
table_name,
1053+
table,
10461054
assignments,
10471055
selection,
10481056
} => {
1049-
write!(f, "UPDATE {}", table_name)?;
1057+
write!(f, "UPDATE {}", table)?;
10501058
if !assignments.is_empty() {
10511059
write!(f, " SET {}", display_comma_separated(assignments))?;
10521060
}
@@ -1452,6 +1460,26 @@ impl fmt::Display for Statement {
14521460
}
14531461
}
14541462

1463+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1464+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1465+
#[non_exhaustive]
1466+
pub enum OnInsert {
1467+
/// ON DUPLICATE KEY UPDATE (MySQL when the key already exists, then execute an update instead)
1468+
DuplicateKeyUpdate(Vec<Assignment>),
1469+
}
1470+
1471+
impl fmt::Display for OnInsert {
1472+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1473+
match self {
1474+
Self::DuplicateKeyUpdate(expr) => write!(
1475+
f,
1476+
" ON DUPLICATE KEY UPDATE {}",
1477+
display_comma_separated(expr)
1478+
),
1479+
}
1480+
}
1481+
}
1482+
14551483
/// Privileges granted in a GRANT statement or revoked in a REVOKE statement.
14561484
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
14571485
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -1587,13 +1615,13 @@ impl fmt::Display for GrantObjects {
15871615
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
15881616
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
15891617
pub struct Assignment {
1590-
pub id: Ident,
1618+
pub id: Vec<Ident>,
15911619
pub value: Expr,
15921620
}
15931621

15941622
impl fmt::Display for Assignment {
15951623
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1596-
write!(f, "{} = {}", self.id, self.value)
1624+
write!(f, "{} = {}", display_separated(&self.id, "."), self.value)
15971625
}
15981626
}
15991627

src/keywords.rs

+3
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ define_keywords!(
114114
CEIL,
115115
CEILING,
116116
CHAIN,
117+
CHANGE,
117118
CHAR,
118119
CHARACTER,
119120
CHARACTER_LENGTH,
@@ -181,6 +182,7 @@ define_keywords!(
181182
DISTRIBUTE,
182183
DOUBLE,
183184
DROP,
185+
DUPLICATE,
184186
DYNAMIC,
185187
EACH,
186188
ELEMENT,
@@ -542,6 +544,7 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[Keyword] = &[
542544
Keyword::DISTRIBUTE,
543545
// for MSSQL-specific OUTER APPLY (seems reserved in most dialects)
544546
Keyword::OUTER,
547+
Keyword::SET,
545548
];
546549

547550
/// Can't be used as a column alias, so that `SELECT <expr> alias`

src/parser.rs

+62-7
Original file line numberDiff line numberDiff line change
@@ -702,11 +702,12 @@ impl<'a> Parser<'a> {
702702
self.expect_token(&Token::LParen)?;
703703
let expr = self.parse_expr()?;
704704
let mut from_expr = None;
705-
let mut to_expr = None;
706-
if self.parse_keyword(Keyword::FROM) {
705+
if self.parse_keyword(Keyword::FROM) || self.consume_token(&Token::Comma) {
707706
from_expr = Some(self.parse_expr()?);
708707
}
709-
if self.parse_keyword(Keyword::FOR) {
708+
709+
let mut to_expr = None;
710+
if self.parse_keyword(Keyword::FOR) || self.consume_token(&Token::Comma) {
710711
to_expr = Some(self.parse_expr()?);
711712
}
712713
self.expect_token(&Token::RParen)?;
@@ -1958,6 +1959,22 @@ impl<'a> Parser<'a> {
19581959
old_partitions: before,
19591960
new_partitions: renames,
19601961
}
1962+
} else if self.parse_keyword(Keyword::CHANGE) {
1963+
let _ = self.parse_keyword(Keyword::COLUMN);
1964+
let old_name = self.parse_identifier()?;
1965+
let new_name = self.parse_identifier()?;
1966+
let data_type = self.parse_data_type()?;
1967+
let mut options = vec![];
1968+
while let Some(option) = self.parse_optional_column_option()? {
1969+
options.push(option);
1970+
}
1971+
1972+
AlterTableOperation::ChangeColumn {
1973+
old_name,
1974+
new_name,
1975+
data_type,
1976+
options,
1977+
}
19611978
} else {
19621979
return self.expected(
19631980
"ADD, RENAME, PARTITION or DROP after ALTER TABLE",
@@ -2234,16 +2251,41 @@ impl<'a> Parser<'a> {
22342251
Ok(ObjectName(idents))
22352252
}
22362253

2254+
/// Parse identifiers strictly i.e. don't parse keywords
2255+
pub fn parse_identifiers_non_keywords(&mut self) -> Result<Vec<Ident>, ParserError> {
2256+
let mut idents = vec![];
2257+
loop {
2258+
match self.peek_token() {
2259+
Token::Word(w) => {
2260+
if w.keyword != Keyword::NoKeyword {
2261+
break;
2262+
}
2263+
2264+
idents.push(w.to_ident());
2265+
}
2266+
Token::EOF | Token::Eq => break,
2267+
_ => {}
2268+
}
2269+
2270+
self.next_token();
2271+
}
2272+
2273+
Ok(idents)
2274+
}
2275+
22372276
/// Parse identifiers
22382277
pub fn parse_identifiers(&mut self) -> Result<Vec<Ident>, ParserError> {
22392278
let mut idents = vec![];
22402279
loop {
22412280
match self.next_token() {
2242-
Token::Word(w) => idents.push(w.to_ident()),
2281+
Token::Word(w) => {
2282+
idents.push(w.to_ident());
2283+
}
22432284
Token::EOF => break,
22442285
_ => {}
22452286
}
22462287
}
2288+
22472289
Ok(idents)
22482290
}
22492291

@@ -2386,6 +2428,7 @@ impl<'a> Parser<'a> {
23862428
})
23872429
} else {
23882430
let insert = self.parse_insert()?;
2431+
23892432
Ok(Query {
23902433
with,
23912434
body: SetExpr::Insert(insert),
@@ -3145,6 +3188,17 @@ impl<'a> Parser<'a> {
31453188
let after_columns = self.parse_parenthesized_column_list(Optional)?;
31463189

31473190
let source = Box::new(self.parse_query()?);
3191+
let on = if self.parse_keyword(Keyword::ON) {
3192+
self.expect_keyword(Keyword::DUPLICATE)?;
3193+
self.expect_keyword(Keyword::KEY)?;
3194+
self.expect_keyword(Keyword::UPDATE)?;
3195+
let l = self.parse_comma_separated(Parser::parse_assignment)?;
3196+
3197+
Some(OnInsert::DuplicateKeyUpdate(l))
3198+
} else {
3199+
None
3200+
};
3201+
31483202
Ok(Statement::Insert {
31493203
or,
31503204
table_name,
@@ -3154,12 +3208,13 @@ impl<'a> Parser<'a> {
31543208
after_columns,
31553209
source,
31563210
table,
3211+
on,
31573212
})
31583213
}
31593214
}
31603215

31613216
pub fn parse_update(&mut self) -> Result<Statement, ParserError> {
3162-
let table_name = self.parse_object_name()?;
3217+
let table = self.parse_table_and_joins()?;
31633218
self.expect_keyword(Keyword::SET)?;
31643219
let assignments = self.parse_comma_separated(Parser::parse_assignment)?;
31653220
let selection = if self.parse_keyword(Keyword::WHERE) {
@@ -3168,15 +3223,15 @@ impl<'a> Parser<'a> {
31683223
None
31693224
};
31703225
Ok(Statement::Update {
3171-
table_name,
3226+
table,
31723227
assignments,
31733228
selection,
31743229
})
31753230
}
31763231

31773232
/// Parse a `var = expr` assignment, used in an UPDATE statement
31783233
pub fn parse_assignment(&mut self) -> Result<Assignment, ParserError> {
3179-
let id = self.parse_identifier()?;
3234+
let id = self.parse_identifiers_non_keywords()?;
31803235
self.expect_token(&Token::Eq)?;
31813236
let value = self.parse_expr()?;
31823237
Ok(Assignment { id, value })

tests/sqlparser_common.rs

+54-5
Original file line numberDiff line numberDiff line change
@@ -141,25 +141,25 @@ fn parse_update() {
141141
let sql = "UPDATE t SET a = 1, b = 2, c = 3 WHERE d";
142142
match verified_stmt(sql) {
143143
Statement::Update {
144-
table_name,
144+
table,
145145
assignments,
146146
selection,
147147
..
148148
} => {
149-
assert_eq!(table_name.to_string(), "t".to_string());
149+
assert_eq!(table.to_string(), "t".to_string());
150150
assert_eq!(
151151
assignments,
152152
vec![
153153
Assignment {
154-
id: "a".into(),
154+
id: vec!["a".into()],
155155
value: Expr::Value(number("1")),
156156
},
157157
Assignment {
158-
id: "b".into(),
158+
id: vec!["b".into()],
159159
value: Expr::Value(number("2")),
160160
},
161161
Assignment {
162-
id: "c".into(),
162+
id: vec!["c".into()],
163163
value: Expr::Value(number("3")),
164164
},
165165
]
@@ -186,6 +186,55 @@ fn parse_update() {
186186
);
187187
}
188188

189+
#[test]
190+
fn parse_update_with_table_alias() {
191+
let sql = "UPDATE users AS u SET u.username = 'new_user' WHERE u.username = 'old_user'";
192+
match verified_stmt(sql) {
193+
Statement::Update {
194+
table,
195+
assignments,
196+
selection,
197+
} => {
198+
assert_eq!(
199+
TableWithJoins {
200+
relation: TableFactor::Table {
201+
name: ObjectName(vec![Ident::new("users")]),
202+
alias: Some(TableAlias {
203+
name: Ident::new("u"),
204+
columns: vec![]
205+
}),
206+
args: vec![],
207+
with_hints: vec![],
208+
},
209+
joins: vec![]
210+
},
211+
table
212+
);
213+
assert_eq!(
214+
vec![Assignment {
215+
id: vec![Ident::new("u"), Ident::new("username")],
216+
value: Expr::Value(Value::SingleQuotedString("new_user".to_string()))
217+
}],
218+
assignments
219+
);
220+
assert_eq!(
221+
Some(Expr::BinaryOp {
222+
left: Box::new(Expr::CompoundIdentifier(vec![
223+
Ident::new("u"),
224+
Ident::new("username")
225+
])),
226+
op: BinaryOperator::Eq,
227+
right: Box::new(Expr::Value(Value::SingleQuotedString(
228+
"old_user".to_string()
229+
)))
230+
}),
231+
selection
232+
);
233+
}
234+
_ => unreachable!(),
235+
}
236+
}
237+
189238
#[test]
190239
fn parse_invalid_table_name() {
191240
let ast = all_dialects()

0 commit comments

Comments
 (0)