Skip to content

Commit 81723ac

Browse files
committed
add support for update statements that contain tuple assignments
This allows the correct parsing of UPDATE x SET (a, b) = (1, 2) while also maintaining the parsing of UPDATE x SET a.b = 1 This includes a change to the ast::Assignment struct
1 parent be77ce5 commit 81723ac

File tree

7 files changed

+112
-24
lines changed

7 files changed

+112
-24
lines changed

src/ast/mod.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4594,13 +4594,33 @@ impl fmt::Display for GrantObjects {
45944594
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
45954595
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
45964596
pub struct Assignment {
4597-
pub id: Vec<Ident>,
4597+
pub target: AssignmentTarget,
45984598
pub value: Expr,
45994599
}
46004600

46014601
impl fmt::Display for Assignment {
46024602
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4603-
write!(f, "{} = {}", display_separated(&self.id, "."), self.value)
4603+
write!(f, "{} = {}", self.target, self.value)
4604+
}
4605+
}
4606+
4607+
/// SQL assignment `foo = expr` as used in SQLUpdate
4608+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
4609+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
4610+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
4611+
pub enum AssignmentTarget {
4612+
/// A single column
4613+
ColumnName(ObjectName),
4614+
/// A tuple of columns
4615+
Tuple(Vec<ObjectName>),
4616+
}
4617+
4618+
impl fmt::Display for AssignmentTarget {
4619+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4620+
match self {
4621+
AssignmentTarget::ColumnName(column) => write!(f, "{}", column),
4622+
AssignmentTarget::Tuple(columns) => write!(f, "({})", display_comma_separated(columns)),
4623+
}
46044624
}
46054625
}
46064626

src/parser/mod.rs

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

99479947
/// Parse a `var = expr` assignment, used in an UPDATE statement
99489948
pub fn parse_assignment(&mut self) -> Result<Assignment, ParserError> {
9949-
let id = self.parse_identifiers()?;
9949+
let target = self.parse_assignment_target()?;
99509950
self.expect_token(&Token::Eq)?;
99519951
let value = self.parse_expr()?;
9952-
Ok(Assignment { id, value })
9952+
Ok(Assignment { target, value })
9953+
}
9954+
9955+
/// Parse the left-hand side of an assignment, used in an UPDATE statement
9956+
pub fn parse_assignment_target(&mut self) -> Result<AssignmentTarget, ParserError> {
9957+
if self.consume_token(&Token::LParen) {
9958+
let columns = self.parse_comma_separated(|p| p.parse_object_name(false))?;
9959+
self.expect_token(&Token::RParen)?;
9960+
Ok(AssignmentTarget::Tuple(columns))
9961+
} else {
9962+
let column = self.parse_object_name(false)?;
9963+
Ok(AssignmentTarget::ColumnName(column))
9964+
}
99539965
}
99549966

99559967
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
@@ -7696,14 +7699,20 @@ fn parse_merge() {
76967699
action: MergeAction::Update {
76977700
assignments: vec![
76987701
Assignment {
7699-
id: vec![Ident::new("dest"), Ident::new("F")],
7702+
target: AssignmentTarget::ColumnName(ObjectName(vec![
7703+
Ident::new("dest"),
7704+
Ident::new("F")
7705+
])),
77007706
value: Expr::CompoundIdentifier(vec![
77017707
Ident::new("stg"),
77027708
Ident::new("F"),
77037709
]),
77047710
},
77057711
Assignment {
7706-
id: vec![Ident::new("dest"), Ident::new("G")],
7712+
target: AssignmentTarget::ColumnName(ObjectName(vec![
7713+
Ident::new("dest"),
7714+
Ident::new("G")
7715+
])),
77077716
value: Expr::CompoundIdentifier(vec![
77087717
Ident::new("stg"),
77097718
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)