diff --git a/src/expr.rs b/src/expr.rs index 25d5216b7..a34d0fcb8 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -37,6 +37,10 @@ pub enum SimpleExpr { Constant(Value), } +pub trait IntoSimpleExpr { + fn into_sub_query_expr(self) -> SimpleExpr; +} + pub(crate) mod private { use crate::{BinOper, LikeExpr, SimpleExpr, UnOper}; diff --git a/src/func.rs b/src/func.rs index 38b079c83..ae862ed9d 100644 --- a/src/func.rs +++ b/src/func.rs @@ -409,7 +409,11 @@ impl Func { /// /// let query = Query::select() /// .expr(Func::coalesce([ - /// Expr::col(Char::SizeW).into(), + /// Query::select() + /// .from(Char::Table) + /// .expr(Func::max(Expr::col(Char::SizeW))) + /// .take() + /// .into_sub_query_expr(), /// Expr::col(Char::SizeH).into(), /// Expr::val(12).into(), /// ])) @@ -418,15 +422,15 @@ impl Func { /// /// assert_eq!( /// query.to_string(MysqlQueryBuilder), - /// r#"SELECT COALESCE(`size_w`, `size_h`, 12) FROM `character`"# + /// r#"SELECT COALESCE((SELECT MAX(`size_w`) FROM `character`), `size_h`, 12) FROM `character`"# /// ); /// assert_eq!( /// query.to_string(PostgresQueryBuilder), - /// r#"SELECT COALESCE("size_w", "size_h", 12) FROM "character""# + /// r#"SELECT COALESCE((SELECT MAX("size_w") FROM "character"), "size_h", 12) FROM "character""# /// ); /// assert_eq!( /// query.to_string(SqliteQueryBuilder), - /// r#"SELECT COALESCE("size_w", "size_h", 12) FROM "character""# + /// r#"SELECT COALESCE((SELECT MAX("size_w") FROM "character"), "size_h", 12) FROM "character""# /// ); /// ``` pub fn coalesce(args: I) -> FunctionCall diff --git a/src/query/select.rs b/src/query/select.rs index b8c11b094..7e2335a0d 100644 --- a/src/query/select.rs +++ b/src/query/select.rs @@ -2380,3 +2380,9 @@ impl ConditionalStatement for SelectStatement { pub fn and_where_option(&mut self, other: Option) -> &mut Self; pub fn and_where(&mut self, other: SimpleExpr) -> &mut Self; } + +impl IntoSimpleExpr for SelectStatement { + fn into_sub_query_expr(self) -> SimpleExpr { + SimpleExpr::SubQuery(None, Box::new(self.into_sub_query_statement())) + } +} diff --git a/tests/mysql/query.rs b/tests/mysql/query.rs index fd656678b..013e5ef04 100644 --- a/tests/mysql/query.rs +++ b/tests/mysql/query.rs @@ -1005,6 +1005,27 @@ fn select_58() { ); } +#[test] +fn select_coalesce() { + let query = Query::select() + .expr(Func::coalesce([ + Query::select() + .from(Char::Table) + .expr(Func::max(Expr::col(Character::Id))) + .take() + .into_sub_query_expr(), + 1.into(), + Value::Bool(None).into(), + ])) + .from(Char::Table) + .to_owned(); + + assert_eq!( + query.to_string(MysqlQueryBuilder), + r#"SELECT COALESCE((SELECT MAX(`id`) FROM `character`), 1, NULL) FROM `character`"# + ); +} + #[test] #[allow(clippy::approx_constant)] fn insert_2() { @@ -1328,6 +1349,32 @@ fn insert_on_conflict_6() { ); } +#[test] +fn insert_coalesce() { + assert_eq!(Query::insert() + .into_table(Glyph::Table) + .columns([Glyph::Image, Glyph::Aspect]) + .values_panic([ + "04108048005887010020060000204E0180400400".into(), + Func::coalesce([Query::select() + .from(Glyph::Table) + .expr(Func::max(Expr::col(Glyph::Aspect))) + .take() + .into_sub_query_expr(), + 1.into(), + Value::Bool(None).into(), + ]) + .into(), + ]) + .to_string(MysqlQueryBuilder), + [ + r#"INSERT INTO `glyph` (`image`, `aspect`)"#, + r#"VALUES ('04108048005887010020060000204E0180400400', COALESCE((SELECT MAX(`aspect`) FROM `glyph`), 1, NULL))"#, + ] + .join(" ") + ); +} + #[test] fn update_1() { assert_eq!( diff --git a/tests/postgres/query.rs b/tests/postgres/query.rs index 352177db5..51e5931c0 100644 --- a/tests/postgres/query.rs +++ b/tests/postgres/query.rs @@ -1,6 +1,7 @@ use super::*; use pretty_assertions::assert_eq; use sea_query::extension::postgres::PgBinOper; +use sea_query::Value; #[test] fn select_1() { @@ -103,7 +104,7 @@ fn select_8() { .from(Char::Table) .left_join( Font::Table, - Expr::col((Char::Table, Char::FontId)).equals((Font::Table, Font::Id)) + Expr::col((Char::Table, Char::FontId)).equals((Font::Table, Font::Id)), ) .to_string(PostgresQueryBuilder), r#"SELECT "character" FROM "character" LEFT JOIN "font" ON "character"."font_id" = "font"."id""# @@ -1095,6 +1096,27 @@ fn select_62() { ); } +#[test] +fn select_coalesce() { + let query = Query::select() + .expr(Func::coalesce([ + Query::select() + .from(Char::Table) + .expr(Func::count(Expr::col(Character::Id))) + .take() + .into_sub_query_expr(), + 1.into(), + Value::Bool(None).into(), + ])) + .from(Char::Table) + .to_owned(); + + assert_eq!( + query.to_string(PostgresQueryBuilder), + r#"SELECT COALESCE((SELECT COUNT("id") FROM "character"), 1, NULL) FROM "character""# + ); +} + #[test] #[allow(clippy::approx_constant)] fn insert_2() { @@ -1473,6 +1495,32 @@ fn insert_returning_specific_columns() { ); } +#[test] +fn insert_coalesce() { + assert_eq!(Query::insert() + .into_table(Glyph::Table) + .columns([Glyph::Image, Glyph::Aspect]) + .values_panic([ + "04108048005887010020060000204E0180400400".into(), + Func::coalesce([Query::select() + .from(Glyph::Table) + .expr(Func::max(Expr::col(Glyph::Aspect))) + .take() + .into_sub_query_expr(), + 1.into(), + Value::Bool(None).into(), + ]) + .into(), + ]) + .to_string(PostgresQueryBuilder), + [ + r#"INSERT INTO "glyph" ("image", "aspect")"#, + r#"VALUES ('04108048005887010020060000204E0180400400', COALESCE((SELECT MAX("aspect") FROM "glyph"), 1, NULL))"#, + ] + .join(" ") + ); +} + #[test] fn update_1() { assert_eq!( diff --git a/tests/sqlite/query.rs b/tests/sqlite/query.rs index 0bfd0c589..36397ce47 100644 --- a/tests/sqlite/query.rs +++ b/tests/sqlite/query.rs @@ -1041,6 +1041,27 @@ fn cast_json_field_bin_oper() { ); } +#[test] +fn select_coalesce() { + let query = Query::select() + .expr(Func::coalesce([ + Query::select() + .from(Char::Table) + .expr(Func::max(Expr::col(Character::Id))) + .take() + .into_sub_query_expr(), + 1.into(), + Value::Bool(None).into(), + ])) + .from(Char::Table) + .to_owned(); + + assert_eq!( + query.to_string(SqliteQueryBuilder), + r#"SELECT COALESCE((SELECT MAX("id") FROM "character"), 1, NULL) FROM "character""# + ); +} + #[test] #[allow(clippy::approx_constant)] fn insert_2() { @@ -1389,6 +1410,32 @@ fn insert_returning_specific_columns() { ); } +#[test] +fn insert_coalesce() { + assert_eq!(Query::insert() + .into_table(Glyph::Table) + .columns([Glyph::Image, Glyph::Aspect]) + .values_panic([ + "04108048005887010020060000204E0180400400".into(), + Func::coalesce([Query::select() + .from(Glyph::Table) + .expr(Func::max(Expr::col(Glyph::Aspect))) + .take() + .into_sub_query_expr(), + 1.into(), + Value::Bool(None).into(), + ]) + .into(), + ]) + .to_string(SqliteQueryBuilder), + [ + r#"INSERT INTO "glyph" ("image", "aspect")"#, + r#"VALUES ('04108048005887010020060000204E0180400400', COALESCE((SELECT MAX("aspect") FROM "glyph"), 1, NULL))"#, + ] + .join(" ") + ); +} + #[test] fn update_1() { assert_eq!(