Skip to content

Commit 345e209

Browse files
authored
add support for update statements that contain tuple assignments (#1317)
1 parent 0330f9d commit 345e209

File tree

7 files changed

+114
-24
lines changed

7 files changed

+114
-24
lines changed

src/ast/mod.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4553,13 +4553,35 @@ impl fmt::Display for GrantObjects {
45534553
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
45544554
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
45554555
pub struct Assignment {
4556-
pub id: Vec<Ident>,
4556+
pub target: AssignmentTarget,
45574557
pub value: Expr,
45584558
}
45594559

45604560
impl fmt::Display for Assignment {
45614561
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4562-
write!(f, "{} = {}", display_separated(&self.id, "."), self.value)
4562+
write!(f, "{} = {}", self.target, self.value)
4563+
}
4564+
}
4565+
4566+
/// Left-hand side of an assignment in an UPDATE statement,
4567+
/// e.g. `foo` in `foo = 5` (ColumnName assignment) or
4568+
/// `(a, b)` in `(a, b) = (1, 2)` (Tuple assignment).
4569+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
4570+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
4571+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
4572+
pub enum AssignmentTarget {
4573+
/// A single column
4574+
ColumnName(ObjectName),
4575+
/// A tuple of columns
4576+
Tuple(Vec<ObjectName>),
4577+
}
4578+
4579+
impl fmt::Display for AssignmentTarget {
4580+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4581+
match self {
4582+
AssignmentTarget::ColumnName(column) => write!(f, "{}", column),
4583+
AssignmentTarget::Tuple(columns) => write!(f, "({})", display_comma_separated(columns)),
4584+
}
45634585
}
45644586
}
45654587

src/parser/mod.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9937,10 +9937,22 @@ impl<'a> Parser<'a> {
99379937

99389938
/// Parse a `var = expr` assignment, used in an UPDATE statement
99399939
pub fn parse_assignment(&mut self) -> Result<Assignment, ParserError> {
9940-
let id = self.parse_identifiers()?;
9940+
let target = self.parse_assignment_target()?;
99419941
self.expect_token(&Token::Eq)?;
99429942
let value = self.parse_expr()?;
9943-
Ok(Assignment { id, value })
9943+
Ok(Assignment { target, value })
9944+
}
9945+
9946+
/// Parse the left-hand side of an assignment, used in an UPDATE statement
9947+
pub fn parse_assignment_target(&mut self) -> Result<AssignmentTarget, ParserError> {
9948+
if self.consume_token(&Token::LParen) {
9949+
let columns = self.parse_comma_separated(|p| p.parse_object_name(false))?;
9950+
self.expect_token(&Token::RParen)?;
9951+
Ok(AssignmentTarget::Tuple(columns))
9952+
} else {
9953+
let column = self.parse_object_name(false)?;
9954+
Ok(AssignmentTarget::ColumnName(column))
9955+
}
99449956
}
99459957

99469958
pub fn parse_function_args(&mut self) -> Result<FunctionArg, ParserError> {

tests/sqlparser_bigquery.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1590,11 +1590,11 @@ fn parse_merge() {
15901590
let update_action = MergeAction::Update {
15911591
assignments: vec![
15921592
Assignment {
1593-
id: vec![Ident::new("a")],
1593+
target: AssignmentTarget::ColumnName(ObjectName(vec![Ident::new("a")])),
15941594
value: Expr::Value(number("1")),
15951595
},
15961596
Assignment {
1597-
id: vec![Ident::new("b")],
1597+
target: AssignmentTarget::ColumnName(ObjectName(vec![Ident::new("b")])),
15981598
value: Expr::Value(number("2")),
15991599
},
16001600
],

tests/sqlparser_common.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -296,15 +296,15 @@ fn parse_update() {
296296
assignments,
297297
vec![
298298
Assignment {
299-
id: vec!["a".into()],
299+
target: AssignmentTarget::ColumnName(ObjectName(vec!["a".into()])),
300300
value: Expr::Value(number("1")),
301301
},
302302
Assignment {
303-
id: vec!["b".into()],
303+
target: AssignmentTarget::ColumnName(ObjectName(vec!["b".into()])),
304304
value: Expr::Value(number("2")),
305305
},
306306
Assignment {
307-
id: vec!["c".into()],
307+
target: AssignmentTarget::ColumnName(ObjectName(vec!["c".into()])),
308308
value: Expr::Value(number("3")),
309309
},
310310
]
@@ -363,7 +363,7 @@ fn parse_update_set_from() {
363363
joins: vec![],
364364
},
365365
assignments: vec![Assignment {
366-
id: vec![Ident::new("name")],
366+
target: AssignmentTarget::ColumnName(ObjectName(vec![Ident::new("name")])),
367367
value: Expr::CompoundIdentifier(vec![Ident::new("t2"), Ident::new("name")])
368368
}],
369369
from: Some(TableWithJoins {
@@ -466,7 +466,10 @@ fn parse_update_with_table_alias() {
466466
);
467467
assert_eq!(
468468
vec![Assignment {
469-
id: vec![Ident::new("u"), Ident::new("username")],
469+
target: AssignmentTarget::ColumnName(ObjectName(vec![
470+
Ident::new("u"),
471+
Ident::new("username")
472+
])),
470473
value: Expr::Value(Value::SingleQuotedString("new_user".to_string())),
471474
}],
472475
assignments
@@ -7702,14 +7705,20 @@ fn parse_merge() {
77027705
action: MergeAction::Update {
77037706
assignments: vec![
77047707
Assignment {
7705-
id: vec![Ident::new("dest"), Ident::new("F")],
7708+
target: AssignmentTarget::ColumnName(ObjectName(vec![
7709+
Ident::new("dest"),
7710+
Ident::new("F")
7711+
])),
77067712
value: Expr::CompoundIdentifier(vec![
77077713
Ident::new("stg"),
77087714
Ident::new("F"),
77097715
]),
77107716
},
77117717
Assignment {
7712-
id: vec![Ident::new("dest"), Ident::new("G")],
7718+
target: AssignmentTarget::ColumnName(ObjectName(vec![
7719+
Ident::new("dest"),
7720+
Ident::new("G")
7721+
])),
77137722
value: Expr::CompoundIdentifier(vec![
77147723
Ident::new("stg"),
77157724
Ident::new("G"),

tests/sqlparser_mysql.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1639,23 +1639,33 @@ fn parse_insert_with_on_duplicate_update() {
16391639
assert_eq!(
16401640
Some(OnInsert::DuplicateKeyUpdate(vec![
16411641
Assignment {
1642-
id: vec![Ident::new("description".to_string())],
1642+
target: AssignmentTarget::ColumnName(ObjectName(vec![Ident::new(
1643+
"description".to_string()
1644+
)])),
16431645
value: call("VALUES", [Expr::Identifier(Ident::new("description"))]),
16441646
},
16451647
Assignment {
1646-
id: vec![Ident::new("perm_create".to_string())],
1648+
target: AssignmentTarget::ColumnName(ObjectName(vec![Ident::new(
1649+
"perm_create".to_string()
1650+
)])),
16471651
value: call("VALUES", [Expr::Identifier(Ident::new("perm_create"))]),
16481652
},
16491653
Assignment {
1650-
id: vec![Ident::new("perm_read".to_string())],
1654+
target: AssignmentTarget::ColumnName(ObjectName(vec![Ident::new(
1655+
"perm_read".to_string()
1656+
)])),
16511657
value: call("VALUES", [Expr::Identifier(Ident::new("perm_read"))]),
16521658
},
16531659
Assignment {
1654-
id: vec![Ident::new("perm_update".to_string())],
1660+
target: AssignmentTarget::ColumnName(ObjectName(vec![Ident::new(
1661+
"perm_update".to_string()
1662+
)])),
16551663
value: call("VALUES", [Expr::Identifier(Ident::new("perm_update"))]),
16561664
},
16571665
Assignment {
1658-
id: vec![Ident::new("perm_delete".to_string())],
1666+
target: AssignmentTarget::ColumnName(ObjectName(vec![Ident::new(
1667+
"perm_delete".to_string()
1668+
)])),
16591669
value: call("VALUES", [Expr::Identifier(Ident::new("perm_delete"))]),
16601670
},
16611671
])),
@@ -1835,7 +1845,10 @@ fn parse_update_with_joins() {
18351845
);
18361846
assert_eq!(
18371847
vec![Assignment {
1838-
id: vec![Ident::new("o"), Ident::new("completed")],
1848+
target: AssignmentTarget::ColumnName(ObjectName(vec![
1849+
Ident::new("o"),
1850+
Ident::new("completed")
1851+
])),
18391852
value: Expr::Value(Value::Boolean(true))
18401853
}],
18411854
assignments

tests/sqlparser_postgres.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1557,7 +1557,7 @@ fn parse_pg_on_conflict() {
15571557
assert_eq!(
15581558
OnConflictAction::DoUpdate(DoUpdate {
15591559
assignments: vec![Assignment {
1560-
id: vec!["dname".into()],
1560+
target: AssignmentTarget::ColumnName(ObjectName(vec!["dname".into()])),
15611561
value: Expr::CompoundIdentifier(vec!["EXCLUDED".into(), "dname".into()])
15621562
},],
15631563
selection: None
@@ -1588,14 +1588,14 @@ fn parse_pg_on_conflict() {
15881588
OnConflictAction::DoUpdate(DoUpdate {
15891589
assignments: vec![
15901590
Assignment {
1591-
id: vec!["dname".into()],
1591+
target: AssignmentTarget::ColumnName(ObjectName(vec!["dname".into()])),
15921592
value: Expr::CompoundIdentifier(vec![
15931593
"EXCLUDED".into(),
15941594
"dname".into()
15951595
])
15961596
},
15971597
Assignment {
1598-
id: vec!["area".into()],
1598+
target: AssignmentTarget::ColumnName(ObjectName(vec!["area".into()])),
15991599
value: Expr::CompoundIdentifier(vec!["EXCLUDED".into(), "area".into()])
16001600
},
16011601
],
@@ -1645,7 +1645,7 @@ fn parse_pg_on_conflict() {
16451645
assert_eq!(
16461646
OnConflictAction::DoUpdate(DoUpdate {
16471647
assignments: vec![Assignment {
1648-
id: vec!["dname".into()],
1648+
target: AssignmentTarget::ColumnName(ObjectName(vec!["dname".into()])),
16491649
value: Expr::Value(Value::Placeholder("$1".to_string()))
16501650
},],
16511651
selection: Some(Expr::BinaryOp {
@@ -1682,7 +1682,7 @@ fn parse_pg_on_conflict() {
16821682
assert_eq!(
16831683
OnConflictAction::DoUpdate(DoUpdate {
16841684
assignments: vec![Assignment {
1685-
id: vec!["dname".into()],
1685+
target: AssignmentTarget::ColumnName(ObjectName(vec!["dname".into()])),
16861686
value: Expr::Value(Value::Placeholder("$1".to_string()))
16871687
},],
16881688
selection: Some(Expr::BinaryOp {

tests/sqlparser_sqlite.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,40 @@ fn parse_attach_database() {
373373
}
374374
}
375375

376+
#[test]
377+
fn parse_update_tuple_row_values() {
378+
// See https://github.com/sqlparser-rs/sqlparser-rs/issues/1311
379+
assert_eq!(
380+
sqlite().verified_stmt("UPDATE x SET (a, b) = (1, 2)"),
381+
Statement::Update {
382+
assignments: vec![Assignment {
383+
target: AssignmentTarget::Tuple(vec![
384+
ObjectName(vec![Ident::new("a"),]),
385+
ObjectName(vec![Ident::new("b"),]),
386+
]),
387+
value: Expr::Tuple(vec![
388+
Expr::Value(Value::Number("1".parse().unwrap(), false)),
389+
Expr::Value(Value::Number("2".parse().unwrap(), false))
390+
])
391+
}],
392+
selection: None,
393+
table: TableWithJoins {
394+
relation: TableFactor::Table {
395+
name: ObjectName(vec![Ident::new("x")]),
396+
alias: None,
397+
args: None,
398+
with_hints: vec![],
399+
version: None,
400+
partitions: vec![]
401+
},
402+
joins: vec![],
403+
},
404+
from: None,
405+
returning: None
406+
}
407+
);
408+
}
409+
376410
#[test]
377411
fn parse_where_in_empty_list() {
378412
let sql = "SELECT * FROM t1 WHERE a IN ()";

0 commit comments

Comments
 (0)