diff --git a/src/backend/query_builder.rs b/src/backend/query_builder.rs index b253588428..cc6a6c248a 100644 --- a/src/backend/query_builder.rs +++ b/src/backend/query_builder.rs @@ -89,9 +89,15 @@ pub trait QueryBuilder: QuotedBuilder { false }); - if let Some(from) = &select.from { + if !select.from.is_empty() { write!(sql, " FROM ").unwrap(); - self.prepare_table_ref(from, sql, collector); + select.from.iter().fold(true, |first, table_ref| { + if !first { + write!(sql, ", ").unwrap() + } + self.prepare_table_ref(table_ref, sql, collector); + false + }); } if !select.join.is_empty() { diff --git a/src/query/select.rs b/src/query/select.rs index 706b6bb468..fa560b18c3 100644 --- a/src/query/select.rs +++ b/src/query/select.rs @@ -41,7 +41,7 @@ use crate::{ pub struct SelectStatement { pub(crate) distinct: Option, pub(crate) selects: Vec, - pub(crate) from: Option>, + pub(crate) from: Vec, pub(crate) join: Vec, pub(crate) r#where: ConditionHolder, pub(crate) groups: Vec, @@ -113,7 +113,7 @@ impl SelectStatement { Self { distinct: None, selects: Vec::new(), - from: None, + from: Vec::new(), join: Vec::new(), r#where: ConditionHolder::new(), groups: Vec::new(), @@ -131,7 +131,7 @@ impl SelectStatement { Self { distinct: self.distinct.take(), selects: std::mem::take(&mut self.selects), - from: self.from.take(), + from: std::mem::take(&mut self.from), join: std::mem::take(&mut self.join), r#where: std::mem::replace(&mut self.r#where, ConditionHolder::new()), groups: std::mem::take(&mut self.groups), @@ -742,7 +742,7 @@ impl SelectStatement { } fn from_from(&mut self, select: TableRef) -> &mut Self { - self.from = Some(Box::new(select)); + self.from.push(select); self } diff --git a/src/query/with.rs b/src/query/with.rs index f38ef9b496..8a0848b107 100644 --- a/src/query/with.rs +++ b/src/query/with.rs @@ -107,11 +107,11 @@ impl CommonTableExpression { /// Create a CTE from a [SelectStatement] if the selections are named columns then this will /// return a [CommonTableExpression] that has the column names set. The [Self::table_name] is - /// not set. + /// set if the [SelectStatement] from clause contains at least one table. pub fn from_select(select: SelectStatement) -> Self { let mut cte = Self::default(); cte.try_set_cols_from_selects(&select.selects); - if let Some(from) = &select.from { + if let Some(from) = select.from.get(0) { match from.deref() { TableRef::Table(iden) => cte.set_table_name_from_select(iden), TableRef::SchemaTable(_, iden) => cte.set_table_name_from_select(iden), diff --git a/tests/mysql/query.rs b/tests/mysql/query.rs index 640b4fd097..2fa04c2828 100644 --- a/tests/mysql/query.rs +++ b/tests/mysql/query.rs @@ -899,6 +899,21 @@ fn select_53() { ); } +#[test] +fn select_54() { + let statement = sea_query::Query::select() + .expr(Expr::asterisk()) + .from(Char::Table) + .from(Font::Table) + .and_where(Expr::tbl(Font::Table, Font::Id).equals(Char::Table, Char::FontId)) + .to_string(MysqlQueryBuilder); + + assert_eq!( + statement, + r#"SELECT * FROM `character`, `font` WHERE `font`.`id` = `character`.`font_id`"# + ); +} + #[test] #[allow(clippy::approx_constant)] fn insert_2() { diff --git a/tests/postgres/query.rs b/tests/postgres/query.rs index 3a4085dec2..4b4e17dda8 100644 --- a/tests/postgres/query.rs +++ b/tests/postgres/query.rs @@ -877,6 +877,21 @@ fn select_53() { ); } +#[test] +fn select_54() { + let statement = sea_query::Query::select() + .expr(Expr::asterisk()) + .from(Char::Table) + .from(Font::Table) + .and_where(Expr::tbl(Font::Table, Font::Id).equals(Char::Table, Char::FontId)) + .to_string(PostgresQueryBuilder); + + assert_eq!( + statement, + r#"SELECT * FROM "character", "font" WHERE "font"."id" = "character"."font_id""# + ); +} + #[test] #[allow(clippy::approx_constant)] fn insert_2() { diff --git a/tests/sqlite/query.rs b/tests/sqlite/query.rs index c78d8d2b6e..5f9ca2b1ca 100644 --- a/tests/sqlite/query.rs +++ b/tests/sqlite/query.rs @@ -877,6 +877,21 @@ fn select_53() { ); } +#[test] +fn select_54() { + let statement = sea_query::Query::select() + .expr(Expr::asterisk()) + .from(Char::Table) + .from(Font::Table) + .and_where(Expr::tbl(Font::Table, Font::Id).equals(Char::Table, Char::FontId)) + .to_string(SqliteQueryBuilder); + + assert_eq!( + statement, + r#"SELECT * FROM "character", "font" WHERE "font"."id" = "character"."font_id""# + ); +} + #[test] #[allow(clippy::approx_constant)] fn insert_2() {