Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[join-lateral] #224

Merged
merged 1 commit into from
Jan 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/backend/query_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,9 @@ pub trait QueryBuilder: QuotedBuilder {
) {
self.prepare_join_type(&join_expr.join, sql, collector);
write!(sql, " ").unwrap();
if join_expr.lateral {
write!(sql, "LATERAL ").unwrap();
}
self.prepare_table_ref(&join_expr.table, sql, collector);
if let Some(on) = &join_expr.on {
write!(sql, " ").unwrap();
Expand Down
77 changes: 76 additions & 1 deletion src/query/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ pub struct JoinExpr {
pub join: JoinType,
pub table: Box<TableRef>,
pub on: Option<JoinOn>,
pub lateral: bool,
}

/// List of lock types that can be used in select statement
Expand Down Expand Up @@ -902,6 +903,7 @@ impl SelectStatement {
JoinOn::Condition(Box::new(ConditionHolder::new_with_condition(
condition.into_condition(),
))),
false,
)
}

Expand Down Expand Up @@ -973,6 +975,7 @@ impl SelectStatement {
JoinOn::Condition(Box::new(ConditionHolder::new_with_condition(
condition.into_condition(),
))),
false,
)
}

Expand Down Expand Up @@ -1061,14 +1064,86 @@ impl SelectStatement {
JoinOn::Condition(Box::new(ConditionHolder::new_with_condition(
condition.into_condition(),
))),
false,
)
}

fn join_join(&mut self, join: JoinType, table: TableRef, on: JoinOn) -> &mut Self {
/// Join Lateral with sub-query.
///
/// # Examples
///
/// ```
/// use sea_query::{*, tests_cfg::*};
///
/// let sub_glyph: DynIden = SeaRc::new(Alias::new("sub_glyph"));
/// let query = Query::select()
/// .column(Font::Name)
/// .from(Font::Table)
/// .join_lateral_subquery(
/// JoinType::LeftJoin,
/// Query::select().column(Glyph::Id).from(Glyph::Table).take(),
/// sub_glyph.clone(),
/// Expr::tbl(Font::Table, Font::Id).equals(sub_glyph.clone(), Glyph::Id)
/// )
/// .to_owned();
///
/// assert_eq!(
/// query.to_string(MysqlQueryBuilder),
/// r#"SELECT `name` FROM `font` LEFT JOIN LATERAL (SELECT `id` FROM `glyph`) AS `sub_glyph` ON `font`.`id` = `sub_glyph`.`id`"#
/// );
/// assert_eq!(
/// query.to_string(PostgresQueryBuilder),
/// r#"SELECT "name" FROM "font" LEFT JOIN LATERAL (SELECT "id" FROM "glyph") AS "sub_glyph" ON "font"."id" = "sub_glyph"."id""#
/// );
/// assert_eq!(
/// query.to_string(SqliteQueryBuilder),
/// r#"SELECT `name` FROM `font` LEFT JOIN LATERAL (SELECT `id` FROM `glyph`) AS `sub_glyph` ON `font`.`id` = `sub_glyph`.`id`"#
/// );
Comment on lines +1098 to +1101
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JOIN LATERAL seems not supported by SQLite. We shouldn't generate invalid select statement

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how do i error on sqlite within the builder?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could panic! when building the select statement for SQLite. Is this be reasonable? @tyt2y3 It's better to let the user know explicitly something went wrong early.

///
/// // Constructing chained join conditions
/// assert_eq!(
/// Query::select()
/// .column(Font::Name)
/// .from(Font::Table)
/// .join_lateral_subquery(
/// JoinType::LeftJoin,
/// Query::select().column(Glyph::Id).from(Glyph::Table).take(),
/// sub_glyph.clone(),
/// Condition::all()
/// .add(Expr::tbl(Font::Table, Font::Id).equals(sub_glyph.clone(), Glyph::Id))
/// .add(Expr::tbl(Font::Table, Font::Id).equals(sub_glyph.clone(), Glyph::Id))
/// )
/// .to_string(MysqlQueryBuilder),
/// r#"SELECT `name` FROM `font` LEFT JOIN LATERAL (SELECT `id` FROM `glyph`) AS `sub_glyph` ON `font`.`id` = `sub_glyph`.`id` AND `font`.`id` = `sub_glyph`.`id`"#
/// );
/// ```
pub fn join_lateral_subquery<T, C>(
&mut self,
join: JoinType,
query: SelectStatement,
alias: T,
condition: C,
) -> &mut Self
where
T: IntoIden,
C: IntoCondition,
Comment on lines +1120 to +1129
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking this method could simply be named join_lateral. As it's assumed to take a (sub)query as argument. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup, that makes sense 👍

{
self.join_join(
join,
TableRef::SubQuery(query, alias.into_iden()),
JoinOn::Condition(Box::new(ConditionHolder::new_with_condition(
condition.into_condition(),
))),
true,
)
}

fn join_join(&mut self, join: JoinType, table: TableRef, on: JoinOn, lateral: bool) -> &mut Self {
self.join.push(JoinExpr {
join,
table: Box::new(table),
on: Some(on),
lateral,
});
self
}
Expand Down