diff --git a/src/backend/mysql/query.rs b/src/backend/mysql/query.rs index 58126014..8180fe70 100644 --- a/src/backend/mysql/query.rs +++ b/src/backend/mysql/query.rs @@ -6,6 +6,10 @@ impl QueryBuilder for MysqlQueryBuilder { "ROW" } + fn prepare_update_from_table_refs(&self, _: &[TableRef], _: &mut dyn SqlWriter) { + unimplemented!(r#""UPDATE ... FROM ..." syntax not yet supported for MySQL backend"#) + } + fn prepare_select_distinct(&self, select_distinct: &SelectDistinct, sql: &mut dyn SqlWriter) { match select_distinct { SelectDistinct::All => write!(sql, "ALL").unwrap(), diff --git a/src/backend/query_builder.rs b/src/backend/query_builder.rs index 41cac862..5c32bc23 100644 --- a/src/backend/query_builder.rs +++ b/src/backend/query_builder.rs @@ -1,6 +1,7 @@ -use crate::*; use std::ops::Deref; +use crate::*; + const QUOTE: Quote = Quote(b'"', b'"'); pub trait QueryBuilder: @@ -210,6 +211,10 @@ pub trait QueryBuilder: false }); + if !update.from.is_empty() { + self.prepare_update_from_table_refs(&update.from, sql); + } + self.prepare_output(&update.returning, sql); self.prepare_condition(&update.r#where, "WHERE", sql); @@ -221,6 +226,20 @@ pub trait QueryBuilder: self.prepare_returning(&update.returning, sql); } + fn prepare_update_from_table_refs(&self, from: &[TableRef], sql: &mut dyn SqlWriter) { + write!(sql, " FROM ").unwrap(); + + from.iter().fold(true, |first, table_ref| { + if !first { + write!(sql, ", ").unwrap() + } + + self.prepare_table_ref(table_ref, sql); + + false + }); + } + /// Translate ORDER BY expression in [`UpdateStatement`]. fn prepare_update_order_by(&self, update: &UpdateStatement, sql: &mut dyn SqlWriter) { if !update.orders.is_empty() { diff --git a/src/query/update.rs b/src/query/update.rs index a514127b..a34c77f8 100644 --- a/src/query/update.rs +++ b/src/query/update.rs @@ -1,3 +1,5 @@ +use inherent::inherent; + use crate::{ backend::QueryBuilder, expr::*, @@ -8,7 +10,6 @@ use crate::{ QueryStatementBuilder, QueryStatementWriter, ReturningClause, SubQueryStatement, WithClause, WithQuery, }; -use inherent::inherent; /// Update existing rows in the table /// @@ -39,6 +40,7 @@ use inherent::inherent; #[derive(Default, Debug, Clone, PartialEq)] pub struct UpdateStatement { pub(crate) table: Option>, + pub(crate) from: Vec, pub(crate) values: Vec<(DynIden, Box)>, pub(crate) r#where: ConditionHolder, pub(crate) orders: Vec, @@ -66,6 +68,62 @@ impl UpdateStatement { self } + /// From table. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{tests_cfg::*, *}; + /// + /// let query = Query::update() + /// .table(Glyph::Table) + /// .value( + /// Glyph::Tokens, + /// SimpleExpr::Column(ColumnRef::TableColumn( + /// SeaRc::new(Char::Table), + /// SeaRc::new(Char::Character), + /// )), + /// ) + /// .from(Char::Table) + /// .cond_where( + /// SimpleExpr::Column(ColumnRef::TableColumn( + /// SeaRc::new(Glyph::Table), + /// SeaRc::new(Glyph::Image), + /// )) + /// .eq(SimpleExpr::Column(ColumnRef::TableColumn( + /// SeaRc::new(Char::Table), + /// SeaRc::new(Char::UserData), + /// ))), + /// ) + /// .to_owned(); + /// + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// "UPDATE `glyph` JOIN `character` ON `glyph`.`image` = `character`.`user_data` SET `glyph`.`tokens` = `character`.`character`" + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"UPDATE "glyph" SET "tokens" = "character"."character" FROM "character" WHERE "glyph"."image" = "character"."user_data""# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"UPDATE "glyph" SET "tokens" = "character"."character" FROM "character" WHERE "glyph"."image" = "character"."user_data""# + /// ); + /// ``` + pub fn from(&mut self, tbl_ref: R) -> &mut Self + where + R: IntoTableRef, + { + self.from_from(tbl_ref.into_table_ref()) + } + + #[allow(clippy::wrong_self_convention)] + fn from_from(&mut self, select: TableRef) -> &mut Self { + self.from.push(select); + self + } + /// Update column values. To set multiple column-value pairs at once. /// /// # Examples