Skip to content

Commit

Permalink
Support join with table alias (#852)
Browse files Browse the repository at this point in the history
billy1624 authored Jul 10, 2022

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent c956805 commit bb9d532
Showing 2 changed files with 111 additions and 5 deletions.
54 changes: 50 additions & 4 deletions src/query/helper.rs
Original file line number Diff line number Diff line change
@@ -3,8 +3,8 @@ use crate::{
PrimaryKeyToColumn, RelationDef,
};
use sea_query::{
Alias, Expr, Iden, IntoCondition, LockType, SeaRc, SelectExpr, SelectStatement, SimpleExpr,
TableRef,
Alias, Expr, Iden, IntoCondition, IntoIden, LockType, SeaRc, SelectExpr, SelectStatement,
SimpleExpr, TableRef,
};
pub use sea_query::{Condition, ConditionalStatement, DynIden, JoinType, Order, OrderedStatement};

@@ -182,6 +182,32 @@ pub trait QuerySelect: Sized {
self
}

/// Join via [`RelationDef`] with table alias.
fn join_as<I>(mut self, join: JoinType, mut rel: RelationDef, alias: I) -> Self
where
I: IntoIden,
{
let alias = alias.into_iden();
rel.to_tbl = rel.to_tbl.alias(SeaRc::clone(&alias));
self.query()
.join(join, rel.to_tbl.clone(), join_condition(rel));
self
}

/// Join via [`RelationDef`] with table alias but in reverse direction.
/// Assume when there exist a relation A to B.
/// You can reverse join B from A.
fn join_as_rev<I>(mut self, join: JoinType, mut rel: RelationDef, alias: I) -> Self
where
I: IntoIden,
{
let alias = alias.into_iden();
rel.from_tbl = rel.from_tbl.alias(SeaRc::clone(&alias));
self.query()
.join(join, rel.from_tbl.clone(), join_condition(rel));
self
}

/// Select lock
fn lock(mut self, lock_type: LockType) -> Self {
self.query().lock(lock_type);
@@ -431,8 +457,15 @@ pub trait QueryFilter: Sized {
}

pub(crate) fn join_condition(mut rel: RelationDef) -> Condition {
let from_tbl = unpack_table_ref(&rel.from_tbl);
let to_tbl = unpack_table_ref(&rel.to_tbl);
// Use table alias (if any) to construct the join condition
let from_tbl = match unpack_table_alias(&rel.from_tbl) {
Some(alias) => alias,
None => unpack_table_ref(&rel.from_tbl),
};
let to_tbl = match unpack_table_alias(&rel.to_tbl) {
Some(alias) => alias,
None => unpack_table_ref(&rel.to_tbl),
};
let owner_keys = rel.from_col;
let foreign_keys = rel.to_col;

@@ -486,3 +519,16 @@ pub(crate) fn unpack_table_ref(table_ref: &TableRef) -> DynIden {
| TableRef::ValuesList(_, tbl) => SeaRc::clone(tbl),
}
}

pub(crate) fn unpack_table_alias(table_ref: &TableRef) -> Option<DynIden> {
match table_ref {
TableRef::Table(_)
| TableRef::SchemaTable(_, _)
| TableRef::DatabaseSchemaTable(_, _, _)
| TableRef::SubQuery(_, _)
| TableRef::ValuesList(_, _) => None,
TableRef::TableAlias(_, alias)
| TableRef::SchemaTableAlias(_, _, alias)
| TableRef::DatabaseSchemaTableAlias(_, _, _, alias) => Some(SeaRc::clone(alias)),
}
}
62 changes: 61 additions & 1 deletion src/query/join.rs
Original file line number Diff line number Diff line change
@@ -124,7 +124,7 @@ mod tests {
RelationTrait,
};
use pretty_assertions::assert_eq;
use sea_query::{Expr, IntoCondition, JoinType};
use sea_query::{Alias, Expr, IntoCondition, JoinType};

#[test]
fn join_1() {
@@ -506,4 +506,64 @@ mod tests {
.join(" ")
);
}

#[test]
fn join_20() {
assert_eq!(
cake::Entity::find()
.column_as(
Expr::tbl(Alias::new("fruit_alias"), fruit::Column::Name).into_simple_expr(),
"fruit_name"
)
.join_as(
JoinType::LeftJoin,
cake::Relation::Fruit
.def()
.on_condition(|_left, right| {
Expr::tbl(right, fruit::Column::Name)
.like("%tropical%")
.into_condition()
}),
Alias::new("fruit_alias")
)
.build(DbBackend::MySql)
.to_string(),
[
"SELECT `cake`.`id`, `cake`.`name`, `fruit_alias`.`name` AS `fruit_name` FROM `cake`",
"LEFT JOIN `fruit` AS `fruit_alias` ON `cake`.`id` = `fruit_alias`.`cake_id` AND `fruit_alias`.`name` LIKE '%tropical%'",
]
.join(" ")
);
}

#[test]
fn join_21() {
assert_eq!(
cake::Entity::find()
.column_as(
Expr::tbl(Alias::new("cake_filling_alias"), cake_filling::Column::CakeId).into_simple_expr(),
"cake_filling_cake_id"
)
.join(JoinType::LeftJoin, cake::Relation::TropicalFruit.def())
.join_as_rev(
JoinType::LeftJoin,
cake_filling::Relation::Cake
.def()
.on_condition(|left, _right| {
Expr::tbl(left, cake_filling::Column::CakeId)
.gt(10)
.into_condition()
}),
Alias::new("cake_filling_alias")
)
.build(DbBackend::MySql)
.to_string(),
[
"SELECT `cake`.`id`, `cake`.`name`, `cake_filling_alias`.`cake_id` AS `cake_filling_cake_id` FROM `cake`",
"LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id` AND `fruit`.`name` LIKE '%tropical%'",
"LEFT JOIN `cake_filling` AS `cake_filling_alias` ON `cake_filling_alias`.`cake_id` = `cake`.`id` AND `cake_filling_alias`.`cake_id` > 10",
]
.join(" ")
);
}
}

0 comments on commit bb9d532

Please sign in to comment.